📦 Out of Context リポジトリ

リポジトリ / emssr / independent_common_compiler

r2
r2 emssr1
# -*- coding: utf-8 -*-
r2 emssr2
"""セル一つを compile して実行し、scope を組む。
r2 emssr3
r2 emssr4
doc: commonc.pipeline casm.lane64.host plain.format
r2 emssr5
r2 emssr6
道は一本になる(19 §7.1)。
r2 emssr7
r2 emssr8
    .dic + .cmn ──(commonc_lane64.bin on lane64)──▶ lane64 image ──(lane64)──▶ M
r2 emssr9
r2 emssr10
compile の受け渡しは toolchain.compile と同じだが、入口が path ではなく文字列で、
r2 emssr11
commonc の作業領域を捨てずに返す。scope はその作業領域から組む。
r2 emssr12
r2 emssr13
CPython でも Pyodide でも同じ code が動く。違うのは commonc の image の場所だけで、
r2 emssr14
それは start() の引数で決まる。
r2 emssr15
"""
r2 emssr16
r2 emssr17
import hashlib
r2 emssr18
import os
r2 emssr19
import sys
r2 emssr20
import tempfile
r2 emssr21
r2 emssr22
import scope
r2 emssr23
r2 emssr24
_HERE = os.path.dirname(os.path.abspath(__file__))
r2 emssr25
if _HERE not in sys.path:
r2 emssr26
    sys.path.insert(0, _HERE)
r2 emssr27
r2 emssr28
import lex          # noqa: E402  Python_vCPU から借りる
r2 emssr29
import toolchain    # noqa: E402
r2 emssr30
import vcpu         # noqa: E402
r2 emssr31
r2 emssr32
WORK = toolchain.WORK               # 本文の作業領域の基底。ワイヤ(語 64+w)の上
r2 emssr33
DEFAULT_WORDS = WORK + (1 << 13)
r2 emssr34
DEFAULT_BUDGET = 1 << 24            # block を辿る上限。止まらないセルをここで止める
r2 emssr35
r2 emssr36
r2 emssr37
class CellError(Exception):
r2 emssr38
    pass
r2 emssr39
r2 emssr40
r2 emssr41
def start(commonc):
r2 emssr42
    """commonc の image の場所を決める。翻訳は最初の compile のときに一度だけ行う"""
r2 emssr43
    toolchain.COMMONC = commonc
r2 emssr44
r2 emssr45
r2 emssr46
# ===== compile =====
r2 emssr47
#
r2 emssr48
# host が commonc へ渡すもの(19 §3.1)。toolchain.compile と同じ値を置く。
r2 emssr49
#
r2 emssr50
#   語 0    N      入力の byte 数(表 + 本文)
r2 emssr51
#   語 1    IB     入力の基底語番号
r2 emssr52
#   語 4    B      配列の刻み
r2 emssr53
#   語 130  LEXB   表の byte 数
r2 emssr54
r2 emssr55
# 読み取り専用の場所へ入れても動くように、表は一時フォルダへ書く
r2 emssr56
_DIC = os.path.join(tempfile.gettempdir(), "common_scope_cell.dic")
r2 emssr57
r2 emssr58
r2 emssr59
def _dic_file(text):
r2 emssr60
    """採用語彙の表を lex へ渡すために一度だけ file にする"""
r2 emssr61
    open(_DIC, "w", encoding="utf-8", newline="\n").write(text)
r2 emssr62
    return _DIC
r2 emssr63
r2 emssr64
r2 emssr65
def compile_text(source, dic=None):
r2 emssr66
    """平文を image にする。返りは (image, n_need, commonc の作業領域)"""
r2 emssr67
    table, est, fold = "", len(source) // 2, {}
r2 emssr68
    if dic and dic.strip():
r2 emssr69
        path = _dic_file(dic)
r2 emssr70
        table = lex.lexicon(path)
r2 emssr71
        est = lex.expanded_syllables(path, source)
r2 emssr72
        fold = scope.lexicon(lex.entries(path))
r2 emssr73
    # commonc を正本にする。scope.check は表示用の補助表から行う診断なので、
r2 emssr74
    # Common 本体より先に文を棄却してはいけない。語彙が増えたときに補助表だけが
r2 emssr75
    # 古いと、正規の commonc では通る文を notebook だけが拒否してしまうためになる。
r2 emssr76
    data = (table + source).encode("utf-8")
r2 emssr77
    n = len(data)
r2 emssr78
    b = max(n + 80, 4096, 2 * est + 80)
r2 emssr79
    base = toolchain.IN_MUL * b
r2 emssr80
    lane = vcpu.Lane(base + n + 4096)
r2 emssr81
    lane.put_input(list(data), base)
r2 emssr82
    lane.M[toolchain.W_B] = b
r2 emssr83
    lane.M[toolchain.W_LEXB] = len(table.encode("utf-8"))
r2 emssr84
    rep = vcpu.run_lane(toolchain._commonc(), lane)
r2 emssr85
    if rep != 0:
r2 emssr86
        # commonc が実際に拒否したときだけ、平文側の診断で理由を細かくする。
r2 emssr87
        # 診断表が Common の全語彙を知らなくても、成功した文を壊すことはない。
r2 emssr88
        hit = scope.check(source, fold)
r2 emssr89
        if hit:
r2 emssr90
            raise CellError(hit)
r2 emssr91
        raise CellError("commonc がこの文を読めない(報告 %s)。"
r2 emssr92
                        "音節表の外の綴りか、机の上が合っていない" % rep)
