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,137 @@
1
+ # frozen_string_literal: true
2
+ module Stupidedi
3
+ using Refinements
4
+
5
+ module Reader
6
+ #
7
+ # Provides an abstract interface for a positioned cursor within an
8
+ # element-based input stream. The main operations are implemented by the
9
+ # {#take} and {#drop} methods.
10
+ #
11
+ # The {DelegatedInput} subclass wraps values that already implement the
12
+ # interface, like `String` and `Array`. The {FileInput} subclass wraps
13
+ # opened `IO` streams like `File`, and possibly others.
14
+ #
15
+ # @example Reading the input
16
+ # input = Input.build("abc")
17
+ # input.at(0) #=> "a"
18
+ # input.take(3) #=> "abc"
19
+ #
20
+ # input = input.drop(1) #=> #<DelegatedInput ...>
21
+ # input.at(0) #=> "b"
22
+ # input.take(3) #=> "bc"
23
+ #
24
+ # input = input.drop(1) #=> #<DelegatedInput ...>
25
+ # input.at(0) #=> "c"
26
+ # input.take(3) #=> "c"
27
+ #
28
+ # @example Querying the position
29
+ # input = Input.build("abc\ndef\nghi")
30
+ # input.offset #=> 0
31
+ # input.line #=> 1
32
+ # input.column #=> 1
33
+ #
34
+ # input.drop(3).bind do |in|
35
+ # in.offset #=> 3
36
+ # in.line #=> 1
37
+ # in.column #=> 4
38
+ # end
39
+ #
40
+ # input.drop(4).bind do |in|
41
+ # in.offset #=> 4
42
+ # in.line #=> 2
43
+ # in.column #=> 1
44
+ # end
45
+ #
46
+ # @note The `String` and `Array` refinements in `lib/ruby` provide compatible
47
+ # implementations of the abstract methods, so code written to target
48
+ # this interface is backward-compatable with plain unwrapped `String`
49
+ # and `Array` values.
50
+ #
51
+ class AbstractInput
52
+ include Inspect
53
+
54
+ # @group Querying the Position
55
+ ########################################################################
56
+
57
+ # The {Position} value that describes the position of the input stream
58
+ #
59
+ # @return [Position]
60
+ abstract :position
61
+
62
+ # The current position as the number of elements previously read
63
+ #
64
+ # @return [Integer]
65
+ def_delegators :position, :offset
66
+
67
+ # The line of the current position
68
+ #
69
+ # @return [Integer]
70
+
71
+ def_delegators :position, :line
72
+
73
+ # The column of the current position. The column resets to `1` each time
74
+ # a newline is read
75
+ #
76
+ # @return [Integer]
77
+
78
+ def_delegators :position, :column
79
+
80
+ # The file name, URI, etc that identifies the input stream
81
+ #
82
+ # @return [String]
83
+
84
+ def_delegators :position, :path
85
+
86
+ # @group Reading the Input
87
+ ########################################################################
88
+
89
+ # Read the first `n` elements
90
+ #
91
+ # @param [Integer] n number of elements to read (`n >= 0`)
92
+ abstract :take, :args => %w(n)
93
+
94
+ # Read a single element at the given index. Result is undefined unless
95
+ # the input contains enough elements, which can be tested with
96
+ # {#defined_at?}
97
+ #
98
+ # @param [Integer] n the index of the element to read (`n >= 0`)
99
+ abstract :at, :args => %w(n)
100
+
101
+ # Returns the smallest `n`, where {#at}`(n)` == `element`
102
+ #
103
+ # @param [Object] element the element to find in the input
104
+ #
105
+ # @return [Integer]
106
+ # @return nil if `element` is not present in the input
107
+ abstract :index, :args => %w(element)
108
+
109
+ # @group Advancing the Cursor
110
+ ########################################################################
111
+
112
+ # Advance the cursor forward `n` elements
113
+ #
114
+ # @param [Integer] n the number of elements to advance (`n >= 0`)
115
+ #
116
+ # @return [AbstractInput] new object with the remaining input
117
+ abstract :drop, :args => %w(n)
118
+
119
+ # @group Testing the Input
120
+ ########################################################################
121
+
122
+ # True if the input contains enough elements such that {#at}`(n)` is
123
+ # defined
124
+ #
125
+ # @param [Integer] n the index to test (`n >= 0`)
126
+ abstract :defined_at?, :args => %w(n)
127
+
128
+ # True if no elements remain in the input
129
+ abstract :empty?
130
+
131
+ # True if `other` equals the remaining input
132
+ #
133
+ # @return [Boolean]
134
+ abstract :==, :args => %w(other)
135
+ end
136
+ end
137
+ end
@@ -0,0 +1,112 @@
1
+ # frozen_string_literal: true
2
+ module Stupidedi
3
+ using Refinements
4
+
5
+ module Reader
6
+ class DelegatedInput < AbstractInput
7
+ def initialize(delegate, offset = 0, line = 1, column = 1)
8
+ @delegate, @offset, @line, @column =
9
+ delegate, offset, line, column
10
+ end
11
+
12
+ # @group Querying the Position
13
+ ########################################################################
14
+
15
+ # (see AbstractInput#offset)
16
+ attr_reader :offset
17
+
18
+ # (see AbstractInput#line)
19
+ attr_reader :line
20
+
21
+ # (see AbstractInput#column)
22
+ attr_reader :column
23
+
24
+ # (see AbstractInput#position)
25
+ def position
26
+ Position.new(@offset, @line, @column, nil)
27
+ end
28
+
29
+ # @group Reading the Input
30
+ ########################################################################
31
+
32
+ # (see AbstractInput#take)
33
+ def_delegators :@delegate, :take
34
+
35
+ # (see AbstractInput#at)
36
+ def_delegators :@delegate, :at
37
+
38
+ # (see AbstractInput#index)
39
+ def_delegators :@delegate, :index
40
+
41
+ # @group Advancing the Cursor
42
+ ########################################################################
43
+
44
+ # (see AbstractInput#drop)
45
+ def drop(n)
46
+ raise ArgumentError, "n must be positive" unless n >= 0
47
+
48
+ suffix = @delegate.drop(n)
49
+ prefix = @delegate.take(n)
50
+
51
+ length = prefix.length
52
+ count = prefix.count("\n")
53
+
54
+ column = unless count.zero?
55
+ length - prefix.rindex("\n")
56
+ else
57
+ @column + length
58
+ end
59
+
60
+ copy(:delegate => suffix,
61
+ :offset => @offset + length,
62
+ :line => @line + count,
63
+ :column => column)
64
+ end
65
+
66
+ # @group Testing the Input
67
+ ########################################################################
68
+
69
+ # (see AbstractInput#defined_at?)
70
+ def_delegators :@delegate, :defined_at?
71
+
72
+ # (see AbstractInput#empty?)
73
+ def_delegators :@delegate, :empty?
74
+
75
+
76
+ # (see AbstractInput#==)
77
+ def_delegators :@delegate, :==
78
+
79
+ # @endgroup
80
+ ########################################################################
81
+
82
+ # @return [void]
83
+ def pretty_print(q)
84
+ q.text("DelegateInput")
85
+ q.group(2, "(", ")") do
86
+ preview = @delegate.take(4)
87
+ preview = if preview.empty?
88
+ "EOF"
89
+ elsif preview.length <= 3
90
+ preview.inspect
91
+ else
92
+ (preview.take(3) + "...").inspect
93
+ end
94
+
95
+ q.text preview
96
+ q.text " at line #{@line}, column #{@column}, offset #{@offset}"
97
+ end
98
+ end
99
+
100
+ private
101
+
102
+ # @return [DelegatedInput]
103
+ def copy(changes = {})
104
+ DelegatedInput.new \
105
+ changes.fetch(:delegate, @delegate),
106
+ changes.fetch(:offset, @offset),
107
+ changes.fetch(:line, @line),
108
+ changes.fetch(:column, @column)
109
+ end
110
+ end
111
+ end
112
+ end
@@ -0,0 +1,157 @@
1
+ # frozen_string_literal: true
2
+ module Stupidedi
3
+ using Refinements
4
+
5
+ module Reader
6
+ #
7
+ # @note This class is not thread-safe. If more than one `Thread` has access
8
+ # to the same instance, and they simultaneously call methods on that
9
+ # instance, the methods may produce incorrect results and the object might
10
+ # be left in an inconsistent state.
11
+ #
12
+ class FileInput < AbstractInput
13
+ # @return [IO]
14
+ attr_reader :io
15
+
16
+ def initialize(io, offset = 0, line = 1, column = 1, size = io.stat.size)
17
+ @io, @offset, @line, @column, @size =
18
+ io, offset, line, column, size
19
+ end
20
+
21
+ # @group Querying the Position
22
+ ########################################################################
23
+
24
+ # (see AbstractInput#offset)
25
+ attr_reader :offset
26
+
27
+ # @return [Integer]
28
+ # (see AbstractInput#line)
29
+ attr_reader :line
30
+
31
+ # (see AbstractInput#column)
32
+ attr_reader :column
33
+
34
+ # (see AbstractInput#path)
35
+ attr_reader :path
36
+
37
+ # (see AbstractInput#position)
38
+ def position
39
+ Position.new(@offset, @line, @column, @io.path)
40
+ end
41
+
42
+ # @group Reading the Input
43
+ ########################################################################
44
+
45
+ # (see AbstractInput#take)
46
+ # @return [String]
47
+ def take(n)
48
+ @io.seek(@offset)
49
+
50
+ # Calling @io.read with more than the number of available bytes will
51
+ # return nil, so we have to calculate how many bytes remain
52
+ @io.read((n <= @size) ? n : @size)
53
+ end
54
+
55
+ # (see AbstractInput#at)
56
+ # @return [String]
57
+ def at(n)
58
+ raise ArgumentError, "n must be positive" unless n >= 0
59
+
60
+ @io.seek(@offset + n)
61
+ @io.read(1)
62
+ end
63
+
64
+ # (see AbstractInput#index)
65
+ def index(element)
66
+ @io.seek(@offset)
67
+ length = element.length
68
+
69
+ # We need to start with element != buffer, and this is a reasonable guess
70
+ buffer = "\377" * length
71
+
72
+ until @io.eof?
73
+ buffer.slice!(0)
74
+ buffer = buffer + @io.read(1)
75
+
76
+ if buffer == element
77
+ return @io.tell - @offset - length
78
+ end
79
+ end
80
+ end
81
+
82
+ # @group Advancing the Cursor
83
+ ########################################################################
84
+
85
+ # (see AbstractInput#drop)
86
+ def drop(n)
87
+ raise ArgumentError, "n must be positive" unless n >= 0
88
+
89
+ @io.seek(@offset)
90
+ prefix = @io.read(n)
91
+ # suffix = @io
92
+
93
+ length = prefix.length
94
+ count = prefix.count("\n")
95
+
96
+ column = unless count.zero?
97
+ length - prefix.rindex("\n")
98
+ else
99
+ @column + length
100
+ end
101
+
102
+ copy(:offset => @offset + length,
103
+ :line => @line + count,
104
+ :column => column,
105
+ :size => @size - length)
106
+ end
107
+
108
+ # @group Testing the Input
109
+ ########################################################################
110
+
111
+ # (see AbstractInput#defined_at?)
112
+ def defined_at?(n)
113
+ n < @size
114
+ end
115
+
116
+ # (see AbstractInput#empty?)
117
+ def empty?
118
+ @io.eof?
119
+ end
120
+
121
+ # @endgroup
122
+ ########################################################################
123
+
124
+ # @return [void]
125
+ # :nocov:
126
+ def pretty_print(q)
127
+ q.text("FileInput")
128
+ q.group(2, "(", ")") do
129
+ preview = take(4)
130
+ preview = if preview.empty?
131
+ "EOF"
132
+ elsif preview.length <= 3
133
+ preview.inspect
134
+ else
135
+ (preview.take(3) + "...").inspect
136
+ end
137
+
138
+ q.text preview
139
+ q.text " at line #{@line}, column #{@column}, offset #{@offset}, file #{File.basename(@io.path)}"
140
+ end
141
+ end
142
+ # :nocov:
143
+
144
+ private
145
+
146
+ # @return [FileInput]
147
+ def copy(changes = {})
148
+ FileInput.new \
149
+ changes.fetch(:io, @io),
150
+ changes.fetch(:offset, @offset),
151
+ changes.fetch(:line, @line),
152
+ changes.fetch(:column, @column),
153
+ changes.fetch(:size, @size)
154
+ end
155
+ end
156
+ end
157
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+ module Stupidedi
3
+ using Refinements
4
+
5
+ module Reader
6
+ module Input
7
+ end
8
+
9
+ class << Input
10
+ # @group Constructors
11
+ #########################################################################
12
+
13
+ # @return [Input]
14
+ def build(o, *args)
15
+ case o
16
+ when AbstractInput
17
+ o
18
+ when IO
19
+ FileInput.new(o, *args)
20
+ when String, Array
21
+ DelegatedInput.new(o, *args)
22
+ else
23
+ raise TypeError
24
+ end
25
+ end
26
+
27
+ # @endgroup
28
+ #########################################################################
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,78 @@
1
+ # frozen_string_literal: true
2
+ module Stupidedi
3
+ using Refinements
4
+
5
+ module Reader
6
+ class Position
7
+ # @return [Integer]
8
+ attr_reader :offset
9
+
10
+ # @return [Integer]
11
+ attr_reader :line
12
+
13
+ # @return [Integer]
14
+ attr_reader :column
15
+
16
+ # @return [String, Pathname]
17
+ attr_reader :path
18
+
19
+ def initialize(offset, line, column, path)
20
+ @offset, @line, @column, @path =
21
+ offset, line, column, path
22
+ end
23
+
24
+ def copy(changes = {})
25
+ Position.new \
26
+ changes.fetch(:offset, @offset),
27
+ changes.fetch(:line, @line),
28
+ changes.fetch(:column, @column),
29
+ changes.fetch(:path, @path)
30
+ end
31
+
32
+ def to_s
33
+ inspect
34
+ end
35
+
36
+ # @return [String]
37
+ def inspect
38
+ if @path.present?
39
+ parts = ["file #{@path}", "line #{@line}"]
40
+ else
41
+ parts = ["line #{@line}"]
42
+ end
43
+
44
+ if @column.present?
45
+ parts << "column #{@column}"
46
+ end
47
+
48
+ parts.join(", ")
49
+ end
50
+
51
+ # @return [void]
52
+ def pretty_print(q)
53
+ q.text "Position"
54
+ q.group(2, "(", ")") do
55
+ q.breakable ""
56
+ q.text "line #{@line},"
57
+ q.breakable
58
+ q.text "column #{@column},"
59
+ q.breakable
60
+ q.text "offset #{@offset}"
61
+
62
+ unless @path.nil?
63
+ q.text ","
64
+ q.breakable
65
+ q.text "path #{@path}"
66
+ end
67
+ end
68
+ end
69
+ end
70
+
71
+ class << Position
72
+ def caller(offset = 1)
73
+ path, line, = Stupidedi.caller(offset + 1)
74
+ new(nil, line, nil, path)
75
+ end
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,172 @@
1
+ # frozen_string_literal: true
2
+ module Stupidedi
3
+ using Refinements
4
+
5
+ module Reader
6
+ module Result
7
+ # @return [Position]
8
+ def position
9
+ if @remainder.respond_to?(:position)
10
+ @remainder.position
11
+ end
12
+ end
13
+
14
+ # @return [Integer]
15
+ def offset
16
+ if @remainder.respond_to?(:offset)
17
+ @remainder.offset
18
+ end
19
+ end
20
+
21
+ # @return [Integer]
22
+ def line
23
+ if @remainder.respond_to?(:line)
24
+ @remainder.line
25
+ end
26
+ end
27
+
28
+ # @return [Integer]
29
+ def column
30
+ if @remainder.respond_to?(:column)
31
+ @remainder.column
32
+ end
33
+ end
34
+ end
35
+
36
+ class << Result
37
+ # @group Constructors
38
+ #########################################################################
39
+
40
+ # @return [Result::Success]
41
+ def success(value, remainder)
42
+ Success.new(value, remainder)
43
+ end
44
+
45
+ # @return [Result::Failure]
46
+ def failure(reason, remainder, fatal)
47
+ Failure.new(reason, remainder, fatal)
48
+ end
49
+
50
+ # @endgroup
51
+ #########################################################################
52
+ end
53
+
54
+ class Success < Either::Success
55
+ include Result
56
+
57
+ attr_reader :remainder
58
+
59
+ def initialize(value, remainder)
60
+ @value, @remainder = value, remainder
61
+ end
62
+
63
+ # @return [Success]
64
+ def copy(changes = {})
65
+ Success.new \
66
+ changes.fetch(:value, @value),
67
+ changes.fetch(:remainder, @remainder)
68
+ end
69
+
70
+ def fatal?
71
+ false
72
+ end
73
+
74
+ # @return [Boolean]
75
+ def ==(other)
76
+ if other.is_a?(self.class)
77
+ @value == other.value and @remainder == other.remainder
78
+ else
79
+ @value == other
80
+ end
81
+ end
82
+
83
+ # @return [void]
84
+ # :nocov:
85
+ def pretty_print(q)
86
+ q.text "Result.success"
87
+ q.group(2, "(", ")") do
88
+ q.breakable ""
89
+ q.pp @value
90
+ q.text ","
91
+ q.breakable
92
+ q.pp @remainder
93
+ end
94
+ end
95
+ # :nocov:
96
+
97
+ # Override `Enumerable#blank?` since we're not really `Enumerable`
98
+ def blank?
99
+ false
100
+ end
101
+
102
+ private
103
+
104
+ def deconstruct(block)
105
+ block.call(@value, @remainder)
106
+ end
107
+
108
+ def failure(reason)
109
+ Failure.new(reason, @remainder, false)
110
+ end
111
+ end
112
+
113
+ class Failure < Either::Failure
114
+ include Result
115
+
116
+ attr_reader :remainder
117
+
118
+ def initialize(reason, remainder, fatal)
119
+ @reason, @remainder, @fatal =
120
+ reason, remainder, fatal
121
+ end
122
+
123
+ # @return [Failure]
124
+ def copy(changes = {})
125
+ Failure.new \
126
+ changes.fetch(:reason, @reason),
127
+ changes.fetch(:remainder, @remainder),
128
+ changes.fetch(:fatal, @fatal)
129
+ end
130
+
131
+ def fatal?
132
+ @fatal
133
+ end
134
+
135
+ def fatal
136
+ copy(:fatal => true)
137
+ end
138
+
139
+ # @return [Boolean]
140
+ def ==(other)
141
+ if other.is_a?(self.class)
142
+ @reason == other.reason
143
+ else
144
+ @reason == other
145
+ end
146
+ end
147
+
148
+ # @return [void]
149
+ # :nocov:
150
+ def pretty_print(q)
151
+ q.text "Result.failure"
152
+ q.group(2, "(", ")") do
153
+ q.breakable ""
154
+ q.pp @reason
155
+ q.text ","
156
+ q.breakable
157
+ q.pp @remainder
158
+ q.text ","
159
+ q.breakable
160
+ q.text "#{@fatal ? "" : "non-"}fatal"
161
+ end
162
+ end
163
+ # :nocov:
164
+
165
+ private
166
+
167
+ def deconstruct(block)
168
+ block.call(@reason, @remainder)
169
+ end
170
+ end
171
+ end
172
+ end