📦 Out of Context リポジトリ

リポジトリ / emssr / independent_common_compiler

r2
r2 emssr1
# -*- coding: utf-8 -*-
r2 emssr2
"""commonc の作業領域を画面の形へ写す。
r2 emssr3
r2 emssr4
doc: commonc.alloc commonc.stages plain.tensor casm.lane64.block
r2 emssr5
r2 emssr6
compile が終わった lane には、commonc が自分で組んだ表がそのまま残っている
r2 emssr7
(19 §3.2)。この module はその表を読むだけで、計算を足さない。
r2 emssr8
r2 emssr9
    語 4    B        配列の刻み
r2 emssr10
    語 5    n_syl    音節の数
r2 emssr11
    語 6    n_box    箱の数
r2 emssr12
    語 132  BODY     本文の最初の音節番号。手前は採用語彙の表
r2 emssr13
    SYL   1B         音節番号
r2 emssr14
    OPK   4B         種類  1 Cst 2 Add 3 Mul 4 Neg 5 Store 6 Ok 7 Load 8 EqBr 9 LtBr
r2 emssr15
    OPA   6B         第一引数の箱番号(Cst では分子)
r2 emssr16
    OPB   8B         第二引数の箱番号
r2 emssr17
    OPD  10B         深度 δ
r2 emssr18
    OPL  12B         分岐先の箱番号
r2 emssr19
r2 emssr20
覆いは λ(w) = 語 64 + w(19 式 6.1)なので、実行した後の語 64+w が箱 w の分子になる。
r2 emssr21
分子と深度の対で二進有理数が決まる。
r2 emssr22
r2 emssr23
    値(w) = OPA・OPB から組んだ分子 / 2^OPD[w]                    01 §1
r2 emssr24
"""
r2 emssr25
r2 emssr26
import generators
r2 emssr27
r2 emssr28
KIND = {1: "Cst", 2: "Add", 3: "Mul", 4: "Neg", 5: "Store",
r2 emssr29
        6: "Ok", 7: "Load", 8: "EqBr", 9: "LtBr"}
r2 emssr30
r2 emssr31
# 引数の欄を辺として読む種類。Cst の OPA は分子であって箱番号ではない
r2 emssr32
EDGES = {2: (True, True), 3: (True, True), 4: (True, False),
r2 emssr33
         5: (True, True), 7: (True, False), 8: (True, True), 9: (True, True)}
r2 emssr34
r2 emssr35
WIRE_BASE = 64          # 19 式 6.1
r2 emssr36
r2 emssr37
r2 emssr38
def arrays(M):
r2 emssr39
    """commonc の表を切り出す。返りは辞書一つ"""
r2 emssr40
    B = M[4]
r2 emssr41
    n_syl, n_box = M[5], M[6]
r2 emssr42
    return {
r2 emssr43
        "B": B,
r2 emssr44
        "nSyl": n_syl,
r2 emssr45
        "nBox": n_box,
r2 emssr46
        "nNeed": M[3],
r2 emssr47
        "body": M[132],
r2 emssr48
        "syl": M[B:B + n_syl],
r2 emssr49
        "opk": M[4 * B:4 * B + n_box],
r2 emssr50
        "opa": M[6 * B:6 * B + n_box],
r2 emssr51
        "opb": M[8 * B:8 * B + n_box],
r2 emssr52
        "opd": M[10 * B:10 * B + n_box],
r2 emssr53
        "opl": M[12 * B:12 * B + n_box],
r2 emssr54
    }
r2 emssr55
r2 emssr56
r2 emssr57
# ===== 第一段:音節列を語と層へ切る(16 §2)=====
r2 emssr58
#
r2 emssr59
# 疎テンソル S(k,q,r) は音節列の中に既に入っている。
r2 emssr60
#
r2 emssr61
#   r   層境界音 ka の出現回数
r2 emssr62
#   q   層の中の位置。無声ワイヤ ki が一本ぶん進める
r2 emssr63
#
r2 emssr64
# 層の切れ目だけを数えるので、ここに文法解析は現れない。
r2 emssr65
r2 emssr66
def words(a, lexicon=None):
r2 emssr67
    """本文の音節を語へ切り、層番号と位置を付ける。表の区間は落とす"""
