expressir 2.4.29-arm-linux-musl

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 (426) hide show
  1. checksums.yaml +7 -0
  2. data/.cargo/config.toml +3 -0
  3. data/.github/scripts/test_installed_gem.rb +34 -0
  4. data/.github/workflows/codeql.yml +35 -0
  5. data/.github/workflows/docs.yml +99 -0
  6. data/.github/workflows/links.yml +100 -0
  7. data/.github/workflows/native-gems.yml +201 -0
  8. data/.github/workflows/rake.yml +19 -0
  9. data/.github/workflows/release.yml +37 -0
  10. data/.github/workflows/rust-ext.yml +41 -0
  11. data/.github/workflows/stress.yml +36 -0
  12. data/.github/workflows/validate_schemas.yml +48 -0
  13. data/.github/workflows/verify_remarks.yml +33 -0
  14. data/.gitignore +55 -0
  15. data/.hound.yml +3 -0
  16. data/.rspec +2 -0
  17. data/.rubocop.yml +18 -0
  18. data/.rubocop_todo.yml +297 -0
  19. data/CHANGELOG.md +159 -0
  20. data/Gemfile +18 -0
  21. data/README.adoc +2783 -0
  22. data/Rakefile +41 -0
  23. data/TODO.bugs/01-stale-transformer-autoload.md +39 -0
  24. data/TODO.bugs/02-parser-class-instance-vars.md +36 -0
  25. data/TODO.bugs/03-builder-mutable-state.md +43 -0
  26. data/TODO.bugs/04-formatter-public-send-dispatch.md +53 -0
  27. data/TODO.bugs/05-anonymous-formatter-subclass.md +45 -0
  28. data/TODO.bugs/06-collection-registry-single-source.md +53 -0
  29. data/TODO.bugs/07-require-relative-cleanup.md +42 -0
  30. data/TODO.bugs/08-require-expressir-in-commands.md +34 -0
  31. data/TODO.bugs/09-parser-split.md +53 -0
  32. data/TODO.bugs/10-to-s-override.md +42 -0
  33. data/TODO.bugs/11-parser-class-variables.md +39 -0
  34. data/TODO.bugs/12-marker-modules-vs-registry.md +64 -0
  35. data/TODO.bugs/13-string-literal-scanner-limitation.md +52 -0
  36. data/TODO.bugs/14-model-formatting-leak.md +30 -0
  37. data/TODO.bugs/15-expression-children-macro.md +27 -0
  38. data/TODO.bugs/16-pretty-formatter-duplication.md +28 -0
  39. data/TODO.bugs/17-snake-case-cache-mutable-constant.md +28 -0
  40. data/TODO.bugs/18-const-get-private-constants.md +30 -0
  41. data/TODO.bugs/19-format-methods-public.md +22 -0
  42. data/TODO.bugs/20-coverage-nested-entities-dedup.md +20 -0
  43. data/TODO.bugs/21-operator-tokens-secondary-dispatch.md +21 -0
  44. data/TODO.bugs/22-builder-fast-path-wrappers.md +32 -0
  45. data/TODO.bugs/23-coverage-inverse-maps.md +21 -0
  46. data/TODO.bugs/24-streaming-builder-complexity.md +19 -0
  47. data/TODO.bugs/25-debug-puts-in-production.md +21 -0
  48. data/TODO.bugs/26-generic-entity-children-misplaced.md +21 -0
  49. data/TODO.bugs/27-package-build-god-method.md +19 -0
  50. data/TODO.bugs/28-package-god-class.md +30 -0
  51. data/TODO.bugs/29-validate-ascii-god-class.md +24 -0
  52. data/TODO.bugs/30-unicode-map-extraction.md +19 -0
  53. data/TODO.bugs/README.md +43 -0
  54. data/TODO.max-perf/01-restore-ci-green.md +36 -0
  55. data/TODO.max-perf/02-streaming-parse-path.md +31 -0
  56. data/TODO.max-perf/03-cli-parallel-opt-in.md +27 -0
  57. data/TODO.max-perf/04-benchmark-harness.md +28 -0
  58. data/TODO.max-perf/05-parallel-fidelity-specs.md +22 -0
  59. data/TODO.max-perf/06-builder-cpu-audit.md +41 -0
  60. data/TODO.max-perf/07-upstream-parsanol-roadmap.md +27 -0
  61. data/TODO.max-perf/08-builder-build-perf.md +45 -0
  62. data/TODO.max-perf/09-grammar-cold-start.md +25 -0
  63. data/TODO.max-perf/10-parser-facade-hygiene.md +23 -0
  64. data/TODO.max-perf/11-ci-green-closeout.md +25 -0
  65. data/TODO.max-perf/12-require-boot-profile.md +25 -0
  66. data/TODO.max-perf/13-key-conversion-specs.md +26 -0
  67. data/TODO.max-perf/14-builder-call-handler-audit.md +28 -0
  68. data/TODO.parity-ee/00-overview.md +76 -0
  69. data/TODO.parity-ee/01-annex-g-rule-extraction.md +41 -0
  70. data/TODO.parity-ee/02-eeng-algorithm-inventory.md +8 -0
  71. data/TODO.parity-ee/03-eeng-oracle-harness.md +33 -0
  72. data/TODO.parity-ee/04-shtolo-concatenate.md +44 -0
  73. data/TODO.parity-ee/05-shtolo-longform-flatten.md +53 -0
  74. data/TODO.parity-ee/06-interface-scheduling-parity.md +47 -0
  75. data/TODO.parity-ee/07-semantic-checks-port.md +35 -0
  76. data/TODO.parity-ee/08-pretty-roundtrip-gate.md +27 -0
  77. data/TODO.parity-ee/09-smrl-index-and-listing.md +22 -0
  78. data/TODO.parity-ee/10-interface-dot-graph.md +21 -0
  79. data/TODO.parity-ee/11-import-eeng-tests.md +79 -0
  80. data/TODO.parity-ee/12-full-check-catalog.md +67 -0
  81. data/TODO.parity-ee/13-population-p21.md +54 -0
  82. data/TODO.parity-ee/14-shtolo-completion.md +44 -0
  83. data/TODO.parity-ee/15-part28-xml.md +51 -0
  84. data/TODO.parity-ee/16-compare-patch.md +48 -0
  85. data/TODO.parity-ee/17-official-tests.md +42 -0
  86. data/TODO.parity-ee/18-mim-mapping.md +47 -0
  87. data/TODO.parity-ee/19-architecture.md +97 -0
  88. data/TODO.parity-ee/parity-matrix.md +108 -0
  89. data/TODO.refpath-v2/00-overview.md +64 -0
  90. data/TODO.suma-improvements/00-overview.md +23 -0
  91. data/TODO.suma-improvements/01-prebuilt-platform-gems.md +33 -0
  92. data/TODO.suma-improvements/02-suma-e2e.md +39 -0
  93. data/TODO.suma-improvements/03-item-graph.md +22 -0
  94. data/TODO.suma-improvements/04-compiled-set-default.md +16 -0
  95. data/TODO.suma-improvements/05-validation-in-docs.md +17 -0
  96. data/TODO.suma-improvements/06-memory-remeasure.md +36 -0
  97. data/TODO.suma-improvements/07-part28-xml.md +15 -0
  98. data/TODO.suma-improvements/08-windows-gnu-toolchain.md +15 -0
  99. data/TODO.suma-improvements/09-checker-tranche3.md +13 -0
  100. data/TODO.suma-improvements/10-expressdoc.md +8 -0
  101. data/benchmark/srl_benchmark.rb +458 -0
  102. data/benchmark/srl_native_benchmark.rb +146 -0
  103. data/benchmark/srl_ruby_benchmark.rb +132 -0
  104. data/bin/console +11 -0
  105. data/bin/rspec +30 -0
  106. data/bin/setup +8 -0
  107. data/docs/Gemfile +12 -0
  108. data/docs/_config.yml +141 -0
  109. data/docs/_guides/changes/changes-format.adoc +778 -0
  110. data/docs/_guides/changes/importing-eengine.adoc +898 -0
  111. data/docs/_guides/changes/index.adoc +396 -0
  112. data/docs/_guides/changes/programmatic-usage.adoc +1038 -0
  113. data/docs/_guides/changes/validating-changes.adoc +681 -0
  114. data/docs/_guides/cli/benchmark-performance.adoc +834 -0
  115. data/docs/_guides/cli/coverage-analysis.adoc +921 -0
  116. data/docs/_guides/cli/format-schemas.adoc +547 -0
  117. data/docs/_guides/cli/index.adoc +8 -0
  118. data/docs/_guides/cli/managing-changes.adoc +927 -0
  119. data/docs/_guides/cli/validate-ascii.adoc +645 -0
  120. data/docs/_guides/cli/validate-schemas.adoc +534 -0
  121. data/docs/_guides/formatter/formatter-architecture.adoc +401 -0
  122. data/docs/_guides/index.adoc +165 -0
  123. data/docs/_guides/ler/creating-packages.adoc +664 -0
  124. data/docs/_guides/ler/index.adoc +305 -0
  125. data/docs/_guides/ler/loading-packages.adoc +707 -0
  126. data/docs/_guides/ler/package-formats.adoc +748 -0
  127. data/docs/_guides/ler/querying-packages.adoc +826 -0
  128. data/docs/_guides/ler/step-packages.adoc +385 -0
  129. data/docs/_guides/ler/validating-packages.adoc +750 -0
  130. data/docs/_guides/liquid/basic-templates.adoc +813 -0
  131. data/docs/_guides/liquid/documentation-generation.adoc +1042 -0
  132. data/docs/_guides/liquid/drops-reference.adoc +829 -0
  133. data/docs/_guides/liquid/filters-and-tags.adoc +912 -0
  134. data/docs/_guides/liquid/index.adoc +468 -0
  135. data/docs/_guides/manifests/creating-manifests.adoc +483 -0
  136. data/docs/_guides/manifests/index.adoc +307 -0
  137. data/docs/_guides/manifests/resolving-manifests.adoc +557 -0
  138. data/docs/_guides/manifests/validating-manifests.adoc +713 -0
  139. data/docs/_guides/ruby-api/formatting-schemas.adoc +605 -0
  140. data/docs/_guides/ruby-api/index.adoc +257 -0
  141. data/docs/_guides/ruby-api/parsing-files.adoc +421 -0
  142. data/docs/_guides/ruby-api/search-engine.adoc +609 -0
  143. data/docs/_guides/ruby-api/working-with-repository.adoc +577 -0
  144. data/docs/_pages/data-model.adoc +665 -0
  145. data/docs/_pages/express-language.adoc +506 -0
  146. data/docs/_pages/getting-started.adoc +414 -0
  147. data/docs/_pages/index.adoc +116 -0
  148. data/docs/_pages/introduction.adoc +256 -0
  149. data/docs/_pages/ler-packages.adoc +837 -0
  150. data/docs/_pages/parsers.adoc +709 -0
  151. data/docs/_pages/schema-manifests.adoc +431 -0
  152. data/docs/_references/index.adoc +228 -0
  153. data/docs/_tutorials/creating-ler-package.adoc +735 -0
  154. data/docs/_tutorials/documentation-coverage.adoc +795 -0
  155. data/docs/_tutorials/formatting-schemas.adoc +89 -0
  156. data/docs/_tutorials/index.adoc +231 -0
  157. data/docs/_tutorials/liquid-templates.adoc +806 -0
  158. data/docs/_tutorials/parsing-your-first-schema.adoc +522 -0
  159. data/docs/_tutorials/querying-schemas.adoc +751 -0
  160. data/docs/_tutorials/working-with-multiple-schemas.adoc +676 -0
  161. data/docs/index.adoc +242 -0
  162. data/docs/lychee.toml +90 -0
  163. data/examples/demo_ler_usage.sh +86 -0
  164. data/examples/ler/README.md +111 -0
  165. data/examples/ler/simple_example.ler +0 -0
  166. data/examples/ler/simple_schema.exp +33 -0
  167. data/examples/ler_build.rb +75 -0
  168. data/examples/ler_cli.rb +79 -0
  169. data/examples/ler_demo_complete.rb +276 -0
  170. data/examples/ler_query.rb +91 -0
  171. data/examples/ler_query_examples.rb +305 -0
  172. data/examples/ler_stats.rb +81 -0
  173. data/examples/phase3_demo.rb +159 -0
  174. data/examples/query_demo_simple.rb +131 -0
  175. data/exe/expressir +11 -0
  176. data/exe/expressir-format +14 -0
  177. data/exe/expressir-format-test +82 -0
  178. data/expressir.gemspec +61 -0
  179. data/lib/expressir/3.2/expressir_core.so +0 -0
  180. data/lib/expressir/3.3/expressir_core.so +0 -0
  181. data/lib/expressir/3.4/expressir_core.so +0 -0
  182. data/lib/expressir/4.0/expressir_core.so +0 -0
  183. data/lib/expressir/benchmark.rb +310 -0
  184. data/lib/expressir/changes/item_change.rb +20 -0
  185. data/lib/expressir/changes/mapping_change.rb +16 -0
  186. data/lib/expressir/changes/schema_change.rb +85 -0
  187. data/lib/expressir/changes/version_change.rb +26 -0
  188. data/lib/expressir/changes.rb +9 -0
  189. data/lib/expressir/cli.rb +148 -0
  190. data/lib/expressir/commands/base.rb +25 -0
  191. data/lib/expressir/commands/benchmark.rb +59 -0
  192. data/lib/expressir/commands/benchmark_cache.rb +80 -0
  193. data/lib/expressir/commands/changes.rb +34 -0
  194. data/lib/expressir/commands/changes_import_eengine.rb +146 -0
  195. data/lib/expressir/commands/changes_validate.rb +63 -0
  196. data/lib/expressir/commands/check.rb +56 -0
  197. data/lib/expressir/commands/clean.rb +20 -0
  198. data/lib/expressir/commands/coverage.rb +458 -0
  199. data/lib/expressir/commands/expand.rb +26 -0
  200. data/lib/expressir/commands/file_violations.rb +70 -0
  201. data/lib/expressir/commands/fix.rb +33 -0
  202. data/lib/expressir/commands/flatten.rb +29 -0
  203. data/lib/expressir/commands/format.rb +43 -0
  204. data/lib/expressir/commands/manifest.rb +420 -0
  205. data/lib/expressir/commands/mapping_validate.rb +68 -0
  206. data/lib/expressir/commands/non_ascii_character.rb +49 -0
  207. data/lib/expressir/commands/non_ascii_violation_collection.rb +301 -0
  208. data/lib/expressir/commands/package.rb +1222 -0
  209. data/lib/expressir/commands/parity_inputs.rb +160 -0
  210. data/lib/expressir/commands/validate.rb +106 -0
  211. data/lib/expressir/commands/validate_ascii.rb +94 -0
  212. data/lib/expressir/commands/validate_load.rb +91 -0
  213. data/lib/expressir/commands/version.rb +9 -0
  214. data/lib/expressir/commands/xsd.rb +23 -0
  215. data/lib/expressir/commands.rb +30 -0
  216. data/lib/expressir/config.rb +60 -0
  217. data/lib/expressir/coverage.rb +510 -0
  218. data/lib/expressir/eengine/arm_compare_report.rb +23 -0
  219. data/lib/expressir/eengine/changes_section.rb +16 -0
  220. data/lib/expressir/eengine/compare_report.rb +69 -0
  221. data/lib/expressir/eengine/mim_compare_report.rb +23 -0
  222. data/lib/expressir/eengine/modified_object.rb +21 -0
  223. data/lib/expressir/eengine.rb +9 -0
  224. data/lib/expressir/errors.rb +113 -0
  225. data/lib/expressir/express/adoc_hyperlink_formatter.rb +30 -0
  226. data/lib/expressir/express/adoc_source_formatter.rb +12 -0
  227. data/lib/expressir/express/ast_key_converter.rb +114 -0
  228. data/lib/expressir/express/builder.rb +277 -0
  229. data/lib/expressir/express/builder_context.rb +22 -0
  230. data/lib/expressir/express/builder_registry.rb +397 -0
  231. data/lib/expressir/express/builders/attribute_decl_builder.rb +32 -0
  232. data/lib/expressir/express/builders/built_in_builder.rb +75 -0
  233. data/lib/expressir/express/builders/constant_builder.rb +108 -0
  234. data/lib/expressir/express/builders/declaration_builder.rb +20 -0
  235. data/lib/expressir/express/builders/derive_clause_builder.rb +14 -0
  236. data/lib/expressir/express/builders/derived_attr_builder.rb +28 -0
  237. data/lib/expressir/express/builders/domain_rule_builder.rb +21 -0
  238. data/lib/expressir/express/builders/entity_decl_builder.rb +116 -0
  239. data/lib/expressir/express/builders/explicit_attr_builder.rb +49 -0
  240. data/lib/expressir/express/builders/expression_builder.rb +416 -0
  241. data/lib/expressir/express/builders/function_decl_builder.rb +86 -0
  242. data/lib/expressir/express/builders/helpers.rb +148 -0
  243. data/lib/expressir/express/builders/interface_builder.rb +151 -0
  244. data/lib/expressir/express/builders/inverse_attr_builder.rb +43 -0
  245. data/lib/expressir/express/builders/inverse_attr_type_builder.rb +30 -0
  246. data/lib/expressir/express/builders/inverse_clause_builder.rb +14 -0
  247. data/lib/expressir/express/builders/literal_builder.rb +93 -0
  248. data/lib/expressir/express/builders/procedure_decl_builder.rb +82 -0
  249. data/lib/expressir/express/builders/qualifier_builder.rb +102 -0
  250. data/lib/expressir/express/builders/reference_builder.rb +18 -0
  251. data/lib/expressir/express/builders/rule_decl_builder.rb +97 -0
  252. data/lib/expressir/express/builders/schema_body_decl_builder.rb +18 -0
  253. data/lib/expressir/express/builders/schema_decl_builder.rb +56 -0
  254. data/lib/expressir/express/builders/schema_version_builder.rb +34 -0
  255. data/lib/expressir/express/builders/simple_id_builder.rb +17 -0
  256. data/lib/expressir/express/builders/statement_builder.rb +243 -0
  257. data/lib/expressir/express/builders/subtype_constraint_builder.rb +166 -0
  258. data/lib/expressir/express/builders/syntax_builder.rb +30 -0
  259. data/lib/expressir/express/builders/type_builder.rb +238 -0
  260. data/lib/expressir/express/builders/type_decl_builder.rb +26 -0
  261. data/lib/expressir/express/builders/unique_clause_builder.rb +20 -0
  262. data/lib/expressir/express/builders/unique_rule_builder.rb +41 -0
  263. data/lib/expressir/express/builders/where_clause_builder.rb +20 -0
  264. data/lib/expressir/express/builders.rb +55 -0
  265. data/lib/expressir/express/cache.rb +76 -0
  266. data/lib/expressir/express/checker.rb +812 -0
  267. data/lib/expressir/express/concatenator.rb +79 -0
  268. data/lib/expressir/express/core.rb +95 -0
  269. data/lib/expressir/express/error.rb +124 -0
  270. data/lib/expressir/express/formatter.rb +149 -0
  271. data/lib/expressir/express/formatters/data_types_formatter.rb +362 -0
  272. data/lib/expressir/express/formatters/declarations_formatter.rb +753 -0
  273. data/lib/expressir/express/formatters/expressions_formatter.rb +180 -0
  274. data/lib/expressir/express/formatters/literals_formatter.rb +55 -0
  275. data/lib/expressir/express/formatters/references_formatter.rb +53 -0
  276. data/lib/expressir/express/formatters/remark_formatter.rb +270 -0
  277. data/lib/expressir/express/formatters/remark_item_formatter.rb +28 -0
  278. data/lib/expressir/express/formatters/statements_formatter.rb +272 -0
  279. data/lib/expressir/express/formatters/supertype_expressions_formatter.rb +54 -0
  280. data/lib/expressir/express/formatters.rb +22 -0
  281. data/lib/expressir/express/grammar/parser.rb +712 -0
  282. data/lib/expressir/express/grammar.rb +11 -0
  283. data/lib/expressir/express/hyperlink_formatter.rb +35 -0
  284. data/lib/expressir/express/interface_dot.rb +105 -0
  285. data/lib/expressir/express/lazy_repository.rb +211 -0
  286. data/lib/expressir/express/line_map.rb +48 -0
  287. data/lib/expressir/express/listing.rb +174 -0
  288. data/lib/expressir/express/model_traversal.rb +42 -0
  289. data/lib/expressir/express/model_visitor.rb +25 -0
  290. data/lib/expressir/express/node_position_index.rb +342 -0
  291. data/lib/expressir/express/parallel_files.rb +229 -0
  292. data/lib/expressir/express/parser.rb +523 -0
  293. data/lib/expressir/express/pretty_formatter.rb +586 -0
  294. data/lib/expressir/express/pretty_gate.rb +61 -0
  295. data/lib/expressir/express/refs_overlay.rb +47 -0
  296. data/lib/expressir/express/remark_attacher.rb +1098 -0
  297. data/lib/expressir/express/remark_overlay.rb +86 -0
  298. data/lib/expressir/express/remark_scanner.rb +245 -0
  299. data/lib/expressir/express/resolve_references_model_visitor.rb +29 -0
  300. data/lib/expressir/express/schema_block_scanner.rb +137 -0
  301. data/lib/expressir/express/schema_head_formatter.rb +22 -0
  302. data/lib/expressir/express/schema_plain_source_formatter.rb +11 -0
  303. data/lib/expressir/express/schema_source_formatter.rb +15 -0
  304. data/lib/expressir/express/scope_resolver.rb +223 -0
  305. data/lib/expressir/express/self_schema_reference.rb +126 -0
  306. data/lib/expressir/express/shtolo.rb +679 -0
  307. data/lib/expressir/express/smrl_xml.rb +125 -0
  308. data/lib/expressir/express/source_formatter.rb +15 -0
  309. data/lib/expressir/express/streaming_builder.rb +435 -0
  310. data/lib/expressir/express/xsd.rb +259 -0
  311. data/lib/expressir/express.rb +53 -0
  312. data/lib/expressir/liquid.rb +1 -0
  313. data/lib/expressir/manifest/resolver.rb +210 -0
  314. data/lib/expressir/manifest/validator.rb +192 -0
  315. data/lib/expressir/manifest.rb +6 -0
  316. data/lib/expressir/mapping/refpath.rb +857 -0
  317. data/lib/expressir/mapping.rb +253 -0
  318. data/lib/expressir/model/cache.rb +18 -0
  319. data/lib/expressir/model/concerns.rb +42 -0
  320. data/lib/expressir/model/data_types/aggregate.rb +26 -0
  321. data/lib/expressir/model/data_types/array.rb +25 -0
  322. data/lib/expressir/model/data_types/bag.rb +21 -0
  323. data/lib/expressir/model/data_types/binary.rb +19 -0
  324. data/lib/expressir/model/data_types/boolean.rb +15 -0
  325. data/lib/expressir/model/data_types/enumeration.rb +21 -0
  326. data/lib/expressir/model/data_types/enumeration_item.rb +24 -0
  327. data/lib/expressir/model/data_types/generic.rb +24 -0
  328. data/lib/expressir/model/data_types/generic_entity.rb +24 -0
  329. data/lib/expressir/model/data_types/integer.rb +15 -0
  330. data/lib/expressir/model/data_types/list.rb +23 -0
  331. data/lib/expressir/model/data_types/logical.rb +15 -0
  332. data/lib/expressir/model/data_types/number.rb +15 -0
  333. data/lib/expressir/model/data_types/real.rb +17 -0
  334. data/lib/expressir/model/data_types/select.rb +23 -0
  335. data/lib/expressir/model/data_types/set.rb +21 -0
  336. data/lib/expressir/model/data_types/string.rb +19 -0
  337. data/lib/expressir/model/data_types.rb +25 -0
  338. data/lib/expressir/model/declarations/attribute.rb +38 -0
  339. data/lib/expressir/model/declarations/constant.rb +28 -0
  340. data/lib/expressir/model/declarations/derived_attribute.rb +25 -0
  341. data/lib/expressir/model/declarations/entity.rb +62 -0
  342. data/lib/expressir/model/declarations/function.rb +59 -0
  343. data/lib/expressir/model/declarations/informal_proposition_rule.rb +25 -0
  344. data/lib/expressir/model/declarations/interface.rb +37 -0
  345. data/lib/expressir/model/declarations/interface_item.rb +21 -0
  346. data/lib/expressir/model/declarations/interfaced_item.rb +33 -0
  347. data/lib/expressir/model/declarations/inverse_attribute.rb +25 -0
  348. data/lib/expressir/model/declarations/parameter.rb +28 -0
  349. data/lib/expressir/model/declarations/procedure.rb +57 -0
  350. data/lib/expressir/model/declarations/remark_item.rb +21 -0
  351. data/lib/expressir/model/declarations/rule.rb +66 -0
  352. data/lib/expressir/model/declarations/schema.rb +215 -0
  353. data/lib/expressir/model/declarations/schema_version.rb +19 -0
  354. data/lib/expressir/model/declarations/schema_version_item.rb +19 -0
  355. data/lib/expressir/model/declarations/subtype_constraint.rb +32 -0
  356. data/lib/expressir/model/declarations/type.rb +45 -0
  357. data/lib/expressir/model/declarations/unique_rule.rb +26 -0
  358. data/lib/expressir/model/declarations/variable.rb +28 -0
  359. data/lib/expressir/model/declarations/where_rule.rb +26 -0
  360. data/lib/expressir/model/declarations.rb +31 -0
  361. data/lib/expressir/model/dependency_resolver.rb +268 -0
  362. data/lib/expressir/model/exp_file.rb +42 -0
  363. data/lib/expressir/model/expressions/aggregate_initializer.rb +18 -0
  364. data/lib/expressir/model/expressions/aggregate_initializer_item.rb +20 -0
  365. data/lib/expressir/model/expressions/binary_expression.rb +59 -0
  366. data/lib/expressir/model/expressions/entity_constructor.rb +20 -0
  367. data/lib/expressir/model/expressions/function_call.rb +20 -0
  368. data/lib/expressir/model/expressions/interval.rb +29 -0
  369. data/lib/expressir/model/expressions/query_expression.rb +31 -0
  370. data/lib/expressir/model/expressions/unary_expression.rb +25 -0
  371. data/lib/expressir/model/expressions.rb +18 -0
  372. data/lib/expressir/model/identifier.rb +26 -0
  373. data/lib/expressir/model/indexes/entity_index.rb +103 -0
  374. data/lib/expressir/model/indexes/item_graph.rb +185 -0
  375. data/lib/expressir/model/indexes/reference_index.rb +148 -0
  376. data/lib/expressir/model/indexes/type_index.rb +149 -0
  377. data/lib/expressir/model/indexes.rb +12 -0
  378. data/lib/expressir/model/interface_validator.rb +384 -0
  379. data/lib/expressir/model/literals/binary.rb +17 -0
  380. data/lib/expressir/model/literals/integer.rb +17 -0
  381. data/lib/expressir/model/literals/logical.rb +21 -0
  382. data/lib/expressir/model/literals/real.rb +17 -0
  383. data/lib/expressir/model/literals/string.rb +20 -0
  384. data/lib/expressir/model/literals.rb +13 -0
  385. data/lib/expressir/model/model_element.rb +377 -0
  386. data/lib/expressir/model/references/attribute_reference.rb +19 -0
  387. data/lib/expressir/model/references/group_reference.rb +19 -0
  388. data/lib/expressir/model/references/index_reference.rb +23 -0
  389. data/lib/expressir/model/references/simple_reference.rb +21 -0
  390. data/lib/expressir/model/references.rb +12 -0
  391. data/lib/expressir/model/remark_format.rb +17 -0
  392. data/lib/expressir/model/remark_info.rb +92 -0
  393. data/lib/expressir/model/remark_placement.rb +42 -0
  394. data/lib/expressir/model/repository.rb +483 -0
  395. data/lib/expressir/model/repository_validator.rb +293 -0
  396. data/lib/expressir/model/search_engine.rb +551 -0
  397. data/lib/expressir/model/statements/alias.rb +31 -0
  398. data/lib/expressir/model/statements/assignment.rb +23 -0
  399. data/lib/expressir/model/statements/case.rb +42 -0
  400. data/lib/expressir/model/statements/case_action.rb +20 -0
  401. data/lib/expressir/model/statements/compound.rb +21 -0
  402. data/lib/expressir/model/statements/escape.rb +18 -0
  403. data/lib/expressir/model/statements/if.rb +26 -0
  404. data/lib/expressir/model/statements/null.rb +18 -0
  405. data/lib/expressir/model/statements/procedure_call.rb +22 -0
  406. data/lib/expressir/model/statements/repeat.rb +40 -0
  407. data/lib/expressir/model/statements/return.rb +20 -0
  408. data/lib/expressir/model/statements/skip.rb +18 -0
  409. data/lib/expressir/model/statements.rb +20 -0
  410. data/lib/expressir/model/supertype_expressions/binary_supertype_expression.rb +25 -0
  411. data/lib/expressir/model/supertype_expressions/oneof_supertype_expression.rb +17 -0
  412. data/lib/expressir/model/supertype_expressions.rb +12 -0
  413. data/lib/expressir/model.rb +39 -0
  414. data/lib/expressir/package/builder.rb +231 -0
  415. data/lib/expressir/package/metadata.rb +79 -0
  416. data/lib/expressir/package/reader.rb +149 -0
  417. data/lib/expressir/package.rb +8 -0
  418. data/lib/expressir/schema_manifest.rb +159 -0
  419. data/lib/expressir/schema_manifest_entry.rb +15 -0
  420. data/lib/expressir/version.rb +8 -0
  421. data/lib/expressir.rb +94 -0
  422. data/lib/tasks/verify_remarks.rake +16 -0
  423. data/rakelib/native.rake +24 -0
  424. data/rakelib/oracle.rake +52 -0
  425. data/rakelib/verify_remarks.rake +16 -0
  426. metadata +692 -0
