lrama 0.6.10 → 0.7.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 +4 -4
- data/.github/workflows/gh-pages.yml +46 -0
- data/.github/workflows/test.yaml +40 -8
- data/.gitignore +1 -0
- data/.rdoc_options +2 -0
- data/Gemfile +4 -2
- data/NEWS.md +125 -30
- data/README.md +44 -15
- data/Rakefile +13 -1
- data/Steepfile +5 -0
- data/doc/Index.md +58 -0
- data/doc/development/compressed_state_table/main.md +635 -0
- data/doc/development/compressed_state_table/parse.output +174 -0
- data/doc/development/compressed_state_table/parse.y +22 -0
- data/doc/development/compressed_state_table/parser.rb +282 -0
- data/lib/lrama/bitmap.rb +4 -1
- data/lib/lrama/command.rb +2 -1
- data/lib/lrama/context.rb +3 -3
- data/lib/lrama/counterexamples/derivation.rb +6 -5
- data/lib/lrama/counterexamples/example.rb +7 -4
- data/lib/lrama/counterexamples/path.rb +4 -0
- data/lib/lrama/counterexamples.rb +19 -9
- data/lib/lrama/digraph.rb +30 -0
- data/lib/lrama/grammar/binding.rb +47 -15
- data/lib/lrama/grammar/parameterizing_rule/rhs.rb +1 -1
- data/lib/lrama/grammar/rule.rb +8 -0
- data/lib/lrama/grammar/rule_builder.rb +4 -16
- data/lib/lrama/grammar/symbols/resolver.rb +4 -0
- data/lib/lrama/grammar.rb +10 -5
- data/lib/lrama/lexer/grammar_file.rb +8 -1
- data/lib/lrama/lexer/location.rb +17 -1
- data/lib/lrama/lexer/token/char.rb +1 -0
- data/lib/lrama/lexer/token/ident.rb +1 -0
- data/lib/lrama/lexer/token/instantiate_rule.rb +6 -1
- data/lib/lrama/lexer/token/tag.rb +3 -1
- data/lib/lrama/lexer/token/user_code.rb +6 -2
- data/lib/lrama/lexer/token.rb +14 -2
- data/lib/lrama/lexer.rb +5 -5
- data/lib/lrama/logger.rb +4 -0
- data/lib/lrama/option_parser.rb +10 -8
- data/lib/lrama/options.rb +2 -1
- data/lib/lrama/parser.rb +529 -490
- data/lib/lrama/state/reduce.rb +2 -3
- data/lib/lrama/state.rb +288 -1
- data/lib/lrama/states/item.rb +8 -0
- data/lib/lrama/states.rb +69 -2
- data/lib/lrama/trace_reporter.rb +17 -2
- data/lib/lrama/version.rb +1 -1
- data/lrama.gemspec +1 -1
- data/parser.y +42 -30
- data/rbs_collection.lock.yaml +10 -2
- data/sig/generated/lrama/bitmap.rbs +11 -0
- data/sig/generated/lrama/digraph.rbs +39 -0
- data/sig/generated/lrama/grammar/binding.rbs +34 -0
- data/sig/generated/lrama/lexer/grammar_file.rbs +28 -0
- data/sig/generated/lrama/lexer/location.rbs +52 -0
- data/sig/{lrama → generated/lrama}/lexer/token/char.rbs +2 -0
- data/sig/{lrama → generated/lrama}/lexer/token/ident.rbs +2 -0
- data/sig/{lrama → generated/lrama}/lexer/token/instantiate_rule.rbs +8 -0
- data/sig/{lrama → generated/lrama}/lexer/token/tag.rbs +3 -0
- data/sig/{lrama → generated/lrama}/lexer/token/user_code.rbs +6 -1
- data/sig/{lrama → generated/lrama}/lexer/token.rbs +26 -3
- data/sig/generated/lrama/logger.rbs +14 -0
- data/sig/generated/lrama/trace_reporter.rbs +25 -0
- data/sig/lrama/counterexamples/derivation.rbs +33 -0
- data/sig/lrama/counterexamples/example.rbs +45 -0
- data/sig/lrama/counterexamples/path.rbs +21 -0
- data/sig/lrama/counterexamples/production_path.rbs +11 -0
- data/sig/lrama/counterexamples/start_path.rbs +13 -0
- data/sig/lrama/counterexamples/state_item.rbs +10 -0
- data/sig/lrama/counterexamples/transition_path.rbs +11 -0
- data/sig/lrama/counterexamples/triple.rbs +20 -0
- data/sig/lrama/counterexamples.rbs +29 -0
- data/sig/lrama/grammar/rule_builder.rbs +0 -1
- data/sig/lrama/grammar/symbol.rbs +1 -1
- data/sig/lrama/grammar/symbols/resolver.rbs +3 -3
- data/sig/lrama/grammar.rbs +13 -0
- data/sig/lrama/options.rbs +1 -0
- data/sig/lrama/state/reduce_reduce_conflict.rbs +2 -2
- data/sig/lrama/state.rbs +79 -0
- data/sig/lrama/states.rbs +101 -0
- metadata +34 -14
- data/sig/lrama/bitmap.rbs +0 -7
- data/sig/lrama/digraph.rbs +0 -23
- data/sig/lrama/grammar/binding.rbs +0 -19
- data/sig/lrama/lexer/grammar_file.rbs +0 -17
- data/sig/lrama/lexer/location.rbs +0 -26
@@ -32,8 +32,10 @@ module Lrama
|
|
32
32
|
conflict_state.conflicts.flat_map do |conflict|
|
33
33
|
case conflict.type
|
34
34
|
when :shift_reduce
|
35
|
+
# @type var conflict: State::ShiftReduceConflict
|
35
36
|
shift_reduce_example(conflict_state, conflict)
|
36
37
|
when :reduce_reduce
|
38
|
+
# @type var conflict: State::ReduceReduceConflict
|
37
39
|
reduce_reduce_examples(conflict_state, conflict)
|
38
40
|
end
|
39
41
|
end.compact
|
@@ -48,7 +50,7 @@ module Lrama
|
|
48
50
|
@reverse_transitions = {}
|
49
51
|
|
50
52
|
@states.states.each do |src_state|
|
51
|
-
trans = {}
|
53
|
+
trans = {} #: Hash[Grammar::Symbol, State]
|
52
54
|
|
53
55
|
src_state.transitions.each do |shift, next_state|
|
54
56
|
trans[shift.next_sym] = next_state
|
@@ -66,6 +68,7 @@ module Lrama
|
|
66
68
|
|
67
69
|
@transitions[[src_state_item, sym]] = dest_state_item
|
68
70
|
|
71
|
+
# @type var key: [StateItem, Grammar::Symbol]
|
69
72
|
key = [dest_state_item, sym]
|
70
73
|
@reverse_transitions[key] ||= Set.new
|
71
74
|
@reverse_transitions[key] << src_state_item
|
@@ -82,7 +85,7 @@ module Lrama
|
|
82
85
|
|
83
86
|
@states.states.each do |state|
|
84
87
|
# LHS => Set(Item)
|
85
|
-
h = {}
|
88
|
+
h = {} #: Hash[Grammar::Symbol, Set[States::Item]]
|
86
89
|
|
87
90
|
state.closure.each do |item|
|
88
91
|
sym = item.lhs
|
@@ -97,6 +100,7 @@ module Lrama
|
|
97
100
|
|
98
101
|
sym = item.next_sym
|
99
102
|
state_item = StateItem.new(state, item)
|
103
|
+
# @type var key: [State, Grammar::Symbol]
|
100
104
|
key = [state, sym]
|
101
105
|
|
102
106
|
@productions[state_item] = h[sym]
|
@@ -109,6 +113,7 @@ module Lrama
|
|
109
113
|
|
110
114
|
def shift_reduce_example(conflict_state, conflict)
|
111
115
|
conflict_symbol = conflict.symbols.first
|
116
|
+
# @type var shift_conflict_item: ::Lrama::States::Item
|
112
117
|
shift_conflict_item = conflict_state.items.find { |item| item.next_sym == conflict_symbol }
|
113
118
|
path2 = shortest_path(conflict_state, conflict.reduce.item, conflict_symbol)
|
114
119
|
path1 = find_shift_conflict_shortest_path(path2, conflict_state, shift_conflict_item)
|
@@ -153,12 +158,14 @@ module Lrama
|
|
153
158
|
prev_state_item = prev_path&.to
|
154
159
|
|
155
160
|
if target_state_item == state_item || target_state_item.item.start_item?
|
156
|
-
result.concat(
|
161
|
+
result.concat(
|
162
|
+
reversed_reduce_path[_j..-1] #: Array[StartPath|TransitionPath|ProductionPath]
|
163
|
+
.map(&:to))
|
157
164
|
break
|
158
165
|
end
|
159
166
|
|
160
167
|
if target_state_item.item.beginning_of_rule?
|
161
|
-
queue = []
|
168
|
+
queue = [] #: Array[Array[StateItem]]
|
162
169
|
queue << [target_state_item]
|
163
170
|
|
164
171
|
# Find reverse production
|
@@ -174,15 +181,17 @@ module Lrama
|
|
174
181
|
end
|
175
182
|
|
176
183
|
if si.item.beginning_of_rule?
|
184
|
+
# @type var key: [State, Grammar::Symbol]
|
177
185
|
key = [si.state, si.item.lhs]
|
178
186
|
@reverse_productions[key].each do |item|
|
179
187
|
state_item = StateItem.new(si.state, item)
|
180
188
|
queue << (sis + [state_item])
|
181
189
|
end
|
182
190
|
else
|
191
|
+
# @type var key: [StateItem, Grammar::Symbol]
|
183
192
|
key = [si, si.item.previous_sym]
|
184
193
|
@reverse_transitions[key].each do |prev_target_state_item|
|
185
|
-
next if prev_target_state_item.state != prev_state_item
|
194
|
+
next if prev_target_state_item.state != prev_state_item&.state
|
186
195
|
sis.shift
|
187
196
|
result.concat(sis)
|
188
197
|
result << prev_target_state_item
|
@@ -195,9 +204,10 @@ module Lrama
|
|
195
204
|
end
|
196
205
|
else
|
197
206
|
# Find reverse transition
|
207
|
+
# @type var key: [StateItem, Grammar::Symbol]
|
198
208
|
key = [target_state_item, target_state_item.item.previous_sym]
|
199
209
|
@reverse_transitions[key].each do |prev_target_state_item|
|
200
|
-
next if prev_target_state_item.state != prev_state_item
|
210
|
+
next if prev_target_state_item.state != prev_state_item&.state
|
201
211
|
result << prev_target_state_item
|
202
212
|
target_state_item = prev_target_state_item
|
203
213
|
i = j
|
@@ -224,9 +234,9 @@ module Lrama
|
|
224
234
|
|
225
235
|
def shortest_path(conflict_state, conflict_reduce_item, conflict_term)
|
226
236
|
# queue: is an array of [Triple, [Path]]
|
227
|
-
queue = []
|
228
|
-
visited = {}
|
229
|
-
start_state = @states.states.first
|
237
|
+
queue = [] #: Array[[Triple, Array[StartPath|TransitionPath|ProductionPath]]]
|
238
|
+
visited = {} #: Hash[Triple, true]
|
239
|
+
start_state = @states.states.first #: Lrama::State
|
230
240
|
raise "BUG: Start state should be just one kernel." if start_state.kernels.count != 1
|
231
241
|
|
232
242
|
start = Triple.new(start_state, start_state.kernels.first, Set.new([@states.eof_symbol]))
|
data/lib/lrama/digraph.rb
CHANGED
@@ -1,23 +1,52 @@
|
|
1
|
+
# rbs_inline: enabled
|
1
2
|
# frozen_string_literal: true
|
2
3
|
|
3
4
|
module Lrama
|
4
5
|
# Algorithm Digraph of https://dl.acm.org/doi/pdf/10.1145/69622.357187 (P. 625)
|
6
|
+
#
|
7
|
+
# @rbs generic X < Object -- Type of a member of `sets`
|
8
|
+
# @rbs generic Y < _Or -- Type of sets assigned to a member of `sets`
|
5
9
|
class Digraph
|
10
|
+
# TODO: rbs-inline 0.10.0 doesn't support instance variables.
|
11
|
+
# Move these type declarations above instance variable definitions, once it's supported.
|
12
|
+
#
|
13
|
+
# @rbs!
|
14
|
+
# interface _Or
|
15
|
+
# def |: (self) -> self
|
16
|
+
# end
|
17
|
+
# @sets: Array[X]
|
18
|
+
# @relation: Hash[X, Array[X]]
|
19
|
+
# @base_function: Hash[X, Y]
|
20
|
+
# @stack: Array[X]
|
21
|
+
# @h: Hash[X, (Integer|Float)?]
|
22
|
+
# @result: Hash[X, Y]
|
23
|
+
|
24
|
+
# @rbs sets: Array[X]
|
25
|
+
# @rbs relation: Hash[X, Array[X]]
|
26
|
+
# @rbs base_function: Hash[X, Y]
|
27
|
+
# @rbs return: void
|
6
28
|
def initialize(sets, relation, base_function)
|
29
|
+
|
7
30
|
# X in the paper
|
8
31
|
@sets = sets
|
32
|
+
|
9
33
|
# R in the paper
|
10
34
|
@relation = relation
|
35
|
+
|
11
36
|
# F' in the paper
|
12
37
|
@base_function = base_function
|
38
|
+
|
13
39
|
# S in the paper
|
14
40
|
@stack = []
|
41
|
+
|
15
42
|
# N in the paper
|
16
43
|
@h = Hash.new(0)
|
44
|
+
|
17
45
|
# F in the paper
|
18
46
|
@result = {}
|
19
47
|
end
|
20
48
|
|
49
|
+
# @rbs () -> Hash[X, Y]
|
21
50
|
def compute
|
22
51
|
@sets.each do |x|
|
23
52
|
next if @h[x] != 0
|
@@ -29,6 +58,7 @@ module Lrama
|
|
29
58
|
|
30
59
|
private
|
31
60
|
|
61
|
+
# @rbs (X x) -> void
|
32
62
|
def traverse(x)
|
33
63
|
@stack.push(x)
|
34
64
|
d = @stack.count
|
@@ -1,34 +1,66 @@
|
|
1
|
+
# rbs_inline: enabled
|
1
2
|
# frozen_string_literal: true
|
2
3
|
|
3
4
|
module Lrama
|
4
5
|
class Grammar
|
5
6
|
class Binding
|
6
|
-
|
7
|
+
# @rbs @actual_args: Array[Lexer::Token]
|
8
|
+
# @rbs @param_to_arg: Hash[String, Lexer::Token]
|
7
9
|
|
8
|
-
|
9
|
-
|
10
|
+
# @rbs (Array[Lexer::Token] params, Array[Lexer::Token] actual_args) -> void
|
11
|
+
def initialize(params, actual_args)
|
10
12
|
@actual_args = actual_args
|
11
|
-
@
|
12
|
-
[param.s_value, arg]
|
13
|
-
end.to_h
|
13
|
+
@param_to_arg = map_params_to_args(params, @actual_args)
|
14
14
|
end
|
15
15
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
Lrama::Lexer::Token::InstantiateRule.new(
|
16
|
+
# @rbs (Lexer::Token sym) -> Lexer::Token
|
17
|
+
def resolve_symbol(sym)
|
18
|
+
if sym.is_a?(Lexer::Token::InstantiateRule)
|
19
|
+
Lrama::Lexer::Token::InstantiateRule.new(
|
20
|
+
s_value: sym.s_value, location: sym.location, args: resolved_args(sym), lhs_tag: sym.lhs_tag
|
21
|
+
)
|
20
22
|
else
|
21
|
-
|
23
|
+
param_to_arg(sym)
|
22
24
|
end
|
23
25
|
end
|
24
26
|
|
27
|
+
# @rbs (Lexer::Token::InstantiateRule token) -> String
|
28
|
+
def concatenated_args_str(token)
|
29
|
+
"#{token.rule_name}_#{token_to_args_s_values(token).join('_')}"
|
30
|
+
end
|
31
|
+
|
25
32
|
private
|
26
33
|
|
27
|
-
|
28
|
-
|
29
|
-
|
34
|
+
# @rbs (Array[Lexer::Token] params, Array[Lexer::Token] actual_args) -> Hash[String, Lexer::Token]
|
35
|
+
def map_params_to_args(params, actual_args)
|
36
|
+
params.zip(actual_args).map do |param, arg|
|
37
|
+
[param.s_value, arg]
|
38
|
+
end.to_h
|
39
|
+
end
|
40
|
+
|
41
|
+
# @rbs (Lexer::Token::InstantiateRule sym) -> Array[Lexer::Token]
|
42
|
+
def resolved_args(sym)
|
43
|
+
sym.args.map { |arg| resolve_symbol(arg) }
|
44
|
+
end
|
45
|
+
|
46
|
+
# @rbs (Lexer::Token sym) -> Lexer::Token
|
47
|
+
def param_to_arg(sym)
|
48
|
+
if (arg = @param_to_arg[sym.s_value].dup)
|
49
|
+
arg.alias_name = sym.alias_name
|
50
|
+
end
|
51
|
+
arg || sym
|
52
|
+
end
|
53
|
+
|
54
|
+
# @rbs (Lexer::Token::InstantiateRule token) -> Array[String]
|
55
|
+
def token_to_args_s_values(token)
|
56
|
+
token.args.flat_map do |arg|
|
57
|
+
resolved = resolve_symbol(arg)
|
58
|
+
if resolved.is_a?(Lexer::Token::InstantiateRule)
|
59
|
+
[resolved.s_value] + resolved.args.map(&:s_value)
|
60
|
+
else
|
61
|
+
[resolved.s_value]
|
62
|
+
end
|
30
63
|
end
|
31
|
-
arg
|
32
64
|
end
|
33
65
|
end
|
34
66
|
end
|
@@ -16,7 +16,7 @@ module Lrama
|
|
16
16
|
return unless user_code
|
17
17
|
|
18
18
|
resolved = Lexer::Token::UserCode.new(s_value: user_code.s_value, location: user_code.location)
|
19
|
-
var_to_arg = {}
|
19
|
+
var_to_arg = {} #: Hash[String, String]
|
20
20
|
symbols.each do |sym|
|
21
21
|
resolved_sym = bindings.resolve_symbol(sym)
|
22
22
|
if resolved_sym != sym
|
data/lib/lrama/grammar/rule.rb
CHANGED
@@ -21,6 +21,14 @@ module Lrama
|
|
21
21
|
def display_name
|
22
22
|
l = lhs.id.s_value
|
23
23
|
r = empty_rule? ? "ε" : rhs.map {|r| r.id.s_value }.join(" ")
|
24
|
+
"#{l} -> #{r}"
|
25
|
+
end
|
26
|
+
|
27
|
+
def display_name_without_action
|
28
|
+
l = lhs.id.s_value
|
29
|
+
r = empty_rule? ? "ε" : rhs.map do |r|
|
30
|
+
r.id.s_value if r.first_set.any?
|
31
|
+
end.compact.join(" ")
|
24
32
|
|
25
33
|
"#{l} -> #{r}"
|
26
34
|
end
|
@@ -67,13 +67,13 @@ module Lrama
|
|
67
67
|
end
|
68
68
|
|
69
69
|
def resolve_inline_rules
|
70
|
-
resolved_builders = []
|
70
|
+
resolved_builders = [] #: Array[RuleBuilder]
|
71
71
|
rhs.each_with_index do |token, i|
|
72
72
|
if (inline_rule = @parameterizing_rule_resolver.find_inline(token))
|
73
73
|
inline_rule.rhs_list.each do |inline_rhs|
|
74
74
|
rule_builder = RuleBuilder.new(@rule_counter, @midrule_action_counter, @parameterizing_rule_resolver, lhs_tag: lhs_tag)
|
75
75
|
if token.is_a?(Lexer::Token::InstantiateRule)
|
76
|
-
resolve_inline_rhs(rule_builder, inline_rhs, i, Binding.new(inline_rule, token.args))
|
76
|
+
resolve_inline_rhs(rule_builder, inline_rhs, i, Binding.new(inline_rule.parameters, token.args))
|
77
77
|
else
|
78
78
|
resolve_inline_rhs(rule_builder, inline_rhs, i)
|
79
79
|
end
|
@@ -135,8 +135,8 @@ module Lrama
|
|
135
135
|
parameterizing_rule = @parameterizing_rule_resolver.find_rule(token)
|
136
136
|
raise "Unexpected token. #{token}" unless parameterizing_rule
|
137
137
|
|
138
|
-
bindings = Binding.new(parameterizing_rule, token.args)
|
139
|
-
lhs_s_value =
|
138
|
+
bindings = Binding.new(parameterizing_rule.parameters, token.args)
|
139
|
+
lhs_s_value = bindings.concatenated_args_str(token)
|
140
140
|
if (created_lhs = @parameterizing_rule_resolver.created_lhs(lhs_s_value))
|
141
141
|
@replaced_rhs << created_lhs
|
142
142
|
else
|
@@ -174,18 +174,6 @@ module Lrama
|
|
174
174
|
end
|
175
175
|
end
|
176
176
|
|
177
|
-
def lhs_s_value(token, bindings)
|
178
|
-
s_values = token.args.map do |arg|
|
179
|
-
resolved = bindings.resolve_symbol(arg)
|
180
|
-
if resolved.is_a?(Lexer::Token::InstantiateRule)
|
181
|
-
[resolved.s_value, resolved.args.map(&:s_value)]
|
182
|
-
else
|
183
|
-
resolved.s_value
|
184
|
-
end
|
185
|
-
end
|
186
|
-
"#{token.rule_name}_#{s_values.join('_')}"
|
187
|
-
end
|
188
|
-
|
189
177
|
def resolve_inline_rhs(rule_builder, inline_rhs, index, bindings = nil)
|
190
178
|
rhs.each_with_index do |token, i|
|
191
179
|
if index == i
|
data/lib/lrama/grammar.rb
CHANGED
@@ -28,14 +28,14 @@ module Lrama
|
|
28
28
|
attr_reader :percent_codes, :eof_symbol, :error_symbol, :undef_symbol, :accept_symbol, :aux, :parameterizing_rule_resolver
|
29
29
|
attr_accessor :union, :expect, :printers, :error_tokens, :lex_param, :parse_param, :initial_action,
|
30
30
|
:after_shift, :before_reduce, :after_reduce, :after_shift_error_token, :after_pop_stack,
|
31
|
-
:symbols_resolver, :types, :rules, :rule_builders, :sym_to_rules, :no_stdlib, :locations
|
31
|
+
:symbols_resolver, :types, :rules, :rule_builders, :sym_to_rules, :no_stdlib, :locations, :define
|
32
32
|
|
33
|
-
def_delegators "@symbols_resolver", :symbols, :nterms, :terms, :add_nterm, :add_term,
|
33
|
+
def_delegators "@symbols_resolver", :symbols, :nterms, :terms, :add_nterm, :add_term, :find_term_by_s_value,
|
34
34
|
:find_symbol_by_number!, :find_symbol_by_id!, :token_to_symbol,
|
35
35
|
:find_symbol_by_s_value!, :fill_symbol_number, :fill_nterm_type,
|
36
36
|
:fill_printer, :fill_destructor, :fill_error_token, :sort_by_number!
|
37
37
|
|
38
|
-
def initialize(rule_counter)
|
38
|
+
def initialize(rule_counter, define = {})
|
39
39
|
@rule_counter = rule_counter
|
40
40
|
|
41
41
|
# Code defined by "%code"
|
@@ -57,6 +57,7 @@ module Lrama
|
|
57
57
|
@aux = Auxiliary.new
|
58
58
|
@no_stdlib = false
|
59
59
|
@locations = false
|
60
|
+
@define = define.map {|d| d.split('=') }.to_h
|
60
61
|
|
61
62
|
append_special_symbols
|
62
63
|
end
|
@@ -171,6 +172,10 @@ module Lrama
|
|
171
172
|
@sym_to_rules[sym.number]
|
172
173
|
end
|
173
174
|
|
175
|
+
def ielr_defined?
|
176
|
+
@define.key?('lr.type') && @define['lr.type'] == 'ielr'
|
177
|
+
end
|
178
|
+
|
174
179
|
private
|
175
180
|
|
176
181
|
def compute_nullable
|
@@ -294,7 +299,7 @@ module Lrama
|
|
294
299
|
end
|
295
300
|
|
296
301
|
def resolve_inline_rules
|
297
|
-
while @rule_builders.any?
|
302
|
+
while @rule_builders.any?(&:has_inline_rules?) do
|
298
303
|
@rule_builders = @rule_builders.flat_map do |builder|
|
299
304
|
if builder.has_inline_rules?
|
300
305
|
builder.resolve_inline_rules
|
@@ -382,7 +387,7 @@ module Lrama
|
|
382
387
|
end
|
383
388
|
|
384
389
|
def validate_rule_lhs_is_nterm!
|
385
|
-
errors = []
|
390
|
+
errors = [] #: Array[String]
|
386
391
|
|
387
392
|
rules.each do |rule|
|
388
393
|
next if rule.lhs.nterm?
|
@@ -1,30 +1,37 @@
|
|
1
|
+
# rbs_inline: enabled
|
1
2
|
# frozen_string_literal: true
|
2
3
|
|
3
4
|
module Lrama
|
4
5
|
class Lexer
|
5
6
|
class GrammarFile
|
6
7
|
class Text < String
|
8
|
+
# @rbs () -> String
|
7
9
|
def inspect
|
8
10
|
length <= 50 ? super : "#{self[0..47]}...".inspect
|
9
11
|
end
|
10
12
|
end
|
11
13
|
|
12
|
-
attr_reader :path
|
14
|
+
attr_reader :path #: String
|
15
|
+
attr_reader :text #: String
|
13
16
|
|
17
|
+
# @rbs (String path, String text) -> void
|
14
18
|
def initialize(path, text)
|
15
19
|
@path = path
|
16
20
|
@text = Text.new(text).freeze
|
17
21
|
end
|
18
22
|
|
23
|
+
# @rbs () -> String
|
19
24
|
def inspect
|
20
25
|
"<#{self.class}: @path=#{path}, @text=#{text.inspect}>"
|
21
26
|
end
|
22
27
|
|
28
|
+
# @rbs (GrammarFile other) -> bool
|
23
29
|
def ==(other)
|
24
30
|
self.class == other.class &&
|
25
31
|
self.path == other.path
|
26
32
|
end
|
27
33
|
|
34
|
+
# @rbs () -> Array[String]
|
28
35
|
def lines
|
29
36
|
@lines ||= text.split("\n")
|
30
37
|
end
|
data/lib/lrama/lexer/location.rb
CHANGED
@@ -1,10 +1,16 @@
|
|
1
|
+
# rbs_inline: enabled
|
1
2
|
# frozen_string_literal: true
|
2
3
|
|
3
4
|
module Lrama
|
4
5
|
class Lexer
|
5
6
|
class Location
|
6
|
-
attr_reader :grammar_file
|
7
|
+
attr_reader :grammar_file #: GrammarFile
|
8
|
+
attr_reader :first_line #: Integer
|
9
|
+
attr_reader :first_column #: Integer
|
10
|
+
attr_reader :last_line #: Integer
|
11
|
+
attr_reader :last_column #: Integer
|
7
12
|
|
13
|
+
# @rbs (grammar_file: GrammarFile, first_line: Integer, first_column: Integer, last_line: Integer, last_column: Integer) -> void
|
8
14
|
def initialize(grammar_file:, first_line:, first_column:, last_line:, last_column:)
|
9
15
|
@grammar_file = grammar_file
|
10
16
|
@first_line = first_line
|
@@ -13,6 +19,7 @@ module Lrama
|
|
13
19
|
@last_column = last_column
|
14
20
|
end
|
15
21
|
|
22
|
+
# @rbs (Location other) -> bool
|
16
23
|
def ==(other)
|
17
24
|
self.class == other.class &&
|
18
25
|
self.grammar_file == other.grammar_file &&
|
@@ -22,6 +29,7 @@ module Lrama
|
|
22
29
|
self.last_column == other.last_column
|
23
30
|
end
|
24
31
|
|
32
|
+
# @rbs (Integer left, Integer right) -> Location
|
25
33
|
def partial_location(left, right)
|
26
34
|
offset = -first_column
|
27
35
|
new_first_line = -1
|
@@ -52,10 +60,12 @@ module Lrama
|
|
52
60
|
)
|
53
61
|
end
|
54
62
|
|
63
|
+
# @rbs () -> String
|
55
64
|
def to_s
|
56
65
|
"#{path} (#{first_line},#{first_column})-(#{last_line},#{last_column})"
|
57
66
|
end
|
58
67
|
|
68
|
+
# @rbs (String error_message) -> String
|
59
69
|
def generate_error_message(error_message)
|
60
70
|
<<~ERROR.chomp
|
61
71
|
#{path}:#{first_line}:#{first_column}: #{error_message}
|
@@ -63,6 +73,7 @@ module Lrama
|
|
63
73
|
ERROR
|
64
74
|
end
|
65
75
|
|
76
|
+
# @rbs () -> String
|
66
77
|
def line_with_carets
|
67
78
|
<<~TEXT
|
68
79
|
#{text}
|
@@ -72,22 +83,27 @@ module Lrama
|
|
72
83
|
|
73
84
|
private
|
74
85
|
|
86
|
+
# @rbs () -> String
|
75
87
|
def path
|
76
88
|
grammar_file.path
|
77
89
|
end
|
78
90
|
|
91
|
+
# @rbs () -> String
|
79
92
|
def blanks
|
80
93
|
(text[0...first_column] or raise "#{first_column} is invalid").gsub(/[^\t]/, ' ')
|
81
94
|
end
|
82
95
|
|
96
|
+
# @rbs () -> String
|
83
97
|
def carets
|
84
98
|
blanks + '^' * (last_column - first_column)
|
85
99
|
end
|
86
100
|
|
101
|
+
# @rbs () -> String
|
87
102
|
def text
|
88
103
|
@text ||= _text.join("\n")
|
89
104
|
end
|
90
105
|
|
106
|
+
# @rbs () -> Array[String]
|
91
107
|
def _text
|
92
108
|
@_text ||=begin
|
93
109
|
range = (first_line - 1)...last_line
|
@@ -1,21 +1,26 @@
|
|
1
|
+
# rbs_inline: enabled
|
1
2
|
# frozen_string_literal: true
|
2
3
|
|
3
4
|
module Lrama
|
4
5
|
class Lexer
|
5
6
|
class Token
|
6
7
|
class InstantiateRule < Token
|
7
|
-
attr_reader :args
|
8
|
+
attr_reader :args #: Array[Lexer::Token]
|
9
|
+
attr_reader :lhs_tag #: Lexer::Token::Tag?
|
8
10
|
|
11
|
+
# @rbs (s_value: String, ?alias_name: String, ?location: Location, ?args: Array[Lexer::Token], ?lhs_tag: Lexer::Token::Tag?) -> void
|
9
12
|
def initialize(s_value:, alias_name: nil, location: nil, args: [], lhs_tag: nil)
|
10
13
|
super s_value: s_value, alias_name: alias_name, location: location
|
11
14
|
@args = args
|
12
15
|
@lhs_tag = lhs_tag
|
13
16
|
end
|
14
17
|
|
18
|
+
# @rbs () -> String
|
15
19
|
def rule_name
|
16
20
|
s_value
|
17
21
|
end
|
18
22
|
|
23
|
+
# @rbs () -> Integer
|
19
24
|
def args_count
|
20
25
|
args.count
|
21
26
|
end
|
@@ -1,11 +1,13 @@
|
|
1
|
+
# rbs_inline: enabled
|
1
2
|
# frozen_string_literal: true
|
2
3
|
|
3
4
|
module Lrama
|
4
5
|
class Lexer
|
5
6
|
class Token
|
6
7
|
class Tag < Token
|
7
|
-
#
|
8
|
+
# @rbs () -> String
|
8
9
|
def member
|
10
|
+
# Omit "<>"
|
9
11
|
s_value[1..-2] or raise "Unexpected Tag format (#{s_value})"
|
10
12
|
end
|
11
13
|
end
|
@@ -1,3 +1,4 @@
|
|
1
|
+
# rbs_inline: enabled
|
1
2
|
# frozen_string_literal: true
|
2
3
|
|
3
4
|
require "strscan"
|
@@ -6,17 +7,19 @@ module Lrama
|
|
6
7
|
class Lexer
|
7
8
|
class Token
|
8
9
|
class UserCode < Token
|
9
|
-
attr_accessor :tag
|
10
|
+
attr_accessor :tag #: Lexer::Token::Tag
|
10
11
|
|
12
|
+
# @rbs () -> Array[Lrama::Grammar::Reference]
|
11
13
|
def references
|
12
14
|
@references ||= _references
|
13
15
|
end
|
14
16
|
|
15
17
|
private
|
16
18
|
|
19
|
+
# @rbs () -> Array[Lrama::Grammar::Reference]
|
17
20
|
def _references
|
18
21
|
scanner = StringScanner.new(s_value)
|
19
|
-
references = []
|
22
|
+
references = [] #: Array[Grammar::Reference]
|
20
23
|
|
21
24
|
until scanner.eos? do
|
22
25
|
case
|
@@ -32,6 +35,7 @@ module Lrama
|
|
32
35
|
references
|
33
36
|
end
|
34
37
|
|
38
|
+
# @rbs (StringScanner scanner) -> Lrama::Grammar::Reference?
|
35
39
|
def scan_reference(scanner)
|
36
40
|
start = scanner.pos
|
37
41
|
case
|
data/lib/lrama/lexer/token.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
# rbs_inline: enabled
|
1
2
|
# frozen_string_literal: true
|
2
3
|
|
3
4
|
require_relative 'token/char'
|
@@ -9,9 +10,12 @@ require_relative 'token/user_code'
|
|
9
10
|
module Lrama
|
10
11
|
class Lexer
|
11
12
|
class Token
|
12
|
-
attr_reader :s_value
|
13
|
-
|
13
|
+
attr_reader :s_value #: String
|
14
|
+
attr_reader :location #: Location
|
15
|
+
attr_accessor :alias_name #: String
|
16
|
+
attr_accessor :referred #: bool
|
14
17
|
|
18
|
+
# @rbs (s_value: String, ?alias_name: String, ?location: Location) -> void
|
15
19
|
def initialize(s_value:, alias_name: nil, location: nil)
|
16
20
|
s_value.freeze
|
17
21
|
@s_value = s_value
|
@@ -19,36 +23,44 @@ module Lrama
|
|
19
23
|
@location = location
|
20
24
|
end
|
21
25
|
|
26
|
+
# @rbs () -> String
|
22
27
|
def to_s
|
23
28
|
"value: `#{s_value}`, location: #{location}"
|
24
29
|
end
|
25
30
|
|
31
|
+
# @rbs (String string) -> bool
|
26
32
|
def referred_by?(string)
|
27
33
|
[self.s_value, self.alias_name].compact.include?(string)
|
28
34
|
end
|
29
35
|
|
36
|
+
# @rbs (Token other) -> bool
|
30
37
|
def ==(other)
|
31
38
|
self.class == other.class && self.s_value == other.s_value
|
32
39
|
end
|
33
40
|
|
41
|
+
# @rbs () -> Integer
|
34
42
|
def first_line
|
35
43
|
location.first_line
|
36
44
|
end
|
37
45
|
alias :line :first_line
|
38
46
|
|
47
|
+
# @rbs () -> Integer
|
39
48
|
def first_column
|
40
49
|
location.first_column
|
41
50
|
end
|
42
51
|
alias :column :first_column
|
43
52
|
|
53
|
+
# @rbs () -> Integer
|
44
54
|
def last_line
|
45
55
|
location.last_line
|
46
56
|
end
|
47
57
|
|
58
|
+
# @rbs () -> Integer
|
48
59
|
def last_column
|
49
60
|
location.last_column
|
50
61
|
end
|
51
62
|
|
63
|
+
# @rbs (Lrama::Grammar::Reference ref, String message) -> bot
|
52
64
|
def invalid_ref(ref, message)
|
53
65
|
location = self.location.partial_location(ref.first_column, ref.last_column)
|
54
66
|
raise location.generate_error_message(message)
|