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,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
|