expressir 2.1.29 → 2.1.31

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 (111) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/docs.yml +98 -0
  3. data/.github/workflows/links.yml +100 -0
  4. data/.github/workflows/rake.yml +4 -0
  5. data/.github/workflows/release.yml +5 -0
  6. data/.github/workflows/validate_schemas.yml +1 -1
  7. data/.gitignore +3 -0
  8. data/.rubocop.yml +1 -1
  9. data/.rubocop_todo.yml +209 -55
  10. data/Gemfile +2 -1
  11. data/README.adoc +650 -83
  12. data/docs/Gemfile +12 -0
  13. data/docs/_config.yml +141 -0
  14. data/docs/_guides/changes/changes-format.adoc +778 -0
  15. data/docs/_guides/changes/importing-eengine.adoc +898 -0
  16. data/docs/_guides/changes/index.adoc +396 -0
  17. data/docs/_guides/changes/programmatic-usage.adoc +1038 -0
  18. data/docs/_guides/changes/validating-changes.adoc +681 -0
  19. data/docs/_guides/cli/benchmark-performance.adoc +834 -0
  20. data/docs/_guides/cli/coverage-analysis.adoc +921 -0
  21. data/docs/_guides/cli/format-schemas.adoc +547 -0
  22. data/docs/_guides/cli/index.adoc +8 -0
  23. data/docs/_guides/cli/managing-changes.adoc +927 -0
  24. data/docs/_guides/cli/validate-ascii.adoc +645 -0
  25. data/docs/_guides/cli/validate-schemas.adoc +534 -0
  26. data/docs/_guides/index.adoc +165 -0
  27. data/docs/_guides/ler/creating-packages.adoc +664 -0
  28. data/docs/_guides/ler/index.adoc +305 -0
  29. data/docs/_guides/ler/loading-packages.adoc +707 -0
  30. data/docs/_guides/ler/package-formats.adoc +748 -0
  31. data/docs/_guides/ler/querying-packages.adoc +826 -0
  32. data/docs/_guides/ler/validating-packages.adoc +750 -0
  33. data/docs/_guides/liquid/basic-templates.adoc +813 -0
  34. data/docs/_guides/liquid/documentation-generation.adoc +1042 -0
  35. data/docs/_guides/liquid/drops-reference.adoc +829 -0
  36. data/docs/_guides/liquid/filters-and-tags.adoc +912 -0
  37. data/docs/_guides/liquid/index.adoc +468 -0
  38. data/docs/_guides/manifests/creating-manifests.adoc +483 -0
  39. data/docs/_guides/manifests/index.adoc +307 -0
  40. data/docs/_guides/manifests/resolving-manifests.adoc +557 -0
  41. data/docs/_guides/manifests/validating-manifests.adoc +713 -0
  42. data/docs/_guides/ruby-api/formatting-schemas.adoc +605 -0
  43. data/docs/_guides/ruby-api/index.adoc +257 -0
  44. data/docs/_guides/ruby-api/parsing-files.adoc +421 -0
  45. data/docs/_guides/ruby-api/search-engine.adoc +609 -0
  46. data/docs/_guides/ruby-api/working-with-repository.adoc +577 -0
  47. data/docs/_pages/data-model.adoc +665 -0
  48. data/docs/_pages/express-language.adoc +506 -0
  49. data/docs/_pages/getting-started.adoc +414 -0
  50. data/docs/_pages/index.adoc +116 -0
  51. data/docs/_pages/introduction.adoc +256 -0
  52. data/docs/_pages/ler-packages.adoc +837 -0
  53. data/docs/_pages/parsers.adoc +683 -0
  54. data/docs/_pages/schema-manifests.adoc +431 -0
  55. data/docs/_references/index.adoc +228 -0
  56. data/docs/_tutorials/creating-ler-package.adoc +735 -0
  57. data/docs/_tutorials/documentation-coverage.adoc +795 -0
  58. data/docs/_tutorials/index.adoc +221 -0
  59. data/docs/_tutorials/liquid-templates.adoc +806 -0
  60. data/docs/_tutorials/parsing-your-first-schema.adoc +522 -0
  61. data/docs/_tutorials/querying-schemas.adoc +751 -0
  62. data/docs/_tutorials/working-with-multiple-schemas.adoc +676 -0
  63. data/docs/index.adoc +242 -0
  64. data/docs/lychee.toml +84 -0
  65. data/examples/demo_ler_usage.sh +86 -0
  66. data/examples/ler/README.md +111 -0
  67. data/examples/ler/simple_example.ler +0 -0
  68. data/examples/ler/simple_schema.exp +33 -0
  69. data/examples/ler_build.rb +75 -0
  70. data/examples/ler_cli.rb +79 -0
  71. data/examples/ler_demo_complete.rb +276 -0
  72. data/examples/ler_query.rb +91 -0
  73. data/examples/ler_query_examples.rb +305 -0
  74. data/examples/ler_stats.rb +81 -0
  75. data/examples/phase3_demo.rb +159 -0
  76. data/examples/query_demo_simple.rb +131 -0
  77. data/expressir.gemspec +2 -0
  78. data/lib/expressir/changes/schema_change.rb +32 -22
  79. data/lib/expressir/changes/{edition_change.rb → version_change.rb} +3 -3
  80. data/lib/expressir/cli.rb +12 -4
  81. data/lib/expressir/commands/changes_import_eengine.rb +2 -2
  82. data/lib/expressir/commands/changes_validate.rb +1 -1
  83. data/lib/expressir/commands/manifest.rb +427 -0
  84. data/lib/expressir/commands/package.rb +1274 -0
  85. data/lib/expressir/commands/validate.rb +70 -37
  86. data/lib/expressir/commands/validate_ascii.rb +607 -0
  87. data/lib/expressir/commands/validate_load.rb +88 -0
  88. data/lib/expressir/express/formatter.rb +5 -1
  89. data/lib/expressir/express/formatters/remark_item_formatter.rb +25 -0
  90. data/lib/expressir/express/parser.rb +33 -0
  91. data/lib/expressir/manifest/resolver.rb +213 -0
  92. data/lib/expressir/manifest/validator.rb +195 -0
  93. data/lib/expressir/model/declarations/entity.rb +6 -0
  94. data/lib/expressir/model/dependency_resolver.rb +270 -0
  95. data/lib/expressir/model/indexes/entity_index.rb +103 -0
  96. data/lib/expressir/model/indexes/reference_index.rb +148 -0
  97. data/lib/expressir/model/indexes/type_index.rb +149 -0
  98. data/lib/expressir/model/interface_validator.rb +384 -0
  99. data/lib/expressir/model/repository.rb +400 -5
  100. data/lib/expressir/model/repository_validator.rb +295 -0
  101. data/lib/expressir/model/search_engine.rb +525 -0
  102. data/lib/expressir/model.rb +4 -94
  103. data/lib/expressir/package/builder.rb +200 -0
  104. data/lib/expressir/package/metadata.rb +81 -0
  105. data/lib/expressir/package/reader.rb +165 -0
  106. data/lib/expressir/schema_manifest.rb +11 -1
  107. data/lib/expressir/version.rb +1 -1
  108. data/lib/expressir.rb +16 -3
  109. metadata +115 -5
  110. data/docs/benchmarking.adoc +0 -107
  111. data/docs/liquid_drops.adoc +0 -1547
