lrama 0.5.10 → 0.5.12
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 +22 -2
- data/LEGAL.md +1 -0
- data/NEWS.md +167 -0
- data/README.md +1 -1
- data/Steepfile +8 -12
- data/lib/lrama/grammar/code/rule_action.rb +19 -3
- data/lib/lrama/grammar/parameterizing_rules/builder/base.rb +10 -2
- data/lib/lrama/grammar/parameterizing_rules/builder/list.rb +12 -4
- data/lib/lrama/grammar/parameterizing_rules/builder/nonempty_list.rb +12 -4
- data/lib/lrama/grammar/parameterizing_rules/builder/option.rb +12 -4
- data/lib/lrama/grammar/parameterizing_rules/builder/separated_list.rb +17 -6
- data/lib/lrama/grammar/parameterizing_rules/builder/separated_nonempty_list.rb +12 -5
- data/lib/lrama/grammar/parameterizing_rules/builder.rb +23 -6
- data/lib/lrama/grammar/rule.rb +2 -1
- data/lib/lrama/grammar/rule_builder.rb +17 -19
- data/lib/lrama/grammar/symbol.rb +16 -2
- data/lib/lrama/grammar/type.rb +6 -0
- data/lib/lrama/grammar.rb +8 -3
- data/lib/lrama/lexer/token/parameterizing.rb +1 -1
- data/lib/lrama/lexer/token.rb +16 -9
- data/lib/lrama/lexer.rb +1 -2
- data/lib/lrama/parser.rb +359 -346
- data/lib/lrama/version.rb +1 -1
- data/lib/lrama.rb +0 -1
- data/parser.y +17 -15
- data/rbs_collection.lock.yaml +2 -8
- data/sig/lrama/grammar/error_token.rbs +11 -0
- data/sig/lrama/grammar/parameterizing_rules/builder/base.rbs +28 -0
- data/sig/lrama/grammar/parameterizing_rules/builder/list.rbs +10 -0
- data/sig/lrama/grammar/parameterizing_rules/builder/nonempty_list.rbs +10 -0
- data/sig/lrama/grammar/parameterizing_rules/builder/option.rbs +10 -0
- data/sig/lrama/grammar/parameterizing_rules/builder/separated_list.rbs +13 -0
- data/sig/lrama/grammar/parameterizing_rules/builder/separated_nonempty_list.rbs +13 -0
- data/sig/lrama/grammar/parameterizing_rules/builder.rbs +15 -1
- data/sig/lrama/grammar/reference.rbs +2 -2
- data/sig/lrama/grammar/rule.rbs +1 -1
- data/sig/lrama/grammar/rule_builder.rbs +1 -0
- data/sig/lrama/grammar/symbol.rbs +37 -0
- data/sig/lrama/lexer/token/parameterizing.rbs +3 -1
- data/sig/lrama/lexer/token.rbs +3 -3
- data/template/bison/yacc.c +0 -2
- metadata +12 -4
- data/doc/TODO.md +0 -59
- data/lib/lrama/type.rb +0 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz: '
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: '008f1a5df39b115123be84cb5e1edd379d88458bc95256b0e4fe4659481ceb51'
|
4
|
+
data.tar.gz: 02af30da8cf4cb2cad841f43b603433edafab09b0cf0e8993201c2e9aea12029
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 12cf8b98035052e89f9060dd73ec2b32ad9c5bec2fe4076e6ee0416832039962cf973c7ca895b482f2f434500648638c01a717d67357aa055eccce851995adaf
|
7
|
+
data.tar.gz: a4cffc85bb5945a3d4d037c0eadee0df22e30236085e01f06bffe1646d0acafd09fdb606860752cdea9dbfdf45baa1fc8d244504b7a0ca29c37ab60f9a727367
|
data/.github/workflows/test.yaml
CHANGED
@@ -35,10 +35,30 @@ jobs:
|
|
35
35
|
with:
|
36
36
|
ruby-version: ${{ matrix.ruby }}
|
37
37
|
bundler-cache: true
|
38
|
-
- run: choco install winflexbison
|
38
|
+
- run: choco install winflexbison || choco install winflexbison
|
39
39
|
- run: win_flex --help
|
40
40
|
- run: bundle install
|
41
41
|
- run: bundle exec rspec
|
42
|
+
test-memory:
|
43
|
+
runs-on: ubuntu-20.04
|
44
|
+
strategy:
|
45
|
+
fail-fast: false
|
46
|
+
matrix:
|
47
|
+
ruby: ['head']
|
48
|
+
steps:
|
49
|
+
- uses: actions/checkout@v4
|
50
|
+
- uses: ruby/setup-ruby@v1
|
51
|
+
with:
|
52
|
+
ruby-version: ${{ matrix.ruby }}
|
53
|
+
bundler-cache: true
|
54
|
+
- run: |
|
55
|
+
sudo apt-get update -q
|
56
|
+
sudo apt-get install --no-install-recommends -q -y valgrind
|
57
|
+
- run: valgrind --version
|
58
|
+
- run: bundle install
|
59
|
+
- run: bundle exec rspec spec/lrama/integration_spec.rb
|
60
|
+
env:
|
61
|
+
ENABEL_VALGRIND: 'true'
|
42
62
|
check-misc:
|
43
63
|
runs-on: ubuntu-20.04
|
44
64
|
strategy:
|
@@ -102,7 +122,7 @@ jobs:
|
|
102
122
|
- run: mkdir -p tool/lrama
|
103
123
|
working-directory: ../ruby
|
104
124
|
- name: Copy Lrama to ruby/tool
|
105
|
-
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
|
106
126
|
working-directory:
|
107
127
|
- run: tree tool/lrama
|
108
128
|
working-directory: ../ruby
|
data/LEGAL.md
CHANGED
data/NEWS.md
ADDED
@@ -0,0 +1,167 @@
|
|
1
|
+
# NEWS for Lrama
|
2
|
+
|
3
|
+
## Lrama 0.5.11 (2023-12-02)
|
4
|
+
|
5
|
+
### Type specification of parameterizing rules
|
6
|
+
|
7
|
+
Allow to specify type of rules by specifying tag, `<i>` in below example.
|
8
|
+
Tag is post-modification style.
|
9
|
+
|
10
|
+
```
|
11
|
+
%union {
|
12
|
+
int i;
|
13
|
+
}
|
14
|
+
|
15
|
+
%%
|
16
|
+
|
17
|
+
program : option(number) <i>
|
18
|
+
| number_alias? <i>
|
19
|
+
;
|
20
|
+
```
|
21
|
+
|
22
|
+
https://github.com/ruby/lrama/pull/272
|
23
|
+
|
24
|
+
|
25
|
+
## Lrama 0.5.10 (2023-11-18)
|
26
|
+
|
27
|
+
### Parameterizing rules (option, nonempty_list, list)
|
28
|
+
|
29
|
+
Support function call style parameterizing rules for `option`, `nonempty_list` and `list`.
|
30
|
+
|
31
|
+
https://github.com/ruby/lrama/pull/197
|
32
|
+
|
33
|
+
### Parameterizing rules (separated_list)
|
34
|
+
|
35
|
+
Support `separated_list` and `separated_nonempty_list` parameterizing rules.
|
36
|
+
|
37
|
+
```
|
38
|
+
program: separated_list(',', number)
|
39
|
+
|
40
|
+
// Expanded to
|
41
|
+
|
42
|
+
program: separated_list_number
|
43
|
+
separated_list_number: ε
|
44
|
+
separated_list_number: separated_nonempty_list_number
|
45
|
+
separated_nonempty_list_number: number
|
46
|
+
separated_nonempty_list_number: separated_nonempty_list_number ',' number
|
47
|
+
```
|
48
|
+
|
49
|
+
```
|
50
|
+
program: separated_nonempty_list(',', number)
|
51
|
+
|
52
|
+
// Expanded to
|
53
|
+
|
54
|
+
program: separated_nonempty_list_number
|
55
|
+
separated_nonempty_list_number: number
|
56
|
+
separated_nonempty_list_number: separated_nonempty_list_number ',' number
|
57
|
+
```
|
58
|
+
|
59
|
+
https://github.com/ruby/lrama/pull/204
|
60
|
+
|
61
|
+
## Lrama 0.5.9 (2023-11-05)
|
62
|
+
|
63
|
+
### Parameterizing rules (suffix)
|
64
|
+
|
65
|
+
Parameterizing rules are template of rules.
|
66
|
+
It's very common pattern to write "list" grammar rule like:
|
67
|
+
|
68
|
+
```
|
69
|
+
opt_args: /* none */
|
70
|
+
| args
|
71
|
+
;
|
72
|
+
|
73
|
+
args: arg
|
74
|
+
| args arg
|
75
|
+
```
|
76
|
+
|
77
|
+
Lrama supports these suffixes:
|
78
|
+
|
79
|
+
* `?`: option
|
80
|
+
* `+`: nonempty list
|
81
|
+
* `*`: list
|
82
|
+
|
83
|
+
Idea of Parameterizing rules comes from Menhir LR(1) parser generator (https://gallium.inria.fr/~fpottier/menhir/manual.html#sec32).
|
84
|
+
|
85
|
+
https://github.com/ruby/lrama/pull/181
|
86
|
+
|
87
|
+
## Lrama 0.5.7 (2023-10-23)
|
88
|
+
|
89
|
+
### Racc parser
|
90
|
+
|
91
|
+
Replace Lrama's parser from hand written parser to LR parser generated by Racc.
|
92
|
+
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).
|
93
|
+
|
94
|
+
https://github.com/ruby/lrama/pull/62
|
95
|
+
|
96
|
+
## Lrama 0.5.4 (2023-08-17)
|
97
|
+
|
98
|
+
### Runtime configuration for error recovery
|
99
|
+
|
100
|
+
Meke error recovery function configurable on runtime by two new macros.
|
101
|
+
|
102
|
+
* `YYMAXREPAIR`: Expected to return max length of repair operations. `%parse-param` is passed to this function.
|
103
|
+
* `YYERROR_RECOVERY_ENABLED`: Expected to return bool value to determine error recovery is enabled or not. `%parse-param` is passed to this function.
|
104
|
+
|
105
|
+
https://github.com/ruby/lrama/pull/74
|
106
|
+
|
107
|
+
## Lrama 0.5.3 (2023-08-05)
|
108
|
+
|
109
|
+
### Error Recovery
|
110
|
+
|
111
|
+
Support token insert base Error Recovery.
|
112
|
+
`-e` option is needed to generate parser with error recovery functions.
|
113
|
+
|
114
|
+
https://github.com/ruby/lrama/pull/44
|
115
|
+
|
116
|
+
## Lrama 0.5.2 (2023-06-14)
|
117
|
+
|
118
|
+
### Named References
|
119
|
+
|
120
|
+
Instead of positional references like `$1` or `$$`,
|
121
|
+
named references allow to access to symbol by name.
|
122
|
+
|
123
|
+
```
|
124
|
+
primary: k_class cpath superclass bodystmt k_end
|
125
|
+
{
|
126
|
+
$primary = new_class($cpath, $bodystmt, $superclass);
|
127
|
+
}
|
128
|
+
```
|
129
|
+
|
130
|
+
Alias name can be declared.
|
131
|
+
|
132
|
+
```
|
133
|
+
expr[result]: expr[ex-left] '+' expr[ex.right]
|
134
|
+
{
|
135
|
+
$result = $[ex-left] + $[ex.right];
|
136
|
+
}
|
137
|
+
```
|
138
|
+
|
139
|
+
Bison supports this feature from 2.5.
|
140
|
+
|
141
|
+
### Add parse params to some macros and functions
|
142
|
+
|
143
|
+
`%parse-param` are added to these macros and functions to remove ytab.sed hack from Ruby.
|
144
|
+
|
145
|
+
* `YY_LOCATION_PRINT`
|
146
|
+
* `YY_SYMBOL_PRINT`
|
147
|
+
* `yy_stack_print`
|
148
|
+
* `YY_STACK_PRINT`
|
149
|
+
* `YY_REDUCE_PRINT`
|
150
|
+
* `yysyntax_error`
|
151
|
+
|
152
|
+
https://github.com/ruby/lrama/pull/40
|
153
|
+
|
154
|
+
See also: https://github.com/ruby/ruby/pull/7807
|
155
|
+
|
156
|
+
## Lrama 0.5.0 (2023-05-17)
|
157
|
+
|
158
|
+
### stdin mode
|
159
|
+
|
160
|
+
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.
|
161
|
+
|
162
|
+
https://github.com/ruby/lrama/pull/8
|
163
|
+
|
164
|
+
## Lrama 0.4.0 (2023-05-13)
|
165
|
+
|
166
|
+
This is the first version migrated to Ruby.
|
167
|
+
This version generates "parse.c" compatible with Bison 3.8.2.
|
data/README.md
CHANGED
@@ -138,7 +138,7 @@ $ stackprof --d3-flamegraph tmp/stackprof-cpu-myapp.dump > tmp/flamegraph.html
|
|
138
138
|
|
139
139
|
1. Update `Lrama::VERSION`
|
140
140
|
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`
|
141
|
+
3. Update Lrama in ruby/ruby by `cp -r LEGAL.md NEWS.md MIT exe lib template ruby/tool/lrama`
|
142
142
|
4. Create new release on [GitHub](https://github.com/ruby/lrama/releases)
|
143
143
|
|
144
144
|
## License
|
data/Steepfile
CHANGED
@@ -4,24 +4,20 @@ target :lib do
|
|
4
4
|
repo_path '.gem_rbs_collection/'
|
5
5
|
signature "sig"
|
6
6
|
|
7
|
-
check "lib/lrama/bitmap.rb"
|
8
|
-
check "lib/lrama/digraph.rb"
|
9
|
-
check "lib/lrama/grammar/code.rb"
|
10
7
|
check "lib/lrama/grammar/code/printer_code.rb"
|
8
|
+
check "lib/lrama/grammar/code.rb"
|
11
9
|
check "lib/lrama/grammar/counter.rb"
|
10
|
+
check "lib/lrama/grammar/error_token.rb"
|
11
|
+
check "lib/lrama/grammar/parameterizing_rules"
|
12
12
|
check "lib/lrama/grammar/percent_code.rb"
|
13
13
|
check "lib/lrama/grammar/precedence.rb"
|
14
14
|
check "lib/lrama/grammar/printer.rb"
|
15
15
|
check "lib/lrama/grammar/reference.rb"
|
16
16
|
check "lib/lrama/grammar/rule_builder.rb"
|
17
|
-
check "lib/lrama/
|
18
|
-
check "lib/lrama/lexer
|
19
|
-
check "lib/lrama/
|
20
|
-
check "lib/lrama/
|
21
|
-
check "lib/lrama/
|
22
|
-
check "lib/lrama/lexer/location.rb"
|
23
|
-
check "lib/lrama/lexer/token.rb"
|
24
|
-
check "lib/lrama/report/duration.rb"
|
25
|
-
check "lib/lrama/report/profile.rb"
|
17
|
+
check "lib/lrama/grammar/symbol.rb"
|
18
|
+
check "lib/lrama/lexer"
|
19
|
+
check "lib/lrama/report"
|
20
|
+
check "lib/lrama/bitmap.rb"
|
21
|
+
check "lib/lrama/digraph.rb"
|
26
22
|
check "lib/lrama/warning.rb"
|
27
23
|
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,12 +59,14 @@ 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
|
55
71
|
|
56
72
|
def raise_tag_not_found_error(ref)
|
@@ -2,16 +2,24 @@ module Lrama
|
|
2
2
|
class Grammar
|
3
3
|
class ParameterizingRules
|
4
4
|
class Builder
|
5
|
+
# Base class for parameterizing rules builder
|
5
6
|
class Base
|
6
|
-
|
7
|
+
attr_reader :build_token
|
8
|
+
|
9
|
+
def initialize(token, rule_counter, lhs_tag, user_code, precedence_sym, line)
|
7
10
|
@args = token.args
|
8
11
|
@token = @args.first
|
9
12
|
@rule_counter = rule_counter
|
10
|
-
@
|
13
|
+
@lhs_tag = lhs_tag
|
11
14
|
@user_code = user_code
|
12
15
|
@precedence_sym = precedence_sym
|
13
16
|
@line = line
|
14
17
|
@expected_argument_num = 1
|
18
|
+
@build_token = nil
|
19
|
+
end
|
20
|
+
|
21
|
+
def build
|
22
|
+
raise NotImplementedError
|
15
23
|
end
|
16
24
|
|
17
25
|
private
|
@@ -2,15 +2,23 @@ module Lrama
|
|
2
2
|
class Grammar
|
3
3
|
class ParameterizingRules
|
4
4
|
class Builder
|
5
|
+
# Builder for list of general parameterizing rules
|
5
6
|
class List < Base
|
7
|
+
|
8
|
+
# program: list(number)
|
9
|
+
#
|
10
|
+
# =>
|
11
|
+
#
|
12
|
+
# program: list_number
|
13
|
+
# list_number: ε
|
14
|
+
# list_number: list_number number
|
6
15
|
def build
|
7
16
|
validate_argument_number!
|
8
17
|
|
9
18
|
rules = []
|
10
|
-
|
11
|
-
rules << Rule.new(id: @rule_counter.increment, _lhs: @
|
12
|
-
rules << Rule.new(id: @rule_counter.increment, _lhs:
|
13
|
-
rules << Rule.new(id: @rule_counter.increment, _lhs: list_token, _rhs: [list_token, @token], token_code: @user_code, precedence_sym: @precedence_sym, lineno: @line)
|
19
|
+
@build_token = Lrama::Lexer::Token::Ident.new(s_value: "list_#{@token.s_value}")
|
20
|
+
rules << Rule.new(id: @rule_counter.increment, _lhs: @build_token, _rhs: [], lhs_tag: @lhs_tag, token_code: @user_code, precedence_sym: @precedence_sym, lineno: @line)
|
21
|
+
rules << Rule.new(id: @rule_counter.increment, _lhs: @build_token, _rhs: [@build_token, @token], lhs_tag: @lhs_tag, token_code: @user_code, precedence_sym: @precedence_sym, lineno: @line)
|
14
22
|
rules
|
15
23
|
end
|
16
24
|
end
|
@@ -2,15 +2,23 @@ module Lrama
|
|
2
2
|
class Grammar
|
3
3
|
class ParameterizingRules
|
4
4
|
class Builder
|
5
|
+
# Builder for nonempty list of general parameterizing rules
|
5
6
|
class NonemptyList < Base
|
7
|
+
|
8
|
+
# program: nonempty_list(number)
|
9
|
+
#
|
10
|
+
# =>
|
11
|
+
#
|
12
|
+
# program: nonempty_list_number
|
13
|
+
# nonempty_list_number: number
|
14
|
+
# nonempty_list_number: nonempty_list_number number
|
6
15
|
def build
|
7
16
|
validate_argument_number!
|
8
17
|
|
9
18
|
rules = []
|
10
|
-
|
11
|
-
rules << Rule.new(id: @rule_counter.increment, _lhs: @
|
12
|
-
rules << Rule.new(id: @rule_counter.increment, _lhs:
|
13
|
-
rules << Rule.new(id: @rule_counter.increment, _lhs: nonempty_list_token, _rhs: [nonempty_list_token, @token], token_code: @user_code, precedence_sym: @precedence_sym, lineno: @line)
|
19
|
+
@build_token = Lrama::Lexer::Token::Ident.new(s_value: "nonempty_list_#{@token.s_value}")
|
20
|
+
rules << Rule.new(id: @rule_counter.increment, _lhs: @build_token, _rhs: [@token], lhs_tag: @lhs_tag, token_code: @user_code, precedence_sym: @precedence_sym, lineno: @line)
|
21
|
+
rules << Rule.new(id: @rule_counter.increment, _lhs: @build_token, _rhs: [@build_token, @token], lhs_tag: @lhs_tag, token_code: @user_code, precedence_sym: @precedence_sym, lineno: @line)
|
14
22
|
rules
|
15
23
|
end
|
16
24
|
end
|
@@ -2,15 +2,23 @@ module Lrama
|
|
2
2
|
class Grammar
|
3
3
|
class ParameterizingRules
|
4
4
|
class Builder
|
5
|
+
# Builder for option of general parameterizing rules
|
5
6
|
class Option < Base
|
7
|
+
|
8
|
+
# program: option(number)
|
9
|
+
#
|
10
|
+
# =>
|
11
|
+
#
|
12
|
+
# program: option_number
|
13
|
+
# option_number: ε
|
14
|
+
# option_number: number
|
6
15
|
def build
|
7
16
|
validate_argument_number!
|
8
17
|
|
9
18
|
rules = []
|
10
|
-
|
11
|
-
rules << Rule.new(id: @rule_counter.increment, _lhs: @
|
12
|
-
rules << Rule.new(id: @rule_counter.increment, _lhs:
|
13
|
-
rules << Rule.new(id: @rule_counter.increment, _lhs: option_token, _rhs: [@token], token_code: @ser_code, precedence_sym: @precedence_sym, lineno: @line)
|
19
|
+
@build_token = Lrama::Lexer::Token::Ident.new(s_value: "option_#{@token.s_value}")
|
20
|
+
rules << Rule.new(id: @rule_counter.increment, _lhs: @build_token, _rhs: [], lhs_tag: @lhs_tag, token_code: @user_code, precedence_sym: @precedence_sym, lineno: @line)
|
21
|
+
rules << Rule.new(id: @rule_counter.increment, _lhs: @build_token, _rhs: [@token], lhs_tag: @lhs_tag, token_code: @user_code, precedence_sym: @precedence_sym, lineno: @line)
|
14
22
|
rules
|
15
23
|
end
|
16
24
|
end
|
@@ -2,23 +2,34 @@ module Lrama
|
|
2
2
|
class Grammar
|
3
3
|
class ParameterizingRules
|
4
4
|
class Builder
|
5
|
+
# Builder for separated list of general parameterizing rules
|
5
6
|
class SeparatedList < Base
|
6
|
-
def initialize(token, rule_counter,
|
7
|
+
def initialize(token, rule_counter, lhs_tag, user_code, precedence_sym, line)
|
7
8
|
super
|
8
9
|
@separator = @args[0]
|
9
10
|
@token = @args[1]
|
10
11
|
@expected_argument_num = 2
|
11
12
|
end
|
12
13
|
|
14
|
+
# program: separated_list(',', number)
|
15
|
+
#
|
16
|
+
# =>
|
17
|
+
#
|
18
|
+
# program: separated_list_number
|
19
|
+
# separated_list_number: ε
|
20
|
+
# separated_list_number: separated_nonempty_list_number
|
21
|
+
# separated_nonempty_list_number: number
|
22
|
+
# separated_nonempty_list_number: separated_nonempty_list_number ',' number
|
13
23
|
def build
|
14
24
|
validate_argument_number!
|
15
25
|
|
16
26
|
rules = []
|
17
|
-
|
18
|
-
|
19
|
-
rules << Rule.new(id: @rule_counter.increment, _lhs:
|
20
|
-
rules << Rule.new(id: @rule_counter.increment, _lhs:
|
21
|
-
rules << Rule.new(id: @rule_counter.increment, _lhs:
|
27
|
+
@build_token = Lrama::Lexer::Token::Ident.new(s_value: "separated_list_#{@token.s_value}")
|
28
|
+
separated_nonempty_list_token = Lrama::Lexer::Token::Ident.new(s_value: "separated_nonempty_list_#{@token.s_value}")
|
29
|
+
rules << Rule.new(id: @rule_counter.increment, _lhs: @build_token, _rhs: [], lhs_tag: @lhs_tag, token_code: @user_code, precedence_sym: @precedence_sym, lineno: @line)
|
30
|
+
rules << Rule.new(id: @rule_counter.increment, _lhs: @build_token, _rhs: [separated_nonempty_list_token], lhs_tag: @lhs_tag, token_code: @user_code, precedence_sym: @precedence_sym, lineno: @line)
|
31
|
+
rules << Rule.new(id: @rule_counter.increment, _lhs: separated_nonempty_list_token, _rhs: [@token], lhs_tag: @lhs_tag, token_code: @user_code, precedence_sym: @precedence_sym, lineno: @line)
|
32
|
+
rules << Rule.new(id: @rule_counter.increment, _lhs: separated_nonempty_list_token, _rhs: [separated_nonempty_list_token, @separator, @token], lhs_tag: @lhs_tag, token_code: @user_code, precedence_sym: @precedence_sym, lineno: @line)
|
22
33
|
rules
|
23
34
|
end
|
24
35
|
end
|
@@ -2,22 +2,29 @@ module Lrama
|
|
2
2
|
class Grammar
|
3
3
|
class ParameterizingRules
|
4
4
|
class Builder
|
5
|
+
# Builder for separated nonempty list of general parameterizing rules
|
5
6
|
class SeparatedNonemptyList < Base
|
6
|
-
def initialize(token, rule_counter,
|
7
|
+
def initialize(token, rule_counter, lhs_tag, user_code, precedence_sym, line)
|
7
8
|
super
|
8
9
|
@separator = @args[0]
|
9
10
|
@token = @args[1]
|
10
11
|
@expected_argument_num = 2
|
11
12
|
end
|
12
13
|
|
14
|
+
# program: separated_nonempty_list(',', number)
|
15
|
+
#
|
16
|
+
# =>
|
17
|
+
#
|
18
|
+
# program: separated_nonempty_list_number
|
19
|
+
# separated_nonempty_list_number: number
|
20
|
+
# separated_nonempty_list_number: separated_nonempty_list_number ',' number
|
13
21
|
def build
|
14
22
|
validate_argument_number!
|
15
23
|
|
16
24
|
rules = []
|
17
|
-
|
18
|
-
rules << Rule.new(id: @rule_counter.increment, _lhs: @
|
19
|
-
rules << Rule.new(id: @rule_counter.increment, _lhs:
|
20
|
-
rules << Rule.new(id: @rule_counter.increment, _lhs: separated_list_token, _rhs: [separated_list_token, @separator, @token], token_code: @user_code, precedence_sym: @precedence_sym, lineno: @line)
|
25
|
+
@build_token = Lrama::Lexer::Token::Ident.new(s_value: "separated_nonempty_list_#{@token.s_value}")
|
26
|
+
rules << Rule.new(id: @rule_counter.increment, _lhs: @build_token, _rhs: [@token], lhs_tag: @lhs_tag, token_code: @user_code, precedence_sym: @precedence_sym, lineno: @line)
|
27
|
+
rules << Rule.new(id: @rule_counter.increment, _lhs: @build_token, _rhs: [@build_token, @separator, @token], lhs_tag: @lhs_tag, token_code: @user_code, precedence_sym: @precedence_sym, lineno: @line)
|
21
28
|
rules
|
22
29
|
end
|
23
30
|
end
|
@@ -8,6 +8,7 @@ require 'lrama/grammar/parameterizing_rules/builder/separated_list'
|
|
8
8
|
module Lrama
|
9
9
|
class Grammar
|
10
10
|
class ParameterizingRules
|
11
|
+
# Builder for parameterizing rules
|
11
12
|
class Builder
|
12
13
|
RULES = {
|
13
14
|
option: Lrama::Grammar::ParameterizingRules::Builder::Option,
|
@@ -20,23 +21,39 @@ module Lrama
|
|
20
21
|
separated_list: Lrama::Grammar::ParameterizingRules::Builder::SeparatedList,
|
21
22
|
}
|
22
23
|
|
23
|
-
def initialize(token, rule_counter,
|
24
|
+
def initialize(token, rule_counter, lhs_tag, user_code, precedence_sym, line)
|
24
25
|
@token = token
|
25
26
|
@key = token.s_value.to_sym
|
26
27
|
@rule_counter = rule_counter
|
27
|
-
@
|
28
|
+
@lhs_tag = lhs_tag
|
28
29
|
@user_code = user_code
|
29
30
|
@precedence_sym = precedence_sym
|
30
31
|
@line = line
|
32
|
+
@builder = nil
|
31
33
|
end
|
32
34
|
|
33
35
|
def build
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
36
|
+
create_builder
|
37
|
+
@builder.build
|
38
|
+
end
|
39
|
+
|
40
|
+
def build_token
|
41
|
+
create_builder
|
42
|
+
@builder.build_token
|
43
|
+
end
|
44
|
+
|
45
|
+
private
|
46
|
+
|
47
|
+
def create_builder
|
48
|
+
unless @builder
|
49
|
+
validate_key!
|
50
|
+
@builder = RULES[@key].new(@token, @rule_counter, @lhs_tag, @user_code, @precedence_sym, @line)
|
38
51
|
end
|
39
52
|
end
|
53
|
+
|
54
|
+
def validate_key!
|
55
|
+
raise "Parameterizing rule does not exist. `#{@key}`" unless RULES.key?(@key)
|
56
|
+
end
|
40
57
|
end
|
41
58
|
end
|
42
59
|
end
|
data/lib/lrama/grammar/rule.rb
CHANGED
@@ -1,12 +1,13 @@
|
|
1
1
|
module Lrama
|
2
2
|
class Grammar
|
3
3
|
# _rhs holds original RHS element. Use rhs to refer to Symbol.
|
4
|
-
class Rule < Struct.new(:id, :_lhs, :lhs, :_rhs, :rhs, :token_code, :position_in_original_rule_rhs, :nullable, :precedence_sym, :lineno, keyword_init: true)
|
4
|
+
class Rule < Struct.new(:id, :_lhs, :lhs, :lhs_tag, :_rhs, :rhs, :token_code, :position_in_original_rule_rhs, :nullable, :precedence_sym, :lineno, keyword_init: true)
|
5
5
|
attr_accessor :original_rule
|
6
6
|
|
7
7
|
def ==(other)
|
8
8
|
self.class == other.class &&
|
9
9
|
self.lhs == other.lhs &&
|
10
|
+
self.lhs_tag == other.lhs_tag &&
|
10
11
|
self.rhs == other.rhs &&
|
11
12
|
self.token_code == other.token_code &&
|
12
13
|
self.position_in_original_rule_rhs == other.position_in_original_rule_rhs &&
|
@@ -3,7 +3,7 @@ require 'lrama/grammar/parameterizing_rules/builder'
|
|
3
3
|
module Lrama
|
4
4
|
class Grammar
|
5
5
|
class RuleBuilder
|
6
|
-
attr_accessor :lhs, :line
|
6
|
+
attr_accessor :lhs, :lhs_tag, :line
|
7
7
|
attr_reader :rhs, :user_code, :precedence_sym
|
8
8
|
|
9
9
|
def initialize(rule_counter, midrule_action_counter, position_in_original_rule_rhs = nil, skip_preprocess_references: false)
|
@@ -14,6 +14,7 @@ module Lrama
|
|
14
14
|
|
15
15
|
@lhs = nil
|
16
16
|
@rhs = []
|
17
|
+
@lhs_tag = nil
|
17
18
|
@user_code = nil
|
18
19
|
@precedence_sym = nil
|
19
20
|
@line = nil
|
@@ -81,22 +82,16 @@ module Lrama
|
|
81
82
|
def build_rules
|
82
83
|
tokens = @replaced_rhs
|
83
84
|
|
84
|
-
|
85
|
-
|
86
|
-
@
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
@midrule_action_rules = @rule_builders_for_derived_rules.map do |rule_builder|
|
95
|
-
rule_builder.rules
|
96
|
-
end.flatten
|
97
|
-
@midrule_action_rules.each do |r|
|
98
|
-
r.original_rule = rule
|
99
|
-
end
|
85
|
+
rule = Rule.new(
|
86
|
+
id: @rule_counter.increment, _lhs: lhs, _rhs: tokens, token_code: user_code,
|
87
|
+
position_in_original_rule_rhs: @position_in_original_rule_rhs, precedence_sym: precedence_sym, lineno: line
|
88
|
+
)
|
89
|
+
@rules = [rule]
|
90
|
+
@midrule_action_rules = @rule_builders_for_derived_rules.map do |rule_builder|
|
91
|
+
rule_builder.rules
|
92
|
+
end.flatten
|
93
|
+
@midrule_action_rules.each do |r|
|
94
|
+
r.original_rule = rule
|
100
95
|
end
|
101
96
|
end
|
102
97
|
|
@@ -115,8 +110,11 @@ module Lrama
|
|
115
110
|
when Lrama::Lexer::Token::Ident
|
116
111
|
@replaced_rhs << token
|
117
112
|
when Lrama::Lexer::Token::Parameterizing
|
118
|
-
|
119
|
-
|
113
|
+
parameterizing = ParameterizingRules::Builder.new(token, @rule_counter, @lhs_tag, user_code, precedence_sym, line)
|
114
|
+
parameterizing.build.each do |r|
|
115
|
+
@parameterizing_rules << r
|
116
|
+
end
|
117
|
+
@replaced_rhs << parameterizing.build_token
|
120
118
|
when Lrama::Lexer::Token::UserCode
|
121
119
|
prefix = token.referred ? "@" : "$@"
|
122
120
|
new_token = Lrama::Lexer::Token::Ident.new(s_value: prefix + @midrule_action_counter.increment.to_s)
|