lrama 0.5.12 → 0.6.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/test.yaml +1 -1
- data/Gemfile +1 -1
- data/NEWS.md +20 -0
- data/README.md +14 -3
- data/Steepfile +3 -0
- data/lib/lrama/grammar/code/printer_code.rb +1 -1
- data/lib/lrama/grammar/code/rule_action.rb +1 -1
- data/lib/lrama/grammar/code.rb +19 -7
- data/lib/lrama/grammar/parameterizing_rule.rb +6 -0
- data/lib/lrama/grammar/parameterizing_rule_builder.rb +34 -0
- data/lib/lrama/grammar/parameterizing_rule_resolver.rb +30 -0
- data/lib/lrama/grammar/parameterizing_rule_rhs_builder.rb +53 -0
- data/lib/lrama/grammar/rule_builder.rb +26 -22
- data/lib/lrama/grammar.rb +15 -41
- data/lib/lrama/lexer/grammar_file.rb +21 -0
- data/lib/lrama/lexer/location.rb +77 -2
- data/lib/lrama/lexer/token/instantiate_rule.rb +18 -0
- data/lib/lrama/lexer/token/user_code.rb +10 -10
- data/lib/lrama/lexer/token.rb +1 -1
- data/lib/lrama/lexer.rb +21 -11
- data/lib/lrama/parser.rb +619 -454
- data/lib/lrama/states_reporter.rb +1 -1
- data/lib/lrama/version.rb +1 -1
- data/parser.y +95 -30
- data/sig/lrama/grammar/code/printer_code.rbs +1 -1
- data/sig/lrama/grammar/code.rbs +5 -5
- data/sig/lrama/grammar/parameterizing_rule.rbs +10 -0
- data/sig/lrama/grammar/parameterizing_rule_builder.rbs +19 -0
- data/sig/lrama/grammar/parameterizing_rule_resolver.rbs +16 -0
- data/sig/lrama/grammar/parameterizing_rule_rhs_builder.rbs +18 -0
- data/sig/lrama/grammar/parameterizing_rules/builder/base.rbs +2 -2
- data/sig/lrama/grammar/parameterizing_rules/builder.rbs +1 -1
- data/sig/lrama/grammar/rule_builder.rbs +2 -4
- data/sig/lrama/lexer/grammar_file.rbs +15 -0
- data/sig/lrama/lexer/location.rbs +13 -1
- data/sig/lrama/lexer/token/{parameterizing.rbs → instantiate_rule.rbs} +2 -7
- metadata +15 -5
- data/lib/lrama/lexer/token/parameterizing.rb +0 -34
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7b1a3e3181fc7060235e167ea97a92809b31aa5b74ef5978ab70415252bb87e3
|
4
|
+
data.tar.gz: 51a6ee0620321936fcd9d81c44ece3f9e444b977dc8fa940ddf920a5bb3b1dad
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f2b8805ad79cc0c3f125eba2715d274387191e6a355910d11c88f918eae4906b9398444162210c7ce955b1ba3f67cabf3b0679569a65d63bdc52328f2d7dc2f7
|
7
|
+
data.tar.gz: 93de8cc0c7f9154f37825d59010a4850cc81b8a66f460d70f95aed44c0eb1224e583d1afb95155be2d9ae063d286a4fa719c133c993c6ec9bfba1c490dfc997b
|
data/.github/workflows/test.yaml
CHANGED
data/Gemfile
CHANGED
@@ -12,6 +12,6 @@ gem "stackprof", platforms: [:ruby] # stackprof doesn't support Windows
|
|
12
12
|
# Recent steep requires Ruby >= 3.0.0.
|
13
13
|
# Then skip install on some CI jobs.
|
14
14
|
if !ENV['GITHUB_ACTION'] || ENV['INSTALL_STEEP'] == 'true'
|
15
|
-
gem "rbs", "3.
|
15
|
+
gem "rbs", "3.4.0", require: false
|
16
16
|
gem "steep", "1.6.0", require: false
|
17
17
|
end
|
data/NEWS.md
CHANGED
@@ -1,5 +1,25 @@
|
|
1
1
|
# NEWS for Lrama
|
2
2
|
|
3
|
+
## Lrama 0.6.0 (2023-12-25)
|
4
|
+
|
5
|
+
### User defined parameterizing rules
|
6
|
+
|
7
|
+
Allow to define parameterizing rule by `%rule` directive.
|
8
|
+
|
9
|
+
```
|
10
|
+
%rule pair(X, Y): X Y { $$ = $1 + $2; }
|
11
|
+
;
|
12
|
+
|
13
|
+
%%
|
14
|
+
|
15
|
+
program: stmt
|
16
|
+
;
|
17
|
+
|
18
|
+
stmt: pair(ODD, EVEN) <num>
|
19
|
+
| pair(EVEN, ODD) <num>
|
20
|
+
;
|
21
|
+
```
|
22
|
+
|
3
23
|
## Lrama 0.5.11 (2023-12-02)
|
4
24
|
|
5
25
|
### Type specification of parameterizing rules
|
data/README.md
CHANGED
@@ -49,9 +49,13 @@ Enter the formula:
|
|
49
49
|
|
50
50
|
## Versions and Branches
|
51
51
|
|
52
|
-
###
|
52
|
+
### v0_6 (`master` branch)
|
53
53
|
|
54
|
-
This branch is for Ruby 3.
|
54
|
+
This branch is for Ruby 3.4. `lrama_0_6` branch is created from this branch, once Ruby 3.4 is released.
|
55
|
+
|
56
|
+
### v0_5 (`lrama_0_5` branch)
|
57
|
+
|
58
|
+
This branch is for Ruby 3.3.
|
55
59
|
|
56
60
|
### v0_4 (`lrama_0_4` branch)
|
57
61
|
|
@@ -91,6 +95,13 @@ $ bundle exec rbs collection install
|
|
91
95
|
$ bundle exec steep check
|
92
96
|
```
|
93
97
|
|
98
|
+
Running both of them:
|
99
|
+
|
100
|
+
```shell
|
101
|
+
$ bundle install
|
102
|
+
$ bundle exec rake
|
103
|
+
```
|
104
|
+
|
94
105
|
### Profiling Lrama
|
95
106
|
|
96
107
|
#### 1. Create parse.tmp.y in ruby/ruby
|
@@ -136,7 +147,7 @@ $ stackprof --d3-flamegraph tmp/stackprof-cpu-myapp.dump > tmp/flamegraph.html
|
|
136
147
|
|
137
148
|
## Release flow
|
138
149
|
|
139
|
-
1. Update `Lrama::VERSION`
|
150
|
+
1. Update `Lrama::VERSION` and NEWS.md
|
140
151
|
2. Release as a gem by `rake release`
|
141
152
|
3. Update Lrama in ruby/ruby by `cp -r LEGAL.md NEWS.md MIT exe lib template ruby/tool/lrama`
|
142
153
|
4. Create new release on [GitHub](https://github.com/ruby/lrama/releases)
|
data/Steepfile
CHANGED
@@ -8,6 +8,9 @@ target :lib do
|
|
8
8
|
check "lib/lrama/grammar/code.rb"
|
9
9
|
check "lib/lrama/grammar/counter.rb"
|
10
10
|
check "lib/lrama/grammar/error_token.rb"
|
11
|
+
check "lib/lrama/grammar/parameterizing_rule_builder.rb"
|
12
|
+
check "lib/lrama/grammar/parameterizing_rule_resolver.rb"
|
13
|
+
check "lib/lrama/grammar/parameterizing_rule_rhs_builder.rb"
|
11
14
|
check "lib/lrama/grammar/parameterizing_rules"
|
12
15
|
check "lib/lrama/grammar/percent_code.rb"
|
13
16
|
check "lib/lrama/grammar/precedence.rb"
|
data/lib/lrama/grammar/code.rb
CHANGED
@@ -1,12 +1,29 @@
|
|
1
1
|
require "forwardable"
|
2
|
+
require "lrama/grammar/code/initial_action_code"
|
3
|
+
require "lrama/grammar/code/no_reference_code"
|
4
|
+
require "lrama/grammar/code/printer_code"
|
5
|
+
require "lrama/grammar/code/rule_action"
|
2
6
|
|
3
7
|
module Lrama
|
4
8
|
class Grammar
|
5
|
-
class Code
|
9
|
+
class Code
|
6
10
|
extend Forwardable
|
7
11
|
|
8
12
|
def_delegators "token_code", :s_value, :line, :column, :references
|
9
13
|
|
14
|
+
attr_reader :type, :token_code
|
15
|
+
|
16
|
+
def initialize(type:, token_code:)
|
17
|
+
@type = type
|
18
|
+
@token_code = token_code
|
19
|
+
end
|
20
|
+
|
21
|
+
def ==(other)
|
22
|
+
self.class == other.class &&
|
23
|
+
self.type == other.type &&
|
24
|
+
self.token_code == other.token_code
|
25
|
+
end
|
26
|
+
|
10
27
|
# $$, $n, @$, @n are translated to C code
|
11
28
|
def translated_code
|
12
29
|
t_code = s_value.dup
|
@@ -17,7 +34,7 @@ module Lrama
|
|
17
34
|
|
18
35
|
str = reference_to_c(ref)
|
19
36
|
|
20
|
-
t_code[first_column
|
37
|
+
t_code[first_column...last_column] = str
|
21
38
|
end
|
22
39
|
|
23
40
|
return t_code
|
@@ -31,8 +48,3 @@ module Lrama
|
|
31
48
|
end
|
32
49
|
end
|
33
50
|
end
|
34
|
-
|
35
|
-
require "lrama/grammar/code/initial_action_code"
|
36
|
-
require "lrama/grammar/code/no_reference_code"
|
37
|
-
require "lrama/grammar/code/printer_code"
|
38
|
-
require "lrama/grammar/code/rule_action"
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module Lrama
|
2
|
+
class Grammar
|
3
|
+
class ParameterizingRuleBuilder
|
4
|
+
attr_reader :name, :parameters, :rhs
|
5
|
+
|
6
|
+
def initialize(name, parameters, rhs)
|
7
|
+
@name = name
|
8
|
+
@parameters = parameters
|
9
|
+
@rhs = rhs
|
10
|
+
@required_parameters_count = parameters.count
|
11
|
+
end
|
12
|
+
|
13
|
+
def build_rules(token, actual_args, rule_counter, lhs_tag, line, rule_builders)
|
14
|
+
validate_argument_number!(token)
|
15
|
+
lhs = lhs(actual_args)
|
16
|
+
@rhs.map do |rhs|
|
17
|
+
rhs.build_rules(token, actual_args, parameters, rule_counter, lhs, lhs_tag, line, rule_builders)
|
18
|
+
end.flatten
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def validate_argument_number!(token)
|
24
|
+
unless @required_parameters_count == token.args.count
|
25
|
+
raise "Invalid number of arguments. expect: #{@required_parameters_count} actual: #{token.args.count}"
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def lhs(actual_args)
|
30
|
+
Lrama::Lexer::Token::Ident.new(s_value: "#{name}_#{actual_args.map(&:s_value).join('_')}")
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module Lrama
|
2
|
+
class Grammar
|
3
|
+
class ParameterizingRuleResolver
|
4
|
+
def initialize
|
5
|
+
@parameterizing_rule_builders = []
|
6
|
+
end
|
7
|
+
|
8
|
+
def add_parameterizing_rule_builder(builder)
|
9
|
+
@parameterizing_rule_builders << builder
|
10
|
+
end
|
11
|
+
|
12
|
+
def defined?(name)
|
13
|
+
!rule_builders(name).empty?
|
14
|
+
end
|
15
|
+
|
16
|
+
def build_rules(token, rule_counter, lhs_tag, line)
|
17
|
+
builder = rule_builders(token.s_value).last
|
18
|
+
raise "Unknown parameterizing rule #{token.s_value} at line #{token.line}" unless builder
|
19
|
+
|
20
|
+
builder.build_rules(token, token.args, rule_counter, lhs_tag, line, @parameterizing_rule_builders)
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def rule_builders(name)
|
26
|
+
@parameterizing_rule_builders.select { |builder| builder.name == name }
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
module Lrama
|
2
|
+
class Grammar
|
3
|
+
class ParameterizingRuleRhsBuilder
|
4
|
+
attr_accessor :symbols, :user_code, :precedence_sym
|
5
|
+
|
6
|
+
def initialize
|
7
|
+
@symbols = []
|
8
|
+
@user_code = nil
|
9
|
+
@precedence_sym = nil
|
10
|
+
end
|
11
|
+
|
12
|
+
def build_rules(token, actual_args, parameters, rule_counter, lhs, lhs_tag, line, rule_builders)
|
13
|
+
nested_rules = build_nested_rules(token, actual_args, parameters, rule_counter, lhs_tag, line, rule_builders)
|
14
|
+
rule = Rule.new(id: rule_counter.increment, _lhs: lhs, _rhs: rhs(token, actual_args, parameters, nested_rules.last), lhs_tag: lhs_tag, token_code: user_code, precedence_sym: precedence_sym, lineno: line)
|
15
|
+
ParameterizingRule.new(rules: nested_rules.map(&:rules) + [rule], token: lhs)
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
def build_nested_rules(token, actual_args, parameters, rule_counter, lhs_tag, line, rule_builders)
|
21
|
+
symbols.each_with_index.map do |sym, i|
|
22
|
+
next unless sym.is_a?(Lexer::Token::InstantiateRule)
|
23
|
+
|
24
|
+
builder = rule_builders.select { |builder| builder.name == sym.s_value }.last
|
25
|
+
raise "Unknown parameterizing rule #{token.s_value} at line #{token.line}" unless builder
|
26
|
+
|
27
|
+
builder.build_rules(sym, nested_actual_args(actual_args, parameters, i), rule_counter, lhs_tag, line, rule_builders)
|
28
|
+
end.flatten.compact
|
29
|
+
end
|
30
|
+
|
31
|
+
def nested_actual_args(actual_args, parameters, idx)
|
32
|
+
symbols[idx].args.map do |arg|
|
33
|
+
i = parameters.index { |parameter| parameter.s_value == arg.s_value }
|
34
|
+
i.nil? ? arg : actual_args[i]
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def rhs(token, actual_args, parameters, nested_rule)
|
39
|
+
symbols.map do |sym|
|
40
|
+
if sym.is_a?(Lexer::Token::InstantiateRule)
|
41
|
+
sym.args.map do |arg|
|
42
|
+
idx = parameters.index { |parameter| parameter.s_value == arg.s_value }
|
43
|
+
idx.nil? ? sym : nested_rule&.token
|
44
|
+
end
|
45
|
+
else
|
46
|
+
idx = parameters.index { |parameter| parameter.s_value == sym.s_value }
|
47
|
+
idx.nil? ? sym : actual_args[idx]
|
48
|
+
end
|
49
|
+
end.flatten
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -51,22 +51,14 @@ module Lrama
|
|
51
51
|
freeze_rhs
|
52
52
|
end
|
53
53
|
|
54
|
-
def setup_rules
|
54
|
+
def setup_rules(parameterizing_resolver)
|
55
55
|
preprocess_references unless @skip_preprocess_references
|
56
|
-
process_rhs
|
56
|
+
process_rhs(parameterizing_resolver)
|
57
57
|
build_rules
|
58
58
|
end
|
59
59
|
|
60
|
-
def parameterizing_rules
|
61
|
-
@parameterizing_rules
|
62
|
-
end
|
63
|
-
|
64
|
-
def midrule_action_rules
|
65
|
-
@midrule_action_rules
|
66
|
-
end
|
67
|
-
|
68
60
|
def rules
|
69
|
-
@rules
|
61
|
+
@parameterizing_rules + @midrule_action_rules + @rules
|
70
62
|
end
|
71
63
|
|
72
64
|
private
|
@@ -95,9 +87,9 @@ module Lrama
|
|
95
87
|
end
|
96
88
|
end
|
97
89
|
|
98
|
-
# rhs is a mixture of variety type of tokens like `Ident`, `
|
90
|
+
# rhs is a mixture of variety type of tokens like `Ident`, `InstantiateRule`, `UserCode` and so on.
|
99
91
|
# `#process_rhs` replaces some kind of tokens to `Ident` so that all `@replaced_rhs` are `Ident` or `Char`.
|
100
|
-
def process_rhs
|
92
|
+
def process_rhs(parameterizing_resolver)
|
101
93
|
return if @replaced_rhs
|
102
94
|
|
103
95
|
@replaced_rhs = []
|
@@ -109,12 +101,17 @@ module Lrama
|
|
109
101
|
@replaced_rhs << token
|
110
102
|
when Lrama::Lexer::Token::Ident
|
111
103
|
@replaced_rhs << token
|
112
|
-
when Lrama::Lexer::Token::
|
113
|
-
|
114
|
-
|
115
|
-
@parameterizing_rules
|
104
|
+
when Lrama::Lexer::Token::InstantiateRule
|
105
|
+
if parameterizing_resolver.defined?(token.rule_name)
|
106
|
+
parameterizing = parameterizing_resolver.build_rules(token, @rule_counter, @lhs_tag, line)
|
107
|
+
@parameterizing_rules = @parameterizing_rules + parameterizing.map(&:rules).flatten
|
108
|
+
@replaced_rhs = @replaced_rhs + parameterizing.map(&:token).flatten.uniq
|
109
|
+
else
|
110
|
+
# TODO: Delete when the standard library will defined as a grammar file.
|
111
|
+
parameterizing = ParameterizingRules::Builder.new(token, @rule_counter, @lhs_tag, user_code, precedence_sym, line)
|
112
|
+
@parameterizing_rules = @parameterizing_rules + parameterizing.build
|
113
|
+
@replaced_rhs << parameterizing.build_token
|
116
114
|
end
|
117
|
-
@replaced_rhs << parameterizing.build_token
|
118
115
|
when Lrama::Lexer::Token::UserCode
|
119
116
|
prefix = token.referred ? "@" : "$@"
|
120
117
|
new_token = Lrama::Lexer::Token::Ident.new(s_value: prefix + @midrule_action_counter.increment.to_s)
|
@@ -124,7 +121,7 @@ module Lrama
|
|
124
121
|
rule_builder.lhs = new_token
|
125
122
|
rule_builder.user_code = token
|
126
123
|
rule_builder.complete_input
|
127
|
-
rule_builder.setup_rules
|
124
|
+
rule_builder.setup_rules(parameterizing_resolver)
|
128
125
|
|
129
126
|
@rule_builders_for_derived_rules << rule_builder
|
130
127
|
else
|
@@ -146,8 +143,15 @@ module Lrama
|
|
146
143
|
else
|
147
144
|
candidates = rhs.each_with_index.select {|token, i| token.referred_by?(ref_name) }
|
148
145
|
|
149
|
-
|
150
|
-
|
146
|
+
if candidates.size >= 2
|
147
|
+
location = token.location.partial_location(ref.first_column, ref.last_column)
|
148
|
+
raise location.generate_error_message("Referring symbol `#{ref_name}` is duplicated.")
|
149
|
+
end
|
150
|
+
|
151
|
+
unless (referring_symbol = candidates.first)
|
152
|
+
location = token.location.partial_location(ref.first_column, ref.last_column)
|
153
|
+
raise location.generate_error_message("Referring symbol `#{ref_name}` is not found.")
|
154
|
+
end
|
151
155
|
|
152
156
|
ref.index = referring_symbol[1] + 1
|
153
157
|
end
|
@@ -167,7 +171,7 @@ module Lrama
|
|
167
171
|
end
|
168
172
|
|
169
173
|
def flush_user_code
|
170
|
-
if c = @user_code
|
174
|
+
if (c = @user_code)
|
171
175
|
@rhs << c
|
172
176
|
@user_code = nil
|
173
177
|
end
|
data/lib/lrama/grammar.rb
CHANGED
@@ -8,6 +8,10 @@ require "lrama/grammar/printer"
|
|
8
8
|
require "lrama/grammar/reference"
|
9
9
|
require "lrama/grammar/rule"
|
10
10
|
require "lrama/grammar/rule_builder"
|
11
|
+
require "lrama/grammar/parameterizing_rule_builder"
|
12
|
+
require "lrama/grammar/parameterizing_rule_resolver"
|
13
|
+
require "lrama/grammar/parameterizing_rule_rhs_builder"
|
14
|
+
require "lrama/grammar/parameterizing_rule"
|
11
15
|
require "lrama/grammar/symbol"
|
12
16
|
require "lrama/grammar/type"
|
13
17
|
require "lrama/grammar/union"
|
@@ -36,6 +40,7 @@ module Lrama
|
|
36
40
|
@rule_builders = []
|
37
41
|
@rules = []
|
38
42
|
@sym_to_rules = {}
|
43
|
+
@parameterizing_resolver = ParameterizingRuleResolver.new
|
39
44
|
@empty_symbol = nil
|
40
45
|
@eof_symbol = nil
|
41
46
|
@error_symbol = nil
|
@@ -69,7 +74,7 @@ module Lrama
|
|
69
74
|
return sym
|
70
75
|
end
|
71
76
|
|
72
|
-
if sym = @symbols.find {|s| s.id == id }
|
77
|
+
if (sym = @symbols.find {|s| s.id == id })
|
73
78
|
return sym
|
74
79
|
end
|
75
80
|
|
@@ -129,6 +134,10 @@ module Lrama
|
|
129
134
|
@rule_builders << builder
|
130
135
|
end
|
131
136
|
|
137
|
+
def add_parameterizing_rule_builder(builder)
|
138
|
+
@parameterizing_resolver.add_parameterizing_rule_builder(builder)
|
139
|
+
end
|
140
|
+
|
132
141
|
def prologue_first_lineno=(prologue_first_lineno)
|
133
142
|
@aux.prologue_first_lineno = prologue_first_lineno
|
134
143
|
end
|
@@ -310,7 +319,7 @@ module Lrama
|
|
310
319
|
|
311
320
|
def setup_rules
|
312
321
|
@rule_builders.each do |builder|
|
313
|
-
builder.setup_rules
|
322
|
+
builder.setup_rules(@parameterizing_resolver)
|
314
323
|
end
|
315
324
|
end
|
316
325
|
|
@@ -350,56 +359,21 @@ module Lrama
|
|
350
359
|
@accept_symbol = term
|
351
360
|
end
|
352
361
|
|
353
|
-
# 1. Add $accept rule to the top of rules
|
354
|
-
# 2. Extract action in the middle of RHS into new Empty rule
|
355
|
-
# 3. Append id and extract action then create Rule
|
356
|
-
#
|
357
|
-
# Bison 3.8.2 uses different orders for symbol number and rule number
|
358
|
-
# when a rule has actions in the middle of a rule.
|
359
|
-
#
|
360
|
-
# For example,
|
361
|
-
#
|
362
|
-
# `program: $@1 top_compstmt`
|
363
|
-
#
|
364
|
-
# Rules are ordered like below,
|
365
|
-
#
|
366
|
-
# 1 $@1: ε
|
367
|
-
# 2 program: $@1 top_compstmt
|
368
|
-
#
|
369
|
-
# Symbols are ordered like below,
|
370
|
-
#
|
371
|
-
# 164 program
|
372
|
-
# 165 $@1
|
373
|
-
#
|
374
362
|
def normalize_rules
|
375
|
-
#
|
376
|
-
accept = @accept_symbol
|
377
|
-
eof = @eof_symbol
|
363
|
+
# Add $accept rule to the top of rules
|
378
364
|
lineno = @rule_builders.first ? @rule_builders.first.line : 0
|
379
|
-
@rules << Rule.new(id: @rule_counter.increment, _lhs:
|
365
|
+
@rules << Rule.new(id: @rule_counter.increment, _lhs: @accept_symbol.id, _rhs: [@rule_builders.first.lhs, @eof_symbol.id], token_code: nil, lineno: lineno)
|
380
366
|
|
381
367
|
setup_rules
|
382
368
|
|
383
369
|
@rule_builders.each do |builder|
|
384
|
-
# Extract actions in the middle of RHS into new rules.
|
385
|
-
builder.midrule_action_rules.each do |rule|
|
386
|
-
@rules << rule
|
387
|
-
end
|
388
|
-
|
389
370
|
builder.rules.each do |rule|
|
390
|
-
add_nterm(id: rule._lhs)
|
391
|
-
@rules << rule
|
392
|
-
end
|
393
|
-
|
394
|
-
builder.parameterizing_rules.each do |rule|
|
395
371
|
add_nterm(id: rule._lhs, tag: rule.lhs_tag)
|
396
372
|
@rules << rule
|
397
373
|
end
|
398
|
-
|
399
|
-
builder.midrule_action_rules.each do |rule|
|
400
|
-
add_nterm(id: rule._lhs)
|
401
|
-
end
|
402
374
|
end
|
375
|
+
|
376
|
+
@rules.sort_by!(&:id)
|
403
377
|
end
|
404
378
|
|
405
379
|
# Collect symbols from rules
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Lrama
|
2
|
+
class Lexer
|
3
|
+
class GrammarFile
|
4
|
+
attr_reader :path, :text
|
5
|
+
|
6
|
+
def initialize(path, text)
|
7
|
+
@path = path
|
8
|
+
@text = text
|
9
|
+
end
|
10
|
+
|
11
|
+
def ==(other)
|
12
|
+
self.class == other.class &&
|
13
|
+
self.path == other.path
|
14
|
+
end
|
15
|
+
|
16
|
+
def lines
|
17
|
+
@lines ||= text.split("\n")
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
data/lib/lrama/lexer/location.rb
CHANGED
@@ -1,9 +1,10 @@
|
|
1
1
|
module Lrama
|
2
2
|
class Lexer
|
3
3
|
class Location
|
4
|
-
attr_reader :first_line, :first_column, :last_line, :last_column
|
4
|
+
attr_reader :grammar_file, :first_line, :first_column, :last_line, :last_column
|
5
5
|
|
6
|
-
def initialize(first_line:, first_column:, last_line:, last_column:)
|
6
|
+
def initialize(grammar_file:, first_line:, first_column:, last_line:, last_column:)
|
7
|
+
@grammar_file = grammar_file
|
7
8
|
@first_line = first_line
|
8
9
|
@first_column = first_column
|
9
10
|
@last_line = last_line
|
@@ -12,11 +13,85 @@ module Lrama
|
|
12
13
|
|
13
14
|
def ==(other)
|
14
15
|
self.class == other.class &&
|
16
|
+
self.grammar_file == other.grammar_file &&
|
15
17
|
self.first_line == other.first_line &&
|
16
18
|
self.first_column == other.first_column &&
|
17
19
|
self.last_line == other.last_line &&
|
18
20
|
self.last_column == other.last_column
|
19
21
|
end
|
22
|
+
|
23
|
+
def partial_location(left, right)
|
24
|
+
offset = -first_column
|
25
|
+
new_first_line = -1
|
26
|
+
new_first_column = -1
|
27
|
+
new_last_line = -1
|
28
|
+
new_last_column = -1
|
29
|
+
|
30
|
+
_text.each.with_index do |line, index|
|
31
|
+
new_offset = offset + line.length + 1
|
32
|
+
|
33
|
+
if offset <= left && left <= new_offset
|
34
|
+
new_first_line = first_line + index
|
35
|
+
new_first_column = left - offset
|
36
|
+
end
|
37
|
+
|
38
|
+
if offset <= right && right <= new_offset
|
39
|
+
new_last_line = first_line + index
|
40
|
+
new_last_column = right - offset
|
41
|
+
end
|
42
|
+
|
43
|
+
offset = new_offset
|
44
|
+
end
|
45
|
+
|
46
|
+
Location.new(
|
47
|
+
grammar_file: grammar_file,
|
48
|
+
first_line: new_first_line, first_column: new_first_column,
|
49
|
+
last_line: new_last_line, last_column: new_last_column
|
50
|
+
)
|
51
|
+
end
|
52
|
+
|
53
|
+
def to_s
|
54
|
+
"#{path} (#{first_line},#{first_column})-(#{last_line},#{last_column})"
|
55
|
+
end
|
56
|
+
|
57
|
+
def generate_error_message(error_message)
|
58
|
+
<<~ERROR.chomp
|
59
|
+
#{path}:#{first_line}:#{first_column}: #{error_message}
|
60
|
+
#{line_with_carets}
|
61
|
+
ERROR
|
62
|
+
end
|
63
|
+
|
64
|
+
def line_with_carets
|
65
|
+
<<~TEXT
|
66
|
+
#{text}
|
67
|
+
#{carets}
|
68
|
+
TEXT
|
69
|
+
end
|
70
|
+
|
71
|
+
private
|
72
|
+
|
73
|
+
def path
|
74
|
+
grammar_file.path
|
75
|
+
end
|
76
|
+
|
77
|
+
def blanks
|
78
|
+
(text[0...first_column] or raise "#{first_column} is invalid").gsub(/[^\t]/, ' ')
|
79
|
+
end
|
80
|
+
|
81
|
+
def carets
|
82
|
+
blanks + '^' * (last_column - first_column)
|
83
|
+
end
|
84
|
+
|
85
|
+
def text
|
86
|
+
@text ||= _text.join("\n")
|
87
|
+
end
|
88
|
+
|
89
|
+
def _text
|
90
|
+
@_text ||=begin
|
91
|
+
range = (first_line - 1)...last_line
|
92
|
+
grammar_file.lines[range] or raise "#{range} is invalid"
|
93
|
+
end
|
94
|
+
end
|
20
95
|
end
|
21
96
|
end
|
22
97
|
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Lrama
|
2
|
+
class Lexer
|
3
|
+
class Token
|
4
|
+
class InstantiateRule < Token
|
5
|
+
attr_accessor :args
|
6
|
+
|
7
|
+
def initialize(s_value:, alias_name: nil, location: nil, args: [])
|
8
|
+
super s_value: s_value, alias_name: alias_name, location: location
|
9
|
+
@args = args
|
10
|
+
end
|
11
|
+
|
12
|
+
def rule_name
|
13
|
+
s_value
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|