lrama 0.6.1 → 0.6.3

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 (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{};