lrama 0.5.11 → 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 +2 -2
- data/Gemfile +1 -1
- data/LEGAL.md +1 -0
- data/NEWS.md +187 -0
- data/README.md +15 -4
- data/Steepfile +3 -0
- data/lib/lrama/grammar/code/printer_code.rb +1 -1
- data/lib/lrama/grammar/code/rule_action.rb +19 -3
- 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 +5 -3
- data/sig/lrama/grammar/parameterizing_rules/builder/separated_list.rbs +2 -0
- data/sig/lrama/grammar/parameterizing_rules/builder/separated_nonempty_list.rbs +2 -0
- data/sig/lrama/grammar/parameterizing_rules/builder.rbs +4 -3
- 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/instantiate_rule.rbs +12 -0
- metadata +16 -6
- data/doc/TODO.md +0 -59
- data/lib/lrama/lexer/token/parameterizing.rb +0 -34
- data/sig/lrama/lexer/token/parameterizing.rbs +0 -17
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
@@ -13,7 +13,7 @@ jobs:
|
|
13
13
|
strategy:
|
14
14
|
fail-fast: false
|
15
15
|
matrix:
|
16
|
-
ruby: ['head', '3.2', '3.1', '3.0', '2.7', '2.6', '2.5']
|
16
|
+
ruby: ['head', '3.3', '3.2', '3.1', '3.0', '2.7', '2.6', '2.5']
|
17
17
|
steps:
|
18
18
|
- uses: actions/checkout@v4
|
19
19
|
- uses: ruby/setup-ruby@v1
|
@@ -122,7 +122,7 @@ jobs:
|
|
122
122
|
- run: mkdir -p tool/lrama
|
123
123
|
working-directory: ../ruby
|
124
124
|
- name: Copy Lrama to ruby/tool
|
125
|
-
run: cp -r LEGAL.md MIT exe lib template ../ruby/tool/lrama
|
125
|
+
run: cp -r LEGAL.md NEWS.md MIT exe lib template ../ruby/tool/lrama
|
126
126
|
working-directory:
|
127
127
|
- run: tree tool/lrama
|
128
128
|
working-directory: ../ruby
|
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/LEGAL.md
CHANGED
data/NEWS.md
ADDED
@@ -0,0 +1,187 @@
|
|
1
|
+
# NEWS for Lrama
|
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
|
+
|
23
|
+
## Lrama 0.5.11 (2023-12-02)
|
24
|
+
|
25
|
+
### Type specification of parameterizing rules
|
26
|
+
|
27
|
+
Allow to specify type of rules by specifying tag, `<i>` in below example.
|
28
|
+
Tag is post-modification style.
|
29
|
+
|
30
|
+
```
|
31
|
+
%union {
|
32
|
+
int i;
|
33
|
+
}
|
34
|
+
|
35
|
+
%%
|
36
|
+
|
37
|
+
program : option(number) <i>
|
38
|
+
| number_alias? <i>
|
39
|
+
;
|
40
|
+
```
|
41
|
+
|
42
|
+
https://github.com/ruby/lrama/pull/272
|
43
|
+
|
44
|
+
|
45
|
+
## Lrama 0.5.10 (2023-11-18)
|
46
|
+
|
47
|
+
### Parameterizing rules (option, nonempty_list, list)
|
48
|
+
|
49
|
+
Support function call style parameterizing rules for `option`, `nonempty_list` and `list`.
|
50
|
+
|
51
|
+
https://github.com/ruby/lrama/pull/197
|
52
|
+
|
53
|
+
### Parameterizing rules (separated_list)
|
54
|
+
|
55
|
+
Support `separated_list` and `separated_nonempty_list` parameterizing rules.
|
56
|
+
|
57
|
+
```
|
58
|
+
program: separated_list(',', number)
|
59
|
+
|
60
|
+
// Expanded to
|
61
|
+
|
62
|
+
program: separated_list_number
|
63
|
+
separated_list_number: ε
|
64
|
+
separated_list_number: separated_nonempty_list_number
|
65
|
+
separated_nonempty_list_number: number
|
66
|
+
separated_nonempty_list_number: separated_nonempty_list_number ',' number
|
67
|
+
```
|
68
|
+
|
69
|
+
```
|
70
|
+
program: separated_nonempty_list(',', number)
|
71
|
+
|
72
|
+
// Expanded to
|
73
|
+
|
74
|
+
program: separated_nonempty_list_number
|
75
|
+
separated_nonempty_list_number: number
|
76
|
+
separated_nonempty_list_number: separated_nonempty_list_number ',' number
|
77
|
+
```
|
78
|
+
|
79
|
+
https://github.com/ruby/lrama/pull/204
|
80
|
+
|
81
|
+
## Lrama 0.5.9 (2023-11-05)
|
82
|
+
|
83
|
+
### Parameterizing rules (suffix)
|
84
|
+
|
85
|
+
Parameterizing rules are template of rules.
|
86
|
+
It's very common pattern to write "list" grammar rule like:
|
87
|
+
|
88
|
+
```
|
89
|
+
opt_args: /* none */
|
90
|
+
| args
|
91
|
+
;
|
92
|
+
|
93
|
+
args: arg
|
94
|
+
| args arg
|
95
|
+
```
|
96
|
+
|
97
|
+
Lrama supports these suffixes:
|
98
|
+
|
99
|
+
* `?`: option
|
100
|
+
* `+`: nonempty list
|
101
|
+
* `*`: list
|
102
|
+
|
103
|
+
Idea of Parameterizing rules comes from Menhir LR(1) parser generator (https://gallium.inria.fr/~fpottier/menhir/manual.html#sec32).
|
104
|
+
|
105
|
+
https://github.com/ruby/lrama/pull/181
|
106
|
+
|
107
|
+
## Lrama 0.5.7 (2023-10-23)
|
108
|
+
|
109
|
+
### Racc parser
|
110
|
+
|
111
|
+
Replace Lrama's parser from hand written parser to LR parser generated by Racc.
|
112
|
+
Lrama uses `--embedded` option to generate LR parser because Racc is changed from default gem to bundled gem by Ruby 3.3 (https://github.com/ruby/lrama/pull/132).
|
113
|
+
|
114
|
+
https://github.com/ruby/lrama/pull/62
|
115
|
+
|
116
|
+
## Lrama 0.5.4 (2023-08-17)
|
117
|
+
|
118
|
+
### Runtime configuration for error recovery
|
119
|
+
|
120
|
+
Meke error recovery function configurable on runtime by two new macros.
|
121
|
+
|
122
|
+
* `YYMAXREPAIR`: Expected to return max length of repair operations. `%parse-param` is passed to this function.
|
123
|
+
* `YYERROR_RECOVERY_ENABLED`: Expected to return bool value to determine error recovery is enabled or not. `%parse-param` is passed to this function.
|
124
|
+
|
125
|
+
https://github.com/ruby/lrama/pull/74
|
126
|
+
|
127
|
+
## Lrama 0.5.3 (2023-08-05)
|
128
|
+
|
129
|
+
### Error Recovery
|
130
|
+
|
131
|
+
Support token insert base Error Recovery.
|
132
|
+
`-e` option is needed to generate parser with error recovery functions.
|
133
|
+
|
134
|
+
https://github.com/ruby/lrama/pull/44
|
135
|
+
|
136
|
+
## Lrama 0.5.2 (2023-06-14)
|
137
|
+
|
138
|
+
### Named References
|
139
|
+
|
140
|
+
Instead of positional references like `$1` or `$$`,
|
141
|
+
named references allow to access to symbol by name.
|
142
|
+
|
143
|
+
```
|
144
|
+
primary: k_class cpath superclass bodystmt k_end
|
145
|
+
{
|
146
|
+
$primary = new_class($cpath, $bodystmt, $superclass);
|
147
|
+
}
|
148
|
+
```
|
149
|
+
|
150
|
+
Alias name can be declared.
|
151
|
+
|
152
|
+
```
|
153
|
+
expr[result]: expr[ex-left] '+' expr[ex.right]
|
154
|
+
{
|
155
|
+
$result = $[ex-left] + $[ex.right];
|
156
|
+
}
|
157
|
+
```
|
158
|
+
|
159
|
+
Bison supports this feature from 2.5.
|
160
|
+
|
161
|
+
### Add parse params to some macros and functions
|
162
|
+
|
163
|
+
`%parse-param` are added to these macros and functions to remove ytab.sed hack from Ruby.
|
164
|
+
|
165
|
+
* `YY_LOCATION_PRINT`
|
166
|
+
* `YY_SYMBOL_PRINT`
|
167
|
+
* `yy_stack_print`
|
168
|
+
* `YY_STACK_PRINT`
|
169
|
+
* `YY_REDUCE_PRINT`
|
170
|
+
* `yysyntax_error`
|
171
|
+
|
172
|
+
https://github.com/ruby/lrama/pull/40
|
173
|
+
|
174
|
+
See also: https://github.com/ruby/ruby/pull/7807
|
175
|
+
|
176
|
+
## Lrama 0.5.0 (2023-05-17)
|
177
|
+
|
178
|
+
### stdin mode
|
179
|
+
|
180
|
+
When `-` is given as grammar file name, reads the grammar source from STDIN, and takes the next argument as the input file name. This mode helps pre-process a grammar source.
|
181
|
+
|
182
|
+
https://github.com/ruby/lrama/pull/8
|
183
|
+
|
184
|
+
## Lrama 0.4.0 (2023-05-13)
|
185
|
+
|
186
|
+
This is the first version migrated to Ruby.
|
187
|
+
This version generates "parse.c" compatible with Bison 3.8.2.
|
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,9 +147,9 @@ $ 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
|
-
3. Update Lrama in ruby/ruby by `cp -r LEGAL.md MIT exe lib template ruby/tool/lrama`
|
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)
|
143
154
|
|
144
155
|
## License
|
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"
|
@@ -2,7 +2,7 @@ module Lrama
|
|
2
2
|
class Grammar
|
3
3
|
class Code
|
4
4
|
class RuleAction < Code
|
5
|
-
def initialize(type
|
5
|
+
def initialize(type:, token_code:, rule:)
|
6
6
|
super(type: type, token_code: token_code)
|
7
7
|
@rule = rule
|
8
8
|
end
|
@@ -14,9 +14,23 @@ module Lrama
|
|
14
14
|
# * ($1) yyvsp[i]
|
15
15
|
# * (@1) yylsp[i]
|
16
16
|
#
|
17
|
+
#
|
18
|
+
# Consider a rule like
|
19
|
+
#
|
20
|
+
# class: keyword_class { $1 } tSTRING { $2 + $3 } keyword_end { $class = $1 + $keyword_end }
|
21
|
+
#
|
22
|
+
# For the semantic action of original rule:
|
23
|
+
#
|
24
|
+
# "Rule" class: keyword_class { $1 } tSTRING { $2 + $3 } keyword_end { $class = $1 + $keyword_end }
|
25
|
+
# "Position in grammar" $1 $2 $3 $4 $5 $6
|
26
|
+
# "Index for yyvsp" -4 -3 -2 -1 0
|
27
|
+
#
|
28
|
+
#
|
29
|
+
# For the first midrule action:
|
30
|
+
#
|
17
31
|
# "Rule" class: keyword_class { $1 } tSTRING { $2 + $3 } keyword_end { $class = $1 + $keyword_end }
|
18
|
-
# "Position in grammar" $1
|
19
|
-
# "Index for yyvsp"
|
32
|
+
# "Position in grammar" $1
|
33
|
+
# "Index for yyvsp" 0
|
20
34
|
def reference_to_c(ref)
|
21
35
|
case
|
22
36
|
when ref.type == :dollar && ref.name == "$" # $$
|
@@ -45,10 +59,12 @@ module Lrama
|
|
45
59
|
@rule.position_in_original_rule_rhs || @rule.rhs.count
|
46
60
|
end
|
47
61
|
|
62
|
+
# If this is midrule action, RHS is a RHS of the original rule.
|
48
63
|
def rhs
|
49
64
|
(@rule.original_rule || @rule).rhs
|
50
65
|
end
|
51
66
|
|
67
|
+
# Unlike `rhs`, LHS is always a LHS of the rule.
|
52
68
|
def lhs
|
53
69
|
@rule.lhs
|
54
70
|
end
|
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
|