tediparse 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/CHANGELOG.md +319 -0
- data/LICENSE +26 -0
- data/README.md +308 -0
- data/Rakefile +33 -0
- data/bin/tediparse +102 -0
- data/doc/Defining.md +97 -0
- data/doc/Generating-Grammars.md +241 -0
- data/doc/Generating.md +332 -0
- data/doc/Navigating.md +670 -0
- data/doc/Parsing.md +133 -0
- data/doc/README.md +51 -0
- data/doc/Serializing.md +100 -0
- data/doc/Tokenizing.md +136 -0
- data/doc/Validating.md +140 -0
- data/lib/ruby/array.rb +226 -0
- data/lib/ruby/blank.rb +52 -0
- data/lib/ruby/exception.rb +14 -0
- data/lib/ruby/hash.rb +14 -0
- data/lib/ruby/module.rb +60 -0
- data/lib/ruby/object.rb +56 -0
- data/lib/ruby/string.rb +89 -0
- data/lib/ruby/to_d.rb +82 -0
- data/lib/ruby/to_date.rb +26 -0
- data/lib/ruby/to_time.rb +21 -0
- data/lib/ruby/try.rb +46 -0
- data/lib/stupidedi/builder.rb +6 -0
- data/lib/stupidedi/color.rb +94 -0
- data/lib/stupidedi/config/code_list_config.rb +45 -0
- data/lib/stupidedi/config/functional_group_config.rb +72 -0
- data/lib/stupidedi/config/interchange_config.rb +86 -0
- data/lib/stupidedi/config/transaction_set_config.rb +73 -0
- data/lib/stupidedi/config.rb +100 -0
- data/lib/stupidedi/either.rb +286 -0
- data/lib/stupidedi/exceptions/invalid_element_error.rb +7 -0
- data/lib/stupidedi/exceptions/invalid_schema_error.rb +7 -0
- data/lib/stupidedi/exceptions/missing_grammar_error.rb +50 -0
- data/lib/stupidedi/exceptions/output_error.rb +7 -0
- data/lib/stupidedi/exceptions/parse_error.rb +7 -0
- data/lib/stupidedi/exceptions/stupidedi_error.rb +7 -0
- data/lib/stupidedi/exceptions/tokenize_error.rb +7 -0
- data/lib/stupidedi/exceptions/zipper_error.rb +7 -0
- data/lib/stupidedi/exceptions.rb +13 -0
- data/lib/stupidedi/inspect.rb +26 -0
- data/lib/stupidedi/interchanges/element_types/separator_val.rb +79 -0
- data/lib/stupidedi/interchanges/element_types/special_val.rb +48 -0
- data/lib/stupidedi/interchanges/element_types.rb +11 -0
- data/lib/stupidedi/interchanges.rb +21 -0
- data/lib/stupidedi/parser/builder_dsl.rb +292 -0
- data/lib/stupidedi/parser/constraint_table.rb +566 -0
- data/lib/stupidedi/parser/generation.rb +147 -0
- data/lib/stupidedi/parser/identifier_stack.rb +248 -0
- data/lib/stupidedi/parser/instruction.rb +112 -0
- data/lib/stupidedi/parser/instruction_table.rb +222 -0
- data/lib/stupidedi/parser/navigation.rb +786 -0
- data/lib/stupidedi/parser/state_machine.rb +62 -0
- data/lib/stupidedi/parser/states/abstract_state.rb +385 -0
- data/lib/stupidedi/parser/states/failure_state.rb +69 -0
- data/lib/stupidedi/parser/states/functional_group_state.rb +101 -0
- data/lib/stupidedi/parser/states/initial_state.rb +62 -0
- data/lib/stupidedi/parser/states/interchange_state.rb +96 -0
- data/lib/stupidedi/parser/states/loop_state.rb +74 -0
- data/lib/stupidedi/parser/states/table_state.rb +90 -0
- data/lib/stupidedi/parser/states/transaction_set_state.rb +120 -0
- data/lib/stupidedi/parser/states/transmission_state.rb +59 -0
- data/lib/stupidedi/parser/tokenization.rb +195 -0
- data/lib/stupidedi/parser.rb +32 -0
- data/lib/stupidedi/reader/input/abstract_input.rb +137 -0
- data/lib/stupidedi/reader/input/delegated_input.rb +112 -0
- data/lib/stupidedi/reader/input/file_input.rb +157 -0
- data/lib/stupidedi/reader/input.rb +31 -0
- data/lib/stupidedi/reader/position.rb +78 -0
- data/lib/stupidedi/reader/result.rb +172 -0
- data/lib/stupidedi/reader/segment_dict.rb +176 -0
- data/lib/stupidedi/reader/separators.rb +90 -0
- data/lib/stupidedi/reader/stream_reader.rb +173 -0
- data/lib/stupidedi/reader/token_reader.rb +465 -0
- data/lib/stupidedi/reader/tokens/component_element_tok.rb +71 -0
- data/lib/stupidedi/reader/tokens/composite_element_tok.rb +85 -0
- data/lib/stupidedi/reader/tokens/repeated_element_tok.rb +74 -0
- data/lib/stupidedi/reader/tokens/segment_tok.rb +74 -0
- data/lib/stupidedi/reader/tokens/simple_element_tok.rb +76 -0
- data/lib/stupidedi/reader.rb +121 -0
- data/lib/stupidedi/schema/abstract_def.rb +76 -0
- data/lib/stupidedi/schema/abstract_element_def.rb +35 -0
- data/lib/stupidedi/schema/abstract_element_use.rb +47 -0
- data/lib/stupidedi/schema/abstract_use.rb +79 -0
- data/lib/stupidedi/schema/code_list.rb +99 -0
- data/lib/stupidedi/schema/component_element_use.rb +76 -0
- data/lib/stupidedi/schema/composite_element_def.rb +103 -0
- data/lib/stupidedi/schema/composite_element_use.rb +78 -0
- data/lib/stupidedi/schema/element_req.rb +57 -0
- data/lib/stupidedi/schema/functional_group_def.rb +124 -0
- data/lib/stupidedi/schema/generation/definition_generator.rb +139 -0
- data/lib/stupidedi/schema/generation/element_generator.rb +221 -0
- data/lib/stupidedi/schema/generation/flat_file_reader.rb +551 -0
- data/lib/stupidedi/schema/generation/functional_group_generator.rb +64 -0
- data/lib/stupidedi/schema/generation/interchange_generator.rb +145 -0
- data/lib/stupidedi/schema/generation/master_loader_generator.rb +121 -0
- data/lib/stupidedi/schema/generation/models.rb +85 -0
- data/lib/stupidedi/schema/generation/module_loader_generator.rb +64 -0
- data/lib/stupidedi/schema/generation/registration_generator.rb +230 -0
- data/lib/stupidedi/schema/generation/runner.rb +161 -0
- data/lib/stupidedi/schema/generation/segment_generator.rb +130 -0
- data/lib/stupidedi/schema/generation/support.rb +78 -0
- data/lib/stupidedi/schema/generation/support_modules_generator.rb +126 -0
- data/lib/stupidedi/schema/generation/version_modules.rb +35 -0
- data/lib/stupidedi/schema/generation.rb +94 -0
- data/lib/stupidedi/schema/interchange_def.rb +103 -0
- data/lib/stupidedi/schema/loop_def.rb +156 -0
- data/lib/stupidedi/schema/repeat_count.rb +86 -0
- data/lib/stupidedi/schema/segment_def.rb +122 -0
- data/lib/stupidedi/schema/segment_req.rb +46 -0
- data/lib/stupidedi/schema/segment_use.rb +99 -0
- data/lib/stupidedi/schema/simple_element_def.rb +51 -0
- data/lib/stupidedi/schema/simple_element_use.rb +83 -0
- data/lib/stupidedi/schema/syntax_note.rb +52 -0
- data/lib/stupidedi/schema/table_def.rb +178 -0
- data/lib/stupidedi/schema/transaction_set_def.rb +125 -0
- data/lib/stupidedi/schema.rb +30 -0
- data/lib/stupidedi/sets.rb +42 -0
- data/lib/stupidedi/transaction_sets/builder/dsl.rb +192 -0
- data/lib/stupidedi/transaction_sets/builder.rb +188 -0
- data/lib/stupidedi/transaction_sets/common/implementations/element_reqs.rb +37 -0
- data/lib/stupidedi/transaction_sets/common/implementations/segment_reqs.rb +31 -0
- data/lib/stupidedi/transaction_sets/common/implementations.rb +11 -0
- data/lib/stupidedi/transaction_sets/common.rb +8 -0
- data/lib/stupidedi/transaction_sets/validation/ambiguity.rb +395 -0
- data/lib/stupidedi/transaction_sets/validation/implementation.rb +12 -0
- data/lib/stupidedi/transaction_sets/validation.rb +9 -0
- data/lib/stupidedi/transaction_sets.rb +25 -0
- data/lib/stupidedi/values/abstract_element_val.rb +19 -0
- data/lib/stupidedi/values/abstract_val.rb +133 -0
- data/lib/stupidedi/values/composite_element_val.rb +102 -0
- data/lib/stupidedi/values/functional_group_val.rb +105 -0
- data/lib/stupidedi/values/interchange_val.rb +102 -0
- data/lib/stupidedi/values/invalid_envelope_val.rb +61 -0
- data/lib/stupidedi/values/invalid_segment_val.rb +89 -0
- data/lib/stupidedi/values/loop_val.rb +73 -0
- data/lib/stupidedi/values/repeated_element_val.rb +113 -0
- data/lib/stupidedi/values/segment_val.rb +105 -0
- data/lib/stupidedi/values/segment_val_group.rb +20 -0
- data/lib/stupidedi/values/simple_element_val.rb +80 -0
- data/lib/stupidedi/values/table_val.rb +69 -0
- data/lib/stupidedi/values/transaction_set_val.rb +69 -0
- data/lib/stupidedi/values/transmission_val.rb +56 -0
- data/lib/stupidedi/values.rb +22 -0
- data/lib/stupidedi/version.rb +4 -0
- data/lib/stupidedi/versions/common/element_reqs.rb +13 -0
- data/lib/stupidedi/versions/common/element_types/an.rb +386 -0
- data/lib/stupidedi/versions/common/element_types/dt.rb +572 -0
- data/lib/stupidedi/versions/common/element_types/id.rb +304 -0
- data/lib/stupidedi/versions/common/element_types/nn.rb +312 -0
- data/lib/stupidedi/versions/common/element_types/operators.rb +128 -0
- data/lib/stupidedi/versions/common/element_types/r.rb +342 -0
- data/lib/stupidedi/versions/common/element_types/simple_element.rb +73 -0
- data/lib/stupidedi/versions/common/element_types/tm.rb +347 -0
- data/lib/stupidedi/versions/common/element_types.rb +29 -0
- data/lib/stupidedi/versions/common/segment_reqs.rb +15 -0
- data/lib/stupidedi/versions/common/syntax_notes.rb +172 -0
- data/lib/stupidedi/versions/common.rb +11 -0
- data/lib/stupidedi/versions.rb +25 -0
- data/lib/stupidedi/writer/claredi.rb +178 -0
- data/lib/stupidedi/writer/default.rb +119 -0
- data/lib/stupidedi/writer.rb +7 -0
- data/lib/stupidedi/zipper/abstract_cursor.rb +351 -0
- data/lib/stupidedi/zipper/dangling_cursor.rb +103 -0
- data/lib/stupidedi/zipper/edited_cursor.rb +157 -0
- data/lib/stupidedi/zipper/memoized_cursor.rb +133 -0
- data/lib/stupidedi/zipper/path.rb +132 -0
- data/lib/stupidedi/zipper/root_cursor.rb +120 -0
- data/lib/stupidedi/zipper/stack_cursor.rb +107 -0
- data/lib/stupidedi/zipper.rb +45 -0
- data/lib/stupidedi.rb +69 -0
- data/lib/tediparse.rb +1 -0
- metadata +249 -0
|
@@ -0,0 +1,465 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
module Stupidedi
|
|
3
|
+
using Refinements
|
|
4
|
+
|
|
5
|
+
module Reader
|
|
6
|
+
class TokenReader
|
|
7
|
+
# @private
|
|
8
|
+
SEGMENT_ID = /\A[A-Z][A-Z0-9]{1,2}\Z/
|
|
9
|
+
|
|
10
|
+
include Inspect
|
|
11
|
+
|
|
12
|
+
# @return [String, Input]
|
|
13
|
+
attr_reader :input
|
|
14
|
+
|
|
15
|
+
# @return [Separators]
|
|
16
|
+
attr_reader :separators
|
|
17
|
+
|
|
18
|
+
# @return [SegmentDict]
|
|
19
|
+
attr_accessor :segment_dict
|
|
20
|
+
|
|
21
|
+
def initialize(input, separators, segment_dict = SegmentDict.empty)
|
|
22
|
+
@input, @separators, @segment_dict =
|
|
23
|
+
input, separators, segment_dict
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# @return [TokenReader]
|
|
27
|
+
def copy(changes = {})
|
|
28
|
+
TokenReader.new \
|
|
29
|
+
changes.fetch(:input, @input),
|
|
30
|
+
changes.fetch(:separators, @separators),
|
|
31
|
+
changes.fetch(:segment_dict, @segment_dict)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# @return false
|
|
35
|
+
def stream?
|
|
36
|
+
false
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# @return [StreamReader]
|
|
40
|
+
def stream
|
|
41
|
+
StreamReader.new(@input)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def empty?
|
|
45
|
+
@input.empty?
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# If `s` is a prefix of {#input}, then `s` is skipped and the remaining
|
|
49
|
+
# input is returned as a new `TokenReader` wrapped by `Either.success`.
|
|
50
|
+
# Otherwise, an {Either::Failure} is returned.
|
|
51
|
+
#
|
|
52
|
+
# @return [Either<TokenReader>]
|
|
53
|
+
def consume_prefix(s)
|
|
54
|
+
return success(self) if s.empty?
|
|
55
|
+
|
|
56
|
+
position = 0
|
|
57
|
+
buffer = ""
|
|
58
|
+
|
|
59
|
+
while @input.defined_at?(position)
|
|
60
|
+
character = @input.at(position)
|
|
61
|
+
position += 1
|
|
62
|
+
|
|
63
|
+
unless is_control?(character)
|
|
64
|
+
buffer = buffer + character
|
|
65
|
+
|
|
66
|
+
if s.length == buffer.length
|
|
67
|
+
if s == buffer
|
|
68
|
+
return success(advance(position))
|
|
69
|
+
else
|
|
70
|
+
return failure("found #{buffer.inspect} instead of #{s.inspect}")
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
failure("reached end of input without finding #{s.inspect}")
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def consume_control_chars
|
|
80
|
+
position = 0
|
|
81
|
+
|
|
82
|
+
while @input.defined_at?(position) and is_control?(@input.at(position))
|
|
83
|
+
position += 1
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
if position.zero?
|
|
87
|
+
success(self)
|
|
88
|
+
else
|
|
89
|
+
success(advance(position))
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
# If `s` occurs within {#input}, then the input up to and including `s`
|
|
94
|
+
# is skipped and the remaining input is returned as a new `TokenReader`
|
|
95
|
+
# wrapped by `Either.success`. Otherwise, {Either::Failure} is returned.
|
|
96
|
+
#
|
|
97
|
+
# @return [Either<TokenReader>]
|
|
98
|
+
def consume(s)
|
|
99
|
+
return success(self) if s.empty?
|
|
100
|
+
|
|
101
|
+
position = 0
|
|
102
|
+
buffer = " " * s.length
|
|
103
|
+
|
|
104
|
+
while @input.defined_at?(position)
|
|
105
|
+
character = @input.at(position)
|
|
106
|
+
|
|
107
|
+
unless is_control?(character)
|
|
108
|
+
# Slide the "window" forward one character
|
|
109
|
+
buffer = buffer.slice(1..-1) + character
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
position += 1
|
|
113
|
+
|
|
114
|
+
if s == buffer
|
|
115
|
+
return success(advance(position))
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
failure("reached end of input without finding #{s.inspect}")
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
# Returns a single character and the remaining input as a {Result} with
|
|
123
|
+
# a `value` of the character and a `remainder` of the reamining input as
|
|
124
|
+
# a new instance of {TokenReader}. If {#input} has less than a single
|
|
125
|
+
# character, returns an {Either::Failure}
|
|
126
|
+
#
|
|
127
|
+
# @return [Either<Result<String>>]
|
|
128
|
+
def read_character
|
|
129
|
+
position = 0
|
|
130
|
+
|
|
131
|
+
while @input.defined_at?(position)
|
|
132
|
+
character = @input.at(position)
|
|
133
|
+
position += 1
|
|
134
|
+
|
|
135
|
+
if is_control?(character)
|
|
136
|
+
next
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
return result(character, advance(position))
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
failure("less than one character available")
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
# @return [Either<Result<SegmentTok, TokenReader>>]
|
|
146
|
+
def read_segment
|
|
147
|
+
consume_control_chars.flatmap do |start|
|
|
148
|
+
# We might start reading a segment at "\nNM1...", where the "\n" is on
|
|
149
|
+
# line 5, but "NM1" is on line 6. So to ensure the segment position is
|
|
150
|
+
# line 6, we start with consume_control_characters.
|
|
151
|
+
start.read_segment_id.flatmap do |segment_id, aR|
|
|
152
|
+
if @segment_dict.defined_at?(segment_id)
|
|
153
|
+
element_uses = @segment_dict.at(segment_id).element_uses
|
|
154
|
+
else
|
|
155
|
+
element_uses = []
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
aR.read_delimiter.flatmap do |delim, bR|
|
|
159
|
+
case delim
|
|
160
|
+
when @separators.element
|
|
161
|
+
rest = bR.read_elements(segment_id, element_uses)
|
|
162
|
+
rest.map{|es, cR| segment(segment_id, start.input, cR.input, es) }
|
|
163
|
+
when @separators.segment
|
|
164
|
+
remainder =
|
|
165
|
+
if segment_id == :IEA
|
|
166
|
+
bR.stream
|
|
167
|
+
else
|
|
168
|
+
bR
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
# Consume the segment terminator
|
|
172
|
+
result(segment(segment_id, start.input, bR.input), remainder)
|
|
173
|
+
end
|
|
174
|
+
end
|
|
175
|
+
end
|
|
176
|
+
end
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
# @return [Either<Result<Array<SimpleElementTok, CompositeElementTok>, TokenReader>>]
|
|
180
|
+
def read_elements(segment_id, element_uses)
|
|
181
|
+
if element_uses.empty?
|
|
182
|
+
read_simple_element
|
|
183
|
+
else
|
|
184
|
+
element_use = element_uses.head
|
|
185
|
+
repeatable = element_use.repeatable?
|
|
186
|
+
|
|
187
|
+
if element_use.composite?
|
|
188
|
+
read_composite_element(repeatable)
|
|
189
|
+
else
|
|
190
|
+
read_simple_element(repeatable)
|
|
191
|
+
end
|
|
192
|
+
end.flatmap do |element, aR|
|
|
193
|
+
aR.read_delimiter.flatmap do |delim, bR|
|
|
194
|
+
case delim
|
|
195
|
+
when @separators.segment
|
|
196
|
+
remainder =
|
|
197
|
+
if segment_id == :IEA
|
|
198
|
+
bR.stream
|
|
199
|
+
else
|
|
200
|
+
bR
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
# This is the last element before the segment terminator, make
|
|
204
|
+
# it into a singleton list and _do_ consume the delimiter
|
|
205
|
+
result(element.cons, remainder)
|
|
206
|
+
when @separators.element
|
|
207
|
+
# There is another element following the delimiter
|
|
208
|
+
rest = bR.read_elements(segment_id, element_uses.tail)
|
|
209
|
+
rest.map{|es, _| element.cons(es) }
|
|
210
|
+
end
|
|
211
|
+
end
|
|
212
|
+
end
|
|
213
|
+
end
|
|
214
|
+
|
|
215
|
+
# @return [Either<Result<Array<ComponentElementTok, TokenReader>>>]
|
|
216
|
+
def read_component_elements(repeatable = false)
|
|
217
|
+
read_component_element(repeatable).flatmap do |component, aR|
|
|
218
|
+
aR.read_delimiter.flatmap do |delim, bR|
|
|
219
|
+
case delim
|
|
220
|
+
when @separators.segment,
|
|
221
|
+
@separators.element,
|
|
222
|
+
@separators.repetition
|
|
223
|
+
# This is the last component element within the composite element,
|
|
224
|
+
# so make it into a singleton list and don't consume the delimiter
|
|
225
|
+
result(component.cons, aR)
|
|
226
|
+
when @separators.component
|
|
227
|
+
rest = bR.read_component_elements(repeatable)
|
|
228
|
+
rest.map{|es, _| component.cons(es) }
|
|
229
|
+
end
|
|
230
|
+
end
|
|
231
|
+
end
|
|
232
|
+
end
|
|
233
|
+
|
|
234
|
+
# @return [Either<Result<Symbol, TokenReader>>]
|
|
235
|
+
def read_segment_id
|
|
236
|
+
position = 0
|
|
237
|
+
buffer = ""
|
|
238
|
+
|
|
239
|
+
while true
|
|
240
|
+
unless @input.defined_at?(position)
|
|
241
|
+
return eof("reached end of input without finding a segment identifier")
|
|
242
|
+
end
|
|
243
|
+
|
|
244
|
+
character = @input.at(position)
|
|
245
|
+
position += 1
|
|
246
|
+
|
|
247
|
+
if is_delimiter?(character)
|
|
248
|
+
break
|
|
249
|
+
end
|
|
250
|
+
|
|
251
|
+
unless is_control?(character)
|
|
252
|
+
if buffer.length == 3
|
|
253
|
+
break
|
|
254
|
+
end
|
|
255
|
+
|
|
256
|
+
buffer = buffer + character
|
|
257
|
+
end
|
|
258
|
+
end
|
|
259
|
+
|
|
260
|
+
# We only arrive here if {character} is a delimiter, or if we read
|
|
261
|
+
# three characters into {buffer} and an additional into {character}
|
|
262
|
+
if buffer =~ SEGMENT_ID
|
|
263
|
+
remainder = advance(position - 1)
|
|
264
|
+
|
|
265
|
+
case character
|
|
266
|
+
when @separators.segment,
|
|
267
|
+
@separators.element
|
|
268
|
+
# Don't consume the delimiter
|
|
269
|
+
result(buffer.upcase.to_sym, remainder)
|
|
270
|
+
else
|
|
271
|
+
failure("found #{character.inspect} following segment identifier")
|
|
272
|
+
end
|
|
273
|
+
else
|
|
274
|
+
failure("found #{(buffer + character).inspect} instead of segment identifier")
|
|
275
|
+
end
|
|
276
|
+
end
|
|
277
|
+
|
|
278
|
+
# @return [Either<Result<Character, TokenReader>>]
|
|
279
|
+
def read_delimiter
|
|
280
|
+
position = 0
|
|
281
|
+
|
|
282
|
+
while @input.defined_at?(position)
|
|
283
|
+
character = @input.at(position)
|
|
284
|
+
position += 1
|
|
285
|
+
|
|
286
|
+
if is_control?(character)
|
|
287
|
+
next
|
|
288
|
+
end
|
|
289
|
+
|
|
290
|
+
if is_delimiter?(character)
|
|
291
|
+
return result(character, advance(position))
|
|
292
|
+
else
|
|
293
|
+
return failure("found #{character.inspect} instead of a delimiter")
|
|
294
|
+
end
|
|
295
|
+
end
|
|
296
|
+
|
|
297
|
+
failure("reached end of input without finding a delimiter")
|
|
298
|
+
end
|
|
299
|
+
|
|
300
|
+
# @return [Either<Result<SimpleElementToken, TokenReader>>]
|
|
301
|
+
def read_simple_element(repeatable = false)
|
|
302
|
+
position = 0
|
|
303
|
+
buffer = ""
|
|
304
|
+
|
|
305
|
+
while @input.defined_at?(position)
|
|
306
|
+
character = @input.at(position)
|
|
307
|
+
position += 1
|
|
308
|
+
|
|
309
|
+
if is_control?(character)
|
|
310
|
+
next
|
|
311
|
+
end
|
|
312
|
+
|
|
313
|
+
case character
|
|
314
|
+
when @separators.segment,
|
|
315
|
+
@separators.element
|
|
316
|
+
# These delimiters mark the end of the element. We don't consume
|
|
317
|
+
# the delimiter because the next reader can use the delimiter to
|
|
318
|
+
# know which token to next expect.
|
|
319
|
+
token = simple(buffer, @input, @input.drop(position))
|
|
320
|
+
token = token.repeated if repeatable
|
|
321
|
+
return result(token, advance(position - 1))
|
|
322
|
+
when @separators.repetition
|
|
323
|
+
if repeatable
|
|
324
|
+
token = simple(buffer, @input, @input.drop(position))
|
|
325
|
+
rest = advance(position).read_simple_element(repeatable)
|
|
326
|
+
return rest.map{|e, _| e.repeated(token) }
|
|
327
|
+
# else
|
|
328
|
+
# # @todo: Read this as data but sound the alarms
|
|
329
|
+
end
|
|
330
|
+
# when @separators.component
|
|
331
|
+
# # @todo: Read this as data but sound the alarms
|
|
332
|
+
end
|
|
333
|
+
|
|
334
|
+
buffer = buffer + character
|
|
335
|
+
end
|
|
336
|
+
|
|
337
|
+
failure("reached end of input without finding a simple data element")
|
|
338
|
+
end
|
|
339
|
+
|
|
340
|
+
# @return [Either<Result<ComponentElementTok, TokenReader>>]
|
|
341
|
+
def read_component_element(repeatable = false)
|
|
342
|
+
position = 0
|
|
343
|
+
buffer = ""
|
|
344
|
+
|
|
345
|
+
while @input.defined_at?(position)
|
|
346
|
+
character = @input.at(position)
|
|
347
|
+
position += 1
|
|
348
|
+
|
|
349
|
+
if is_control?(character)
|
|
350
|
+
next
|
|
351
|
+
end
|
|
352
|
+
|
|
353
|
+
case character
|
|
354
|
+
when @separators.element,
|
|
355
|
+
@separators.segment,
|
|
356
|
+
@separators.component
|
|
357
|
+
# Don't consume the separator/terminator
|
|
358
|
+
token = component(buffer, @input, @input.drop(position))
|
|
359
|
+
return result(token, advance(position - 1))
|
|
360
|
+
when @separators.repetition
|
|
361
|
+
if repeatable
|
|
362
|
+
# Don't consume the repetition separator
|
|
363
|
+
token = component(buffer, @input, @input.drop(position))
|
|
364
|
+
return result(token, advance(position - 1))
|
|
365
|
+
# else
|
|
366
|
+
# # @todo: Read this as data but sound the alarms
|
|
367
|
+
end
|
|
368
|
+
end
|
|
369
|
+
|
|
370
|
+
buffer = buffer + character
|
|
371
|
+
end
|
|
372
|
+
|
|
373
|
+
failure("reached end of input without finding a component data element")
|
|
374
|
+
end
|
|
375
|
+
|
|
376
|
+
# @return [Either<Result<CompositeElementTok, TokenReader>>]
|
|
377
|
+
def read_composite_element(repeatable = false)
|
|
378
|
+
read_component_elements(repeatable).flatmap do |components, aR|
|
|
379
|
+
token = composite(components, @input, aR.input)
|
|
380
|
+
|
|
381
|
+
aR.read_delimiter.flatmap do |delim, bR|
|
|
382
|
+
case delim
|
|
383
|
+
when @separators.segment,
|
|
384
|
+
@separators.element
|
|
385
|
+
token = token.repeated if repeatable
|
|
386
|
+
result(token, aR)
|
|
387
|
+
when @separators.repetition
|
|
388
|
+
bR.read_composite_element(repeatable).map do |c, cR|
|
|
389
|
+
c.repeated(token)
|
|
390
|
+
end
|
|
391
|
+
end
|
|
392
|
+
end
|
|
393
|
+
end
|
|
394
|
+
end
|
|
395
|
+
|
|
396
|
+
# @return [void]
|
|
397
|
+
def pretty_print(q)
|
|
398
|
+
q.text("TokenReader")
|
|
399
|
+
q.group(2, "(", ")") do
|
|
400
|
+
q.breakable ""
|
|
401
|
+
|
|
402
|
+
q.pp @input
|
|
403
|
+
q.text ","
|
|
404
|
+
q.breakable
|
|
405
|
+
|
|
406
|
+
q.pp @separators
|
|
407
|
+
end
|
|
408
|
+
end
|
|
409
|
+
|
|
410
|
+
private
|
|
411
|
+
|
|
412
|
+
# @return [TokenReader]
|
|
413
|
+
def advance(n)
|
|
414
|
+
unless @input.defined_at?(n-1)
|
|
415
|
+
raise IndexError, "less than #{n} characters available"
|
|
416
|
+
else
|
|
417
|
+
TokenReader.new(@input.drop(n), @separators, @segment_dict)
|
|
418
|
+
end
|
|
419
|
+
end
|
|
420
|
+
|
|
421
|
+
def is_delimiter?(character)
|
|
422
|
+
character == @separators.segment or
|
|
423
|
+
character == @separators.element or
|
|
424
|
+
character == @separators.component or
|
|
425
|
+
character == @separators.repetition
|
|
426
|
+
end
|
|
427
|
+
|
|
428
|
+
def is_control?(character)
|
|
429
|
+
Reader.is_control_character?(character) and not is_delimiter?(character)
|
|
430
|
+
end
|
|
431
|
+
|
|
432
|
+
def failure(message, remainder = @input)
|
|
433
|
+
Result.failure(message, remainder, true)
|
|
434
|
+
end
|
|
435
|
+
|
|
436
|
+
def eof(message, remainder = @input)
|
|
437
|
+
Result.failure(message, remainder, false)
|
|
438
|
+
end
|
|
439
|
+
|
|
440
|
+
def success(value)
|
|
441
|
+
Either.success(value)
|
|
442
|
+
end
|
|
443
|
+
|
|
444
|
+
def result(value, remainder)
|
|
445
|
+
Result.success(value, remainder)
|
|
446
|
+
end
|
|
447
|
+
|
|
448
|
+
def segment(segment_id, input, remainder, elements = [])
|
|
449
|
+
SegmentTok.build(segment_id, elements, input.position, remainder.position)
|
|
450
|
+
end
|
|
451
|
+
|
|
452
|
+
def simple(value, input, remainder)
|
|
453
|
+
SimpleElementTok.build(value, input.position, remainder.position)
|
|
454
|
+
end
|
|
455
|
+
|
|
456
|
+
def component(value, input, remainder)
|
|
457
|
+
ComponentElementTok.build(value, input.position, remainder.position)
|
|
458
|
+
end
|
|
459
|
+
|
|
460
|
+
def composite(value, input, remainder)
|
|
461
|
+
CompositeElementTok.build(value, input.position, remainder.position)
|
|
462
|
+
end
|
|
463
|
+
end
|
|
464
|
+
end
|
|
465
|
+
end
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
module Stupidedi
|
|
3
|
+
using Refinements
|
|
4
|
+
|
|
5
|
+
module Reader
|
|
6
|
+
class ComponentElementTok
|
|
7
|
+
include Inspect
|
|
8
|
+
|
|
9
|
+
# @return [String]
|
|
10
|
+
attr_reader :value
|
|
11
|
+
|
|
12
|
+
# @return [Position]
|
|
13
|
+
attr_reader :position
|
|
14
|
+
|
|
15
|
+
# @return [Position]
|
|
16
|
+
attr_reader :remainder
|
|
17
|
+
|
|
18
|
+
def initialize(value, position, remainder)
|
|
19
|
+
@value, @position, @remainder =
|
|
20
|
+
value, position, remainder
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# @return [CompositeElementTok]
|
|
24
|
+
def copy(changes = {})
|
|
25
|
+
ComponentElementTok.new \
|
|
26
|
+
changes.fetch(:value, @value),
|
|
27
|
+
changes.fetch(:position, @position),
|
|
28
|
+
changes.fetch(:remainder, @remainder)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# :nocov:
|
|
32
|
+
def pretty_print(q)
|
|
33
|
+
q.pp(:component.cons(@value.cons))
|
|
34
|
+
end
|
|
35
|
+
# :nocov:
|
|
36
|
+
|
|
37
|
+
def blank?
|
|
38
|
+
@value.blank?
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def present?
|
|
42
|
+
not blank?
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def simple?
|
|
46
|
+
true
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def composite?
|
|
50
|
+
false
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def to_x12(separators)
|
|
54
|
+
@value.to_s
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
class << ComponentElementTok
|
|
59
|
+
# @group Constructors
|
|
60
|
+
#########################################################################
|
|
61
|
+
|
|
62
|
+
# @return [ComponentElementTok]
|
|
63
|
+
def build(value, position, remainder)
|
|
64
|
+
new(value, position, remainder)
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
# @endgroup
|
|
68
|
+
#########################################################################
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
module Stupidedi
|
|
3
|
+
using Refinements
|
|
4
|
+
|
|
5
|
+
module Reader
|
|
6
|
+
class CompositeElementTok
|
|
7
|
+
include Inspect
|
|
8
|
+
|
|
9
|
+
# @return [Array<ComponentElementTok>]
|
|
10
|
+
attr_reader :component_toks
|
|
11
|
+
|
|
12
|
+
# @return [Position]
|
|
13
|
+
attr_reader :position
|
|
14
|
+
|
|
15
|
+
# @return [Position]
|
|
16
|
+
attr_reader :remainder
|
|
17
|
+
|
|
18
|
+
def initialize(component_toks, position, remainder)
|
|
19
|
+
@component_toks, @position, @remainder =
|
|
20
|
+
component_toks, position, remainder
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# @return [CompositeElementTok]
|
|
24
|
+
def copy(changes = {})
|
|
25
|
+
CompositeElementTok.new \
|
|
26
|
+
changes.fetch(:component_toks, @component_toks),
|
|
27
|
+
changes.fetch(:position, @position),
|
|
28
|
+
changes.fetch(:remainder, @remainder)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# :nocov:
|
|
32
|
+
def pretty_print(q)
|
|
33
|
+
q.pp(:composite.cons(@component_toks))
|
|
34
|
+
end
|
|
35
|
+
# :nocov:
|
|
36
|
+
|
|
37
|
+
def repeated
|
|
38
|
+
RepeatedElementTok.new(self.cons, @position)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def repeated?
|
|
42
|
+
false
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def blank?
|
|
46
|
+
@component_toks.all?(&:blank?)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def present?
|
|
50
|
+
not blank?
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def simple?
|
|
54
|
+
false
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def composite?
|
|
58
|
+
true
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def to_x12(separators)
|
|
62
|
+
if blank?
|
|
63
|
+
""
|
|
64
|
+
else
|
|
65
|
+
cs = @component_toks.map{|x| x.to_x12(separators) }
|
|
66
|
+
sep = separators.component || ":"
|
|
67
|
+
cs.join(sep).gsub(/#{Regexp.escape(sep)}+$/, "")
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
class << CompositeElementTok
|
|
73
|
+
# @group Constructors
|
|
74
|
+
#########################################################################
|
|
75
|
+
|
|
76
|
+
# @return [CompositeElementTok]
|
|
77
|
+
def build(component_toks, position, remainder)
|
|
78
|
+
new(component_toks, position, remainder)
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
# @endgroup
|
|
82
|
+
#########################################################################
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
end
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
module Stupidedi
|
|
3
|
+
using Refinements
|
|
4
|
+
|
|
5
|
+
module Reader
|
|
6
|
+
class RepeatedElementTok
|
|
7
|
+
include Inspect
|
|
8
|
+
|
|
9
|
+
# @return [Array<CompositeElementTok>]
|
|
10
|
+
# @return [Array<SimpleElementTok>]
|
|
11
|
+
attr_reader :element_toks
|
|
12
|
+
|
|
13
|
+
attr_reader :position
|
|
14
|
+
|
|
15
|
+
def_delegators "element_toks.last", :remainder
|
|
16
|
+
|
|
17
|
+
def initialize(element_toks, position)
|
|
18
|
+
@element_toks, @position =
|
|
19
|
+
element_toks, position
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def repeated(element_tok)
|
|
23
|
+
@element_toks.unshift(element_tok)
|
|
24
|
+
self
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def pretty_print(q)
|
|
28
|
+
q.pp(:repeated.cons(@element_toks))
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def simple?
|
|
32
|
+
false
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def repeated?
|
|
36
|
+
true
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def blank?
|
|
40
|
+
@element_toks.all?(&:blank?)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def present?
|
|
44
|
+
not blank?
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def composite?
|
|
48
|
+
false
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def to_x12(separators)
|
|
52
|
+
if blank?
|
|
53
|
+
""
|
|
54
|
+
else
|
|
55
|
+
rs = @element_toks.map{|x| x.to_x12(separators) }
|
|
56
|
+
rs.join(separators.repetition || "^")
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
class << RepeatedElementTok
|
|
62
|
+
# @group Constructors
|
|
63
|
+
#########################################################################
|
|
64
|
+
|
|
65
|
+
# @return [RepeatedElementTok]
|
|
66
|
+
def build(element_toks, position)
|
|
67
|
+
new(element_toks, position)
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
# @endgroup
|
|
71
|
+
#########################################################################
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
end
|