r2 emssr68
    body = generators.read(a["syl"][a["body"]:], lexicon)
r2 emssr69
    out, r, q = [], 0, 0
r2 emssr70
    for spelling, symbol, pin, pout, boxes, layer, _n in body:
r2 emssr71
        if spelling == "ka":
r2 emssr72
            r += 1
r2 emssr73
            q = 0
r2 emssr74
            continue
r2 emssr75
        out.append({"syl": spelling, "sym": symbol, "pin": pin, "pout": pout,
r2 emssr76
                    "boxes": boxes, "cls": layer, "r": r, "q": q})
r2 emssr77
        q += 1
r2 emssr78
    return out
r2 emssr79
r2 emssr80
r2 emssr81
# ===== 採用語彙の見出し(16 §1.3〜1.5)=====
r2 emssr82
#
r2 emssr83
# 見出しは文を引く。Lex ⊆ Σ* × C(16 式 1.5)なので、余域はチェンバーであって
r2 emssr84
# 原子箱ではない。差し込み(式 1.6)は書かれた位置 q から文の層を置くので、
r2 emssr85
# 見出しの境界の幅は文を畳めば出る。
r2 emssr86
#
r2 emssr87
# 文を畳むときは、机が足りなくなった分を「外から借りた」と数える。
r2 emssr88
# 借りた本数が Γ⁻ の幅、畳み終えた机の高さが Γ⁺ の幅になる。
r2 emssr89
r2 emssr90
def fold(text, table):
r2 emssr91
    """文ひとつの (pin, pout, 箱の本数)。table は先に畳んだ見出しの表"""
r2 emssr92
    front, borrowed, boxes = [], 0, 0
r2 emssr93
    layer = []
r2 emssr94
    for spelling, _sym, pin, pout, bx, _cls, _n in generators.read(
r2 emssr95
            generators.stream(text), table):
r2 emssr96
        if spelling == "ka":
r2 emssr97
            front, borrowed, boxes = _fold_layer(front, layer, borrowed, boxes)
r2 emssr98
            layer = []
r2 emssr99
            continue
r2 emssr100
        layer.append((spelling, pin, pout, bx))
r2 emssr101
    front, borrowed, boxes = _fold_layer(front, layer, borrowed, boxes)
r2 emssr102
    return borrowed, len(front), boxes
r2 emssr103
r2 emssr104
r2 emssr105
def _fold_layer(front, layer, borrowed, boxes):
r2 emssr106
    cur, out = 0, []
r2 emssr107
    for spelling, pin, pout, bx in layer:
r2 emssr108
        if bx is None:
r2 emssr109
            boxes = None
r2 emssr110
        elif boxes is not None:
r2 emssr111
            boxes += bx
r2 emssr112
        take = 1 if spelling == "ki" else pin
r2 emssr113
        for _ in range(take):
r2 emssr114
            if cur >= len(front):
r2 emssr115
                front = front + ["Γ⁻"]
r2 emssr116
                borrowed += 1
r2 emssr117
            if spelling == "ki":
r2 emssr118
                out.append(front[cur])
r2 emssr119
            cur += 1
r2 emssr120
        if spelling != "ki":
r2 emssr121
            out += [spelling] * pout
r2 emssr122
    return out + front[cur:], borrowed, boxes
r2 emssr123
r2 emssr124
r2 emssr125
def lexicon(entries):
r2 emssr126
    """`.dic` の行を順に畳む。後の行は前の見出しを引ける"""
r2 emssr127
    table = {}
r2 emssr128
    for _n, name, body in entries:
r2 emssr129
        try:
r2 emssr130
            table[name] = fold(body, table)
r2 emssr131
        except ValueError:
r2 emssr132
            table[name] = (0, 0, None)
r2 emssr133
    return table
