expressir 2.4.3 → 2.4.4

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 (77) hide show
  1. checksums.yaml +4 -4
  2. data/.cargo/config.toml +3 -0
  3. data/.github/workflows/rust-ext.yml +41 -0
  4. data/.github/workflows/verify_remarks.yml +33 -0
  5. data/.gitignore +6 -0
  6. data/.rubocop.yml +4 -0
  7. data/.rubocop_todo.yml +1 -0
  8. data/Cargo.toml +7 -0
  9. data/README.adoc +65 -0
  10. data/Rakefile +12 -0
  11. data/TODO.parity-ee/00-overview.md +58 -0
  12. data/TODO.parity-ee/01-annex-g-rule-extraction.md +41 -0
  13. data/TODO.parity-ee/02-eeng-algorithm-inventory.md +8 -0
  14. data/TODO.parity-ee/03-eeng-oracle-harness.md +33 -0
  15. data/TODO.parity-ee/04-shtolo-concatenate.md +44 -0
  16. data/TODO.parity-ee/05-shtolo-longform-flatten.md +53 -0
  17. data/TODO.parity-ee/06-interface-scheduling-parity.md +47 -0
  18. data/TODO.parity-ee/07-semantic-checks-port.md +35 -0
  19. data/TODO.parity-ee/08-pretty-roundtrip-gate.md +27 -0
  20. data/TODO.parity-ee/09-smrl-index-and-listing.md +22 -0
  21. data/TODO.parity-ee/10-interface-dot-graph.md +21 -0
  22. data/TODO.parity-ee/11-import-eeng-tests.md +79 -0
  23. data/TODO.parity-ee/parity-matrix.md +108 -0
  24. data/exe/expressir +6 -0
  25. data/ext/expressir_core/Cargo.toml +24 -0
  26. data/ext/expressir_core/extconf.rb +30 -0
  27. data/ext/expressir_core/src/lib.rs +446 -0
  28. data/lib/expressir/cli.rb +49 -4
  29. data/lib/expressir/commands/check.rb +56 -0
  30. data/lib/expressir/commands/expand.rb +26 -0
  31. data/lib/expressir/commands/fix.rb +33 -0
  32. data/lib/expressir/commands/flatten.rb +29 -0
  33. data/lib/expressir/commands/parity_inputs.rb +85 -0
  34. data/lib/expressir/commands/validate.rb +22 -0
  35. data/lib/expressir/commands.rb +5 -0
  36. data/lib/expressir/express/builder_registry.rb +11 -2
  37. data/lib/expressir/express/builders/entity_decl_builder.rb +17 -7
  38. data/lib/expressir/express/builders/subtype_constraint_builder.rb +3 -1
  39. data/lib/expressir/express/builders/type_builder.rb +8 -0
  40. data/lib/expressir/express/builders/unique_rule_builder.rb +8 -1
  41. data/lib/expressir/express/checker.rb +458 -0
  42. data/lib/expressir/express/concatenator.rb +79 -0
  43. data/lib/expressir/express/core.rb +66 -0
  44. data/lib/expressir/express/formatter.rb +25 -3
  45. data/lib/expressir/express/formatters/data_types_formatter.rb +2 -2
  46. data/lib/expressir/express/formatters/declarations_formatter.rb +1 -0
  47. data/lib/expressir/express/formatters/remark_formatter.rb +30 -6
  48. data/lib/expressir/express/formatters/statements_formatter.rb +7 -1
  49. data/lib/expressir/express/interface_dot.rb +105 -0
  50. data/lib/expressir/express/listing.rb +174 -0
  51. data/lib/expressir/express/model_traversal.rb +42 -0
  52. data/lib/expressir/express/node_position_index.rb +18 -4
  53. data/lib/expressir/express/parser.rb +242 -4
  54. data/lib/expressir/express/pretty_gate.rb +61 -0
  55. data/lib/expressir/express/refs_overlay.rb +47 -0
  56. data/lib/expressir/express/remark_attacher.rb +122 -10
  57. data/lib/expressir/express/remark_overlay.rb +86 -0
  58. data/lib/expressir/express/schema_plain_source_formatter.rb +11 -0
  59. data/lib/expressir/express/schema_source_formatter.rb +5 -5
  60. data/lib/expressir/express/self_schema_reference.rb +126 -0
  61. data/lib/expressir/express/shtolo.rb +522 -0
  62. data/lib/expressir/express.rb +13 -0
  63. data/lib/expressir/model/concerns.rb +15 -1
  64. data/lib/expressir/model/declarations/entity.rb +6 -1
  65. data/lib/expressir/model/declarations/interface.rb +13 -0
  66. data/lib/expressir/model/declarations/schema.rb +74 -23
  67. data/lib/expressir/model/indexes/item_graph.rb +135 -0
  68. data/lib/expressir/model/indexes.rb +1 -0
  69. data/lib/expressir/model/model_element.rb +30 -1
  70. data/lib/expressir/model/remark_info.rb +20 -1
  71. data/lib/expressir/model/remark_placement.rb +15 -2
  72. data/lib/expressir/model/repository.rb +8 -2
  73. data/lib/expressir/model.rb +2 -1
  74. data/lib/expressir/version.rb +1 -1
  75. data/lib/tasks/verify_remarks.rake +9 -0
  76. data/rakelib/verify_remarks.rake +16 -0
  77. metadata +42 -2