r2 emssr93
    imgb, nimg, need = lane.M[1], lane.M[2], lane.M[3]
r2 emssr94
    if not (imgb and nimg):
r2 emssr95
        raise CellError("commonc が image を出さない")
r2 emssr96
    if need > WORK:
r2 emssr97
        raise CellError("箱が多すぎてワイヤが作業領域に重なる(n_need %d > %d)" % (need, WORK))
r2 emssr98
    return bytes(v & 255 for v in lane.M[imgb:imgb + nimg]), need, lane.M
r2 emssr99
r2 emssr100
r2 emssr101
# ===== 実行する =====
r2 emssr102
#
r2 emssr103
# ワイヤの語を落とす覆い(toolchain.load の wires)は使わない。scope は
r2 emssr104
# 箱ひとつずつの値を見せるので、語 64+w を全部残す。
r2 emssr105
r2 emssr106
def run_image(image, seeds=None, words=DEFAULT_WORDS, budget=DEFAULT_BUDGET):
r2 emssr107
    tab = vcpu.translate(vcpu.decode(image))[1]
r2 emssr108
    lane = vcpu.Lane(words)
r2 emssr109
    for k, v in (seeds or {}).items():
r2 emssr110
        lane.M[int(k)] = int(v)
r2 emssr111
    try:
r2 emssr112
        rep = vcpu.run_lane(tab, lane, budget=budget)
r2 emssr113
    except RuntimeError:
r2 emssr114
        raise CellError("block を %d 回辿っても終わりません。Ω の「もう良いか」を見直す" % budget)
r2 emssr115
    except IndexError:
r2 emssr116
        raise CellError("語番号が作業領域を越えた(語数 %d)。セルの words を増やす" % words)
r2 emssr117
    return rep, lane
r2 emssr118
r2 emssr119
r2 emssr120
def cell(source, dic=None, seeds=None, words=DEFAULT_WORDS, budget=DEFAULT_BUDGET):
r2 emssr121
    """セル一つ。compile して実行し、scope を返す"""
r2 emssr122
    image, need, cm = compile_text(source, dic)
r2 emssr123
    rep, lane = run_image(image, seeds, words, budget)
r2 emssr124
r2 emssr125
    table = scope.lexicon(lex.entries(_DIC)) if (dic and dic.strip()) else {}
r2 emssr126
    a = scope.arrays(cm)
r2 emssr127
    ws = scope.words(a, table)
r2 emssr128
    bx = scope.boxes(a, lane.M)
r2 emssr129
    gm = scope.gamma(a)
r2 emssr130
    depth = {g["word"]: a["opd"][g["box"]] for g in gm}
r2 emssr131
    return {
r2 emssr132
        "ok": True,
r2 emssr133
        "report": rep,
r2 emssr134
        "source": source,
r2 emssr135
        "size": {"syl": a["nSyl"] - a["body"], "box": a["nBox"],
r2 emssr136
                 "need": need, "image": len(image)},
r2 emssr137
        # 二つの平文が同じチェンバーかを見る hash。image が一致すれば読みも一致する
r2 emssr138
        "mark": hashlib.sha256(image).hexdigest()[:16],
r2 emssr139
        "words": ws,
r2 emssr140
        "frontier": scope.frontier(ws),
r2 emssr141
        "boxes": bx,
r2 emssr142
        "align": scope.align(ws, a),
r2 emssr143
        "gamma": [dict(g, **scope.dyad(lane.M[g["word"]], depth[g["word"]]))
r2 emssr144
                  for g in gm],
r2 emssr145
        "image": scope.image(vcpu, image),
r2 emssr146
        "out": [lane.M[k] for k in range(64)],
r2 emssr147
        "put": bytes(lane.OUT).decode("latin-1"),
r2 emssr148
    }
r2 emssr149
r2 emssr150
r2 emssr151
# ===== 接合秩(Common-Lang/1 定理 4.1)=====
r2 emssr152
#
r2 emssr153
#   F[α][β] = f(α, β),   ρ = rank_F2 F,   f = ⊕_{r<ρ} p_r(x_A) q_r(x_B)
r2 emssr154
#
r2 emssr155
# 階数は common_apps/joinrank の平文が持つ。ここは頂点値の表を作って渡すだけになる。
r2 emssr156
# 表は同じ image を 2^n 回実行して集める。lane の作業領域は交わらない(20 式 1.2)ので、
r2 emssr157
# 実行は互いに独立になる。
r2 emssr158
r2 emssr159
def vertices(image, inputs, words=DEFAULT_WORDS, budget=DEFAULT_BUDGET, out=0):
r2 emssr160
    """入力の語へ {0,1}^n を全部当て、語 out の値を頂点値として並べる"""
r2 emssr161
    n = len(inputs)
r2 emssr162
    if n > 12:
r2 emssr163
        raise CellError("入口が %d 本。頂点は 2^n なので 12 本までにする" % n)
r2 emssr164
    tab = vcpu.translate(vcpu.decode(image))[1]
r2 emssr165
    tbl = []
r2 emssr166
    for v in range(1 << n):
r2 emssr167
        lane = vcpu.Lane(words)
r2 emssr168
        for k, w in enumerate(inputs):
r2 emssr169
            lane.M[int(w)] = (v >> k) & 1
r2 emssr170
        vcpu.run_lane(tab, lane, budget=budget)
r2 emssr171
        tbl.append(1 if lane.M[out] else 0)
r2 emssr172
    return tbl