lrama 0.5.10 → 0.5.12

Sign up to get free protection for your applications and to get access to all the features.
Files changed (45) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/test.yaml +22 -2
  3. data/LEGAL.md +1 -0
  4. data/NEWS.md +167 -0
  5. data/README.md +1 -1
  6. data/Steepfile +8 -12
  7. data/lib/lrama/grammar/code/rule_action.rb +19 -3
  8. data/lib/lrama/grammar/parameterizing_rules/builder/base.rb +10 -2
  9. data/lib/lrama/grammar/parameterizing_rules/builder/list.rb +12 -4
  10. data/lib/lrama/grammar/parameterizing_rules/builder/nonempty_list.rb +12 -4
  11. data/lib/lrama/grammar/parameterizing_rules/builder/option.rb +12 -4
  12. data/lib/lrama/grammar/parameterizing_rules/builder/separated_list.rb +17 -6
  13. data/lib/lrama/grammar/parameterizing_rules/builder/separated_nonempty_list.rb +12 -5
  14. data/lib/lrama/grammar/parameterizing_rules/builder.rb +23 -6
  15. data/lib/lrama/grammar/rule.rb +2 -1
  16. data/lib/lrama/grammar/rule_builder.rb +17 -19
  17. data/lib/lrama/grammar/symbol.rb +16 -2
  18. data/lib/lrama/grammar/type.rb +6 -0
  19. data/lib/lrama/grammar.rb +8 -3
  20. data/lib/lrama/lexer/token/parameterizing.rb +1 -1
  21. data/lib/lrama/lexer/token.rb +16 -9
  22. data/lib/lrama/lexer.rb +1 -2
  23. data/lib/lrama/parser.rb +359 -346
  24. data/lib/lrama/version.rb +1 -1
  25. data/lib/lrama.rb +0 -1
  26. data/parser.y +17 -15
  27. data/rbs_collection.lock.yaml +2 -8
  28. data/sig/lrama/grammar/error_token.rbs +11 -0
  29. data/sig/lrama/grammar/parameterizing_rules/builder/base.rbs +28 -0
  30. data/sig/lrama/grammar/parameterizing_rules/builder/list.rbs +10 -0
  31. data/sig/lrama/grammar/parameterizing_rules/builder/nonempty_list.rbs +10 -0
  32. data/sig/lrama/grammar/parameterizing_rules/builder/option.rbs +10 -0
  33. data/sig/lrama/grammar/parameterizing_rules/builder/separated_list.rbs +13 -0
  34. data/sig/lrama/grammar/parameterizing_rules/builder/separated_nonempty_list.rbs +13 -0
  35. data/sig/lrama/grammar/parameterizing_rules/builder.rbs +15 -1
  36. data/sig/lrama/grammar/reference.rbs +2 -2
  37. data/sig/lrama/grammar/rule.rbs +1 -1
  38. data/sig/lrama/grammar/rule_builder.rbs +1 -0
  39. data/sig/lrama/grammar/symbol.rbs +37 -0
  40. data/sig/lrama/lexer/token/parameterizing.rbs +3 -1
  41. data/sig/lrama/lexer/token.rbs +3 -3
  42. data/template/bison/yacc.c +0 -2
  43. metadata +12 -4
  44. data/doc/TODO.md +0 -59
  45. data/lib/lrama/type.rb +0 -4
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: '0097beb1eb7af4947e417b356a7e3c81c3002916f4a1b2940b2df196ee58f144'
4
- data.tar.gz: 982c957cdb8c42d8b77210756046e35f395875bb527f32a66b3a82ff236cbe78
3
+ metadata.gz: '008f1a5df39b115123be84cb5e1edd379d88458bc95256b0e4fe4659481ceb51'
4
+ data.tar.gz: 02af30da8cf4cb2cad841f43b603433edafab09b0cf0e8993201c2e9aea12029
5
5
  SHA512:
6
- metadata.gz: a4bbdebc7f100a1b26c801c3c153519e3fb11d34f6dc5001391302665e779905d95041781917af93b2dac64854f174630044e384465bd5606df40f9f878707b1
7
- data.tar.gz: b46f8eb2dffa4df04563bbc8997a9efd06f5a70dcbc26e19f6301624aadf5bc416721045ecf6650b83ae64b9889689c8aeef078e9637e0a152a9ac204ab766de
6
+ metadata.gz: 12cf8b98035052e89f9060dd73ec2b32ad9c5bec2fe4076e6ee0416832039962cf973c7ca895b482f2f434500648638c01a717d67357aa055eccce851995adaf
7
+ data.tar.gz: a4cffc85bb5945a3d4d037c0eadee0df22e30236085e01f06bffe1646d0acafd09fdb606860752cdea9dbfdf45baa1fc8d244504b7a0ca29c37ab60f9a727367
@@ -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
@@ -7,5 +7,6 @@ mentioned below.
7
7
 
