The Principia Machina/TPC-PY/tpc record.py: Difference between revisions
Appearance
TPC-PY source tpc_record.py (0.1.0), SHA-256 463ef77c1d8ade48; conformed TPS 0.4.0; PROPOSED, not declared (TPC-PY 0.2.0) |
m 1 revision imported |
(No difference)
| |
Latest revision as of 02:11, 30 September 2026
# -*- coding: utf-8 -*-
"""
tpc_record 0.1.0 (proposed, increment 12): the Research Register's records
=========================================================================
TPC-PY residence. The closed table of tpm_record_schema 0.4.0 (section 7,
embedded below as its text and read by the table's own line grammar, 7.1)
and the functions of tpm_record_functions 0.3.0 that tpc_record carries.
Both texts are the specification; this module states them as code
(D192.2-D202.1 as the user's selections of 2026-09-28 fix them, read under
RD-PT-10 to RD-PT-16; D214.1, D215.2 and D218.1 as the user's answers of
2026-09-29 fix them, tpm_post_tpcpy_selections 0.4.0). No rule rests on a
drafted item and none carries a mark (rulings S105, S114).
D214.1: STATUS UNMARKED at landing, one closed set UNMARKED, HELD or
REFUTED, on the classes an account authors (schema 6.4-6.8).
D215.2: INHERITS (RID, TPX-written, inside the digest) on a record a
COPY-FORWARD writes; every inherited record lands at its class's landing
grade (REC-32 whatever INHERITS; REC-40 retired, never raised); the copy
keeps a predecessor's REFUTED or WITHDRAWN grade by a WITHDRAWAL of cause
INHERITED (functions 6.2, 6.3); schema_json's GRADE the landing grade alone.
D218.1: a RESULT's U_C, whose table line carries PREC, enters the record
preimage in its precision form (prec_decimal, functions 7.3), and
budget_check reads that form, so U_C is checked at its last written digit
and the record digest keeps that digit.
THE TABLE'S FUNCTIONS (tpm_record_schema 0.4.0)
record_check(record, tfk, setting, stage) -> ["OK"] | ["REFUSED", problems]
record_preimage(record) -> ["OK", fields] | ["REFUSED", problems]
record_digest(record) -> ["OK", hex] | ["REFUSED", problems]
record_view(record, view, grade) -> ["OK", shape] | ["REFUSED", problems]
schema_json() -> ["OK", text] (non-normative; RD-PT-15)
problems: [[code, field, message], ...] sorted in code point order, codes
REC-01 to REC-40 (schema 12) and REC-50 to REC-97 (functions 17).
run_case(fn, args) renders a result in TPM's canonical JSON (schema 11.4):
keys sorted in code point order, no white space, only the quotation mark,
the backslash and U+0000-U+001F escaped (lowercase, as u00xx after a
backslash), every other
character as itself; the corpora record_corpus, record_view_corpus,
conditions_corpus (tpc_strike's new functions) and settlement_corpus
(tpc_eom's) are answered here.
Standard library only; Python 3.8 or later. Imports tpc_digest, tpc_zone,
tpc_units, tpc_form, tpc_tests, tpc_strike (and tpc_eom for run_case);
none of those imports tpc_record (RD-RS-33).
"""
import unicodedata
import tpc_digest as G
import tpc_form as F
import tpc_strike as S
import tpc_tests as TT
import tpc_units as U
import tpc_zone as Z
TPC_RECORD_VERSION = "0.1.0" # proposed: new module (increment 12), unreleased until H-DECL declares TPC-PY 0.2.0
VERSION = TPC_RECORD_VERSION
SCHEMA_TEXT_VERSION = "0.4.0" # tpm_record_schema (0.2.1: schema_json's GRADE enumeration, S100; 0.3.0: U_C's PREC; 0.4.0: D215.2, the LANDGRADE alone)
# ---- the table: tpm_record_schema 0.4.0 section 7.5 (0.3.0's line for line; 0.2.1's but U_C's PREC), BEGIN to END RECORD TABLE, verbatim
TABLE_TEXT = """BEGIN RECORD TABLE
ENUM CLASS = RESULT | ASSUMPTION | CORROBORATION | VERIFICATION
| WITHDRAWAL | NAMED_GAP | ENTRY_STATUS | PROMOTION | MASTER | HOLD
| RELEASE
ENUM GRADE_VALUES = PROVISIONAL | ESTABLISHED | REFUTED | WITHDRAWN
| NOT GRADED
ENUM REGIME = L | M
ENUM REGIME_M = M
ENUM FALSIFIER_CLASS = F1 | F2 | F3
ENUM SITUATION = INITIAL | BOUNDARY
ENUM OUTCOME = MATCH | MATCH WITH DIFFERENCE NOTED
ENUM TARGET_STATUS = PROVISIONAL | ESTABLISHED | HELD
ENUM EVALUATION = A | B
ENUM ORIGIN = SAME BLOCK | REPRODUCTION BLOCK
ENUM GAP_SOURCE = IC2 | UNDER | SR5_LOCUS | SR7_ASSUMPTIONS | SR7_DOMAIN
ENUM ROLE_WORDS = CONSTRAINT | CONSTITUTIVE | INVARIANT | DEFINITION
CLASS RESULT KEYED PUSH
CLASS ASSUMPTION KEYED PUSH
CLASS CORROBORATION KEYED PUSH
CLASS VERIFICATION KEYED PUSH
CLASS WITHDRAWAL LIST TPX
CLASS NAMED_GAP KEYED TPX
CLASS ENTRY_STATUS KEYED PUSH
CLASS PROMOTION LIST TPX
CLASS MASTER KEYED TPX
CLASS HOLD LIST TPX
CLASS RELEASE LIST TPX
LANDGRADE RESULT = PROVISIONAL
LANDGRADE ASSUMPTION = PROVISIONAL
LANDGRADE CORROBORATION = PROVISIONAL
LANDGRADE VERIFICATION = PROVISIONAL
LANDGRADE WITHDRAWAL = NOT GRADED
LANDGRADE NAMED_GAP = NOT GRADED
LANDGRADE ENTRY_STATUS = NOT GRADED
LANDGRADE PROMOTION = NOT GRADED
LANDGRADE MASTER = NOT GRADED
LANDGRADE HOLD = NOT GRADED
LANDGRADE RELEASE = NOT GRADED
GRADES RESULT = PROVISIONAL | ESTABLISHED | REFUTED | WITHDRAWN
GRADES ASSUMPTION = PROVISIONAL | WITHDRAWN
GRADES CORROBORATION = PROVISIONAL | REFUTED | WITHDRAWN
GRADES VERIFICATION = PROVISIONAL | WITHDRAWN
GRADES WITHDRAWAL = NOT GRADED
GRADES NAMED_GAP = NOT GRADED
GRADES ENTRY_STATUS = NOT GRADED
GRADES PROMOTION = NOT GRADED
GRADES MASTER = NOT GRADED
GRADES HOLD = NOT GRADED
GRADES RELEASE = NOT GRADED
STATUSES RESULT = UNMARKED | HELD | REFUTED
STATUSES ASSUMPTION = UNMARKED | HELD
STATUSES CORROBORATION = UNMARKED | HELD | REFUTED
STATUSES VERIFICATION = UNMARKED | HELD
STATUSES ENTRY_STATUS = UNMARKED | HELD
SUB DERIVATION KEY -
SUB COMPONENT KEY NAME
SUB POINT KEY DOF
SUB INDEPENDENCE KEY -
SUB EXTERNAL KEY -
SUB DIFFERENCE KEY -
FIELD RESULT ID RID ONE R LEAD D - -
FIELD RESULT CLASS ENUM ONE R AUTHOR D - ENUM=CLASS
FIELD RESULT GRADE ENUM ONE R TPX D - GRADE
FIELD RESULT STATUS ENUM ONE R TPX X - STATUS
FIELD RESULT BLOCK TEXT ONE R TPX D - NOTBLANK
FIELD RESULT AUTHOR TEXT ONE R TPX D - NOTBLANK
FIELD RESULT LANDED_BY TEXT ONE R TPX D - NOTBLANK
FIELD RESULT LANDED TEXT ONE R TPX D - UTC
FIELD RESULT VETTED_IN TEXT ONE O TPX D - NOTBLANK
FIELD RESULT CITES IDSET SET R AUTHOR D - RID
FIELD RESULT ASSUMPTIONS IDSET SET R AUTHOR D - RID
FIELD RESULT LITERATURE IDSET SET O AUTHOR D - BIBID,SETTING
FIELD RESULT SUPERSEDES RID ONE O AUTHOR D - -
FIELD RESULT INHERITS RID ONE O TPX D - -
FIELD RESULT TPS TEXT ONE R TPX D - VERSION
FIELD RESULT D_IS TEXT ONE R TPX D - HEX64
FIELD RESULT STATEMENT TEXT ONE R AUTHOR D - NOTBLANK
FIELD RESULT REGIME ENUM ONE R AUTHOR D - ENUM=REGIME
FIELD RESULT DERIVATION OBJECT ONE R AUTHOR D - SUB=DERIVATION
FIELD RESULT FALSIFIER TEXT ONE R AUTHOR D - NOTBLANK
FIELD RESULT FALSIFIER_CLASS ENUM ONE R AUTHOR D - ENUM=FALSIFIER_CLASS
FIELD RESULT DOMAIN TEXT ONE O AUTHOR D - NOTBLANK
FIELD RESULT DOMAIN_CONDITION PREDICATE ONE O AUTHOR D - -
FIELD RESULT REFS IDENT SET C AUTHOR D - TPKNAME
FIELD RESULT EQUATION FORM ONE C AUTHOR D - -
FIELD RESULT ROLE ENUM ONE C AUTHOR D - ROLE
FIELD RESULT ENTRIES IDENT SET O AUTHOR D - TRMENTRY
FIELD RESULT QUANTITY IDENT ONE C AUTHOR D - TRMENTRY
FIELD RESULT VALUE DECIMAL ONE C AUTHOR D - -
FIELD RESULT UNIT UNIT ONE C AUTHOR D - DIM=QUANTITY
FIELD RESULT U_C DECIMAL ONE C AUTHOR D - POSITIVE,PREC
FIELD RESULT BUDGET OBJECT LIST C AUTHOR D - SUB=COMPONENT,NONEMPTY
FIELD RESULT CORRELATED BOOL ONE C AUTHOR D FALSE -
FIELD RESULT AT OBJECT LIST C AUTHOR D EMPTY SUB=POINT
FIELD RESULT DIGEST TEXT ONE O TPX X - HEX64,DIGEST
RULE RESULT | IF REGIME = M | REQUIRE | QUANTITY, VALUE, UNIT, U_C, BUDGET
RULE RESULT | IF REGIME = M | FORBID | EQUATION, ROLE
RULE RESULT | IF REGIME = L | FORBID | U_C, BUDGET
RULE RESULT | IF REGIME = L | ALL OR NONE | QUANTITY, VALUE, UNIT
RULE RESULT | IF BUDGET ABSENT | FORBID | CORRELATED
RULE RESULT | IF VALUE ABSENT | FORBID | AT
RULE RESULT | IF ROLE PRESENT | REQUIRE | EQUATION
RULE RESULT | IF EQUATION PRESENT | REQUIRE | REFS
RULE RESULT | IF DOMAIN_CONDITION PRESENT | REQUIRE | REFS
FIELD ASSUMPTION ID RID ONE R LEAD D - -
FIELD ASSUMPTION CLASS ENUM ONE R AUTHOR D - ENUM=CLASS
FIELD ASSUMPTION GRADE ENUM ONE R TPX D - GRADE
FIELD ASSUMPTION STATUS ENUM ONE R TPX X - STATUS
FIELD ASSUMPTION BLOCK TEXT ONE R TPX D - NOTBLANK
FIELD ASSUMPTION AUTHOR TEXT ONE R TPX D - NOTBLANK
FIELD ASSUMPTION LANDED_BY TEXT ONE R TPX D - NOTBLANK
FIELD ASSUMPTION LANDED TEXT ONE R TPX D - UTC
FIELD ASSUMPTION VETTED_IN TEXT ONE O TPX D - NOTBLANK
FIELD ASSUMPTION CITES IDSET SET R AUTHOR D - RID
FIELD ASSUMPTION ASSUMPTIONS IDSET SET R AUTHOR D - RID
FIELD ASSUMPTION LITERATURE IDSET SET O AUTHOR D - BIBID,SETTING
FIELD ASSUMPTION SUPERSEDES RID ONE O AUTHOR D - -
FIELD ASSUMPTION INHERITS RID ONE O TPX D - -
FIELD ASSUMPTION TPS TEXT ONE R TPX D - VERSION
FIELD ASSUMPTION D_IS TEXT ONE R TPX D - HEX64
FIELD ASSUMPTION PROPOSITION TEXT ONE R AUTHOR D - NOTBLANK,T2
FIELD ASSUMPTION REFS IDENT SET R AUTHOR D - TPKNAME
FIELD ASSUMPTION SITUATION ENUM ONE O AUTHOR D - ENUM=SITUATION
FIELD ASSUMPTION DOF IDENT ONE C AUTHOR D - TPKDOF
FIELD ASSUMPTION VALUE DECIMAL ONE C AUTHOR D - -
FIELD ASSUMPTION UNIT UNIT ONE C AUTHOR D - DIM=DOF
FIELD ASSUMPTION AT OBJECT LIST C AUTHOR D EMPTY SUB=POINT
FIELD ASSUMPTION EQUATION FORM ONE C AUTHOR D - -
FIELD ASSUMPTION ROLE ENUM ONE C AUTHOR D - ROLE
FIELD ASSUMPTION DIGEST TEXT ONE O TPX X - HEX64,DIGEST
RULE ASSUMPTION | IF SITUATION PRESENT | REQUIRE | DOF, VALUE, UNIT
RULE ASSUMPTION | IF SITUATION ABSENT | FORBID | DOF, VALUE, UNIT, AT
RULE ASSUMPTION | IF SITUATION PRESENT | FORBID | EQUATION, ROLE
RULE ASSUMPTION | IF ROLE PRESENT | REQUIRE | EQUATION
FIELD CORROBORATION ID RID ONE R LEAD D - -
FIELD CORROBORATION CLASS ENUM ONE R AUTHOR D - ENUM=CLASS
FIELD CORROBORATION GRADE ENUM ONE R TPX D - GRADE
FIELD CORROBORATION STATUS ENUM ONE R TPX X - STATUS
FIELD CORROBORATION BLOCK TEXT ONE R TPX D - NOTBLANK
FIELD CORROBORATION AUTHOR TEXT ONE R TPX D - NOTBLANK
FIELD CORROBORATION LANDED_BY TEXT ONE R TPX D - NOTBLANK
FIELD CORROBORATION LANDED TEXT ONE R TPX D - UTC
FIELD CORROBORATION VETTED_IN TEXT ONE O TPX D - NOTBLANK
FIELD CORROBORATION CITES IDSET SET R AUTHOR D - RID
FIELD CORROBORATION ASSUMPTIONS IDSET SET R AUTHOR D - RID
FIELD CORROBORATION LITERATURE IDSET SET O AUTHOR D - BIBID,SETTING
FIELD CORROBORATION SUPERSEDES RID ONE O AUTHOR D - -
FIELD CORROBORATION INHERITS RID ONE O TPX D - -
FIELD CORROBORATION TPS TEXT ONE R TPX D - VERSION
FIELD CORROBORATION D_IS TEXT ONE R TPX D - HEX64
FIELD CORROBORATION TARGET RID ONE R AUTHOR D - -
FIELD CORROBORATION OUTCOME ENUM ONE R TPX D - ENUM=OUTCOME
FIELD CORROBORATION DIFFERENCE TEXT ONE C TPX D - NOTBLANK
FIELD CORROBORATION INDEPENDENCE OBJECT ONE R TPX D - SUB=INDEPENDENCE
FIELD CORROBORATION TARGET_STATUS ENUM ONE R TPX D - ENUM=TARGET_STATUS
FIELD CORROBORATION SELF_REPORTED TEXT ONE O AUTHOR D - NOTBLANK
FIELD CORROBORATION DIGEST TEXT ONE O TPX X - HEX64,DIGEST
RULE CORROBORATION | IF OUTCOME = MATCH WITH DIFFERENCE NOTED
| REQUIRE | DIFFERENCE
RULE CORROBORATION | IF OUTCOME = MATCH | FORBID | DIFFERENCE
FIELD VERIFICATION ID RID ONE R LEAD D - -
FIELD VERIFICATION CLASS ENUM ONE R AUTHOR D - ENUM=CLASS
FIELD VERIFICATION GRADE ENUM ONE R TPX D - GRADE
FIELD VERIFICATION STATUS ENUM ONE R TPX X - STATUS
FIELD VERIFICATION BLOCK TEXT ONE R TPX D - NOTBLANK
FIELD VERIFICATION AUTHOR TEXT ONE R TPX D - NOTBLANK
FIELD VERIFICATION LANDED_BY TEXT ONE R TPX D - NOTBLANK
FIELD VERIFICATION LANDED TEXT ONE R TPX D - UTC
FIELD VERIFICATION VETTED_IN TEXT ONE O TPX D - NOTBLANK
FIELD VERIFICATION CITES IDSET SET R AUTHOR D - RID
FIELD VERIFICATION ASSUMPTIONS IDSET SET R AUTHOR D - RID
FIELD VERIFICATION LITERATURE IDSET SET O AUTHOR D - BIBID,SETTING
FIELD VERIFICATION SUPERSEDES RID ONE O AUTHOR D - -
FIELD VERIFICATION INHERITS RID ONE O TPX D - -
FIELD VERIFICATION TPS TEXT ONE R TPX D - VERSION
FIELD VERIFICATION D_IS TEXT ONE R TPX D - HEX64
FIELD VERIFICATION REGIME ENUM ONE R AUTHOR D - ENUM=REGIME_M
FIELD VERIFICATION TARGET RID ONE R AUTHOR D - -
FIELD VERIFICATION EXTERNAL OBJECT ONE R AUTHOR D - SUB=EXTERNAL
FIELD VERIFICATION SOURCE TEXT ONE R AUTHOR D - NOTBLANK,BIBSOURCE
FIELD VERIFICATION AT OBJECT LIST O AUTHOR D EMPTY SUB=POINT
FIELD VERIFICATION DIFFERENCE OBJECT ONE R AUTHOR D - SUB=DIFFERENCE
FIELD VERIFICATION DIGEST TEXT ONE O TPX X - HEX64,DIGEST
FIELD NAMED_GAP ID RID ONE R TPX D - -
FIELD NAMED_GAP CLASS ENUM ONE R TPX D - ENUM=CLASS
FIELD NAMED_GAP GRADE ENUM ONE R TPX D - GRADE
FIELD NAMED_GAP LANDED_BY TEXT ONE R TPX D - TPXONLY
FIELD NAMED_GAP LANDED TEXT ONE R TPX D - UTC
FIELD NAMED_GAP TPS TEXT ONE R TPX D - VERSION
FIELD NAMED_GAP INHERITS RID ONE O TPX D - -
FIELD NAMED_GAP SOURCE ENUM ONE R TPX D - ENUM=GAP_SOURCE
FIELD NAMED_GAP AGAINST TEXT SET R TPX D - AGAINST
FIELD NAMED_GAP TRIGGER TEXT ONE R TPX D - NOTBLANK,TRIGGER
FIELD NAMED_GAP STATEMENT TEXT ONE R TPX D - NOTBLANK
FIELD NAMED_GAP STATE TEXT ONE R TPX X - STATELINE
FIELD NAMED_GAP DIGEST TEXT ONE O TPX X - HEX64,DIGEST
FIELD ENTRY_STATUS ID RID ONE R LEAD D - -
FIELD ENTRY_STATUS CLASS ENUM ONE R AUTHOR D - ENUM=CLASS
FIELD ENTRY_STATUS GRADE ENUM ONE R TPX D - GRADE
FIELD ENTRY_STATUS STATUS ENUM ONE R TPX X - STATUS
FIELD ENTRY_STATUS BLOCK TEXT ONE R TPX D - NOTBLANK
FIELD ENTRY_STATUS AUTHOR TEXT ONE R TPX D - NOTBLANK
FIELD ENTRY_STATUS LANDED_BY TEXT ONE R TPX D - NOTBLANK
FIELD ENTRY_STATUS LANDED TEXT ONE R TPX D - UTC
FIELD ENTRY_STATUS VETTED_IN TEXT ONE O TPX D - NOTBLANK
FIELD ENTRY_STATUS CITES IDSET SET R AUTHOR D - RID
FIELD ENTRY_STATUS ASSUMPTIONS IDSET SET R AUTHOR D - RID
FIELD ENTRY_STATUS LITERATURE IDSET SET O AUTHOR D - BIBID,SETTING
FIELD ENTRY_STATUS INHERITS RID ONE O TPX D - -
FIELD ENTRY_STATUS TPS TEXT ONE R TPX D - VERSION
FIELD ENTRY_STATUS D_IS TEXT ONE R TPX D - HEX64
FIELD ENTRY_STATUS ENTRY IDENT ONE R AUTHOR D - TRMENTRY
FIELD ENTRY_STATUS RIDER TEXT ONE O AUTHOR D - NOTBLANK
FIELD ENTRY_STATUS NOTE TEXT ONE O AUTHOR D - NOTBLANK
FIELD ENTRY_STATUS SCRUTINY TEXT ONE O AUTHOR D - NOTBLANK
FIELD ENTRY_STATUS CHECKS_OWED TEXT ONE O AUTHOR D - NOTBLANK
FIELD ENTRY_STATUS DIGEST TEXT ONE O TPX X - HEX64,DIGEST
RULE ENTRY_STATUS | ALWAYS | AT LEAST ONE | RIDER, NOTE, SCRUTINY, CHECKS_OWED
FIELD MASTER ID RID ONE R TPX D - MASTERID
FIELD MASTER CLASS ENUM ONE R TPX D - ENUM=CLASS
FIELD MASTER GRADE ENUM ONE R TPX D - GRADE
FIELD MASTER LANDED_BY TEXT ONE R TPX D - TPXONLY
FIELD MASTER LANDED TEXT ONE R TPX D - UTC
FIELD MASTER TPS TEXT ONE R TPX D - VERSION
FIELD MASTER UUID TEXT ONE R TPX D - UUID4
FIELD MASTER PREFIX IDENT ONE R TPX D - -
FIELD MASTER D_TPK TEXT ONE R TPX D - HEX64,ZONE=TPK
FIELD MASTER D_TRM TEXT ONE R TPX D - HEX64,ZONE=TrM
FIELD MASTER D_TMS TEXT ONE R TPX D - HEX64,ZONE=TMS
FIELD MASTER D_TFK TEXT ONE R TPX D - HEX64,DTFK
FIELD MASTER CERTIFICATE TEXT ONE R TPX D - NOTBLANK
FIELD MASTER STRUCTURE_LINE TEXT ONE R TPX D - NOTBLANK
FIELD MASTER MODULES_USED TEXT ONE R TPX D - NOTBLANK
FIELD MASTER DIGEST TEXT ONE O TPX X - HEX64,DIGEST
FIELD SUB:DERIVATION STEPS INT ONE R AUTHOR D - STEPS
FIELD SUB:DERIVATION TEXT TEXT ONE R AUTHOR D - NOTBLANK
FIELD SUB:COMPONENT NAME IDENT ONE R AUTHOR D - KEY
FIELD SUB:COMPONENT U DECIMAL ONE R AUTHOR D - NONNEG
FIELD SUB:COMPONENT UNIT UNIT ONE R AUTHOR D - -
FIELD SUB:COMPONENT EVALUATION ENUM ONE R AUTHOR D - ENUM=EVALUATION
FIELD SUB:COMPONENT BASIS TEXT ONE R AUTHOR D - NOTBLANK
FIELD SUB:POINT DOF IDENT ONE R AUTHOR D - KEY,TPKDOF
FIELD SUB:POINT VALUE DECIMAL ONE R AUTHOR D - -
FIELD SUB:POINT UNIT UNIT ONE R AUTHOR D - DIM=DOF
FIELD SUB:INDEPENDENCE ACCOUNT TEXT ONE R TPX D - NOTBLANK
FIELD SUB:INDEPENDENCE SNAPSHOT TEXT ONE R TPX D - NOTBLANK
FIELD SUB:INDEPENDENCE VENDOR TEXT ONE R TPX D - NOTBLANK
FIELD SUB:INDEPENDENCE IMPLEMENTATION TEXT ONE R TPX D - NOTBLANK
FIELD SUB:INDEPENDENCE ORIGIN ENUM ONE R TPX D - ENUM=ORIGIN
FIELD SUB:INDEPENDENCE NOT_SERVED_BEFORE BOOL ONE R TPX D - -
FIELD SUB:INDEPENDENCE SAME_SPONSOR BOOL ONE R TPX D - -
FIELD SUB:INDEPENDENCE SAME_MODEL BOOL ONE R TPX D - -
FIELD SUB:EXTERNAL VALUE DECIMAL ONE R AUTHOR D - -
FIELD SUB:EXTERNAL U_C DECIMAL ONE R AUTHOR D - NONNEG
FIELD SUB:EXTERNAL UNIT UNIT ONE R AUTHOR D - DIM=QUANTITY
FIELD SUB:EXTERNAL QUANTITY IDENT ONE R AUTHOR D - TRMENTRY
FIELD SUB:DIFFERENCE VALUE DECIMAL ONE R AUTHOR D - -
FIELD SUB:DIFFERENCE U DECIMAL ONE R AUTHOR D - POSITIVE
FIELD SUB:DIFFERENCE ZETA DECIMAL ONE R AUTHOR D - -
END RECORD TABLE
"""
def _parse_table(text):
"""The table's line grammar (schema 7.1): continuation lines joined; the
ENUM, CLASS, LANDGRADE, GRADES, STATUSES, SUB, FIELD and RULE lines read."""
lines = text.split("\n")
i0 = lines.index("BEGIN RECORD TABLE")
i1 = lines.index("END RECORD TABLE")
joined = []
for ln in lines[i0 + 1:i1]:
if ln.startswith(" "):
joined[-1] = joined[-1] + " " + ln.strip()
elif ln.strip() != "":
joined.append(ln)
T = {"enums": {}, "classes": [], "form": {}, "writer": {}, "landgrade": {},
"grades": {}, "statuses": {}, "subs": [], "subkey": {}, "fields": {}, "rules": {}}
for ln in joined:
kw = ln.split(" ", 1)[0]
if kw == "ENUM":
name, vals = ln[len("ENUM "):].split(" = ", 1)
T["enums"][name] = vals.split(" | ")
elif kw == "CLASS":
_, name, form, writer = ln.split(" ")
T["classes"].append(name)
T["form"][name] = form
T["writer"][name] = writer
elif kw in ("LANDGRADE", "GRADES", "STATUSES"):
name, vals = ln[len(kw) + 1:].split(" = ", 1)
if kw == "LANDGRADE":
T["landgrade"][name] = vals
else:
T[kw.lower()][name] = vals.split(" | ")
elif kw == "SUB":
_, name, _key, key = ln.split(" ")
T["subs"].append(name)
T["subkey"][name] = None if key == "-" else key
elif kw == "FIELD":
p = ln.split(" ")
if len(p) != 10:
raise ValueError("table: FIELD line of %d words" % len(p))
T["fields"].setdefault(p[1], []).append(
{"name": p[2], "type": p[3], "card": p[4], "req": p[5], "writer": p[6],
"dig": p[7], "default": p[8], "cons": [] if p[9] == "-" else p[9].split(",")})
elif kw == "RULE":
parts = ln.split(" | ")
cls = parts[0].split(" ")[1]
cond = parts[1]
if cond == "ALWAYS":
c = None
elif cond.endswith(" PRESENT") or cond.endswith(" ABSENT"):
w = cond.split(" ")
c = {"field": w[1], "op": w[2]}
else:
f, v = cond[len("IF "):].split(" = ", 1)
c = {"field": f, "op": "=", "value": v}
T["rules"].setdefault(cls, []).append(
{"if": c, "action": parts[2], "fields": parts[3].split(", ")})
else:
raise ValueError("table: unknown line")
return T
T = _parse_table(TABLE_TEXT)
KEYED = [c for c in T["classes"] if T["form"][c] == "KEYED"]
VIEWS = ["HOLD", "STRIKE", "SUITE", "PROBE_L", "PROBE_M", "PROBE_LOAD", "LITERATURE", "EOM"]
GRADE_VALUES = T["enums"]["GRADE_VALUES"]
STATUS_WORDS = ["PROVISIONAL", "ESTABLISHED", "REFUTED", "WITHDRAWN", "HELD", "NOT GRADED"]
STATUS_VALUES = ["UNMARKED", "HELD", "REFUTED"]
# ---- small helpers --------------------------------------------------------------------
def _fullmatch_rid(s):
"""[A-Za-z_][A-Za-z0-9_]*-[1-9][0-9]* over the whole string (\\Z: a final line
feed never matches)."""
return _RID.match(s) is not None
import re # noqa: E402
_RID = re.compile(r"[A-Za-z_][A-Za-z0-9_]*-[1-9][0-9]*\Z")
_BIB = re.compile(r"[A-Za-z_][A-Za-z0-9_]*-BIB[1-9][0-9]*\Z")
_IDENT = re.compile(r"[A-Za-z_][A-Za-z0-9_]*\Z")
_HEX64 = re.compile(r"[0-9a-f]{64}\Z")
_UUID4 = re.compile(r"[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}\Z")
_VERSION = re.compile(r"(0|[1-9][0-9]{0,3})\.(0|[1-9][0-9]{0,3})\.(0|[1-9][0-9]{0,3})\Z")
_UTC = re.compile(r"([0-9]{4})-([0-9]{2})-([0-9]{2})T([0-9]{2}):([0-9]{2}):([0-9]{2})Z\Z")
_DATE = re.compile(r"([0-9]{4})-([0-9]{2})-([0-9]{2})\Z")
_EOM = re.compile(r"EOM\(([A-Za-z_][A-Za-z0-9_]*)\)\Z")
_SETTLED = re.compile(r"SETTLED ([0-9]{4}-[0-9]{2}-[0-9]{2}) BY (.+)\Z", re.S)
_STANDING = re.compile(r"STANDING ([0-9]{4}-[0-9]{2}-[0-9]{2}) BY (.+)\Z", re.S)
def _m(rx, s):
return type(s) is str and rx.match(s) is not None
def _is_int(v):
return type(v) is int
def _shown_char(c):
return unicodedata.category(c)[0] == "C" or c in ("
", "
")
def _show(v):
"""A slot value in a message (schema 12.1): every code point of general
category C, and U+2028 and U+2029, written <U+XXXX>, TAB and LF included."""
return "".join("<U+%04X>" % ord(c) if _shown_char(c) else c for c in v)
def cj(v):
"""TPM canonical JSON (schema 11.4). A float, which no result of this module
holds, is written by tpc_strike canon so the writer never fails."""
if v is True:
return "true"
if v is False:
return "false"
if v is None:
return "null"
if type(v) is int:
return str(v)
if type(v) is float:
return S.canon(v)
if type(v) is str:
out = ['"']
for ch in v:
if ch == '"':
out.append('\\"')
elif ch == "\\":
out.append("\\\\")
elif ord(ch) < 0x20:
out.append("\\u%04x" % ord(ch))
else:
out.append(ch)
out.append('"')
return "".join(out)
if type(v) in (list, tuple):
return "[" + ",".join(cj(x) for x in v) + "]"
if type(v) is dict:
return "{" + ",".join(cj(k) + ":" + cj(v[k]) for k in sorted(v)) + "}"
raise TypeError("cj: unsupported type")
def _or_list(xs):
return xs[0] if len(xs) == 1 else ", ".join(xs[:-1]) + " or " + xs[-1]
def _and_list(xs):
return xs[0] if len(xs) == 1 else ", ".join(xs[:-1]) + " and " + xs[-1]
def _date_ok(y, mo, d):
y, mo, d = int(y), int(mo), int(d)
if mo < 1 or mo > 12 or d < 1:
return False
leap = (y % 4 == 0 and y % 100 != 0) or y % 400 == 0
days = [31, 29 if leap else 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
return d <= days[mo - 1]
def _utc_ok(s):
m = _UTC.match(s)
if not m:
return False
y, mo, d, h, mi, se = m.groups()
return _date_ok(y, mo, d) and int(h) <= 23 and int(mi) <= 59 and int(se) <= 59
def _day_ok(s):
m = _DATE.match(s) if type(s) is str else None
return m is not None and _date_ok(*m.groups())
def _stateline_ok(s):
if s == "OPEN":
return True
m = _SETTLED.match(s)
if m:
return _day_ok(m.group(1)) and all(_m(_RID, x) for x in m.group(2).split(", "))
m = _STANDING.match(s)
if m:
return _day_ok(m.group(1)) and not Z._blank(m.group(2))
return False
def _problems(p):
"""Sorted by code, field, message in code point order; no duplicate triple."""
return [list(x) for x in sorted(set(tuple(x) for x in p))]
def _refused(p):
return ["REFUSED", _problems(p)]
def _one(code, field, msg):
return ["REFUSED", [[code, field, msg]]]
TYPE_PHRASE = {("RID", "ONE"): "a record identifier", ("IDSET", "SET"): "a set of record identifiers",
("IDENT", "ONE"): "an identifier", ("IDENT", "SET"): "a set of identifiers",
("TEXT", "ONE"): "a text", ("TEXT", "SET"): "a set of texts",
("DECIMAL", "ONE"): "a decimal string", ("UNIT", "ONE"): "a unit string",
("DIMENSION", "ONE"): "a dimension string", ("FORM", "ONE"): "a form string",
("PREDICATE", "ONE"): "a predicate string", ("ENUM", "ONE"): "a string",
("INT", "ONE"): "an integer", ("BOOL", "ONE"): "a boolean",
("OBJECT", "ONE"): "an object", ("OBJECT", "LIST"): "a list of objects"}
FORM_PHRASE = {"RID": "a record identifier", "BIBID": "a BIB ID", "IDENT": "an identifier",
"HEX64": "64 lowercase hexadecimal characters", "UUID4": "a version-4 UUID",
"VERSION": "a version n.n.n", "UTC": "a UTC time YYYY-MM-DDTHH:MM:SSZ",
"STEPS": "an integer from 1 to 1000000", "TPXONLY": "TPX",
"STATELINE": "a STATE line",
"ROLE": "EOM(<DOF>), CONSTRAINT, CONSTITUTIVE, INVARIANT or DEFINITION",
"DIMENSION": "a dimension"}
TEXT_FORMS = [("HEX64", lambda s: _m(_HEX64, s)), ("UUID4", lambda s: _m(_UUID4, s)),
("VERSION", lambda s: _m(_VERSION, s)), ("UTC", _utc_ok),
("STATELINE", _stateline_ok), ("TPXONLY", lambda s: s == "TPX")]
def _cons_value(f, prefix):
for c in f["cons"]:
if c.startswith(prefix):
return c[len(prefix):]
return None
def _field(fields, name):
for f in fields:
if f["name"] == name:
return f
return None
# ---- record_check (schema 8) ---------------------------------------------------------
class _Ctx(object):
def __init__(self, cls, stage, tfk):
self.cls, self.stage, self.tfk = cls, stage, tfk
self.p = []
self.ok = set() # paths that passed S2
def add(self, code, field, msg):
self.p.append([code, field, msg])
def _check_value(ctx, f, v, path):
"""S2 (c) and (d) for one value; True when it passed."""
t, card, cons = f["type"], f["card"], f["cons"]
bad_type = False
if card == "SET":
bad_type = type(v) is not list or any(type(x) is not str for x in v)
elif t == "OBJECT" and card == "LIST":
bad_type = type(v) is not list or any(type(x) is not dict for x in v)
elif t == "OBJECT":
bad_type = type(v) is not dict
elif t == "INT":
bad_type = not _is_int(v)
elif t == "BOOL":
bad_type = type(v) is not bool
else:
bad_type = type(v) is not str
if bad_type:
ctx.add("REC-09", path, "%s is not %s" % (path, TYPE_PHRASE[(t, card)]))
return False
if card == "SET":
seen = set()
for x in v:
if t == "IDSET":
form = "BIBID" if "BIBID" in cons else "RID"
if not _m(_BIB if form == "BIBID" else _RID, x):
ctx.add("REC-19", path, "%s member %s is not %s" % (path, _show(x), FORM_PHRASE[form]))
return False
c = x
elif t == "IDENT":
if not _m(_IDENT, x):
ctx.add("REC-19", path, "%s member %s is not %s" % (path, _show(x), FORM_PHRASE["IDENT"]))
return False
c = x
else: # TEXT
b = Z._first_bad(x)
if b is not None:
ctx.add("REC-10", path, "%s holds the disallowed code point %s" % (path, b))
return False
if "NOTBLANK" in cons and Z._blank(x):
ctx.add("REC-11", path, "%s is blank" % path)
return False
c = Z._text(x)
if c in seen:
ctx.add("REC-13", path, "%s repeats %s" % (path, _show(c)))
return False
seen.add(c)
return True
if t == "OBJECT":
sub = _cons_value(f, "SUB=")
sfields = T["fields"]["SUB:" + sub]
if card == "ONE":
_check_object(ctx, sfields, v, path)
return True
if "NONEMPTY" in cons and len(v) == 0:
ctx.add("REC-35", path, "%s is empty" % path)
return False
key = T["subkey"][sub]
keys = []
for i, o in enumerate(v, 1):
_check_object(ctx, sfields, o, "%s[%d]" % (path, i))
if key is not None and ("%s[%d].%s" % (path, i, key)) in ctx.ok:
keys.append(_canon_scalar(_field(sfields, key), o[key]))
seen = set()
for kv in keys:
if kv in seen:
ctx.add("REC-13", path, "%s repeats %s" % (path, _show(kv)))
return False
seen.add(kv)
return True
if t in ("RID", "IDENT"):
if not _m(_RID if t == "RID" else _IDENT, v):
ctx.add("REC-19", path, "%s %s is not %s" % (path, _show(v), FORM_PHRASE[t]))
return False
return True
if t == "TEXT":
b = Z._first_bad(v)
if b is not None:
ctx.add("REC-10", path, "%s holds the disallowed code point %s" % (path, b))
return False
if "NOTBLANK" in cons and Z._blank(v):
ctx.add("REC-11", path, "%s is blank" % path)
return False
for form, test in TEXT_FORMS:
if form in cons and not test(v):
ctx.add("REC-19", path, "%s %s is not %s" % (path, _show(v), FORM_PHRASE[form]))
return False
return True
if t == "ENUM":
if "ROLE" in cons:
if v in T["enums"]["ROLE_WORDS"] or _m(_EOM, v):
return True
ctx.add("REC-19", path, "%s %s is not %s" % (path, _show(v), FORM_PHRASE["ROLE"]))
return False
if "GRADE" in cons:
if v not in GRADE_VALUES:
ctx.add("REC-12", path, "%s %s is not one of %s" % (path, _show(v), _or_list(GRADE_VALUES)))
return False
lg = T["landgrade"][ctx.cls] # D215.2: INHERITS or not (REC-40 retired)
if v != lg:
ctx.add("REC-32", path, "GRADE %s is not %s, the landing grade of %s"
% (_show(v), lg, ctx.cls))
return False
return True
vals = T["statuses"][ctx.cls] if "STATUS" in cons else T["enums"][_cons_value(f, "ENUM=")]
if v not in vals:
ctx.add("REC-12", path, "%s %s is not one of %s" % (path, _show(v), _or_list(vals)))
return False
return True
if t == "DECIMAL":
c = Z.canon_decimal(v)
if c is None:
ctx.add("REC-14", path, "%s %s is not a decimal" % (path, _show(v)))
return False
if "POSITIVE" in cons and (c == "0" or c.startswith("-")):
ctx.add("REC-15", path, "%s %s is not positive" % (path, _show(v)))
return False
if "NONNEG" in cons and c.startswith("-"):
ctx.add("REC-16", path, "%s %s is negative" % (path, _show(v)))
return False
return True
if t == "UNIT":
r = U.unit_parse(v)
if r[0] != "OK":
ctx.add("REC-17", path, "%s: %s" % (path, r[1]))
return False
return True
if t in ("FORM", "PREDICATE"):
r = F.parse_form(v, "equation" if t == "FORM" else "predicate")
if r[0] != "OK":
ctx.add("REC-18", path, "%s: %s" % (path, r[1]))
return False
return True
if t == "DIMENSION":
if Z.normalize_dimension(v) is None:
ctx.add("REC-19", path, "%s %s is not %s" % (path, _show(v), FORM_PHRASE["DIMENSION"]))
return False
return True
if t == "INT":
if "STEPS" in cons and not (1 <= v <= 1000000):
ctx.add("REC-19", path, "%s %d is not %s" % (path, v, FORM_PHRASE["STEPS"]))
return False
return True
return True # BOOL
def _check_object(ctx, fields, obj, path):
names = [f["name"] for f in fields]
for k in obj:
if k not in names:
ctx.add("REC-06", "%s.%s" % (path, _show(k)), "unknown field %s.%s" % (path, _show(k)))
for f in fields:
p = "%s.%s" % (path, f["name"])
if f["name"] in obj:
if _check_value(ctx, f, obj[f["name"]], p):
ctx.ok.add(p)
elif f["req"] == "R":
ctx.add("REC-08", p, "%s is missing" % p)
def _cond_true(c, rec):
if c is None:
return True
if c["op"] == "PRESENT":
return c["field"] in rec
if c["op"] == "ABSENT":
return c["field"] not in rec
return c["field"] in rec and rec[c["field"]] == c["value"]
def _cond_text(c):
if c["op"] == "PRESENT":
return "%s is present" % c["field"]
if c["op"] == "ABSENT":
return "%s is absent" % c["field"]
return "%s is %s" % (c["field"], c["value"])
def _tfk_ok(tfk):
"""8.2: null, or an object whose TPK and TrM are objects, the TPK's DOF and
CONSTANT (absent meaning empty) lists of objects with a string NAME (a DOF
also a DIMENSION normalize_dimension reads), the TrM's ENTRIES a list of
objects with a string NAME and a DIMENSION normalize_dimension reads."""
if tfk is None:
return True
if type(tfk) is not dict or type(tfk.get("TPK")) is not dict or type(tfk.get("TrM")) is not dict:
return False
tpk, trm = tfk["TPK"], tfk["TrM"]
for kind in ("DOF", "CONSTANT"):
lst = tpk.get(kind, [])
if type(lst) is not list:
return False
for e in lst:
if type(e) is not dict or type(e.get("NAME")) is not str:
return False
if kind == "DOF" and (type(e.get("DIMENSION")) is not str
or Z.normalize_dimension(e["DIMENSION"]) is None):
return False
ents = trm.get("ENTRIES")
if type(ents) is not list:
return False
for e in ents:
if type(e) is not dict or type(e.get("NAME")) is not str:
return False
if type(e.get("DIMENSION")) is not str or Z.normalize_dimension(e["DIMENSION"]) is None:
return False
return True
def _tfk_checks(ctx, obj, fields, prefix, tfk):
"""S5 over the fields of one object that passed S2."""
tpk, trm = tfk["TPK"], tfk["TrM"]
dofs = dict((e["NAME"], e["DIMENSION"]) for e in tpk.get("DOF", []))
consts = set(e["NAME"] for e in tpk.get("CONSTANT", []))
ents = dict((e["NAME"], e["DIMENSION"]) for e in trm["ENTRIES"])
resolved = {}
for f in fields:
n = f["name"]
p = prefix + n
if p not in ctx.ok:
continue
v = obj[n]
cons = f["cons"]
if "TPKNAME" in cons:
for x in (v if f["card"] == "SET" else [v]):
if x not in dofs and x not in consts:
ctx.add("REC-25", p, "%s %s%s is not a DOF or CONSTANT of the TPK"
% (p, "member " if f["card"] == "SET" else "", _show(x)))
break
if "TPKDOF" in cons:
if v in dofs:
resolved[n] = dofs[v]
else:
ctx.add("REC-25", p, "%s %s is not a DOF of the TPK" % (p, _show(v)))
if "TRMENTRY" in cons:
if f["card"] == "SET":
for x in v:
if x not in ents:
ctx.add("REC-25", p, "%s member %s is not an entry of the TrM" % (p, _show(x)))
break
elif v in ents:
resolved[n] = ents[v]
else:
ctx.add("REC-25", p, "%s %s is not an entry of the TrM" % (p, _show(v)))
for f in fields:
n = f["name"]
p = prefix + n
if p not in ctx.ok:
continue
for c in f["cons"]:
if c in ("DIM=QUANTITY", "DIM=DOF") and c[4:] in resolved:
want = Z.normalize_dimension(resolved[c[4:]])
got = U.unit_parse(obj[n])[2]
if got != want:
ctx.add("REC-26", p, "%s %s has dimension %s, not %s" % (p, _show(obj[n]), got, want))
for f in fields:
if f["type"] == "OBJECT" and (prefix + f["name"]) in ctx.ok:
sf = T["fields"]["SUB:" + _cons_value(f, "SUB=")]
v = obj[f["name"]]
if f["card"] == "ONE":
_tfk_checks(ctx, v, sf, prefix + f["name"] + ".", tfk)
else:
for i, o in enumerate(v, 1):
_tfk_checks(ctx, o, sf, "%s%s[%d]." % (prefix, f["name"], i), tfk)
def _zone_digest(tfk, zn, fn):
"""8.2's "a zone tpc_zone cannot digest", read by the zone checks (8.2 since 0.2.1, C-RS-18):
the zone's digest when the zone is an object the zone check passes
(t1_well_formed; trm_well_formed with the TPK; tms_well_formed), else
None, so both residences answer alike on a malformed zone."""
zone = tfk.get(zn)
if type(zone) is not dict:
return None
if zn == "TPK":
good = Z.t1_well_formed(zone)[0]
elif zn == "TrM":
good = type(tfk.get("TPK")) is dict and Z.trm_well_formed(zone, tfk["TPK"])[0]
else:
good = Z.tms_well_formed(zone)[0]
return fn(zone) if good else None
def record_check(record, tfk, setting, stage):
"""tpm_record_schema 0.4.0 section 8."""
if stage not in ("PUSH", "LANDED"):
return _one("REC-36", "-", "stage %s is not PUSH or LANDED" % _show(cj(stage)))
if setting not in ("OFF", "ON", "CLOSED", None):
return _one("REC-36", "-", "setting %s is not OFF, ON, CLOSED or null" % _show(cj(setting)))
if not _tfk_ok(tfk):
return _one("REC-39", "-", "the TFK is not readable")
# S1
if type(record) is not dict:
return _one("REC-01", "-", "the record is not an object")
if "CLASS" not in record:
return _one("REC-02", "CLASS", "CLASS is missing")
cv = record["CLASS"]
if type(cv) is not str:
return _one("REC-09", "CLASS", "CLASS is not a string")
if cv not in T["classes"]:
return _one("REC-03", "CLASS", "CLASS %s is not a record class" % _show(cv))
if T["form"][cv] == "LIST":
return _one("REC-04", "CLASS", "CLASS %s is written by TPX in its own form" % cv)
if stage == "PUSH" and T["writer"][cv] == "TPX":
return _one("REC-05", "CLASS", "CLASS %s is written by TPX and never pushed" % cv)
ctx = _Ctx(cv, stage, tfk)
fields = T["fields"][cv]
names = [f["name"] for f in fields]
# S2
for k in record:
if k not in names:
ctx.add("REC-06", _show(k), "unknown field %s" % _show(k))
for f in fields:
n = f["name"]
if n in record:
if stage == "PUSH" and f["writer"] == "TPX":
ctx.add("REC-07", n, "%s is written by TPX" % n)
continue
if _check_value(ctx, f, record[n], n):
ctx.ok.add(n)
elif f["req"] == "R" and (stage == "LANDED" or f["writer"] != "TPX"):
ctx.add("REC-08", n, "%s is missing" % n)
# S3 rules
for r in T["rules"].get(cv, []):
if not _cond_true(r["if"], record):
continue
a, fl = r["action"], r["fields"]
if a == "REQUIRE":
for x in fl:
if stage == "PUSH" and _field(fields, x)["writer"] == "TPX":
continue
if x not in record:
ctx.add("REC-20", x, "%s is required when %s" % (x, _cond_text(r["if"])))
elif a == "FORBID":
for x in fl:
if x in record:
ctx.add("REC-21", x, "%s is not allowed when %s" % (x, _cond_text(r["if"])))
elif a == "ALL OR NONE":
pres = [x for x in fl if x in record]
if 0 < len(pres) < len(fl):
first = [x for x in fl if x not in record][0]
ctx.add("REC-22", first, "%s are given together or not at all" % _and_list(fl))
elif a == "AT LEAST ONE":
if not any(x in record for x in fl):
ctx.add("REC-23", "-", "one of %s is required" % _or_list(fl))
ok = ctx.ok
if cv == "RESULT" and {"REGIME", "QUANTITY", "ENTRIES"} <= ok and record["REGIME"] == "M":
if record["QUANTITY"] not in record["ENTRIES"]:
ctx.add("REC-27", "ENTRIES", "ENTRIES does not name QUANTITY %s" % _show(record["QUANTITY"]))
if cv == "MASTER":
if {"ID", "PREFIX"} <= ok and record["ID"] != record["PREFIX"] + "-1":
ctx.add("REC-28", "ID", "ID %s is not %s-1" % (_show(record["ID"]), record["PREFIX"]))
if {"D_TFK", "D_TPK", "D_TRM", "D_TMS"} <= ok:
if G.d_tfk(record["D_TPK"], record["D_TRM"], record["D_TMS"]) != record["D_TFK"]:
ctx.add("REC-33", "D_TFK", "D_TFK %s is not the combination of D_TPK, D_TRM and D_TMS"
% record["D_TFK"])
if setting == "OFF" and "LITERATURE" in ok:
ctx.add("REC-30", "LITERATURE", "LITERATURE is not kept under BIBLIOGRAPHY SETTING OFF")
if setting in ("ON", "CLOSED") and "SOURCE" in ok and cv == "VERIFICATION" \
and ("LITERATURE" in ok or "LITERATURE" not in record):
s = record["SOURCE"]
if not (_m(_BIB, s) and s in record.get("LITERATURE", [])):
ctx.add("REC-31", "SOURCE", "SOURCE %s is not a BIB ID in LITERATURE" % _show(s))
if cv == "NAMED_GAP" and "SOURCE" in ok:
src = record["SOURCE"]
if "AGAINST" in ok:
ag = record["AGAINST"]
if src == "SR7_DOMAIN":
if ag != ["TFK DOMAIN"]:
ctx.add("REC-19", "AGAINST", "AGAINST is not the one member TFK DOMAIN")
ok.discard("AGAINST")
else:
rx, form = (_IDENT, "IDENT") if src in ("IC2", "UNDER") else (_RID, "RID")
for x in ag:
if not _m(rx, x):
ctx.add("REC-19", "AGAINST", "AGAINST member %s is not %s" % (_show(x), FORM_PHRASE[form]))
ok.discard("AGAINST")
break
if "TRIGGER" in ok and src in ("SR5_LOCUS", "SR7_ASSUMPTIONS", "SR7_DOMAIN"):
if not _m(_RID, record["TRIGGER"]):
ctx.add("REC-19", "TRIGGER", "TRIGGER %s is not %s" % (_show(record["TRIGGER"]), FORM_PHRASE["RID"]))
# S4 T2
if cv == "ASSUMPTION" and "PROPOSITION" in ok:
for fnd in TT.t2_findings(record["PROPOSITION"]):
ctx.add("REC-24", "PROPOSITION", "PROPOSITION " + fnd)
# S5 the TFK
if tfk is not None:
_tfk_checks(ctx, record, fields, "", tfk)
if cv == "NAMED_GAP" and {"SOURCE", "AGAINST"} <= ok and record["SOURCE"] in ("IC2", "UNDER"):
dofs = set(e["NAME"] for e in tfk["TPK"].get("DOF", []))
for x in record["AGAINST"]:
if x not in dofs:
ctx.add("REC-25", "AGAINST", "AGAINST member %s is not a DOF of the TPK" % _show(x))
break
if cv == "MASTER":
for fld, zn, fn in (("D_TPK", "TPK", Z.d_tpk), ("D_TRM", "TrM", Z.d_trm), ("D_TMS", "TMS", Z.d_tms)):
if fld not in ok:
continue
d = _zone_digest(tfk, zn, fn)
if d is None:
ctx.add("REC-39", "-", "the TFK is not readable")
elif d != record[fld]:
ctx.add("REC-34", fld, "%s %s is not the TFK's D(%s)" % (fld, record[fld], zn))
# S6 the digest
if stage == "LANDED" and "DIGEST" in ok and not ctx.p:
r2 = dict(record)
del r2["DIGEST"]
d = record_digest(r2)
if d[0] == "OK" and d[1] != record["DIGEST"]:
ctx.add("REC-29", "DIGEST", "DIGEST %s is not the record's digest %s" % (record["DIGEST"], d[1]))
if not ctx.p:
return ["OK"]
return _refused(ctx.p)
# ---- the canonical form and the record digest (schema 9) ---------------------------
def _canon_scalar(f, v):
t = f["type"]
if t == "TEXT":
return Z._text(v)
if t == "DECIMAL":
if "PREC" in f["cons"]: # a RESULT's U_C: its precision form
return prec_decimal(v)[1]
return Z.canon_decimal(v)
if t == "UNIT":
return U.unit_parse(v)[1]
if t == "FORM":
return F.parse_form(v, "equation")[1]
if t == "PREDICATE":
return F.parse_form(v, "predicate")[1]
if t == "DIMENSION":
return Z.normalize_dimension(v)
if t == "BOOL":
return "true" if v else "false"
return v
def _forbidden(cls, rec):
out = set()
for r in T["rules"].get(cls, []):
if r["action"] == "FORBID" and _cond_true(r["if"], rec):
out.update(r["fields"])
return out
def _encode(f, v, out):
t, card = f["type"], f["card"]
if card == "SET":
mem = sorted(Z._text(x) if t == "TEXT" else x for x in v)
out.append(len(mem))
out.extend(mem)
elif t == "OBJECT":
sub = _cons_value(f, "SUB=")
sf = T["fields"]["SUB:" + sub]
if card == "ONE":
_encode_obj(sf, v, out)
else:
kf = _field(sf, T["subkey"][sub])
mem = sorted(v, key=lambda o: _canon_scalar(kf, o[kf["name"]]))
out.append(len(mem))
for o in mem:
_encode_obj(sf, o, out)
elif t == "INT":
out.append(v)
else:
out.append(_canon_scalar(f, v))
def _encode_obj(sf, o, out):
pres = [f for f in sf if f["name"] in o]
out.append(len(pres))
for f in pres:
out.append(f["name"])
_encode(f, o[f["name"]], out)
def record_preimage(record):
"""["OK", the "TPC-RECORD" form 1 field list] (schema 9.2-9.4)."""
rec = record
if type(record) is dict and "DIGEST" in record:
rec = dict(record)
del rec["DIGEST"]
r = record_check(rec, None, None, "LANDED")
if r[0] != "OK":
return r
cls = rec["CLASS"]
forb = _forbidden(cls, rec)
body, n = [], 0
for f in T["fields"][cls]:
if f["dig"] != "D":
continue
nm = f["name"]
if nm in rec:
v = rec[nm]
elif f["default"] == "EMPTY" and nm not in forb:
v = []
elif f["default"] == "FALSE" and nm not in forb:
v = False
else:
continue
n += 1
body.append(nm)
_encode(f, v, body)
return ["OK", ["TPC-RECORD", "1", n] + body]
def record_digest(record):
r = record_preimage(record)
if r[0] != "OK":
return r
return ["OK", G.digest_fields(r[1])]
# ---- the views (schema 10) --------------------------------------------------------------
def record_view(record, view, grade):
"""A view maps, it does not check (10.2); the status word W of 10.3."""
if type(record) is not dict:
return _one("REC-01", "-", "the record is not an object")
if type(view) is not str or view not in VIEWS:
return _one("REC-37", "-", "view %s is not a view" % _show(cj(view)))
if grade is not None and (type(grade) is not str or grade not in GRADE_VALUES):
return _one("REC-38", "-", "grade %s is not a grade" % _show(cj(grade)))
r = record
st = r.get("STATUS")
if type(st) is str and st in ("HELD", "REFUTED"):
hw, w = True, st
elif grade is not None:
hw, w = True, grade
elif "GRADE" in r:
hw, w = True, r["GRADE"]
else:
hw, w = False, None
o = {}
def put(k, src):
if src in r:
o[k] = r[src]
def steps():
d = r.get("DERIVATION")
if type(d) is dict and "STEPS" in d:
o["steps"] = d["STEPS"]
tpx_corr = r.get("CLASS") == "CORROBORATION" and r.get("LANDED_BY") == "TPX"
if view == "HOLD":
put("id", "ID")
put("block_id", "BLOCK")
if tpx_corr:
o["author"] = "TPX"
else:
put("author", "AUTHOR")
put("cites", "CITES")
put("assumptions", "ASSUMPTIONS")
put("kind", "CLASS")
if hw:
o["status"] = w
if tpx_corr and "AUTHOR" in r:
o["supplied_by"] = r["AUTHOR"]
if "LANDED_BY" in r and r["LANDED_BY"] != "TPX" and ("AUTHOR" not in r or r["LANDED_BY"] != r["AUTHOR"]):
o["vetted_by"] = r["LANDED_BY"]
put("vetted_in", "VETTED_IN")
elif view == "STRIKE":
put("assumptions", "ASSUMPTIONS")
put("cites", "CITES")
steps()
elif view == "SUITE":
put("regime", "REGIME")
put("assumptions", "ASSUMPTIONS")
put("cites", "CITES")
steps()
put("domain", "DOMAIN")
elif view == "PROBE_L":
if "EQUATION" in r:
o["finding"] = {"type": "EQUATION", "value": r["EQUATION"]}
elif "VALUE" in r:
o["finding"] = {"type": "DECIMAL", "value": r["VALUE"]}
elif "STATEMENT" in r:
o["finding"] = {"type": "TEXT", "value": r["STATEMENT"]}
elif view == "PROBE_M":
put("x", "VALUE")
put("u", "U_C")
put("unit", "UNIT")
elif view == "PROBE_LOAD":
put("cites", "CITES")
put("assumptions", "ASSUMPTIONS")
if hw:
o["standing"] = w in ("PROVISIONAL", "ESTABLISHED")
elif view == "LITERATURE":
put("id", "ID")
if hw:
o["status"] = w
o["literature"] = r["LITERATURE"] if "LITERATURE" in r else []
else: # EOM
put("ID", "ID")
if hw:
o["STATUS"] = w
put("ROLE", "ROLE")
put("EQUATION", "EQUATION")
return ["OK", o]
# ---- schema_json (schema 11; non-normative, RD-PT-15) ---------------------------------
_DESC = ("Generated by TPC tpc_record schema_json from tpm_record_schema " + SCHEMA_TEXT_VERSION
+ ". Non-normative: the table governs and TPC record_check decides; a validator run on "
"this document can pass a record TPX refuses.")
_QUIET = ("NOTBLANK", "KEY", "NONEMPTY", "SETTING", "TPKNAME", "TPKDOF", "TRMENTRY", "DTFK",
"DIGEST", "T2", "GRADE", "STATUS")
_TYPE_FN = {"FORM": "tpc_form parse_form", "PREDICATE": "tpc_form parse_form",
"DECIMAL": "tpc_zone canon_decimal", "UNIT": "tpc_units unit_parse",
"DIMENSION": "tpc_zone normalize_dimension"}
def _desc(f):
t, cons = f["type"], f["cons"]
d = "%s (%s), written by %s" % (t, f["card"], f["writer"])
fn = _TYPE_FN.get(t)
if fn is None:
other = [c for c in cons if c not in _QUIET and not c.startswith("ENUM=")
and not c.startswith("SUB=") and not c.startswith("DIM=") and not c.startswith("ZONE=")]
if other or t in ("RID", "IDSET") or "ROLE" in cons:
fn = "tpc_record record_check"
if fn is not None:
d += "; checked by TPC " + fn
return d + "."
def _fs(cls, f):
t, card, cons = f["type"], f["card"], f["cons"]
D = _desc(f)
if card == "SET":
return {"type": "array", "items": {"type": "string"}, "uniqueItems": True, "description": D}
if t == "OBJECT":
ref = "#/$defs/" + _cons_value(f, "SUB=")
if card == "ONE":
return {"$ref": ref, "description": D}
return {"type": "array", "items": {"$ref": ref}, "description": D}
if t == "ENUM" and "ROLE" not in cons:
if "GRADE" in cons: # 11.3 (S100; D215.2): the class's LANDGRADE alone
return {"enum": [T["landgrade"][cls]], "description": D}
if "STATUS" in cons:
return {"enum": list(T["statuses"][cls]), "description": D}
return {"enum": list(T["enums"][_cons_value(f, "ENUM=")]), "description": D}
if t == "INT":
return {"type": "integer", "minimum": 1, "maximum": 1000000, "description": D}
if t == "BOOL":
return {"type": "boolean", "description": D}
return {"type": "string", "description": D}
def schema_json():
"""["OK", the JSON Schema 2020-12 document generated from the table]."""
branches = []
for c in KEYED:
fl = T["fields"][c]
props = {}
for f in fl:
props[f["name"]] = _fs(c, f)
branches.append({"if": {"properties": {"CLASS": {"const": c}}, "required": ["CLASS"]},
"then": {"type": "object", "properties": props,
"required": [f["name"] for f in fl
if f["req"] == "R" and f["writer"] in ("AUTHOR", "LEAD")],
"additionalProperties": False}})
defs = {}
for s in T["subs"]:
fl = T["fields"]["SUB:" + s]
props = {}
for f in fl:
props[f["name"]] = _fs(None, f)
defs[s] = {"type": "object", "properties": props,
"required": [f["name"] for f in fl if f["req"] == "R"],
"additionalProperties": False}
doc = {"$schema": "https://json-schema.org/draft/2020-12/schema",
"title": "TPM register record", "description": _DESC, "type": "object",
"required": ["CLASS"], "properties": {"CLASS": {"enum": list(KEYED)}},
"allOf": branches, "$defs": defs}
return ["OK", cj(doc)]
# =========================================================================================
# tpm_record_functions 0.3.0
# =========================================================================================
def _v(v):
"""<v> of an argument error: a string shown as given, any other value as its
canonical JSON (RD-TQ-31)."""
return _show(v) if type(v) is str else _show(cj(v))
def _vj(v):
"""<v> as REC-36's: the value's canonical JSON, shown."""
return _show(cj(v))
def _bad_arg(argument, value, what):
return _one("REC-50", "-", "%s %s is not %s" % (argument, _vj(value), what))
RANK = {"REFUTED": 3, "WITHDRAWN": 2, "ESTABLISHED": 1}
LIST_CLASSES = ("HOLD", "RELEASE", "WITHDRAWAL", "PROMOTION")
def _pairs_ok(v):
return type(v) is list and all(type(p) is list and len(p) == 2 and type(p[0]) is str
and type(p[1]) is str for p in v)
def _register(register, argument="register"):
"""(keyed {id: record} in order, list-form records) or a REC-50 refusal (0.3)."""
bad = _bad_arg(argument, register, "a register")
if type(register) is not list:
return None, None, bad
keyed, lists = {}, []
for r in register:
if type(r) is dict:
if r.get("CLASS") not in KEYED or type(r.get("ID")) is not str or r["ID"] in keyed \
or r.get("GRADE") not in GRADE_VALUES:
return None, None, bad
if "STATUS" in r and type(r["STATUS"]) is not str:
return None, None, bad
keyed[r["ID"]] = r
elif type(r) is list:
if len(r) < 2 or r[1] not in LIST_CLASSES:
return None, None, bad
if r[1] == "WITHDRAWAL" and not (len(r) == 14 and type(r[3]) is str and r[5] in ("REFUTED", "WITHDRAWN")
and _pairs_ok(r[10])):
return None, None, bad
if r[1] == "PROMOTION" and not (len(r) == 5 and type(r[3]) is list and all(
type(t) is list and len(t) == 3 and type(t[0]) is str for t in r[3])):
return None, None, bad
lists.append(r)
else:
return None, None, bad
return keyed, lists, None
def _grade_now(keyed, lists, rid):
g0 = keyed[rid]["GRADE"]
if g0 == "NOT GRADED":
return g0
best = g0
for r in lists:
given = []
if r[1] == "WITHDRAWAL":
if r[3] == rid:
given.append(r[5])
if rid in [p[0] for p in r[10]]:
given.append("WITHDRAWN")
elif r[1] == "PROMOTION":
if rid in [t[0] for t in r[3]]:
given.append("ESTABLISHED")
for g in given:
if RANK.get(g, 0) > RANK.get(best, 0):
best = g
return best
def _words(keyed, lists):
out = {}
for rid, r in keyed.items():
st = r.get("STATUS")
out[rid] = st if st in ("HELD", "REFUTED") else _grade_now(keyed, lists, rid)
return out
def _standing(w):
return w in ("PROVISIONAL", "ESTABLISHED")
def _class_arg(c):
if type(c) is not str or c not in T["classes"]:
return _one("REC-51", "-", "class %s is not a record class" % _vj(c))
return None
# ---- 4 grade and status ----------------------------------------------------------------
def grade_values(cls):
r = _class_arg(cls)
return r if r else ["OK", list(T["grades"][cls])]
def status_values(cls):
r = _class_arg(cls)
return r if r else ["OK", list(T["statuses"].get(cls, []))]
def grade_now(register, rid):
keyed, lists, bad = _register(register)
if bad:
return bad
if type(rid) is not str or rid not in keyed:
return _one("REC-52", "-", "record %s is not in the register" % _v(rid))
return ["OK", _grade_now(keyed, lists, rid)]
def status_words(register):
keyed, lists, bad = _register(register)
if bad:
return bad
return ["OK", _words(keyed, lists)]
def is_standing(word):
if type(word) is not str or word not in STATUS_WORDS:
return _one("REC-53", "-", "word %s is not a status word" % _v(word))
return ["OK", _standing(word)]
def status_word(grade, status):
if type(grade) is not str or grade not in GRADE_VALUES:
return _one("REC-38", "-", "grade %s is not a grade" % _vj(grade))
if status is not None and (type(status) is not str or status not in STATUS_VALUES):
return _one("REC-53", "-", "word %s is not a status word" % _v(status))
return ["OK", status if status in ("HELD", "REFUTED") else grade]
def grade_next(cls, grade, event):
r = _class_arg(cls)
if r:
return r
if type(grade) is not str or grade not in GRADE_VALUES:
return _one("REC-38", "-", "grade %s is not a grade" % _vj(grade))
if type(event) is not str or event not in ("PROMOTE", "STRIKE", "WITHDRAW"):
return _one("REC-55", "-", "event %s is not PROMOTE, STRIKE or WITHDRAW" % _v(event))
grades = T["grades"][cls]
out = None
if event == "PROMOTE" and cls == "RESULT" and grade == "PROVISIONAL":
out = "ESTABLISHED"
elif event == "STRIKE" and cls in ("RESULT", "CORROBORATION") and grade in ("PROVISIONAL", "ESTABLISHED") \
and grade in grades:
out = "REFUTED"
elif event == "WITHDRAW" and grades != ["NOT GRADED"] and grade in ("PROVISIONAL", "ESTABLISHED") \
and grade in grades:
out = "WITHDRAWN"
if out is None:
return _one("REC-54", "-", "%s does not apply to %s %s of grade %s" % (event, _art(cls), cls, grade))
return ["OK", out]
def _art(cls):
"""REC-54's article (F-GV2-11): "an" before ASSUMPTION and ENTRY_STATUS, "a" before
every other class of the table."""
return "an" if cls in ("ASSUMPTION", "ENTRY_STATUS") else "a"
def status_next(cls, status, event):
"""functions 4.7 (D214.1)."""
r = _class_arg(cls)
if r:
return r
statuses = T["statuses"].get(cls, [])
if not statuses:
return _one("REC-56", "-", "%s carries no STATUS" % cls)
ok_event = type(event) is str and (event in ("HOLD", "STRIKE") or (
event.startswith("RELEASE:") and event[len("RELEASE:"):] in STATUS_WORDS))
if not ok_event:
return _one("REC-55", "-", "event %s is not HOLD, STRIKE or RELEASE:<word>" % _v(event))
out = None
if type(status) is str and status in statuses:
if event == "HOLD":
out = "REFUTED" if status == "REFUTED" else "HELD"
elif event == "STRIKE":
if cls in ("RESULT", "CORROBORATION") and status in ("UNMARKED", "HELD"):
out = "REFUTED"
else:
w = event[len("RELEASE:"):]
out = w if w in ("HELD", "REFUTED") else "UNMARKED"
if out not in statuses:
out = None
if out is None:
return _one("REC-54", "-", "%s does not apply to %s %s of STATUS %s" % (event, _art(cls), cls, _v(status)))
return ["OK", out]
def inherit_grade(cls, grade):
"""functions 4.8 (D215.2): the grade the inherited record holds once the copy is
written; its landing GRADE is its class's LANDGRADE in every case (RD-RS-50)."""
r = _class_arg(cls)
if r:
return r
if type(grade) is not str or grade not in GRADE_VALUES:
return _one("REC-38", "-", "grade %s is not a grade" % _vj(grade))
if T["grades"][cls] == ["NOT GRADED"]:
return ["OK", "NOT GRADED"]
return ["OK", grade if grade in ("REFUTED", "WITHDRAWN") else "PROVISIONAL"]
# ---- 5 citations --------------------------------------------------------------------------
def _members(v):
return [x for x in v if type(x) is str] if type(v) is list else []
def citation_check(record, register, entries, supersede_decided):
keyed, lists, bad = _register(register)
if bad:
return bad
if type(entries) is not list or not all(_m(_BIB, x) for x in entries):
return _bad_arg("entries", entries, "a list of BIB IDs")
if type(supersede_decided) is not bool:
return _bad_arg("supersede_decided", supersede_decided, "a boolean")
if type(record) is not dict:
return _one("REC-01", "-", "the record is not an object")
words = _words(keyed, lists)
p = []
if type(record.get("ID")) is str and record["ID"] in keyed: # an ID that is not a string: not looked up (PHP's is_string)
p.append(["REC-57", "ID", "ID %s is already in the register" % _show(record["ID"])])
for field, want, article in (("CITES", "RESULT", "a RESULT"), ("ASSUMPTIONS", "ASSUMPTION", "an ASSUMPTION")):
for x in _members(record.get(field)):
if x not in keyed or keyed[x]["CLASS"] != want:
p.append(["REC-58", field, "%s member %s is not %s of the register" % (field, _show(x), article)])
break
if not _standing(words[x]):
p.append(["REC-59", field, "%s member %s does not stand (%s)" % (field, _show(x), words[x])])
break
cls = record.get("CLASS")
if cls in ("CORROBORATION", "VERIFICATION") and type(record.get("TARGET")) is str:
t = record["TARGET"]
if t not in keyed or keyed[t]["CLASS"] != "RESULT":
p.append(["REC-58", "TARGET", "TARGET %s is not a RESULT of the register" % _show(t)])
elif words[t] not in ("PROVISIONAL", "ESTABLISHED", "HELD"):
p.append(["REC-59", "TARGET", "TARGET %s does not stand (%s)" % (_show(t), words[t])])
if type(record.get("SUPERSEDES")) is str:
s = record["SUPERSEDES"]
if s not in keyed or keyed[s]["CLASS"] != cls:
p.append(["REC-58", "SUPERSEDES", "SUPERSEDES %s is not a %s of the register" % (_show(s), _v(cls))])
elif not _standing(words[s]):
p.append(["REC-59", "SUPERSEDES", "SUPERSEDES %s does not stand (%s)" % (_show(s), words[s])])
elif not supersede_decided and keyed[s].get("AUTHOR") != record.get("AUTHOR"):
p.append(["REC-60", "SUPERSEDES", "SUPERSEDES %s was authored by %s, not %s"
% (_show(s), _v(keyed[s].get("AUTHOR")), _v(record.get("AUTHOR")))])
for b in _members(record.get("LITERATURE")):
if b not in entries:
p.append(["REC-61", "LITERATURE", "LITERATURE member %s is not a landed entry of the UWC" % _show(b)])
break
return _refused(p) if p else ["OK"]
# ---- 6 the withdrawal -------------------------------------------------------------------------
def _rid_list(v):
return type(v) is list and all(_m(_RID, x) for x in v) and len(set(v)) == len(v)
def withdrawal_scope(register, root, still_stands):
keyed, lists, bad = _register(register)
if bad:
return bad
if type(root) is not str or root not in keyed:
return _one("REC-52", "-", "record %s is not in the register" % _v(root))
if not _rid_list(still_stands):
return _bad_arg("still_stands", still_stands, "a list of record identifiers")
ss = set(still_stands)
reached, frontier = set(), [root]
while frontier:
cur = frontier.pop()
for rid, r in keyed.items():
if rid in reached or rid == root:
continue
if cur in _members(r.get("CITES")) or cur in _members(r.get("ASSUMPTIONS")):
reached.add(rid)
if rid not in ss:
frontier.append(rid)
probs = [["REC-62", "-", "STILL STANDS member %s is not reached by the withdrawal" % x]
for x in still_stands if x not in reached]
if probs:
return _refused(probs)
words = _words(keyed, lists)
casc = [[rid, words[rid]] for rid in sorted(reached - ss)
if _grade_now(keyed, lists, rid) in ("PROVISIONAL", "ESTABLISHED")]
return ["OK", casc]
ACT_KEYS = ["cause", "withdrawn", "withdrawn_by", "status_set", "prior_status", "superseded_by", "locus_gap",
"still_stands", "cascade", "tps_version", "d_tms"]
CAUSES = ["SR1", "SR5", "AUDIT", "AUTHOR", "SUPERSEDED", "INHERITED"]
CAUSE_SETS = {"SR1": ["REFUTED"], "SR5": ["REFUTED"], "AUTHOR": ["WITHDRAWN"], "SUPERSEDED": ["WITHDRAWN"],
"AUDIT": ["REFUTED", "WITHDRAWN"], "INHERITED": ["REFUTED", "WITHDRAWN"]}
def _pair_list_ok(v):
if not _pairs_ok(v):
return False
ids = [q[0] for q in v]
return all(_m(_RID, i) for i in ids) and len(set(ids)) == len(ids) and ids == sorted(ids) \
and all(q[1] in STATUS_WORDS for q in v)
def _act(act):
"""The act's problems (REC-63 alone, or REC-64 per key and REC-65)."""
if type(act) is not dict or sorted(act) != sorted(ACT_KEYS):
return [["REC-63", "-", "act is not a withdrawal act"]]
p = []
def bad(key, form):
p.append(["REC-64", key, "%s %s is not %s" % (key, _v(act[key]), form)])
cause = act["cause"]
cause_ok = cause in CAUSES
if not cause_ok:
bad("cause", "one of SR1, SR5, AUDIT, AUTHOR, SUPERSEDED or INHERITED")
if not _m(_RID, act["withdrawn"]):
bad("withdrawn", "a record identifier")
wb = act["withdrawn_by"]
if type(wb) is not str or Z._first_bad(wb) is not None or Z._blank(wb):
bad("withdrawn_by", "a text")
if cause_ok and act["status_set"] not in CAUSE_SETS[cause]:
bad("status_set", "the grade cause %s sets" % cause)
if cause == "INHERITED":
if act["prior_status"] != "PROVISIONAL":
bad("prior_status", "PROVISIONAL")
elif act["prior_status"] not in ("PROVISIONAL", "ESTABLISHED", "HELD"):
bad("prior_status", "a status word of a record that stands or is held")
if cause == "SUPERSEDED":
if not _m(_RID, act["superseded_by"]):
bad("superseded_by", "a record identifier")
elif act["superseded_by"] != "":
bad("superseded_by", "empty")
if cause == "SR5":
if act["locus_gap"] != "" and not _m(_RID, act["locus_gap"]):
bad("locus_gap", "a record identifier")
elif act["locus_gap"] != "":
bad("locus_gap", "empty")
if cause == "AUDIT":
if not _pair_list_ok(act["still_stands"]):
bad("still_stands", "a sorted list of [id, word] pairs")
elif act["still_stands"] != []:
bad("still_stands", "empty")
if cause == "INHERITED":
if act["cascade"] != []:
bad("cascade", "empty")
elif not _pair_list_ok(act["cascade"]):
bad("cascade", "a sorted list of [id, word] pairs")
if not _m(_VERSION, act["tps_version"]):
bad("tps_version", "a version n.n.n")
if not _m(_HEX64, act["d_tms"]):
bad("d_tms", "64 lowercase hexadecimal characters")
if not p:
ss = [q[0] for q in act["still_stands"]]
cs = [q[0] for q in act["cascade"]]
for i in sorted(set([act["withdrawn"]] + ss + cs)):
if ([act["withdrawn"]] + ss + cs).count(i) > 1:
p.append(["REC-65", "-", "%s is named twice" % i])
return p
def _act_fields(act):
f = ["TPC-WITHDRAW", "1", act["cause"], act["withdrawn"], act["withdrawn_by"], act["status_set"],
act["prior_status"], act["superseded_by"], act["locus_gap"], len(act["still_stands"])]
for i, w in act["still_stands"]:
f += [i, w]
f.append(len(act["cascade"]))
for i, w in act["cascade"]:
f += [i, w]
return f + [act["tps_version"], act["d_tms"]]
def withdrawal_preimage(act):
p = _act(act)
if p:
return _refused(p)
return ["OK", _act_fields(act)]
def withdrawal_record(withdrawal_id, act):
p = _act(act)
if p and p[0][0] == "REC-63":
return _refused(p)
if not _m(_RID, withdrawal_id):
p.append(["REC-64", "withdrawal_id", "withdrawal_id %s is not a record identifier" % _v(withdrawal_id)])
if p:
return _refused(p)
return ["OK", [withdrawal_id, "WITHDRAWAL", act["cause"], act["withdrawn"], act["withdrawn_by"],
act["status_set"], act["prior_status"], act["superseded_by"], act["locus_gap"],
act["still_stands"], act["cascade"], act["tps_version"], act["d_tms"],
G.digest_fields(_act_fields(act))]]
# ---- 2, 7 exact decimal arithmetic and budget_check -------------------------------------
def _nat_add(a, b):
a, b = a[::-1], b[::-1]
out, carry = [], 0
for i in range(max(len(a), len(b))):
s = carry + (ord(a[i]) - 48 if i < len(a) else 0) + (ord(b[i]) - 48 if i < len(b) else 0)
out.append(chr(48 + s % 10))
carry = s // 10
if carry:
out.append(chr(48 + carry))
r = "".join(reversed(out)).lstrip("0")
return r if r else "0"
def _nat_mul(a, b):
if a == "0" or b == "0":
return "0"
res = [0] * (len(a) + len(b))
for i in range(len(a) - 1, -1, -1):
for j in range(len(b) - 1, -1, -1):
res[i + j + 1] += (ord(a[i]) - 48) * (ord(b[j]) - 48)
for k in range(len(res) - 1, 0, -1): # carry after each row keeps every cell small
if res[k] >= 10:
res[k - 1] += res[k] // 10
res[k] %= 10
r = "".join(chr(48 + d) for d in res).lstrip("0")
return r if r else "0"
def _nat_dec1(a):
d = list(a)
i = len(d) - 1
while d[i] == "0":
d[i] = "9"
i -= 1
d[i] = chr(ord(d[i]) - 1)
r = "".join(d).lstrip("0")
return r if r else "0"
def _nat_cmp(a, b):
if len(a) != len(b):
return -1 if len(a) < len(b) else 1
return -1 if a < b else (1 if a > b else 0)
def _nat_shift(a, k):
return a if a == "0" else a + "0" * k
def _mexp(c):
"""A canonical decimal's (m, e); the sign is dropped (callers pass values
that are not negative)."""
c = c[1:] if c.startswith("-") else c
if "e" in c:
m, e = c.split("e")
return m, int(e)
return c, 0
def _m_result(record):
"""An M RESULT whose measured fields budget_check reads."""
if type(record) is not dict or record.get("CLASS") != "RESULT" or record.get("REGIME") != "M":
return False
u_c = Z.canon_decimal(record.get("U_C"))
if u_c is None or u_c == "0" or u_c.startswith("-") or U.unit_parse(record.get("UNIT"))[0] != "OK":
return False
b = record.get("BUDGET")
if type(b) is not list or not b:
return False
for c in b:
if type(c) is not dict or U.unit_parse(c.get("UNIT"))[0] != "OK":
return False
cu = Z.canon_decimal(c.get("U"))
if cu is None or cu.startswith("-"):
return False
return True
def budget_check(record):
if not _m_result(record):
return _one("REC-66", "-", "budget_check reads an M RESULT")
if record.get("CORRELATED") is True:
return ["INSPECT", "BUDGET is correlated: its consistency is INSPECTION"]
ru = U.unit_parse(record["UNIT"])
rtok = U._tokens(record["UNIT"])
pre = dict(U.PREFIXES)
first_dim, first_conv, first_insp, shifts = None, None, None, []
for i, c in enumerate(record["BUDGET"], 1):
cu = U.unit_parse(c["UNIT"])
if cu[2] != ru[2]:
if first_dim is None:
first_dim = _one("REC-67", "BUDGET[%d].UNIT" % i, "BUDGET[%d].UNIT %s has dimension %s, not %s"
% (i, _show(c["UNIT"]), cu[2], ru[2]))
shifts.append(None)
continue
if cu[3] != ru[3]:
if first_conv is None:
first_conv = ["CONVENTION", "units of different kind: %s and %s" % (cu[3], ru[3])]
shifts.append(None)
continue
ctok = U._tokens(c["UNIT"])
if len(ctok) != len(rtok) or any(a[1] != b[1] or a[2] != b[2] for a, b in zip(ctok, rtok)):
if first_insp is None:
first_insp = ["INSPECT", "BUDGET[%d].UNIT %s is not a power of ten of UNIT %s"
% (i, _show(c["UNIT"]), _show(record["UNIT"]))]
shifts.append(None)
continue
shifts.append(sum(((pre[a[0]] if a[0] else 0) - (pre[b[0]] if b[0] else 0)) * a[2]
for a, b in zip(ctok, rtok)))
for outcome in (first_dim, first_conv, first_insp):
if outcome is not None:
return outcome
mc, ec = _mexp(prec_decimal(record["U_C"])[1]) # U_C at its last written digit
d1 = _nat_dec1(mc)
lowf = "5" if d1 == "0" else d1 + "5"
low, high = _nat_mul(lowf, lowf), _nat_mul(mc + "5", mc + "5")
eb = 2 * ec - 2
terms = []
for c, s in zip(record["BUDGET"], shifts):
m, e = _mexp(Z.canon_decimal(c["U"]))
if m != "0":
terms.append((_nat_mul(m, m), 2 * (e + s)))
exps = [eb] + [t[1] for t in terms]
b = min(exps)
if max(exps) - b > 20000:
return ["INSPECT", "BUDGET spans more than 20000 decimal digits"]
total = "0"
for m2, e in terms:
total = _nat_add(total, _nat_shift(m2, e - b))
if _nat_cmp(_nat_shift(low, eb - b), total) <= 0 and _nat_cmp(total, _nat_shift(high, eb - b)) <= 0:
return ["OK"]
return _one("REC-68", "U_C", "U_C %s is not the root sum of squares of BUDGET at its last digit"
% _show(record["U_C"]))
def _cd(x):
return Z.canon_decimal(S.canon(x))
def prec_decimal(s):
"""D218.1 (functions 0.3.0 7.3): s of tpc_zone's DECIMAL grammar read as sign,
integer digits I, fraction digits F and exponent X; D = I then F, leading zeros
removed; "0" when D is empty, else sign, D (trailing zeros kept) and "e" X - len(F)
when that is not 0. The value canon_decimal(s) names, at s's last written digit."""
m = Z._DECIMAL.match(s) if type(s) is str else None
if not m:
return _bad_arg("s", s, "a decimal string")
sign, ip, fp, ex = m.group(1), m.group(2), m.group(3) or "", m.group(4)
d = (ip + fp).lstrip("0")
if d == "":
return ["OK", "0"]
x = (int(ex) if ex else 0) - len(fp)
return ["OK", sign + d + ("e" + str(x) if x != 0 else "")]
def field_values(record):
if type(record) is not dict or record.get("CLASS") != "RESULT":
return _one("REC-69", "-", "field_values reads a RESULT")
if record.get("REGIME") != "M":
return ["OK", {"U_C": "0"}]
n = U.normalize_measurement(record.get("VALUE"), record.get("U_C"), record.get("UNIT"))
if n[0] != "OK":
return _one("REC-70", "U_C", "U_C: %s" % n[1])
return ["OK", {"U_C": _cd(n[2])}]
# ---- 8 the parrot comparison's pair ---------------------------------------------------------
def _l_result(r):
return type(r) is dict and r.get("CLASS") == "RESULT" and r.get("REGIME") == "L"
def l_finding_pair(original, reproduction):
if not (_l_result(original) and _l_result(reproduction)):
return _one("REC-71", "-", "l_finding_pair reads two L RESULTs")
pair = (original, reproduction)
if all(type(x.get("EQUATION")) is str for x in pair):
out = []
for x in pair:
f = F.parse_form(x["EQUATION"], "equation")
if f[0] != "OK":
return _one("REC-71", "-", "l_finding_pair reads two L RESULTs")
out.append({"finding": {"type": "EQUATION", "value": f[1]}})
return ["OK"] + out
if all("VALUE" in x and "UNIT" in x for x in pair):
a = U.normalize_measurement(original["VALUE"], "0", original["UNIT"])
b = U.normalize_measurement(reproduction["VALUE"], "0", reproduction["UNIT"])
if a[0] != "OK" or b[0] != "OK":
return _one("REC-71", "-", "l_finding_pair reads two L RESULTs")
if a[3] != b[3]:
return _one("REC-72", "-", "units of different dimension: %s and %s" % (a[3], b[3]))
if a[4] != b[4]:
return ["CONVENTION", "units of different kind: %s and %s" % (a[4], b[4])]
return ["OK", {"finding": {"type": "DECIMAL", "value": _cd(a[1])}},
{"finding": {"type": "DECIMAL", "value": _cd(b[1])}}]
if not all(type(x.get("STATEMENT")) is str for x in pair):
return _one("REC-71", "-", "l_finding_pair reads two L RESULTs")
return ["OK"] + [{"finding": {"type": "TEXT", "value": Z._text(x["STATEMENT"])}} for x in pair]
# ---- 11 the declared form on a record -----------------------------------------------------
def _tpk_dims(tfk):
tpk = tfk["TPK"]
dims = {}
for kind in ("DOF", "CONSTANT"):
for e in tpk.get(kind, []):
dims[e["NAME"]] = e.get("DIMENSION")
return dims
def _dofs(tfk):
return [e["NAME"] for e in tfk["TPK"].get("DOF", [])]
def _names(text, mode):
r = F.form_names(text, mode) if type(text) is str else ["REFUSED", ""]
return r[1] if r[0] == "OK" else []
def record_form_check(record, tfk):
if type(record) is not dict or record.get("CLASS") not in ("RESULT", "ASSUMPTION"):
return _one("REC-75", "-", "record_form_check reads a RESULT or an ASSUMPTION")
if tfk is None or not _tfk_ok(tfk):
return _one("REC-39", "-", "the TFK is not readable")
if "EQUATION" not in record:
return ["OK"]
eq = record["EQUATION"]
refs = _members(record.get("REFS"))
en = _names(eq, "equation")
dn = _names(record.get("DOMAIN_CONDITION"), "predicate") if "DOMAIN_CONDITION" in record else []
p = []
for n in en:
if n not in refs:
p.append(["REC-76", "EQUATION", "EQUATION names %s, which is not in REFS" % n])
for n in refs:
if n not in en and n not in dn:
p.append(["REC-77", "REFS", "REFS %s is named in neither EQUATION nor DOMAIN_CONDITION" % _show(n)])
if p:
return _refused(p)
r = F.form_dimension(eq, "equation", _tpk_dims(tfk))
if r[0] != "OK":
return _one("REC-78", "EQUATION", "EQUATION %s (T4)" % r[1])
tree = F.form_tree(eq, "equation")[1]
dv, stack = set(), [tree]
while stack:
n = stack.pop()
if type(n) is list:
if n and n[0] == "D":
dv.add(n[2])
stack.extend(n)
dofs = _dofs(tfk)
cons = tfk["TPK"].get("CONSTRAINT", [])
if type(cons) is not list: # not a list: none read, as TPC-PHP reads it
cons = []
for s in sorted(dv):
if s not in dofs:
p.append(["REC-79", "EQUATION", "EQUATION's D variable %s is not a DOF" % s])
for c in sorted(c.get("NAME") for c in cons if type(c) is dict and c.get("TARGET") == s
and type(c.get("NAME")) is str):
p.append(["REC-79", "EQUATION", "EQUATION's D variable %s is the TARGET of CONSTRAINT:%s" % (s, c)])
params = tfk["TPK"].get("PARAMETERS")
one = not (type(params) is dict and params.get("D_VARIABLES") == "SEVERAL")
tdv = F.d_variables(tfk["TPK"])
if one and len(set(tdv) | dv) > 1:
p.append(["REC-80", "EQUATION", "EQUATION's D variables %s and the TPK's %s are more than one under "
"D_VARIABLES ONE" % (", ".join(sorted(dv)) or "none", ", ".join(tdv) or "none")])
if p:
return _refused(p)
role = record.get("ROLE")
m = _EOM.match(role) if type(role) is str else None
if m and m.group(1) not in dofs:
return _one("REC-81", "ROLE", "ROLE EOM(%s): %s is not a DOF of the TPK" % (m.group(1), m.group(1)))
return ["OK"]
def domain_check(record, tfk):
if type(record) is not dict:
return _one("REC-01", "-", "the record is not an object")
if tfk is None or not _tfk_ok(tfk):
return _one("REC-39", "-", "the TFK is not readable")
if "DOMAIN_CONDITION" not in record:
return ["OK"]
dc = record["DOMAIN_CONDITION"]
refs = _members(record.get("REFS"))
dn = _names(dc, "predicate")
p = []
for n in dn:
if n not in refs:
p.append(["REC-82", "DOMAIN_CONDITION", "DOMAIN_CONDITION names %s, which is not in REFS" % n])
if "EQUATION" not in record:
for n in refs:
if n not in dn:
p.append(["REC-77", "REFS", "REFS %s is named in neither EQUATION nor DOMAIN_CONDITION" % _show(n)])
if p:
return _refused(p)
r = F.form_totality(dc, _tpk_dims(tfk))
if r[0] != "OK":
return _one("REC-83", "DOMAIN_CONDITION", "DOMAIN_CONDITION %s (T6)" % r[1])
p = [["REC-83", "DOMAIN_CONDITION", "DOMAIN_CONDITION %s (T6)" % q] for q in r[1]]
return _refused(p) if p else ["OK"]
# ---- 13 named gaps ---------------------------------------------------------------------------
GAP_TEMPLATES = {"IC2": "The DOF {d} is tied by no TrM entry (IC2).",
"UNDER": "The unknown DOF {d} is determined by no form (UNDER).",
"SR5_LOCUS": "The ASSUMPTIONS {ids} lie outside the closer record's closure in the withdrawal {t} (SR5).",
"SR7_ASSUMPTIONS": "The ASSUMPTIONS {ids} of an L record are contradicted by {t} (SR7).",
"SR7_DOMAIN": "{t} contradicts an L record whose a(r) is 0: a finding against the TFK DOMAIN (SR7)."}
def _gap(rid, source, against, trigger, landed, tps):
r = {"ID": rid, "CLASS": "NAMED_GAP", "GRADE": "NOT GRADED", "LANDED_BY": "TPX", "LANDED": landed,
"TPS": tps, "SOURCE": source, "AGAINST": sorted(against) if _members(against) == against else against,
"TRIGGER": trigger, "STATE": "OPEN"}
if type(source) is str and source in GAP_TEMPLATES and type(trigger) is str and _members(against) == against:
ids = ", ".join(sorted(against))
r["STATEMENT"] = GAP_TEMPLATES[source].replace("{d}", ids).replace("{ids}", ids).replace("{t}", trigger)
c = record_check(r, None, None, "LANDED")
if c[0] != "OK":
return c
r["DIGEST"] = record_digest(r)[1]
return ["OK", r]
def _ident_list(v):
return type(v) is list and all(_m(_IDENT, x) for x in v)
def gap_records(prefix, ic2, under, trigger, landed, tps):
for name, v in (("ic2", ic2), ("under", under)):
if not _ident_list(v):
return _bad_arg(name, v, "a list of identifiers")
if not (type(landed) is str and _utc_ok(landed)):
return _bad_arg("landed", landed, "a UTC time")
if not _m(_VERSION, tps):
return _bad_arg("tps", tps, "a version n.n.n")
p = []
for name, v in (("ic2", ic2), ("under", under)):
for d in sorted(set(v)):
if v.count(d) > 1:
p.append(["REC-89", "-", "%s repeats %s" % (name, d)])
if p:
return _refused(p)
out, n = [], 2
for src, lst in (("IC2", ic2), ("UNDER", under)):
for d in sorted(lst):
g = _gap("%s-%d" % (prefix if type(prefix) is str else "", n), src, [d], trigger, landed, tps)
if g[0] != "OK":
return g
out.append(g[1])
n += 1
return ["OK", out]
def gap_record(rid, source, against, trigger, landed, tps):
return _gap(rid, source, against, trigger, landed, tps)
def _closure(keyed, rid):
"""The ASSUMPTIONS closure of rid: its own and those of every record it
CITES, transitively; identifiers outside the register ignored."""
acc, seen, stack = set(), set(), [rid]
while stack:
cur = stack.pop()
if cur in seen or cur not in keyed:
continue
seen.add(cur)
r = keyed[cur]
acc.update(_members(r.get("ASSUMPTIONS")))
stack.extend(_members(r.get("CITES")))
return acc
def _trm_entries(tfk):
return dict((e["NAME"], e) for e in tfk["TrM"]["ENTRIES"])
def gap_view(gap, records, tfk):
keyed, lists, bad = _register(records, "records")
if bad:
return bad
if type(gap) is not dict or gap.get("CLASS") != "NAMED_GAP" or type(gap.get("AGAINST")) is not list:
return _bad_arg("gap", gap, "a NAMED_GAP record")
if tfk is None or not _tfk_ok(tfk):
return _one("REC-39", "-", "the TFK is not readable")
words = _words(keyed, lists)
against = _members(gap["AGAINST"])
ents = _trm_entries(tfk)
out = []
src = gap.get("SOURCE")
for rid in sorted(keyed):
if not _standing(words[rid]):
continue
r = keyed[rid]
hit = False
if src in ("IC2", "UNDER"):
if any(d in _members(r.get("REFS")) for d in against):
hit = True
named = _members(r.get("ENTRIES")) + ([r["QUANTITY"]] if type(r.get("QUANTITY")) is str else [])
if any(n in ents and ents[n].get("DOF") in against for n in named):
hit = True
elif src in ("SR5_LOCUS", "SR7_ASSUMPTIONS"):
hit = bool(_closure(keyed, rid) & set(against))
if hit:
out.append(rid)
return ["OK", out]
def gap_state(state, date, what):
if type(state) is not str or state not in ("OPEN", "SETTLED", "STANDING"):
return _one("REC-90", "-", "state %s is not OPEN, SETTLED or STANDING" % _v(state))
if state == "OPEN":
return ["OK", "OPEN"]
p = []
if not _day_ok(date):
p.append(["REC-50", "-", "date %s is not a date YYYY-MM-DD" % _vj(date)])
if state == "SETTLED":
if not (_rid_list(what) and what):
p.append(["REC-50", "-", "what %s is not a list of record identifiers" % _vj(what)])
elif type(what) is not str or Z._first_bad(what) is not None or Z._blank(what):
p.append(["REC-50", "-", "what %s is not a text" % _vj(what)])
if p:
return _refused(p)
if state == "SETTLED":
return ["OK", "SETTLED %s BY %s" % (date, ", ".join(sorted(what)))]
return ["OK", "STANDING %s BY %s" % (date, what)]
def eom0_open(records):
keyed, lists, bad = _register(records, "records")
if bad:
return bad
return ["OK", any(r["CLASS"] == "NAMED_GAP" and r.get("SOURCE") == "UNDER" and r.get("STATE") == "OPEN"
for r in keyed.values())]
# ---- 14 verification ---------------------------------------------------------------------------
def verification_order(target_push, verification_push, source_push):
def nat(x):
return _is_int(x) and x >= 0
if not (nat(target_push) and nat(verification_push) and (source_push is None or nat(source_push))):
return _bad_arg("pushes", [target_push, verification_push, source_push], "integers from 0")
if verification_push <= target_push:
return _one("REC-91", "-", "the VERIFICATION lands in its TARGET's push or before it")
if source_push is not None and source_push < target_push:
return ["INSPECT", "the SOURCE entry landed before the TARGET"]
return ["OK"]
def _at_list(at):
"""9.1's form [DOF, v, kind] per point, sorted by DOF; None when a point does
not normalize."""
out = []
for q in (at if type(at) is list else []):
if type(q) is not dict:
return None
n = U.normalize_measurement(q.get("VALUE"), "0", q.get("UNIT"))
if n[0] != "OK" or type(q.get("DOF")) is not str:
return None
out.append([q["DOF"], _cd(n[1]), n[4]])
return sorted(out)
def verification_difference(target, verification, k):
if not (type(target) is dict and target.get("CLASS") == "RESULT"
and all(type(target.get(f)) is str for f in ("ID", "QUANTITY", "VALUE", "UNIT"))
and type(verification) is dict and verification.get("CLASS") == "VERIFICATION"
and verification.get("TARGET") == target["ID"] and type(verification.get("EXTERNAL")) is dict):
return _one("REC-92", "-", "verification_difference reads a RESULT and a VERIFICATION of it")
if not (type(k) is str and Z.canon_decimal(k) == k and Z._is_pos_finite(k)):
return _bad_arg("k", k, "a positive canonical decimal")
ext = verification["EXTERNAL"]
p = []
if ext.get("QUANTITY") != target["QUANTITY"]:
p.append(["REC-93", "EXTERNAL.QUANTITY", "EXTERNAL.QUANTITY %s is not the TARGET's QUANTITY %s"
% (_v(ext.get("QUANTITY")), _show(target["QUANTITY"]))])
at_t, at_v = _at_list(target.get("AT")), _at_list(verification.get("AT"))
if at_t is None or at_v is None or [q[:2] for q in at_t] != [q[:2] for q in at_v]:
p.append(["REC-94", "AT", "AT is not the TARGET's AT"])
if p:
return _refused(p)
nt = U.normalize_measurement(target["VALUE"], target.get("U_C", "0") if target.get("REGIME") == "M" else "0",
target["UNIT"])
ne = U.normalize_measurement(ext.get("VALUE"), ext.get("U_C"), ext.get("UNIT"))
if nt[0] != "OK" or ne[0] != "OK":
return _one("REC-92", "-", "verification_difference reads a RESULT and a VERIFICATION of it")
if nt[3] != ne[3]:
return _one("REC-72", "-", "units of different dimension: %s and %s" % (nt[3], ne[3]))
if nt[4] != ne[4]:
return ["CONVENTION", "units of different kind: %s and %s" % (nt[4], ne[4])]
import math
val = nt[1] - ne[1]
uu = math.sqrt(nt[2] * nt[2] + ne[2] * ne[2])
if uu == 0.0:
return _one("REC-95", "-", "U is 0: both uncertainties are 0")
z = val / uu
got = {"VALUE": _cd(val), "U": _cd(uu), "ZETA": _cd(z)}
contra = S.mm_contradicts(nt[1], nt[2], ne[1], ne[2], float(k), 1.0)
pres = verification.get("DIFFERENCE")
pres = pres if type(pres) is dict else {}
for f in ("VALUE", "U", "ZETA"):
if Z.canon_decimal(pres.get(f)) != got[f]:
p.append(["REC-96", "DIFFERENCE." + f, "DIFFERENCE.%s %s is not the recomputed %s"
% (f, _v(pres.get(f)), got[f])])
if p:
return _refused(p)
return ["OK", got, contra]
# ---- 15 promotion, the entry view, successors ------------------------------------------------
FCLASS = ("F1", "F2", "F3")
def _floor(tfk):
tms = tfk.get("TMS")
params = tms.get("PARAMETERS") if type(tms) is dict else None
f = params.get("PROMOTION_FLOOR", "F2") if type(params) is dict else "F2"
return f if f in FCLASS else "F2"
def _words_arg(words):
return type(words) is dict and all(type(v) is str for v in words.values())
def _eligible(keyed, words, ents, floor, audits, rid):
r = keyed[rid]
if r["CLASS"] != "RESULT":
return ["NOT", ["not a RESULT"]]
reasons = []
w = words.get(rid, "NOT GRADED")
if w != "PROVISIONAL":
reasons.append("does not stand as PROVISIONAL (%s)" % _v(w))
if "DOMAIN" not in r:
reasons.append("DOMAIN is absent")
if "DOMAIN_CONDITION" not in r:
reasons.append("DOMAIN_CONDITION is absent")
entries = _members(r.get("ENTRIES"))
if not entries:
reasons.append("ENTRIES is absent or empty")
classes = [r.get("FALSIFIER_CLASS")] + [ents[e].get("FALSIFIER_CLASS") for e in entries if e in ents]
classes = [c for c in classes if c in FCLASS]
class_read = min(classes) if classes else "F1"
if class_read < floor:
reasons.append("FALSIFIER_CLASS %s is below PROMOTION_FLOOR %s" % (class_read, floor))
for c in sorted(_members(r.get("CITES"))):
if words.get(c, "NOT GRADED") != "ESTABLISHED":
reasons.append("cites %s, which is not ESTABLISHED" % c)
closure = set([rid])
seen, stack = set(), [rid]
while stack:
cur = stack.pop()
if cur in seen or cur not in keyed:
continue
seen.add(cur)
nxt = _members(keyed[cur].get("CITES")) + _members(keyed[cur].get("ASSUMPTIONS"))
closure.update(nxt)
stack.extend(nxt)
for a in sorted(closure & set(audits)):
reasons.append("an audit block is open on %s" % a)
if reasons:
return ["NOT", reasons]
return ["ELIGIBLE", class_read, floor]
def _promotion_args(records, words, tfk, open_audits):
keyed, lists, bad = _register(records, "records")
if bad:
return None, bad
if not _words_arg(words):
return None, _bad_arg("words", words, "a map of status words")
if tfk is None or not _tfk_ok(tfk):
return None, _one("REC-39", "-", "the TFK is not readable")
if not _rid_list(open_audits):
return None, _bad_arg("open_audits", open_audits, "a list of record identifiers")
return keyed, None
def promotion_eligible(records, words, tfk, open_audits, rid):
keyed, bad = _promotion_args(records, words, tfk, open_audits)
if bad:
return bad
if type(rid) is not str or rid not in keyed:
return _one("REC-52", "-", "record %s is not in the register" % _v(rid))
return _eligible(keyed, words, _trm_entries(tfk), _floor(tfk), open_audits, rid)
def _fixed_point(keyed, words, tfk, audits, candidates):
words = dict(words)
ents, floor = _trm_entries(tfk), _floor(tfk)
promoted = []
while True:
rnd = []
for rid in sorted(candidates):
if rid in [p[0] for p in promoted]:
continue
e = _eligible(keyed, words, ents, floor, audits, rid)
if e[0] == "ELIGIBLE":
rnd.append([rid, e[1], e[2]])
if not rnd:
return sorted(promoted)
for t in rnd:
words[t[0]] = "ESTABLISHED"
promoted.extend(rnd)
def promotion_pass(records, words, tfk, open_audits):
keyed, bad = _promotion_args(records, words, tfk, open_audits)
if bad:
return bad
return ["OK", _fixed_point(keyed, words, tfk, open_audits,
[rid for rid, r in keyed.items() if r["CLASS"] == "RESULT"])]
def promotion_record(promotion_id, landing_block, promoted):
if not _m(_RID, promotion_id):
return _bad_arg("promotion_id", promotion_id, "a record identifier")
if type(landing_block) is not str or Z._first_bad(landing_block) is not None or Z._blank(landing_block):
return _bad_arg("landing_block", landing_block, "a text")
ok_list = type(promoted) is list and all(
type(t) is list and len(t) == 3 and _m(_RID, t[0]) and t[1] in FCLASS and t[2] in FCLASS for t in promoted)
if ok_list and len(set(t[0] for t in promoted)) != len(promoted):
ok_list = False
if not ok_list:
return _bad_arg("promoted", promoted, "a list of [id, class_read, floor] triples")
if not promoted:
return _one("REC-97", "-", "a PROMOTION with nothing promoted is never written")
pr = sorted(promoted)
f = ["TPC-PROMOTE", "1", landing_block, len(pr)]
for t in pr:
f += t
return ["OK", [promotion_id, "PROMOTION", landing_block, pr, G.digest_fields(f)]]
def entry_view(records, words, tfk, entry):
keyed, lists, bad = _register(records, "records")
if bad:
return bad
if not _words_arg(words):
return _bad_arg("words", words, "a map of status words")
if tfk is None or not _tfk_ok(tfk):
return _one("REC-39", "-", "the TFK is not readable")
ents = _trm_entries(tfk)
if type(entry) is not str or entry not in ents:
return _one("REC-74", "-", "quantity %s is not an entry of the TrM" % _v(entry))
dof = ents[entry].get("DOF")
recs, gaps, vers, es = [], [], [], None
for rid in sorted(keyed):
r = keyed[rid]
c = r["CLASS"]
if c == "RESULT" and (entry in _members(r.get("ENTRIES")) or r.get("QUANTITY") == entry):
recs.append([rid, words.get(rid, "NOT GRADED")])
elif c == "NAMED_GAP" and r.get("SOURCE") in ("IC2", "UNDER") and r.get("STATE") == "OPEN" \
and dof in _members(r.get("AGAINST")):
gaps.append(rid)
elif c == "VERIFICATION" and type(r.get("EXTERNAL")) is dict and r["EXTERNAL"].get("QUANTITY") == entry:
vers.append(rid)
elif c == "ENTRY_STATUS" and r.get("ENTRY") == entry:
key = (r.get("LANDED") if type(r.get("LANDED")) is str else "", rid)
if es is None or key > es:
es = key
return ["OK", {"ENTRY": entry, "RECORDS": recs, "GAPS": gaps, "VERIFICATIONS": vers,
"ENTRY_STATUS": es[1] if es else None}]
def _zone_sub(tms, part, fields):
return Z._node([part, Z._sorted_entries(tms.get(part, []), fields)])
def reaudit_scope(src, dst):
for name, v in (("src", src), ("dst", dst)):
if not (type(v) is dict and all(k in v for k in ("D_TPK", "D_TRM", "TMS")) and type(v["TMS"]) is dict
and type(v["TMS"].get("PARAMETERS")) is dict and Z.tms_well_formed(v["TMS"])[0]):
return _bad_arg(name, v, "an object with D_TPK, D_TRM and TMS")
if src["D_TPK"] != dst["D_TPK"] or src["D_TRM"] != dst["D_TRM"]:
return ["OK", ["ALL"]]
a, b = src["TMS"], dst["TMS"]
pa, pb = a["PARAMETERS"], b["PARAMETERS"]
def canon_param(pm, name):
v = pm.get(name)
if name == "K":
return Z.canon_decimal(v) if type(v) is str else cj(v)
if name == "STEP_CONVENTION":
return Z._text(v) if type(v) is str else cj(v)
defaults = {"IC2_POLICY": "GAP", "PROMOTION_FLOOR": "F2", "STRUCTURE_CHECK": "ON"}
if name in defaults and name not in pm:
v = defaults[name]
return cj(v)
checks = set()
table = [("K", ["SR3", "SR8"]), ("N_X", ["SR6"]), ("STEP_CONVENTION", ["SR4", "SR5"]),
("PROMOTION_FLOOR", ["PROMOTION"]), ("STRUCTURE_CHECK", ["STRUCTURE", "EOM-0"]),
("S_MAX", ["T3"]), ("IC2_POLICY", ["IC2"])]
for name, adds in table:
if canon_param(pa, name) != canon_param(pb, name):
checks.update(adds)
cz = _zone_sub(a, "CONSTANTS", Z.TMSC_FIELDS[0]) != _zone_sub(b, "CONSTANTS", Z.TMSC_FIELDS[0])
yz = _zone_sub(a, "YARDSTICKS", Z.YARD_FIELDS[0]) != _zone_sub(b, "YARDSTICKS", Z.YARD_FIELDS[0])
if cz or yz:
checks.update(["SR4", "SR5"])
return ["OK", sorted(checks)]
def repromote_batch(src_records, src_words, dst_records, dst_words, tfk, open_audits):
"""functions 15.6 (D202.1; D215.2: a copy the successor withdraws is never a
candidate, RD-RS-49)."""
skeyed, _l, bad = _register(src_records, "src_records")
if bad:
return bad
if not _words_arg(src_words):
return _bad_arg("src_words", src_words, "a map of status words")
keyed, bad = _promotion_args(dst_records, dst_words, tfk, open_audits)
if bad:
return bad
cands = [rid for rid, r in keyed.items() if r["CLASS"] == "RESULT" and type(r.get("INHERITS")) is str
and src_words.get(r["INHERITS"]) == "ESTABLISHED"]
return ["OK", _fixed_point(keyed, dst_words, tfk, open_audits, cands)]
# ---- run_case: one corpus case, rendered in canonical JSON ------------------------------
def _a(args, k):
return args.get(k) if type(args) is dict else None
def _strike(fn):
return lambda a: fn(_a(a, "records"), _a(a, "words"), _a(a, "tfk"), _a(a, "quantity"))
FNS = {
"record_check": lambda a: record_check(_a(a, "record"), _a(a, "tfk"), _a(a, "setting"), _a(a, "stage")),
"record_preimage": lambda a: record_preimage(_a(a, "record")),
"record_digest": lambda a: record_digest(_a(a, "record")),
"record_view": lambda a: record_view(_a(a, "record"), _a(a, "view"), _a(a, "grade")),
"schema_json": lambda a: schema_json(),
"grade_values": lambda a: grade_values(_a(a, "class")),
"status_values": lambda a: status_values(_a(a, "class")),
"grade_now": lambda a: grade_now(_a(a, "register"), _a(a, "id")),
"status_words": lambda a: status_words(_a(a, "register")),
"is_standing": lambda a: is_standing(_a(a, "word")),
"status_word": lambda a: status_word(_a(a, "grade"), _a(a, "status")),
"grade_next": lambda a: grade_next(_a(a, "class"), _a(a, "grade"), _a(a, "event")),
"status_next": lambda a: status_next(_a(a, "class"), _a(a, "status"), _a(a, "event")),
"inherit_grade": lambda a: inherit_grade(_a(a, "class"), _a(a, "grade")),
"citation_check": lambda a: citation_check(_a(a, "record"), _a(a, "register"), _a(a, "entries"),
_a(a, "supersede_decided")),
"withdrawal_scope": lambda a: withdrawal_scope(_a(a, "register"), _a(a, "root"), _a(a, "still_stands")),
"withdrawal_preimage": lambda a: withdrawal_preimage(_a(a, "act")),
"withdrawal_record": lambda a: withdrawal_record(_a(a, "withdrawal_id"), _a(a, "act")),
"budget_check": lambda a: budget_check(_a(a, "record")),
"field_values": lambda a: field_values(_a(a, "record")),
"prec_decimal": lambda a: prec_decimal(_a(a, "s")),
"l_finding_pair": lambda a: l_finding_pair(_a(a, "original"), _a(a, "reproduction")),
"record_form_check": lambda a: record_form_check(_a(a, "record"), _a(a, "tfk")),
"domain_check": lambda a: domain_check(_a(a, "record"), _a(a, "tfk")),
"gap_records": lambda a: gap_records(_a(a, "prefix"), _a(a, "ic2"), _a(a, "under"), _a(a, "trigger"),
_a(a, "landed"), _a(a, "tps")),
"gap_record": lambda a: gap_record(_a(a, "id"), _a(a, "source"), _a(a, "against"), _a(a, "trigger"),
_a(a, "landed"), _a(a, "tps")),
"gap_view": lambda a: gap_view(_a(a, "gap"), _a(a, "records"), _a(a, "tfk")),
"gap_state": lambda a: gap_state(_a(a, "state"), _a(a, "date"), _a(a, "what")),
"eom0_open": lambda a: eom0_open(_a(a, "records")),
"verification_order": lambda a: verification_order(_a(a, "target_push"), _a(a, "verification_push"),
_a(a, "source_push")),
"verification_difference": lambda a: verification_difference(_a(a, "target"), _a(a, "verification"),
_a(a, "k")),
"promotion_eligible": lambda a: promotion_eligible(_a(a, "records"), _a(a, "words"), _a(a, "tfk"),
_a(a, "open_audits"), _a(a, "id")),
"promotion_pass": lambda a: promotion_pass(_a(a, "records"), _a(a, "words"), _a(a, "tfk"),
_a(a, "open_audits")),
"promotion_record": lambda a: promotion_record(_a(a, "promotion_id"), _a(a, "landing_block"),
_a(a, "promoted")),
"entry_view": lambda a: entry_view(_a(a, "records"), _a(a, "words"), _a(a, "tfk"), _a(a, "entry")),
"reaudit_scope": lambda a: reaudit_scope(_a(a, "src"), _a(a, "dst")),
"repromote_batch": lambda a: repromote_batch(_a(a, "src_records"), _a(a, "src_words"),
_a(a, "dst_records"), _a(a, "dst_words"), _a(a, "tfk"),
_a(a, "open_audits")),
# tpc_strike 0.3.0 (D196.1) and tpc_eom 0.2.0 (D198.1 (b)), whose corpora tpc_record answers
"conditions_of": lambda a: S.conditions_of(_a(a, "records"), _a(a, "id")),
"comparison_set": _strike(S.comparison_set),
"sr8_groups": _strike(S.sr8_groups),
"settlement_check": lambda a: _eom().settlement_check(_a(a, "tfk"), _a(a, "records"), _a(a, "words"),
_a(a, "naming")),
}
def _eom():
import tpc_eom
return tpc_eom
def run_case(fn, args):
"""The rendered result of one corpus case. An unknown fn raises: a corpus
error, not a vector."""
if fn not in FNS:
raise ValueError("fn")
return cj(FNS[fn](args))