expressir 2.4.35-arm-linux → 2.4.37-arm-linux

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (66) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/conformance.yml +34 -0
  3. data/.gitignore +3 -0
  4. data/Gemfile +1 -1
  5. data/TODO.sota/00-overview.md +57 -0
  6. data/TODO.sota/01-type-checking.md +115 -0
  7. data/TODO.sota/02-evaluator.md +86 -0
  8. data/TODO.sota/03-conformance.md +53 -0
  9. data/TODO.sota/04-lsp.md +70 -0
  10. data/TODO.sota/05-interop.md +55 -0
  11. data/TODO.sota/06-dialects.md +57 -0
  12. data/TODO.sota/07-reference-artifacts.md +55 -0
  13. data/docs/_guides/interop/index.adoc +25 -0
  14. data/docs/_guides/interop/json-schema.adoc +88 -0
  15. data/docs/_guides/interop/owl-rdf.adoc +87 -0
  16. data/docs/_guides/reference/checker-notes.adoc +55 -0
  17. data/docs/_guides/reference/checker.adoc +36 -0
  18. data/docs/_guides/reference/evaluator.adoc +40 -0
  19. data/docs/_guides/reference/grammar-crosswalk.adoc +299 -0
  20. data/docs/_guides/reference/index.adoc +22 -0
  21. data/docs/_guides/reference/lsp.adoc +41 -0
  22. data/docs/_guides/reference/p21.adoc +30 -0
  23. data/docs/_guides/reference/shtolo.adoc +36 -0
  24. data/exe/expressir-lsp +6 -0
  25. data/expressir.gemspec +2 -2
  26. data/lib/expressir/3.2/expressir_core.so +0 -0
  27. data/lib/expressir/3.3/expressir_core.so +0 -0
  28. data/lib/expressir/3.4/expressir_core.so +0 -0
  29. data/lib/expressir/4.0/expressir_core.so +0 -0
  30. data/lib/expressir/cli.rb +18 -0
  31. data/lib/expressir/commands/check.rb +3 -1
  32. data/lib/expressir/commands/export_json_schema.rb +29 -0
  33. data/lib/expressir/commands/export_owl.rb +26 -0
  34. data/lib/expressir/commands/validate.rb +3 -0
  35. data/lib/expressir/commands.rb +2 -0
  36. data/lib/expressir/conformance/trend.rb +100 -0
  37. data/lib/expressir/conformance.rb +8 -0
  38. data/lib/expressir/eval/environment.rb +83 -0
  39. data/lib/expressir/eval/expression.rb +610 -0
  40. data/lib/expressir/eval/interpreter.rb +316 -0
  41. data/lib/expressir/eval/rules.rb +88 -0
  42. data/lib/expressir/eval/values.rb +74 -0
  43. data/lib/expressir/eval.rb +17 -0
  44. data/lib/expressir/express/builders/expression_builder.rb +9 -1
  45. data/lib/expressir/express/checker.rb +779 -2
  46. data/lib/expressir/express/dialect.rb +44 -0
  47. data/lib/expressir/express/formatters/expressions_formatter.rb +4 -1
  48. data/lib/expressir/express/parser.rb +9 -4
  49. data/lib/expressir/express.rb +1 -0
  50. data/lib/expressir/interop/json_schema.rb +195 -0
  51. data/lib/expressir/interop/owl.rb +216 -0
  52. data/lib/expressir/interop.rb +13 -0
  53. data/lib/expressir/lsp/document.rb +226 -0
  54. data/lib/expressir/lsp/server.rb +184 -0
  55. data/lib/expressir/lsp/workspace.rb +402 -0
  56. data/lib/expressir/lsp.rb +11 -0
  57. data/lib/expressir/mapping/refpath.rb +65 -4
  58. data/lib/expressir/reference/grammar_crosswalk.rb +122 -0
  59. data/lib/expressir/reference/grammar_crosswalk.yml +800 -0
  60. data/lib/expressir/reference/note_catalog.rb +54 -0
  61. data/lib/expressir/reference.rb +10 -0
  62. data/lib/expressir/version.rb +1 -1
  63. data/lib/expressir.rb +4 -0
  64. data/lib/tasks/conformance.rake +27 -0
  65. data/lib/tasks/reference.rake +25 -0
  66. metadata +50 -4
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7bf5328141c79747cc48057b4b6e2d8d9624c6f76d1c5a6f932604944c4fdbb4
4
- data.tar.gz: 9d5b7da5c9bc1ee108c50a438c637fe59812674f97636bd66ba9c71e6a761861
3
+ metadata.gz: 338318df9cdcf3b183a219e8a46e52dbbe16af84ad476f9897eb4934b56079a8
4
+ data.tar.gz: 8e4209a9594213d74dcac93943d120e29254548d3b3e9c4d448af2bc9d7ea203
5
5
  SHA512:
6
- metadata.gz: 1fe348c37227ec8b93c3ac20e1aa8459154d3db5fb6a2ef6f33d5a645a3248ac74fe9c74d53e39d9b962aedf4bb618bdb62db45ff9c520a596879ff325ca697f
7
- data.tar.gz: 34cb46fa2a5f262700606c57675c3070c2fed553552643b1102e29142a7878aa5038f4c03f0b469d8c25e46b4456c249287e0a3ce2b0d710a3deb12347d6b72a
6
+ metadata.gz: a3a92a528498b5f2e44f6d36fb791c265e4d322ad5c99c59a9203bf6cad43d73070b795d196c4cd48cbf19dfd2b155e94238294117c08242c5fa5f9fc3875810
7
+ data.tar.gz: a692014272ade39551293efde4b6af3bde833406243c27531e70f13e933c43ec566141439560f9ecff3a3c3a9589d84e79f95ad8aee8511b88582f5ce2e63a11
@@ -0,0 +1,34 @@
1
+ name: conformance
2
+
3
+ on:
4
+ schedule:
5
+ - cron: "0 3 * * *"
6
+ workflow_dispatch:
7
+
8
+ jobs:
9
+ conformance:
10
+ runs-on: ubuntu-latest
11
+ steps:
12
+ - uses: actions/checkout@v4
13
+ - uses: actions/checkout@v4
14
+ with:
15
+ repository: metanorma/iso-10303-stepmod
16
+ path: stepmod
17
+ sparse-checkout: data/resources
18
+ - uses: ruby/setup-ruby@v1
19
+ with:
20
+ ruby-version: "3.4"
21
+ bundler-cache: true
22
+ - name: Round-trip conformance over the stepmod corpus
23
+ run: bundle exec rake conformance
24
+ env:
25
+ STEPMOD_DIR: ${{ github.workspace }}/stepmod
26
+ - name: Trended property metrics
27
+ run: bundle exec rake conformance:trend
28
+ env:
29
+ STEPMOD_DIR: ${{ github.workspace }}/stepmod
30
+ - name: Upload the trend artifact
31
+ uses: actions/upload-artifact@v4
32
+ with:
33
+ name: conformance-trend
34
+ path: conformance-trend.json
data/.gitignore CHANGED
@@ -53,3 +53,6 @@ Cargo.lock
53
53
  /lib/expressir/expressir_core.bundle
54
54
  /lib/expressir/expressir_core.so
55
55
  /lib/expressir/expressir_core.dll
56
+
57
+ # nightly conformance trend artifact (rake conformance:trend)
58
+ /conformance-trend.json
data/Gemfile CHANGED
@@ -6,7 +6,7 @@ gemspec
6
6
 
7
7
  gem "canon"
8
8
  gem "irb"
9
- gem "lutaml-model", "0.8.76"
9
+ gem "lutaml-model", "0.8.114"
10
10
  gem "openssl", "~> 3.0"
11
11
  gem "rake"
12
12
  gem "rake-compiler"
