expressir 2.4.0 → 2.4.2

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 (129) hide show
  1. checksums.yaml +4 -4
  2. data/TODO.bugs/01-stale-transformer-autoload.md +39 -0
  3. data/TODO.bugs/02-parser-class-instance-vars.md +36 -0
  4. data/TODO.bugs/03-builder-mutable-state.md +43 -0
  5. data/TODO.bugs/04-formatter-public-send-dispatch.md +53 -0
  6. data/TODO.bugs/05-anonymous-formatter-subclass.md +45 -0
  7. data/TODO.bugs/06-collection-registry-single-source.md +53 -0
  8. data/TODO.bugs/07-require-relative-cleanup.md +42 -0
  9. data/TODO.bugs/08-require-expressir-in-commands.md +34 -0
  10. data/TODO.bugs/09-parser-split.md +53 -0
  11. data/TODO.bugs/10-to-s-override.md +42 -0
  12. data/TODO.bugs/11-parser-class-variables.md +39 -0
  13. data/TODO.bugs/12-marker-modules-vs-registry.md +64 -0
  14. data/TODO.bugs/13-string-literal-scanner-limitation.md +52 -0
  15. data/TODO.bugs/14-model-formatting-leak.md +30 -0
  16. data/TODO.bugs/15-expression-children-macro.md +27 -0
  17. data/TODO.bugs/16-pretty-formatter-duplication.md +28 -0
  18. data/TODO.bugs/17-snake-case-cache-mutable-constant.md +28 -0
  19. data/TODO.bugs/18-const-get-private-constants.md +30 -0
  20. data/TODO.bugs/19-format-methods-public.md +22 -0
  21. data/TODO.bugs/20-coverage-nested-entities-dedup.md +20 -0
  22. data/TODO.bugs/21-operator-tokens-secondary-dispatch.md +21 -0
  23. data/TODO.bugs/22-builder-fast-path-wrappers.md +32 -0
  24. data/TODO.bugs/23-coverage-inverse-maps.md +21 -0
  25. data/TODO.bugs/24-streaming-builder-complexity.md +19 -0
  26. data/TODO.bugs/25-debug-puts-in-production.md +21 -0
  27. data/TODO.bugs/26-generic-entity-children-misplaced.md +21 -0
  28. data/TODO.bugs/27-package-build-god-method.md +19 -0
  29. data/TODO.bugs/28-package-god-class.md +30 -0
  30. data/TODO.bugs/29-validate-ascii-god-class.md +24 -0
  31. data/TODO.bugs/30-unicode-map-extraction.md +19 -0
  32. data/TODO.bugs/README.md +43 -0
  33. data/TODO.max-perf/01-restore-ci-green.md +29 -0
  34. data/TODO.max-perf/02-streaming-parse-path.md +31 -0
  35. data/TODO.max-perf/03-cli-parallel-opt-in.md +27 -0
  36. data/TODO.max-perf/04-benchmark-harness.md +28 -0
  37. data/TODO.max-perf/05-parallel-fidelity-specs.md +22 -0
  38. data/TODO.max-perf/06-builder-cpu-audit.md +41 -0
  39. data/TODO.max-perf/07-upstream-parsanol-roadmap.md +27 -0
  40. data/TODO.max-perf/08-builder-build-perf.md +45 -0
  41. data/TODO.max-perf/09-grammar-cold-start.md +25 -0
  42. data/TODO.max-perf/10-parser-facade-hygiene.md +23 -0
  43. data/TODO.max-perf/11-ci-green-closeout.md +25 -0
  44. data/TODO.max-perf/12-require-boot-profile.md +25 -0
  45. data/TODO.max-perf/13-key-conversion-specs.md +26 -0
  46. data/TODO.max-perf/14-builder-call-handler-audit.md +28 -0
  47. data/benchmark/srl_benchmark.rb +76 -17
  48. data/docs/_tutorials/formatting-schemas.adoc +89 -0
  49. data/docs/_tutorials/index.adoc +10 -0
  50. data/docs/lychee.toml +3 -0
  51. data/expressir.gemspec +4 -2
  52. data/lib/expressir/cli.rb +3 -0
  53. data/lib/expressir/commands/changes_import_eengine.rb +0 -6
  54. data/lib/expressir/commands/changes_validate.rb +0 -2
  55. data/lib/expressir/commands/clean.rb +1 -1
  56. data/lib/expressir/commands/coverage.rb +6 -2
  57. data/lib/expressir/commands/file_violations.rb +70 -0
  58. data/lib/expressir/commands/format.rb +1 -1
  59. data/lib/expressir/commands/non_ascii_character.rb +49 -0
  60. data/lib/expressir/commands/non_ascii_violation_collection.rb +301 -0
  61. data/lib/expressir/commands/package.rb +143 -187
  62. data/lib/expressir/commands/validate_ascii.rb +0 -510
  63. data/lib/expressir/commands/version.rb +1 -1
  64. data/lib/expressir/commands.rb +3 -0
  65. data/lib/expressir/coverage.rb +49 -117
  66. data/lib/expressir/express/ast_key_converter.rb +114 -0
  67. data/lib/expressir/express/builder.rb +71 -194
  68. data/lib/expressir/express/builder_context.rb +22 -0
  69. data/lib/expressir/express/builders/expression_builder.rb +16 -16
  70. data/lib/expressir/express/cache.rb +35 -8
  71. data/lib/expressir/express/error.rb +24 -0
  72. data/lib/expressir/express/formatter.rb +33 -18
  73. data/lib/expressir/express/formatters/declarations_formatter.rb +24 -2
  74. data/lib/expressir/express/formatters/remark_formatter.rb +75 -2
  75. data/lib/expressir/express/formatters/statements_formatter.rb +20 -3
  76. data/lib/expressir/express/grammar/parser.rb +705 -0
  77. data/lib/expressir/express/grammar.rb +11 -0
  78. data/lib/expressir/express/node_position_index.rb +215 -0
  79. data/lib/expressir/express/parallel_files.rb +229 -0
  80. data/lib/expressir/express/parser.rb +138 -950
  81. data/lib/expressir/express/pretty_formatter.rb +23 -5
  82. data/lib/expressir/express/remark_attacher.rb +497 -581
  83. data/lib/expressir/express/remark_scanner.rb +84 -19
  84. data/lib/expressir/express/schema_block_scanner.rb +137 -0
  85. data/lib/expressir/express/schema_source_formatter.rb +15 -0
  86. data/lib/expressir/express/scope_resolver.rb +223 -0
  87. data/lib/expressir/express/source_formatter.rb +15 -0
  88. data/lib/expressir/express/streaming_builder.rb +147 -176
  89. data/lib/expressir/express.rb +10 -6
  90. data/lib/expressir/model/concerns.rb +3 -0
  91. data/lib/expressir/model/data_types/generic_entity.rb +6 -6
  92. data/lib/expressir/model/declarations/entity.rb +5 -0
  93. data/lib/expressir/model/declarations/function.rb +5 -0
  94. data/lib/expressir/model/declarations/procedure.rb +5 -0
  95. data/lib/expressir/model/declarations/rule.rb +6 -0
  96. data/lib/expressir/model/declarations/schema.rb +21 -8
  97. data/lib/expressir/model/declarations/type.rb +3 -0
  98. data/lib/expressir/model/exp_file.rb +2 -0
  99. data/lib/expressir/model/expressions/aggregate_initializer.rb +1 -0
  100. data/lib/expressir/model/expressions/aggregate_initializer_item.rb +1 -0
  101. data/lib/expressir/model/expressions/binary_expression.rb +1 -0
  102. data/lib/expressir/model/expressions/entity_constructor.rb +1 -0
  103. data/lib/expressir/model/expressions/function_call.rb +1 -0
  104. data/lib/expressir/model/expressions/interval.rb +1 -0
  105. data/lib/expressir/model/expressions/query_expression.rb +2 -0
  106. data/lib/expressir/model/expressions/unary_expression.rb +1 -0
  107. data/lib/expressir/model/model_element.rb +133 -14
  108. data/lib/expressir/model/remark_format.rb +17 -0
  109. data/lib/expressir/model/remark_info.rb +31 -3
  110. data/lib/expressir/model/remark_placement.rb +29 -0
  111. data/lib/expressir/model/repository.rb +18 -5
  112. data/lib/expressir/model/statements/alias.rb +2 -0
  113. data/lib/expressir/model/statements/assignment.rb +4 -0
  114. data/lib/expressir/model/statements/case.rb +21 -0
  115. data/lib/expressir/model/statements/case_action.rb +1 -0
  116. data/lib/expressir/model/statements/compound.rb +4 -0
  117. data/lib/expressir/model/statements/escape.rb +3 -0
  118. data/lib/expressir/model/statements/if.rb +5 -0
  119. data/lib/expressir/model/statements/null.rb +3 -0
  120. data/lib/expressir/model/statements/procedure_call.rb +3 -0
  121. data/lib/expressir/model/statements/repeat.rb +3 -0
  122. data/lib/expressir/model/statements/return.rb +3 -0
  123. data/lib/expressir/model/statements/skip.rb +3 -0
  124. data/lib/expressir/model.rb +12 -3
  125. data/lib/expressir/package/builder.rb +2 -2
  126. data/lib/expressir/version.rb +6 -1
  127. data/lib/expressir.rb +56 -3
  128. metadata +81 -7
  129. data/lib/expressir/express/transformer/remark_handling.rb +0 -194