8
8
  These files are licensed under the GNU General Public License version 3 or later. See these files for more information.
9
9
 
10
+ * template/bison/_yacc.h
10
11
  * template/bison/yacc.c
11
12
  * template/bison/yacc.h
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/lexer/token/char.rb"
18
- check "lib/lrama/lexer/token/ident.rb"
19
- check "lib/lrama/lexer/token/parameterizing.rb"
20
- check "lib/lrama/lexer/token/tag.rb"
21
- check "lib/lrama/lexer/token/user_code.rb"
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 $2 $3 $4 $5 $6
19
- # "Index for yyvsp" -4 -3 -2 -1 0
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
- (@rule.original_rule || @rule).lhs
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
- def initialize(token, rule_counter, lhs, user_code, precedence_sym, line)
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
- @lhs = lhs
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
- list_token = Lrama::Lexer::Token::Ident.new(s_value: "list_#{@token.s_value}")
11
- rules << Rule.new(id: @rule_counter.increment, _lhs: @lhs, _rhs: [list_token], token_code: @user_code, precedence_sym: @precedence_sym, lineno: @line)
12
- rules << Rule.new(id: @rule_counter.increment, _lhs: list_token, _rhs: [], token_code: @user_code, precedence_sym: @precedence_sym, lineno: @line)
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
- nonempty_list_token = Lrama::Lexer::Token::Ident.new(s_value: "nonempty_list_#{@token.s_value}")
11
- rules << Rule.new(id: @rule_counter.increment, _lhs: @lhs, _rhs: [nonempty_list_token], token_code: @user_code, precedence_sym: @precedence_sym, lineno: @line)
12
- rules << Rule.new(id: @rule_counter.increment, _lhs: nonempty_list_token, _rhs: [@token], token_code: @user_code, precedence_sym: @precedence_sym, lineno: @line)
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
- option_token = Lrama::Lexer::Token::Ident.new(s_value: "option_#{@token.s_value}")
11
- rules << Rule.new(id: @rule_counter.increment, _lhs: @lhs, _rhs: [option_token], token_code: @user_code, precedence_sym: @precedence_sym, lineno: @line)
12
- rules << Rule.new(id: @rule_counter.increment, _lhs: option_token, _rhs: [], token_code: @user_code, precedence_sym: @precedence_sym, lineno: @line)
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, lhs, user_code, precedence_sym, line)
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
- separated_list_token = Lrama::Lexer::Token::Ident.new(s_value: "separated_list_#{@token.s_value}")
18
- rules << Rule.new(id: @rule_counter.increment, _lhs: @lhs, _rhs: [separated_list_token], token_code: @user_code, precedence_sym: @precedence_sym, lineno: @line)
19
- rules << Rule.new(id: @rule_counter.increment, _lhs: separated_list_token, _rhs: [], token_code: @user_code, precedence_sym: @precedence_sym, lineno: @line)
20
- rules << Rule.new(id: @rule_counter.increment, _lhs: separated_list_token, _rhs: [@token], token_code: @user_code, precedence_sym: @precedence_sym, lineno: @line)
21
- 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)
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, lhs, user_code, precedence_sym, line)
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
- separated_list_token = Lrama::Lexer::Token::Ident.new(s_value: "separated_nonempty_list_#{@token.s_value}")
18
- rules << Rule.new(id: @rule_counter.increment, _lhs: @lhs, _rhs: [separated_list_token], token_code: @user_code, precedence_sym: @precedence_sym, lineno: @line)
19
- rules << Rule.new(id: @rule_counter.increment, _lhs: separated_list_token, _rhs: [@token], token_code: @user_code, precedence_sym: @precedence_sym, lineno: @line)
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, lhs, user_code, precedence_sym, line)
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
- @lhs = lhs
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
- if RULES.key?(@key)
35
- RULES[@key].new(@token, @rule_counter, @lhs, @user_code, @precedence_sym, @line).build
36
- else
37
- raise "Parameterizing rule does not exist. `#{@key}`"
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
@@ -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
- # Expand Parameterizing rules
85
- if tokens.any? {|r| r.is_a?(Lrama::Lexer::Token::Parameterizing) }
86
- @rules = @parameterizing_rules
87
- @midrule_action_rules = []
88
- else
89
- rule = Rule.new(
90
- id: @rule_counter.increment, _lhs: lhs, _rhs: tokens, token_code: user_code,
91
- position_in_original_rule_rhs: @position_in_original_rule_rhs, precedence_sym: precedence_sym, lineno: line
92
- )
93
- @rules = [rule]
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
- @parameterizing_rules = ParameterizingRules::Builder.new(token, @rule_counter, lhs, user_code, precedence_sym, line).build
119
- @replaced_rhs << token
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)