lrama 0.6.1 → 0.6.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (53) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/test.yaml +7 -2
  3. data/NEWS.md +43 -0
  4. data/README.md +23 -0
  5. data/Steepfile +2 -0
  6. data/lib/lrama/command.rb +10 -2
  7. data/lib/lrama/context.rb +6 -6
  8. data/lib/lrama/counterexamples/example.rb +2 -2
  9. data/lib/lrama/grammar/code/initial_action_code.rb +6 -0
  10. data/lib/lrama/grammar/code/no_reference_code.rb +4 -0
  11. data/lib/lrama/grammar/code/printer_code.rb +6 -0
  12. data/lib/lrama/grammar/code/rule_action.rb +11 -1
  13. data/lib/lrama/grammar/code.rb +1 -1
  14. data/lib/lrama/grammar/parameterizing_rule/resolver.rb +17 -9
  15. data/lib/lrama/grammar/reference.rb +4 -3
  16. data/lib/lrama/grammar/rule.rb +2 -2
  17. data/lib/lrama/grammar/rule_builder.rb +38 -36
  18. data/lib/lrama/grammar/stdlib.y +80 -0
  19. data/lib/lrama/grammar/symbol.rb +1 -1
  20. data/lib/lrama/grammar/symbols/resolver.rb +276 -0
  21. data/lib/lrama/grammar/symbols.rb +1 -0
  22. data/lib/lrama/grammar.rb +36 -246
  23. data/lib/lrama/lexer/token/user_code.rb +13 -2
  24. data/lib/lrama/lexer.rb +7 -0
  25. data/lib/lrama/output.rb +56 -2
  26. data/lib/lrama/parser.rb +571 -485
  27. data/lib/lrama/state.rb +4 -4
  28. data/lib/lrama/states/item.rb +19 -17
  29. data/lib/lrama/states_reporter.rb +10 -12
  30. data/lib/lrama/version.rb +1 -1
  31. data/lrama.gemspec +7 -0
  32. data/parser.y +24 -5
  33. data/sig/lrama/grammar/parameterizing_rule/resolver.rbs +1 -0
  34. data/sig/lrama/grammar/reference.rbs +2 -1
  35. data/sig/lrama/grammar/symbol.rbs +4 -4
  36. data/sig/lrama/grammar/symbols/resolver.rbs +41 -0
  37. data/sig/lrama/grammar/type.rbs +11 -0
  38. data/template/bison/yacc.c +6 -0
  39. metadata +13 -17
  40. data/lib/lrama/grammar/parameterizing_rules/builder/base.rb +0 -36
  41. data/lib/lrama/grammar/parameterizing_rules/builder/list.rb +0 -28
  42. data/lib/lrama/grammar/parameterizing_rules/builder/nonempty_list.rb +0 -28
  43. data/lib/lrama/grammar/parameterizing_rules/builder/option.rb +0 -28
  44. data/lib/lrama/grammar/parameterizing_rules/builder/separated_list.rb +0 -39
  45. data/lib/lrama/grammar/parameterizing_rules/builder/separated_nonempty_list.rb +0 -34
  46. data/lib/lrama/grammar/parameterizing_rules/builder.rb +0 -60
  47. data/sig/lrama/grammar/parameterizing_rules/builder/base.rbs +0 -28
  48. data/sig/lrama/grammar/parameterizing_rules/builder/list.rbs +0 -10
  49. data/sig/lrama/grammar/parameterizing_rules/builder/nonempty_list.rbs +0 -10
  50. data/sig/lrama/grammar/parameterizing_rules/builder/option.rbs +0 -10
  51. data/sig/lrama/grammar/parameterizing_rules/builder/separated_list.rbs +0 -13
  52. data/sig/lrama/grammar/parameterizing_rules/builder/separated_nonempty_list.rbs +0 -13
  53. data/sig/lrama/grammar/parameterizing_rules/builder.rbs +0 -24
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 50dc10cc2d790e778dcb6a35b789f146e69157b3c99d5b66059d95c025546ea6
4
- data.tar.gz: cbc16ede0db21b5d22f74c47dfd7a87af2ae20e15f72afc9ea8a79980531861d
3
+ metadata.gz: ecd30d3fab4dd73442ed6d3b2802db5b463159cb6ddf1f1835d9b8e860d4c9dd
4
+ data.tar.gz: 79b6087e68d3c2e95db81fa1d25f58280a5543e9c5fa91f5b6ecc7c40b5599d7
5
5
  SHA512:
