リポジトリ / emssr / independent_common_compiler
r2
| r2 emssr | 1 | # -*- coding: utf-8 -*- |
| r2 emssr | 2 | """セル一つを compile して実行し、scope を組む。 |
| r2 emssr | 3 | |
| r2 emssr | 4 | doc: commonc.pipeline casm.lane64.host plain.format |
| r2 emssr | 5 | |
| r2 emssr | 6 | 道は一本になる(19 §7.1)。 |
| r2 emssr | 7 | |
| r2 emssr | 8 | .dic + .cmn ──(commonc_lane64.bin on lane64)──▶ lane64 image ──(lane64)──▶ M |
| r2 emssr | 9 | |
| r2 emssr | 10 | compile の受け渡しは toolchain.compile と同じだが、入口が path ではなく文字列で、 |
| r2 emssr | 11 | commonc の作業領域を捨てずに返す。scope はその作業領域から組む。 |
| r2 emssr | 12 | |
| r2 emssr | 13 | CPython でも Pyodide でも同じ code が動く。違うのは commonc の image の場所だけで、 |
| r2 emssr | 14 | それは start() の引数で決まる。 |
| r2 emssr | 15 | """ |
| r2 emssr | 16 | |
| r2 emssr | 17 | import hashlib |
| r2 emssr | 18 | import os |
| r2 emssr | 19 | import sys |
| r2 emssr | 20 | import tempfile |
| r2 emssr | 21 | |
| r2 emssr | 22 | import scope |
| r2 emssr | 23 | |
| r2 emssr | 24 | _HERE = os.path.dirname(os.path.abspath(__file__)) |
| r2 emssr | 25 | if _HERE not in sys.path: |
| r2 emssr | 26 | sys.path.insert(0, _HERE) |
| r2 emssr | 27 | |
| r2 emssr | 28 | import lex # noqa: E402 Python_vCPU から借りる |
| r2 emssr | 29 | import toolchain # noqa: E402 |
| r2 emssr | 30 | import vcpu # noqa: E402 |
| r2 emssr | 31 | |
| r2 emssr | 32 | WORK = toolchain.WORK # 本文の作業領域の基底。ワイヤ(語 64+w)の上 |
| r2 emssr | 33 | DEFAULT_WORDS = WORK + (1 << 13) |
| r2 emssr | 34 | DEFAULT_BUDGET = 1 << 24 # block を辿る上限。止まらないセルをここで止める |
| r2 emssr | 35 | |
| r2 emssr | 36 | |
| r2 emssr | 37 | class CellError(Exception): |
| r2 emssr | 38 | pass |
| r2 emssr | 39 | |
| r2 emssr | 40 | |
| r2 emssr | 41 | def start(commonc): |
| r2 emssr | 42 | """commonc の image の場所を決める。翻訳は最初の compile のときに一度だけ行う""" |
| r2 emssr | 43 | toolchain.COMMONC = commonc |
| r2 emssr | 44 | |
| r2 emssr | 45 | |
| r2 emssr | 46 | # ===== compile ===== |
| r2 emssr | 47 | # |
| r2 emssr | 48 | # host が commonc へ渡すもの(19 §3.1)。toolchain.compile と同じ値を置く。 |
| r2 emssr | 49 | # |
| r2 emssr | 50 | # 語 0 N 入力の byte 数(表 + 本文) |
| r2 emssr | 51 | # 語 1 IB 入力の基底語番号 |
| r2 emssr | 52 | # 語 4 B 配列の刻み |
| r2 emssr | 53 | # 語 130 LEXB 表の byte 数 |
| r2 emssr | 54 | |
| r2 emssr | 55 | # 読み取り専用の場所へ入れても動くように、表は一時フォルダへ書く |
| r2 emssr | 56 | _DIC = os.path.join(tempfile.gettempdir(), "common_scope_cell.dic") |
| r2 emssr | 57 | |
| r2 emssr | 58 | |
| r2 emssr | 59 | def _dic_file(text): |
| r2 emssr | 60 | """採用語彙の表を lex へ渡すために一度だけ file にする""" |
| r2 emssr | 61 | open(_DIC, "w", encoding="utf-8", newline="\n").write(text) |
| r2 emssr | 62 | return _DIC |
| r2 emssr | 63 | |
| r2 emssr | 64 | |
| r2 emssr | 65 | def compile_text(source, dic=None): |
| r2 emssr | 66 | """平文を image にする。返りは (image, n_need, commonc の作業領域)""" |
| r2 emssr | 67 | table, est, fold = "", len(source) // 2, {} |
| r2 emssr | 68 | if dic and dic.strip(): |
| r2 emssr | 69 | path = _dic_file(dic) |
| r2 emssr | 70 | table = lex.lexicon(path) |
| r2 emssr | 71 | est = lex.expanded_syllables(path, source) |
| r2 emssr | 72 | fold = scope.lexicon(lex.entries(path)) |
| r2 emssr | 73 | # commonc を正本にする。scope.check は表示用の補助表から行う診断なので、 |
| r2 emssr | 74 | # Common 本体より先に文を棄却してはいけない。語彙が増えたときに補助表だけが |
| r2 emssr | 75 | # 古いと、正規の commonc では通る文を notebook だけが拒否してしまうためになる。 |
| r2 emssr | 76 | data = (table + source).encode("utf-8") |
| r2 emssr | 77 | n = len(data) |
| r2 emssr | 78 | b = max(n + 80, 4096, 2 * est + 80) |
| r2 emssr | 79 | base = toolchain.IN_MUL * b |
| r2 emssr | 80 | lane = vcpu.Lane(base + n + 4096) |
| r2 emssr | 81 | lane.put_input(list(data), base) |
| r2 emssr | 82 | lane.M[toolchain.W_B] = b |
| r2 emssr | 83 | lane.M[toolchain.W_LEXB] = len(table.encode("utf-8")) |
| r2 emssr | 84 | rep = vcpu.run_lane(toolchain._commonc(), lane) |
| r2 emssr | 85 | if rep != 0: |
| r2 emssr | 86 | # commonc が実際に拒否したときだけ、平文側の診断で理由を細かくする。 |
| r2 emssr | 87 | # 診断表が Common の全語彙を知らなくても、成功した文を壊すことはない。 |
| r2 emssr | 88 | hit = scope.check(source, fold) |
| r2 emssr | 89 | if hit: |
| r2 emssr | 90 | raise CellError(hit) |
| r2 emssr | 91 | raise CellError("commonc がこの文を読めない(報告 %s)。" |
| r2 emssr | 92 | "音節表の外の綴りか、机の上が合っていない" % rep) |
| r2 emssr | 93 | imgb, nimg, need = lane.M[1], lane.M[2], lane.M[3] |
| r2 emssr | 94 | if not (imgb and nimg): |
| r2 emssr | 95 | raise CellError("commonc が image を出さない") |
| r2 emssr | 96 | if need > WORK: |
| r2 emssr | 97 | raise CellError("箱が多すぎてワイヤが作業領域に重なる(n_need %d > %d)" % (need, WORK)) |
| r2 emssr | 98 | return bytes(v & 255 for v in lane.M[imgb:imgb + nimg]), need, lane.M |
| r2 emssr | 99 | |
| r2 emssr | 100 | |
| r2 emssr | 101 | # ===== 実行する ===== |
| r2 emssr | 102 | # |
| r2 emssr | 103 | # ワイヤの語を落とす覆い(toolchain.load の wires)は使わない。scope は |
| r2 emssr | 104 | # 箱ひとつずつの値を見せるので、語 64+w を全部残す。 |
| r2 emssr | 105 | |
| r2 emssr | 106 | def run_image(image, seeds=None, words=DEFAULT_WORDS, budget=DEFAULT_BUDGET): |
| r2 emssr | 107 | tab = vcpu.translate(vcpu.decode(image))[1] |
| r2 emssr | 108 | lane = vcpu.Lane(words) |
| r2 emssr | 109 | for k, v in (seeds or {}).items(): |
| r2 emssr | 110 | lane.M[int(k)] = int(v) |
| r2 emssr | 111 | try: |
| r2 emssr | 112 | rep = vcpu.run_lane(tab, lane, budget=budget) |
| r2 emssr | 113 | except RuntimeError: |
| r2 emssr | 114 | raise CellError("block を %d 回辿っても終わりません。Ω の「もう良いか」を見直す" % budget) |
| r2 emssr | 115 | except IndexError: |
| r2 emssr | 116 | raise CellError("語番号が作業領域を越えた(語数 %d)。セルの words を増やす" % words) |
| r2 emssr | 117 | return rep, lane |
| r2 emssr | 118 | |
| r2 emssr | 119 | |
| r2 emssr | 120 | def cell(source, dic=None, seeds=None, words=DEFAULT_WORDS, budget=DEFAULT_BUDGET): |
| r2 emssr | 121 | """セル一つ。compile して実行し、scope を返す""" |
| r2 emssr | 122 | image, need, cm = compile_text(source, dic) |
| r2 emssr | 123 | rep, lane = run_image(image, seeds, words, budget) |
| r2 emssr | 124 | |
| r2 emssr | 125 | table = scope.lexicon(lex.entries(_DIC)) if (dic and dic.strip()) else {} |
| r2 emssr | 126 | a = scope.arrays(cm) |
| r2 emssr | 127 | ws = scope.words(a, table) |
| r2 emssr | 128 | bx = scope.boxes(a, lane.M) |
| r2 emssr | 129 | gm = scope.gamma(a) |
| r2 emssr | 130 | depth = {g["word"]: a["opd"][g["box"]] for g in gm} |
| r2 emssr | 131 | return { |
| r2 emssr | 132 | "ok": True, |
| r2 emssr | 133 | "report": rep, |
| r2 emssr | 134 | "source": source, |
| r2 emssr | 135 | "size": {"syl": a["nSyl"] - a["body"], "box": a["nBox"], |
| r2 emssr | 136 | "need": need, "image": len(image)}, |
| r2 emssr | 137 | # 二つの平文が同じチェンバーかを見る hash。image が一致すれば読みも一致する |
| r2 emssr | 138 | "mark": hashlib.sha256(image).hexdigest()[:16], |
| r2 emssr | 139 | "words": ws, |
| r2 emssr | 140 | "frontier": scope.frontier(ws), |
| r2 emssr | 141 | "boxes": bx, |
| r2 emssr | 142 | "align": scope.align(ws, a), |
| r2 emssr | 143 | "gamma": [dict(g, **scope.dyad(lane.M[g["word"]], depth[g["word"]])) |
| r2 emssr | 144 | for g in gm], |
| r2 emssr | 145 | "image": scope.image(vcpu, image), |
| r2 emssr | 146 | "out": [lane.M[k] for k in range(64)], |
| r2 emssr | 147 | "put": bytes(lane.OUT).decode("latin-1"), |
| r2 emssr | 148 | } |
| r2 emssr | 149 | |
| r2 emssr | 150 | |
| r2 emssr | 151 | # ===== 接合秩(Common-Lang/1 定理 4.1)===== |
| r2 emssr | 152 | # |
| r2 emssr | 153 | # F[α][β] = f(α, β), ρ = rank_F2 F, f = ⊕_{r<ρ} p_r(x_A) q_r(x_B) |
| r2 emssr | 154 | # |
| r2 emssr | 155 | # 階数は common_apps/joinrank の平文が持つ。ここは頂点値の表を作って渡すだけになる。 |
| r2 emssr | 156 | # 表は同じ image を 2^n 回実行して集める。lane の作業領域は交わらない(20 式 1.2)ので、 |
| r2 emssr | 157 | # 実行は互いに独立になる。 |
| r2 emssr | 158 | |
| r2 emssr | 159 | def vertices(image, inputs, words=DEFAULT_WORDS, budget=DEFAULT_BUDGET, out=0): |
| r2 emssr | 160 | """入力の語へ {0,1}^n を全部当て、語 out の値を頂点値として並べる""" |
| r2 emssr | 161 | n = len(inputs) |
| r2 emssr | 162 | if n > 12: |
| r2 emssr | 163 | raise CellError("入口が %d 本。頂点は 2^n なので 12 本までにする" % n) |
| r2 emssr | 164 | tab = vcpu.translate(vcpu.decode(image))[1] |
| r2 emssr | 165 | tbl = [] |
| r2 emssr | 166 | for v in range(1 << n): |
| r2 emssr | 167 | lane = vcpu.Lane(words) |
| r2 emssr | 168 | for k, w in enumerate(inputs): |
| r2 emssr | 169 | lane.M[int(w)] = (v >> k) & 1 |
| r2 emssr | 170 | vcpu.run_lane(tab, lane, budget=budget) |
| r2 emssr | 171 | tbl.append(1 if lane.M[out] else 0) |
| r2 emssr | 172 | return tbl |