expressir 2.1.31 → 2.2.0

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 (75) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/docs.yml +3 -2
  3. data/.github/workflows/release.yml +6 -0
  4. data/.rubocop_todo.yml +98 -89
  5. data/Gemfile +1 -1
  6. data/README.adoc +372 -1
  7. data/docs/_guides/formatter/formatter-architecture.adoc +401 -0
  8. data/docs/_guides/ruby-api/parsing-files.adoc +1 -1
  9. data/docs/_pages/parsers.adoc +31 -5
  10. data/docs/lychee.toml +3 -0
  11. data/expressir.gemspec +2 -2
  12. data/lib/expressir/benchmark.rb +6 -6
  13. data/lib/expressir/cli.rb +9 -0
  14. data/lib/expressir/commands/format.rb +28 -0
  15. data/lib/expressir/coverage.rb +15 -11
  16. data/lib/expressir/express/builder.rb +350 -0
  17. data/lib/expressir/express/builders/attribute_decl_builder.rb +38 -0
  18. data/lib/expressir/express/builders/built_in_builder.rb +88 -0
  19. data/lib/expressir/express/builders/constant_builder.rb +115 -0
  20. data/lib/expressir/express/builders/declaration_builder.rb +24 -0
  21. data/lib/expressir/express/builders/derive_clause_builder.rb +16 -0
  22. data/lib/expressir/express/builders/derived_attr_builder.rb +28 -0
  23. data/lib/expressir/express/builders/domain_rule_builder.rb +21 -0
  24. data/lib/expressir/express/builders/entity_decl_builder.rb +108 -0
  25. data/lib/expressir/express/builders/explicit_attr_builder.rb +52 -0
  26. data/lib/expressir/express/builders/expression_builder.rb +453 -0
  27. data/lib/expressir/express/builders/function_decl_builder.rb +84 -0
  28. data/lib/expressir/express/builders/helpers.rb +148 -0
  29. data/lib/expressir/express/builders/interface_builder.rb +171 -0
  30. data/lib/expressir/express/builders/inverse_attr_builder.rb +45 -0
  31. data/lib/expressir/express/builders/inverse_attr_type_builder.rb +36 -0
  32. data/lib/expressir/express/builders/inverse_clause_builder.rb +16 -0
  33. data/lib/expressir/express/builders/literal_builder.rb +107 -0
  34. data/lib/expressir/express/builders/procedure_decl_builder.rb +80 -0
  35. data/lib/expressir/express/builders/qualifier_builder.rb +128 -0
  36. data/lib/expressir/express/builders/reference_builder.rb +27 -0
  37. data/lib/expressir/express/builders/rule_decl_builder.rb +95 -0
  38. data/lib/expressir/express/builders/schema_body_decl_builder.rb +22 -0
  39. data/lib/expressir/express/builders/schema_decl_builder.rb +62 -0
  40. data/lib/expressir/express/builders/schema_version_builder.rb +40 -0
  41. data/lib/expressir/express/builders/simple_id_builder.rb +26 -0
  42. data/lib/expressir/express/builders/statement_builder.rb +250 -0
  43. data/lib/expressir/express/builders/subtype_constraint_builder.rb +188 -0
  44. data/lib/expressir/express/builders/syntax_builder.rb +19 -0
  45. data/lib/expressir/express/builders/token_builder.rb +15 -0
  46. data/lib/expressir/express/builders/type_builder.rb +264 -0
  47. data/lib/expressir/express/builders/type_decl_builder.rb +32 -0
  48. data/lib/expressir/express/builders/unique_clause_builder.rb +22 -0
  49. data/lib/expressir/express/builders/unique_rule_builder.rb +36 -0
  50. data/lib/expressir/express/builders/where_clause_builder.rb +22 -0
  51. data/lib/expressir/express/builders.rb +43 -0
  52. data/lib/expressir/express/error.rb +18 -2
  53. data/lib/expressir/express/formatter.rb +18 -1508
  54. data/lib/expressir/express/formatters/data_types_formatter.rb +317 -0
  55. data/lib/expressir/express/formatters/declarations_formatter.rb +689 -0
  56. data/lib/expressir/express/formatters/expressions_formatter.rb +160 -0
  57. data/lib/expressir/express/formatters/literals_formatter.rb +46 -0
  58. data/lib/expressir/express/formatters/references_formatter.rb +42 -0
  59. data/lib/expressir/express/formatters/remark_formatter.rb +296 -0
  60. data/lib/expressir/express/formatters/statements_formatter.rb +224 -0
  61. data/lib/expressir/express/formatters/supertype_expressions_formatter.rb +48 -0
  62. data/lib/expressir/express/parser.rb +129 -14
  63. data/lib/expressir/express/pretty_formatter.rb +624 -0
  64. data/lib/expressir/express/remark_attacher.rb +1155 -0
  65. data/lib/expressir/express/resolve_references_model_visitor.rb +1 -0
  66. data/lib/expressir/express/streaming_builder.rb +467 -0
  67. data/lib/expressir/express/transformer/remark_handling.rb +196 -0
  68. data/lib/expressir/model/identifier.rb +1 -1
  69. data/lib/expressir/model/model_element.rb +30 -2
  70. data/lib/expressir/model/remark_info.rb +51 -0
  71. data/lib/expressir/model/search_engine.rb +58 -9
  72. data/lib/expressir/version.rb +1 -1
  73. data/lib/expressir.rb +5 -1
  74. metadata +56 -7
  75. data/lib/expressir/express/visitor.rb +0 -2815
