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.
Files changed (176) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +319 -0
  3. data/LICENSE +26 -0
  4. data/README.md +308 -0
  5. data/Rakefile +33 -0
  6. data/bin/tediparse +102 -0
  7. data/doc/Defining.md +97 -0
  8. data/doc/Generating-Grammars.md +241 -0
  9. data/doc/Generating.md +332 -0
  10. data/doc/Navigating.md +670 -0
  11. data/doc/Parsing.md +133 -0
  12. data/doc/README.md +51 -0
  13. data/doc/Serializing.md +100 -0
  14. data/doc/Tokenizing.md +136 -0
  15. data/doc/Validating.md +140 -0
  16. data/lib/ruby/array.rb +226 -0
  17. data/lib/ruby/blank.rb +52 -0
  18. data/lib/ruby/exception.rb +14 -0
  19. data/lib/ruby/hash.rb +14 -0
  20. data/lib/ruby/module.rb +60 -0
  21. data/lib/ruby/object.rb +56 -0
  22. data/lib/ruby/string.rb +89 -0
  23. data/lib/ruby/to_d.rb +82 -0
  24. data/lib/ruby/to_date.rb +26 -0
  25. data/lib/ruby/to_time.rb +21 -0
  26. data/lib/ruby/try.rb +46 -0
  27. data/lib/stupidedi/builder.rb +6 -0
  28. data/lib/stupidedi/color.rb +94 -0
  29. data/lib/stupidedi/config/code_list_config.rb +45 -0
  30. data/lib/stupidedi/config/functional_group_config.rb +72 -0
  31. data/lib/stupidedi/config/interchange_config.rb +86 -0
  32. data/lib/stupidedi/config/transaction_set_config.rb +73 -0
  33. data/lib/stupidedi/config.rb +100 -0
  34. data/lib/stupidedi/either.rb +286 -0
  35. data/lib/stupidedi/exceptions/invalid_element_error.rb +7 -0
  36. data/lib/stupidedi/exceptions/invalid_schema_error.rb +7 -0
  37. data/lib/stupidedi/exceptions/missing_grammar_error.rb +50 -0
  38. data/lib/stupidedi/exceptions/output_error.rb +7 -0
  39. data/lib/stupidedi/exceptions/parse_error.rb +7 -0
  40. data/lib/stupidedi/exceptions/stupidedi_error.rb +7 -0
  41. data/lib/stupidedi/exceptions/tokenize_error.rb +7 -0
  42. data/lib/stupidedi/exceptions/zipper_error.rb +7 -0
  43. data/lib/stupidedi/exceptions.rb +13 -0
  44. data/lib/stupidedi/inspect.rb +26 -0
  45. data/lib/stupidedi/interchanges/element_types/separator_val.rb +79 -0
  46. data/lib/stupidedi/interchanges/element_types/special_val.rb +48 -0
  47. data/lib/stupidedi/interchanges/element_types.rb +11 -0
  48. data/lib/stupidedi/interchanges.rb +21 -0
  49. data/lib/stupidedi/parser/builder_dsl.rb +292 -0
  50. data/lib/stupidedi/parser/constraint_table.rb +566 -0
  51. data/lib/stupidedi/parser/generation.rb +147 -0
  52. data/lib/stupidedi/parser/identifier_stack.rb +248 -0
  53. data/lib/stupidedi/parser/instruction.rb +112 -0
  54. data/lib/stupidedi/parser/instruction_table.rb +222 -0
  55. data/lib/stupidedi/parser/navigation.rb +786 -0
  56. data/lib/stupidedi/parser/state_machine.rb +62 -0
  57. data/lib/stupidedi/parser/states/abstract_state.rb +385 -0
  58. data/lib/stupidedi/parser/states/failure_state.rb +69 -0
  59. data/lib/stupidedi/parser/states/functional_group_state.rb +101 -0
  60. data/lib/stupidedi/parser/states/initial_state.rb +62 -0
  61. data/lib/stupidedi/parser/states/interchange_state.rb +96 -0
  62. data/lib/stupidedi/parser/states/loop_state.rb +74 -0
  63. data/lib/stupidedi/parser/states/table_state.rb +90 -0
  64. data/lib/stupidedi/parser/states/transaction_set_state.rb +120 -0
  65. data/lib/stupidedi/parser/states/transmission_state.rb +59 -0
  66. data/lib/stupidedi/parser/tokenization.rb +195 -0
  67. data/lib/stupidedi/parser.rb +32 -0
  68. data/lib/stupidedi/reader/input/abstract_input.rb +137 -0
  69. data/lib/stupidedi/reader/input/delegated_input.rb +112 -0
  70. data/lib/stupidedi/reader/input/file_input.rb +157 -0
  71. data/lib/stupidedi/reader/input.rb +31 -0
  72. data/lib/stupidedi/reader/position.rb +78 -0
  73. data/lib/stupidedi/reader/result.rb +172 -0
  74. data/lib/stupidedi/reader/segment_dict.rb +176 -0
  75. data/lib/stupidedi/reader/separators.rb +90 -0
  76. data/lib/stupidedi/reader/stream_reader.rb +173 -0
  77. data/lib/stupidedi/reader/token_reader.rb +465 -0
  78. data/lib/stupidedi/reader/tokens/component_element_tok.rb +71 -0
  79. data/lib/stupidedi/reader/tokens/composite_element_tok.rb +85 -0
  80. data/lib/stupidedi/reader/tokens/repeated_element_tok.rb +74 -0
  81. data/lib/stupidedi/reader/tokens/segment_tok.rb +74 -0
  82. data/lib/stupidedi/reader/tokens/simple_element_tok.rb +76 -0
  83. data/lib/stupidedi/reader.rb +121 -0
  84. data/lib/stupidedi/schema/abstract_def.rb +76 -0
  85. data/lib/stupidedi/schema/abstract_element_def.rb +35 -0
  86. data/lib/stupidedi/schema/abstract_element_use.rb +47 -0
  87. data/lib/stupidedi/schema/abstract_use.rb +79 -0
  88. data/lib/stupidedi/schema/code_list.rb +99 -0
  89. data/lib/stupidedi/schema/component_element_use.rb +76 -0
  90. data/lib/stupidedi/schema/composite_element_def.rb +103 -0
  91. data/lib/stupidedi/schema/composite_element_use.rb +78 -0
  92. data/lib/stupidedi/schema/element_req.rb +57 -0
  93. data/lib/stupidedi/schema/functional_group_def.rb +124 -0
  94. data/lib/stupidedi/schema/generation/definition_generator.rb +139 -0
  95. data/lib/stupidedi/schema/generation/element_generator.rb +221 -0
  96. data/lib/stupidedi/schema/generation/flat_file_reader.rb +551 -0
  97. data/lib/stupidedi/schema/generation/functional_group_generator.rb +64 -0
  98. data/lib/stupidedi/schema/generation/interchange_generator.rb +145 -0
  99. data/lib/stupidedi/schema/generation/master_loader_generator.rb +121 -0
  100. data/lib/stupidedi/schema/generation/models.rb +85 -0
  101. data/lib/stupidedi/schema/generation/module_loader_generator.rb +64 -0
  102. data/lib/stupidedi/schema/generation/registration_generator.rb +230 -0
  103. data/lib/stupidedi/schema/generation/runner.rb +161 -0
  104. data/lib/stupidedi/schema/generation/segment_generator.rb +130 -0
  105. data/lib/stupidedi/schema/generation/support.rb +78 -0
  106. data/lib/stupidedi/schema/generation/support_modules_generator.rb +126 -0
  107. data/lib/stupidedi/schema/generation/version_modules.rb +35 -0
  108. data/lib/stupidedi/schema/generation.rb +94 -0
  109. data/lib/stupidedi/schema/interchange_def.rb +103 -0
  110. data/lib/stupidedi/schema/loop_def.rb +156 -0
  111. data/lib/stupidedi/schema/repeat_count.rb +86 -0
  112. data/lib/stupidedi/schema/segment_def.rb +122 -0
  113. data/lib/stupidedi/schema/segment_req.rb +46 -0
  114. data/lib/stupidedi/schema/segment_use.rb +99 -0
  115. data/lib/stupidedi/schema/simple_element_def.rb +51 -0
  116. data/lib/stupidedi/schema/simple_element_use.rb +83 -0
  117. data/lib/stupidedi/schema/syntax_note.rb +52 -0
  118. data/lib/stupidedi/schema/table_def.rb +178 -0
  119. data/lib/stupidedi/schema/transaction_set_def.rb +125 -0
  120. data/lib/stupidedi/schema.rb +30 -0
  121. data/lib/stupidedi/sets.rb +42 -0
  122. data/lib/stupidedi/transaction_sets/builder/dsl.rb +192 -0
  123. data/lib/stupidedi/transaction_sets/builder.rb +188 -0
  124. data/lib/stupidedi/transaction_sets/common/implementations/element_reqs.rb +37 -0
  125. data/lib/stupidedi/transaction_sets/common/implementations/segment_reqs.rb +31 -0
  126. data/lib/stupidedi/transaction_sets/common/implementations.rb +11 -0
  127. data/lib/stupidedi/transaction_sets/common.rb +8 -0
  128. data/lib/stupidedi/transaction_sets/validation/ambiguity.rb +395 -0
  129. data/lib/stupidedi/transaction_sets/validation/implementation.rb +12 -0
  130. data/lib/stupidedi/transaction_sets/validation.rb +9 -0
  131. data/lib/stupidedi/transaction_sets.rb +25 -0
  132. data/lib/stupidedi/values/abstract_element_val.rb +19 -0
  133. data/lib/stupidedi/values/abstract_val.rb +133 -0
  134. data/lib/stupidedi/values/composite_element_val.rb +102 -0
  135. data/lib/stupidedi/values/functional_group_val.rb +105 -0
  136. data/lib/stupidedi/values/interchange_val.rb +102 -0
  137. data/lib/stupidedi/values/invalid_envelope_val.rb +61 -0
  138. data/lib/stupidedi/values/invalid_segment_val.rb +89 -0
  139. data/lib/stupidedi/values/loop_val.rb +73 -0
  140. data/lib/stupidedi/values/repeated_element_val.rb +113 -0
  141. data/lib/stupidedi/values/segment_val.rb +105 -0
  142. data/lib/stupidedi/values/segment_val_group.rb +20 -0
  143. data/lib/stupidedi/values/simple_element_val.rb +80 -0
  144. data/lib/stupidedi/values/table_val.rb +69 -0
  145. data/lib/stupidedi/values/transaction_set_val.rb +69 -0
  146. data/lib/stupidedi/values/transmission_val.rb +56 -0
  147. data/lib/stupidedi/values.rb +22 -0
  148. data/lib/stupidedi/version.rb +4 -0
  149. data/lib/stupidedi/versions/common/element_reqs.rb +13 -0
  150. data/lib/stupidedi/versions/common/element_types/an.rb +386 -0
  151. data/lib/stupidedi/versions/common/element_types/dt.rb +572 -0
  152. data/lib/stupidedi/versions/common/element_types/id.rb +304 -0
  153. data/lib/stupidedi/versions/common/element_types/nn.rb +312 -0
  154. data/lib/stupidedi/versions/common/element_types/operators.rb +128 -0
  155. data/lib/stupidedi/versions/common/element_types/r.rb +342 -0
  156. data/lib/stupidedi/versions/common/element_types/simple_element.rb +73 -0
  157. data/lib/stupidedi/versions/common/element_types/tm.rb +347 -0
  158. data/lib/stupidedi/versions/common/element_types.rb +29 -0
  159. data/lib/stupidedi/versions/common/segment_reqs.rb +15 -0
  160. data/lib/stupidedi/versions/common/syntax_notes.rb +172 -0
  161. data/lib/stupidedi/versions/common.rb +11 -0
  162. data/lib/stupidedi/versions.rb +25 -0
  163. data/lib/stupidedi/writer/claredi.rb +178 -0
  164. data/lib/stupidedi/writer/default.rb +119 -0
  165. data/lib/stupidedi/writer.rb +7 -0
  166. data/lib/stupidedi/zipper/abstract_cursor.rb +351 -0
  167. data/lib/stupidedi/zipper/dangling_cursor.rb +103 -0
  168. data/lib/stupidedi/zipper/edited_cursor.rb +157 -0
  169. data/lib/stupidedi/zipper/memoized_cursor.rb +133 -0
  170. data/lib/stupidedi/zipper/path.rb +132 -0
  171. data/lib/stupidedi/zipper/root_cursor.rb +120 -0
  172. data/lib/stupidedi/zipper/stack_cursor.rb +107 -0
  173. data/lib/stupidedi/zipper.rb +45 -0
  174. data/lib/stupidedi.rb +69 -0
  175. data/lib/tediparse.rb +1 -0
  176. 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