data/README.adoc ADDED
@@ -0,0 +1,2783 @@
1
+ = Expressir: EXPRESS in Ruby
2
+
3
+ image:https://img.shields.io/gem/v/expressir.svg["Gem Version", link="https://rubygems.org/gems/expressir"]
4
+ // image:https://codeclimate.com/github/lutaml/expressir/badges/gpa.svg["Code Climate", link="https://codeclimate.com/github/lutaml/expressir"]
5
+ image:https://github.com/lutaml/expressir/workflows/rake/badge.svg["Build Status", link="https://github.com/lutaml/expressir/actions?workflow=rake"]
6
+
7
+ == Purpose
8
+
9
+ Expressir ("`EXPRESS in Ruby`") is a Ruby parser for EXPRESS and
10
+ a set of Ruby tools for accessing ISO EXPRESS data models.
11
+
12
+ == Architecture
13
+
14
+ Expressir consists of 3 parts:
15
+
16
+ . Parsers. A parser allows Expressir to read EXPRESS files, including:
17
+
18
+ ** EXPRESS data modelling language (ISO 10303-11:2007)
19
+ ** EXPRESS data modelling language in XML (STEPmod)
20
+ ** EXPRESS XML (ISO 10303-28:2007)
21
+ "`Industrial automation systems and integration — Product data representation and exchange — Part 28: Implementation methods: XML representations of EXPRESS schemas and data, using XML schemas`"
22
+
23
+ . Data model. The data model (`lib/expressir/express`) is the Ruby data model that fully represents an EXPRESS data model.
24
+
25
+ . Converters. A converter transforms the EXPRESS Ruby data model into an interoperable export format, including:
26
+ ** EXPRESS data modelling language (ISO 10303-11:2007)
27
+ // ** W3C OWL
28
+ // ** OMG SysML (XMI 2.1, XMI 2.5)
29
+ // ** OMG UML 2 (XMI 2.1)
30
+ // ** OMG UML 2 for Eclipse (XMI 2.1)
31
+
32
+
33
+ == Features
34
+
35
+ === Remark preservation
36
+
37
+ Expressir fully preserves EXPRESS remarks (comments) during parsing and formatting, maintaining them in their original positions:
38
+
39
+ ==== Preamble remarks
40
+
41
+ Remarks between a scope declaration and its first child are preserved as preamble remarks:
42
+
43
+ [source,express]
44
+ ----
45
+ SCHEMA example;
46
+ -- This is a preamble remark
47
+ -- It appears after SCHEMA but before declarations
48
+
49
+ ENTITY person;
50
+ -- Entity preamble remark
51
+ name : STRING;
52
+ END_ENTITY;
53
+
54
+ END_SCHEMA;
55
+ ----
56
+
57
+ ==== Inline tail remarks
58
+
59
+ Remarks on the same line as attribute or enumeration item declarations:
60
+
61
+ [source,express]
62
+ ----
63
+ ENTITY person;
64
+ name : STRING; -- Inline remark for name attribute
65
+ age : INTEGER; -- Inline remark for age attribute
66
+ END_ENTITY;
67
+
68
+ TYPE status = ENUMERATION OF
69
+ (active, -- Active status
70
+ inactive, -- Inactive status
71
+ pending); -- Pending status
72
+ END_TYPE;
73
+ ----
74
+
75
+ ==== END_* scope remarks
76
+
77
+ Remarks on END_TYPE, END_ENTITY, END_SCHEMA, etc. lines:
78
+
79
+ [source,express]
80
+ ----
81
+ TYPE status = ENUMERATION OF
82
+ (active,
83
+ inactive);
84
+ END_TYPE; -- Status enumeration type
85
+
86
+ ENTITY person;
87
+ name : STRING;
88
+ END_ENTITY; -- Person entity
89
+
90
+ END_SCHEMA; -- schema_name
91
+ ----
92
+
93
+ ==== Unicode support
94
+
95
+ All remark types support full Unicode content:
96
+
97
+ [source,express]
98
+ ----
99
+ SCHEMA test;
100
+ -- 日本語、中文、한글 in remarks
101
+
102
+ ENTITY person;
103
+ name : STRING; -- Name in Japanese: 名前
104
+ END_ENTITY;
105
+
106
+ END_SCHEMA; -- test
107
+ ----
108
+
109
+ For implementation details, see link:docs/ARCHITECTURE.md#remark-attachment-system[Remark Attachment System].
110
+
111
+
112
+ == Performance: Parsanol Integration
113
+
114
+ Expressir uses the link:https://github.com/parsanol/parsanol-ruby[Parsanol] gem for high-performance parsing when available.
115
+
116
+ === Performance Comparison
117
+
118
+ [cols="3,2,2,3"]
119
+ |===
120
+ | Mode | Time (179K lines) | Speedup | Notes
121
+
122
+ | Ruby (Parslet) | 507 s | 1x (baseline) | Pure Ruby parsing
123
+ | Native (Parsanol) | 29 s | 17x faster | Rust parser with AST transformation
124
+ |===
125
+
126
+ === Features
127
+
128
+ When Parsanol is installed:
129
+
130
+ * **17x faster parsing** - Rust native backend
131
+ * **Lazy line/column** - Zero overhead for position info
132
+ * **Batch FFI** - Efficient u64 array transfer across language boundary
133
+
134
+ === Usage
135
+
136
+ Expressir automatically uses Parsanol (Rust parser) when available for better performance:
137
+
138
+ [source,ruby]
139
+ ----
140
+ # Parse a single file - returns ExpFile
141
+ exp_file = Expressir::Express::Parser.from_file("geometry.exp")
142
+ schema = exp_file.schemas.first
143
+ puts "Schema: #{schema.id}"
144
+
145
+ # Check if native parser is being used
146
+ if Parsanol::Native.available?
147
+ puts "Using Parsanol (Rust parser)"
148
+ end
149
+
150
+ # Parse multiple files - returns Repository
151
+ files = ["schema1.exp", "schema2.exp"]
152
+ repo = Expressir::Express::Parser.from_files(files)
153
+ repo.schemas.each do |s|
154
+ puts "Schema: #{s.id}"
155
+ end
156
+ ----
157
+
158
+ For maximum performance, ensure the Parsanol gem is installed:
159
+
160
+ [source,sh]
161
+ ----
162
+ gem install parsanol
163
+ ----
164
+
165
+
166
+ == Installation
167
+
168
+ Add this line to your application's `Gemfile`:
169
+
170
+ [source, sh]
171
+ ----
172
+ gem "expressir"
173
+ ----
174
+
175
+ And then execute:
176
+
177
+ [source, sh]
178
+ ----
179
+ $ bundle install
180
+ ----
181
+
182
+ Or install it yourself as:
183
+
184
+ [source, sh]
185
+ ----
186
+ $ gem install expressir
187
+ ----
188
+
189
+
190
+ == Testing
191
+
192
+ Run the test suite:
193
+
194
+ [source, sh]
195
+ ----
196
+ bundle exec rake
197
+ ----
198
+
199
+
200
+ == Usage: CLI
201
+
202
+ This gem ships with a CLI tool. To check what's available you can simply run
203
+ `expressir`, by default it will display some usage instructions.
204
+
205
+ [source, sh]
206
+ ----
207
+ $ expressir
208
+
209
+ Commands:
210
+ expressir benchmark FILE_OR_YAML # Benchmark schema loading performance for a file or list of files from YAML
211
+ expressir benchmark-cache FILE_OR_YAML # Benchmark schema loading with caching
212
+ expressir changes SUBCOMMAND # Commands for EXPRESS Changes files
213
+ expressir clean PATH # Strip remarks and prettify EXPRESS schema at PATH
214
+ expressir expand PATH # Concatenate the interface closure into one .exp
215
+ expressir flatten PATH # Flatten the interface closure into an ISO 10303-11:1994 longform schema
216
+ expressir fix PATH # Rewrite self-schema-qualified references (#125)
217
+ expressir format PATH # pretty print EXPRESS schema located at PATH
218
+ expressir mapping_validate PATH # Validate <<express:...>> links in a module mapping.yaml against its ARM/MIM closure
219
+ expressir xsd PATH -o OUT.xsd # Write an XML Schema rendering of the EXPRESS schema at PATH
220
+ expressir help [COMMAND] # Describe available commands or one specific command
221
+ expressir validate load *PATH # validate EXPRESS schema located at PATH
222
+ expressir validate ascii PATH # Validate EXPRESS files for ASCII-only content (excluding remarks)
223
+ expressir validate check *PATHS # eeng check-p11 semantic checks
224
+ expressir coverage *PATH # List EXPRESS entities and check documentation coverage
225
+ expressir version # Expressir Version
226
+ ----
227
+
228
+ === Format schema
229
+
230
+ The `format` command pretty prints an EXPRESS schema, making it more readable
231
+ while preserving its structure.
232
+
233
+ [source, sh]
234
+ ----
235
+ # Pretty print a schema to stdout
236
+ expressir format schemas/resources/action_schema/action_schema.exp
237
+ ----
238
+
239
+ This command:
240
+
241
+ . Parses the EXPRESS schema
242
+ . Formats it with consistent indentation and spacing
243
+ . Outputs the formatted schema to stdout
244
+
245
+ === Pretty print with ELF compliance
246
+
247
+ The `PrettyFormatter` class provides ELF (EXPRESS Language Foundation) compliant
248
+ pretty printing with configurable formatting options. This formatter follows the
249
+ https://www.express-language-foundation.org/pretty-print-spec/[ELF Pretty Print specification].
250
+
251
+ ==== Using PrettyFormatter in Ruby
252
+
253
+ [source,ruby]
254
+ ----
255
+ # Basic usage - formats with default settings
256
+ # Parser.from_file returns an ExpFile
257
+ exp_file = Expressir::Express::Parser.from_file("schema.exp")
258
+ formatter = Expressir::Express::PrettyFormatter.new
259
+ formatted = formatter.format(exp_file)
260
+ puts formatted
261
+
262
+ # The formatter also accepts Repository objects
263
+ repo = Expressir::Express::Parser.from_files(["schema1.exp", "schema2.exp"])
264
+ formatted = formatter.format(repo)
265
+ puts formatted
266
+ ----
267
+
268
+ ==== Configuration options
269
+
270
+ The PrettyFormatter supports several configuration options to customize the output.
271
+
272
+ [source,ruby]
273
+ ----
274
+ formatter = Expressir::Express::PrettyFormatter.new(
275
+ indent: 4, # Spaces per indentation level (default: 4)
276
+ line_length: 80, # Maximum line length (default: nil)
277
+ provenance: true, # Include provenance info (default: true)
278
+ provenance_name: "MyTool", # Tool name (default: "Expressir")
279
+ provenance_version: "1.0.0", # Tool version (default: Expressir::VERSION)
280
+ no_remarks: false # Suppress remarks (default: false)
281
+ )
282
+ ----
283
+
284
+ [options="header"]
285
+ |===
286
+ | Option | Type | Default | Description
287
+
288
+ | `indent`
289
+ | Integer
290
+ | `4`
291
+ | Number of spaces per indentation level
292
+
293
+ | `line_length`
294
+ | Integer or nil
295
+ | `nil`
296
+ | Maximum line length (not yet enforced)
297
+
298
+ | `provenance`
299
+ | Boolean
300
+ | `true`
301
+ | Include provenance information in output
302
+
303
+ | `provenance_name`
304
+ | String
305
+ | `"Expressir"`
306
+ | Tool name for provenance
307
+
308
+ | `provenance_version`
309
+ | String
310
+ | `Expressir::VERSION`
311
+ | Tool version for provenance
312
+
313
+ | `no_remarks`
314
+ | Boolean
315
+ | `false`
316
+ | Suppress remarks from source schema
317
+ |===
318
+
319
+ ==== Environment variable configuration
320
+
321
+ Configuration can be overridden using environment variables. Environment variables
322
+ take precedence over defaults but are overridden by explicit options:
323
+
324
+ [source,sh]
325
+ ----
326
+ # Set environment variables
327
+ export EXPRESSIR_INDENT=2
328
+ export EXPRESSIR_LINE_LENGTH=100
329
+ export EXPRESSIR_PROVENANCE=false
330
+ export EXPRESSIR_PROVENANCE_NAME="CustomTool"
331
+ export EXPRESSIR_PROVENANCE_VERSION="2.0.0"
332
+
333
+ # These will be used unless overridden by options
334
+ ruby my_formatter.rb
335
+ ----
336
+
337
+ [options="header"]
338
+ |===
339
+ | Environment Variable | Description
340
+
341
+ | `EXPRESSIR_INDENT`
342
+ | Indentation width (spaces)
343
+
344
+ | `EXPRESSIR_LINE_LENGTH`
345
+ | Maximum line length
346
+
347
+ | `EXPRESSIR_PROVENANCE`
348
+ | Enable/disable provenance (`true`/`false`)
349
+
350
+ | `EXPRESSIR_PROVENANCE_NAME`
351
+ | Tool name for provenance
352
+
353
+ | `EXPRESSIR_PROVENANCE_VERSION`
354
+ | Tool version for provenance
355
+ |===
356
+
357
+ The configuration follows a MECE (Mutually Exclusive, Collectively Exhaustive)
358
+ hierarchy: **Options > ENV > Defaults**
359
+
360
+ ==== Key features
361
+
362
+ The PrettyFormatter provides several enhancements over the standard formatter:
363
+
364
+ CONSTANT alignment:: Constants in CONSTANT blocks are aligned at both the colon
365
+ and assignment operator positions for improved readability.
366
+ +
367
+ [example]
368
+ ====
369
+ [source]
370
+ ----
371
+ CONSTANT
372
+ short_name : INTEGER := 1;
373
+ longer_name : STRING := 'test';
374
+ x : REAL := 3.14;
375
+ END_CONSTANT;
376
+ ----
377
+ ====
378
+
379
+ Provenance information:: Automatically includes metadata about the formatting
380
+ tool and parameters used, aiding in reproducibility.
381
+ +
382
+ [example]
383
+ ====
384
+ [source]
385
+ ----
386
+ (*
387
+ Generated by: Expressir version 2.1.31
388
+ Format parameters: indent: 4
389
+ *)
390
+ ----
391
+ ====
392
+
393
+ Configurable indentation:: Supports custom indentation width (2, 4, 8 spaces, etc.)
394
+ to match project coding standards.
395
+
396
+ Preamble support:: Preserves and formats source-level remarks that appear before
397
+ the first SCHEMA declaration.
398
+
399
+ ==== Comparison with standard Formatter
400
+
401
+ [options="header"]
402
+ |===
403
+ | Feature | Formatter | PrettyFormatter
404
+
405
+ | Indentation
406
+ | 2 spaces (fixed)
407
+ | Configurable (default: 4)
408
+
409
+ | CONSTANT alignment
410
+ | No
411
+ | Yes (colons and assignments)
412
+
413
+ | Provenance
414
+ | No
415
+ | Yes (configurable)
416
+
417
+ | Preamble formatting
418
+ | No
419
+ | Yes
420
+
421
+ | Line length enforcement
422
+ | No
423
+ | Planned (not yet implemented)
424
+
425
+ | ELF compliant
426
+ | No
427
+ | Yes
428
+
429
+ | Configuration via ENV
430
+ | No
431
+ | Yes
432
+ |===
433
+
434
+ ==== Example usage
435
+
436
+ .Formatting with custom settings
437
+ [example]
438
+ ====
439
+ [source,ruby]
440
+ ----
441
+ # Parse schema
442
+ repository = Expressir::Express::Parser.from_file("examples/ler/simple_schema.exp")
443
+
444
+ # Format with custom indentation and provenance
445
+ formatter = Expressir::Express::PrettyFormatter.new(
446
+ indent: 2,
447
+ provenance_name: "MyFormatter",
448
+ provenance_version: "1.0.0"
449
+ )
450
+
451
+ formatted = formatter.format(repository)
452
+
453
+ # Save to file
454
+ File.write("formatted_schema.exp", formatted)
455
+ ----
456
+ ====
457
+
458
+ .Formatting without provenance
459
+ [example]
460
+ ====
461
+ [source,ruby]
462
+ ----
463
+ # Format without provenance information
464
+ formatter = Expressir::Express::PrettyFormatter.new(provenance: false)
465
+ repository = Expressir::Express::Parser.from_file("schema.exp")
466
+ formatted = formatter.format(repository)
467
+
468
+ # Output is clean without metadata comments
469
+ puts formatted
470
+ ----
471
+ ====
472
+
473
+ .Round-trip formatting verification
474
+ [example]
475
+ ====
476
+ [source,ruby]
477
+ ----
478
+ # Format a schema
479
+ original = Expressir::Express::Parser.from_file("schema.exp")
480
+ formatter = Expressir::Express::PrettyFormatter.new
481
+
482
+ # Format once
483
+ formatted1 = formatter.format(original)
484
+
485
+ # Parse the formatted output
486
+ File.write("temp.exp", formatted1)
487
+ reparsed = Expressir::Express::Parser.from_file("temp.exp")
488
+
489
+ # Format again - should be identical (stable formatting)
490
+ formatted2 = formatter.format(reparsed)
491
+
492
+ puts "Formatting is stable" if formatted1 == formatted2
493
+ ----
494
+ ====
495
+
496
+ === Clean schema
497
+
498
+ The `clean` command strips remarks and prettifies EXPRESS schemas. This is
499
+ useful for removing all documentation comments while maintaining the schema's
500
+ functional definition. You can optionally save the result to a file.
501
+
502
+ [source, sh]
503
+ ----
504
+ # Output to stdout
505
+ expressir clean schemas/resources/action_schema/action_schema.exp
506
+
507
+ # Save to file
508
+ expressir clean schemas/resources/action_schema/action_schema.exp --output clean_schema.exp
509
+ ----
510
+
511
+ [options="header"]
512
+ |===
513
+ | Option | Description
514
+ | `--output PATH` | Path to save the cleaned schema (optional, defaults to stdout)
515
+ |===
516
+
517
+ === Expand schema (concatenated artifact)
518
+
519
+ The `expand` command flattens the interface closure of a root EXPRESS schema
520
+ into a single concatenated artifact (one section per source schema, ordered
521
+ alphabetically — the same shape eeng emits for `--concat_schema`).
522
+
523
+ [source, sh]
524
+ ----
525
+ expressir expand schemas/resources/action_schema/action_schema.exp \
526
+ --output action-concatenated.exp
527
+ ----
528
+
529
+ [options="header"]
530
+ |===
531
+ | Option | Description
532
+ | `--output PATH`, `-o PATH` | Write the artifact to PATH (defaults to stdout)
533
+ | `--manifest PATH` | ELF schema manifest YAML defining where schemas live (primary
534
+ resolution mode)
535
+ | `--stepmod DIR` | STEPmod checkout root — explicit opt-in to directory-convention
536
+ resolution as the fallback
537
+ |===
538
+
539
+ The closure is resolved by scanning `USE FROM` / `REFERENCE FROM` names in the
540
+ root source. Schema resolution order: `--manifest PATH` (an ELF schema
541
+ manifest — explicit, layout-independent) first; `--stepmod DIR` (the STEPmod
542
+ directory convention) as an explicit fallback; otherwise the file's own
543
+ directory, with a failed lookup warning and pointing at the two explicit
544
+ modes.
545
+
546
+ === Flatten schema (SHTOLO longform)
547
+
548
+ The `flatten` command converts a STEPmod-style multi-schema root into a single
549
+ ISO 10303-11:1994 "longform" schema (expressir#32). It runs the Annex G
550
+ conversion locally: interface-closure copy with rename resolution and name
551
+ munging, then extensible enum/select resolution, subtype-constraint
552
+ elimination, RENAMED→DERIVE, GENERIC_ENTITY→GENERIC.
553
+
554
+ [source, sh]
555
+ ----
556
+ expressir flatten schemas/resources/action_schema/action_schema.exp \
557
+ --output action_lf.exp --longform_name action_schema_lf
558
+ ----
559
+
560
+ [options="header"]
561
+ |===
562
+ | Option | Description
563
+ | `--output PATH`, `-o PATH` | Write the longform schema to PATH (defaults to stdout)
564
+ | `--longform_name ID` | Identifier for the resulting SCHEMA (default: `<root>_lf`)
565
+ | `--extenders all|none` | `all` (default, the WG12 requirement): fold every extensible
566
+ SELECT/ENUMERATION extension in the closure into its base type — for
567
+ `EXTENSIBLE GENERIC_ENTITY SELECT` that means every entity the closure
568
+ carries. `none`: leave extensible types exactly as declared.
569
+ | `--manifest PATH` | ELF schema manifest YAML defining where schemas live (primary
570
+ resolution mode)
571
+ | `--stepmod DIR` | STEPmod checkout root — explicit opt-in to directory-convention
572
+ resolution as the fallback
573
+ |===
574
+
575
+ The longform output is verified against Express Engine: for
576
+ `description_assignment`, `eengine --compare` of our longform against
577
+ eengine's own reference artifact reports *No differences detected*
578
+ (`spec/expressir/express/shtolo_eengine_oracle_differential_spec.rb`).
579
+
580
+ === Fix schema (self-schema references)
581
+
582
+ The `fix` command rewrites string literals that qualify an item with the
583
+ CURRENT schema's name — `'THIS_SCHEMA.ITEM'` inside THIS_SCHEMA is
584
+ technically incorrect: the item usually comes from another schema,
585
+ imported implicitly through a USEd supertype. Local items drop the
586
+ prefix; foreign items gain their true defining schema
587
+ (`'GEOMETRY_SCHEMA.B_SPLINE_SURFACE'`); unknown items are left untouched.
588
+
589
+ [source, sh]
590
+ ----
591
+ expressir fix schemas/aic_topologically_bounded_surface.exp --output fixed.exp
592
+ ----
593
+
594
+ === Validate schema
595
+
596
+ The `validate load` command performs validation checks on EXPRESS schema files.
597
+
598
+ It verifies:
599
+
600
+ . That the schema can be parsed correctly into the EXPRESS data model
601
+ . That the schema includes a version string
602
+
603
+ [source, sh]
604
+ ----
605
+ # Validate a single schema
606
+ expressir validate load schemas/resources/action_schema/action_schema.exp
607
+
608
+ # Validate multiple schemas
609
+ expressir validate load schemas/resources/action_schema/action_schema.exp schemas/resources/approval_schema/approval_schema.exp
610
+
611
+ # Validate schemas from a schema manifest YAML
612
+ expressir validate load schemas.yml
613
+ ----
614
+
615
+ The command reports any schemas that:
616
+
617
+ * Failed to parse into the EXPRESS data model
618
+ * Are missing a version string
619
+
620
+ If all validations pass, it will display "Validation passed for all EXPRESS schemas."
621
+
622
+ === Validate semantic checks (eeng check-p11)
623
+
624
+ The `validate check` command runs the eeng `check-p11` note subset
625
+ implemented in `Expressir::Express::Checker`: redundant interfaces,
626
+ duplicate interface resources, unresolved interface schema/resource
627
+ refs, SUBTYPE OF targets, SELECT/ENUM BASED_ON targets, and WHERE/UNIQUE
628
+ label patterns.
629
+
630
+ [source, sh]
631
+ ----
632
+ # Check a single file
633
+ expressir validate check schemas/resources/action_schema/action_schema.exp
634
+
635
+ # Check all .exp files under a directory
636
+ expressir validate check schemas/resources/
637
+
638
+ # Resolve the closure through an ELF schema manifest (or --stepmod DIR)
639
+ expressir validate check modules/description_assignment/mim.exp \
640
+ --manifest schemas-srl.yaml
641
+
642
+ # Exit 1 when any :error note fires
643
+ expressir validate check schemas/resources/action_schema/action_schema.exp
644
+ ----
645
+
646
+ === Validate ASCII content
647
+
648
+ The `validate ascii` command validates that EXPRESS schema files contain only
649
+ ASCII characters outside of remarks. This ensures compatibility with systems
650
+ that don't support Unicode, while allowing Unicode in documentation comments.
651
+
652
+ [source, sh]
653
+ ----
654
+ # Validate a single file
655
+ expressir validate ascii schema.exp
656
+
657
+ # Validate schema manifest YAML
658
+ expressir validate ascii schemas.yml
659
+
660
+ # Validate directory (non-recursive)
661
+ expressir validate ascii schemas/
662
+
663
+ # Validate directory recursively
664
+ expressir validate ascii schemas/ --recursive
665
+
666
+ # Output in YAML format
667
+ expressir validate ascii schemas/ --yaml
668
+
669
+ # Check remarks as well (include remarks in validation)
670
+ expressir validate ascii schema.exp --check-remarks
671
+ ----
672
+
673
+ The command checks that all EXPRESS code (excluding remarks) contains only
674
+ 7-bit ASCII characters. Tagged remarks (both embedded `(* ... *)` and tail
675
+ `-- ...` comments) are excluded from validation, allowing documentation to
676
+ contain Unicode without triggering errors.
677
+
678
+ [options="header"]
679
+ |===
680
+ | Option | Description
681
+
682
+ | `--recursive`, `-r`
683
+ | Validate EXPRESS files under the specified path recursively
684
+
685
+ | `--yaml`, `-y`
686
+ | Output results in YAML format for programmatic processing
687
+
688
+ | `--check-remarks`
689
+ | Include remarks in ASCII validation (default: false, remarks are excluded)
690
+ |===
691
+
692
+ The validator provides:
693
+
694
+ * Detailed violation reports with line and column numbers
695
+ * Replacement suggestions (AsciiMath for math symbols, ISO 10303-11 encoding for others)
696
+ * Summary statistics
697
+ * Visual indicators for non-ASCII sequences
698
+
699
+ This command is particularly useful before exporting schemas to formats that
700
+ don't support Unicode, such as EEP (Express Engine Parser) and eengine (Express
701
+ Engine).
702
+
703
+
704
+ [example]
705
+ ====
706
+ [source, sh]
707
+ ----
708
+ $ expressir validate ascii spec/fixtures/validate_ascii/non_ascii_in_code.exp
709
+
710
+ spec/fixtures/validate_ascii/non_ascii_in_code.exp:
711
+ Line 3, Column 9:
712
+ ENTITY 製品;
713
+ ^^ Non-ASCII sequence
714
+ "製" - Hex: 0x88fd, UTF-8 bytes: 0xe8 0xa3 0xbd
715
+ Replacement: ISO 10303-11: "000088FD"
716
+ "品" - Hex: 0x54c1, UTF-8 bytes: 0xe5 0x93 0x81
717
+ Replacement: ISO 10303-11: "000054C1"
718
+
719
+ ...
720
+ Line 10, Column 24:
721
+ symbol : STRING := 'μ';
722
+ ^ Non-ASCII sequence
723
+ "μ" - Hex: 0x3bc, UTF-8 bytes: 0xce 0xbc
724
+ Replacement: AsciiMath: mu
725
+
726
+ ...
727
+
728
+ Found 7 non-ASCII sequence(s) in non_ascii_in_code.exp
729
+
730
+ Summary:
731
+ Scanned 1 EXPRESS file(s)
732
+ Found 7 non-ASCII sequence(s) in 1 file(s)
733
+
734
+ ╭──────────────────────────────────────────────────────────────────────────╮
735
+ │ Non-ASCII Characters Summary │
736
+ ├──────────────────────────┬─────────────┬────────────────────┬────────────┤
737
+ │ File │ Symbol │ Replacement │ Occurrenc… │
738
+ ├──────────────────────────┼─────────────┼────────────────────┼────────────┤
739
+ │ validate_ascii/non_asci… │ "製" (0x88… │ ISO 10303-11: "00… │ 1 │
740
+ │ validate_ascii/non_asci… │ "品" (0x54… │ ISO 10303-11: "00… │ 1 │
741
+ │ ... │ │ │ │
742
+ │ validate_ascii/non_asci… │ "μ" (0x3bc) │ AsciiMath: mu │ 1 │
743
+ │ ... │ │ │ │
744
+ │ TOTAL │ 10 unique │ — │ 11 │
745
+ ╰──────────────────────────┴─────────────┴────────────────────┴────────────╯
746
+ ----
747
+ ====
748
+
749
+
750
+ === Version
751
+
752
+ The `version` command displays the current version of the Expressir gem.
753
+
754
+ [source, sh]
755
+ ----
756
+ expressir version
757
+ ----
758
+
759
+ === Benchmarking
760
+
761
+ Expressir includes powerful benchmarking capabilities for measuring schema
762
+ loading performance. You can benchmark individual files or multiple files listed
763
+ in a YAML configuration.
764
+
765
+ ==== Benchmark a single file
766
+
767
+ [source, sh]
768
+ ----
769
+ # Basic benchmarking
770
+ expressir benchmark schemas/resources/action_schema/action_schema.exp
771
+
772
+ # With detailed output
773
+ expressir benchmark schemas/resources/action_schema/action_schema.exp --verbose
774
+
775
+ # Using benchmark-ips for more detailed statistics
776
+ expressir benchmark schemas/resources/action_schema/action_schema.exp --ips
777
+
778
+ # With specific output format
779
+ expressir benchmark schemas/resources/action_schema/action_schema.exp --format json
780
+ ----
781
+
782
+ ==== Benchmark multiple files from schema manifest
783
+
784
+ Create a schema manifest YAML file with a list of schema paths:
785
+
786
+ .schemas.yml
787
+ [source, yaml]
788
+ ----
789
+ schemas:
790
+ - path: schemas/resources/action_schema/action_schema.exp
791
+ - path: schemas/resources/approval_schema/approval_schema.exp
792
+ - path: schemas/resources/date_time_schema/date_time_schema.exp
793
+ ----
794
+
795
+ Then benchmark all schemas at once:
796
+
797
+ [source, sh]
798
+ ----
799
+ expressir benchmark schemas.yml --verbose
800
+ ----
801
+
802
+ ==== Benchmark with caching
803
+
804
+ You can also benchmark schema loading with caching to measure parsing time,
805
+ cache writing time, and cache reading time:
806
+
807
+ [source, sh]
808
+ ----
809
+ # Benchmark a single file with caching
810
+ expressir benchmark-cache schemas/resources/action_schema/action_schema.exp
811
+
812
+ # With custom cache location
813
+ expressir benchmark-cache schemas/resources/action_schema/action_schema.exp --cache_path /tmp/schema_cache.bin
814
+
815
+ # Benchmark multiple files from YAML with caching
816
+ expressir benchmark-cache schemas.yml --verbose
817
+ ----
818
+
819
+ ==== Benchmark options
820
+
821
+ The benchmark commands support several options:
822
+
823
+ [options="header"]
824
+ |===
825
+ | Option | Description
826
+ | `--ips` | Use benchmark-ips for detailed statistics
827
+ | `--verbose` | Show detailed output
828
+ | `--save` | Save benchmark results to file
829
+ | `--format FORMAT` | Output format: json, csv, or default
830
+ | `--cache_path PATH` | (benchmark-cache only) Path to store the cache file
831
+ |===
832
+
833
+ When using the `--format json` option, results will be output in JSON format,
834
+ making it easy to parse for further analysis or visualization.
835
+
836
+ === Documentation coverage
837
+
838
+ Expressir can analyze EXPRESS schemas to check for documentation coverage. This helps
839
+ identify which entities are properly documented with remarks and which ones require
840
+ documentation.
841
+
842
+ ==== Analyzing documentation coverage
843
+
844
+ Use the `coverage` command to check documentation coverage of EXPRESS schemas:
845
+
846
+ [source, sh]
847
+ ----
848
+ # Analyze a single EXPRESS file
849
+ expressir coverage schemas/resources/action_schema/action_schema.exp
850
+
851
+ # Analyze multiple EXPRESS files
852
+ expressir coverage schemas/resources/action_schema/action_schema.exp schemas/resources/approval_schema/approval_schema.exp
853
+
854
+ # Analyze all EXPRESS files in a directory (recursively)
855
+ expressir coverage schemas/resources/
856
+
857
+ # Analyze files specified in a YAML file
858
+ expressir coverage schemas.yml
859
+ ----
860
+
861
+ The output shows which entities are missing documentation, calculates coverage percentages,
862
+ and provides an overall documentation coverage summary.
863
+
864
+ ==== Coverage options
865
+
866
+ The coverage command supports different output formats and exclusion options:
867
+
868
+ [options="header"]
869
+ |===
870
+ | Option | Description
871
+ | `--format text` | (Default) Display a human-readable table with coverage information
872
+ | `--format json` | Output in JSON format for programmatic processing
873
+ | `--format yaml` | Output in YAML format for programmatic processing
874
+ | `--exclude TYPES` | Comma-separated list of EXPRESS entity types to exclude from coverage analysis
875
+ | `--ignore-files PATH` | Path to YAML file containing array of files to ignore from overall coverage calculation
876
+ |===
877
+
878
+ ==== Excluding entity types from coverage
879
+
880
+ You can exclude specific EXPRESS entity types from coverage analysis using the
881
+ `--exclude` option.
882
+
883
+ This is useful when certain entity types don't require documentation coverage,
884
+ such as TYPE entities whose descriptions are generated by template strings.
885
+
886
+ [source, sh]
887
+ ----
888
+ # Exclude TYPE entities from coverage analysis
889
+ expressir coverage --exclude=TYPE schemas/resources/action_schema/action_schema.exp
890
+
891
+ # Exclude only SELECT type definitions (useful since TYPE descriptions are often template-generated)
892
+ expressir coverage --exclude=TYPE:SELECT schemas/resources/action_schema/action_schema.exp
893
+
894
+ # Exclude multiple entity types
895
+ expressir coverage --exclude=TYPE,CONSTANT,FUNCTION schemas/resources/action_schema/action_schema.exp
896
+
897
+ # Exclude parameters and variables (often don't require individual documentation)
898
+ expressir coverage --exclude=PARAMETER,VARIABLE schemas/resources/action_schema/action_schema.exp
899
+
900
+ # Combine with output format options
901
+ expressir coverage --exclude=TYPE:SELECT --format=json schemas/resources/action_schema/action_schema.exp
902
+ ----
903
+
904
+ ==== Elements checked in documentation coverage
905
+
906
+ Expressir checks documentation coverage for all EXPRESS elements (ModelElement
907
+ subclasses), including:
908
+
909
+ Schema-level entities:
910
+
911
+ `TYPE`:: Type definitions (supports subtype exclusion, see below)
912
+ `ENTITY`:: Entity definitions
913
+ `CONSTANT`:: Constant definitions
914
+ `FUNCTION`:: Function definitions
915
+ `RULE`:: Rule definitions
916
+ `PROCEDURE`:: Procedure definitions
917
+ `SUBTYPE_CONSTRAINT`:: Subtype constraint definitions
918
+ `INTERFACE`:: Interface definitions
919
+
920
+ Nested entities within other constructs:
921
+
922
+ `PARAMETER`:: Function and procedure parameters
923
+ `VARIABLE`:: Variables within functions, rules, and procedures
924
+ `ATTRIBUTE`:: Entity attributes
925
+ `DERIVED_ATTRIBUTE`:: Derived attributes in entities
926
+ `INVERSE_ATTRIBUTE`:: Inverse attributes in entities
927
+ `UNIQUE_RULE`:: Unique rules within entities
928
+ `WHERE_RULE`:: Where rules within entities and types
929
+ `ENUMERATION_ITEM`:: Items within enumeration types
930
+ `INTERFACE_ITEM`:: Items within interfaces
931
+ `INTERFACED_ITEM`:: Interfaced items
932
+ `SCHEMA_VERSION`:: Schema version information
933
+ `SCHEMA_VERSION_ITEM`:: Schema version items
934
+
935
+ ==== TYPE subtype exclusion
936
+
937
+ For TYPE elements, you can exclude specific subtypes using the `TYPE:SUBTYPE`
938
+ syntax:
939
+
940
+ [source, sh]
941
+ ----
942
+ # Exclude only SELECT types
943
+ expressir coverage --exclude=TYPE:SELECT schemas/resources/action_schema/action_schema.exp
944
+
945
+ # Exclude multiple TYPE subtypes
946
+ expressir coverage --exclude=TYPE:SELECT,TYPE:ENUMERATION schemas/resources/action_schema/action_schema.exp
947
+ ----
948
+
949
+ ==== FUNCTION subtype exclusion
950
+
951
+ For FUNCTION elements, you can exclude inner functions (functions nested within
952
+ other functions, rules, or procedures) using the `FUNCTION:INNER` syntax:
953
+
954
+ [source, sh]
955
+ ----
956
+ # Exclude inner functions from coverage analysis
957
+ expressir coverage --exclude=FUNCTION:INNER schemas/resources/action_schema/action_schema.exp
958
+
959
+ # Combine with other exclusions
960
+ expressir coverage --exclude=TYPE:SELECT,FUNCTION:INNER schemas/resources/action_schema/action_schema.exp
961
+ ----
962
+
963
+ This is useful when you want to focus documentation coverage on top-level
964
+ functions while excluding nested helper functions that may not require
965
+ individual documentation. The exclusion works recursively, excluding functions
966
+ at any nesting level within other constructs.
967
+
968
+ Valid FUNCTION subtypes that can be excluded:
969
+
970
+ `INNER`:: Inner functions nested within other functions, rules, or procedures (at any depth)
971
+ +
972
+ [example]
973
+ ====
974
+ ----
975
+ FUNCTION outer_function : BOOLEAN;
976
+ -- This inner function would be excluded with FUNCTION:INNER
977
+ FUNCTION inner_helper_function : BOOLEAN;
978
+ -- Even deeply nested functions are excluded
979
+ FUNCTION deeply_nested_function : BOOLEAN;
980
+ RETURN (TRUE);
981
+ END_FUNCTION;
982
+ RETURN (TRUE);
983
+ END_FUNCTION;
984
+
985
+ RETURN (TRUE);
986
+ END_FUNCTION;
987
+
988
+ RULE example_rule FOR (some_entity);
989
+ -- Inner functions in rules are also excluded
990
+ FUNCTION inner_function_in_rule : BOOLEAN;
991
+ RETURN (TRUE);
992
+ END_FUNCTION;
993
+ WHERE
994
+ WR1: inner_function_in_rule();
995
+ END_RULE;
996
+
997
+ PROCEDURE example_procedure;
998
+ -- Inner functions in procedures are also excluded
999
+ FUNCTION inner_function_in_procedure : BOOLEAN;
1000
+ RETURN (TRUE);
1001
+ END_FUNCTION;
1002
+ END_PROCEDURE;
1003
+ ----
1004
+ ====
1005
+
1006
+ The `FUNCTION:INNER` exclusion helps maintain focus on documenting the primary
1007
+ API functions while ignoring implementation details of nested helper functions.
1008
+
1009
+ ==== Ignoring files from coverage calculation
1010
+
1011
+ You can exclude entire files from the overall coverage calculation using the
1012
+ `--ignore-files` option. This is useful when you have files that should not
1013
+ contribute to the overall documentation coverage statistics, such as test
1014
+ schemas, example files, or legacy schemas.
1015
+
1016
+ [source, sh]
1017
+ ----
1018
+ # Use ignore files to exclude specific files from coverage calculation
1019
+ expressir coverage --ignore-files ignore_list.yaml schemas/resources/
1020
+
1021
+ # Combine with other options
1022
+ expressir coverage --ignore-files ignore_list.yaml --exclude=TYPE:SELECT --format=json schemas/resources/
1023
+ ----
1024
+
1025
+ ===== Ignore files YAML format
1026
+
1027
+ The ignore files YAML should contain an array of file patterns. Each pattern
1028
+ can be either an exact file path or use glob patterns for matching multiple files.
1029
+
1030
+ .ignore_list.yaml
1031
+ [source, yaml]
1032
+ ----
1033
+ # Array of file patterns to ignore
1034
+ - examples/test_schema.exp # Exact file path
1035
+ - examples/*_test_*.exp # Glob pattern for test files
1036
+ - legacy/old_*.exp # Glob pattern for legacy files
1037
+ - temp/temporary_schema.exp # Another exact path
1038
+ ----
1039
+
1040
+ ===== Pattern matching behavior
1041
+
1042
+ File patterns in the ignore files YAML support:
1043
+
1044
+ * **Exact paths**: Match specific files exactly
1045
+ * **Glob patterns**: Use `*` for wildcard matching
1046
+ * **Relative paths**: Patterns are resolved relative to the YAML file's directory
1047
+ * **Absolute paths**: Full system paths are also supported
1048
+
1049
+ [source, yaml]
1050
+ ----
1051
+ # Examples of different pattern types
1052
+ - schemas/action_schema/action_schema.exp # Exact relative path
1053
+ - /full/path/to/schema.exp # Absolute path
1054
+ - schemas/**/test_*.exp # Recursive glob pattern
1055
+ - temp/*.exp # All .exp files in temp directory
1056
+ ----
1057
+
1058
+ ===== Behavior of ignored files
1059
+
1060
+ When files are ignored using the `--ignore-files` option:
1061
+
1062
+ . **Excluded from overall statistics**: Ignored files do not contribute to the
1063
+ overall coverage percentage calculation
1064
+
1065
+ . **Still processed and reported**: Ignored files are still analyzed and appear
1066
+ in the output, but marked with an `ignored: true` flag
1067
+
1068
+ . **Separate reporting section**: In JSON/YAML output formats, ignored files
1069
+ appear in both the main `files` section (with the ignored flag) and in a
1070
+ separate `ignored_files` section
1071
+
1072
+ . **Overall statistics updated**: The overall statistics include additional
1073
+ fields showing the count of ignored files and entities
1074
+
1075
+ .Example JSON output with ignored files:
1076
+ [source, json]
1077
+ ----
1078
+ {
1079
+ "overall": {
1080
+ "coverage_percentage": 75.0,
1081
+ "total_entities": 100,
1082
+ "documented_entities": 75,
1083
+ "undocumented_entities": 25,
1084
+ "ignored_files_count": 2,
1085
+ "ignored_entities_count": 15
1086
+ },
1087
+ "files": [
1088
+ {
1089
+ "file": "schemas/main_schema.exp",
1090
+ "ignored": false,
1091
+ "coverage": 80.0,
1092
+ "total": 50,
1093
+ "documented": 40,
1094
+ "undocumented": ["entity1", "entity2"]
1095
+ },
1096
+ {
1097
+ "file": "examples/test_schema.exp",
1098
+ "ignored": true,
1099
+ "matched_pattern": "examples/*_test_*.exp",
1100
+ "coverage": 20.0,
1101
+ "total": 10,
1102
+ "documented": 2,
1103
+ "undocumented": ["test_entity1", "test_entity2"]
1104
+ }
1105
+ ],
1106
+ "ignored_files": [
1107
+ {
1108
+ "file": "examples/test_schema.exp",
1109
+ "matched_pattern": "examples/*_test_*.exp",
1110
+ "coverage": 20.0,
1111
+ "total": 10,
1112
+ "documented": 2,
1113
+ "undocumented": ["test_entity1", "test_entity2"]
1114
+ }
1115
+ ]
1116
+ }
1117
+ ----
1118
+
1119
+ ===== Error handling
1120
+
1121
+ The ignore files functionality handles various error conditions gracefully:
1122
+
1123
+ * **Missing YAML file**: If the specified ignore files YAML doesn't exist, a
1124
+ warning is displayed and coverage analysis continues normally
1125
+
1126
+ * **Invalid YAML format**: If the YAML file is malformed or doesn't contain an
1127
+ array, a warning is displayed and the file is ignored
1128
+
1129
+ * **Non-matching patterns**: Patterns that don't match any files are silently
1130
+ ignored (no error or warning)
1131
+
1132
+ * **Permission errors**: File access errors are reported as warnings
1133
+
1134
+ ===== Use cases for ignore files
1135
+
1136
+ Common scenarios where ignore files are useful:
1137
+
1138
+ * **Test schemas**: Exclude test or example schemas from production coverage metrics
1139
+ * **Legacy files**: Ignore old schemas that are being phased out
1140
+ * **Generated files**: Exclude automatically generated schemas
1141
+ * **Work-in-progress**: Temporarily ignore files under development
1142
+ * **Different coverage standards**: Apply different documentation standards to different file sets
1143
+
1144
+ Valid TYPE subtypes that can be excluded:
1145
+
1146
+ `AGGREGATE`:: Aggregate type
1147
+ `ARRAY`:: Array type
1148
+ `BAG`:: Bag type
1149
+ `BINARY`:: Binary type
1150
+ `BOOLEAN`:: Boolean type
1151
+ `ENUMERATION`:: Enumeration type
1152
+ +
1153
+ [example]
1154
+ ====
1155
+ ----
1156
+ TYPE uuid_relationship_role = ENUMERATION OF
1157
+ (supersedes,
1158
+ merge,
1159
+ split,
1160
+ derive_from,
1161
+ same_as,
1162
+ similar_to);
1163
+ END_TYPE;
1164
+ ----
1165
+ ====
1166
+
1167
+ `GENERIC`:: Generic type
1168
+ `GENERIC_ENTITY`:: Generic entity type
1169
+ +
1170
+ [example]
1171
+ ====
1172
+ ----
1173
+ TYPE uuid_attribute_select = EXTENSIBLE GENERIC_ENTITY SELECT;
1174
+ END_TYPE;
1175
+ ----
1176
+ ====
1177
+
1178
+ `INTEGER`:: Integer type
1179
+ `LIST`:: List type
1180
+ +
1181
+ [example]
1182
+ ====
1183
+ ----
1184
+ TYPE uuid_list_item = LIST [1:?] OF UNIQUE LIST [1:?] OF UNIQUE uuid_attribute_select;
1185
+ END_TYPE;
1186
+ ----
1187
+ ====
1188
+
1189
+ `LOGICAL`:: Logical type
1190
+ `NUMBER`:: Number type
1191
+ `REAL`:: Real type
1192
+ `SELECT`:: Select type
1193
+ +
1194
+ [example]
1195
+ ====
1196
+ ----
1197
+ TYPE uuid_set_or_list_attribute_select = SELECT
1198
+ (uuid_list_item,
1199
+ uuid_set_item);
1200
+ END_TYPE;
1201
+ ----
1202
+ ====
1203
+
1204
+ `SET`:: Set type
1205
+ +
1206
+ [example]
1207
+ ====
1208
+ ----
1209
+ TYPE uuid_set_item = SET [1:?] OF uuid_attribute_select;
1210
+ END_TYPE;
1211
+ ----
1212
+ ====
1213
+
1214
+ `STRING`::
1215
+ String type
1216
+ +
1217
+ [example]
1218
+ ====
1219
+ ----
1220
+ TYPE uuid = STRING (36) FIXED;
1221
+ END_TYPE;
1222
+ ----
1223
+ ====
1224
+
1225
+ This is particularly useful since TYPE entities with certain subtypes (like
1226
+ SELECT) often have descriptions generated by template strings and may not
1227
+ require individual remark item coverage.
1228
+
1229
+ NOTE: ISO 10303 excludes documentation coverage for TYPE:SELECT and
1230
+ TYPE:ENUMERATION.
1231
+
1232
+ If you specify an invalid entity type or subtype, the command will display an
1233
+ error message with the list of valid options.
1234
+
1235
+ .Example with JSON output:
1236
+ [example]
1237
+ ====
1238
+ [source, sh]
1239
+ ----
1240
+ expressir coverage schemas/resources/ --format json
1241
+ ----
1242
+ ====
1243
+
1244
+ When using JSON or YAML output formats, all file and directory paths are
1245
+ displayed relative to the current working directory:
1246
+
1247
+ [source, yaml]
1248
+ ----
1249
+ - file: "schemas/resources/action_schema/action_schema.exp"
1250
+ file_basename: action_schema.exp
1251
+ directory: "schemas/resources/action_schema"
1252
+ # ... other fields
1253
+ ----
1254
+
1255
+ ==== Coverage output
1256
+
1257
+ The default text output displays:
1258
+
1259
+ . Directory coverage (when analyzing multiple directories)
1260
+
1261
+ . File coverage, showing:
1262
+ ** File path
1263
+ ** List of undocumented entities
1264
+ ** Coverage percentage
1265
+
1266
+ . Overall documentation coverage statistics
1267
+
1268
+ This helps identify areas of your EXPRESS schemas that need documentation
1269
+ improvement.
1270
+
1271
+ === Schema hyperlinked source faces
1272
+
1273
+ `Schema#source`, `Schema#source_hyperlinked`, and
1274
+ `Schema#formatted_hyperlinked(_adoc)` render a schema head or the full
1275
+ schema with cross-references either verbatim or as links (the adoc
1276
+ variant emits `<<schema.item,item>>` xref macros — render inside a
1277
+ `[source,subs="+macros"]` block for live links; see the
1278
+ `Expressir::Express::SmrlXml` and `Expressir::Express::Xsd` writers
1279
+ for the XML faces).
1280
+
1281
+ == MIM mapping validation (mapping.yaml)
1282
+
1283
+ `expressir mapping_validate PATH` loads a module `mapping.yaml`
1284
+ (lutaml-model typed: `Expressir::Mapping::Document`) and resolves every
1285
+ `<<express:SCHEMA.ITEM,ITEM>>` link against the ARM/MIM interface
1286
+ closure, reporting links whose schema or item is unknown.
1287
+
1288
+ == XML faces
1289
+
1290
+ - `Expressir::Express::SmrlXml.format(repository)` — the SMRL index
1291
+ block per schema (eeng wo-smrl-xml shape).
1292
+ - `Expressir::Express::Xsd.format(schema)` — an XML Schema rendering
1293
+ (entities as element/complexType, substitutionGroup for subtypes,
1294
+ enumeration facets).
1295
+
1296
+ == Lazy compiled-set access
1297
+
1298
+ `Expressir::Express::Core.lazy_set(artifact)` lists a compiled set's
1299
+ wire paths without hydrating and hydrates individual files on demand —
1300
+ renderers that touch one schema per page hydrate one schema per page.
1301
+
1302
+ == EXPRESS Changes files
1303
+
1304
+ Expressir provides commands for working with EXPRESS Changes files that track
1305
+ schema modifications across versions.
1306
+
1307
+ ==== Validating change files
1308
+
1309
+ The `changes validate` command validates EXPRESS Changes YAML files and
1310
+ optionally normalizes them through round-trip serialization.
1311
+
1312
+ [source, sh]
1313
+ ----
1314
+ # Validate a changes file
1315
+ expressir changes validate schema.changes.yaml
1316
+
1317
+ # Validate with verbose output
1318
+ expressir changes validate schema.changes.yaml --verbose
1319
+
1320
+ # Validate and normalize (outputs to stdout)
1321
+ expressir changes validate schema.changes.yaml --normalize
1322
+
1323
+ # Validate and normalize in-place
1324
+ expressir changes validate schema.changes.yaml --normalize --in-place
1325
+
1326
+ # Validate and save normalized output to a new file
1327
+ expressir changes validate schema.changes.yaml --normalize --output normalized.yaml
1328
+ ----
1329
+
1330
+ [options="header"]
1331
+ |===
1332
+ | Option | Description
1333
+ | `--normalize` | Normalize file through round-trip serialization
1334
+ | `--in-place` | Update file in place (requires `--normalize`)
1335
+ | `--output PATH` | Output file path for normalized output
1336
+ | `--verbose` | Show verbose output with validation details
1337
+ |===
1338
+
1339
+ The validate command performs the following checks:
1340
+
1341
+ . Verifies the YAML file can be parsed
1342
+ . Validates against the SchemaChange model structure
1343
+ . Ensures all required fields are present
1344
+ . Checks that change items have valid types
1345
+
1346
+ When using `--normalize`, the command:
1347
+
1348
+ . Loads the file and validates it
1349
+ . Serializes it back to YAML with consistent formatting
1350
+ . Either outputs to stdout, saves in-place, or writes to a new file
1351
+
1352
+ This is useful for:
1353
+
1354
+ * **Standardizing formatting**: Ensures consistent YAML structure
1355
+ * **Catching errors early**: Validates before committing changes
1356
+ * **Cleaning up files**: Removes inconsistencies in formatting
1357
+
1358
+ ==== Importing from Express Engine comparison report XML
1359
+
1360
+ The `changes import-eengine` command converts Express Engine (eengine) comparison
1361
+ XML files to EXPRESS Changes YAML format.
1362
+
1363
+ The eengine compare XML format is created through
1364
+ `exp-engine-engine/kernel/compare.lisp` in the Express Engine code. Expressir is
1365
+ compatible with v5.2.7 of eengine output.
1366
+
1367
+ The import command:
1368
+
1369
+ . Parses the eengine XML comparison file
1370
+ . Automatically detects the XML mode (Schema/ARM/MIM)
1371
+ . Extracts additions, modifications, and deletions
1372
+ . Converts modified objects to EXPRESS Changes item format
1373
+ . Preserves interface change information (`interfaced.items`)
1374
+ . Creates or updates an EXPRESS Changes YAML file
1375
+ . Supports appending new versions to existing files
1376
+
1377
+ When the output file already exists:
1378
+
1379
+ * **Same version**: Replaces the existing version with that version
1380
+ * **New version**: Adds a new version to the file
1381
+
1382
+ This allows you to build up a complete change history incrementally across
1383
+ multiple schema versions.
1384
+
1385
+ Syntax:
1386
+
1387
+ [source, sh]
1388
+ ----
1389
+ # Basic syntax
1390
+ expressir changes import-eengine INPUT_XML SCHEMA_NAME VERSION [options]
1391
+
1392
+ # Import and output to stdout
1393
+ expressir changes import-eengine comparison.xml schema_name "2"
1394
+
1395
+ # Import and save to file
1396
+ expressir changes import-eengine comparison.xml schema_name "2" -o output.yaml
1397
+
1398
+ # Import with verbose output
1399
+ expressir changes import-eengine comparison.xml schema_name "2" -o output.yaml --verbose
1400
+
1401
+ # Append to existing changes file
1402
+ expressir changes import-eengine comparison.xml schema_name "3" -o existing.yaml
1403
+ ----
1404
+
1405
+ Where:
1406
+
1407
+ `INPUT_XML`:: Path to the eengine comparison XML file
1408
+ `SCHEMA_NAME`:: Name of the schema being tracked (e.g., `aic_csg`, `action_schema`)
1409
+ `VERSION`:: Version number for this set of changes (e.g., `"2"`, `"3"`)
1410
+
1411
+ Options:
1412
+
1413
+ `-o, --output PATH`:: Output YAML file path (stdout if not specified)
1414
+ `--verbose`:: Show verbose output including parsed items
1415
+
1416
+ The command automatically detects and handles all three eengine XML comparison
1417
+ formats:
1418
+
1419
+ Schema mode:: `<schema.changes>` root element with `<schema.additions>`,
1420
+ `<schema.modifications>`, and `<schema.deletions>` sections. See API docs for
1421
+ details.
1422
+
1423
+ Example workflow for importing changes from multiple versions:
1424
+
1425
+ .Importing multiple versions incrementally
1426
+ [example]
1427
+ [source, sh]
1428
+ ----
1429
+ # Import version 2 changes
1430
+ expressir changes import-eengine v2_comparison.xml action_schema "2" \
1431
+ -o action_schema.changes.yaml
1432
+
1433
+ # Import version 3 changes (appends to existing file)
1434
+ expressir changes import-eengine v3_comparison.xml action_schema "3" \
1435
+ -o action_schema.changes.yaml
1436
+
1437
+ # Import version 4 changes (appends to existing file)
1438
+ expressir changes import-eengine v4_comparison.xml action_schema "4" \
1439
+ -o action_schema.changes.yaml
1440
+
1441
+ # Verify the result
1442
+ cat action_schema.changes.yaml
1443
+ ----
1444
+
1445
+ This creates a single YAML file tracking changes across all three versions:
1446
+
1447
+ [source,yaml]
1448
+ ----
1449
+ schema: action_schema
1450
+ versions:
1451
+ - version: 2
1452
+ description: Changes from eengine comparison
1453
+ additions: [...]
1454
+ - version: 3
1455
+ description: Changes from eengine comparison
1456
+ additions: [...]
1457
+ - version: 4
1458
+ description: Changes from eengine comparison
1459
+ additions: [...]
1460
+ ----
1461
+
1462
+
1463
+ == Usage: Ruby
1464
+
1465
+ === Parsing EXPRESS schema files
1466
+
1467
+ ==== General
1468
+
1469
+ The library provides two main methods for parsing EXPRESS files.
1470
+
1471
+ ==== Parsing a single file
1472
+
1473
+ Use the `from_file` method to parse a single EXPRESS schema file.
1474
+ This returns an `ExpFile` object containing the parsed schemas:
1475
+
1476
+ [source,ruby]
1477
+ ----
1478
+ # Parse a single file - returns ExpFile
1479
+ exp_file = Expressir::Express::Parser.from_file("path/to/schema.exp")
1480
+
1481
+ # Access schemas from the file
1482
+ schema = exp_file.schemas.first
1483
+
1484
+ # With options
1485
+ exp_file = Expressir::Express::Parser.from_file(
1486
+ "path/to/schema.exp",
1487
+ skip_references: false, # Set to true to skip resolving references
1488
+ include_source: true, # Set to true to include original source in the model
1489
+ root_path: "/base/path" # Optional base path for relative paths
1490
+ )
1491
+ ----
1492
+
1493
+ The `from_file` method will raise a `SchemaParseFailure` exception if the schema
1494
+ fails to parse, providing information about the specific file and the parsing
1495
+ error:
1496
+
1497
+ [source,ruby]
1498
+ ----
1499
+ begin
1500
+ exp_file = Expressir::Express::Parser.from_file("path/to/schema.exp")
1501
+ rescue Expressir::Express::Error::SchemaParseFailure => e
1502
+ puts "Failed to parse schema: #{e.message}"
1503
+ puts "Filename: #{e.filename}"
1504
+ puts "Error details: #{e.parse_failure_cause.ascii_tree}"
1505
+ end
1506
+ ----
1507
+
1508
+ ==== Parsing multiple files
1509
+
1510
+ Use the `from_files` method to parse multiple EXPRESS schema files.
1511
+ This returns a `Repository` object containing all parsed files:
1512
+
1513
+ [source,ruby]
1514
+ ----
1515
+ # Parse multiple files - returns Repository
1516
+ files = ["schema1.exp", "schema2.exp", "schema3.exp"]
1517
+ repository = Expressir::Express::Parser.from_files(files)
1518
+
1519
+ # Access all schemas across all files
1520
+ repository.schemas.each do |schema|
1521
+ puts "Schema: #{schema.id}"
1522
+ end
1523
+ ----
1524
+
1525
+ You can provide a block to track loading progress and handle errors:
1526
+
1527
+ [source,ruby]
1528
+ ----
1529
+ files = ["schema1.exp", "schema2.exp", "schema3.exp"]
1530
+ repository = Expressir::Express::Parser.from_files(files) do |filename, schemas, error|
1531
+ if error
1532
+ puts "Error loading #{filename}: #{error.message}"
1533
+ # Skip the file with an error or take other action
1534
+ else
1535
+ puts "Successfully loaded #{schemas.length} schemas from #{filename}"
1536
+ end
1537
+ end
1538
+ ----
1539
+
1540
+ === Filtering out schemas
1541
+
1542
+ You can filter out specific schemas from the repository easily since
1543
+ `Expressir::Model::Repository` implements `Enumerable`.
1544
+
1545
+ [source,ruby]
1546
+ ----
1547
+ schema_yaml = YAML.load_file('documents/iso-10303-41/schemas.yaml')
1548
+ schema_paths = schema_yaml['schemas'].map {|x,y| y['path'].gsub("../../", "")}
1549
+
1550
+ repo = Expressir::Express::Parser.from_files(schema_paths)
1551
+
1552
+ filtered_schemas = ["action_schema", "date_time_schema"]
1553
+ repo.select do |schema|
1554
+ filtered_schemas.include?(schema.name)
1555
+ end.each do |schema|
1556
+ puts "Schema name: #{schema.name}"
1557
+ puts "Schema file: #{schema.file}"
1558
+ puts "Schema version: #{schema.version}"
1559
+ end
1560
+ ----
1561
+
1562
+ === Convert models to Liquid
1563
+
1564
+ Use `to_liquid` method to convert the models of `Expressir::Model::*` to liquid
1565
+ drop models (`Expressir::Liquid::*`).
1566
+
1567
+ Example:
1568
+
1569
+ [source,ruby]
1570
+ ----
1571
+ # Single file (ExpFile)
1572
+ exp_file = Expressir::Express::Parser.from_file("path/to/file.exp")
1573
+ file_drop = exp_file.to_liquid
1574
+
1575
+ # Multiple files (Repository)
1576
+ repo = Expressir::Express::Parser.from_files(["file1.exp", "file2.exp"])
1577
+ repo_drop = repo.to_liquid
1578
+ ----
1579
+
1580
+ where `repo` is an instance of `Expressir::Model::Repository` and
1581
+ `repo_drop` is an instance of `Expressir::Liquid::RepositoryDrop`.
1582
+
1583
+ The Liquid drop models of `Expressir::Liquid::*` have the same attributes
1584
+ (`model_attr`) as the models of `Expressir::Model::*`.
1585
+
1586
+ For example, `Expressir::Model::Repository` has the following attributes:
1587
+
1588
+ * `schemas`
1589
+
1590
+ and each `Expressir::Model::Declarations::Schema` has the following attributes:
1591
+
1592
+ * `file`
1593
+ * `version`
1594
+ * `interfaces`
1595
+ * `constants`
1596
+ * `entities`
1597
+ * `subtype_constraints`
1598
+ * `functions`
1599
+ * `rules`
1600
+ * `procedures`
1601
+
1602
+ Thus, `Expressir::Liquid::Repository` has the same attribute `schemas`
1603
+ and `Expressir::Liquid::Declarations::SchemaDrop` has same attribute `file`.
1604
+
1605
+ [source,ruby]
1606
+ ----
1607
+ # Parse file (returns ExpFile)
1608
+ exp_file = Expressir::Express::Parser.from_file("path/to/file.exp")
1609
+ file_drop = exp_file.to_liquid
1610
+ schema = file_drop.schemas.first
1611
+ schema.file = "path/to/file.exp"
1612
+
1613
+ # Or parse multiple files (returns Repository)
1614
+ repo = Expressir::Express::Parser.from_files(["file1.exp", "file2.exp"])
1615
+ repo_drop = repo.to_liquid
1616
+ ----
1617
+
1618
+ === Documentation coverage analysis
1619
+
1620
+ Expressir's documentation coverage feature can be used programmatically to
1621
+ analyze and report on documentation coverage of EXPRESS schemas.
1622
+
1623
+ [source,ruby]
1624
+ ----
1625
+ # Create a coverage report from a file
1626
+ report = Expressir::Coverage::Report.from_file("path/to/schema.exp")
1627
+
1628
+ # Or create a report from an ExpFile
1629
+ exp_file = Expressir::Express::Parser.from_file("path/to/schema.exp")
1630
+ report = Expressir::Coverage::Report.from_exp_file(exp_file)
1631
+
1632
+ # Or create a report from a repository
1633
+ repo = Expressir::Express::Parser.from_files(["schema1.exp", "schema2.exp"])
1634
+ report = Expressir::Coverage::Report.from_repository(repo)
1635
+
1636
+ # Access overall statistics
1637
+ puts "Overall coverage: #{report.coverage_percentage}%"
1638
+ puts "Total entities: #{report.total_entities.size}"
1639
+ puts "Documented entities: #{report.documented_entities.size}"
1640
+ puts "Undocumented entities: #{report.undocumented_entities.size}"
1641
+
1642
+ # Access file-level reports
1643
+ report.file_reports.each do |file_report|
1644
+ puts "File: #{file_report[:file]}"
1645
+ puts " Coverage: #{file_report[:coverage]}%"
1646
+ puts " Total entities: #{file_report[:total]}"
1647
+ puts " Documented entities: #{file_report[:documented]}"
1648
+ puts " Undocumented entities: #{file_report[:undocumented].join(', ')}"
1649
+ end
1650
+
1651
+ # Access directory-level reports
1652
+ report.directory_reports.each do |dir_report|
1653
+ puts "Directory: #{dir_report[:directory]}"
1654
+ puts " Coverage: #{dir_report[:coverage]}%"
1655
+ puts " Total entities: #{dir_report[:total]}"
1656
+ puts " Documented entities: #{dir_report[:documented]}"
1657
+ puts " Undocumented entities: #{dir_report[:undocumented]}"
1658
+ puts " Number of files: #{dir_report[:files]}"
1659
+ end
1660
+
1661
+ # Generate a structured hash representation
1662
+ report_hash = report.to_h # Contains overall, directories and files sections
1663
+ ----
1664
+
1665
+ You can also use the core methods directly to check documentation status:
1666
+
1667
+ [source,ruby]
1668
+ ----
1669
+ # Check if an entity has documentation
1670
+ schema = repository.schemas.first
1671
+ entity = schema.entities.first
1672
+
1673
+ if Expressir::Coverage.entity_documented?(entity)
1674
+ puts "Entity #{entity.id} is documented"
1675
+ else
1676
+ puts "Entity #{entity.id} is not documented"
1677
+ end
1678
+
1679
+ # Find all entities in a schema
1680
+ all_entities = Expressir::Coverage.find_entities(schema)
1681
+ puts "Found #{all_entities.size} entities in schema #{schema.id}"
1682
+ ----
1683
+
1684
+
1685
+ == EXPRESS schema manifests
1686
+
1687
+ === General
1688
+
1689
+ The EXPRESS schema manifest is a file format defined by ELF at
1690
+ https://www.expresslang.org/docs[EXPRESS schema manifest specification].
1691
+
1692
+ Expressir provides a `SchemaManifest` class for managing collections of EXPRESS
1693
+ schema files. This is particularly useful when working with multiple related
1694
+ schemas or when you need to organize schema files in a structured way.
1695
+
1696
+ The `SchemaManifest` class allows you to:
1697
+
1698
+ * Load schema file lists from YAML manifest files
1699
+ * Manage schema metadata and paths
1700
+ * Programmatically create and manipulate schema collections
1701
+ * Save manifest configurations to files
1702
+
1703
+
1704
+ === File format
1705
+
1706
+ The schema manifest uses a structured YAML format:
1707
+
1708
+ [source,yaml]
1709
+ ----
1710
+ schemas:
1711
+ - path: schemas/resources/action_schema/action_schema.exp
1712
+ id: action_schema
1713
+ - path: schemas/resources/approval_schema/approval_schema.exp
1714
+ id: approval_schema
1715
+ - path: schemas/resources/date_time_schema/date_time_schema.exp
1716
+ id: date_time_schema
1717
+ ----
1718
+
1719
+ Each schema entry in the manifest can have the following attributes:
1720
+
1721
+ `path`:: (Required) The file path to the EXPRESS schema file
1722
+ `id`:: (Optional) A unique identifier for the schema
1723
+ `container_path`:: (Optional) Container path information
1724
+
1725
+
1726
+ [example]
1727
+ .Example project structure with schema manifest
1728
+ ====
1729
+ [source]
1730
+ ----
1731
+ project/
1732
+ ├── schemas.yml # Schema manifest
1733
+ └── schemas/
1734
+ ├── core/
1735
+ │ ├── action_schema.exp
1736
+ │ └── approval_schema.exp
1737
+ └── extensions/
1738
+ └── date_time_schema.exp
1739
+ ----
1740
+
1741
+ .schemas.yml
1742
+ [source,yaml]
1743
+ ----
1744
+ schemas:
1745
+ - path: schemas/core/action_schema.exp
1746
+ id: action_schema
1747
+ - path: schemas/core/approval_schema.exp
1748
+ id: approval_schema
1749
+ - path: schemas/extensions/date_time_schema.exp
1750
+ id: date_time_schema
1751
+ ----
1752
+ ====
1753
+
1754
+
1755
+ === Commands for schema manifests
1756
+
1757
+ ==== Creating a manifest from a root schema
1758
+
1759
+ The `manifest create` command generates a schema manifest YAML file by
1760
+ resolving all referenced schemas starting from a specified root schema file.
1761
+
1762
+ .`manifest create` - Generate schema manifest
1763
+ [source,sh]
1764
+ ----
1765
+ Usage:
1766
+ expressir manifest create ROOT_SCHEMA [MORE_SCHEMAS...] -o, --output=OUTPUT
1767
+
1768
+ Options:
1769
+ -o, --output=OUTPUT # Output YAML file path
1770
+ [--base-dirs=BASE_DIRS] # Comma-separated base directories for schema resolution
1771
+ [--verbose], [--no-verbose], [--skip-verbose] # Show detailed output
1772
+ # Default: false
1773
+
1774
+ Description:
1775
+ Generate a YAML manifest of all schemas required for packaging.
1776
+
1777
+ The manifest uses the existing SchemaManifest format: schemas: schema_id: path:
1778
+ /path/to/schema.exp
1779
+
1780
+ Workflow: 1. Create manifest from root schema 2. Edit manifest to add paths for
1781
+ schemas with null paths 3. Validate manifest 4. Build package using manifest
1782
+
1783
+ Example: expressir manifest create schemas/activity/mim.exp \ -o
1784
+ activity_manifest.yaml \ --base-dirs /path/to/schemas
1785
+ ----
1786
+
1787
+ Options:
1788
+
1789
+ `--output, -o FILE`:: (Required) Output YAML file path
1790
+
1791
+ `--base-dirs DIRS`:: Base directories for schema resolution (space-separated). Also supports comma-separated for backward compatibility.
1792
+ +
1793
+ Searches for schema files using these patterns:
1794
+ schema files named according to schema ID::: `<SCHEMA_ID>.exp`
1795
+ schema files named in the STEP module pattern::: `<LOWERCASE_SCHEMA_WO_MIMARM>/{mim,arm}.exp`
1796
+ +
1797
+ [source,sh]
1798
+ ----
1799
+ # Space-separated (preferred)
1800
+ --base-dirs /path/to/schemas /another/path
1801
+
1802
+ # Comma-separated (backward compatible)
1803
+ --base-dirs /path/to/schemas,/another/path
1804
+ ----
1805
+
1806
+ `--verbose`:: Show detailed output
1807
+
1808
+ Base directories can be provided to help locate schema files:
1809
+
1810
+ * During the resolving process, Expressir will search these directories for
1811
+ schema files matching known naming patterns.
1812
+ * Once a referenced schema is found, it will indicate from which source they
1813
+ were resolved.
1814
+ * If multiple matches are found, the first one encountered will be used, and a
1815
+ warning will be displayed for the user to manually edit the manifest if a
1816
+ different path is desired.
1817
+
1818
+ In the case of unresolved schemas, the created manifest will include entries
1819
+ without the `path:` field. The manifest can then be edited manually to add the
1820
+ correct paths.
1821
+
1822
+
1823
+ .Creating a manifest from a root schema
1824
+ [example]
1825
+ ====
1826
+ The following command creates a manifest starting from the
1827
+ `schemas/modules/activity/mim.exp` root schema, searching for referenced schemas
1828
+ in the `schemas/resources` and `schemas/modules` base directories.
1829
+
1830
+ [source,sh]
1831
+ ----
1832
+ $ expressir manifest create \
1833
+ schemas/modules/activity/mim.exp \
1834
+ -o new.yaml \
1835
+ --base-dirs schemas/modules schemas/resources/ \
1836
+ --verbose
1837
+
1838
+ Creating manifest from 1 root schema(s)...
1839
+ Base directories:
1840
+ - [source 1]: ~/src/mn/iso-10303/schemas/modules
1841
+ - [source 2]: ~/src/mn/iso-10303/schemas/resources/
1842
+ Resolving dependencies...
1843
+ USE FROM action_schema (in mim): ✓ [source 2] action_schema/action_schema.exp
1844
+ REFERENCE FROM basic_attribute_schema (in action_schema): ✓ [source 2] basic_attribute_schema/basic_attribute_schema.exp
1845
+ REFERENCE FROM support_resource_schema (in basic_attribute_schema): ✓ [source 2] support_resource_schema/support_resource_schema.exp
1846
+ REFERENCE FROM support_resource_schema (in action_schema): ✓ [source 2] support_resource_schema/support_resource_schema.exp
1847
+ USE FROM Activity_method_mim (in mim): ✓ [source 1] activity_method/mim.exp
1848
+ ...
1849
+ REFERENCE FROM support_resource_schema (in management_resources_schema): ✓ [source 2] support_resource_schema/support_resource_schema.exp
1850
+ ✓ Manifest created: new.yaml
1851
+ Resolved schemas: 42
1852
+ All schemas resolved successfully!
1853
+ ----
1854
+ ====
1855
+
1856
+ .Creating a manifest from a root schema with unresolved schemas
1857
+ [example]
1858
+ ====
1859
+ The following command creates a manifest starting from the
1860
+ `schemas/modules/activity/mim.exp` root schema, searching for referenced schemas
1861
+ in the `schemas/resources` base directory only (i.e. it will miss schemas
1862
+ in `schemas/modules`).
1863
+
1864
+ [source,sh]
1865
+ ----
1866
+ $ expressir manifest create \
1867
+ -o manifest.yaml \
1868
+ --base-dirs schemas/resources \
1869
+ --verbose \
1870
+ schemas/modules/activity/mim.exp
1871
+
1872
+ Creating manifest from 1 root schema(s)...
1873
+ Base directories:
1874
+ - [source 1]: ~/src/mn/iso-10303/schemas/resources
1875
+ Resolving dependencies...
1876
+ USE FROM action_schema (in mim): ✓ [source 1] action_schema/action_schema.exp
1877
+ REFERENCE FROM basic_attribute_schema (in action_schema): ✓ [source 1] basic_attribute_schema/basic_attribute_schema.exp
1878
+ REFERENCE FROM support_resource_schema (in basic_attribute_schema): ✓ [source 1] support_resource_schema/support_resource_schema.exp
1879
+ REFERENCE FROM support_resource_schema (in action_schema): ✓ [source 1] support_resource_schema/support_resource_schema.exp
1880
+ USE FROM Activity_method_mim (in mim): ✗ not found
1881
+ ...
1882
+ REFERENCE FROM support_resource_schema (in management_resources_schema): ✓ [source 2] support_resource_schema/support_resource_schema.exp
1883
+ ✓ Manifest created: manifest.yaml
1884
+ Resolved schemas: 41
1885
+
1886
+ ⚠ Unresolved schemas (1):
1887
+ - Activity_method_mim
1888
+
1889
+ Please edit manifest.yaml and set 'path:' for unresolved schemas
1890
+ Then validate with: expressir manifest validate manifest.yaml
1891
+ ----
1892
+ ====
1893
+
1894
+
1895
+
1896
+ ==== Resolving schemas from a manifest
1897
+
1898
+ A schema manifest can be used as input to resolve and load all referenced
1899
+ schemas into a new manifest.
1900
+
1901
+
1902
+ [source,sh]
1903
+ ----
1904
+ expressir manifest resolve MANIFEST -o, --output=OUTPUT
1905
+ ----
1906
+
1907
+ [source,sh]
1908
+ ----
1909
+ Usage:
1910
+ expressir manifest resolve MANIFEST -o, --output=OUTPUT
1911
+
1912
+ Options:
1913
+ -o, --output=OUTPUT # Output file path for resolved manifest
1914
+ [--base-dirs=one two three] # Base directories for schema search (can be specified multiple times)
1915
+ [--verbose], [--no-verbose], [--skip-verbose] # Show detailed resolution progress
1916
+ # Default: false
1917
+
1918
+ Description:
1919
+ Resolve missing or incomplete schema paths in a manifest file.
1920
+
1921
+ This command attempts to find file paths for schemas with missing or empty
1922
+ paths by searching in base directories using naming patterns:
1923
+
1924
+ Supported patterns:
1925
+ - Resource schemas: {schema_name}.exp
1926
+ - Module ARM/MIM: {module_name}/{arm|mim}.exp
1927
+ Example: Activity_method_mim -> activity_method/mim.exp
1928
+
1929
+ The resolved manifest is written to the output file, leaving the original
1930
+ unchanged.
1931
+
1932
+ Use this command after 'expressir manifest validate --check-references' fails
1933
+ to automatically resolve missing schema paths.
1934
+
1935
+ Examples: # Resolve paths using manifest's existing base directories expressir
1936
+ manifest resolve manifest.yaml -o resolved_manifest.yaml
1937
+ # Resolve with explicit base directories
1938
+ expressir manifest resolve manifest.yaml -o resolved.yaml \
1939
+ --base-dirs /path/to/schemas,/another/path
1940
+ # With verbose output
1941
+ expressir manifest resolve manifest.yaml -o resolved.yaml --verbose
1942
+ ----
1943
+
1944
+ Options:
1945
+
1946
+ `--output, -o FILE`:: (Required) Output file path for resolved manifest
1947
+
1948
+ `--base-dirs DIRS`:: Base directories for schema search (space-separated). Also
1949
+ supports comma-separated for backward compatibility.
1950
+ +
1951
+ Searches for schema files using these patterns:
1952
+ schema files named according to schema ID::: `<SCHEMA_ID>.exp`
1953
+ schema files named in the STEP module pattern::: `<LOWERCASE_SCHEMA_WO_MIMARM>/{mim,arm}.exp`
1954
+ +
1955
+ [source,sh]
1956
+ ----
1957
+ # Space-separated (preferred)
1958
+ --base-dirs /path/to/schemas /another/path
1959
+
1960
+ # Comma-separated (backward compatible)
1961
+ --base-dirs /path/to/schemas,/another/path
1962
+ ----
1963
+
1964
+ `--verbose`:: Show detailed resolution progress
1965
+
1966
+ .Fully resolving a schema manifest
1967
+ [example]
1968
+ ====
1969
+ The following command resolves all schemas in the `manifest.yaml` file and writes the
1970
+ fully resolved manifest to `resolved_manifest.yaml`.
1971
+
1972
+ [source,sh]
1973
+ ----
1974
+ $ expressir manifest resolve manifest.yaml -o resolved.yaml --verbose
1975
+
1976
+ Resolving schema paths in: manifest.yaml...
1977
+ Using base directories:
1978
+ - [source 1] ~/src/mn/iso-10303/schemas/
1979
+ Attempting to resolve paths...
1980
+ Resolving dependencies from 1 root schema(s)...
1981
+ USE FROM action_schema (in mim): ✓ [source 1] resources/action_schema/action_schema.exp
1982
+ REFERENCE FROM basic_attribute_schema (in action_schema): ✓ [source 1] resources/basic_attribute_schema/basic_attribute_schema.exp
1983
+ REFERENCE FROM support_resource_schema (in basic_attribute_schema): ✓ [source 1] resources/support_resource_schema/support_resource_schema.exp
1984
+ REFERENCE FROM support_resource_schema (in action_schema): ✓ [source 1] resources/support_resource_schema/support_resource_schema.exp
1985
+ USE FROM Activity_method_mim (in mim): ✗ not found
1986
+ USE FROM basic_attribute_schema (in mim): ✓ [source 1] resources/basic_attribute_schema/basic_attribute_schema.exp
1987
+ USE FROM management_resources_schema (in mim): ✓ [source 1] resources/management_resources_schema/management_resources_schema.exp
1988
+ ...
1989
+ REFERENCE FROM support_resource_schema (in management_resources_schema): ✓ [source 1] resources/support_resource_schema/support_resource_schema.exp
1990
+ ✓ Manifest resolved: resolved.yaml
1991
+ Total schemas: 42
1992
+ Resolved schemas: 41
1993
+
1994
+ ⚠ Unresolved schemas (1):
1995
+ - Activity_method_mim
1996
+
1997
+ These schemas could not be found in the search directories.
1998
+ You may need to:
1999
+ 1. Add more base directories with --base-dirs
2000
+ 2. Manually edit resolved.yaml and set their paths
2001
+ ----
2002
+ ====
2003
+
2004
+ .Resolving a manifest with multiple base directories
2005
+ [example]
2006
+ ====
2007
+ [source,sh]
2008
+ ----
2009
+ $ expressir manifest resolve manifest.yaml \
2010
+ -o resolved.yaml \
2011
+ --base-dirs ~/src/mn/iso-10303/schemas/modules \
2012
+ ~/src/mn/iso-10303/schemas/resources/ --verbose
2013
+
2014
+ Resolving schema paths in: manifest.yaml...
2015
+ Using base directories:
2016
+ - [source 1]: ~/src/mn/iso-10303/schemas/modules
2017
+ - [source 2]: ~/src/mn/iso-10303/schemas/resources/
2018
+ Attempting to resolve paths...
2019
+ Resolving dependencies from 1 root schema(s)...
2020
+ Using base directories:
2021
+ - [source 1]: ~/src/mn/iso-10303/schemas/modules
2022
+ - [source 2]: ~/src/mn/iso-10303/schemas/resources
2023
+ USE FROM action_schema (in mim): ✓ [source 2] action_schema/action_schema.exp
2024
+ REFERENCE FROM basic_attribute_schema (in action_schema): ✓ [source 2] basic_attribute_schema/basic_attribute_schema.exp
2025
+ REFERENCE FROM support_resource_schema (in basic_attribute_schema): ✓ [source 2] support_resource_schema/support_resource_schema.exp
2026
+ REFERENCE FROM support_resource_schema (in action_schema): ✓ [source 2] support_resource_schema/support_resource_schema.exp
2027
+ USE FROM Activity_method_mim (in mim): ✓ [source 1] activity_method/mim.exp
2028
+ ...
2029
+ REFERENCE FROM support_resource_schema (in management_resources_schema): ✓ [source 2] support_resource_schema/support_resource_schema.exp
2030
+ ✓ Manifest resolved: resolved.yaml
2031
+ Total schemas: 42
2032
+ Resolved schemas: 42
2033
+ All schema paths resolved successfully!
2034
+ ----
2035
+ ====
2036
+
2037
+
2038
+ ==== Validating a manifest
2039
+
2040
+ A schema manifest can be validated to ensure all referenced schemas are fully
2041
+ resolvable and correctly defined.
2042
+
2043
+ .`manifest validate` - Validate manifest
2044
+ [source,sh]
2045
+ ----
2046
+ expressir manifest validate MANIFEST.yaml [OPTIONS]
2047
+ ----
2048
+
2049
+ [source,sh]
2050
+ ----
2051
+ Usage:
2052
+ expressir manifest validate MANIFEST
2053
+
2054
+ Options:
2055
+ [--verbose], [--no-verbose], [--skip-verbose] # Show detailed validation results
2056
+ # Default: false
2057
+ [--check-references], [--no-check-references], [--skip-check-references] # Validate referential integrity using dependency resolver
2058
+ # Default: false
2059
+
2060
+ Description:
2061
+ Validate a schema manifest file.
2062
+
2063
+ Validation types: - File existence: All schema paths exist on disk - Path
2064
+ completeness: All schemas have paths specified (warnings) - Referential integrity
2065
+ (--check-references): All USE/REFERENCE FROM resolve
2066
+
2067
+ Examples: # Basic validation (file existence and path completeness) expressir manifest
2068
+ validate activity_manifest.yaml # With referential integrity checking expressir
2069
+ manifest validate activity_manifest.yaml --check-references # With verbose output
2070
+ expressir manifest validate activity_manifest.yaml --check-references --verbose
2071
+ ----
2072
+
2073
+ Options:
2074
+
2075
+ `--verbose`:: Show detailed validation results
2076
+
2077
+ `--check-references`:: Validate referential integrity using dependency resolver
2078
+
2079
+ .Validating a manifest to ensure all dependencies are defined
2080
+ [example]
2081
+ ====
2082
+ Checking `REFERENCE FROM` and `USE FROM` references to ensure all dependencies
2083
+ are defined in EXPRESS schemas included in the manifest:
2084
+
2085
+ [source,sh]
2086
+ ----
2087
+ $ expressir manifest validate manifest.yaml --check-references --verbose
2088
+
2089
+ Validating manifest: manifest.yaml...
2090
+ Checking referential integrity...
2091
+ Validating referential integrity for 42 schemas...
2092
+
2093
+ USE FROM action_schema (in Activity_method_mim): ✓ action_schema.exp
2094
+ REFERENCE FROM basic_attribute_schema (in action_schema): ✓ basic_attribute_schema.exp
2095
+ ...
2096
+ USE FROM action_schema (in mim): ✓ action_schema.exp
2097
+ ...
2098
+ REFERENCE FROM support_resource_schema (in topology_schema): ✓ support_resource_schema.exp
2099
+ ✓ Manifest is valid
2100
+ Total schemas: 42
2101
+ Resolved schemas: 42
2102
+ ----
2103
+ ====
2104
+
2105
+
2106
+
2107
+ === Ruby API for schema manifests
2108
+
2109
+ ==== Loading manifests
2110
+
2111
+ Load an existing schema manifest from a YAML file:
2112
+
2113
+ [source,ruby]
2114
+ ----
2115
+ # Load manifest from file
2116
+ manifest = Expressir::SchemaManifest.from_file("schemas.yml")
2117
+
2118
+ # Access schema entries
2119
+ manifest.schemas.each do |schema_entry|
2120
+ puts "Schema path: #{schema_entry.path}"
2121
+ puts "Schema ID: #{schema_entry.id}" if schema_entry.id
2122
+ end
2123
+
2124
+ # Get all schema file paths
2125
+ schema_paths = manifest.schemas.map(&:path)
2126
+ ----
2127
+
2128
+ ==== Creating manifests
2129
+
2130
+ Create a new schema manifest programmatically:
2131
+
2132
+ [source,ruby]
2133
+ ----
2134
+ # Create an empty manifest
2135
+ manifest = Expressir::SchemaManifest.new
2136
+
2137
+ # Add schema entries
2138
+ manifest.schemas << Expressir::SchemaManifestEntry.new(
2139
+ path: "schemas/action_schema.exp",
2140
+ id: "action_schema"
2141
+ )
2142
+
2143
+ manifest.schemas << Expressir::SchemaManifestEntry.new(
2144
+ path: "schemas/approval_schema.exp"
2145
+ )
2146
+
2147
+ # Set base path for the manifest
2148
+ manifest.base_path = "/path/to/schemas"
2149
+ ----
2150
+
2151
+ ==== Saving manifests
2152
+
2153
+ Save a manifest to a file:
2154
+
2155
+ [source,ruby]
2156
+ ----
2157
+ # Save to a specific file
2158
+ manifest.to_file("output_schemas.yml")
2159
+
2160
+ # Save to a path with automatic filename
2161
+ manifest.save_to_path("/path/to/output/")
2162
+ ----
2163
+
2164
+ ==== Concatenating manifests
2165
+
2166
+ Combine multiple manifests:
2167
+
2168
+ [source,ruby]
2169
+ ----
2170
+ manifest1 = Expressir::SchemaManifest.from_file("schemas1.yml")
2171
+ manifest2 = Expressir::SchemaManifest.from_file("schemas2.yml")
2172
+
2173
+ # Concatenate manifests
2174
+ combined_manifest = manifest1.concat(manifest2)
2175
+
2176
+ # Or use the + operator
2177
+ combined_manifest = manifest1 + manifest2
2178
+ ----
2179
+
2180
+ ==== Using manifests with parsers
2181
+
2182
+ Parse all schemas from a manifest:
2183
+
2184
+ [source,ruby]
2185
+ ----
2186
+ # Load manifest
2187
+ manifest = Expressir::SchemaManifest.from_file("schemas.yml")
2188
+
2189
+ # Get schema file paths
2190
+ schema_paths = manifest.schemas.map(&:path)
2191
+
2192
+ # Parse all schemas
2193
+ repository = Expressir::Express::Parser.from_files(schema_paths)
2194
+
2195
+ # With progress tracking
2196
+ repository = Expressir::Express::Parser.from_files(schema_paths) do |filename, schemas, error|
2197
+ if error
2198
+ puts "Error loading #{filename}: #{error.message}"
2199
+ else
2200
+ puts "Successfully loaded #{schemas.length} schemas from #{filename}"
2201
+ end
2202
+ end
2203
+ ----
2204
+
2205
+
2206
+
2207
+ == LER packages
2208
+
2209
+ === General
2210
+
2211
+ LER (LutaML EXPRESS Repository) is a package format for distributing EXPRESS
2212
+ schemas and related resources in a single binary file.
2213
+
2214
+ EXPRESS schemas often utilize `REFERENCE FROM` or `USE FROM` statements to
2215
+ import definitions from other schemas, and those referenced schemas
2216
+ can deviate across different versions. Managing these dependencies
2217
+ is crucial for maintaining compatibility.
2218
+
2219
+ The LER package format helps encapsulate these dependencies, ensuring that all
2220
+ required schemas are included and versioned correctly, allowing the package
2221
+ to be used reliably in different environments.
2222
+
2223
+ LER packages have the `.ler` file extension and are essentially ZIP archives
2224
+ containing:
2225
+
2226
+ * EXPRESS schema files (`.exp`)
2227
+ * Metadata files (`metadata.yaml`)
2228
+ * pre-parsed model cache (serialized Ruby objects)
2229
+
2230
+
2231
+ === Creating a package
2232
+
2233
+ Expressir provides a command-line interface for creating LER packages from
2234
+ an EXPRESS manifest.
2235
+
2236
+ Expressir supports building LER packages from EXPRESS schemas using a
2237
+ manifest-based workflow.
2238
+
2239
+ **Using a manifest (recommended for production)**::
2240
+
2241
+ [source,sh]
2242
+ ----
2243
+ # Build from validated manifest
2244
+ expressir package build --manifest activity_manifest.yaml activity.ler \
2245
+ --name "Activity Module" \
2246
+ --validate
2247
+ ----
2248
+
2249
+ **Using auto-resolution (quick prototyping)**:
2250
+
2251
+ [source,sh]
2252
+ ----
2253
+ # Build from root schema with auto-resolution
2254
+ expressir package build schemas/activity/mim.exp activity.ler \
2255
+ --base-dirs ~/schemas/resources ~/schemas/modules \
2256
+ --name "Activity Module" \
2257
+ --validate
2258
+ ----
2259
+
2260
+ Complete manifest workflow:
2261
+
2262
+ [source,sh]
2263
+ ----
2264
+ # 1. Create manifest from root schema
2265
+ expressir manifest create schemas/activity/mim.exp \
2266
+ -o activity_manifest.yaml \
2267
+ --base-dirs ~/iso-10303/schemas \
2268
+ --name "Activity Module"
2269
+
2270
+ # 2. Edit manifest to add missing schema paths
2271
+
2272
+ # 3. Validate manifest
2273
+ expressir manifest validate activity_manifest.yaml
2274
+
2275
+ # 4. Build package from manifest
2276
+ expressir package build --manifest activity_manifest.yaml activity.ler
2277
+ ----
2278
+
2279
+
2280
+ Unresolved schemas appear without `path:` field. Edit the manifest to add missing paths.
2281
+
2282
+ [source,yaml]
2283
+ ----
2284
+ schemas:
2285
+ - name: action_schema
2286
+ path: /path/to/action_schema.exp
2287
+ - name: Activity_method_mim # Unresolved - no path
2288
+ ----
2289
+
2290
+
2291
+ .`package build --manifest` - Build from manifest
2292
+ [source,sh]
2293
+ ----
2294
+ expressir package build --manifest MANIFEST.yaml OUTPUT.ler [OPTIONS]
2295
+ ----
2296
+
2297
+ See link:docs/_pages/ler-packages#manifest-workflow[LER Packages documentation] for complete details.
2298
+
2299
+
2300
+
2301
+ == Benchmarking
2302
+
2303
+ === General
2304
+
2305
+ Expressir includes benchmarking tools to measure the performance of parsing and
2306
+ processing EXPRESS schemas.
2307
+
2308
+ === Benchmarking with manifests
2309
+
2310
+ [source,sh]
2311
+ ----
2312
+ # Benchmark all schemas in a manifest
2313
+ expressir benchmark schemas.yml --verbose
2314
+
2315
+ # Benchmark with caching
2316
+ expressir benchmark-cache schemas.yml --cache_path /tmp/cache.bin
2317
+ ----
2318
+
2319
+
2320
+ == Coverage reporting
2321
+
2322
+ === General
2323
+
2324
+ Expressir provides coverage analysis tools to evaluate the documentation
2325
+ coverage of EXPRESS schemas.
2326
+
2327
+ === Coverage analysis with manifests
2328
+
2329
+ [source,sh]
2330
+ ----
2331
+ # Analyze coverage for all schemas in manifest
2332
+ expressir coverage schemas.yml
2333
+
2334
+ # With output format and exclusions
2335
+ expressir coverage schemas.yml --format json --exclude=TYPE:SELECT
2336
+ ----
2337
+
2338
+
2339
+
2340
+ == EXPRESS Changes
2341
+
2342
+ === General
2343
+
2344
+ Expressir provides the `Changes` module for managing and tracking changes to
2345
+ EXPRESS schemas across versions. This module implements the EXPRESS Changes YAML
2346
+ format defined by ELF (Express Language Foundation) (ELF 5005).
2347
+
2348
+ The Changes module enables:
2349
+
2350
+ * Loading and saving schema change records from/to YAML files
2351
+ * Programmatic creation and manipulation of change records
2352
+ * Smart version handling (replace same version, add new version)
2353
+ * Support for all change types: additions, modifications, deletions
2354
+ * Support for mapping changes in ARM/MIM schemas
2355
+ * Programmatic import from Express Engine comparison XML files
2356
+
2357
+
2358
+ === Importing from Express Engine XML programmatically
2359
+
2360
+ ==== General
2361
+
2362
+ Expressir provides programmatic API access for parsing Express Engine comparison XML
2363
+ and converting it to EXPRESS Changes format.
2364
+
2365
+ Parse Eengine XML into a structured object model:
2366
+
2367
+ [source,ruby]
2368
+ ----
2369
+ require "expressir/eengine/compare_report"
2370
+
2371
+ # Parse from XML string
2372
+ xml_content = File.read("comparison.xml")
2373
+ report = Expressir::Eengine::CompareReport.from_xml(xml_content)
2374
+
2375
+ # Or parse from file
2376
+ report = Expressir::Eengine::CompareReport.from_file("comparison.xml")
2377
+ ----
2378
+
2379
+ Convert Eengine XML directly to EXPRESS Changes format:
2380
+
2381
+ [source,ruby]
2382
+ ----
2383
+ require "expressir/commands/changes_import_eengine"
2384
+
2385
+ # Parse XML and convert to SchemaChange in one step
2386
+ xml_content = File.read("comparison.xml")
2387
+ change_schema = Expressir::Commands::ChangesImportEengine.from_xml(
2388
+ xml_content,
2389
+ "aic_csg", # schema name
2390
+ "1.0" # version
2391
+ )
2392
+
2393
+ # Use the SchemaChange object
2394
+ change_schema.versions.first.additions.each do |item|
2395
+ puts "#{item.type}: #{item.name}"
2396
+ puts " interfaced_items: #{item.interfaced_items}" if item.interfaced_items
2397
+ end
2398
+
2399
+ # Save to file
2400
+ change_schema.to_file("output.changes.yaml")
2401
+ ----
2402
+
2403
+ === Compare modes
2404
+
2405
+ ==== General
2406
+
2407
+ Expressir automatically detects and parses all three Eengine XML modes.
2408
+
2409
+ [source,ruby]
2410
+ ----
2411
+ # All modes are handled automatically
2412
+ arm_report = Expressir::Eengine::CompareReport.from_file("arm_comparison.xml")
2413
+ mim_report = Expressir::Eengine::CompareReport.from_file("mim_comparison.xml")
2414
+ schema_report = Expressir::Eengine::CompareReport.from_file("schema_comparison.xml")
2415
+
2416
+ puts arm_report.mode # => "arm"
2417
+ puts mim_report.mode # => "mim"
2418
+ puts schema_report.mode # => "schema"
2419
+ ----
2420
+
2421
+ ==== Schema mode
2422
+
2423
+ `<schema.changes>` with `<schema.additions>`, `<schema.modifications>`, `<schema.deletions>`
2424
+
2425
+ [example]
2426
+ ====
2427
+ [source,xml]
2428
+ ----
2429
+ <schema.changes schema_name="aic_csg">
2430
+ <schema.additions>
2431
+ <modified.object type="ENTITY" name="new_entity" />
2432
+ </schema.additions>
2433
+ <schema.modifications>
2434
+ <modified.object type="TYPE" name="modified_type" />
2435
+ </schema.modifications>
2436
+ <schema.deletions>
2437
+ <modified.object type="FUNCTION" name="removed_function" />
2438
+ </schema.deletions>
2439
+ </schema.changes>
2440
+ ----
2441
+ ====
2442
+
2443
+ ==== ARM mode
2444
+
2445
+ `<arm.changes>` root element with `<arm.additions>`,
2446
+ `<arm.modifications>`, and `<arm.deletions>` sections
2447
+
2448
+ [example]
2449
+ ====
2450
+ [source,xml]
2451
+ ----
2452
+ <arm.changes schema_name="example_arm">
2453
+ <arm.additions>
2454
+ <modified.object type="ENTITY" name="new_arm_entity" />
2455
+ </arm.additions>
2456
+ </arm.changes>
2457
+ ----
2458
+ ====
2459
+
2460
+
2461
+ ==== MIM mode
2462
+
2463
+ `<mim.changes>` root element with `<mim.additions>`,
2464
+ `<mim.modifications>`, and `<mim.deletions>` sections
2465
+
2466
+ [example]
2467
+ ====
2468
+ [source,xml]
2469
+ ----
2470
+ <mim.changes schema_name="example_mim">
2471
+ <mim.additions>
2472
+ <modified.object type="ENTITY" name="new_mim_entity" />
2473
+ </mim.additions>
2474
+ </mim.changes>
2475
+ ----
2476
+ ====
2477
+
2478
+ === Interface changes
2479
+
2480
+ The eengine XML format tracks interface changes (USE_FROM, REFERENCE_FROM) with
2481
+ the `interfaced.items` attribute. This attribute lists the specific items being
2482
+ imported or referenced from another schema.
2483
+
2484
+ .Example XML with interface changes
2485
+ [example]
2486
+ ====
2487
+ [source,xml]
2488
+ ----
2489
+ <schema.changes schema_name="aic_csg">
2490
+ <schema.additions>
2491
+ <modified.object type="USE_FROM" name="geometric_model_schema"
2492
+ interfaced.items="convex_hexahedron" />
2493
+
2494
+ <modified.object type="USE_FROM" name="geometric_model_schema"
2495
+ interfaced.items="cyclide_segment_solid" />
2496
+
2497
+ <modified.object type="REFERENCE_FROM" name="measure_schema"
2498
+ interfaced.items="length_measure" />
2499
+ </schema.additions>
2500
+ </schema.changes>
2501
+ ----
2502
+
2503
+ This will be converted to EXPRESS Changes YAML as:
2504
+
2505
+ [source,yaml]
2506
+ ----
2507
+ schema: aic_csg
2508
+ versions:
2509
+ - version: 2
2510
+ description: Changes from eengine comparison
2511
+ additions:
2512
+ - type: USE_FROM
2513
+ name: geometric_model_schema
2514
+ interfaced_items: convex_hexahedron
2515
+ - type: USE_FROM
2516
+ name: geometric_model_schema
2517
+ interfaced_items: cyclide_segment_solid
2518
+ - type: REFERENCE_FROM
2519
+ name: measure_schema
2520
+ interfaced_items: length_measure
2521
+ ----
2522
+ ====
2523
+
2524
+ Eengine XML files track interface changes (USE_FROM, REFERENCE_FROM) with the
2525
+ `interfaced.items` attribute:
2526
+
2527
+ [source,ruby]
2528
+ ----
2529
+ report = Expressir::Eengine::CompareReport.from_file("comparison.xml")
2530
+
2531
+ # Find interface changes
2532
+ report.additions&.modified_objects&.each do |obj|
2533
+ if obj.type == "USE_FROM" || obj.type == "REFERENCE_FROM"
2534
+ puts "#{obj.type} #{obj.name}"
2535
+ puts " Items: #{obj.interfaced_items}" if obj.interfaced_items
2536
+ end
2537
+ end
2538
+ ----
2539
+
2540
+
2541
+ === Supported change types
2542
+
2543
+ The import command recognizes all standard EXPRESS construct types:
2544
+
2545
+ * `ENTITY` - Entity definitions
2546
+ * `TYPE` - Type definitions
2547
+ * `FUNCTION` - Function definitions
2548
+ * `PROCEDURE` - Procedure definitions
2549
+ * `RULE` - Rule definitions
2550
+ * `CONSTANT` - Constant definitions
2551
+ * `USE_FROM` - Interface imports (preserves `interfaced.items`)
2552
+ * `REFERENCE_FROM` - Interface references (preserves `interfaced.items`)
2553
+
2554
+
2555
+ === Reading change files
2556
+
2557
+ Load an existing schema change file:
2558
+
2559
+ [source,ruby]
2560
+ ----
2561
+ require "expressir/changes"
2562
+
2563
+ # Load from file
2564
+ change_schema = Expressir::Changes::SchemaChange.from_file("schema.changes.yaml")
2565
+
2566
+ # Access schema name
2567
+ puts "Schema: #{change_schema.schema}"
2568
+
2569
+ # Iterate through change versions
2570
+ change_schema.versions.each do |version|
2571
+ puts "Version #{version.version}: #{version.description}"
2572
+
2573
+ # Access changes by type
2574
+ puts " Additions: #{version.additions.size}" if version.additions
2575
+ puts " Modifications: #{version.modifications.size}" if version.modifications
2576
+ puts " Deletions: #{version.deletions.size}" if version.deletions
2577
+ end
2578
+ ----
2579
+
2580
+ === Creating change records
2581
+
2582
+ Create a new change schema programmatically:
2583
+
2584
+ [source,ruby]
2585
+ ----
2586
+ # Create a new empty change schema
2587
+ change_schema = Expressir::Changes::SchemaChange.new(schema: "my_schema")
2588
+
2589
+ # Create change items
2590
+ new_entity = Expressir::Changes::ItemChange.new(
2591
+ type: "ENTITY",
2592
+ name: "new_entity_name"
2593
+ )
2594
+
2595
+ modified_function = Expressir::Changes::ItemChange.new(
2596
+ type: "FUNCTION",
2597
+ name: "modified_function",
2598
+ description: "Updated parameters"
2599
+ )
2600
+
2601
+ # Add a change version
2602
+ changes = {
2603
+ additions: [new_entity],
2604
+ modifications: [modified_function],
2605
+ deletions: []
2606
+ }
2607
+
2608
+ change_schema.add_or_update_version(
2609
+ "2",
2610
+ "Added new entity and modified function",
2611
+ changes
2612
+ )
2613
+
2614
+ # Save to file
2615
+ change_schema.to_file("my_schema.changes.yaml")
2616
+ ----
2617
+
2618
+ === Updating existing change files
2619
+
2620
+ The `add_or_update_version` method provides smart handling:
2621
+
2622
+ * **Same version**: Replaces the existing version
2623
+ * **Different version**: Adds a new version
2624
+
2625
+ [source,ruby]
2626
+ ----
2627
+ # Load existing change file
2628
+ change_schema = Expressir::Changes::SchemaChange.from_file("schema.changes.yaml")
2629
+
2630
+ # Add a new version
2631
+ changes = {
2632
+ modifications: [
2633
+ Expressir::Changes::ItemChange.new(type: "TYPE", name: "updated_type")
2634
+ ]
2635
+ }
2636
+ change_schema.add_or_update_version("3", "Modified type definition", changes)
2637
+
2638
+ # Or replace existing version
2639
+ change_schema.add_or_update_version("2", "Revised description", changes)
2640
+
2641
+ # Save changes
2642
+ change_schema.to_file("schema.changes.yaml")
2643
+ ----
2644
+
2645
+ === Change item fields
2646
+
2647
+ Change items support the following fields:
2648
+
2649
+ `type`:: (Required) The EXPRESS construct type (ENTITY, TYPE, FUNCTION, etc.)
2650
+ `name`:: (Required) The name of the construct
2651
+ `description`:: (Optional) Additional details about the change
2652
+ `interfaced_items`:: (Optional) For REFERENCE_FROM items
2653
+
2654
+ [source,ruby]
2655
+ ----
2656
+ item = Expressir::Changes::ItemChange.new(
2657
+ type: "REFERENCE_FROM",
2658
+ name: "measure_schema",
2659
+ interfaced_items: "length_measure"
2660
+ )
2661
+ ----
2662
+
2663
+ === Change version fields
2664
+
2665
+ Change versions support categorizing changes into:
2666
+
2667
+ `additions`:: New elements added to the schema
2668
+ `modifications`:: Existing elements that were modified
2669
+ `deletions`:: Elements removed from the schema
2670
+ `mappings`:: Mapping-related changes (for ARM/MIM modules)
2671
+ `changes`:: General changes (alternative to mapping)
2672
+
2673
+ [source,ruby]
2674
+ ----
2675
+ version = Expressir::Changes::VersionChange.new(
2676
+ version: "2",
2677
+ description: "Added support for new functionality",
2678
+ additions: [item1, item2],
2679
+ modifications: [item3],
2680
+ deletions: [item4],
2681
+ mappings: [mapping_change]
2682
+ )
2683
+ ----
2684
+
2685
+ === Mapping changes
2686
+
2687
+ For ARM/MIM schema mappings, use `MappingChange`:
2688
+
2689
+ [source,ruby]
2690
+ ----
2691
+ mapping_change = Expressir::Changes::MappingChange.new(
2692
+ change: "Entity_name ENTITY mapping updated"
2693
+ )
2694
+ ----
2695
+
2696
+ === Example change file format
2697
+
2698
+ [source,yaml]
2699
+ ----
2700
+ ---
2701
+ schema: support_resource_schema
2702
+ versions:
2703
+ - version: 2
2704
+ description: |-
2705
+ The definitions of the following EXPRESS entity data types were modified:
2706
+
2707
+ * action;
2708
+ * action_directive;
2709
+ * action_method.
2710
+ additions:
2711
+ - type: FUNCTION
2712
+ name: type_check_function
2713
+ modifications:
2714
+ - type: FUNCTION
2715
+ name: bag_to_set
2716
+ - version: 4
2717
+ description: |-
2718
+ Added support for external element references.
2719
+ additions:
2720
+ - type: ENTITY
2721
+ name: component_path_shape_aspect
2722
+ modifications:
2723
+ - type: FUNCTION
2724
+ name: type_check_function
2725
+ ----
2726
+
2727
+ == Contributing
2728
+
2729
+ First, thank you for contributing! We love pull requests from everyone. By
2730
+ participating in this project, you hereby grant
2731
+ https://www.ribose.com[Ribose Inc.] the right to grant or transfer an unlimited
2732
+ number of non exclusive licenses or sub-licenses to third parties, under the
2733
+ copyright covering the contribution to use the contribution by all means.
2734
+
2735
+ Here are a few technical guidelines to follow:
2736
+
2737
+ * Open an https://github.com/lutaml/expressir/issues[issues] to discuss a new
2738
+ feature.
2739
+ * Write tests to support your new feature.
2740
+ * Make sure the entire test suite passes locally and on CI.
2741
+ * Open a Pull Request.
2742
+ * https://github.com/thoughtbot/guides/tree/master/protocol/git#write-a-feature[Squash your commits] after receiving feedback.
2743
+ * Party!
2744
+
2745
+
2746
+ == Documentation
2747
+
2748
+ Expressir provides detailed documentation on various aspects of its functionality:
2749
+
2750
+ * link:docs/benchmarking.adoc[Benchmarking]: Learn about Expressir's built-in
2751
+ capabilities for measuring schema loading performance, particularly useful for
2752
+ large schemas or when optimizing performance.
2753
+
2754
+ * link:docs/liquid_drops.adoc[Liquid Integration]: Documentation on how to use
2755
+ Expressir models with Liquid templates for flexible document generation.
2756
+
2757
+ == License
2758
+
2759
+ Expressir is distributed under the BSD 2-clause license.
2760
+
2761
+ NOTE: Expressir originally contained some code from the NIST Reeper project but no
2762
+ longer contains them.
2763
+
2764
+ The https://www.nist.gov/services-resources/software/reeper[NIST Reeper license]
2765
+ is reproduced below:
2766
+
2767
+ [quote]
2768
+ ____
2769
+ This software was funded by NIST and developed by EuroSTEP.
2770
+ Pursuant to title 17 Section 105 of the United States Code this
2771
+ software is not subject to copyright protection and is in the public
2772
+ domain.
2773
+
2774
+ We would appreciate acknowledgment if the software is used. Links to
2775
+ non-Federal Government Web sites do not imply NIST endorsement of any
2776
+ particular product, service, organization, company, information
2777
+ provider, or content.
2778
+ ____
2779
+
2780
+
2781
+ == Credits
2782
+
2783
+ Copyright Ribose Inc.