@@ -0,0 +1,57 @@
1
+ # TODO.sota — the SOTA program (vision)
2
+
3
+ Expressir is the open compiler-infrastructure layer for ISO 10303
4
+ information models: a faithful, programmable model of EXPRESS
5
+ (ISO 10303-11), the standard transformations between the language's
6
+ normative forms (Annex G longform, Part 21, Part 28/XML, XSD, MIM
7
+ mappings), and CI-grade guarantees for the standards corpus. The
8
+ eengine-parity program (TODO.eengine, done) made it the ecosystem's
9
+ reference toolchain. This program takes it to SOTA — the point where
10
+ Expressir is not just the ecosystem's parser but its *engine*, its
11
+ developer platform, and its interoperability hub.
12
+
13
+ ## Execution order and status
14
+
15
+ | n | task | tier | status |
16
+ |---|------|------|--------|
17
+ | 01 | static type checking of expressions | semantics | done (tiers 1-6; :check_expr_operand_domain/:check_expr_initializer_type/:check_expr_assignment_type/:check_expr_return/:check_expr_where_boolean/:check_expr_unique_duplicate; corpus 261-slice 0 notes) |
18
+ | 02 | evaluator — run EXPRESS (functions, WHERE rules over populations) | execution | done (value model + ch.12 expressions, ch.13 interpreter + builtin set A, instance-aware eval over P21 populations, Rules.check with SELF; eengine validate broken — mutation-sweep differential) |
19
+ | 03 | conformance (Part 21 conformance corpus, property-based grammar harness, G.1.7 closure) | rigor | done (P21 annex corpus + negative ladder-exclusion gate; generated round-trip harness + nightly corpus tier + rake conformance; every Annex G rule audited) |
20
+ | 04 | LSP + incremental platform | platform | done (stdio LSP 3.17 server + `expressir-lsp` binary; Checker diagnostics; USE/alias definition, references, hover; cross-schema rename verified by SchemaCompare+Formatter; full reparse — expr-rs incremental API is the measured gap) |
21
+ | 05 | interop exports (JSON Schema, OWL/RDF) | interop | done (JSON Schema 2020-12 + OWL/Turtle exporters, CLI commands, decision guides; Annex H population consumer round-trip; SRL corpus tier; UML/XMI optional not shipped) |
22
+ | 06 | dialect awareness (1994 vs 2004, amendments) | semantics | done (Dialect normalize + parser/checker flag; :check_dialect_2004_only/:check_dialect_deprecated/:check_dialect_mixed; audited 2004 change list fixture with live examples; both-dialect corpus tier) |
23
+ | 07 | reference artifacts (grammar cross-walk, published guides) | reference | done (250-rule generated cross-walk with divergence classes + sync spec; trended nightly metrics artifact; checker/p21/shtolo/evaluator/lsp guides with generated note-id catalog) |
24
+
25
+ Dependency shape: 02 consumes 01's type tiers loosely (interleave by
26
+ tier when a check needs execution semantics). 04 scaffolds
27
+ independently; its diagnostics richness grows with 01. 03/05/06/07 are
28
+ independent.
29
+
30
+ ## Sources
31
+
32
+ - ISO 10303-11 text: `~/src/mn/iso-10303-11/sources/sections/`
33
+ (expressions ch.12, statements ch.13, data types ch.8, rules 9.x,
34
+ Annex A/B productions).
35
+ - Part 21: `~/src/mn/iso-10303-detached-docs/sources/iso-10303-21/`.
36
+ - stepmod corpus: `~/src/mn/iso-10303-stepmod/data/` (SRL/BOM/module
37
+ manifests; the 265-file <250KB resource slice is the fast
38
+ differential set).
39
+ - eengine oracle: `~/bin/eengine` — schema ops need
40
+ `-MODE RESOURCE -STEPMOD ~/src/mn/iso-10303-stepmod`;
41
+ --validate/--xref are broken on bare p11 (never build on them).
42
+ - leptris runs FFI-fallback on this box; no spec may assume the native
43
+ engine.
44
+
45
+ ## Standing rules
46
+
47
+ - Where eengine and ISO 10303-11 disagree, the standard wins; classify
48
+ the divergence in the differential output, never copy silently.
49
+ - Every ported check keeps its eengine note id (where one exists) and
50
+ cites its ISO 10303-11 clause.
51
+ - Semantics land in Ruby; parse-adjacent and graph-scale hot paths may
52
+ land in expressir-rs (gated per task).
53
+ - All model (de)serialization goes through lutaml-model mappings —
54
+ never hand-rolled to_h/from_h.
55
+ - Specs use real model instances, never doubles; rubocop clean is a
56
+ merge gate; the suite runs in chunks on this box (full single-process
57
+ runs OOM).
@@ -0,0 +1,115 @@
1
+ # 01 — Static type checking of expressions
2
+
3
+ The Checker resolves references (TODO.eengine/02/03); this task gives
4
+ it TYPES: full static checking of the expression and statement
5
+ language per ISO 10303-11 ch.12/13/8/9.
6
+
7
+ ## Tiers (each ships)
8
+
9
+ 1. Clause map: expression grammar ↔ ISO 10303-11 12.x productions ↔
10
+ eeng check.lisp expression methods, with the typing rule for each
11
+ (operand domains, result type). Table in this file before code.
12
+ 2. Operator domains: arithmetic on the NUMBER family, logical on
13
+ BOOLEAN/LOGICAL, relational polymorphism, string/binary ops,
14
+ aggregate operators (INDEX/QUERY/SUM and friends).
15
+ 3. Aggregates: element types through indexing/slicing, bounds vs
16
+ literals where static, nested aggregates.
17
+ 4. SELECT/ENUM membership: assignment compatibility, IN-of-enum,
18
+ `x : t := expr` compatibility in CONSTANT/LOCAL/DERIVE initializers.
19
+ 5. Statement flow: assignment compatibility, IF/CASE selector types,
20
+ REPEAT control types, function RETURN type vs return statements.
21
+ 6. WHERE/UNIQUE structural checks: expression boolean-ness, UNIQUE
22
+ label uniqueness, rule FOR-entity containment for referenced
23
+ attributes.
24
+
25
+ ## Interface
26
+
27
+ `Checker#check_expression_types(schema)` emitting notes with eeng ids
28
+ where check.lisp has them, own ids (`:check_expr_*`) citing clauses
29
+ where it does not (mirror TODO.eengine/02's SELF-redeclaration
30
+ precedent).
31
+
32
+ ## Acceptance
33
+
34
+ - Corpus-clean over the 265-file resource slice (foreign-blindness
35
+ rule applies: unresolvable names skipped, not flagged).
36
+ - eeng differential for the expression checks eengine actually
37
+ implements; standard wins on divergence.
38
+
39
+ ## Clause map (tier 2 — operator domains)
40
+
41
+ Model operator vocabulary: `BinaryExpression` constants (ADDITION,
42
+ SUBTRACTION, MULTIPLICATION, REAL_DIVISION, INTEGER_DIVISION, MODULO,
43
+ EXPONENTIATION, COMBINE, EQUAL, NOT_EQUAL, LESS_THAN,
44
+ LESS_THAN_OR_EQUAL, GREATER_THAN, GREATER_THAN_OR_EQUAL, IN, LIKE,
45
+ INSTANCE_EQUAL, INSTANCE_NOT_EQUAL, AND, OR, XOR);
46
+ `UnaryExpression` (PLUS, MINUS, NOT); `Interval`.
47
+
48
+ | operator | ISO 10303-11:2004 clause | operand domains | result |
49
+ |----------|--------------------------|-----------------|--------|
50
+ | unary `+` `-` (PLUS/MINUS) | 12.1 | numeric | numeric |
51
+ | `not` (NOT) | 12.4 | logical | logical |
52
+ | `+` (ADDITION) | 12.1 add; 12.5.2 string concat; 12.3.2 binary concat; 12.6 union (table 17 admits aggregate+element singletons on either side) | num,num; str,str; bin,bin; agg,agg; agg,elem; elem,agg | num / str / bin / agg |
53
+ | `-` (SUBTRACTION) | 12.1; 12.6 difference (table 18: aggregate left, element singleton right) | num,num; agg,agg; agg,elem | num / agg |
54
+ | `*` (MULTIPLICATION) | 12.1; 12.6 intersection (both operands aggregates) | num,num; agg,agg | num / agg |
55
+ | `/` (REAL_DIVISION) | 12.1 | numeric | real |
56
+ | `**` (EXPONENTIATION) | 12.1 | numeric | num (int if both int) |
57
+ | `div` `mod` | 12.1 | numeric (reals truncated) | integer |
58
+ | `and` `or` `xor` | 12.4 | logical,logical | logical |
59
+ | `=` `<>` | 12.2.1 | comparable: numeric, logical, string, binary, enum; aggregates and entities also admitted | logical |
60
+ | `<` `<=` `>` `>=` | 12.2.1; 12.6 subset/superset | ordering families only (numeric, logical, string, binary, non-extensible enum); if either side is aggregate, subset/superset semantics (agg,agg) | logical |
61
+ | `:=:` `:<>>:` (INSTANCE_EQUAL/NOT_EQUAL) | 12.2.2 | instance types (entity, aggregate) | logical |
62
+ | `in` (IN) | 12.2.3 | rhs aggregate; lhs compatible with rhs base | logical |
63
+ | `like` (LIKE) | 12.2.5 | string,string | logical |
64
+ | `\|\|` (COMBINE) | 12 intro (precedence table note) | complex entity construction (entity operands) | entity |
65
+ | interval | 12.2.4 | low/item/high mutually comparable (numeric, string, enum) | logical |
66
+
67
+ Static type lattice: `:integer :real :number :logical :boolean :string
68
+ :binary :enum :aggregate :entity :select :generic :unknown`. Inference:
69
+ literals direct; declared types via the schema's types (SimpleReference
70
+ → find_type → underlying_type recursion); references via the enclosing
71
+ declaration's environment (constants, parameters, variables, entity
72
+ attributes); enum members are AttributeReferences whose base resolves
73
+ to an enumeration TYPE; function calls resolve to the function's
74
+ return type.
75
+
76
+ Flagging rule: a domain violation is noted only when BOTH operand
77
+ types are known (`:generic` and `:unknown` never flag) — the corpus
78
+ never produces false positives.
79
+
80
+ eengine divergence: kernel/check.lisp's expression check-p11 methods
81
+ traverse structurally only — eengine performs NO operator-domain
82
+ typing. These checks therefore carry new `:check_expr_operand_domain`
83
+ ids citing their clauses; there is no eengine note id to keep.
84
+
85
+ - Tier 1 clause map: done (above).
86
+ - Tier 2 operator domains: done — `Checker#check_expression_types`,
87
+ note id `:check_expr_operand_domain` with the clause in the message;
88
+ spec `spec/expressir/express/checker_expression_types_spec.rb`
89
+ (9 examples); corpus-clean over the 265-file resource slice (255
90
+ scanned, 0 notes) after clause 12.6 table 17's union-with-singleton
91
+ was admitted.
92
+ - Tier 3 indexing: done — IndexReference resolves to the aggregate's
93
+ base element (12.6.1/12.3.1/12.5.1); index positions numeric
94
+ (production 236); the difference operator admits an element
95
+ singleton right operand with an aggregate left (12.6 table 18).
96
+ - Tier 4 initializer compatibility: done — CONSTANT/DERIVE/LOCAL
97
+ initializers checked against their declared types (12.11): numeric
98
+ widening integer→real→number, logical/boolean family, enum identity
99
+ via declaration resolution, entity specialization via
100
+ supertype_closure, select-accepting slots, aggregate slots. Corpus
101
+ clean (261 schemas, 0 notes).
102
+ - Tier 5 statement flow: done — assignment compatibility (13.2.1),
103
+ if conditions logical (13.5), repeat controls (13.6), return
104
+ compatibility + procedure-return restriction (13.7).
105
+ - Tier 6 WHERE/UNIQUE structure: done — WHERE expressions logical
106
+ (9.4), duplicate UNIQUE labels (9.4); rule FOR-entity containment
107
+ remains covered by bound_identifiers' deliberate over-approximation.
108
+
109
+ ## Status: done
110
+
111
+ Engine finding: parsing identical source after many interleaved
112
+ check-parse cycles in one process can diverge (attr type arrives as a
113
+ SimpleReference instead of the builtin node) — a parsanol parse-cache
114
+ issue, worked around in the initializer spec by checking in a fresh
115
+ subprocess; separate investigation owed.
@@ -0,0 +1,86 @@
1
+ # 02 — The evaluator: run EXPRESS
2
+
3
+ The SOTA-defining capability: execute the language. Functions and
4
+ WHERE rules evaluated over Part 21 populations — the line between a
5
+ toolchain that reads EXPRESS and one that runs it. No open
6
+ implementation has this (eengine's --validate is the cautionary tale).
7
+
8
+ ## Steps (each ships)
9
+
10
+ 1. Value model + expression core: IMMUTABLE value classes (NUMBER
11
+ family, STRING/BINARY, LOGICAL, enumeration, aggregate arrays as
12
+ frozen structs), literal → value, pure expression evaluation
13
+ (operators per 01's domain table), environmental lookup through a
14
+ Scope chain. Interface: `Expressir::Eval.expression(ast, env)`.
15
+ 2. Function interpreter: parameter binding (incl. VAR semantics),
16
+ LOCALs with initializers, IF/CASE/REPEAT/compound/RETURN, ESCAPE/
17
+ SKIP, recursion with depth guard. Builtin set A (math/string,
18
+ ISO 10303-11 15.x): SIN..BLENGTH class.
19
+ 3. Instance-aware builtins: TYPEOF, USEDIN, EXISTS, QUERY (lazy
20
+ binding), attribute access through the model's computation layer
21
+ (Entity#all_explicit_attributes, supertype_closure, redeclaration
22
+ resolution — TODO.eengine/03), complex-instance (GROUP) access.
23
+ Builtin set B (instance/aggregate: SIZEOF, HIBOUND..).
24
+ 4. WHERE-rule checking: `Expressir::Rules.check(population, schema)`
25
+ → per-instance, per-rule results with the failing subexpression
26
+ path. Wraps the P21 Validator's structural tiers (TODO.eengine/06)
27
+ into semantic validation.
28
+ 5. Differential: eengine where its validate answers; property-based
29
+ round-trips otherwise (evaluate a rule against a corpus population,
30
+ cross-check pure-Ruby reference impls for builtins). If hot loops
31
+ dominate, drop the evaluation core to expressir-rs (option B) —
32
+ gate on measured need, not speculation.
33
+
34
+ ## Design constraints
35
+
36
+ - The evaluator consumes the SAME model the parser produces — no
37
+ parallel AST.
38
+ - Errors are notes/exceptions carrying node + path, never exit.
39
+ - Determinism: no reliance on hash order (corpus QA depends on it).
40
+
41
+ ## Acceptance
42
+
43
+ - Annex-level worked examples (function bodies from ISO 10303-11)
44
+ evaluate to the documented results.
45
+ - WHERE evaluation over the Annex H population: every rule provably
46
+ passes; a seeded-fault population fails the intended rule only.
47
+
48
+ ## Status: in progress
49
+
50
+ - Step 1 (value model + expression core): done — `Expressir::Eval`
51
+ (Values/Environment/Expression autoloads): ISO 10303-11 ch.12
52
+ arithmetic with result typing, logical tables (12.4), relational
53
+ orderings (12.2.1), IN (12.2.3), LIKE (12.2.5, table 11 patterns),
54
+ intervals, aggregates (tables 16-18 incl. element singletons), and
55
+ QUERY. Indeterminate (`?`) propagates per clause. Function calls
56
+ raise until step 2. Spec `spec/expressir/eval/expression_spec.rb`.
57
+ - Grammar bug fixed en route: `factor`'s `** rhs` capture was dropped
58
+ by the builder (`2 ** 3 + 1` parsed as `2 + 1`); build_factor now
59
+ emits EXPONENTIATION.
60
+ - Step 2 (function interpreter + builtin set A): done — functions
61
+ with parameter/local binding, assignment (incl. indexed), IF/CASE,
62
+ WHILE/UNTIL/increment REPEAT with SKIP and env-carrying ESCAPE,
63
+ RETURN (falling off the end is an error per 13.7), recursion.
64
+ Builtins: ABS SIN COS TAN ASIN ACOS ATAN ATAN2 SQRT EXP LOG LOG2
65
+ LOG10 ODD LENGTH BLENGTH SIZEOF HIINDEX LOINDEX HIBOUND LOBOUND
66
+ EXISTS NVL VALUE, plus PI/E/CONST_E constants. NOTE: the logical
67
+ value symbols live in Values::LOGICAL_* constants — bare :true/
68
+ :false literals get rewritten to booleans by Lint/BooleanSymbol.
69
+ - Step 3 (instance-aware evaluation): done — Values::Instance over
70
+ P21 population records with P21-data-to-value conversion (arrays →
71
+ aggregates, `#id` → instances, enums/binaries mapped); attribute
72
+ access through the Environment's Part 21 slot mapping (inherited
73
+ then own, same order as the P21 validator, incl. complex records);
74
+ TYPEOF (unqualified + schema-qualified names), USEDIN, EXISTS.
75
+ Fixtures: spec/fixtures/eval/population.{exp,p21}.
76
+ - Step 4 (WHERE-rule checking): done — `Expressir::Eval::Rules.check`
77
+ evaluates every instance's entity domain rules with SELF and the
78
+ mapped attributes bound (bare names and SELF\attr both resolve);
79
+ statuses pass/fail/unknown/error; a rule is violated only when it
80
+ evaluates false (9.4), indeterminate is not a violation.
81
+ - Step 5 (differential): done as substituted — eengine's --validate
82
+ aborts on bare p11 (TODO.eengine/06 step 3 finding), so the
83
+ differential face is the mutation sweep: corrupting one instance
84
+ attribute fails exactly the intended rule and no other (rules spec).
85
+
86
+ ## Status: done
@@ -0,0 +1,53 @@
1
+ # 03 — Conformance: measure against the standard's own yardstick
2
+
3
+ ## Parts (each ships)
4
+
5
+ 1. Part 21 conformance corpus: extract the conformance-requirement
6
+ examples from the detached ISO 10303-21 source (annexes + body
7
+ worked examples) into fixtures; a gate spec runs the P21
8
+ reader/validator over every positive fixture (clean) and every
9
+ negative fixture (the intended note id, only).
10
+ 2. Grammar-conformance harness: property-based generation from the
11
+ ISO 10303-11 Annex A/B productions (finite fragments first:
12
+ declarations, then expressions). Invariants: parse→format→parse
13
+ idempotence, formatter output re-parses, ill-formed input rejected
14
+ with a diagnostic (no silent recovery on the strict faces).
15
+ Promoted to a nightly CI tier.
16
+ 3. G.1.7 closure: the BASED_ON enumeration-chain corpus fixture —
17
+ the last unaudited Annex G rule (annex-g-rules.md table tail).
18
+
19
+ ## Acceptance
20
+
21
+ - `rake conformance` (or the spec tier) green; negatives fail with
22
+ exactly the intended note, positives with zero notes.
23
+
24
+ ## Status: done
25
+
26
+ - Part 1 (Part 21 conformance corpus): the ISO 10303-21 detachment
27
+ examples are extracted to `spec/fixtures/p21/conformance/`:
28
+ annex-e-0/1 (named data sections), annex-h-0 (the worked exchange
29
+ structure, 13 instances), annex-i-0/1 (ANCHOR/REFERENCE), annex-j-0
30
+ (constants). `spec/expressir/p21/parser_conformance_spec.rb` parses
31
+ every positive fixture with zero notes and asserts the intended
32
+ failure for the forms this ladder does not implement yet (annex E
33
+ named sections and `..`, annex I ANCHOR/REFERENCE, annex J
34
+ constants/`....` and STEP-21-ED2 extended literals) — they fail
35
+ loudly, not silently. Malformed negatives are rejected.
36
+ - Part 2 (grammar-conformance harness):
37
+ `spec/expressir/express/formatter_roundtrip_conformance_spec.rb`
38
+ asserts parse → format → parse structural idempotence over a fixture
39
+ slice, over generated fragments (Annex A/B production levels:
40
+ expression → simple_expression → term, declarations, arrays, QUERY,
41
+ unary/logical/relational forms, deterministically seeded), and
42
+ rejection of terminators removed from generated input. The
43
+ `EXPRESSIR_CONFORMANCE=1` tier round-trips the full stepmod resource
44
+ slice (<250 KB; 266 files on this box) and `rake conformance` runs it; the
45
+ nightly `conformance.yml` workflow checks out the sparse stepmod
46
+ corpus. En route the generator found a formatter bug: nested unary
47
+ minus formatted as `--x`, which EXPRESS reads as a comment; unary
48
+ operands are now parenthesized. Generator lesson: relational
49
+ operators are non-associative, and reserved words (e.g. `VALUE`)
50
+ cannot name parameters.
51
+ - Part 3 (G.1.7 closure): the BASED_ON enumeration-chain example is in
52
+ `shtolo_annex_g_rule_audit_spec.rb`; every Annex G rule now has a
53
+ live worked example and the audit table has no unaudited tail.
@@ -0,0 +1,70 @@
1
+ # 04 — LSP + incremental platform
2
+
3
+ Make Expressir IDE-grade. The resolution/checking machinery is the
4
+ hard part and it exists; this task wires it to a language server.
5
+
6
+ ## Parts (each ships)
7
+
8
+ 1. Scaffold: `expressir-lsp` (solo binary or gem — decide by the
9
+ metanorma toolchain's needs) speaking stdio LSP 3.17: initialize,
10
+ diagnostics push, document symbols.
11
+ 2. Diagnostics: Checker notes as publishDiagnostics with model-node
12
+ → LSP range mapping (NodePositionIndex exists).
13
+ 3. Navigation: definition (ScopeResolver + the USE/REFERENCE ladder +
14
+ alias map), references, document/rename — rename updates the
15
+ model in place and re-emits via the Formatter (the compare/patch
16
+ engine provides the structural diff safety net).
17
+ 4. Hover: PrettyFormatter excerpt of the target declaration + its
18
+ remarks.
19
+ 5. Incremental: document-open/parse via the expressir-rs core path
20
+ when available (parsanol incremental machinery); fallback full
21
+ reparse. Cold-open target: SRL-scale schema set interactive on
22
+ this box.
23
+
24
+ ## Acceptance
25
+
26
+ - A live editor session on the stepmod corpus: diagnostics on open,
27
+ go-to-definition across a USE ladder, rename across two schemas
28
+ with the diff verified by SchemaCompare.
29
+
30
+ ## Status: done
31
+
32
+ - Part 1 (scaffold): `exe/expressir-lsp` ships in the gem
33
+ (`executables`), speaking JSON-RPC 2.0 over stdio with LSP 3.17
34
+ framing (`Content-Length`), lifecycle (initialize/shutdown/exit) and
35
+ full-sync text documents (didOpen/didChange/didSave/didClose).
36
+ `Expressir::Lsp::{Document,Workspace,Server}` are autoloaded from
37
+ `lib/expressir/lsp.rb`.
38
+ - Part 2 (diagnostics): `Workspace#diagnostics` runs the Checker over
39
+ the open documents, maps notes through NodePositionIndex/LineMap to
40
+ byte→UTF-16 ranges, and publishes `textDocument/publishDiagnostics`.
41
+ Parse failures publish a diagnostic at the reported line/char and
42
+ keep the last good tree for navigation. Cross-document unresolved
43
+ notes are suppressed when another open document supplies the
44
+ referenced schema or interfaced declaration (the checker sees one
45
+ file at a time).
46
+ - Part 3 (navigation): definition resolves declaration tokens,
47
+ local references, the USE/REFERENCE ladder and its alias map
48
+ (`colour AS hue` jumps to `colour`); references walks every open
49
+ document and interface item; rename computes minimal edits for the
50
+ declaration and every reference across schemas, then applies them in
51
+ memory, verifies each document reparses, runs SchemaCompare to
52
+ reject any removal other than the renamed declaration, and re-emits
53
+ the renamed schema through the Formatter to prove it formats. Rename
54
+ keeps aliases untouched: only the imported name at the interface
55
+ item and usages written with the original name change.
56
+ - Part 4 (hover): `Express::PrettyFormatter` excerpt in a markdown
57
+ `express` code fence (remarks included by the formatter).
58
+ - Part 5 (incremental): changes reparse through the native/core parser
59
+ path; identical text is a no-op and checker results are memoized
60
+ across requests for the same document set. There is no incremental
61
+ parse API in the expressir-rs core to call, so true content-
62
+ incremental reparse remains a measured gap: cold open of the 186 KB
63
+ geometric_model_schema on this loaded box takes ~9.4 s (document
64
+ parse) and ~0.03 s for the symbol index. Recorded for an
65
+ expressir-rs incremental API when it exists; semantics stay Ruby.
66
+ - Specs: `spec/expressir/lsp/document_spec.rb` (positions, UTF-16,
67
+ tokens, symbols, parse-failure retention) and
68
+ `spec/expressir/lsp/server_spec.rb` (capabilities, diagnostics,
69
+ symbols, USE/alias definition, references, hover, cross-schema
70
+ rename verified by SchemaCompare and Formatter) — 14 examples.
@@ -0,0 +1,55 @@
1
+ # 05 — Interop exports: EXPRESS as a source
2
+
3
+ Expressir as the interchange point between EXPRESS and the rest of the
4
+ modeling world. These mappings are contributions (no ISO mapping
5
+ exists) — every decision is documented in the output module's docs.
6
+
7
+ ## Parts (each ships)
8
+
9
+ 1. JSON Schema export: entities → object schemas (attribute order,
10
+ OPTIONAL → nullable, aggregates → arrays with bounds where
11
+ expressible, SELECT → anyOf, ENUM → enum). Subtype handling: the
12
+ closure flattened into allOf groups. Documented decisions file.
13
+ 2. OWL/RDF export: the ifcOWL lineage generalized to arbitrary
14
+ EXPRESS — entities → classes, supertype closure → subclass axes,
15
+ attributes → properties with domain/range from the model, SELECT
16
+ → union semantics, WHERE rules NOT exported (documented scope
17
+ limit). Cite ifcOWL/EXPRESS→OWL literature in the decisions file.
18
+ 3. Optional tier: UML/XMI export.
19
+
20
+ ## Acceptance
21
+
22
+ - Export the SRL corpus; a consumer round-trip (validate exported
23
+ JSON Schema against a Part 21-derived JSON serialization of the
24
+ Annex H population).
25
+
26
+ ## Status: done
27
+
28
+ - Part 1 (JSON Schema): `Expressir::Interop::JsonSchema` renders a
29
+ schema as JSON Schema 2020-12 — entities as object definitions
30
+ composed with `allOf` over `SUBTYPE OF` parents, explicit attributes
31
+ in declaration order, OPTIONAL attributes nullable and out of
32
+ `required`, aggregates as arrays with declared bounds, SELECT as
33
+ `anyOf` (BASED_ON folded), ENUMERATION as a string enum (BASED_ON
34
+ folded), defined types through to their underlying primitive.
35
+ Derived/inverse attributes, WHERE/UNIQUE rules and constants are
36
+ documented exclusions. CLI: `expressir export-json-schema`.
37
+ Decisions: `docs/_guides/interop/json-schema.adoc`.
38
+ - Part 2 (OWL/RDF): `Expressir::Interop::Owl` renders Turtle — entities
39
+ as `owl:Class` with `rdfs:subClassOf` per supertype, explicit
40
+ attributes as object/datatype properties with domain and range,
41
+ SELECT as an `owl:unionOf` equivalent class, ENUMERATION items as
42
+ individuals, primitive defined types as `rdfs:Datatype` with XSD
43
+ equivalences; aggregates range over `rdf:List`. WHERE/UNIQUE bodies,
44
+ cardinalities and aggregate bounds are documented scope limits. CLI:
45
+ `expressir export-owl`. Decisions and the ifcOWL lineage citations:
46
+ `docs/_guides/interop/owl-rdf.adoc`.
47
+ - Part 3 (UML/XMI): explicitly the optional tier; not shipped.
48
+ - Acceptance: the Annex H population is serialized to JSON in Part 21
49
+ slot order (supertype attributes first, `$`/`*` null, references
50
+ expanded with a cycle guard) and validated against the exported
51
+ entity definitions with an independent consumer validator in
52
+ `spec/expressir/interop/json_schema_spec.rb` — 13 instances, zero
53
+ errors; dropping a required attribute fails. A gated
54
+ `EXPRESSIR_CONFORMANCE` tier exports every schema in the stepmod
55
+ resource slice (<250 KB; >200 schemas).
@@ -0,0 +1,57 @@
1
+ # 06 — Dialect awareness
2
+
3
+ EXPRESS the standard has editions; Expressir should speak them
4
+ explicitly.
5
+
6
+ ## Parts (each ships)
7
+
8
+ 1. Dialect flag: parser + checker accept `dialect: :"10303-11:1994" |
9
+ :"10303-11:2004"` (default 2004). Gating: EXTENSIBLE/BASED_ON/
10
+ GENERIC_ENTITY are 2004-only (1994 corpora exist in legacy
11
+ resources); 1994 rejects them with a dialect note, 2004 accepts.
12
+ 2. Amendment tracking: the Amd1 change list as a source document;
13
+ edition-sensitive checker notes (`:check_dialect_*`).
14
+ 3. Corpus pass: run both dialects over the resource slice; mixed
15
+ dialects in one file produce a clear diagnostic.
16
+
17
+ ## Acceptance
18
+
19
+ - A 1994-era resource schema parses clean under 1994, flags its
20
+ non-1994 constructs under 2004-diagnostics mode.
21
+
22
+ ## Status: done
23
+
24
+ - Part 1 (dialect flag): `Expressir::Express::Dialect` normalizes the
25
+ `:"10303-11:1994"` / `:"10303-11:2004"` editions (also "1994",
26
+ "2004", full strings) and rejects anything else with
27
+ `InvalidOptionError`. `Parser.from_file/from_files/from_exp` accept
28
+ `dialect:` (default 2004) and validate it; the grammar parses the
29
+ union of both editions so a model can be inspected under either.
30
+ `Checker.new(repository, dialect:)` emits the edition notes:
31
+ under 1994, `:check_dialect_2004_only` (error) for EXTENSIBLE,
32
+ BASED_ON, GENERIC_ENTITY and SUBTYPE_CONSTRAINT declarations; under
33
+ 2004, `:check_dialect_deprecated` (warning) for the edition-1 inline
34
+ subtype-constraint syntax that Annex K deprecates in favour of
35
+ SUBTYPE_CONSTRAINT. Notes are emitted only when a dialect is passed,
36
+ so the default checker report is unchanged.
37
+ - Part 2 (amendment tracking): the audited change list is a checked-in
38
+ source document at
39
+ `spec/fixtures/dialects/iso-10303-11-2004-changes.yaml`, with the
40
+ clause each construct comes from (8.4.1, 8.4.2, clause 9, Annex K,
41
+ Annex G) and a live example per entry.
42
+ `spec/expressir/express/dialect_spec.rb` asserts every change entry's
43
+ declared note fires under 1994 (or 2004 for the deprecated syntax),
44
+ that no note id is outside `Dialect::NOTE_IDS`, and that the 2004
45
+ examples are accepted under 2004. A 1994-era plain schema produces
46
+ no notes under either dialect.
47
+ - Part 3 (corpus pass): a gated `EXPRESSIR_CONFORMANCE` tier runs both
48
+ dialects over the stepmod resource slice (266 files <250 KB), and
49
+ `:check_dialect_mixed` gives one clear diagnostic when a file
50
+ combines 2004-only declarations with edition-1 inline
51
+ subtype-constraint syntax. Installations without the corpus skip the
52
+ tier.
53
+ - Limitation recorded: the 2004 text grammar is effectively a superset
54
+ of 1994, so the only edition-1-only signals available to a text
55
+ checker are the Annex K deprecations; EXPRESS-G notation and the
56
+ enumeration-ordering semantics deprecated by Annex K have no text
57
+ form to flag.
@@ -0,0 +1,55 @@
1
+ # 07 — Reference artifacts: the citable Expressir
2
+
3
+ The academic endgame — make the implementation provable and citable.
4
+
5
+ ## Parts (each ships)
6
+
7
+ 1. Grammar cross-walk: rule-by-rule table, parsanol grammar rule ↔
8
+ ISO 10303-11 Annex A/B production, with divergences (extensions,
9
+ recoveries) classified — rendered into docs/ and kept in sync by a
10
+ spec that fails when a grammar rule lacks a row.
11
+ 2. Property-based corpus testing as a published nightly tier (03's
12
+ harness, trended: parse rate, diagnostic rate, idempotence rate).
13
+ 3. Published guides: one doc per subsystem (checker + note-id
14
+ catalog, shtolo + annex-g-rules linkage, p21, evaluator, lsp),
15
+ generated where possible from the model (note-id catalog from the
16
+ Checker's constants).
17
+
18
+ ## Acceptance
19
+
20
+ - docs/ builds with the cross-walk and guides; the sync spec is part
21
+ of the default suite.
22
+
23
+ ## Status: done
24
+
25
+ - Part 1 (grammar cross-walk): `Expressir::Reference::GrammarCrosswalk`
26
+ reads the audited mapping in
27
+ `lib/expressir/reference/grammar_crosswalk.yml` and renders
28
+ `docs/_guides/reference/grammar-crosswalk.adoc`. All 250 parsanol
29
+ rules are accounted for: 207 aligned with the same-named Annex A
30
+ production (CamelCase normalized to snake_case), 8 decomposed into
31
+ PEG sub-rules, 30 literal operator/delimiter tokens, 4 lexical
32
+ whitespace/delimiter rules and 1 PEG-internal keyword guard. The 12
33
+ Annex A productions without a dedicated rule are listed and
34
+ classified (remark productions are handled by
35
+ RemarkScanner/RemarkAttacher; the rest are PEG lookaheads or
36
+ character classes). The Annex A snapshot lives at
37
+ `spec/fixtures/reference/annex-ab-productions.txt`. The spec fails
38
+ when a grammar rule lacks a row, a row names a missing rule/status,
39
+ an Annex production is unaccounted for, or the published guide is
40
+ stale; regenerate with `rake reference:crosswalk`.
41
+ - Part 2 (trended nightly tier): `Expressir::Conformance::Trend`
42
+ measures a corpus slice — file count, parse rate, format-idempotence
43
+ rate, diagnostic rate (Checker notes per parsed file), error/warning counts and per-note-id counts —
44
+ and writes a JSON artifact. `rake conformance:trend` runs it over the
45
+ stepmod resource slice; the nightly `conformance.yml` workflow runs
46
+ it after the round-trip tier and uploads `conformance-trend.json`.
47
+ Spec: `spec/expressir/conformance/trend_spec.rb`.
48
+ - Part 3 (published guides): `docs/_guides/reference/` now carries an
49
+ index plus subsystem guides — checker (with the generated note-id
50
+ catalog, `rake reference:notes`), Part 21, SHTOLO/Annex G (linked to
51
+ `annex-g-rules.md` and the audit spec), evaluator, and the language
52
+ server. The two generated pages are checked for staleness by
53
+ `spec/expressir/reference/*_spec.rb`, which run in the default suite.
54
+ The docs site build (`.github/workflows/docs.yml`) covers the new
55
+ pages.
@@ -0,0 +1,25 @@
1
+ ---
2
+ title: Interop Exports
3
+ nav_order: 12
4
+ has_children: true
5
+ ---
6
+
7
+ = Interop Exports
8
+
9
+ == Purpose
10
+
11
+ Expressir can render an EXPRESS schema into JSON Schema and OWL/RDF.
12
+ No ISO mapping exists for either rendering: these are contributions of
13
+ the Expressir project, and the decisions behind them are documented in
14
+ the child guides so consumers can rely on stable semantics.
15
+
16
+ == Commands
17
+
18
+ [source,shell]
19
+ ----
20
+ expressir export-json-schema path/to/schema.exp -o schema.json
21
+ expressir export-owl path/to/schema.exp -o schema.ttl
22
+ ----
23
+
24
+ The Ruby API is available as `Expressir::Interop::JsonSchema` and
25
+ `Expressir::Interop::Owl`.