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.
- checksums.yaml +4 -4
- data/.github/workflows/test.yaml +7 -2
- data/NEWS.md +43 -0
- data/README.md +23 -0
- data/Steepfile +2 -0
- data/lib/lrama/command.rb +10 -2
- data/lib/lrama/context.rb +6 -6
- data/lib/lrama/counterexamples/example.rb +2 -2
- data/lib/lrama/grammar/code/initial_action_code.rb +6 -0
- data/lib/lrama/grammar/code/no_reference_code.rb +4 -0
- data/lib/lrama/grammar/code/printer_code.rb +6 -0
- data/lib/lrama/grammar/code/rule_action.rb +11 -1
- data/lib/lrama/grammar/code.rb +1 -1
- data/lib/lrama/grammar/parameterizing_rule/resolver.rb +17 -9
- data/lib/lrama/grammar/reference.rb +4 -3
- data/lib/lrama/grammar/rule.rb +2 -2
- data/lib/lrama/grammar/rule_builder.rb +38 -36
- data/lib/lrama/grammar/stdlib.y +80 -0
- data/lib/lrama/grammar/symbol.rb +1 -1
- data/lib/lrama/grammar/symbols/resolver.rb +276 -0
- data/lib/lrama/grammar/symbols.rb +1 -0
- data/lib/lrama/grammar.rb +36 -246
- data/lib/lrama/lexer/token/user_code.rb +13 -2
- data/lib/lrama/lexer.rb +7 -0
- data/lib/lrama/output.rb +56 -2
- data/lib/lrama/parser.rb +571 -485
- data/lib/lrama/state.rb +4 -4
- data/lib/lrama/states/item.rb +19 -17
- data/lib/lrama/states_reporter.rb +10 -12
- data/lib/lrama/version.rb +1 -1
- data/lrama.gemspec +7 -0
- data/parser.y +24 -5
- data/sig/lrama/grammar/parameterizing_rule/resolver.rbs +1 -0
- data/sig/lrama/grammar/reference.rbs +2 -1
- data/sig/lrama/grammar/symbol.rbs +4 -4
- data/sig/lrama/grammar/symbols/resolver.rbs +41 -0
- data/sig/lrama/grammar/type.rbs +11 -0
- data/template/bison/yacc.c +6 -0
- metadata +13 -17
- data/lib/lrama/grammar/parameterizing_rules/builder/base.rb +0 -36
- data/lib/lrama/grammar/parameterizing_rules/builder/list.rb +0 -28
- data/lib/lrama/grammar/parameterizing_rules/builder/nonempty_list.rb +0 -28
- data/lib/lrama/grammar/parameterizing_rules/builder/option.rb +0 -28
- data/lib/lrama/grammar/parameterizing_rules/builder/separated_list.rb +0 -39
- data/lib/lrama/grammar/parameterizing_rules/builder/separated_nonempty_list.rb +0 -34
- data/lib/lrama/grammar/parameterizing_rules/builder.rb +0 -60
- data/sig/lrama/grammar/parameterizing_rules/builder/base.rbs +0 -28
- data/sig/lrama/grammar/parameterizing_rules/builder/list.rbs +0 -10
- data/sig/lrama/grammar/parameterizing_rules/builder/nonempty_list.rbs +0 -10
- data/sig/lrama/grammar/parameterizing_rules/builder/option.rbs +0 -10
- data/sig/lrama/grammar/parameterizing_rules/builder/separated_list.rbs +0 -13
- data/sig/lrama/grammar/parameterizing_rules/builder/separated_nonempty_list.rbs +0 -13
- 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:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ecd30d3fab4dd73442ed6d3b2802db5b463159cb6ddf1f1835d9b8e860d4c9dd
|
4
|
+
data.tar.gz: 79b6087e68d3c2e95db81fa1d25f58280a5543e9c5fa91f5b6ecc7c40b5599d7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f3302156423399987015deb90afbaa0d6916e5e61b14c5297ff6a0e01ab9db3bbd164b334a11e26d38cf626425b7faee404e5d1cdec236a9b08b576ced4fe201
|
7
|
+
data.tar.gz: 380d8d31c93e5ae6c5a406c2b2eedad0d4b52dd311ecd742ca1412bd1acade7dae8fe12f7a6325ef8a0b4922ffd927b0dfa2caf3cd54c095669ab2b2cab85516
|
data/.github/workflows/test.yaml
CHANGED
@@ -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.
|
124
|
-
baseruby: ['head', '3.0', '2.
|
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
|
+
[](https://badge.fury.io/rb/lrama)
|
4
|
+
[](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 =
|
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.
|
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.
|
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.
|
268
|
+
end.reject do |i, n|
|
269
269
|
# Remove default_reduction_rule entries
|
270
|
-
n
|
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.
|
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.
|
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.
|
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.
|
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
|
83
|
+
raise "Tag is not specified for '$#{ref.value}' in '#{@rule}'"
|
74
84
|
end
|
75
85
|
end
|
76
86
|
end
|
data/lib/lrama/grammar/code.rb
CHANGED
@@ -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.
|
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
|
-
|
32
|
-
|
33
|
-
|
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
|
-
#
|
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 ||
|
10
|
+
name || number
|
10
11
|
end
|
11
12
|
end
|
12
13
|
end
|
data/lib/lrama/grammar/rule.rb
CHANGED
@@ -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 =
|
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 =
|
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 + @
|
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
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
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
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
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
|
-
|
177
|
-
|
166
|
+
|
167
|
+
if ref_name
|
168
|
+
if ref_name == '$'
|
178
169
|
ref.name = '$'
|
179
170
|
else
|
180
|
-
candidates = rhs.each_with_index.select {|token,
|
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
|
-
|
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{};
|