r2 emssr134
r2 emssr135
r2 emssr136
# ===== 第二段:層ごとの frontier(19 §4.2)=====
r2 emssr137
#
r2 emssr138
# 箱は現在位置 q から消費する。無声ワイヤ ki が一本を素通しして位置を一つ進める。
r2 emssr139
#
r2 emssr140
#   fr(Λ_{r+1}) = fr(Λ_r) の未消費部 ‖ 層 r の出力
r2 emssr141
#
r2 emssr142
# 位置 0 が机の上になる。hu は机の上へ置き、ki hu は一つ奥へ差し込む。
r2 emssr143
r2 emssr144
def frontier(ws):
r2 emssr145
    """層ごとの机と、箱が机のどこを取ってどこへ置いたか。
r2 emssr146
r2 emssr147
    返りの一行は
r2 emssr148
r2 emssr149
        r      層番号
r2 emssr150
        in     入口の机。欄は語の綴り
r2 emssr151
        out    出口の机
r2 emssr152
        put    [{syl, take, give, at}]  take は入口の区間、give は出口の区間
r2 emssr153
    """
r2 emssr154
    rows, front = [], []
r2 emssr155
    for r in range(max((w["r"] for w in ws), default=-1) + 1):
r2 emssr156
        layer = [w for w in ws if w["r"] == r]
r2 emssr157
        if not layer:
r2 emssr158
            continue
r2 emssr159
        cur, out, put = 0, [], []
r2 emssr160
        for w in layer:
r2 emssr161
            if w["syl"] == "ki":
r2 emssr162
                if cur < len(front):
r2 emssr163
                    out.append(front[cur])
r2 emssr164
                put.append({"syl": "ki", "take": [cur, cur + 1],
r2 emssr165
                            "give": [len(out) - 1, len(out)], "wire": True})
r2 emssr166
                cur += 1
r2 emssr167
                continue
r2 emssr168
            at = len(out)
r2 emssr169
            out += [w["syl"]] * w["pout"]
r2 emssr170
            put.append({"syl": w["syl"], "cls": w["cls"],
r2 emssr171
                        "take": [cur, cur + w["pin"]],
r2 emssr172
                        "give": [at, at + w["pout"]], "wire": False})
r2 emssr173
            cur += w["pin"]
r2 emssr174
        rest = front[cur:]
r2 emssr175
        for k in range(len(rest)):
r2 emssr176
            put.append({"syl": rest[k], "take": [cur + k, cur + k + 1],
r2 emssr177
                        "give": [len(out) + k, len(out) + k + 1], "wire": True})
r2 emssr178
        rows.append({"r": r, "in": list(front), "out": out + rest, "put": put})
r2 emssr179
        front = out + rest
r2 emssr180
    return rows
r2 emssr181
r2 emssr182
r2 emssr183
# ===== 第三段:箱の DAG(19 §4.2、§4.3)=====
r2 emssr184
r2 emssr185
def boxes(a, wire=None):
r2 emssr186
    """箱を節点、OPA と OPB を辺として並べる。
r2 emssr187
r2 emssr188
    wire は実行した後の作業領域。渡すと語 64+w を分子として添える。
r2 emssr189
    """
r2 emssr190
    opk, opa, opb, opd, opl = a["opk"], a["opa"], a["opb"], a["opd"], a["opl"]
r2 emssr191
    out = []
r2 emssr192
    for i in range(a["nBox"]):
r2 emssr193
        k = opk[i]
r2 emssr194
        ua, ub = EDGES.get(k, (False, False))
r2 emssr195
        node = {"i": i, "kind": KIND.get(k, str(k)), "d": opd[i],
r2 emssr196
                "in": [x for x, use in ((opa[i], ua), (opb[i], ub)) if use]}
r2 emssr197
        if k == 1:
r2 emssr198
            node["num"] = opa[i]
r2 emssr199
        if k in (8, 9):
r2 emssr200
            node["to"] = opl[i]
r2 emssr201
        if wire is not None and WIRE_BASE + i < len(wire):
r2 emssr202
            node["v"] = wire[WIRE_BASE + i]
r2 emssr203
        out.append(node)
r2 emssr204
    return out
r2 emssr205
r2 emssr206
r2 emssr207
def gamma(a):
r2 emssr208
    """出口境界 Γ⁺。末尾の Cst と Store の連なりが語 0.. を埋める(19 §2.1)"""
