lrama 0.5.11 → 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 +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
|