@@ -91,202 +91,158 @@ module Expressir
91
91
  desc: "Skip manifest verification (may result in incomplete packages)"
92
92
  option :verbose, type: :boolean, default: false,
93
93
  desc: "Enable verbose output"
94
+ option :max_processes, type: :numeric, default: 1,
95
+ desc: "Parallel parse workers for schema loading (1 = sequential)"
94
96
  def build(root_schema = nil, output = nil)
95
- schema_files = if options[:manifest]
96
- # Manifest-based mode
97
- unless File.exist?(options[:manifest])
98
- raise Expressir::ManifestNotFoundError.new(options[:manifest])
99
- end
100
-
101
- # Override output if not provided
102
- output ||= root_schema
103
- unless output
104
- raise Expressir::MissingRequiredArgumentError.new(
105
- "OUTPUT path is required",
106
- usage_hint: "expressir package build --manifest MANIFEST.yaml OUTPUT.ler",
107
- )
108
- end
109
-
110
- # Build repository
111
- say "Building LER package from manifest #{options[:manifest]}..." if options[:verbose]
112
-
113
- # Load manifest for verification and data extraction
114
- manifest = Expressir::SchemaManifest.from_file(options[:manifest])
115
- manifest_data = YAML.load_file(options[:manifest])
116
- manifest_dir = File.dirname(File.expand_path(options[:manifest]))
117
-
118
- # Verify manifest unless --skip-verify is used
119
- if options[:skip_verify]
120
- say "⚠ Warning: Skipping manifest verification",
121
- :yellow
122
- say " This set of EXPRESS schemas may not be fully internally consistent.",
123
- :yellow
124
- say ""
125
- else
126
- say "Verifying manifest integrity..."
127
-
128
- validator = Expressir::Manifest::Validator.new(
129
- manifest, options.merge(verbose: true)
130
- )
131
-
132
- # Check file existence
133
- file_errors = validator.validate_file_existence
134
- unless file_errors.empty?
135
- raise Expressir::ManifestValidationError.new(
136
- "Manifest validation failed",
137
- errors: file_errors.map { |e| e[:message] },
138
- )
139
- end
140
-
141
- # Check referential integrity
142
- reference_errors = validator.validate_referential_integrity
143
- unless reference_errors.empty?
144
- raise Expressir::ReferentialIntegrityError.new(
145
- reference_errors,
146
- message: "Manifest has unresolved dependencies",
147
- )
148
- end
149
-
150
- say "✓ Manifest verified", :green
151
- end
152
-
153
- # Validate and collect paths
154
- errors = []
155
- warnings = []
156
- paths = []
157
-
158
- manifest_data["schemas"].each do |schema_id, schema_data|
159
- schema_path = schema_data["path"]
160
-
161
- if schema_path.nil? || schema_path.empty?
162
- warnings << "Schema '#{schema_id}' has no path specified"
163
- next
164
- end
165
-
166
- # Expand relative paths relative to manifest directory
167
- full_path = if Pathname.new(schema_path).absolute?
168
- schema_path
169
- else
170
- File.expand_path(schema_path,
171
- manifest_dir)
172
- end
173
-
174
- unless File.exist?(full_path)
175
- errors << "Schema file not found: #{full_path} (#{schema_id})"
176
- next
177
- end
178
-
179
- paths << full_path
180
- end
181
-
182
- unless errors.empty?
183
- raise Expressir::ManifestValidationError.new(
184
- "Manifest validation failed",
185
- errors: errors,
186
- )
187
- end
188
-
189
- if options[:verbose] && warnings.any?
190
- say "Warnings:"
191
- warnings.each { |w| say " - #{w}", :yellow }
192
- end
193
-
194
- say " Using #{paths.size} schema(s) from manifest" if options[:verbose]
195
- paths
196
- else
197
- # Auto-resolution mode
198
- unless root_schema
199
- raise Expressir::MissingRequiredArgumentError.new(
200
- "ROOT_SCHEMA is required when not using --manifest",
201
- usage_hint: "expressir package build ROOT_SCHEMA OUTPUT.ler",
202
- )
203
- end
204
-
205
- unless output
206
- raise Expressir::MissingRequiredArgumentError.new(
207
- "OUTPUT path is required",
208
- usage_hint: "expressir package build ROOT_SCHEMA OUTPUT.ler",
209
- )
210
- end
211
-
212
- say "Building LER package from #{root_schema}..." if options[:verbose]
213
-
214
- # Resolve dependencies
215
- say "Resolving dependencies..." if options[:verbose]
216
- base_dirs = if options[:base_dirs]
217
- options[:base_dirs].split(",").map(&:strip)
218
- else
219
- # Default to the directory containing the root schema
220
- [File.dirname(File.expand_path(root_schema))]
221
- end
222
-
223
- if options[:verbose] && base_dirs.size == 1
224
- say " Using base directory: #{base_dirs.first}"
225
- say " Tip: Use --base-dirs to specify additional search paths for dependencies"
226
- end
227
-
228
- resolver = Expressir::Model::DependencyResolver.new(
229
- base_dirs: base_dirs,
230
- verbose: options[:verbose],
231
- )
232
- resolved = resolver.resolve_dependencies(root_schema)
233
- say " Found #{resolved.size} schema(s)" if options[:verbose]
234
- resolved
235
- end
236
-
237
- # Build repository
238
- say "Building repository..." if options[:verbose]
239
- repo = Expressir::Model::Repository.from_files(schema_files)
240
-
241
- # Validate if requested
242
- # When using --skip-verify, skip validation unless explicitly requested with --validate
243
- should_validate = if options[:manifest] && options[:skip_verify]
244
- # Check if --validate was explicitly passed
245
- # Thor doesn't have a way to check if option was set by user vs default
246
- # So we check if --no-validate was explicitly disabled
247
- false
248
- else
249
- options[:validate]
250
- end
251
-
252
- if should_validate
253
- say "Validating repository..." if options[:verbose]
254
- validation = repo.validate
255
- unless validation[:valid?]
256
- raise Expressir::SchemaValidationError.new(
257
- "Repository validation failed",
258
- errors: (validation[:errors] || []).map do |e|
259
- if e.is_a?(Hash)
260
- msg = e[:message] || "Unknown error"
261
- type = e[:type] ? "[#{e[:type]}] " : ""
262
- schema = e[:schema] ? " (schema: #{e[:schema]})" : ""
263
- ref = e[:referenced_schema] ? " (referenced: #{e[:referenced_schema]})" : ""
264
- iface = e[:interface_type] ? " (interface: #{e[:interface_type]})" : ""
265
- "#{type}#{msg}#{schema}#{ref}#{iface}"
266
- else
267
- e.to_s
268
- end
269
- end,
270
- )
97
+ schema_files, output = resolve_schema_files(root_schema, output)
98
+ repo = Expressir::Model::Repository.from_files(schema_files,
99
+ max_processes: options[:max_processes])
100
+ validate_repository(repo) if should_validate?
101
+ create_package(repo, output)
102
+ rescue Expressir::Error
103
+ raise
104
+ rescue StandardError => e
105
+ raise Expressir::PackageBuildError.new(
106
+ "Error building package: #{e.message}",
107
+ command_name: "package build",
108
+ )
109
+ end
110
+
111
+ private
112
+
113
+ def resolve_schema_files(root_schema, output)
114
+ if options[:manifest]
115
+ [resolve_from_manifest(output), output || root_schema]
116
+ else
117
+ [resolve_from_auto_resolution(root_schema), output]
118
+ end
119
+ end
120
+
121
+ def resolve_from_manifest(output)
122
+ raise Expressir::ManifestNotFoundError.new(options[:manifest]) unless File.exist?(options[:manifest])
123
+
124
+ unless output || root_schema_arg
125
+ raise Expressir::MissingRequiredArgumentError.new(
126
+ "OUTPUT path is required",
127
+ usage_hint: "expressir package build --manifest MANIFEST.yaml OUTPUT.ler",
128
+ )
129
+ end
130
+
131
+ say "Building LER package from manifest #{options[:manifest]}..." if options[:verbose]
132
+
133
+ manifest = Expressir::SchemaManifest.from_file(options[:manifest])
134
+ manifest_data = YAML.load_file(options[:manifest])
135
+ manifest_dir = File.dirname(File.expand_path(options[:manifest]))
136
+
137
+ verify_manifest(manifest) unless options[:skip_verify]
138
+ collect_manifest_paths(manifest_data, manifest_dir)
139
+ end
140
+
141
+ def verify_manifest(manifest)
142
+ say "Verifying manifest integrity..."
143
+ validator = Expressir::Manifest::Validator.new(manifest, options.merge(verbose: true))
144
+
145
+ file_errors = validator.validate_file_existence
146
+ unless file_errors.empty?
147
+ raise Expressir::ManifestValidationError.new(
148
+ "Manifest validation failed", errors: file_errors.map { |e| e[:message] }
149
+ )
150
+ end
151
+
152
+ reference_errors = validator.validate_referential_integrity
153
+ unless reference_errors.empty?
154
+ raise Expressir::ReferentialIntegrityError.new(
155
+ reference_errors, message: "Manifest has unresolved dependencies"
156
+ )
157
+ end
158
+
159
+ say "✓ Manifest verified", :green
160
+ end
161
+
162
+ def collect_manifest_paths(manifest_data, manifest_dir)
163
+ errors = []
164
+ warnings = []
165
+ paths = []
166
+
167
+ manifest_data["schemas"].each do |schema_id, schema_data|
168
+ schema_path = schema_data["path"]
169
+ if schema_path.nil? || schema_path.empty?
170
+ warnings << "Schema '#{schema_id}' has no path specified"
171
+ next
271
172
  end