6
- metadata.gz: 7567e78e28cb912eb6dae8182827b5c6555924757bffe4fd9db81f79dd117aaf00f0918c90ead79d3567eeee802b90556540141fd5d4083bf5d6013c075cb20c
7
- data.tar.gz: e58f803bc04114bdeb69b74a5e34ab9649241dafb9fe69a62f7d17ea87026411e3f743c54537fa092a9d6cea547a046fc762a78d9daebf92c3184be5502a24c1
6
+ metadata.gz: f3302156423399987015deb90afbaa0d6916e5e61b14c5297ff6a0e01ab9db3bbd164b334a11e26d38cf626425b7faee404e5d1cdec236a9b08b576ced4fe201
7
+ data.tar.gz: 380d8d31c93e5ae6c5a406c2b2eedad0d4b52dd311ecd742ca1412bd1acade7dae8fe12f7a6325ef8a0b4922ffd927b0dfa2caf3cd54c095669ab2b2cab85516
@@ -87,6 +87,11 @@ jobs:
87
87
  bundler-cache: true
88
88
  - run: bundle install
89
89
 
90
+ # Copy from https://github.com/ruby/ruby/blob/cb9a47f2acd6e373ef868b890a9d07da6f565dd4/.github/workflows/check_misc.yml#L31
91
+ - name: Check if C-sources are US-ASCII
92
+ run: |
93
+ grep -r -n --include='*.[chyS]' --include='*.asm' $'[^\t-~]' -- . && exit 1 || :
94
+
90
95
  # Copy from https://github.com/ruby/ruby/blob/089227e94823542acfdafa68541d330eee42ffea/.github/workflows/check_misc.yml#L27
91
96
  - name: Check for trailing spaces
92
97
  run: |
@@ -120,8 +125,8 @@ jobs:
120
125
  fail-fast: false
121
126
  matrix:
122
127
  # '3.0' is the oldest living ruby version
123
- # '2.5' is for BASERUBY
124
- baseruby: ['head', '3.0', '2.5']
128
+ # '2.7' is for BASERUBY
129
+ baseruby: ['head', '3.0', '2.7']
125
130
  ruby_branch: ['master']
126
131
  defaults:
127
132
  run:
data/NEWS.md CHANGED
@@ -1,5 +1,48 @@
1
1
  # NEWS for Lrama
2
2
 
3
+ ## Lrama 0.6.3 (2024-02-15)
4
+
5
+ ### Bring Your Own Stack
6
+
7
+ Provide functionalities for Bring Your Own Stack.
8
+
9
+ Ruby’s Ripper library requires their own semantic value stack to manage Ruby Objects returned by user defined callback method. Currently Ripper uses semantic value stack (`yyvsa`) which is used by parser to manage Node. This hack introduces some limitation on Ripper. For example, Ripper can not execute semantic analysis depending on Node structure.
10
+
11
+ Lrama introduces two features to support another semantic value stack by parser generator users.
12
+
13
+ 1. Callback entry points
14
+
15
+ User can emulate semantic value stack by these callbacks.
16
+ Lrama provides these five callbacks. Registered functions are called when each event happen. For example %after-shift function is called when shift happens on original semantic value stack.
17
+
18
+ * `%after-shift` function_name
19
+ * `%before-reduce` function_name
20
+ * `%after-reduce` function_name
21
+ * `%after-shift-error-token` function_name
22
+ * `%after-pop-stack` function_name
23
+
24
+ 2. `$:n` variable to access index of each grammar symbols
25
+
26
+ User also needs to access semantic value of their stack in grammar action. `$:n` provides the way to access to it. `$:n` is translated to the minus index from the top of the stack.
27
+ For example
28
+
29
+ ```
30
+ primary: k_if expr_value then compstmt if_tail k_end
31
+ {
32
+ /*% ripper: if!($:2, $:4, $:5) %*/
33
+ /* $:2 = -5, $:4 = -3, $:5 = -2. */
34
+ }
35
+ ```
36
+
37
+ ## Lrama 0.6.2 (2024-01-27)
38
+
39
+ ### %no-stdlib directive
40
+
41
+ If `%no-stdlib` directive is set, Lrama doesn't load Lrama standard library for
42
+ parameterizing rules, stdlib.y.
43
+
44
+ https://github.com/ruby/lrama/pull/344
45
+
3
46
  ## Lrama 0.6.1 (2024-01-13)
