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.
Files changed (44) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/test.yaml +2 -2
  3. data/Gemfile +1 -1
  4. data/LEGAL.md +1 -0
  5. data/NEWS.md +187 -0
  6. data/README.md +15 -4
  7. data/Steepfile +3 -0
  8. data/lib/lrama/grammar/code/printer_code.rb +1 -1
  9. data/lib/lrama/grammar/code/rule_action.rb +19 -3
  10. data/lib/lrama/grammar/code.rb +19 -7
  11. data/lib/lrama/grammar/parameterizing_rule.rb +6 -0
  12. data/lib/lrama/grammar/parameterizing_rule_builder.rb +34 -0
  13. data/lib/lrama/grammar/parameterizing_rule_resolver.rb +30 -0
  14. data/lib/lrama/grammar/parameterizing_rule_rhs_builder.rb +53 -0
  15. data/lib/lrama/grammar/rule_builder.rb +26 -22
  16. data/lib/lrama/grammar.rb +15 -41
  17. data/lib/lrama/lexer/grammar_file.rb +21 -0
  18. data/lib/lrama/lexer/location.rb +77 -2
  19. data/lib/lrama/lexer/token/instantiate_rule.rb +18 -0
  20. data/lib/lrama/lexer/token/user_code.rb +10 -10
  21. data/lib/lrama/lexer/token.rb +1 -1
  22. data/lib/lrama/lexer.rb +21 -11
  23. data/lib/lrama/parser.rb +619 -454
  24. data/lib/lrama/states_reporter.rb +1 -1
  25. data/lib/lrama/version.rb +1 -1
  26. data/parser.y +95 -30
  27. data/sig/lrama/grammar/code/printer_code.rbs +1 -1
  28. data/sig/lrama/grammar/code.rbs +5 -5
  29. data/sig/lrama/grammar/parameterizing_rule.rbs +10 -0
  30. data/sig/lrama/grammar/parameterizing_rule_builder.rbs +19 -0
  31. data/sig/lrama/grammar/parameterizing_rule_resolver.rbs +16 -0
  32. data/sig/lrama/grammar/parameterizing_rule_rhs_builder.rbs +18 -0
  33. data/sig/lrama/grammar/parameterizing_rules/builder/base.rbs +5 -3
  34. data/sig/lrama/grammar/parameterizing_rules/builder/separated_list.rbs +2 -0
  35. data/sig/lrama/grammar/parameterizing_rules/builder/separated_nonempty_list.rbs +2 -0
  36. data/sig/lrama/grammar/parameterizing_rules/builder.rbs +4 -3
  37. data/sig/lrama/grammar/rule_builder.rbs +2 -4
  38. data/sig/lrama/lexer/grammar_file.rbs +15 -0
  39. data/sig/lrama/lexer/location.rbs +13 -1
  40. data/sig/lrama/lexer/token/instantiate_rule.rbs +12 -0
  41. metadata +16 -6
  42. data/doc/TODO.md +0 -59
  43. data/lib/lrama/lexer/token/parameterizing.rb +0 -34
  44. data/sig/lrama/lexer/token/parameterizing.rbs +0 -17
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 634093bc73fd1504910364bffa6827a66fa7479f1f5b5238fdf577f0bb6d3d9d
4
- data.tar.gz: 96db570d049f47f20bf619535ee91e7f0f846a492404a54f7fe41a6addcce484
3
+ metadata.gz: 7b1a3e3181fc7060235e167ea97a92809b31aa5b74ef5978ab70415252bb87e3
4
+ data.tar.gz: 51a6ee0620321936fcd9d81c44ece3f9e444b977dc8fa940ddf920a5bb3b1dad
5
5
  SHA512:
6
- metadata.gz: 6b09e84cd16cd162f263e68abf7ada808418212ca449fab0f4a380c42c0193f479cad3d0d1afcc1d5f17d81b890e3754642d1a9433f7be5f6fc71fdcb2df3f97
7
- data.tar.gz: 1994a670f1033e737f2193e2e71ccaa0818e4144ac7f4424ebf24046ece23efc3f67eeb0927c5336663a244f270b321247b39dbbdbe76cfacc13b94da6289a8b
6
+ metadata.gz: f2b8805ad79cc0c3f125eba2715d274387191e6a355910d11c88f918eae4906b9398444162210c7ce955b1ba3f67cabf3b0679569a65d63bdc52328f2d7dc2f7
7
+ data.tar.gz: 93de8cc0c7f9154f37825d59010a4850cc81b8a66f460d70f95aed44c0eb1224e583d1afb95155be2d9ae063d286a4fa719c133c993c6ec9bfba1c490dfc997b
@@ -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.3.0", require: false
15
+ gem "rbs", "3.4.0", require: false
16
16
  gem "steep", "1.6.0", require: false
17
17
  end
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,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
- ### v0_5 (`master` branch)
52
+ ### v0_6 (`master` branch)
53
53
 
54
- This branch is for Ruby 3.3. `lrama_0_5` branch is created from this branch, once Ruby 3.3 is released.
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 PrinterCode < Code
5
- def initialize(type: nil, token_code: nil, tag: nil)
5
+ def initialize(type:, token_code:, tag:)
6
6
  super(type: type, token_code: token_code)
7
7
  @tag = tag
8
8
  end
@@ -2,7 +2,7 @@ module Lrama
2
2
  class Grammar
3
3
  class Code
4
4
  class RuleAction < Code
5
- def initialize(type: nil, token_code: nil, rule: nil)
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 $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,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
@@ -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 < Struct.new(:type, :token_code, keyword_init: true)
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..last_column] = str
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,6 @@
1
+ module Lrama
2
+ class Grammar
3
+ class ParameterizingRule < Struct.new(:rules, :token, keyword_init: true)
4
+ end
5
+ end
6
+ end
@@ -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`, `Parameterizing`, `UserCode` and so on.
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::Parameterizing
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
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
- raise "Referring symbol `#{ref_name}` is duplicated. #{token}" if candidates.size >= 2
150
- raise "Referring symbol `#{ref_name}` is not found. #{token}" unless referring_symbol = candidates.first
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