272
- say " ✓ Validation passed" if options[:verbose]
173
+
174
+ full_path = Pathname.new(schema_path).absolute? ? schema_path : File.expand_path(schema_path, manifest_dir)
175
+ if File.exist?(full_path)
176
+ paths << full_path
177
+ else
178
+ errors << "Schema file not found: #{full_path} (#{schema_id})"
179
+ end
180
+ end
181
+
182
+ raise Expressir::ManifestValidationError.new("Manifest validation failed", errors: errors) unless errors.empty?
183
+
184
+ say "Warnings:" if options[:verbose] && warnings.any?
185
+ warnings.each { |w| say " - #{w}", :yellow } if options[:verbose] && warnings.any?
186
+ say " Using #{paths.size} schema(s) from manifest" if options[:verbose]
187
+ paths
188
+ end
189
+
190
+ def resolve_from_auto_resolution(root_schema)
191
+ unless root_schema
192
+ raise Expressir::MissingRequiredArgumentError.new(
193
+ "ROOT_SCHEMA is required when not using --manifest",
194
+ usage_hint: "expressir package build ROOT_SCHEMA OUTPUT.ler",
195
+ )
196
+ end
197
+
198
+ say "Building LER package from #{root_schema}..." if options[:verbose]
199
+ say "Resolving dependencies..." if options[:verbose]
200
+
201
+ base_dirs = options[:base_dirs] ? options[:base_dirs].split(",").map(&:strip) : [File.dirname(File.expand_path(root_schema))]
202
+ if options[:verbose] && base_dirs.size == 1
203
+ say " Using base directory: #{base_dirs.first}"
204
+ say " Tip: Use --base-dirs to specify additional search paths for dependencies"
205
+ end
206
+
207
+ resolver = Expressir::Model::DependencyResolver.new(base_dirs: base_dirs, verbose: options[:verbose])
208
+ resolved = resolver.resolve_dependencies(root_schema)
209
+ say " Found #{resolved.size} schema(s)" if options[:verbose]
210
+ resolved
211
+ end
212
+
213
+ def should_validate?
214
+ return false if options[:manifest] && options[:skip_verify]
215
+
216
+ options[:validate]
217
+ end
218
+
219
+ def validate_repository(repo)
220
+ say "Validating repository..." if options[:verbose]
221
+ validation = repo.validate
222
+ return if validation[:valid?]
223
+
224
+ formatted_errors = (validation[:errors] || []).map do |e|
225
+ next e.to_s unless e.is_a?(Hash)
226
+
227
+ msg = e[:message] || "Unknown error"
228
+ type = e[:type] ? "[#{e[:type]}] " : ""
229
+ schema = e[:schema] ? " (schema: #{e[:schema]})" : ""
230
+ ref = e[:referenced_schema] ? " (referenced: #{e[:referenced_schema]})" : ""
231
+ iface = e[:interface_type] ? " (interface: #{e[:interface_type]})" : ""
232
+ "#{type}#{msg}#{schema}#{ref}#{iface}"
273
233
  end