4
47
 
5
48
  ### Nested parameterizing rules
data/README.md CHANGED
@@ -1,7 +1,23 @@
1
1
  # Lrama
2
2
 
3
+ [![Gem Version](https://badge.fury.io/rb/lrama.svg)](https://badge.fury.io/rb/lrama)
4
+ [![build](https://github.com/ruby/lrama/actions/workflows/test.yaml/badge.svg)](https://github.com/ruby/lrama/actions/workflows/test.yaml)
5
+
3
6
  Lrama is LALR (1) parser generator written by Ruby. The first goal of this project is providing error tolerant parser for CRuby with minimal changes on CRuby parse.y file.
4
7
 
8
+ * [Features](#features)
9
+ * [Installation](#installation)
10
+ * [Usage](#usage)
11
+ * [Versions and Branches](#versions-and-branches)
12
+ * [Supported Ruby version](#supported-ruby-version)
13
+ * [Development](#development)
14
+ * [How to generate parser.rb](#how-to-generate-parserrb)
15
+ * [Test](#test)
16
+ * [Profiling Lrama](#profiling-lrama)
17
+ * [Build Ruby](#build-ruby)
18
+ * [Release flow](#release-flow)
19
+ * [License](#license)
20
+
5
21
  ## Features
6
22
 
7
23
  * Bison style grammar file is supported with some assumptions
@@ -11,6 +27,9 @@ Lrama is LALR (1) parser generator written by Ruby. The first goal of this proje
11
27
  * b4_lac_if is always false
12
28
  * Error Tolerance parser
13
29
  * Subset of [Repairing Syntax Errors in LR Parsers (Corchuelo et al.)](https://idus.us.es/bitstream/handle/11441/65631/Repairing%20syntax%20errors.pdf) algorithm is supported
30
+ * Parameterizing rules
31
+ * The definition of a non-terminal symbol can be parameterized with other (terminal or non-terminal) symbols.
32
+ * Providing a generic definition of parameterizing rules as a [standard library](lib/lrama/grammar/stdlib.y).
14
33
 
15
34
  ## Installation
16
35
 
@@ -85,6 +104,8 @@ Running tests:
85
104
  ```shell
86
105
  $ bundle install
87
106
  $ bundle exec rspec
107
+ # or
108
+ $ bundle exec rake spec
88
109
  ```
89
110
 
90
111
  Running type check:
@@ -93,6 +114,8 @@ Running type check:
93
114
  $ bundle install
94
115
  $ bundle exec rbs collection install
95
116
  $ bundle exec steep check
117
+ # or
118
+ $ bundle exec rake steep
96
119
  ```
97
120
 
98
121
  Running both of them:
data/Steepfile CHANGED
@@ -11,12 +11,14 @@ target :lib do
11
11
  check "lib/lrama/grammar/error_token.rb"
12
12
  check "lib/lrama/grammar/parameterizing_rule"
13
13
  check "lib/lrama/grammar/parameterizing_rules"
14
+ check "lib/lrama/grammar/symbols"
14
15
  check "lib/lrama/grammar/percent_code.rb"
15
16
  check "lib/lrama/grammar/precedence.rb"
16
17
  check "lib/lrama/grammar/printer.rb"
17
18
  check "lib/lrama/grammar/reference.rb"
18
19
  check "lib/lrama/grammar/rule_builder.rb"
19
20
  check "lib/lrama/grammar/symbol.rb"
21
+ check "lib/lrama/grammar/type.rb"
20
22
  check "lib/lrama/lexer"
21
23
  check "lib/lrama/report"
22
24
  check "lib/lrama/bitmap.rb"
data/lib/lrama/command.rb CHANGED
@@ -1,5 +1,8 @@
1
1
  module Lrama
2
2
  class Command
3
+ LRAMA_LIB = File.realpath(File.join(File.dirname(__FILE__)))
4
+ STDLIB_FILE_PATH = File.join(LRAMA_LIB, 'grammar', 'stdlib.y')
5
+
3
6
  def run(argv)
4
7
  begin
5
8
  options = OptionParser.new.parse(argv)
@@ -14,9 +17,14 @@ module Lrama
14
17
  warning = Lrama::Warning.new
15
18
  text = options.y.read
16
19
  options.y.close if options.y != STDIN
17
- parser = Lrama::Parser.new(text, options.grammar_file, options.debug)
18
20
  begin
19
- grammar = parser.parse
21
+ grammar = Lrama::Parser.new(text, options.grammar_file, options.debug).parse
22
+ unless grammar.no_stdlib
23
+ stdlib_grammar = Lrama::Parser.new(File.read(STDLIB_FILE_PATH), STDLIB_FILE_PATH, options.debug).parse
24
+ grammar.insert_before_parameterizing_rules(stdlib_grammar.parameterizing_rules)
25
+ end
26
+ grammar.prepare
27
+ grammar.validate!
20
28
  rescue => e
21
29
  raise e if options.debug
22
30
  message = e.message
data/lib/lrama/context.rb CHANGED
@@ -41,7 +41,7 @@ module Lrama
41
41
  def yyfinal
42
42
  @states.states.find do |state|
43
43
  state.items.find do |item|
44
- item.rule.lhs.accept_symbol? && item.end_of_rule?
44
+ item.lhs.accept_symbol? && item.end_of_rule?
45
45
  end
46
46
  end.id
47
47
  end
@@ -221,7 +221,7 @@ module Lrama
221
221
 
222
222
  if state.reduces.map(&:selected_look_ahead).any? {|la| !la.empty? }
223
223
  # Iterate reduces with reverse order so that first rule is used.
224
- state.reduces.reverse.each do |reduce|
224
+ state.reduces.reverse_each do |reduce|
225
225
  reduce.look_ahead.each do |term|
226
226
  actions[term.number] = rule_id_to_action_number(reduce.rule.id)
227
227
  end
@@ -265,9 +265,9 @@ module Lrama
265
265
 
266
266
  s = actions.each_with_index.map do |n, i|
267
267
  [i, n]
268
- end.select do |i, n|
268
+ end.reject do |i, n|
269
269
  # Remove default_reduction_rule entries
270
- n != 0
270
+ n == 0
271
271
  end
272
272
 
273
273
  if s.count != 0
@@ -462,7 +462,7 @@ module Lrama
462
462
  @yylast = high
463
463
 
464
464
  # replace_ninf
465
- @yypact_ninf = (@base.select {|i| i != BaseMin } + [0]).min - 1
465
+ @yypact_ninf = (@base.reject {|i| i == BaseMin } + [0]).min - 1
466
466
  @base.map! do |i|
467
467
  case i
468
468
  when BaseMin
@@ -472,7 +472,7 @@ module Lrama
472
472
  end
473
473
  end
474
474
 
475
- @yytable_ninf = (@table.compact.select {|i| i != ErrorActionNumber } + [0]).min - 1
475
+ @yytable_ninf = (@table.compact.reject {|i| i == ErrorActionNumber } + [0]).min - 1
476
476
  @table.map! do |i|
477
477
  case i
478
478
  when nil
@@ -40,7 +40,7 @@ module Lrama
40
40
  current = :production
41
41
  lookahead_sym = paths.last.to.item.end_of_rule? ? @conflict_symbol : nil
42
42
 
43
- paths.reverse.each do |path|
43
+ paths.reverse_each do |path|
44
44
  item = path.to.item
45
45
 
46
46
  case current
@@ -97,7 +97,7 @@ module Lrama
97
97
  if next_sym == sym
98
98
  derivation = nil
99
99
 
100
- sis.reverse.each do |si|
100
+ sis.reverse_each do |si|
101
101
  derivation = Derivation.new(si.item, derivation)
102
102
  end
103
103
 
@@ -6,18 +6,24 @@ module Lrama
6
6
 
7
7
  # * ($$) yylval
8
8
  # * (@$) yylloc
9
+ # * ($:$) error
9
10
  # * ($1) error
10
11
  # * (@1) error
12
+ # * ($:1) error
11
13
  def reference_to_c(ref)
12
14
  case
13
15
  when ref.type == :dollar && ref.name == "$" # $$
14
16
  "yylval"
15
17
  when ref.type == :at && ref.name == "$" # @$
16
18
  "yylloc"
19
+ when ref.type == :index && ref.name == "$" # $:$
20
+ raise "$:#{ref.value} can not be used in initial_action."
17
21
  when ref.type == :dollar # $n
18
22
  raise "$#{ref.value} can not be used in initial_action."
19
23
  when ref.type == :at # @n
20
24
  raise "@#{ref.value} can not be used in initial_action."
25
+ when ref.type == :index # $:n
26
+ raise "$:#{ref.value} can not be used in initial_action."
21
27
  else
22
28
  raise "Unexpected. #{self}, #{ref}"
23
29
  end
@@ -6,14 +6,18 @@ module Lrama
6
6
 
7
7
  # * ($$) error
8
8
  # * (@$) error
9
+ # * ($:$) error
9
10
  # * ($1) error
10
11
  # * (@1) error
12
+ # * ($:1) error
11
13
  def reference_to_c(ref)
12
14
  case
13
15
  when ref.type == :dollar # $$, $n
14
16
  raise "$#{ref.value} can not be used in #{type}."
15
17
  when ref.type == :at # @$, @n
16
18
  raise "@#{ref.value} can not be used in #{type}."
19
+ when ref.type == :index # $:$, $:n
20
+ raise "$:#{ref.value} can not be used in #{type}."
17
21
  else
18
22
  raise "Unexpected. #{self}, #{ref}"
19
23
  end
@@ -11,8 +11,10 @@ module Lrama
11
11
 
12
12
  # * ($$) *yyvaluep
13
13
  # * (@$) *yylocationp
14
+ # * ($:$) error
14
15
  # * ($1) error
15
16
  # * (@1) error
17
+ # * ($:1) error
16
18
  def reference_to_c(ref)
17
19
  case
18
20
  when ref.type == :dollar && ref.name == "$" # $$
@@ -20,10 +22,14 @@ module Lrama
20
22
  "((*yyvaluep).#{member})"
21
23
  when ref.type == :at && ref.name == "$" # @$
22
24
  "(*yylocationp)"
25
+ when ref.type == :index && ref.name == "$" # $:$
26
+ raise "$:#{ref.value} can not be used in #{type}."
23
27
  when ref.type == :dollar # $n
24
28
  raise "$#{ref.value} can not be used in #{type}."
25
29
  when ref.type == :at # @n
26
30
  raise "@#{ref.value} can not be used in #{type}."
31
+ when ref.type == :index # $:n
32
+ raise "$:#{ref.value} can not be used in #{type}."
27
33
  else
28
34
  raise "Unexpected. #{self}, #{ref}"
29
35
  end
@@ -11,8 +11,10 @@ module Lrama
11
11
 
12
12
  # * ($$) yyval
13
13
  # * (@$) yyloc
14
+ # * ($:$) error
14
15
  # * ($1) yyvsp[i]
15
16
  # * (@1) yylsp[i]
17
+ # * ($:1) i - 1
16
18
  #
17
19
  #
18
20
  # Consider a rule like
@@ -24,6 +26,8 @@ module Lrama
24
26
  # "Rule" class: keyword_class { $1 } tSTRING { $2 + $3 } keyword_end { $class = $1 + $keyword_end }
25
27
  # "Position in grammar" $1 $2 $3 $4 $5
26
28
  # "Index for yyvsp" -4 -3 -2 -1 0
29
+ # "$:n" $:1 $:2 $:3 $:4 $:5
30
+ # "index of $:n" -5 -4 -3 -2 -1
27
31
  #
28
32
  #
29
33
  # For the first midrule action:
@@ -31,6 +35,7 @@ module Lrama
31
35
  # "Rule" class: keyword_class { $1 } tSTRING { $2 + $3 } keyword_end { $class = $1 + $keyword_end }
32
36
  # "Position in grammar" $1
33
37
  # "Index for yyvsp" 0
38
+ # "$:n" $:1
34
39
  def reference_to_c(ref)
35
40
  case
36
41
  when ref.type == :dollar && ref.name == "$" # $$
@@ -39,6 +44,8 @@ module Lrama
39
44
  "(yyval.#{tag.member})"
40
45
  when ref.type == :at && ref.name == "$" # @$
41
46
  "(yyloc)"
47
+ when ref.type == :index && ref.name == "$" # $:$
48
+ raise "$:$ is not supported"
42
49
  when ref.type == :dollar # $n
43
50
  i = -position_in_rhs + ref.index
44
51
  tag = ref.ex_tag || rhs[ref.index - 1].tag
@@ -47,6 +54,9 @@ module Lrama
47
54
  when ref.type == :at # @n
48
55
  i = -position_in_rhs + ref.index
49
56
  "(yylsp[#{i}])"
57
+ when ref.type == :index # $:n
58
+ i = -position_in_rhs + ref.index
59
+ "(#{i} - 1)"
50
60
  else
51
61
  raise "Unexpected. #{self}, #{ref}"
52
62
  end
@@ -70,7 +80,7 @@ module Lrama
70
80
  end
71
81
 
72
82
  def raise_tag_not_found_error(ref)
73
- raise "Tag is not specified for '$#{ref.value}' in '#{@rule.to_s}'"
83
+ raise "Tag is not specified for '$#{ref.value}' in '#{@rule}'"
74
84
  end
75
85
  end
76
86
  end
@@ -28,7 +28,7 @@ module Lrama
28
28
  def translated_code
29
29
  t_code = s_value.dup
30
30
 
31
- references.reverse.each do |ref|
31
+ references.reverse_each do |ref|
32
32
  first_column = ref.first_column
33
33
  last_column = ref.last_column
34
34
 
@@ -2,7 +2,7 @@ module Lrama
2
2
  class Grammar
3
3
  class ParameterizingRule
4
4
  class Resolver
5
- attr_accessor :created_lhs_list
5
+ attr_accessor :rules, :created_lhs_list
6
6
 
7
7
  def initialize
8
8
  @rules = []
@@ -13,24 +13,32 @@ module Lrama
13
13
  @rules << rule
14
14
  end
15
15
 
16
- def defined?(token)
17
- !select_rules(token).empty?
18
- end
19
-
20
16
  def find(token)
21
17
  select_rules(token).last
22
18
  end
23
19
 
24
20
  def created_lhs(lhs_s_value)
25
- @created_lhs_list.select { |created_lhs| created_lhs.s_value == lhs_s_value }.last
21
+ @created_lhs_list.reverse.find { |created_lhs| created_lhs.s_value == lhs_s_value }
26
22
  end
27
23
 
28
24
  private
29
25
 
30
26
  def select_rules(token)
31
- @rules.select do |rule|
32
- rule.name == token.rule_name &&
33
- rule.required_parameters_count == token.args_count
27
+ rules = select_rules_by_name(token.rule_name)
28
+ rules = rules.select { |rule| rule.required_parameters_count == token.args_count }
29
+ if rules.empty?
30
+ raise "Invalid number of arguments. `#{token.rule_name}`"
31
+ else
32
+ rules
33
+ end
34
+ end
35
+
36
+ def select_rules_by_name(rule_name)
37
+ rules = @rules.select { |rule| rule.name == rule_name }
38
+ if rules.empty?
39
+ raise "Parameterizing rule does not exist. `#{rule_name}`"
40
+ else
41
+ rules
34
42
  end
35
43
  end
36
44
  end
@@ -2,11 +2,12 @@ module Lrama
2
2
  class Grammar
3
3
  # type: :dollar or :at
4
4
  # name: String (e.g. $$, $foo, $expr.right)
5
- # index: Integer (e.g. $1)
5
+ # number: Integer (e.g. $1)
6
+ # index: Integer
6
7
  # ex_tag: "$<tag>1" (Optional)
7
- class Reference < Struct.new(:type, :name, :index, :ex_tag, :first_column, :last_column, keyword_init: true)
8
+ class Reference < Struct.new(:type, :name, :number, :index, :ex_tag, :first_column, :last_column, keyword_init: true)
8
9
  def value
9
- name || index
10
+ name || number
10
11
  end
11
12
  end
12
13
  end
@@ -19,7 +19,7 @@ module Lrama
19
19
  # TODO: Change this to display_name
20
20
  def to_s
21
21
  l = lhs.id.s_value
22
- r = rhs.empty? ? "ε" : rhs.map {|r| r.id.s_value }.join(", ")
22
+ r = empty_rule? ? "ε" : rhs.map {|r| r.id.s_value }.join(", ")
23
23
 
24
24
  "#{l} -> #{r}"
25
25
  end
@@ -27,7 +27,7 @@ module Lrama
27
27
  # Used by #user_actions
28
28
  def as_comment
29
29
  l = lhs.id.s_value
30
- r = rhs.empty? ? "%empty" : rhs.map(&:display_name).join(" ")
30
+ r = empty_rule? ? "%empty" : rhs.map(&:display_name).join(" ")
31
31
 
32
32
  "#{l}: #{r}"
33
33
  end
@@ -1,5 +1,3 @@
1
- require 'lrama/grammar/parameterizing_rules/builder'
2
-
3
1
  module Lrama
4
2
  class Grammar
5
3
  class RuleBuilder
@@ -59,7 +57,7 @@ module Lrama
59
57
  end
60
58
 
61
59
  def rules
62
- @parameterizing_rules + @old_parameterizing_rules + @midrule_action_rules + @rules
60
+ @parameterizing_rules + @midrule_action_rules + @rules
63
61
  end
64
62
 
65
63
  private
@@ -97,7 +95,6 @@ module Lrama
97
95
  return if @replaced_rhs
98
96
 
99
97
  @replaced_rhs = []
100
- @old_parameterizing_rules = []
101
98
 
102
99
  rhs.each_with_index do |token, i|
103
100
  case token
@@ -106,35 +103,28 @@ module Lrama
106
103
  when Lrama::Lexer::Token::Ident
107
104
  @replaced_rhs << token
108
105
  when Lrama::Lexer::Token::InstantiateRule
109
- if parameterizing_rule_resolver.defined?(token)
110
- parameterizing_rule = parameterizing_rule_resolver.find(token)
111
- raise "Unexpected token. #{token}" unless parameterizing_rule
112
-
113
- bindings = Binding.new(parameterizing_rule, token.args)
114
- lhs_s_value = lhs_s_value(token, bindings)
115
- if (created_lhs = parameterizing_rule_resolver.created_lhs(lhs_s_value))
116
- @replaced_rhs << created_lhs
117
- else
118
- lhs_token = Lrama::Lexer::Token::Ident.new(s_value: lhs_s_value, location: token.location)
119
- @replaced_rhs << lhs_token
120
- parameterizing_rule_resolver.created_lhs_list << lhs_token
121
- parameterizing_rule.rhs_list.each do |r|
122
- rule_builder = RuleBuilder.new(@rule_counter, @midrule_action_counter, i, lhs_tag: token.lhs_tag, skip_preprocess_references: true)
123
- rule_builder.lhs = lhs_token
124
- r.symbols.each { |sym| rule_builder.add_rhs(bindings.resolve_symbol(sym)) }
125
- rule_builder.line = line
126
- rule_builder.user_code = r.user_code
127
- rule_builder.precedence_sym = r.precedence_sym
128
- rule_builder.complete_input
129
- rule_builder.setup_rules(parameterizing_rule_resolver)
130
- @rule_builders_for_parameterizing_rules << rule_builder
131
- end
132
- end
106
+ parameterizing_rule = parameterizing_rule_resolver.find(token)
107
+ raise "Unexpected token. #{token}" unless parameterizing_rule
108
+
109
+ bindings = Binding.new(parameterizing_rule, token.args)
110
+ lhs_s_value = lhs_s_value(token, bindings)
111
+ if (created_lhs = parameterizing_rule_resolver.created_lhs(lhs_s_value))
112
+ @replaced_rhs << created_lhs
133
113
  else
134
- # TODO: Delete when the standard library will defined as a grammar file.
135
- parameterizing_rule = ParameterizingRules::Builder.new(token, @rule_counter, token.lhs_tag, user_code, precedence_sym, line)
136
- @old_parameterizing_rules = @old_parameterizing_rules + parameterizing_rule.build
137
- @replaced_rhs << parameterizing_rule.build_token
114
+ lhs_token = Lrama::Lexer::Token::Ident.new(s_value: lhs_s_value, location: token.location)
115
+ @replaced_rhs << lhs_token
116
+ parameterizing_rule_resolver.created_lhs_list << lhs_token
117
+ parameterizing_rule.rhs_list.each do |r|
118
+ rule_builder = RuleBuilder.new(@rule_counter, @midrule_action_counter, i, lhs_tag: token.lhs_tag, skip_preprocess_references: true)
119
+ rule_builder.lhs = lhs_token
120
+ r.symbols.each { |sym| rule_builder.add_rhs(bindings.resolve_symbol(sym)) }
121
+ rule_builder.line = line
122
+ rule_builder.user_code = r.user_code
123
+ rule_builder.precedence_sym = r.precedence_sym
124
+ rule_builder.complete_input
125
+ rule_builder.setup_rules(parameterizing_rule_resolver)
126
+ @rule_builders_for_parameterizing_rules << rule_builder
127
+ end
138
128
  end
139
129
  when Lrama::Lexer::Token::UserCode
140
130
  prefix = token.referred ? "@" : "$@"
@@ -173,11 +163,12 @@ module Lrama
173
163
 
174
164
  token.references.each do |ref|
175
165
  ref_name = ref.name
176
- if ref_name && ref_name != '$'
177
- if lhs.referred_by?(ref_name)
166
+
167
+ if ref_name
168
+ if ref_name == '$'
178
169
  ref.name = '$'
179
170
  else
180
- candidates = rhs.each_with_index.select {|token, i| token.referred_by?(ref_name) }
171
+ candidates = ([lhs] + rhs).each_with_index.select {|token, _i| token.referred_by?(ref_name) }
181
172
 
182
173
  if candidates.size >= 2
183
174
  token.invalid_ref(ref, "Referring symbol `#{ref_name}` is duplicated.")
@@ -187,10 +178,21 @@ module Lrama
187
178
  token.invalid_ref(ref, "Referring symbol `#{ref_name}` is not found.")
188
179
  end
189
180
 
190
- ref.index = referring_symbol[1] + 1
181
+ if referring_symbol[1] == 0 # Refers to LHS
182
+ ref.name = '$'
183
+ else
184
+ ref.number = referring_symbol[1]
185
+ end
191
186
  end
192
187
  end
193
188
 
189
+ if ref.number
190
+ # TODO: When Inlining is implemented, for example, if `$1` is expanded to multiple RHS tokens,
191
+ # `$2` needs to access `$2 + n` to actually access it. So, after the Inlining implementation,
192
+ # it needs resolves from number to index.
193
+ ref.index = ref.number
194
+ end
195
+
194
196
  # TODO: Need to check index of @ too?
195
197
  next if ref.type == :at
196
198
 
@@ -0,0 +1,80 @@
1
+ /**********************************************************************
2
+
3
+ stdlib.y
4
+
5
+ This is lrama's standard library. It provides a number of
6
+ parameterizing rule definitions, such as options and lists,
7
+ that should be useful in a number of situations.
8
+
9
+ **********************************************************************/
10
+
11
+ /*
12
+ * program: option(number)
13
+ *
14
+ * =>
15
+ *
16
+ * program: option_number
17
+ * option_number: %empty
18
+ * option_number: number
19
+ */
20
+ %rule option(X): /* empty */
21
+ | X
22
+ ;
23
+
24
+ /*
25
+ * program: list(number)
26
+ *
27
+ * =>
28
+ *
29
+ * program: list_number
30
+ * list_number: %empty
31
+ * list_number: list_number number
32
+ */
33
+ %rule list(X): /* empty */
34
+ | list(X) X
35
+ ;
36
+
37
+ /*
38
+ * program: nonempty_list(number)
39
+ *
40
+ * =>
41
+ *
42
+ * program: nonempty_list_number
43
+ * nonempty_list_number: number
44
+ * nonempty_list_number: nonempty_list_number number
45
+ */
46
+ %rule nonempty_list(X): X
47
+ | nonempty_list(X) X
48
+ ;
49
+
50
+ /*
51
+ * program: separated_nonempty_list(comma, number)
52
+ *
53
+ * =>
54
+ *
55
+ * program: separated_nonempty_list_comma_number
56
+ * separated_nonempty_list_comma_number: number
57
+ * separated_nonempty_list_comma_number: separated_nonempty_list_comma_number comma number
58
+ */
59
+ %rule separated_nonempty_list(separator, X): X
60
+ | separated_nonempty_list(separator, X) separator X
61
+ ;
62
+
63
+ /*
64
+ * program: separated_list(comma, number)
65
+ *
66
+ * =>
67
+ *
68
+ * program: separated_list_comma_number
69
+ * separated_list_comma_number: option_separated_nonempty_list_comma_number
70
+ * option_separated_nonempty_list_comma_number: %empty
71
+ * option_separated_nonempty_list_comma_number: separated_nonempty_list_comma_number
72
+ * separated_nonempty_list_comma_number: number
73
+ * separated_nonempty_list_comma_number: comma separated_nonempty_list_comma_number number
74
+ */
75
+ %rule separated_list(separator, X): option(separated_nonempty_list(separator, X))
76
+ ;
77
+
78
+ %%
79
+
80
+ %union{};