data/README.adoc CHANGED
@@ -30,6 +30,132 @@ Expressir consists of 3 parts:
30
30
  // ** OMG UML 2 for Eclipse (XMI 2.1)
31
31
 
32
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 | Speedup | Notes
121
+
122
+ | Ruby (Parsanol) | 3036 ms | 1x (baseline) | Pure Ruby parsing
123
+ | Native Batch (Parsanol) | 153 ms | 19.9x faster | AST via u64 array transfer
124
+ | Native ZeroCopy (Parsanol) | 106 ms | 28.7x faster | Zero-copy with source positions
125
+ |===
126
+
127
+ === Features
128
+
129
+ When Parsanol is installed:
130
+
131
+ * **18-44x faster parsing** - Rust native backend
132
+ * **99.5% fewer allocations** - Arena-based AST construction
133
+ * **Source position tracking** - Slice objects for error reporting
134
+ * **Streaming Builder API** - Zero-allocation custom parsing
135
+
136
+ === Usage
137
+
138
+ Expressir automatically uses Parsanol when available:
139
+
140
+ [source,ruby]
141
+ ----
142
+ # Automatically uses Parsanol native parser when available
143
+ repo = Expressir::Express::Parser.from_file("geometry.exp")
144
+
145
+ # Check if native parser is being used
146
+ if Parsanol::Native.available?
147
+ puts "Using Parsanol (Rust parser)"
148
+ end
149
+ ----
150
+
151
+ For maximum performance, ensure the Parsanol gem is installed:
152
+
153
+ [source,sh]
154
+ ----
155
+ gem install parsanol
156
+ ----
157
+
158
+
33
159
  == Installation
34
160
 
35
161
  Add this line to your application's `Gemfile`:
@@ -80,7 +206,7 @@ Commands:
80
206
  expressir clean PATH # Strip remarks and prettify EXPRESS schema at PATH
81
207
  expressir format PATH # pretty print EXPRESS schema located at PATH
82
208
  expressir help [COMMAND] # Describe available commands or one specific command
83
- expressir validate *PATH # validate EXPRESS schema located at PATH
209
+ expressir validate load *PATH # validate EXPRESS schema located at PATH
84
210
  expressir validate ascii PATH # Validate EXPRESS files for ASCII-only content (excluding remarks)
85
211
  expressir coverage *PATH # List EXPRESS entities and check documentation coverage
86
212
  expressir version # Expressir Version
@@ -103,6 +229,251 @@ This command:
103
229
  . Formats it with consistent indentation and spacing
104
230
  . Outputs the formatted schema to stdout
105
231
 
