lrama 0.5.12 → 0.6.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/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
|