@@ -0,0 +1,79 @@
1
+ # 11 — Import the Express Engine test suite
2
+
3
+ **Status: NOT STARTED**
4
+
5
+ ## Goal
6
+
7
+ Fully import eeng's tests — their release-suite scripts, the bug
8
+ corpus, and the reference documents that define pass/fail — as
9
+ executable RSpec feature specs and regression fixtures in expressir.
10
+ Nothing in their suite should remain unrepresented.
11
+
12
+ ## Inventory (verified 2026-09-21)
13
+
14
+ eeng has no formal test framework (`tests/` is a README describing an
15
+ unrealized Makefile suite); the suite is:
16
+
17
+ ### CLI drivers — `scripts/*.sh` (14)
18
+
19
+ | script | drives | our target operation |
20
+ |---|---|---|
21
+ | `qual-arm.sh`, `qual-mod.sh` | per-module qualification loop (arm/mim × every qualify.sh op) | 04, 05, 07-10 end-to-end specs |
22
+ | `qual-res.sh` | same over `qualify-res.txt` resources | idem, resource side |
23
+ | `pretty-test.sh` | `--pretty` ap209/ap210/ap242 × {arm,mim} × {shortform,longform} | 08 formatter specs |
24
+ | `ap210.sh` | the 2.5MB long-form giant, pretty × 4 modes | 08 scale spec |
25
+ | `test-tt.sh`, `test-tt1.sh` | `--xml` arm/mim shortform + Saxon HTM conversion + regenerate-exp compare | 09 (XML chain) |
26
+ | `build-xml.sh` | XML builds over the whole `modules.lst` + `resources.lst` | 09 corpus spec |
27
+ | `test.sh` | ad-hoc resource XML/HTM driver (kinematic schema) | 09 |
28
+ | `scan-res.lisp` | resource corpus scan | 07 check corpus |
29
+ | `kevin.sh`, `veronique.sh`, `copy-mod-xml.sh` | person-named scratch drivers | triage: keep as scenarios or drop with rationale |
30
+
31
+ Each script's eeng CLI matrix (`-mode {arm,mim}_{shortform,longform,
32
+ concatenated}` `-stepmod` `-schema` `--pretty/--xml/--flat/--dot
33
+ --check`) maps 1:1 onto our planned operations — the flag matrix
34
+ becomes our spec matrix.
35
+
36
+ ### Bug corpus — `docs/issues/`
37
+
38
+ - `bug39-Greedy-Parens.txt` — **shtolo fixture**: contains "results
39
+ of old shtolo and stepcode exppp" vs "results of new shtolo"
40
+ outputs; doubles as a differential vs stepcode exppp.
41
+ - `bug40-Pretty-Expressions.txt` — pretty-printer expression cases.
42
+ Both become regression specs with the literal inputs embedded.
43
+
44
+ ### Reference documents — `docs/` (define pass/fail)
45
+
46
+ - `shtolo_converting_STEP_short_listings_to_annotated_listings_libes.pdf`
47
+ — the SHTOLO paper (short listings → annotated listings); required
48
+ reading for 01/05 semantics.
49
+ - BNF grammars: `iso-10303-11--2004{,-raw}.bnf`, `iso-10303-14*.bnf`,
50
+ **`iso-10303-21--{1994,2002}.bnf`** (Part 21!), `xml.bnf`,
51
+ `patch_schema.bnf` — grammar cross-checks (05's 1994 profile; the
52
+ Part 21 BNFs feed the future p21 parser).
53
+ - `express_model.dtd`, `module.dtd` — the DTDs their XML emits (09's
54
+ structural contract).
55
+ - `qualify.txt`, `interfaces.txt`, `interface-notes.pdf` —
56
+ qualification + interface semantics notes.
57
+ - `00check.txt` (repo root) — check.lisp design notes → 07.
58
+
59
+ ## Steps
60
+
61
+ 1. Copy the durable assets into the repo: scripts verbatim under
62
+ `spec/fixtures/eeng/scripts/` (provenance header), bug corpus under
63
+ `spec/fixtures/eeng/issues/`, DTDs + BNFs under
64
+ `spec/fixtures/eeng/grammar/` (license is MIT-style per eeng
65
+ headers — verify and record attribution).
66
+ 2. Translate the CLI matrix into RSpec feature specs
67
+ (`spec/expressir/eeng_parity/`): one describe per driver family,
68
+ one example per (schema × mode × operation), corpus-scaled ones
69
+ tagged `smrl`.
70
+ 3. Port bug39/bug40 as literal regression specs now (they need only
71
+ parser + formatter, both of which exist).
72
+ 4. Extend 03's oracle harness to emit/refresh the expected outputs
73
+ these specs consume.
74
+
75
+ ## Acceptance
76
+
77
+ Every driver row and both bugs have a corresponding spec (or a
78
+ documented drop rationale); `smrl`-tagged parity suite green against
79
+ the 03 golden corpus.
@@ -0,0 +1,108 @@
1
+ # eeng ↔ expressir parity matrix (2026-09-21)
2
+
3
+ Reference: `~/src/external/exp-engine-engine` (eengine-5.0.20-Beta1).
4
+ Audited files: kernel/{find-declaration,find-entity,find-type,
5
+ resolve-interface,resolve-object,resolve-ref,references,schema-search,
6
+ interfaces,compute-inheritance,supertypes,derive-attrs,
7
+ inverse-attrs,check,compare,dot-graph}.lisp; plugins/p11/{schedule-*
8
+ ,resolve-declaration,cleanup-interface,wo-*}.lisp.
9
+
10
+ ## 1. Name resolution ladder (kernel/find-declaration.lisp:125-172)
11
+
12
+ eeng's schema-scope restriction ladder for `find-declaration`:
13
+
14
+ ```
15
+ nil = :use-from
16
+ :local → own declarations + constants only
17
+ :use-from → :local OR :use-only
18
+ :use-only → walk USE FROM interfaces only, transitively, each in
19
+ :use-from scope (so USE visibility is transitive)
20
+ :all-iface → :local OR all interfaces (USE and REFERENCE), transitively
21
+ cycle guard → *schema-path*: schema already on path ⇒ nil
22
+ ```
23
+
24
+ Per-kind restrictions (`resolve-interface.lisp:30-66`):
25
+ - USE FROM resolves **type + entity only**.
26
+ - REFERENCE FROM resolves constant/entity/function/procedure/type.
27
+ - Unresolved resource ⇒ hard **error** `"Resource '~A' not found"`.
28
+
29
+ **expressir today:** `ModelElement#find` climbs parent scopes through
30
+ `children_by_id`; `Schema#children` = `interfaced_items + safe_children`
31
+ where `interfaced_items` is **single-hop** (own interfaces only, no
32
+ transitivity), and the resolver never errors on unresolved resources.
33
+ Our `find` also treats REFERENCE and USE identically (no visibility
34
+ tiering), and has no cycle guard (schema interface cycles loop until
35
+ the memo fills — the memo makes this safe in practice, not by design).
36
+
37
+ **Gaps:** (a) transitive USE visibility — item visible through a chain
38
+ of USEs is invisible to our single-hop lookup; (b) USE/REFERENCE
39
+ visibility tiering absent; (c) no unresolved-resource diagnostics;
40
+ (d) no cycle guard by construction. Fix tracked in TODO 06.
41
+
42
+ ## 2. Load scheduling (plugins/p11/schedule-*.lisp)
43
+
44
+ eeng: `schedule-load` (two-pass parse), `schedule-interfaces`
45
+ (one method per session kind: double-stepmod keeps trl/ref schema
46
+ sets — schema1/schemata1 = trial, schemata2 = reference), recursing
47
+ per interface symbol → find/load the foreign file via the stepmod
48
+ directory layout, then `schedule-resolve` → `resolve-declaration` per
49
+ schema with `*schema?*` dynamic scope (:trl-schema / :ref-schema).
50
+
51
+ **expressir today:** `from_files` (batch + compiled set), repository
52
+ flat over all files; no trl/ref duality (we never compare two
53
+ specification universes); interface targets located by name at resolve
54
+ time through the repository (single-hop, see §1).
55
+
56
+ **Verdict:** structural difference, not a bug — our repository model
57
+ subsumes their session model for everything except `--compare`
58
+ (two-universe schema comparison, kernel/compare.lisp, unported; TODO
59
+ 07 candidate).
60
+
61
+ ## 3. Entity computations (kernel/compute-inheritance.lisp,
62
+ derive-attrs.lisp, inverse-attrs.lisp, supertypes.lisp,
63
+ explicit-attrs.lisp)
64
+
65
+ eeng **materializes** inherited attributes onto entities:
66
+ explicit attributes from the full supertype closure, derived attrs,
67
+ inverse attrs, subtype/supertype links — used by `--validate`,
68
+ population checks, and SHTOLO's generalized-attribute handling
69
+ (Annex G.2.6 needs exactly these).
70
+
71
+ **expressir today:** nothing materialized; consumers walk the
72
+ supertype closure themselves (our item graph does this for edges, not
73
+ attributes).
74
+
75
+ **Gaps:** inherited-explicit-attribute flattening, derived/inverse
76
+ closures. Needed by 05 (G.2.6 select synthesis) and 07 (checks).
77
+ TODO 05 scope.
78
+
79
+ ## 4. Semantic checks (kernel/check.lisp, 835 lines)
80
+
81
+ check-p11 methods per node type: schema-level (interface resource
82
+ existence, per-kind resolution rules), entity (attribute redeclare
83
+ compatibility, inverse target/entity/inverse-attr existence, derive
84
+ aggregation rules), function/procedure/rule parameters, subtype
85
+ constraints, types. Check severity/notes via check-notes.lisp.
86
+
87
+ **expressir today:** `RepositoryValidator` (interface-level only) +
88
+ `validate ascii` CLI. Gap tracked in TODO 07.
89
+
90
+ ## 5. Writers (plugins/p11/wo-*.lisp)
91
+
92
+ | eeng | semantics | expressir |
93
+ |---|---|---|
94
+ | wo-pretty | normal encoding, 2-space indent | Formatter clean mode — parity specs landed (bug39/40) |
95
+ | wo-flat | `:flat` encoding, downcased symbols, typeof-option banner | 05 (Annex G — note eeng's flat KEEPS 2003 constructs: partial) |
96
+ | wo-list | declaration listing | 09 |
97
+ | wo-smrl-xml | `<concatenated_express_file_content_list>` index for longform/concatenated modes | 09 |
98
+ | wo-lexp | lisp S-expression dump | not planned (internal debugging) |
99
+
100
+ ## 6. Bugs found by this audit
101
+
102
+ - **expressir#373** — Ruby parse path corrupts small USE FROM schemas
103
+ (<~70B, non-monotonic; core path correct). Found by the concatenator
104
+ fixtures; `dependency_resolver_spec` failures on main are the same
105
+ root cause. Fixed by the #371/#370 stack.
106
+ - eeng partial Annex G coverage — documented in
107
+ `spec/fixtures/shtolo/annex-g-rules.md` (eeng is a partial oracle;
108
+ the annex is the contract).
data/exe/expressir CHANGED
@@ -1,5 +1,11 @@
1
1
  #!/usr/bin/env ruby
2
2
  # encoding: UTF-8
3
3
 
4
+ # Ruby block-buffers stdout when it is not a terminal, so progress from a long
5
+ # run reaches a CI log only when the buffer fills or the process exits. A job
6
+ # that is working then looks hung. Set here rather than in the library, which
7
+ # runs inside other people's processes and does not own their streams.
8
+ $stdout.sync = true
9
+
4
10
  require_relative "../lib/expressir"
5
11
  Expressir::Cli.start(ARGV)
@@ -0,0 +1,24 @@
1
+ [package]
2
+ name = "expressir_core"
3
+ version = "0.1.0"
4
+ edition = "2021"
5
+ publish = false
6
+
7
+ [lib]
8
+ name = "expressir_core"
9
+ crate-type = ["cdylib"]
10
+
11
+ [dependencies]
12
+ magnus = { version = "0.9" }
13
+ expressir-rs = { git = "https://github.com/lutaml/expressir-rs", branch = "main" }
14
+ serde_json = "1.0"
15
+
16
+ # Standalone workspace: the repo root workspace owns crates/, the ext
17
+ # is built by rb_sys with its own cargo invocation.
18
+ [workspace]
19
+
20
+ # Same magnus/rb-sys pins as parsanol-ruby (Ruby 4.0 support).
21
+ [patch.crates-io]
22
+ magnus = { git = "https://github.com/matsadler/magnus", rev = "9407bd9" }
23
+ magnus-macros = { git = "https://github.com/matsadler/magnus", rev = "9407bd9" }
24
+ rb-sys = { git = "https://github.com/oxidize-rb/rb-sys.git", rev = "daa12b6521a0c442a705d7f9a929029119aab1d6" }
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "mkmf"
4
+
5
+ # The Rust extension only builds against MRI's C API. Other engines
6
+ # fall back to the pure-Ruby parser path and must install cleanly.
7
+ if RUBY_ENGINE != "ruby" || ENV["EXPRESSIR_CORE"] == "0"
8
+ File.write("Makefile", dummy_makefile("").to_s)
9
+ warn "expressir: skipping the Rust core extension on #{RUBY_ENGINE}"
10
+ exit 0
11
+ end
12
+
13
+ # Pure-Ruby fallback when no Rust toolchain is available: the gem must
14
+ # install and keep working without the native core
15
+ # (Expressir::Core::NATIVE_AVAILABLE stays false). Prebuilt platform
16
+ # gems are the follow-up so cargo-less MRI installs get binaries too.
17
+ unless find_executable("cargo")
18
+ File.write("Makefile", dummy_makefile("").to_s)
19
+ warn "expressir: cargo not found — skipping the Rust core extension " \
20
+ "(Expressir::Core falls back to the Ruby parser)"
21
+ exit 0
22
+ end
23
+
24
+ require "rb_sys/mkmf"
25
+
26
+ create_rust_makefile("expressir/expressir_core") do |r|
27
+ r.profile = ENV.fetch("RB_SYS_CARGO_PROFILE", :dev).to_sym
28
+ r.use_stable_api_compiled_fallback = true
29
+ r.force_install_rust_toolchain = false
30
+ end