232
+ === Pretty print with ELF compliance
233
+
234
+ The `PrettyFormatter` class provides ELF (EXPRESS Language Foundation) compliant
235
+ pretty printing with configurable formatting options. This formatter follows the
236
+ https://www.express-language-foundation.org/pretty-print-spec/[ELF Pretty Print specification].
237
+
238
+ ==== Using PrettyFormatter in Ruby
239
+
240
+ [source,ruby]
241
+ ----
242
+ # Basic usage - formats with default settings
243
+ repository = Expressir::Express::Parser.from_file("schema.exp")
244
+ formatter = Expressir::Express::PrettyFormatter.new
245
+ formatted = formatter.format(repository)
246
+ puts formatted
247
+ ----
248
+
249
+ ==== Configuration options
250
+
251
+ The PrettyFormatter supports several configuration options to customize the output.
252
+
253
+ [source,ruby]
254
+ ----
255
+ formatter = Expressir::Express::PrettyFormatter.new(
256
+ indent: 4, # Spaces per indentation level (default: 4)
257
+ line_length: 80, # Maximum line length (default: nil)
258
+ provenance: true, # Include provenance info (default: true)
259
+ provenance_name: "MyTool", # Tool name (default: "Expressir")
260
+ provenance_version: "1.0.0", # Tool version (default: Expressir::VERSION)
261
+ no_remarks: false # Suppress remarks (default: false)
262
+ )
263
+ ----
264
+
265
+ [options="header"]
266
+ |===
267
+ | Option | Type | Default | Description
268
+
269
+ | `indent`
270
+ | Integer
271
+ | `4`
272
+ | Number of spaces per indentation level
273
+
274
+ | `line_length`
275
+ | Integer or nil
276
+ | `nil`
277
+ | Maximum line length (not yet enforced)
278
+
279
+ | `provenance`
280
+ | Boolean
281
+ | `true`
282
+ | Include provenance information in output
283
+
284
+ | `provenance_name`
285
+ | String
286
+ | `"Expressir"`
287
+ | Tool name for provenance
288
+
289
+ | `provenance_version`
290
+ | String
291
+ | `Expressir::VERSION`
292
+ | Tool version for provenance
293
+
294
+ | `no_remarks`
295
+ | Boolean
296
+ | `false`
297
+ | Suppress remarks from source schema
298
+ |===
299
+
300
+ ==== Environment variable configuration
301
+
302
+ Configuration can be overridden using environment variables. Environment variables
303
+ take precedence over defaults but are overridden by explicit options:
304
+
305
+ [source,sh]
306
+ ----
307
+ # Set environment variables
308
+ export EXPRESSIR_INDENT=2
309
+ export EXPRESSIR_LINE_LENGTH=100
310
+ export EXPRESSIR_PROVENANCE=false
311
+ export EXPRESSIR_PROVENANCE_NAME="CustomTool"
312
+ export EXPRESSIR_PROVENANCE_VERSION="2.0.0"
313
+
314
+ # These will be used unless overridden by options
315
+ ruby my_formatter.rb
316
+ ----
317
+
318
+ [options="header"]
319
+ |===
320
+ | Environment Variable | Description
321
+
322
+ | `EXPRESSIR_INDENT`
323
+ | Indentation width (spaces)
324
+
325
+ | `EXPRESSIR_LINE_LENGTH`
326
+ | Maximum line length
327
+
328
+ | `EXPRESSIR_PROVENANCE`
329
+ | Enable/disable provenance (`true`/`false`)
330
+
331
+ | `EXPRESSIR_PROVENANCE_NAME`
332
+ | Tool name for provenance
333
+
334
+ | `EXPRESSIR_PROVENANCE_VERSION`
335
+ | Tool version for provenance
336
+ |===
337
+
338
+ The configuration follows a MECE (Mutually Exclusive, Collectively Exhaustive)
339
+ hierarchy: **Options > ENV > Defaults**
340
+
341
+ ==== Key features
342
+
343
+ The PrettyFormatter provides several enhancements over the standard formatter:
344
+
345
+ CONSTANT alignment:: Constants in CONSTANT blocks are aligned at both the colon
346
+ and assignment operator positions for improved readability.
347
+ +
348
+ [example]
349
+ ====
350
+ [source]
351
+ ----
352
+ CONSTANT
353
+ short_name : INTEGER := 1;
354
+ longer_name : STRING := 'test';
355
+ x : REAL := 3.14;
356
+ END_CONSTANT;
357
+ ----
358
+ ====
359
+
360
+ Provenance information:: Automatically includes metadata about the formatting
361
+ tool and parameters used, aiding in reproducibility.
362
+ +
363
+ [example]
364
+ ====
365
+ [source]
366
+ ----
367
+ (*
368
+ Generated by: Expressir version 2.1.31
369
+ Format parameters: indent: 4
370
+ *)
371
+ ----
372
+ ====
373
+
374
+ Configurable indentation:: Supports custom indentation width (2, 4, 8 spaces, etc.)
375
+ to match project coding standards.
376
+
377
+ Preamble support:: Preserves and formats source-level remarks that appear before
378
+ the first SCHEMA declaration.
379
+
380
+ ==== Comparison with standard Formatter
381
+
382
+ [options="header"]
383
+ |===
384
+ | Feature | Formatter | PrettyFormatter
385
+
386
+ | Indentation
387
+ | 2 spaces (fixed)
388
+ | Configurable (default: 4)
389
+
390
+ | CONSTANT alignment
391
+ | No
392
+ | Yes (colons and assignments)
393
+
394
+ | Provenance
395
+ | No
396
+ | Yes (configurable)
397
+
398
+ | Preamble formatting
399
+ | No
400
+ | Yes
401
+
402
+ | Line length enforcement
403
+ | No
404
+ | Planned (not yet implemented)
405
+
406
+ | ELF compliant
407
+ | No
408
+ | Yes
409
+
410
+ | Configuration via ENV
411
+ | No
412
+ | Yes
413
+ |===
414
+
415
+ ==== Example usage
416
+
417
+ .Formatting with custom settings
418
+ [example]
419
+ ====
420
+ [source,ruby]
421
+ ----
422
+ # Parse schema
423
+ repository = Expressir::Express::Parser.from_file("examples/ler/simple_schema.exp")
424
+
425
+ # Format with custom indentation and provenance
426
+ formatter = Expressir::Express::PrettyFormatter.new(
427
+ indent: 2,
428
+ provenance_name: "MyFormatter",
429
+ provenance_version: "1.0.0"
430
+ )
431
+
432
+ formatted = formatter.format(repository)
433
+
434
+ # Save to file
435
+ File.write("formatted_schema.exp", formatted)
436
+ ----
437
+ ====
438
+
439
+ .Formatting without provenance
440
+ [example]
441
+ ====
442
+ [source,ruby]
443
+ ----
444
+ # Format without provenance information
445
+ formatter = Expressir::Express::PrettyFormatter.new(provenance: false)
446
+ repository = Expressir::Express::Parser.from_file("schema.exp")
447
+ formatted = formatter.format(repository)
448
+
449
+ # Output is clean without metadata comments
450
+ puts formatted
451
+ ----
452
+ ====
453
+
454
+ .Round-trip formatting verification
455
+ [example]
456
+ ====
457
+ [source,ruby]
458
+ ----
459
+ # Format a schema
460
+ original = Expressir::Express::Parser.from_file("schema.exp")
461
+ formatter = Expressir::Express::PrettyFormatter.new
462
+
463
+ # Format once
464
+ formatted1 = formatter.format(original)
465
+
466
+ # Parse the formatted output
467
+ File.write("temp.exp", formatted1)
468
+ reparsed = Expressir::Express::Parser.from_file("temp.exp")
469
+
470
+ # Format again - should be identical (stable formatting)
471
+ formatted2 = formatter.format(reparsed)
472
+
473
+ puts "Formatting is stable" if formatted1 == formatted2
474
+ ----
475
+ ====
476
+
106
477
  === Clean schema
107
478
 
108
479
  The `clean` command strips remarks and prettifies EXPRESS schemas. This is