234
+ raise Expressir::SchemaValidationError.new("Repository validation failed", errors: formatted_errors)
235
+ end
274
236
 
275
- # Build package
237
+ def create_package(repo, output)
276
238
  say "Creating package..." if options[:verbose]
277
239
  repo.export_to_package(output, build_package_options)
278
-
279
240
  say "✓ Package created: #{output}", :green
280
241
  say " Schemas: #{repo.schemas.size}", :green if options[:verbose]
281
- rescue Expressir::Error
282
- raise # Re-raise Expressir errors
283
- rescue StandardError => e
284
- raise Expressir::PackageBuildError.new(
285
- "Error building package: #{e.message}",
286
- command_name: "package build",
287
- )
288
242
  end
289
243
 
244
+ public
245
+
290
246
  desc "info PACKAGE", "Display package metadata and statistics"
291
247
  long_desc <<~DESC
292
248
  Display comprehensive information about an LER package including metadata,
@@ -430,7 +386,7 @@ module Expressir
430
386
  zip.each do |entry|
431
387
  dest_path = File.join(output_dir, entry.name)
432
388
  FileUtils.mkdir_p(File.dirname(dest_path))
433
- entry.extract(dest_path) unless File.exist?(dest_path)
389
+ entry.extract(entry.name, destination_directory: output_dir) unless File.exist?(dest_path)
434
390
  end
435
391
  end
436
392