r2 emssr209
    opk, opa = a["opk"], a["opa"]
r2 emssr210
    i = a["nBox"] - 1
r2 emssr211
    out = []
r2 emssr212
    while i >= 0 and opk[i] in (1, 5, 6):
r2 emssr213
        if opk[i] == 5 and opk[opa[i]] == 1 and opa[opa[i]] < WIRE_BASE:
r2 emssr214
            out.append({"word": opa[opa[i]], "box": a["opb"][i]})
r2 emssr215
        i -= 1
r2 emssr216
    return sorted(out, key=lambda x: x["word"])
r2 emssr217
r2 emssr218
r2 emssr219
def align(ws, a):
r2 emssr220
    """語と箱を並べて突き合わせる。
r2 emssr221
r2 emssr222
    生成元ごとに開く箱の本数は 16 §5 の表で決まる。読みの順に数えて合えば、
r2 emssr223
    画面の語と DAG の節点が一対一で結べる。判定値 Q の語は本数が固定でないので、
r2 emssr224
    その場合は結ばずに ok = False を返す。
r2 emssr225
    """
r2 emssr226
    n, spans = 0, []
r2 emssr227
    for w in ws:
r2 emssr228
        if w["boxes"] is None:
r2 emssr229
            return {"ok": False, "why": "箱の本数が固定でない語がある", "spans": []}
r2 emssr230
        spans.append([n, n + w["boxes"]])
r2 emssr231
        n += w["boxes"]
r2 emssr232
    tail = a["nBox"] - n
r2 emssr233
    if tail < 0:
r2 emssr234
        return {"ok": False, "why": "箱が語の総和に足りない", "spans": []}
r2 emssr235
    return {"ok": True, "spans": spans, "tail": [n, a["nBox"]]}
r2 emssr236
r2 emssr237
r2 emssr238
# ===== 第四段:lane64 の image(20 §7)=====
r2 emssr239
r2 emssr240
def image(vcpu, img):
r2 emssr241
    """block への分け方(式 7.4)と、block の中の層(式 7.1、7.2)"""
r2 emssr242
    prog = vcpu.decode(img)
r2 emssr243
    _ent, _idx, bl = vcpu.blocks(prog)
r2 emssr244
    out = []
r2 emssr245
    for pc, body, term, _stop in bl:
r2 emssr246
        lay = vcpu.layers(prog, body)
r2 emssr247
        out.append({"pc": pc, "n": len(body),
r2 emssr248
                    "term": vcpu.NAME[prog[term][0]] if term is not None else "落ちる",
r2 emssr249
                    "layers": [len(x) for x in lay]})
r2 emssr250
    kinds = {}
r2 emssr251
    for ins in prog:
r2 emssr252
        kinds[vcpu.NAME[ins[0]]] = kinds.get(vcpu.NAME[ins[0]], 0) + 1
r2 emssr253
    return {"bytes": len(img), "insns": len(prog), "blocks": out, "kinds": kinds}
r2 emssr254
r2 emssr255
r2 emssr256
# ===== 二進有理数(01 §1、20 §0.3)=====
r2 emssr257
r2 emssr258
def dyad(num, d):
r2 emssr259
    """x/2^δ を約分して (分子, 分母, 十進) にする"""
r2 emssr260
    while d > 0 and num % 2 == 0:
r2 emssr261
        num //= 2
r2 emssr262
        d -= 1
r2 emssr263
    den = 1 << d
r2 emssr264
    return {"num": num, "den": den, "dec": num / den if den else 0.0}
