expressir 2.4.0 → 2.4.1
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.
- checksums.yaml +4 -4
- data/TODO.bugs/01-stale-transformer-autoload.md +39 -0
- data/TODO.bugs/02-parser-class-instance-vars.md +36 -0
- data/TODO.bugs/03-builder-mutable-state.md +43 -0
- data/TODO.bugs/04-formatter-public-send-dispatch.md +53 -0
- data/TODO.bugs/05-anonymous-formatter-subclass.md +45 -0
- data/TODO.bugs/06-collection-registry-single-source.md +53 -0
- data/TODO.bugs/07-require-relative-cleanup.md +42 -0
- data/TODO.bugs/08-require-expressir-in-commands.md +34 -0
- data/TODO.bugs/09-parser-split.md +53 -0
- data/TODO.bugs/10-to-s-override.md +42 -0
- data/TODO.bugs/11-parser-class-variables.md +39 -0
- data/TODO.bugs/12-marker-modules-vs-registry.md +64 -0
- data/TODO.bugs/13-string-literal-scanner-limitation.md +52 -0
- data/TODO.bugs/14-model-formatting-leak.md +30 -0
- data/TODO.bugs/15-expression-children-macro.md +27 -0
- data/TODO.bugs/16-pretty-formatter-duplication.md +28 -0
- data/TODO.bugs/17-snake-case-cache-mutable-constant.md +28 -0
- data/TODO.bugs/18-const-get-private-constants.md +30 -0
- data/TODO.bugs/19-format-methods-public.md +22 -0
- data/TODO.bugs/20-coverage-nested-entities-dedup.md +20 -0
- data/TODO.bugs/21-operator-tokens-secondary-dispatch.md +21 -0
- data/TODO.bugs/22-builder-fast-path-wrappers.md +32 -0
- data/TODO.bugs/23-coverage-inverse-maps.md +21 -0
- data/TODO.bugs/24-streaming-builder-complexity.md +19 -0
- data/TODO.bugs/25-debug-puts-in-production.md +21 -0
- data/TODO.bugs/26-generic-entity-children-misplaced.md +21 -0
- data/TODO.bugs/27-package-build-god-method.md +19 -0
- data/TODO.bugs/28-package-god-class.md +30 -0
- data/TODO.bugs/29-validate-ascii-god-class.md +24 -0
- data/TODO.bugs/30-unicode-map-extraction.md +19 -0
- data/TODO.bugs/README.md +43 -0
- data/docs/_tutorials/formatting-schemas.adoc +89 -0
- data/docs/_tutorials/index.adoc +10 -0
- data/docs/lychee.toml +3 -0
- data/expressir.gemspec +4 -2
- data/lib/expressir/commands/changes_import_eengine.rb +0 -6
- data/lib/expressir/commands/changes_validate.rb +0 -2
- data/lib/expressir/commands/clean.rb +1 -1
- data/lib/expressir/commands/file_violations.rb +70 -0
- data/lib/expressir/commands/format.rb +1 -1
- data/lib/expressir/commands/non_ascii_character.rb +49 -0
- data/lib/expressir/commands/non_ascii_violation_collection.rb +301 -0
- data/lib/expressir/commands/package.rb +138 -185
- data/lib/expressir/commands/validate_ascii.rb +0 -510
- data/lib/expressir/commands/version.rb +1 -1
- data/lib/expressir/commands.rb +3 -0
- data/lib/expressir/coverage.rb +49 -117
- data/lib/expressir/express/builder.rb +74 -86
- data/lib/expressir/express/builder_context.rb +22 -0
- data/lib/expressir/express/builders/expression_builder.rb +16 -16
- data/lib/expressir/express/cache.rb +35 -8
- data/lib/expressir/express/error.rb +7 -0
- data/lib/expressir/express/formatter.rb +33 -18
- data/lib/expressir/express/formatters/declarations_formatter.rb +24 -2
- data/lib/expressir/express/formatters/remark_formatter.rb +75 -2
- data/lib/expressir/express/formatters/statements_formatter.rb +20 -3
- data/lib/expressir/express/grammar/parser.rb +705 -0
- data/lib/expressir/express/grammar.rb +11 -0
- data/lib/expressir/express/node_position_index.rb +215 -0
- data/lib/expressir/express/parser.rb +114 -884
- data/lib/expressir/express/pretty_formatter.rb +23 -5
- data/lib/expressir/express/remark_attacher.rb +475 -577
- data/lib/expressir/express/remark_scanner.rb +84 -19
- data/lib/expressir/express/schema_block_scanner.rb +136 -0
- data/lib/expressir/express/schema_source_formatter.rb +15 -0
- data/lib/expressir/express/scope_resolver.rb +194 -0
- data/lib/expressir/express/source_formatter.rb +15 -0
- data/lib/expressir/express/streaming_builder.rb +147 -176
- data/lib/expressir/express.rb +8 -6
- data/lib/expressir/model/concerns.rb +3 -0
- data/lib/expressir/model/data_types/generic_entity.rb +6 -6
- data/lib/expressir/model/declarations/entity.rb +5 -0
- data/lib/expressir/model/declarations/function.rb +5 -0
- data/lib/expressir/model/declarations/procedure.rb +5 -0
- data/lib/expressir/model/declarations/rule.rb +6 -0
- data/lib/expressir/model/declarations/schema.rb +21 -8
- data/lib/expressir/model/declarations/type.rb +3 -0
- data/lib/expressir/model/exp_file.rb +2 -0
- data/lib/expressir/model/expressions/aggregate_initializer.rb +1 -0
- data/lib/expressir/model/expressions/aggregate_initializer_item.rb +1 -0
- data/lib/expressir/model/expressions/binary_expression.rb +1 -0
- data/lib/expressir/model/expressions/entity_constructor.rb +1 -0
- data/lib/expressir/model/expressions/function_call.rb +1 -0
- data/lib/expressir/model/expressions/interval.rb +1 -0
- data/lib/expressir/model/expressions/query_expression.rb +2 -0
- data/lib/expressir/model/expressions/unary_expression.rb +1 -0
- data/lib/expressir/model/model_element.rb +128 -14
- data/lib/expressir/model/remark_format.rb +17 -0
- data/lib/expressir/model/remark_info.rb +31 -3
- data/lib/expressir/model/remark_placement.rb +29 -0
- data/lib/expressir/model/statements/alias.rb +2 -0
- data/lib/expressir/model/statements/assignment.rb +4 -0
- data/lib/expressir/model/statements/case.rb +21 -0
- data/lib/expressir/model/statements/case_action.rb +1 -0
- data/lib/expressir/model/statements/compound.rb +4 -0
- data/lib/expressir/model/statements/escape.rb +3 -0
- data/lib/expressir/model/statements/if.rb +5 -0
- data/lib/expressir/model/statements/null.rb +3 -0
- data/lib/expressir/model/statements/procedure_call.rb +3 -0
- data/lib/expressir/model/statements/repeat.rb +3 -0
- data/lib/expressir/model/statements/return.rb +3 -0
- data/lib/expressir/model/statements/skip.rb +3 -0
- data/lib/expressir/model.rb +12 -3
- data/lib/expressir/package/builder.rb +2 -2
- data/lib/expressir/version.rb +6 -1
- data/lib/expressir.rb +38 -3
- metadata +63 -5
- data/lib/expressir/express/transformer/remark_handling.rb +0 -194
|
@@ -1,755 +1,103 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require "parsanol"
|
|
2
4
|
|
|
3
5
|
module Expressir
|
|
4
6
|
module Express
|
|
7
|
+
# Public-facing parser facade.
|
|
8
|
+
#
|
|
9
|
+
# Three concerns were split out (TODO.bugs/09):
|
|
10
|
+
# - Grammar rules → {Grammar::Parser} (~250 Parsanol rules)
|
|
11
|
+
# - Schema-block splitting → {SchemaBlockScanner}
|
|
12
|
+
# - I/O orchestration → this class (from_file, from_exp, etc.)
|
|
13
|
+
#
|
|
14
|
+
# The facade delegates grammar work to Grammar::Parser and block
|
|
15
|
+
# extraction to SchemaBlockScanner. All public methods remain
|
|
16
|
+
# backward-compatible.
|
|
5
17
|
class Parser
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
@@cached_schema_grammar_json = nil
|
|
19
|
-
|
|
20
|
-
# Threshold for using memory-bounded fresh parse (bytes)
|
|
21
|
-
# Files above this use parse_fresh which has no packrat cache
|
|
22
|
-
LARGE_FILE_THRESHOLD = 1024 * 1024 # 1 MB
|
|
23
|
-
|
|
24
|
-
# Get cached parser instance (thread-safe)
|
|
25
|
-
# Reusing the parser avoids the overhead of reinitializing all rule definitions
|
|
26
|
-
def self.cached_parser
|
|
27
|
-
return @@cached_parser if @@cached_parser
|
|
28
|
-
|
|
29
|
-
@@parser_mutex.synchronize do
|
|
30
|
-
@@cached_parser ||= new
|
|
31
|
-
end
|
|
32
|
-
end
|
|
33
|
-
|
|
34
|
-
# Clear the cached parser (useful for testing or after grammar changes)
|
|
35
|
-
def self.clear_parser_cache
|
|
36
|
-
@@parser_mutex.synchronize do
|
|
37
|
-
@@cached_parser = nil
|
|
38
|
-
end
|
|
39
|
-
end
|
|
40
|
-
|
|
41
|
-
# Check if native parsing is available
|
|
42
|
-
def self.native_available?
|
|
43
|
-
return @native_available unless @native_available.nil?
|
|
44
|
-
|
|
45
|
-
@native_available = begin
|
|
46
|
-
require "parsanol/native"
|
|
47
|
-
Parsanol::Native.available?
|
|
48
|
-
rescue LoadError
|
|
49
|
-
false
|
|
50
|
-
end
|
|
51
|
-
end
|
|
52
|
-
|
|
53
|
-
# Get cached grammar JSON for native parsing
|
|
54
|
-
def self.cached_grammar_json
|
|
55
|
-
return @@cached_grammar_json if @@cached_grammar_json
|
|
56
|
-
|
|
57
|
-
parser = new
|
|
58
|
-
atom = parser.syntax
|
|
59
|
-
|
|
60
|
-
# Cache by atom object_id
|
|
61
|
-
if atom.object_id != @@cached_grammar_atom_id
|
|
62
|
-
@@cached_grammar_atom_id = atom.object_id
|
|
63
|
-
@@cached_grammar_json = Parsanol::Native.serialize_grammar(atom)
|
|
64
|
-
end
|
|
65
|
-
|
|
66
|
-
@@cached_grammar_json
|
|
67
|
-
end
|
|
68
|
-
|
|
69
|
-
# Get cached grammar JSON for schemaDecl (used for streaming parse)
|
|
70
|
-
def self.cached_schema_grammar_json
|
|
71
|
-
return @@cached_schema_grammar_json if @@cached_schema_grammar_json
|
|
72
|
-
|
|
73
|
-
schema_atom = cached_parser.schemaDecl
|
|
74
|
-
@@cached_schema_grammar_json = Parsanol::Native.serialize_grammar(schema_atom)
|
|
75
|
-
@@cached_schema_grammar_json
|
|
76
|
-
end
|
|
77
|
-
|
|
78
|
-
# Parse using native engine with Rust-side transformation (fastest)
|
|
79
|
-
#
|
|
80
|
-
# This method provides ~17x speedup over pure Ruby parsing.
|
|
81
|
-
# The transformation happens in Rust using to_parslet_compatible,
|
|
82
|
-
# producing Parslet-compatible output that Builder.build can consume directly.
|
|
83
|
-
#
|
|
84
|
-
# @param source [String] EXPRESS source code to parse
|
|
85
|
-
# @return [Hash, Array] Transformed AST in Parslet-compatible format
|
|
86
|
-
# @raise [LoadError] If native parser is not available
|
|
87
|
-
def self.parse_native(source)
|
|
88
|
-
unless native_available?
|
|
89
|
-
raise LoadError, "Native parser not available"
|
|
90
|
-
end
|
|
91
|
-
|
|
92
|
-
grammar_atom = cached_parser.syntax
|
|
93
|
-
# Use fresh-parse (no cache) for large files to bound memory
|
|
94
|
-
if source.bytesize > LARGE_FILE_THRESHOLD
|
|
95
|
-
Parsanol::Native.parse_fresh(grammar_atom, source)
|
|
96
|
-
else
|
|
97
|
-
Parsanol::Native.parse(grammar_atom, source)
|
|
98
|
-
end
|
|
99
|
-
end
|
|
100
|
-
|
|
101
|
-
def cts(atom)
|
|
102
|
-
spaces >> atom
|
|
103
|
-
end
|
|
104
|
-
|
|
105
|
-
def cstr(atom)
|
|
106
|
-
cts(str(atom).as(:str))
|
|
107
|
-
end
|
|
108
|
-
|
|
109
|
-
KEYWORDS = %i[
|
|
110
|
-
ABS ABSTRACT ACOS AGGREGATE ALIAS AND ANDOR ARRAY AS ASIN ATAN BAG BASED_ON
|
|
111
|
-
BEGIN BINARY BLENGTH BOOLEAN BY CASE CONSTANT CONST_E COS DERIVE DIV ELSE
|
|
112
|
-
END END_ALIAS END_CASE END_CONSTANT END_ENTITY END_FUNCTION END_IF
|
|
113
|
-
END_LOCAL END_PROCEDURE END_REPEAT END_RULE END_SCHEMA
|
|
114
|
-
END_SUBTYPE_CONSTRAINT END_TYPE ENTITY ENUMERATION ESCAPE EXISTS EXP
|
|
115
|
-
EXTENSIBLE FALSE FIXED FOR FORMAT FROM FUNCTION GENERIC GENERIC_ENTITY
|
|
116
|
-
HIBOUND HIINDEX IF IN INSERT INTEGER INVERSE LENGTH LIKE LIST LOBOUND LOCAL
|
|
117
|
-
LOG LOG10 LOG2 LOGICAL LOINDEX MOD NOT NUMBER NVL ODD OF ONEOF OPTIONAL OR
|
|
118
|
-
OTHERWISE PI PROCEDURE QUERY REAL REFERENCE REMOVE RENAMED REPEAT RETURN
|
|
119
|
-
ROLESOF RULE SCHEMA SELECT SELF SET SIN SIZEOF SKIP SQRT STRING SUBTYPE
|
|
120
|
-
SUBTYPE_CONSTRAINT SUPERTYPE TAN THEN TO TRUE TYPE TYPEOF TOTAL_OVER UNIQUE
|
|
121
|
-
UNKNOWN UNTIL USE USEDIN VALUE VALUE_IN VALUE_UNIQUE VAR WITH WHERE WHILE
|
|
122
|
-
XOR
|
|
123
|
-
].freeze
|
|
18
|
+
# Threshold for using memory-bounded fresh parse (bytes)
|
|
19
|
+
LARGE_FILE_THRESHOLD = 1024 * 1024 # 1 MB
|
|
20
|
+
|
|
21
|
+
class << self
|
|
22
|
+
# Delegate grammar-level queries to Grammar::Parser.
|
|
23
|
+
def native_available? = Grammar::Parser.native_available?
|
|
24
|
+
def cached_parser = Grammar::Parser.cached_parser
|
|
25
|
+
def cached_grammar_json = Grammar::Parser.cached_grammar_json
|
|
26
|
+
def cached_schema_grammar_json = Grammar::Parser.cached_schema_grammar_json
|
|
27
|
+
def parse_native(source) = Grammar::Parser.parse_native(source)
|
|
28
|
+
def clear_parser_cache = Grammar::Parser.clear_parser_cache
|
|
29
|
+
end
|
|
124
30
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
31
|
+
# UTF-8 BOM byte sequence (EF BB BF). Some editors prepend this
|
|
32
|
+
# to EXPRESS files; the parser fails because SCHEMA isn't at byte 0.
|
|
33
|
+
def self.strip_bom(source)
|
|
34
|
+
bytes = source.b
|
|
35
|
+
return source unless bytes.bytesize >= 3 &&
|
|
36
|
+
bytes.getbyte(0) == 0xEF &&
|
|
37
|
+
bytes.getbyte(1) == 0xBB &&
|
|
38
|
+
bytes.getbyte(2) == 0xBF
|
|
131
39
|
|
|
132
|
-
|
|
133
|
-
sym = :"t#{keyword}"
|
|
134
|
-
rule(sym) { cts(keyword_rule(keyword).as(:str)).as(sym) }
|
|
135
|
-
end
|
|
136
|
-
|
|
137
|
-
rule(:abstractEntityDeclaration) do
|
|
138
|
-
tABSTRACT.as(:abstractEntityDeclaration)
|
|
139
|
-
end
|
|
140
|
-
rule(:abstractSupertypeDeclaration) do
|
|
141
|
-
(tABSTRACT >> tSUPERTYPE >> subtypeConstraint.maybe).as(:abstractSupertypeDeclaration)
|
|
142
|
-
end
|
|
143
|
-
rule(:abstractSupertype) do
|
|
144
|
-
(tABSTRACT >> tSUPERTYPE >> op_delim).as(:abstractSupertype)
|
|
145
|
-
end
|
|
146
|
-
rule(:actualParameterList) do
|
|
147
|
-
(op_leftparen >> (parameter >> (op_comma >> parameter).repeat).as(:listOf_parameter).maybe >> op_rightparen)
|
|
148
|
-
.as(:actualParameterList)
|
|
149
|
-
end
|
|
150
|
-
rule(:addLikeOp) { (op_plus | op_minus | tOR | tXOR).as(:addLikeOp) }
|
|
151
|
-
rule(:aggregateInitializer) do
|
|
152
|
-
(op_leftbracket >> (element >> (op_comma >> element).repeat).as(:listOf_element).maybe >> op_rightbracket)
|
|
153
|
-
.as(:aggregateInitializer)
|
|
154
|
-
end
|
|
155
|
-
rule(:aggregateSource) { simpleExpression.as(:aggregateSource) }
|
|
156
|
-
rule(:aggregateType) do
|
|
157
|
-
(tAGGREGATE >> (op_colon >> typeLabel).maybe >> tOF >> parameterType).as(:aggregateType)
|
|
158
|
-
end
|
|
159
|
-
rule(:aggregationTypes) do
|
|
160
|
-
(arrayType | bagType | listType | setType).as(:aggregationTypes)
|
|
161
|
-
end
|
|
162
|
-
rule(:algorithmHead) do
|
|
163
|
-
(declaration.repeat.as(:declaration) >> constantDecl.maybe >> localDecl.maybe).as(:algorithmHead)
|
|
164
|
-
end
|
|
165
|
-
rule(:aliasStmt) do
|
|
166
|
-
(tALIAS >> variableId >> tFOR >> generalRef >> qualifier.repeat.as(:qualifier) >>
|
|
167
|
-
op_delim >> stmt.repeat(1).as(:stmt) >> tEND_ALIAS >> op_delim.as(:op_delim2)).as(:aliasStmt)
|
|
168
|
-
end
|
|
169
|
-
rule(:anyKeyword) { KEYWORDS.map { |kw| send("t#{kw}") }.inject(:|) }
|
|
170
|
-
rule(:arrayType) do
|
|
171
|
-
(tARRAY >> boundSpec >> tOF >> tOPTIONAL.maybe >> tUNIQUE.maybe >> instantiableType).as(:arrayType)
|
|
172
|
-
end
|
|
173
|
-
rule(:assignmentStmt) do
|
|
174
|
-
(generalRef >> qualifier.repeat.as(:qualifier) >> op_decl >> expression >> op_delim).as(:assignmentStmt)
|
|
175
|
-
end
|
|
176
|
-
rule(:attributeDecl) do
|
|
177
|
-
(attributeId | redeclaredAttribute).as(:attributeDecl)
|
|
178
|
-
end
|
|
179
|
-
rule(:attributeId) { simpleId.as(:attributeId) }
|
|
180
|
-
rule(:attributeQualifier) do
|
|
181
|
-
(op_period >> attributeRef).as(:attributeQualifier)
|
|
182
|
-
end
|
|
183
|
-
rule(:attributeRef) { attributeId.as(:attributeRef) }
|
|
184
|
-
rule(:bagType) do
|
|
185
|
-
(tBAG >> boundSpec.maybe >> tOF >> instantiableType).as(:bagType)
|
|
186
|
-
end
|
|
187
|
-
rule(:binaryLiteral) do
|
|
188
|
-
cts((str("%") >> bit.repeat(1)).as(:str)).as(:binaryLiteral)
|
|
189
|
-
end
|
|
190
|
-
rule(:binaryType) { (tBINARY >> widthSpec.maybe).as(:binaryType) }
|
|
191
|
-
rule(:bit) { match["0-1"] }
|
|
192
|
-
rule(:booleanType) { tBOOLEAN.as(:booleanType) }
|
|
193
|
-
rule(:bound1) { numericExpression.as(:bound1) }
|
|
194
|
-
rule(:bound2) { numericExpression.as(:bound2) }
|
|
195
|
-
rule(:boundSpec) do
|
|
196
|
-
(op_leftbracket >> bound1 >> op_colon >> bound2 >> op_rightbracket).as(:boundSpec)
|
|
197
|
-
end
|
|
198
|
-
rule(:builtInConstant) do
|
|
199
|
-
(tCONST_E | tPI | tSELF | op_question_mark).as(:builtInConstant)
|
|
200
|
-
end
|
|
201
|
-
rule(:builtInFunction) do
|
|
202
|
-
(tABS | tACOS | tASIN | tATAN | tBLENGTH | tCOS | tEXISTS | tEXP | tFORMAT | tHIBOUND | tHIINDEX | tLENGTH |
|
|
203
|
-
tLOBOUND | tLOINDEX | tLOG2 | tLOG10 | tLOG | tNVL | tODD | tROLESOF | tSIN | tSIZEOF | tSQRT | tTAN |
|
|
204
|
-
tTYPEOF | tUSEDIN | tVALUE_IN | tVALUE_UNIQUE | tVALUE).as(:builtInFunction)
|
|
205
|
-
end
|
|
206
|
-
rule(:builtInProcedure) { (tINSERT | tREMOVE).as(:builtInProcedure) }
|
|
207
|
-
rule(:caseAction) do
|
|
208
|
-
((caseLabel >> (op_comma >> caseLabel).repeat).as(:listOf_caseLabel) >> op_colon >> stmt).as(:caseAction)
|
|
209
|
-
end
|
|
210
|
-
rule(:caseLabel) { expression.as(:caseLabel) }
|
|
211
|
-
rule(:caseStmt) do
|
|
212
|
-
(tCASE >> selector >> tOF >> caseAction.repeat.as(:caseAction) >> (tOTHERWISE >> op_colon >> stmt).maybe >> tEND_CASE >> op_delim).as(:caseStmt)
|
|
213
|
-
end
|
|
214
|
-
rule(:compoundStmt) do
|
|
215
|
-
(tBEGIN >> stmt.repeat(1).as(:stmt) >> tEND >> op_delim).as(:compoundStmt)
|
|
216
|
-
end
|
|
217
|
-
rule(:concreteTypes) do
|
|
218
|
-
(aggregationTypes | simpleTypes | typeRef).as(:concreteTypes)
|
|
219
|
-
end
|
|
220
|
-
rule(:constantBody) do
|
|
221
|
-
(constantId >> op_colon >> instantiableType >> op_decl >> expression >> op_delim).as(:constantBody)
|
|
222
|
-
end
|
|
223
|
-
rule(:constantDecl) do
|
|
224
|
-
(tCONSTANT >> constantBody.repeat(1).as(:constantBody) >> tEND_CONSTANT >> op_delim).as(:constantDecl)
|
|
225
|
-
end
|
|
226
|
-
rule(:constantFactor) do
|
|
227
|
-
(builtInConstant | constantRef).as(:constantFactor)
|
|
228
|
-
end
|
|
229
|
-
rule(:constantId) { simpleId.as(:constantId) }
|
|
230
|
-
rule(:constantRef) { constantId.as(:constantRef) }
|
|
231
|
-
rule(:constructedTypes) do
|
|
232
|
-
(enumerationType | selectType).as(:constructedTypes)
|
|
233
|
-
end
|
|
234
|
-
rule(:declaration) do
|
|
235
|
-
(entityDecl | functionDecl | procedureDecl | subtypeConstraintDecl | typeDecl).as(:declaration)
|
|
236
|
-
end
|
|
237
|
-
rule(:delim) { cts(str(";").as(:delim)) }
|
|
238
|
-
rule(:deriveClause) do
|
|
239
|
-
(tDERIVE >> derivedAttr.repeat(1).as(:derivedAttr)).as(:deriveClause)
|
|
240
|
-
end
|
|
241
|
-
rule(:derivedAttr) do
|
|
242
|
-
(attributeDecl >> op_colon >> parameterType >> op_decl >> expression >> op_delim).as(:derivedAttr)
|
|
243
|
-
end
|
|
244
|
-
rule(:digit) { match["0-9"] }
|
|
245
|
-
rule(:digits) { digit >> digit.repeat }
|
|
246
|
-
rule(:domainRule) do
|
|
247
|
-
((ruleLabelId >> op_colon).maybe >> expression).as(:domainRule)
|
|
248
|
-
end
|
|
249
|
-
rule(:element) do
|
|
250
|
-
(expression >> (op_colon >> repetition).maybe).as(:element)
|
|
251
|
-
end
|
|
252
|
-
rule(:embeddedRemark) do
|
|
253
|
-
(str("(*") >> (str("*)").absent? >> (embeddedRemark | any)).repeat >> str("*)")).as(:embeddedRemark)
|
|
254
|
-
end
|
|
255
|
-
rule(:encodedCharacter) { octet >> octet >> octet >> octet }
|
|
256
|
-
rule(:encodedStringLiteral) do
|
|
257
|
-
cts((str('"') >> encodedCharacter.repeat(1) >> str('"')).as(:str)).as(:encodedStringLiteral)
|
|
258
|
-
end
|
|
259
|
-
rule(:entityBody) do
|
|
260
|
-
(explicitAttr.repeat.as(:explicitAttr) >> deriveClause.maybe >> inverseClause.maybe >> uniqueClause.maybe >> whereClause.maybe).as(:entityBody)
|
|
261
|
-
end
|
|
262
|
-
rule(:entityConstructor) do
|
|
263
|
-
(entityRef >> op_leftparen >> (expression >> (op_comma >> expression).repeat).as(:listOf_expression).maybe >> op_rightparen).as(:entityConstructor)
|
|
264
|
-
end
|
|
265
|
-
rule(:entityDecl) do
|
|
266
|
-
(entityHead >> entityBody >> tEND_ENTITY >> op_delim).as(:entityDecl)
|
|
267
|
-
end
|
|
268
|
-
rule(:entityHead) do
|
|
269
|
-
(tENTITY >> entityId >> subsuper >> op_delim).as(:entityHead)
|
|
270
|
-
end
|
|
271
|
-
rule(:entityId) { simpleId.as(:entityId) }
|
|
272
|
-
rule(:entityRef) { entityId.as(:entityRef) }
|
|
273
|
-
rule(:enumerationExtension) do
|
|
274
|
-
(tBASED_ON >> typeRef >> (tWITH >> enumerationItems).maybe).as(:enumerationExtension)
|
|
275
|
-
end
|
|
276
|
-
rule(:enumerationId) { simpleId.as(:enumerationId) }
|
|
277
|
-
rule(:enumerationItem) { enumerationId.as(:enumerationItem) }
|
|
278
|
-
rule(:enumerationItems) do
|
|
279
|
-
(op_leftparen >> (enumerationItem >> (op_comma >> enumerationItem).repeat).as(:listOf_enumerationItem) >> op_rightparen).as(:enumerationItems)
|
|
280
|
-
end
|
|
281
|
-
rule(:enumerationRef) { enumerationId.as(:enumerationRef) }
|
|
282
|
-
rule(:enumerationReference) do
|
|
283
|
-
((typeRef >> op_period).maybe >> enumerationRef).as(:enumerationReference)
|
|
284
|
-
end
|
|
285
|
-
rule(:enumerationType) do
|
|
286
|
-
(tEXTENSIBLE.maybe >> tENUMERATION >> ((tOF >> enumerationItems) | enumerationExtension).maybe).as(:enumerationType)
|
|
287
|
-
end
|
|
288
|
-
rule(:escapeStmt) { (tESCAPE >> op_delim).as(:escapeStmt) }
|
|
289
|
-
rule(:explicitAttr) do
|
|
290
|
-
((attributeDecl >> (op_comma >> attributeDecl).repeat).as(:listOf_attributeDecl) >> op_colon >>
|
|
291
|
-
tOPTIONAL.maybe >> parameterType >> op_delim).as(:explicitAttr)
|
|
292
|
-
end
|
|
293
|
-
rule(:expression) do
|
|
294
|
-
(simpleExpression >> (relOpExtended >> simpleExpression.as(:rhs)).maybe).as(:expression)
|
|
295
|
-
end
|
|
296
|
-
rule(:factor) do
|
|
297
|
-
(simpleFactor >> (op_double_asterisk >> simpleFactor.as(:rhs)).maybe).as(:factor)
|
|
298
|
-
end
|
|
299
|
-
rule(:formalParameter) do
|
|
300
|
-
((parameterId >> (op_comma >> parameterId).repeat).as(:listOf_parameterId) >> op_colon >> parameterType).as(:formalParameter)
|
|
301
|
-
end
|
|
302
|
-
rule(:functionCall) do
|
|
303
|
-
((builtInFunction | functionRef) >> actualParameterList.maybe).as(:functionCall)
|
|
304
|
-
end
|
|
305
|
-
rule(:functionDecl) do
|
|
306
|
-
(functionHead >> algorithmHead >> stmt.repeat(1).as(:stmt) >> tEND_FUNCTION >> op_delim).as(:functionDecl)
|
|
307
|
-
end
|
|
308
|
-
rule(:functionHead) do
|
|
309
|
-
(tFUNCTION >> functionId >>
|
|
310
|
-
(op_leftparen >> (formalParameter >> (op_delim >> formalParameter).repeat).as(:listOf_formalParameter) >> op_rightparen).maybe >>
|
|
311
|
-
op_colon >> parameterType >> op_delim.as(:op_delim2)).as(:functionHead)
|
|
312
|
-
end
|
|
313
|
-
rule(:functionId) { simpleId.as(:functionId) }
|
|
314
|
-
rule(:functionRef) { functionId.as(:functionRef) }
|
|
315
|
-
rule(:generalAggregationTypes) do
|
|
316
|
-
(generalArrayType | generalBagType | generalListType | generalSetType).as(:generalAggregationTypes)
|
|
317
|
-
end
|
|
318
|
-
rule(:generalArrayType) do
|
|
319
|
-
(tARRAY >> boundSpec.maybe >> tOF >> tOPTIONAL.maybe >> tUNIQUE.maybe >> parameterType).as(:generalArrayType)
|
|
320
|
-
end
|
|
321
|
-
rule(:generalBagType) do
|
|
322
|
-
(tBAG >> boundSpec.maybe >> tOF >> parameterType).as(:generalBagType)
|
|
323
|
-
end
|
|
324
|
-
rule(:generalizedTypes) do
|
|
325
|
-
(aggregateType | generalAggregationTypes | genericEntityType | genericType).as(:generalizedTypes)
|
|
326
|
-
end
|
|
327
|
-
rule(:generalListType) do
|
|
328
|
-
(tLIST >> boundSpec.maybe >> tOF >> tUNIQUE.maybe >> parameterType).as(:generalListType)
|
|
329
|
-
end
|
|
330
|
-
rule(:generalRef) { (parameterRef | variableId).as(:generalRef) }
|
|
331
|
-
rule(:generalSetType) do
|
|
332
|
-
(tSET >> boundSpec.maybe >> tOF >> parameterType).as(:generalSetType)
|
|
333
|
-
end
|
|
334
|
-
rule(:genericEntityType) do
|
|
335
|
-
(tGENERIC_ENTITY >> (op_colon >> typeLabel).maybe).as(:genericEntityType)
|
|
336
|
-
end
|
|
337
|
-
rule(:genericType) do
|
|
338
|
-
(tGENERIC >> (op_colon >> typeLabel).maybe).as(:genericType)
|
|
339
|
-
end
|
|
340
|
-
rule(:groupQualifier) do
|
|
341
|
-
(op_double_backslash >> entityRef).as(:groupQualifier)
|
|
342
|
-
end
|
|
343
|
-
rule(:hexDigit) { match["0-9a-fA-F"] }
|
|
344
|
-
rule(:ifStmtElseStatements) do
|
|
345
|
-
stmt.repeat(1).as(:stmt).as(:ifStmtElseStatements)
|
|
346
|
-
end
|
|
347
|
-
rule(:ifStmtStatements) do
|
|
348
|
-
stmt.repeat(1).as(:stmt).as(:ifStmtStatements)
|
|
349
|
-
end
|
|
350
|
-
rule(:ifStmt) do
|
|
351
|
-
(tIF >> logicalExpression >> tTHEN >> ifStmtStatements >> (tELSE >> ifStmtElseStatements).maybe >> tEND_IF >> op_delim).as(:ifStmt)
|
|
352
|
-
end
|
|
353
|
-
rule(:incrementControl) do
|
|
354
|
-
(variableId >> op_decl >> bound1 >> tTO >> bound2 >> (tBY >> increment).maybe).as(:incrementControl)
|
|
355
|
-
end
|
|
356
|
-
rule(:increment) { numericExpression.as(:increment) }
|
|
357
|
-
rule(:index1) { index.as(:index1) }
|
|
358
|
-
rule(:index2) { index.as(:index2) }
|
|
359
|
-
rule(:index) { numericExpression.as(:index) }
|
|
360
|
-
rule(:indexQualifier) do
|
|
361
|
-
(op_leftbracket >> index1 >> (op_colon >> index2).maybe >> op_rightbracket).as(:indexQualifier)
|
|
362
|
-
end
|
|
363
|
-
rule(:instantiableType) do
|
|
364
|
-
(concreteTypes | entityRef).as(:instantiableType)
|
|
365
|
-
end
|
|
366
|
-
rule(:integerLiteral) { cts(digits.as(:str)).as(:integerLiteral) }
|
|
367
|
-
rule(:integerType) { tINTEGER.as(:integerType) }
|
|
368
|
-
rule(:interfaceSpecification) do
|
|
369
|
-
(referenceClause | useClause).as(:interfaceSpecification)
|
|
370
|
-
end
|
|
371
|
-
rule(:intervalHigh) { simpleExpression.as(:intervalHigh) }
|
|
372
|
-
rule(:intervalItem) { simpleExpression.as(:intervalItem) }
|
|
373
|
-
rule(:intervalLow) { simpleExpression.as(:intervalLow) }
|
|
374
|
-
rule(:interval) do
|
|
375
|
-
(op_left_curly_brace >> intervalLow >> intervalOp >> intervalItem >> intervalOp.as(:intervalOp2) >>
|
|
376
|
-
intervalHigh >> op_right_curly_brace).as(:interval)
|
|
377
|
-
end
|
|
378
|
-
rule(:intervalOp) { (op_less_equal | op_less_than).as(:intervalOp) }
|
|
379
|
-
rule(:inverseAttr) do
|
|
380
|
-
(attributeDecl >> op_colon >> inverseAttrType >> tFOR >> (entityRef >> op_period).maybe >> attributeRef >> op_delim).as(:inverseAttr)
|
|
381
|
-
end
|
|
382
|
-
rule(:inverseAttrType) do
|
|
383
|
-
(((tSET | tBAG) >> boundSpec.maybe >> tOF).maybe >> entityRef).as(:inverseAttrType)
|
|
384
|
-
end
|
|
385
|
-
rule(:inverseClause) do
|
|
386
|
-
(tINVERSE >> inverseAttr.repeat(1).as(:inverseAttr)).as(:inverseClause)
|
|
387
|
-
end
|
|
388
|
-
rule(:letter) { match["a-zA-Z"] }
|
|
389
|
-
rule(:listType) do
|
|
390
|
-
(tLIST >> boundSpec.maybe >> tOF >> tUNIQUE.maybe >> instantiableType).as(:listType)
|
|
391
|
-
end
|
|
392
|
-
rule(:literal) do
|
|
393
|
-
(binaryLiteral | logicalLiteral | realLiteral | integerLiteral | stringLiteral).as(:literal)
|
|
394
|
-
end
|
|
395
|
-
rule(:localDecl) do
|
|
396
|
-
(tLOCAL >> localVariable.repeat(1).as(:localVariable) >> tEND_LOCAL >> op_delim).as(:localDecl)
|
|
397
|
-
end
|
|
398
|
-
rule(:localVariable) do
|
|
399
|
-
((variableId >> (op_comma >> variableId).repeat).as(:listOf_variableId) >>
|
|
400
|
-
op_colon >> parameterType >>
|
|
401
|
-
(op_decl >> expression).maybe >> op_delim).as(:localVariable)
|
|
402
|
-
end
|
|
403
|
-
rule(:logicalExpression) { expression.as(:logicalExpression) }
|
|
404
|
-
rule(:logicalLiteral) do
|
|
405
|
-
(tFALSE | tTRUE | tUNKNOWN).as(:logicalLiteral)
|
|
406
|
-
end
|
|
407
|
-
rule(:logicalType) { tLOGICAL.as(:logicalType) }
|
|
408
|
-
rule(:multiplicationLikeOp) do
|
|
409
|
-
(op_asterisk | op_slash | tDIV | tMOD | tAND | op_double_pipe).as(:multiplicationLikeOp)
|
|
410
|
-
end
|
|
411
|
-
rule(:namedTypeOrRename) do
|
|
412
|
-
(namedTypes >> (tAS >> (entityId | typeId)).maybe).as(:namedTypeOrRename)
|
|
413
|
-
end
|
|
414
|
-
rule(:namedTypes) { (entityRef | typeRef).as(:namedTypes) }
|
|
415
|
-
rule(:nullStmt) { op_delim.as(:nullStmt) }
|
|
416
|
-
rule(:numberType) { tNUMBER.as(:numberType) }
|
|
417
|
-
rule(:numericExpression) { simpleExpression.as(:numericExpression) }
|
|
418
|
-
rule(:octet) { hexDigit >> hexDigit }
|
|
419
|
-
rule(:oneOf) do
|
|
420
|
-
(tONEOF >> op_leftparen >> (supertypeExpression >>
|
|
421
|
-
(op_comma >> supertypeExpression).repeat).as(:listOf_supertypeExpression) >> op_rightparen).as(:oneOf)
|
|
422
|
-
end
|
|
423
|
-
rule(:op_asterisk) { cstr("*").as(:op_asterisk) }
|
|
424
|
-
rule(:op_colon) { cstr(":").as(:op_colon) }
|
|
425
|
-
rule(:op_colon_equals_colon) { cstr(":=:").as(:op_colon_equals_colon) }
|
|
426
|
-
rule(:op_colon_less_greater_colon) do
|
|
427
|
-
cstr(":<>:").as(:op_colon_less_greater_colon)
|
|
428
|
-
end
|
|
429
|
-
rule(:op_comma) { cstr(",").as(:op_comma) }
|
|
430
|
-
rule(:op_decl) { cstr(":=").as(:op_decl) }
|
|
431
|
-
rule(:op_delim) { cstr(";").as(:op_delim) }
|
|
432
|
-
rule(:op_double_asterisk) { cstr("**").as(:op_double_asterisk) }
|
|
433
|
-
rule(:op_double_backslash) { cstr("\\").as(:op_double_backslash) }
|
|
434
|
-
rule(:op_double_pipe) { cstr("||").as(:op_double_pipe) }
|
|
435
|
-
rule(:op_equals) { cstr("=").as(:op_equals) }
|
|
436
|
-
rule(:op_greater_equal) { cstr(">=").as(:op_greater_equal) }
|
|
437
|
-
rule(:op_greater_than) { cstr(">").as(:op_greater_than) }
|
|
438
|
-
rule(:op_leftbracket) { cstr("[").as(:op_leftbracket) }
|
|
439
|
-
rule(:op_left_curly_brace) { cstr("{").as(:op_left_curly_brace) }
|
|
440
|
-
rule(:op_leftparen) { cstr("(").as(:op_leftparen) }
|
|
441
|
-
rule(:op_less_equal) { cstr("<=").as(:op_less_equal) }
|
|
442
|
-
rule(:op_less_greater) { cstr("<>").as(:op_less_greater) }
|
|
443
|
-
rule(:op_less_than) { cstr("<").as(:op_less_than) }
|
|
444
|
-
rule(:op_minus) { cstr("-").as(:op_minus) }
|
|
445
|
-
rule(:op_period) { cstr(".").as(:op_period) }
|
|
446
|
-
rule(:op_pipe) { cstr("|").as(:op_pipe) }
|
|
447
|
-
rule(:op_plus) { cstr("+").as(:op_plus) }
|
|
448
|
-
rule(:op_query_begin) { cstr("<*").as(:op_query_begin) }
|
|
449
|
-
rule(:op_query_end) { cstr("*>").as(:op_query_end) }
|
|
450
|
-
rule(:op_question_mark) { cstr("?").as(:op_question_mark) }
|
|
451
|
-
rule(:op_rightbracket) { cstr("]").as(:op_rightbracket) }
|
|
452
|
-
rule(:op_right_curly_brace) { cstr("}").as(:op_right_curly_brace) }
|
|
453
|
-
rule(:op_rightparen) { cstr(")").as(:op_rightparen) }
|
|
454
|
-
rule(:op_slash) { cstr("/").as(:op_slash) }
|
|
455
|
-
rule(:parameter) { expression.as(:parameter) }
|
|
456
|
-
rule(:parameterId) { simpleId.as(:parameterId) }
|
|
457
|
-
rule(:parameterRef) { parameterId.as(:parameterRef) }
|
|
458
|
-
rule(:parameterType) do
|
|
459
|
-
(generalizedTypes | namedTypes | simpleTypes).as(:parameterType)
|
|
460
|
-
end
|
|
461
|
-
rule(:population) { entityRef.as(:population) }
|
|
462
|
-
rule(:precisionSpec) { numericExpression.as(:precisionSpec) }
|
|
463
|
-
rule(:primary) do
|
|
464
|
-
(literal | (qualifiableFactor >> qualifier.repeat.as(:qualifier))).as(:primary)
|
|
465
|
-
end
|
|
466
|
-
rule(:procedureCallStmt) do
|
|
467
|
-
((builtInProcedure | procedureRef) >> actualParameterList.maybe >> op_delim).as(:procedureCallStmt)
|
|
468
|
-
end
|
|
469
|
-
rule(:procedureDecl) do
|
|
470
|
-
(procedureHead >> algorithmHead >> stmt.repeat.as(:stmt) >> tEND_PROCEDURE >> op_delim).as(:procedureDecl)
|
|
471
|
-
end
|
|
472
|
-
rule(:procedureHeadParameter) do
|
|
473
|
-
(tVAR.maybe >> formalParameter).as(:procedureHeadParameter)
|
|
474
|
-
end
|
|
475
|
-
rule(:procedureHead) do
|
|
476
|
-
(tPROCEDURE >> procedureId >>
|
|
477
|
-
(op_leftparen >> (procedureHeadParameter >>
|
|
478
|
-
(op_delim >> procedureHeadParameter).repeat).as(:listOf_procedureHeadParameter) >> op_rightparen).maybe >> op_delim.as(:op_delim2)).as(:procedureHead)
|
|
479
|
-
end
|
|
480
|
-
rule(:procedureId) { simpleId.as(:procedureId) }
|
|
481
|
-
rule(:procedureRef) { procedureId.as(:procedureRef) }
|
|
482
|
-
rule(:qualifiableFactor) do
|
|
483
|
-
(functionCall | attributeRef | constantFactor | generalRef | population).as(:qualifiableFactor)
|
|
484
|
-
end
|
|
485
|
-
rule(:qualifiedAttribute) do
|
|
486
|
-
(tSELF >> groupQualifier >> attributeQualifier).as(:qualifiedAttribute)
|
|
487
|
-
end
|
|
488
|
-
rule(:qualifier) do
|
|
489
|
-
(attributeQualifier | groupQualifier | indexQualifier).as(:qualifier)
|
|
490
|
-
end
|
|
491
|
-
rule(:queryExpression) do
|
|
492
|
-
(tQUERY >> op_leftparen >> variableId >> op_query_begin >> aggregateSource >> op_pipe >> logicalExpression >> op_rightparen).as(:queryExpression)
|
|
493
|
-
end
|
|
494
|
-
rule(:realLiteral) do
|
|
495
|
-
cts((digits >> str(".") >> digits.maybe >> (match["eE"] >> sign.maybe >> digits).maybe).as(:str)).as(:realLiteral)
|
|
496
|
-
end
|
|
497
|
-
rule(:realType) do
|
|
498
|
-
(tREAL >> (op_leftparen >> precisionSpec >> op_rightparen).maybe).as(:realType)
|
|
499
|
-
end
|
|
500
|
-
rule(:redeclaredAttribute) do
|
|
501
|
-
(qualifiedAttribute >> (tRENAMED >> attributeId).maybe).as(:redeclaredAttribute)
|
|
502
|
-
end
|
|
503
|
-
rule(:referenceClause) do
|
|
504
|
-
(tREFERENCE >> tFROM >> schemaRef >>
|
|
505
|
-
(op_leftparen >> (resourceOrRename >> (op_comma >> resourceOrRename).repeat).as(:listOf_resourceOrRename) >> op_rightparen).maybe >> op_delim).as(:referenceClause)
|
|
506
|
-
end
|
|
507
|
-
rule(:referencedAttribute) do
|
|
508
|
-
(attributeRef | qualifiedAttribute).as(:referencedAttribute)
|
|
509
|
-
end
|
|
510
|
-
rule(:relOpExtended) { (relOp | tIN | tLIKE).as(:relOpExtended) }
|
|
511
|
-
rule(:relOp) do
|
|
512
|
-
(op_less_equal | op_greater_equal | op_less_greater | op_less_than | op_greater_than | op_equals |
|
|
513
|
-
op_colon_less_greater_colon | op_colon_equals_colon).as(:relOp)
|
|
514
|
-
end
|
|
515
|
-
rule(:renameId) do
|
|
516
|
-
(constantId | entityId | functionId | procedureId | typeId).as(:renameId)
|
|
517
|
-
end
|
|
518
|
-
rule(:repeatControl) do
|
|
519
|
-
(incrementControl.maybe >> whileControl.maybe >> untilControl.maybe).as(:repeatControl)
|
|
520
|
-
end
|
|
521
|
-
rule(:repeatStmt) do
|
|
522
|
-
(tREPEAT >> repeatControl >> op_delim >> stmt.repeat(1).as(:stmt) >> tEND_REPEAT >> op_delim.as(:op_delim2)).as(:repeatStmt)
|
|
523
|
-
end
|
|
524
|
-
rule(:repetition) { numericExpression.as(:repetition) }
|
|
525
|
-
rule(:resourceOrRename) do
|
|
526
|
-
(resourceRef >> (tAS >> renameId).maybe).as(:resourceOrRename)
|
|
527
|
-
end
|
|
528
|
-
rule(:resourceRef) do
|
|
529
|
-
(constantRef | entityRef | functionRef | procedureRef | typeRef).as(:resourceRef)
|
|
530
|
-
end
|
|
531
|
-
rule(:returnStmt) do
|
|
532
|
-
(tRETURN >> (op_leftparen >> expression >> op_rightparen).maybe >> op_delim).as(:returnStmt)
|
|
533
|
-
end
|
|
534
|
-
rule(:ruleDecl) do
|
|
535
|
-
(ruleHead >> algorithmHead >> stmt.repeat.as(:stmt) >> whereClause >> tEND_RULE >> op_delim).as(:ruleDecl)
|
|
536
|
-
end
|
|
537
|
-
rule(:ruleHead) do
|
|
538
|
-
(tRULE >> ruleId >> tFOR >> op_leftparen >>
|
|
539
|
-
(entityRef >> (op_comma >> entityRef).repeat).as(:listOf_entityRef) >> op_rightparen >> op_delim).as(:ruleHead)
|
|
540
|
-
end
|
|
541
|
-
rule(:ruleId) { simpleId.as(:ruleId) }
|
|
542
|
-
rule(:ruleLabelId) { simpleId.as(:ruleLabelId) }
|
|
543
|
-
rule(:ruleLabelRef) { ruleLabelId.as(:ruleLabelRef) }
|
|
544
|
-
rule(:ruleRef) { ruleId.as(:ruleRef) }
|
|
545
|
-
rule(:schemaBodyDeclaration) do
|
|
546
|
-
(declaration | ruleDecl).as(:schemaBodyDeclaration)
|
|
547
|
-
end
|
|
548
|
-
rule(:schemaBody) do
|
|
549
|
-
(interfaceSpecification.repeat.as(:interfaceSpecification) >> constantDecl.maybe >>
|
|
550
|
-
schemaBodyDeclaration.repeat.as(:schemaBodyDeclaration)).as(:schemaBody)
|
|
551
|
-
end
|
|
552
|
-
rule(:schemaDecl) do
|
|
553
|
-
(tSCHEMA >> schemaId >> schemaVersionId.maybe >> op_delim >> schemaBody >> tEND_SCHEMA >> op_delim.as(:op_delim2)).as(:schemaDecl)
|
|
554
|
-
end
|
|
555
|
-
rule(:schemaId) { simpleId.as(:schemaId) }
|
|
556
|
-
rule(:schemaRef) { schemaId.as(:schemaRef) }
|
|
557
|
-
rule(:schemaVersionId) { stringLiteral.as(:schemaVersionId) }
|
|
558
|
-
rule(:selectExtension) do
|
|
559
|
-
(tBASED_ON >> typeRef >> (tWITH >> selectList).maybe).as(:selectExtension)
|
|
560
|
-
end
|
|
561
|
-
rule(:selectList) do
|
|
562
|
-
(op_leftparen >> (namedTypes >> (op_comma >> namedTypes).repeat).as(:listOf_namedTypes) >> op_rightparen).as(:selectList)
|
|
563
|
-
end
|
|
564
|
-
rule(:selector) { expression.as(:selector) }
|
|
565
|
-
rule(:selectType) do
|
|
566
|
-
((tEXTENSIBLE >> tGENERIC_ENTITY.maybe).maybe >> tSELECT >> (selectList | selectExtension).maybe).as(:selectType)
|
|
567
|
-
end
|
|
568
|
-
rule(:setType) do
|
|
569
|
-
(tSET >> boundSpec.maybe >> tOF >> instantiableType).as(:setType)
|
|
570
|
-
end
|
|
571
|
-
rule(:sign) { match["+-"] }
|
|
572
|
-
rule(:simpleExpression) do
|
|
573
|
-
(term >> (addLikeOp.as(:operator) >> term).as(:item).repeat.as(:rhs)).as(:simpleExpression)
|
|
574
|
-
end
|
|
575
|
-
rule(:simpleFactor) do
|
|
576
|
-
(simpleFactorExpression | aggregateInitializer | entityConstructor | interval | queryExpression | stringLiteral |
|
|
577
|
-
simpleFactorUnaryExpression | enumerationReference).as(:simpleFactor)
|
|
578
|
-
end
|
|
579
|
-
rule(:simpleFactorExpression) do
|
|
580
|
-
((op_leftparen >> expression >> op_rightparen) | primary).as(:simpleFactorExpression)
|
|
581
|
-
end
|
|
582
|
-
rule(:simpleFactorUnaryExpression) do
|
|
583
|
-
(unaryOp >> simpleFactorExpression).as(:simpleFactorUnaryExpression)
|
|
584
|
-
end
|
|
585
|
-
rule(:simpleId) do
|
|
586
|
-
anyKeyword.absent? >> cts((match["a-zA-Z_"] >> match["a-zA-Z0-9_"].repeat).as(:str)).as(:simpleId)
|
|
587
|
-
end
|
|
588
|
-
rule(:simpleStringLiteral) do
|
|
589
|
-
cts((str("'") >> (str("'").absent? >> any).repeat >> str("'")).as(:str)).as(:simpleStringLiteral)
|
|
590
|
-
end
|
|
591
|
-
rule(:simpleTypes) do
|
|
592
|
-
(binaryType | booleanType | integerType | logicalType | numberType | realType | stringType).as(:simpleTypes)
|
|
593
|
-
end
|
|
594
|
-
rule(:skipStmt) { (tSKIP >> op_delim).as(:skipStmt) }
|
|
595
|
-
rule(:space) { match[" \r\n\t\f"] | embeddedRemark | tailRemark }
|
|
596
|
-
rule(:spaces) { space.repeat.as(:spaces) }
|
|
597
|
-
rule(:stmt) do
|
|
598
|
-
(aliasStmt | assignmentStmt | caseStmt | compoundStmt | escapeStmt | ifStmt |
|
|
599
|
-
nullStmt | procedureCallStmt | repeatStmt | returnStmt | skipStmt).as(:stmt)
|
|
600
|
-
end
|
|
601
|
-
rule(:stringLiteral) do
|
|
602
|
-
(simpleStringLiteral | encodedStringLiteral).as(:stringLiteral)
|
|
603
|
-
end
|
|
604
|
-
rule(:stringType) { (tSTRING >> widthSpec.maybe).as(:stringType) }
|
|
605
|
-
rule(:subsuper) do
|
|
606
|
-
(supertypeConstraint.maybe >> subtypeDeclaration.maybe).as(:subsuper)
|
|
607
|
-
end
|
|
608
|
-
rule(:subtypeConstraintBody) do
|
|
609
|
-
(abstractSupertype.maybe >> totalOver.maybe >> (supertypeExpression >> op_delim).maybe).as(:subtypeConstraintBody)
|
|
610
|
-
end
|
|
611
|
-
rule(:subtypeConstraintDecl) do
|
|
612
|
-
(subtypeConstraintHead >> subtypeConstraintBody >> tEND_SUBTYPE_CONSTRAINT >> op_delim).as(:subtypeConstraintDecl)
|
|
613
|
-
end
|
|
614
|
-
rule(:subtypeConstraintHead) do
|
|
615
|
-
(tSUBTYPE_CONSTRAINT >> subtypeConstraintId >> tFOR >> entityRef >> op_delim).as(:subtypeConstraintHead)
|
|
616
|
-
end
|
|
617
|
-
rule(:subtypeConstraintId) { simpleId.as(:subtypeConstraintId) }
|
|
618
|
-
rule(:subtypeConstraintRef) do
|
|
619
|
-
subtypeConstraintId.as(:subtypeConstraintRef)
|
|
620
|
-
end
|
|
621
|
-
rule(:subtypeConstraint) do
|
|
622
|
-
(tOF >> op_leftparen >> supertypeExpression >> op_rightparen).as(:subtypeConstraint)
|
|
623
|
-
end
|
|
624
|
-
rule(:subtypeDeclaration) do
|
|
625
|
-
(tSUBTYPE >> tOF >> op_leftparen >> entityRef >> (op_comma >> entityRef).repeat >> op_rightparen).as(:listOf_entityRef).as(:subtypeDeclaration)
|
|
626
|
-
end
|
|
627
|
-
rule(:supertypeConstraint) do
|
|
628
|
-
(abstractSupertypeDeclaration | abstractEntityDeclaration | supertypeRule).as(:supertypeConstraint)
|
|
629
|
-
end
|
|
630
|
-
rule(:supertypeExpression) do
|
|
631
|
-
(supertypeFactor >> (tANDOR.as(:operator) >> supertypeFactor).as(:item).repeat.as(:rhs)).as(:supertypeExpression)
|
|
632
|
-
end
|
|
633
|
-
rule(:supertypeFactor) do
|
|
634
|
-
(supertypeTerm >> (tAND.as(:operator) >> supertypeTerm).as(:item).repeat.as(:rhs)).as(:supertypeFactor)
|
|
635
|
-
end
|
|
636
|
-
rule(:supertypeRule) do
|
|
637
|
-
(tSUPERTYPE >> subtypeConstraint).as(:supertypeRule)
|
|
638
|
-
end
|
|
639
|
-
rule(:supertypeTerm) do
|
|
640
|
-
(entityRef | oneOf | (op_leftparen >> supertypeExpression >> op_rightparen)).as(:supertypeTerm)
|
|
641
|
-
end
|
|
642
|
-
rule(:syntax) do
|
|
643
|
-
(spaces.as(:spaces) >> schemaDecl.repeat(1).as(:schemaDecl) >> spaces.as(:trailer)).as(:syntax)
|
|
644
|
-
end
|
|
645
|
-
rule(:tailRemark) do
|
|
646
|
-
(str("--") >> (str("\n").absent? >> any).repeat >> str("\n").maybe).as(:tailRemark)
|
|
647
|
-
end
|
|
648
|
-
rule(:term) do
|
|
649
|
-
(factor >> (multiplicationLikeOp >> factor).as(:item).repeat.as(:rhs)).as(:term)
|
|
650
|
-
end
|
|
651
|
-
rule(:totalOver) do
|
|
652
|
-
(tTOTAL_OVER >> op_leftparen >> (entityRef >> (op_comma >> entityRef).repeat).as(:listOf_entityRef) >> op_rightparen >> op_delim).as(:totalOver)
|
|
653
|
-
end
|
|
654
|
-
rule(:typeDecl) do
|
|
655
|
-
(tTYPE >> typeId >> op_equals >> underlyingType >> op_delim >> whereClause.maybe >> tEND_TYPE >> op_delim.as(:op_delim2)).as(:typeDecl)
|
|
656
|
-
end
|
|
657
|
-
rule(:typeId) { simpleId.as(:typeId) }
|
|
658
|
-
rule(:typeLabelId) { simpleId.as(:typeLabelId) }
|
|
659
|
-
rule(:typeLabelRef) { typeLabelId.as(:typeLabelRef) }
|
|
660
|
-
rule(:typeLabel) { (typeLabelId | typeLabelRef).as(:typeLabel) }
|
|
661
|
-
rule(:typeRef) { typeId.as(:typeRef) }
|
|
662
|
-
rule(:unaryOp) { (op_plus | op_minus | tNOT).as(:unaryOp) }
|
|
663
|
-
rule(:underlyingType) do
|
|
664
|
-
(concreteTypes | constructedTypes).as(:underlyingType)
|
|
665
|
-
end
|
|
666
|
-
rule(:uniqueClause) do
|
|
667
|
-
(tUNIQUE >> (uniqueRule >> op_delim >> (uniqueRule >> op_delim.as(:op_delim2)).repeat).as(:listOf_uniqueRule)).as(:uniqueClause)
|
|
668
|
-
end
|
|
669
|
-
rule(:uniqueRule) do
|
|
670
|
-
((ruleLabelId >> op_colon).maybe >> (referencedAttribute >> (op_comma >> referencedAttribute).repeat).as(:listOf_referencedAttribute)).as(:uniqueRule)
|
|
671
|
-
end
|
|
672
|
-
rule(:untilControl) { (tUNTIL >> logicalExpression).as(:untilControl) }
|
|
673
|
-
rule(:useClause) do
|
|
674
|
-
(tUSE >> tFROM >> schemaRef >>
|
|
675
|
-
(op_leftparen >> (namedTypeOrRename >> (op_comma >>
|
|
676
|
-
namedTypeOrRename).repeat).as(:listOf_namedTypeOrRename) >> op_rightparen).maybe >> op_delim).as(:useClause)
|
|
677
|
-
end
|
|
678
|
-
rule(:variableId) { simpleId.as(:variableId) }
|
|
679
|
-
rule(:variableRef) { variableId.as(:variableRef) }
|
|
680
|
-
rule(:whereClause) do
|
|
681
|
-
(tWHERE >> (domainRule >> op_delim).repeat.as(:listOf_domainRule)).as(:whereClause)
|
|
682
|
-
end
|
|
683
|
-
rule(:whileControl) { (tWHILE >> logicalExpression).as(:whileControl) }
|
|
684
|
-
rule(:width) { numericExpression.as(:width) }
|
|
685
|
-
rule(:widthSpec) do
|
|
686
|
-
(op_leftparen >> width >> op_rightparen >> tFIXED.maybe).as(:widthSpec)
|
|
687
|
-
end
|
|
40
|
+
bytes.byteslice(3..).force_encoding(source.encoding)
|
|
688
41
|
end
|
|
42
|
+
private_class_method :strip_bom
|
|
689
43
|
|
|
690
|
-
# Parses Express file into an Express model
|
|
691
|
-
# @param [String]
|
|
692
|
-
# @param [Boolean]
|
|
693
|
-
# @param [Boolean]
|
|
694
|
-
# @param [Boolean]
|
|
44
|
+
# Parses Express file into an Express model.
|
|
45
|
+
# @param file [String] Express file path
|
|
46
|
+
# @param skip_references [Boolean] skip resolving references
|
|
47
|
+
# @param include_source [Boolean] attach original source code to model elements
|
|
48
|
+
# @param use_native [Boolean] use native parser (default: true when available)
|
|
695
49
|
# @return [Model::ExpFile] ExpFile containing parsed schemas
|
|
696
|
-
# @raise [SchemaParseFailure] if the schema file fails to parse
|
|
697
|
-
def self.from_file(file, skip_references: nil, include_source: nil,
|
|
50
|
+
# @raise [Error::SchemaParseFailure] if the schema file fails to parse
|
|
51
|
+
def self.from_file(file, skip_references: nil, include_source: nil,
|
|
52
|
+
root_path: nil, use_native: nil) # rubocop:disable Metrics/AbcSize
|
|
698
53
|
Expressir::Benchmark.measure_file(file) do
|
|
699
|
-
source = File.read
|
|
54
|
+
source = strip_bom(File.read(file))
|
|
700
55
|
|
|
701
|
-
# remove root path from file path
|
|
702
56
|
schema_file = root_path ? Pathname.new(file.to_s).relative_path_from(root_path).to_s : file.to_s
|
|
703
57
|
|
|
704
|
-
use_native = Parser.native_available? if use_native.nil?
|
|
58
|
+
use_native = Grammar::Parser.native_available? if use_native.nil?
|
|
705
59
|
|
|
706
60
|
begin
|
|
707
|
-
ast = if use_native && Parser.native_available?
|
|
61
|
+
ast = if use_native && Grammar::Parser.native_available?
|
|
708
62
|
begin
|
|
709
|
-
Parser.parse_native(source)
|
|
63
|
+
Grammar::Parser.parse_native(source)
|
|
710
64
|
rescue StandardError
|
|
711
|
-
|
|
712
|
-
# fall back to Ruby parser
|
|
713
|
-
Parser.cached_parser.parse source
|
|
65
|
+
Grammar::Parser.cached_parser.parse source
|
|
714
66
|
end
|
|
715
67
|
else
|
|
716
|
-
Parser.cached_parser.parse source
|
|
68
|
+
Grammar::Parser.cached_parser.parse source
|
|
717
69
|
end
|
|
718
70
|
rescue Parsanol::ParseFailed => e
|
|
719
|
-
# Instead of just printing, raise a proper error with file context
|
|
720
71
|
raise Error::SchemaParseFailure.new(schema_file, e)
|
|
721
72
|
end
|
|
722
73
|
|
|
723
|
-
|
|
724
|
-
|
|
74
|
+
exp_file = ::Expressir::Express::Builder.build_with_remarks(ast, source: source,
|
|
75
|
+
include_source: include_source)
|
|
76
|
+
|
|
77
|
+
transfer_header_to_schema(exp_file, source)
|
|
725
78
|
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
@exp_file.schemas.each do |schema|
|
|
79
|
+
exp_file.path = schema_file
|
|
80
|
+
exp_file.schemas.each do |schema|
|
|
729
81
|
schema.file = schema_file
|
|
730
82
|
schema.file_basename = File.basename(schema_file, ".exp")
|
|
731
83
|
end
|
|
732
84
|
|
|
733
85
|
unless skip_references
|
|
734
86
|
Expressir::Benchmark.measure_references do
|
|
735
|
-
|
|
736
|
-
@resolve_references_model_visitor.visit(@exp_file)
|
|
87
|
+
ResolveReferencesModelVisitor.new.visit(exp_file)
|
|
737
88
|
end
|
|
738
89
|
end
|
|
739
90
|
|
|
740
|
-
|
|
91
|
+
exp_file
|
|
741
92
|
end
|
|
742
93
|
end
|
|
743
94
|
|
|
744
|
-
# Parses Express files into an Express model
|
|
745
|
-
# @param [Array<String>]
|
|
746
|
-
# @param [Boolean]
|
|
747
|
-
# @param [Boolean]
|
|
748
|
-
# @param [Boolean]
|
|
749
|
-
# @yield [filename, schemas, error] Optional block called for each file
|
|
750
|
-
# @yieldparam filename [String] Name of the file being processed
|
|
751
|
-
# @yieldparam schemas [Array, nil] Array of parsed schemas (nil if parsing failed)
|
|
752
|
-
# @yieldparam error [Exception, nil] Error that occurred (nil if parsing succeeded)
|
|
95
|
+
# Parses Express files into an Express model.
|
|
96
|
+
# @param files [Array<String>] Express file paths
|
|
97
|
+
# @param skip_references [Boolean] skip resolving references
|
|
98
|
+
# @param include_source [Boolean] attach original source code to model elements
|
|
99
|
+
# @param use_native [Boolean] use native parser (default: true when available)
|
|
100
|
+
# @yield [filename, schemas, error] Optional block called for each file
|
|
753
101
|
# @return [Model::Repository] Repository containing all parsed ExpFiles
|
|
754
102
|
def self.from_files(files, skip_references: nil, include_source: nil,
|
|
755
103
|
root_path: nil, use_native: nil)
|
|
@@ -760,53 +108,46 @@ root_path: nil, use_native: nil)
|
|
|
760
108
|
root_path: root_path, use_native: use_native)
|
|
761
109
|
all_exp_files << exp_file
|
|
762
110
|
|
|
763
|
-
# Call the progress block if provided
|
|
764
111
|
yield(file, exp_file&.schemas, nil) if block_given?
|
|
765
112
|
rescue StandardError => e
|
|
766
|
-
# Call the progress block with the error if provided
|
|
767
113
|
yield(file, nil, e) if block_given?
|
|
768
|
-
|
|
769
|
-
# Re-raise the error if it's not a schema parse failure
|
|
770
|
-
# This allows handling of specific schema parse failures while still propagating other errors
|
|
771
114
|
raise unless e.is_a?(Error::SchemaParseFailure)
|
|
772
115
|
end
|
|
773
116
|
|
|
774
|
-
|
|
117
|
+
repository = Model::Repository.new(files: all_exp_files)
|
|
775
118
|
|
|
776
119
|
unless skip_references
|
|
777
120
|
Expressir::Benchmark.measure_references do
|
|
778
|
-
|
|
779
|
-
@resolve_references_model_visitor.visit(@repository)
|
|
121
|
+
ResolveReferencesModelVisitor.new.visit(repository)
|
|
780
122
|
end
|
|
781
123
|
end
|
|
782
124
|
|
|
783
|
-
|
|
125
|
+
repository
|
|
784
126
|
end
|
|
785
127
|
|
|
786
|
-
# Parses Express content string into an Express model
|
|
787
|
-
# @param [String]
|
|
788
|
-
# @param [Boolean]
|
|
789
|
-
# @param [Boolean]
|
|
790
|
-
# @param [Boolean]
|
|
791
|
-
# @param [Boolean]
|
|
128
|
+
# Parses Express content string into an Express model.
|
|
129
|
+
# @param content [String] EXPRESS source code
|
|
130
|
+
# @param skip_references [Boolean] skip resolving references
|
|
131
|
+
# @param include_source [Boolean] attach original source code to model elements
|
|
132
|
+
# @param use_native [Boolean] use native parser (default: true when available)
|
|
133
|
+
# @param use_streaming [Boolean] use streaming builder for maximum performance
|
|
792
134
|
# @return [Model::ExpFile] Parsed ExpFile
|
|
793
|
-
# @raise [SchemaParseFailure] if the content fails to parse
|
|
135
|
+
# @raise [Error::SchemaParseFailure] if the content fails to parse
|
|
794
136
|
def self.from_exp(content, skip_references: nil, include_source: nil,
|
|
795
137
|
use_native: nil, use_streaming: false)
|
|
796
|
-
|
|
797
|
-
if use_streaming && Parser.native_available? && defined?(Parsanol::Native.parse_with_builder)
|
|
138
|
+
content = strip_bom(content)
|
|
139
|
+
if use_streaming && Grammar::Parser.native_available? && defined?(Parsanol::Native.parse_with_builder)
|
|
798
140
|
return from_exp_streaming(content, skip_references: skip_references,
|
|
799
141
|
include_source: include_source)
|
|
800
142
|
end
|
|
801
143
|
|
|
802
|
-
use_native = Parser.native_available? if use_native.nil?
|
|
144
|
+
use_native = Grammar::Parser.native_available? if use_native.nil?
|
|
803
145
|
|
|
804
146
|
begin
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
Parser.parse_native(content)
|
|
147
|
+
ast = if use_native && Grammar::Parser.native_available?
|
|
148
|
+
Grammar::Parser.parse_native(content)
|
|
808
149
|
else
|
|
809
|
-
Parser.cached_parser.parse(content)
|
|
150
|
+
Grammar::Parser.cached_parser.parse(content)
|
|
810
151
|
end
|
|
811
152
|
rescue Parsanol::ParseFailed, StandardError => e
|
|
812
153
|
raise Error::SchemaParseFailure.new("(from string)", e)
|
|
@@ -816,6 +157,8 @@ root_path: nil, use_native: nil)
|
|
|
816
157
|
source: content,
|
|
817
158
|
include_source: include_source)
|
|
818
159
|
|
|
160
|
+
transfer_header_to_schema(exp_file, content)
|
|
161
|
+
|
|
819
162
|
exp_file.schemas.each do |schema|
|
|
820
163
|
schema.file = nil
|
|
821
164
|
schema.file_basename = nil
|
|
@@ -823,23 +166,22 @@ root_path: nil, use_native: nil)
|
|
|
823
166
|
|
|
824
167
|
unless skip_references
|
|
825
168
|
Expressir::Benchmark.measure_references do
|
|
826
|
-
|
|
827
|
-
resolve_references_model_visitor.visit(exp_file)
|
|
169
|
+
ResolveReferencesModelVisitor.new.visit(exp_file)
|
|
828
170
|
end
|
|
829
171
|
end
|
|
830
172
|
|
|
831
173
|
exp_file
|
|
832
174
|
end
|
|
833
175
|
|
|
834
|
-
# Parse using streaming builder (construct-by-construct)
|
|
835
|
-
# @param [String]
|
|
836
|
-
# @param [Boolean]
|
|
837
|
-
# @param [Boolean]
|
|
176
|
+
# Parse using streaming builder (construct-by-construct).
|
|
177
|
+
# @param content [String] EXPRESS source code
|
|
178
|
+
# @param skip_references [Boolean] skip resolving references
|
|
179
|
+
# @param include_source [Boolean] attach original source code to model elements
|
|
838
180
|
# @return [Model::ExpFile] Parsed ExpFile
|
|
839
|
-
# @raise [SchemaParseFailure] if the content fails to parse
|
|
181
|
+
# @raise [Error::SchemaParseFailure] if the content fails to parse
|
|
840
182
|
def self.from_exp_streaming_builder(content, skip_references: nil,
|
|
841
183
|
include_source: nil)
|
|
842
|
-
grammar_json = Parser.cached_grammar_json
|
|
184
|
+
grammar_json = Grammar::Parser.cached_grammar_json
|
|
843
185
|
builder = ::Expressir::Express::StreamingBuilder.new(source: content,
|
|
844
186
|
include_source: include_source)
|
|
845
187
|
|
|
@@ -857,31 +199,29 @@ include_source: nil)
|
|
|
857
199
|
|
|
858
200
|
unless skip_references
|
|
859
201
|
Expressir::Benchmark.measure_references do
|
|
860
|
-
|
|
861
|
-
resolve_references_model_visitor.visit(exp_file)
|
|
202
|
+
ResolveReferencesModelVisitor.new.visit(exp_file)
|
|
862
203
|
end
|
|
863
204
|
end
|
|
864
205
|
|
|
865
206
|
exp_file
|
|
866
207
|
end
|
|
867
208
|
|
|
868
|
-
# Parse each schema separately with fresh arena (memory-bounded)
|
|
209
|
+
# Parse each schema separately with fresh arena (memory-bounded).
|
|
869
210
|
#
|
|
870
|
-
#
|
|
871
|
-
# Memory is bounded by the largest schema, not the
|
|
211
|
+
# Splits source into schema blocks via {SchemaBlockScanner} and parses
|
|
212
|
+
# each independently. Memory is bounded by the largest schema, not the
|
|
213
|
+
# entire file.
|
|
872
214
|
#
|
|
873
215
|
# @param content [String] EXPRESS source code
|
|
874
|
-
# @param skip_references [Boolean]
|
|
875
|
-
# @param include_source [Boolean]
|
|
876
|
-
# @return [
|
|
216
|
+
# @param skip_references [Boolean] skip resolving references
|
|
217
|
+
# @param include_source [Boolean] attach original source code to model elements
|
|
218
|
+
# @return [Model::ExpFile] Parsed ExpFile
|
|
877
219
|
def self.from_exp_streaming(content, skip_references: nil,
|
|
878
220
|
include_source: nil)
|
|
879
|
-
grammar_json = Parser.cached_schema_grammar_json
|
|
221
|
+
grammar_json = Grammar::Parser.cached_schema_grammar_json
|
|
880
222
|
|
|
881
|
-
|
|
882
|
-
schema_blocks = extract_schema_blocks(content)
|
|
223
|
+
schema_blocks = SchemaBlockScanner.extract_schema_blocks(content)
|
|
883
224
|
|
|
884
|
-
# Parse each schema with fresh arena
|
|
885
225
|
schemas = schema_blocks.map do |block|
|
|
886
226
|
ast = Parsanol::Native.parse_fresh(grammar_json, block[:source])
|
|
887
227
|
schema_model = Builder.build(ast)
|
|
@@ -893,7 +233,6 @@ include_source: nil)
|
|
|
893
233
|
)
|
|
894
234
|
end
|
|
895
235
|
|
|
896
|
-
# Build the file model
|
|
897
236
|
exp_file = Expressir::Model::ExpFile.new
|
|
898
237
|
exp_file.schemas = schemas
|
|
899
238
|
|
|
@@ -904,143 +243,34 @@ include_source: nil)
|
|
|
904
243
|
|
|
905
244
|
unless skip_references
|
|
906
245
|
Expressir::Benchmark.measure_references do
|
|
907
|
-
|
|
908
|
-
resolve_references_model_visitor.visit(exp_file)
|
|
246
|
+
ResolveReferencesModelVisitor.new.visit(exp_file)
|
|
909
247
|
end
|
|
910
248
|
end
|
|
911
249
|
|
|
912
250
|
exp_file
|
|
913
251
|
end
|
|
914
252
|
|
|
915
|
-
#
|
|
916
|
-
#
|
|
917
|
-
#
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
blocks = []
|
|
923
|
-
pos = 0
|
|
924
|
-
len = source.length
|
|
925
|
-
|
|
926
|
-
while pos < len
|
|
927
|
-
# Skip whitespace and find SCHEMA keyword
|
|
928
|
-
skip_ws_and_comments(source, pos)
|
|
929
|
-
pos = skip_ws_and_comments(source, pos)
|
|
253
|
+
# Transfer file-level untagged remarks that appear before the first
|
|
254
|
+
# SCHEMA keyword to the first schema's +header+ attribute so they are
|
|
255
|
+
# accessible via +schema.header+ and through Liquid drops.
|
|
256
|
+
def self.transfer_header_to_schema(exp_file, source = nil)
|
|
257
|
+
return unless exp_file.untagged_remarks&.any?
|
|
258
|
+
return unless exp_file.schemas&.any?
|
|
259
|
+
return unless source
|
|
930
260
|
|
|
931
|
-
|
|
261
|
+
schema_pos = source.b.index(/\bSCHEMA\b/)
|
|
262
|
+
return unless schema_pos
|
|
932
263
|
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
result = parse_schema_block(source, pos)
|
|
936
|
-
if result
|
|
937
|
-
blocks << result
|
|
938
|
-
pos = result[:end_pos]
|
|
939
|
-
next
|
|
940
|
-
end
|
|
941
|
-
end
|
|
942
|
-
|
|
943
|
-
pos += 1
|
|
944
|
-
end
|
|
945
|
-
|
|
946
|
-
blocks
|
|
947
|
-
end
|
|
948
|
-
|
|
949
|
-
private_class_method :extract_schema_blocks
|
|
950
|
-
|
|
951
|
-
def self.parse_schema_block(source, start_pos)
|
|
952
|
-
# Must be at SCHEMA keyword
|
|
953
|
-
return nil unless source[start_pos..].start_with?("SCHEMA")
|
|
954
|
-
|
|
955
|
-
pos = start_pos + "SCHEMA".length
|
|
956
|
-
pos = skip_spaces(source, pos)
|
|
957
|
-
|
|
958
|
-
# Extract schema name (identifier)
|
|
959
|
-
name_start = pos
|
|
960
|
-
while pos < source.length && (source[pos] =~ /[a-zA-Z0-9_]/ || source[pos] == "_")
|
|
961
|
-
pos += 1
|
|
962
|
-
end
|
|
963
|
-
schema_name = source[name_start...pos]
|
|
964
|
-
|
|
965
|
-
return nil if schema_name.empty?
|
|
966
|
-
|
|
967
|
-
# Skip to END_SCHEMA
|
|
968
|
-
depth = 1
|
|
969
|
-
search_pos = pos
|
|
970
|
-
end_pos = nil
|
|
971
|
-
|
|
972
|
-
while search_pos < source.length
|
|
973
|
-
if source[search_pos] == '"'
|
|
974
|
-
# String literal - skip to end
|
|
975
|
-
search_pos += 1
|
|
976
|
-
while search_pos < source.length && source[search_pos] != '"'
|
|
977
|
-
search_pos += 1
|
|
978
|
-
end
|
|
979
|
-
search_pos += 1
|
|
980
|
-
elsif source[search_pos] == "(" && source[search_pos + 1] == "*"
|
|
981
|
-
# Comment - skip to end
|
|
982
|
-
search_pos += 2
|
|
983
|
-
while search_pos < source.length && !(source[search_pos] == "*" && source[search_pos + 1] == ")")
|
|
984
|
-
search_pos += 1
|
|
985
|
-
end
|
|
986
|
-
search_pos += 2
|
|
987
|
-
elsif source[search_pos..].start_with?("END_SCHEMA")
|
|
988
|
-
depth -= 1
|
|
989
|
-
if depth.zero?
|
|
990
|
-
end_pos = search_pos + "END_SCHEMA".length
|
|
991
|
-
# Skip trailing whitespace and semicolon
|
|
992
|
-
pos = end_pos
|
|
993
|
-
pos = skip_spaces(source, pos)
|
|
994
|
-
pos += 1 if source[pos] == ";" # semicolon
|
|
995
|
-
break
|
|
996
|
-
end
|
|
997
|
-
search_pos += "END_SCHEMA".length
|
|
998
|
-
else
|
|
999
|
-
search_pos += 1
|
|
1000
|
-
end
|
|
264
|
+
header_remarks = exp_file.untagged_remarks.select do |r|
|
|
265
|
+
r.source_offset && r.source_offset < schema_pos
|
|
1001
266
|
end
|
|
267
|
+
return unless header_remarks.any?
|
|
1002
268
|
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
{
|
|
1007
|
-
name: schema_name,
|
|
1008
|
-
source: schema_source,
|
|
1009
|
-
start_pos: start_pos,
|
|
1010
|
-
end_pos: pos,
|
|
1011
|
-
}
|
|
1012
|
-
end
|
|
1013
|
-
private_class_method :parse_schema_block
|
|
1014
|
-
|
|
1015
|
-
def self.skip_spaces(source, pos)
|
|
1016
|
-
while pos < source.length && [" ", "\t", "\n",
|
|
1017
|
-
"\r"].include?(source[pos])
|
|
1018
|
-
pos += 1
|
|
1019
|
-
end
|
|
1020
|
-
pos
|
|
1021
|
-
end
|
|
1022
|
-
private_class_method :skip_spaces
|
|
1023
|
-
|
|
1024
|
-
def self.skip_ws_and_comments(source, pos)
|
|
1025
|
-
len = source.length
|
|
1026
|
-
while pos < len
|
|
1027
|
-
c = source[pos]
|
|
1028
|
-
if [" ", "\t", "\n", "\r"].include?(c)
|
|
1029
|
-
pos += 1
|
|
1030
|
-
elsif c == "(" && source[pos + 1] == "*"
|
|
1031
|
-
# Comment - skip to end
|
|
1032
|
-
pos += 2
|
|
1033
|
-
while pos < len - 1 && !(source[pos] == "*" && source[pos + 1] == ")")
|
|
1034
|
-
pos += 1
|
|
1035
|
-
end
|
|
1036
|
-
pos += 2
|
|
1037
|
-
else
|
|
1038
|
-
break
|
|
1039
|
-
end
|
|
1040
|
-
end
|
|
1041
|
-
pos
|
|
269
|
+
header_text = header_remarks.map(&:text).join("\n")
|
|
270
|
+
exp_file.schemas.first.header = header_text
|
|
271
|
+
exp_file.untagged_remarks -= header_remarks
|
|
1042
272
|
end
|
|
1043
|
-
private_class_method :
|
|
273
|
+
private_class_method :transfer_header_to_schema
|
|
1044
274
|
|
|
1045
275
|
private_class_method :from_exp_streaming
|
|
1046
276
|
end
|