@@ -0,0 +1,683 @@
1
+ ---
2
+ title: Parsers
3
+ nav_order: 6
4
+ ---
5
+
6
+ == Expressir Parsers
7
+
8
+ === Purpose
9
+
10
+ This page explains how Expressir parses EXPRESS schemas from various formats into its Ruby data model. Understanding the parsing architecture is essential for troubleshooting parsing issues, optimizing performance, and working with different EXPRESS formats.
11
+
12
+ === References
13
+
14
+ * link:express-language.html[EXPRESS Language] - Understanding the source language
15
+ * link:data-model.html[Data Model] - Understanding the target model
16
+ * link:../guides/cli/format-schemas.html[Guide: Format Schemas] - Using the parser via CLI
17
+ * link:../guides/ruby-api/parsing-files.html[Guide: Parsing Files] - Using the parser API
18
+
19
+ === Concepts
20
+
21
+ Parser:: Component that reads text in EXPRESS syntax and produces an Abstract Syntax Tree (AST)
22
+ AST:: Abstract Syntax Tree - intermediate tree representation of parsed code
23
+ Transform:: Conversion of AST into Expressir's Ruby data model
24
+ Reference Resolution:: Process of linking references (by name) to their target definitions
25
+ Visitor:: Design pattern for traversing and transforming the AST
26
+ Cache:: Stored parsed schemas for faster subsequent loads
27
+
28
+ === Parser Architecture
29
+
30
+ Expressir uses a multi-stage parsing pipeline:
31
+
32
+ [source]
33
+ ----
34
+ ┌─────────────────┐
35
+ │ EXPRESS Text │
36
+ │ (.exp file) │
37
+ └────────┬────────┘
38
+
39
+
40
+ ┌──────────┐
41
+ │ Parslet │ (PEG parser)
42
+ │ Grammar │
43
+ └────┬─────┘
44
+
45
+
46
+ ┌────────────────┐
47
+ │ AST (Tree) │ (intermediate)
48
+ └────────┬───────┘
49
+
50
+
51
+ ┌──────────┐
52
+ │ Visitor │ (transform)
53
+ └────┬─────┘
54
+
55
+
56
+ ┌────────────────┐
57
+ │ Data Model │ (Ruby objects)
58
+ │ Repository │
59
+ └────────┬───────┘
60
+
61
+
62
+ ┌────────────────────┐
63
+ │ Reference │
64
+ │ Resolution │
65
+ └────────┬───────────┘
66
+
67
+
68
+ ┌────────────────────┐
69
+ │ Finalized Model │
70
+ │ (ready to use) │
71
+ └────────────────────┘
72
+ ----
73
+
74
+ ==== Stage 1: Lexical Analysis and Parsing
75
+
76
+ Expressir uses **Parslet**, a Parsing Expression Grammar (PEG) parser for Ruby:
77
+
78
+ [source,ruby]
79
+ ----
80
+ # Grammar rules defined in Parser class
81
+ rule(:entityDecl) do
82
+ (entityHead >> entityBody >> tEND_ENTITY >> op_delim).as(:entityDecl)
83
+ end
84
+
85
+ rule(:entityHead) do
86
+ (tENTITY >> entityId >> subsuper >> op_delim).as(:entityHead)
87
+ end
88
+ ----
89
+
90
+ **Parslet advantages**:
91
+
92
+ * **Pure Ruby**: No external dependencies
93
+ * **Composable rules**: Complex grammars from simple parts
94
+ * **Error reporting**: Clear parse failure messages
95
+ * **Type-safe**: Strongly typed AST nodes
96
+
97
+ ==== Stage 2: AST Generation
98
+
99
+ Parsing produces a hierarchical tree structure:
100
+
101
+ [source,ruby]
102
+ ----
103
+ # Example AST for: ENTITY person; name : STRING; END_ENTITY;
104
+ {
105
+ entityDecl: {
106
+ entityHead: {
107
+ entityId: { str: "person" },
108
+ ...
109
+ },
110
+ entityBody: {
111
+ explicitAttr: [
112
+ {
113
+ attributeDecl: { str: "name" },
114
+ parameterType: { str: "STRING" }
115
+ }
116
+ ]
117
+ }
118
+ }
119
+ }
120
+ ----
121
+
122
+ ==== Stage 3: AST Transformation
123
+
124
+ The Visitor pattern transforms AST to data model:
125
+
126
+ [source,ruby]
127
+ ----
128
+ class Visitor
129
+ def visit_entityDecl(node)
130
+ entity = Model::Declarations::Entity.new
131
+ entity.id = node[:entityHead][:entityId]
132
+ entity.attributes = visit_attributes(node[:entityBody])
133
+ entity
134
+ end
135
+ end
136
+ ----
137
+
138
+ **Transformation responsibilities**:
139
+
140
+ * Create appropriate model objects
141
+ * Set attributes and relationships
142
+ * Attach parent links
143
+ * Preserve source text (if requested)
144
+ * Extract documentation (remarks)
145
+
146
+ ==== Stage 4: Reference Resolution
147
+
148
+ Final stage links references to definitions:
149
+
150
+ [source,ruby]
151
+ ----
152
+ # Before resolution
153
+ attribute.type # => SimpleReference(id: "length_measure")
154
+
155
+ # After resolution
156
+ attribute.type.ref # => Type(id: "length_measure")
157
+ ----
158
+
159
+ === Supported Formats
160
+
161
+ Expressir supports multiple EXPRESS formats:
162
+
163
+ ==== EXPRESS Language (ISO 10303-11)
164
+
165
+ Standard textual EXPRESS format:
166
+
167
+ [source,express]
168
+ ----
169
+ SCHEMA geometry_schema;
170
+ ENTITY point;
171
+ x : REAL;
172
+ y : REAL;
173
+ z : REAL;
174
+ END_ENTITY;
175
+ END_SCHEMA;
176
+ ----
177
+
178
+ **File extension**: `.exp`
179
+
180
+ **Characteristics**:
181
+
182
+ * Text-based, human-readable
183
+ * Supports full EXPRESS language
184
+ * Most common format
185
+
186
+ **Usage**:
187
+
188
+ [source,ruby]
189
+ ----
190
+ repo = Expressir::Express::Parser.from_file("geometry.exp")
191
+ ----
192
+
193
+ ==== STEPmod EXPRESS XML
194
+
195
+ XML representation used in STEPmod repository:
196
+
197
+ [source,xml]
198
+ ----
199
+ <express>
200
+ <schema name="geometry_schema">
201
+ <entity name="point">
202
+ <explicit name="x" type="REAL"/>
203
+ <explicit name="y" type="REAL"/>
204
+ <explicit name="z" type="REAL"/>
205
+ </entity>
206
+ </schema>
207
+ </express>
208
+ ----
209
+
210
+ **File extension**: `.xml`
211
+
212
+ **Characteristics**:
213
+
214
+ * XML format
215
+ * Modular schema organization
216
+ * Used in ISO STEP modular repository
217
+
218
+ **Note**: Future support planned
219
+
220
+ ==== EXPRESS XML (ISO 10303-28)
221
+
222
+ Standardized XML representation:
223
+
224
+ **File extension**: `.xml`
225
+
226
+ **Characteristics**:
227
+
228
+ * Follows ISO 10303-28 specification
229
+ * Designed for data exchange
230
+ * Precise mapping to EXPRESS constructs
231
+
232
+ **Note**: Future support planned
233
+
234
+ === Parsing Process
235
+
236
+ ==== Single File Parsing
237
+
238
+ Parse one EXPRESS file:
239
+
240
+ [source,ruby]
241
+ ----
242
+ # Basic parsing
243
+ repository = Expressir::Express::Parser.from_file("schema.exp")
244
+
245
+ # With options
246
+ repository = Expressir::Express::Parser.from_file(
247
+ "schema.exp",
248
+ skip_references: false, # Resolve references (default: false)
249
+ include_source: true, # Attach source text (default: nil)
250
+ root_path: "/base/path" # Base for relative paths (default: nil)
251
+ )
252
+ ----
253
+
254
+ **Process**:
255
+
256
+ 1. Read file content
257
+ 2. Parse to AST
258
+ 3. Transform to model
259
+ 4. Resolve references (unless skipped)
260
+ 5. Return Repository
261
+
262
+ ==== Multiple File Parsing
263
+
264
+ Parse several files into one repository:
265
+
266
+ [source,ruby]
267
+ ----
268
+ files = ["schema1.exp", "schema2.exp", "schema3.exp"]
269
+
270
+ # With progress tracking
271
+ repository = Expressir::Express::Parser.from_files(files) do |filename, schemas, error|
272
+ if error
273
+ puts "Error parsing #{filename}: #{error.message}"
274
+ else
275
+ puts "Loaded #{schemas.length} schemas from #{filename}"
276
+ end
277
+ end
278
+ ----
279
+
280
+ **Process**:
281
+
282
+ 1. Parse each file individually
283
+ 2. Collect all schemas
284
+ 3. Create unified Repository
285
+ 4. Resolve cross-file references
286
+ 5. Return complete Repository
287
+
288
+ ==== String Parsing
289
+
290
+ Parse EXPRESS from string:
291
+
292
+ [source,ruby]
293
+ ----
294
+ express_code = <<~EXPRESS
295
+ SCHEMA example;
296
+ ENTITY person;
297
+ name : STRING;
298
+ END_ENTITY;
299
+ END_SCHEMA;
300
+ EXPRESS
301
+
302
+ repository = Expressir::Express::Parser.from_exp(express_code)
303
+ ----
304
+
305
+ **Use cases**:
306
+
307
+ * Testing
308
+ * Dynamic schema generation
309
+ * Template processing
310
+ * Schema fragments
311
+
312
+ === Reference Resolution
313
+
314
+ ==== What is Reference Resolution?
315
+
316
+ EXPRESS uses names to reference other elements:
317
+
318
+ [source,express]
319
+ ----
320
+ TYPE length_measure = REAL;
321
+ END_TYPE;
322
+
323
+ ENTITY line;
324
+ length : length_measure; -- Reference to type above
325
+ END_ENTITY;
326
+ ----
327
+
328
+ After parsing, `length_measure` is just a string. Reference resolution finds the actual `Type` object.
329
+
330
+ ==== Resolution Process
331
+
332
+ [source,ruby]
333
+ ----
334
+ # Automatic resolution (default)
335
+ repo = Expressir::Express::Parser.from_file("schema.exp")
336
+ # References already resolved
337
+
338
+ # Manual resolution
339
+ repo = Expressir::Express::Parser.from_file("schema.exp", skip_references: true)
340
+ # References not yet resolved
341
+ repo.resolve_all_references
342
+ # Now resolved
343
+ ----
344
+
345
+ ==== Interface Resolution
346
+
347
+ USE FROM and REFERENCE FROM create cross-schema references:
348
+
349
+ [source,express]
350
+ ----
351
+ SCHEMA application_schema;
352
+ USE FROM geometry_schema; -- Import all
353
+ REFERENCE FROM support_schema (date); -- Import specific
354
+
355
+ ENTITY geometric_model;
356
+ base : point; -- From geometry_schema
357
+ created : date; -- From support_schema
358
+ END_ENTITY;
359
+ END_SCHEMA;
360
+ ----
361
+
362
+ Resolution finds `point` in `geometry_schema` and `date` in `support_schema`.
363
+
364
+ ==== Resolution Scope
365
+
366
+ Resolution searches in order:
367
+
368
+ 1. **Current entity/function** (local scope)
369
+ 2. **Current schema** (schema-level declarations)
370
+ 3. **Interfaced schemas** (USE FROM / REFERENCE FROM)
371
+ 4. **Parent scopes** (for nested contexts)
372
+
373
+ ==== Unresolved References
374
+
375
+ If a reference cannot be resolved:
376
+
377
+ [source,ruby]
378
+ ----
379
+ attribute.type.ref # => nil (not found)
380
+ ----
381
+
382
+ This typically indicates:
383
+
384
+ * Typo in reference name
385
+ * Missing interface declaration
386
+ * Missing schema in repository
387
+ * Incorrect schema order
388
+
389
+ === Error Handling
390
+
391
+ ==== Parse Failures
392
+
393
+ When parsing fails, Expressir raises detailed errors:
394
+
395
+ [source,ruby]
396
+ ----
397
+ begin
398
+ repo = Expressir::Express::Parser.from_file("invalid.exp")
399
+ rescue Expressir::Express::Error::SchemaParseFailure => e
400
+ puts "Failed to parse: #{e.filename}"
401
+ puts e.message
402
+ puts e.parse_failure_cause.ascii_tree # Detailed error location
403
+ end
404
+ ----
405
+
406
+ **Error information includes**:
407
+
408
+ * File name
409
+ * Line and column numbers
410
+ * Expected tokens
411
+ * Actual tokens found
412
+ * Parse tree context
413
+
414
+ ==== Common Parse Errors
415
+
416
+ **Missing semicolon**:
417
+
418
+ [source]
419
+ ----
420
+ Expected ';' at line 10, column 5
421
+ ----
422
+
423
+ **Invalid keyword**:
424
+
425
+ [source]
426
+ ----
427
+ Unexpected keyword 'FOO' at line 15, column 3
428
+ ----
429
+
430
+ **Mismatched END statement**:
431
+
432
+ [source]
433
+ ----
434
+ Expected 'END_ENTITY' but found 'END_TYPE' at line 20
435
+ ----
436
+
437
+ **Invalid identifier**:
438
+
439
+ [source]
440
+ ----
441
+ Expected identifier at line 8, column 12
442
+ ----
443
+
444
+ ==== Recovery Strategies
445
+
446
+ **Skip broken file**:
447
+
448
+ [source,ruby]
449
+ ----
450
+ files.each do |file|
451
+ begin
452
+ repo = Expressir::Express::Parser.from_file(file)
453
+ process(repo)
454
+ rescue Expressir::Express::Error::SchemaParseFailure => e
455
+ warn "Skipping #{file}: #{e.message}"
456
+ next
457
+ end
458
+ end
459
+ ----
460
+
461
+ **Continue parsing remaining files**:
462
+
463
+ [source,ruby]
464
+ ----
465
+ Expressir::Express::Parser.from_files(files) do |filename, schemas, error|
466
+ if error
467
+ warn "Failed: #{filename}"
468
+ else
469
+ # Process successful schemas
470
+ end
471
+ end
472
+ ----
473
+
474
+ === Performance Considerations
475
+
476
+ ==== Benchmarking
477
+
478
+ Measure parsing performance:
479
+
480
+ [source,ruby]
481
+ ----
482
+ require 'benchmark'
483
+
484
+ time = Benchmark.realtime do
485
+ repo = Expressir::Express::Parser.from_file("large_schema.exp")
486
+ end
487
+
488
+ puts "Parsed in #{time.round(2)} seconds"
489
+ ----
490
+
491
+ ==== Optimization Techniques
492
+
493
+ **Skip reference resolution for analysis**:
494
+
495
+ [source,ruby]
496
+ ----
497
+ # Faster if you don't need resolved references
498
+ repo = Expressir::Express::Parser.from_file("schema.exp", skip_references: true)
499
+ ----
500
+
501
+ **Omit source text**:
502
+
503
+ [source,ruby]
504
+ ----
505
+ # Reduces memory usage
506
+ repo = Expressir::Express::Parser.from_file("schema.exp", include_source: false)
507
+ ----
508
+
509
+ **Parse in parallel** (for multiple files):
510
+
511
+ [source,ruby]
512
+ ----
513
+ require 'parallel'
514
+
515
+ repos = Parallel.map(files) do |file|
516
+ Expressir::Express::Parser.from_file(file, skip_references: true)
517
+ end
518
+
519
+ # Combine and resolve references once
520
+ combined = combine_repositories(repos)
521
+ combined.resolve_all_references
522
+ ----
523
+
524
+ ==== Caching
525
+
526
+ Use caching for repeated parses:
527
+
528
+ [source,ruby]
529
+ ----
530
+ # Expressir has built-in cache support
531
+ Expressir::Express::Cache.enable
532
+
533
+ # First parse: slow
534
+ repo1 = Expressir::Express::Parser.from_file("schema.exp")
535
+
536
+ # Second parse: fast (from cache)
537
+ repo2 = Expressir::Express::Parser.from_file("schema.exp")
538
+ ----
539
+
540
+ See link:../guides/cli/benchmark-performance.html[Benchmark Performance] guide for details.
541
+
542
+ === Advanced Topics
543
+
544
+ ==== Custom Visitors
545
+
546
+ Extend parsing with custom transformations:
547
+
548
+ [source,ruby]
549
+ ----
550
+ class MyVisitor < Expressir::Express::Visitor
551
+ def visit_entity(node)
552
+ entity = super
553
+ # Custom processing
554
+ entity.custom_flag = true
555
+ entity
556
+ end
557
+ end
558
+ ----
559
+
560
+ ==== Incremental Parsing
561
+
562
+ Parse schemas on demand:
563
+
564
+ [source,ruby]
565
+ ----
566
+ # Parse schema headers only
567
+ repos = files.map do |file|
568
+ Expressir::Express::Parser.from_file(file, skip_references: true)
569
+ end
570
+
571
+ # Parse individual schemas fully as needed
572
+ selected_repo = repos.find { |r| r.schemas.first.id == "target_schema" }
573
+ selected_repo.resolve_all_references
574
+ ----
575
+
576
+ ==== Grammar Extension
577
+
578
+ Expressir's grammar can be extended for custom syntax:
579
+
580
+ [source,ruby]
581
+ ----
582
+ class CustomParser < Expressir::Express::Parser::Parser
583
+ rule(:custom_construct) do
584
+ # Custom grammar rules
585
+ end
586
+ end
587
+ ----
588
+
589
+ === Parsing Best Practices
590
+
591
+ **Always handle errors**::
592
+ Use begin/rescue blocks to handle parse failures gracefully
593
+
594
+ **Validate before parsing**::
595
+ Check file existence and readability first
596
+
597
+ **Use progress callbacks**::
598
+ For multiple files, track progress with callbacks
599
+
600
+ **Skip references when possible**::
601
+ If you don't need resolved references, skip for speed
602
+
603
+ **Cache for production**::
604
+ Enable caching for applications that parse repeatedly
605
+
606
+ **Profile large schemas**::
607
+ Use benchmarking to identify bottlenecks
608
+
609
+ **Process incrementally**::
610
+ For very large sets, parse and process one at a time
611
+
612
+ === Troubleshooting
613
+
614
+ ==== Parser Hangs
615
+
616
+ **Symptom**: Parser doesn't complete
617
+
618
+ **Causes**:
619
+
620
+ * Malformed file with infinite recursion
621
+ * Very large schema
622
+ * Memory exhaustion
623
+
624
+ **Solutions**:
625
+
626
+ * Validate file structure first
627
+ * Parse smaller chunks
628
+ * Increase memory limits
629
+
630
+ ==== Reference Resolution Fails
631
+
632
+ **Symptom**: Many unresolved references
633
+
634
+ **Causes**:
635
+
636
+ * Missing interface declarations
637
+ * Incorrect schema order
638
+ * Typos in names
639
+
640
+ **Solutions**:
641
+
642
+ * Check USE FROM / REFERENCE FROM
643
+ * Parse schemas in dependency order
644
+ * Validate names match
645
+
646
+ ==== Memory Issues
647
+
648
+ **Symptom**: Out of memory errors
649
+
650
+ **Causes**:
651
+
652
+ * Very large schemas
653
+ * Including source text
654
+ * Parsing many files at once
655
+
656
+ **Solutions**:
657
+
658
+ * Parse incrementally
659
+ * Skip source text inclusion
660
+ * Use streaming approaches
661
+
662
+ === Next Steps
663
+
664
+ Now that you understand parsing:
665
+
666
+ **Try parsing**::
667
+ link:../tutorials/parsing-your-first-schema.html[Parse your first schema]
668
+
669
+ **Learn the CLI**::
670
+ link:../guides/cli/format-schemas.html[Format schemas with CLI]
671
+
672
+ **Master the API**::
673
+ link:../guides/ruby-api/parsing-files.html[Parse files programmatically]
674
+
675
+ **Optimize performance**::
676
+ link:../guides/cli/benchmark-performance.html[Benchmark and optimize]
677
+
678
+ === Bibliography
679
+
680
+ * https://github.com/kschiess/parslet[Parslet] - PEG parser for Ruby
681
+ * https://en.wikipedia.org/wiki/Parsing_expression_grammar[PEG on Wikipedia] - Understanding PEG parsers
682
+ * link:express-language.html[EXPRESS Language] - Understanding what is being parsed
683
+ * link:data-model.html[Data Model] - Understanding the parsing result