r2 emssr265
r2 emssr266
r2 emssr267
# ===== 読む前の診断 =====
r2 emssr268
#
r2 emssr269
# commonc は誤りを全部「報告 42」で返す。どこを直せばよいかが分からないので、
r2 emssr270
# 平文の側で分かる範囲を先に見る。ここで見るのは三つだけになる。
r2 emssr271
#
r2 emssr272
#   1. 音節表の外の綴り          19 式 4.1
r2 emssr273
#   2. 机が足りない              平文は Γ⁻ = I から始まる(16 §1.1)
r2 emssr274
#   3. 型の食い違い              Bit を語へ書く、Ω の閉じへ Dyad を渡す
r2 emssr275
#
r2 emssr276
# 断定できない所は黙る。commonc の判定を上書きしない。
r2 emssr277
r2 emssr278
# 語が出す値の型。分からない語は None にして、その先の判定をやめる
r2 emssr279
_CLASS = {
r2 emssr280
    "hu": "dyad", "no": "dyad", "ha": "dyad", "ti": "dyad", "se": "dyad",
r2 emssr281
    "mi": "dyad", "mo": "dyad",
r2 emssr282
    "hi": "bit", "so": "bit", "ne": "bit", "ra": "bit", "go": "bit",
r2 emssr283
    "to": "judge", "nu": "judge", "yu": "judge", "ye": "judge", "yo": "judge",
r2 emssr284
    "ri": "judge", "ru": "judge", "na": "judge", "ni": "judge",
r2 emssr285
    "re": "trace", "sa": "trace", "si": "trace", "ke": "trace",
r2 emssr286
    "su": "trace", "ro": "trace", "wa": "trace",
r2 emssr287
    "wi": "point", "ko": "point", "ta": "point", "de": "point", "do": "point",
r2 emssr288
    "wu": "reflection", "we": "reflection", "wo": "reflection", "ga": "reflection",
r2 emssr289
    "gi": "point",
r2 emssr290
}
r2 emssr291
r2 emssr292
_TYPE_NAME = {"dyad": "数", "bit": "判定", "judge": "判定値 𝒬"}
r2 emssr293
r2 emssr294
r2 emssr295
def check(text, table=None):
r2 emssr296
    """平文を読む前に見る。直す場所が分かる文言を返す。問題が無ければ None"""
r2 emssr297
    try:
r2 emssr298
        syl = generators.stream(text)
r2 emssr299
    except ValueError as e:
r2 emssr300
        return str(e)
r2 emssr301
r2 emssr302
    front, layer = [], []
r2 emssr303
    for spelling, _sym, pin, pout, _bx, _cls, _n in generators.read(syl, table or {}):
r2 emssr304
        if spelling == "ka":
r2 emssr305
            msg, front = _check_layer(front, layer)
r2 emssr306
            if msg:
r2 emssr307
                return msg
r2 emssr308
            layer = []
r2 emssr309
            continue
r2 emssr310
        layer.append((spelling, pin, pout))
r2 emssr311
    msg, _front = _check_layer(front, layer)
r2 emssr312
    return msg
r2 emssr313
r2 emssr314
r2 emssr315
def _check_layer(front, layer):
r2 emssr316
    cur, out = 0, []
r2 emssr317
    for spelling, pin, pout in layer:
r2 emssr318
        take = 1 if spelling == "ki" else pin
r2 emssr319
        got = []
r2 emssr320
        for _ in range(take):
r2 emssr321
            if cur >= len(front):
r2 emssr322
                return ("机に置いた物が足りない。「%s」は %d つ取るが、机にはそれだけ無い。"
r2 emssr323
                        "平文は空の机から始まるので、足りない分は hu で立てる" % (spelling, take)), front
r2 emssr324
            got.append(front[cur])
r2 emssr325
            cur += 1
r2 emssr326
        if spelling == "mu" and len(got) == 2 and got[1] in ("bit", "judge"):
r2 emssr327
            return ("%s を語へ書けない。mu が取る値は数でなければならない。"
r2 emssr328
                    "mo を挟んで数へ戻す" % _TYPE_NAME[got[1]]), front
r2 emssr329
        if spelling == "yi" and got and got[0] == "dyad":
r2 emssr330
            return ("Ω の閉じ yi は判定を取る。いま机の上にあるのは数なので、"
r2 emssr331
                    "hi などの判定をそのまま渡す"), front
r2 emssr332
        if spelling == "ki":
r2 emssr333
            out += got
r2 emssr334
        else:
r2 emssr335
            out += [_CLASS.get(spelling, None)] * pout
r2 emssr336
    return None, out + front[cur:]