lrama 0.6.0 → 0.6.2
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 +27 -7
- data/Gemfile +1 -1
- data/NEWS.md +55 -0
- data/Steepfile +2 -3
- data/lib/lrama/command.rb +25 -3
- data/lib/lrama/context.rb +3 -23
- data/lib/lrama/counterexamples/example.rb +2 -2
- data/lib/lrama/grammar/binding.rb +24 -0
- data/lib/lrama/grammar/code/rule_action.rb +1 -1
- data/lib/lrama/grammar/code.rb +1 -1
- data/lib/lrama/grammar/parameterizing_rule/resolver.rb +47 -0
- data/lib/lrama/grammar/parameterizing_rule/rhs.rb +15 -0
- data/lib/lrama/grammar/parameterizing_rule/rule.rb +16 -0
- data/lib/lrama/grammar/parameterizing_rule.rb +3 -6
- data/lib/lrama/grammar/percent_code.rb +3 -3
- data/lib/lrama/grammar/rule.rb +2 -2
- data/lib/lrama/grammar/rule_builder.rb +60 -31
- data/lib/lrama/grammar/stdlib.y +80 -0
- data/lib/lrama/grammar/type.rb +13 -1
- data/lib/lrama/grammar.rb +18 -11
- data/lib/lrama/lexer/grammar_file.rb +1 -1
- data/lib/lrama/lexer/token/instantiate_rule.rb +7 -2
- data/lib/lrama/lexer/token.rb +5 -0
- data/lib/lrama/lexer.rb +3 -7
- data/lib/lrama/output.rb +2 -2
- data/lib/lrama/parser.rb +508 -467
- data/lib/lrama/states/item.rb +17 -13
- data/lib/lrama/states_reporter.rb +8 -10
- data/lib/lrama/version.rb +1 -1
- data/parser.y +12 -13
- data/sig/lrama/grammar/binding.rbs +16 -0
- data/sig/lrama/grammar/parameterizing_rule/resolver.rbs +22 -0
- data/sig/lrama/grammar/parameterizing_rule/rhs.rbs +13 -0
- data/sig/lrama/grammar/parameterizing_rule/rule.rbs +14 -0
- data/sig/lrama/grammar/parameterizing_rule.rbs +0 -4
- data/sig/lrama/grammar/percent_code.rbs +3 -3
- data/sig/lrama/grammar/rule_builder.rbs +9 -6
- data/sig/lrama/lexer/token/instantiate_rule.rbs +4 -2
- data/sig/lrama/lexer/token.rbs +1 -0
- metadata +12 -23
- data/lib/lrama/grammar/parameterizing_rule_builder.rb +0 -34
- data/lib/lrama/grammar/parameterizing_rule_resolver.rb +0 -30
- data/lib/lrama/grammar/parameterizing_rule_rhs_builder.rb +0 -53
- 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_rule_builder.rbs +0 -19
- data/sig/lrama/grammar/parameterizing_rule_resolver.rbs +0 -16
- data/sig/lrama/grammar/parameterizing_rule_rhs_builder.rbs +0 -18
- 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: e4158de45c42ff62eacfb00737261feaa49d8f0cc646004e30da74ba4e2e69c6
|
4
|
+
data.tar.gz: 734830227f701e18df2e9e8bc3da55d15f49c890e08530e6ac55ef87ae5f952d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 52ebbe4d099ae63d73aa995bddc8e966f989a4d00ad3b39634d2abe2448da404dd9bff8f15e0dedd0577716089329c804ef2c4edcadd39ca6ba47f8d293d101d
|
7
|
+
data.tar.gz: 72e91c79618071b5850c85335cfe3f1b63ff89f11cd332b0141623a4e2a7e2c2c389dd0db9afe38a5a84b7aa891ac1834fc7a2d6c6eed8f62d87734f6b99cbbf
|
data/.github/workflows/test.yaml
CHANGED
@@ -8,12 +8,19 @@ permissions:
|
|
8
8
|
contents: read
|
9
9
|
|
10
10
|
jobs:
|
11
|
+
ruby-versions:
|
12
|
+
uses: ruby/actions/.github/workflows/ruby_versions.yml@master
|
13
|
+
with:
|
14
|
+
engine: cruby
|
15
|
+
min_version: 2.5
|
16
|
+
|
11
17
|
test:
|
18
|
+
needs: ruby-versions
|
12
19
|
runs-on: ubuntu-20.04
|
13
20
|
strategy:
|
14
21
|
fail-fast: false
|
15
22
|
matrix:
|
16
|
-
ruby:
|
23
|
+
ruby: ${{ fromJson(needs.ruby-versions.outputs.versions) }}
|
17
24
|
steps:
|
18
25
|
- uses: actions/checkout@v4
|
19
26
|
- uses: ruby/setup-ruby@v1
|
@@ -29,14 +36,22 @@ jobs:
|
|
29
36
|
fail-fast: false
|
30
37
|
matrix:
|
31
38
|
ruby: ['head']
|
39
|
+
defaults:
|
40
|
+
run:
|
41
|
+
shell: msys2 {0}
|
32
42
|
steps:
|
33
43
|
- uses: actions/checkout@v4
|
34
44
|
- uses: ruby/setup-ruby@v1
|
35
45
|
with:
|
36
46
|
ruby-version: ${{ matrix.ruby }}
|
37
47
|
bundler-cache: true
|
38
|
-
-
|
39
|
-
|
48
|
+
- uses: msys2/setup-msys2@v2
|
49
|
+
id: setup-msys2
|
50
|
+
with:
|
51
|
+
update: true
|
52
|
+
install: >-
|
53
|
+
flex
|
54
|
+
- run: flex --help
|
40
55
|
- run: bundle install
|
41
56
|
- run: bundle exec rspec
|
42
57
|
test-memory:
|
@@ -72,6 +87,11 @@ jobs:
|
|
72
87
|
bundler-cache: true
|
73
88
|
- run: bundle install
|
74
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
|
+
|
75
95
|
# Copy from https://github.com/ruby/ruby/blob/089227e94823542acfdafa68541d330eee42ffea/.github/workflows/check_misc.yml#L27
|
76
96
|
- name: Check for trailing spaces
|
77
97
|
run: |
|
@@ -105,8 +125,8 @@ jobs:
|
|
105
125
|
fail-fast: false
|
106
126
|
matrix:
|
107
127
|
# '3.0' is the oldest living ruby version
|
108
|
-
# '2.
|
109
|
-
baseruby: ['head', '3.0', '2.
|
128
|
+
# '2.7' is for BASERUBY
|
129
|
+
baseruby: ['head', '3.0', '2.7']
|
110
130
|
ruby_branch: ['master']
|
111
131
|
defaults:
|
112
132
|
run:
|
@@ -118,12 +138,12 @@ jobs:
|
|
118
138
|
ruby-version: ${{ matrix.baseruby }}
|
119
139
|
bundler-cache: true
|
120
140
|
- run: git clone --depth=1 https://github.com/ruby/ruby.git -b ${{ matrix.ruby_branch }} ../ruby
|
121
|
-
working-directory:
|
141
|
+
working-directory: .
|
122
142
|
- run: mkdir -p tool/lrama
|
123
143
|
working-directory: ../ruby
|
124
144
|
- name: Copy Lrama to ruby/tool
|
125
145
|
run: cp -r LEGAL.md NEWS.md MIT exe lib template ../ruby/tool/lrama
|
126
|
-
working-directory:
|
146
|
+
working-directory: .
|
127
147
|
- run: tree tool/lrama
|
128
148
|
working-directory: ../ruby
|
129
149
|
# See also https://github.com/ruby/ruby/blob/master/.github/workflows/ubuntu.yml
|
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.4.
|
15
|
+
gem "rbs", "3.4.1", require: false
|
16
16
|
gem "steep", "1.6.0", require: false
|
17
17
|
end
|
data/NEWS.md
CHANGED
@@ -1,5 +1,58 @@
|
|
1
1
|
# NEWS for Lrama
|
2
2
|
|
3
|
+
## Lrama 0.6.2 (2024-01-27)
|
4
|
+
|
5
|
+
### %no-stdlib directive
|
6
|
+
|
7
|
+
If `%no-stdlib` directive is set, Lrama doesn't load Lrama standard library for
|
8
|
+
parameterizing rules, stdlib.y.
|
9
|
+
|
10
|
+
https://github.com/ruby/lrama/pull/344
|
11
|
+
|
12
|
+
## Lrama 0.6.1 (2024-01-13)
|
13
|
+
|
14
|
+
### Nested parameterizing rules
|
15
|
+
|
16
|
+
Allow to pass an instantiated rule to other parameterizing rules.
|
17
|
+
|
18
|
+
```
|
19
|
+
%rule constant(X) : X
|
20
|
+
;
|
21
|
+
|
22
|
+
%rule option(Y) : /* empty */
|
23
|
+
| Y
|
24
|
+
;
|
25
|
+
|
26
|
+
%%
|
27
|
+
|
28
|
+
program : option(constant(number)) // Nested rule
|
29
|
+
;
|
30
|
+
%%
|
31
|
+
```
|
32
|
+
|
33
|
+
Allow to use nested parameterizing rules when define parameterizing rules.
|
34
|
+
|
35
|
+
```
|
36
|
+
%rule option(x) : /* empty */
|
37
|
+
| X
|
38
|
+
;
|
39
|
+
|
40
|
+
%rule double(Y) : Y Y
|
41
|
+
;
|
42
|
+
|
43
|
+
%rule double_opt(A) : option(double(A)) // Nested rule
|
44
|
+
;
|
45
|
+
|
46
|
+
%%
|
47
|
+
|
48
|
+
program : double_opt(number)
|
49
|
+
;
|
50
|
+
|
51
|
+
%%
|
52
|
+
```
|
53
|
+
|
54
|
+
https://github.com/ruby/lrama/pull/337
|
55
|
+
|
3
56
|
## Lrama 0.6.0 (2023-12-25)
|
4
57
|
|
5
58
|
### User defined parameterizing rules
|
@@ -20,6 +73,8 @@ stmt: pair(ODD, EVEN) <num>
|
|
20
73
|
;
|
21
74
|
```
|
22
75
|
|
76
|
+
https://github.com/ruby/lrama/pull/285
|
77
|
+
|
23
78
|
## Lrama 0.5.11 (2023-12-02)
|
24
79
|
|
25
80
|
### Type specification of parameterizing rules
|
data/Steepfile
CHANGED
@@ -4,13 +4,12 @@ target :lib do
|
|
4
4
|
repo_path '.gem_rbs_collection/'
|
5
5
|
signature "sig"
|
6
6
|
|
7
|
+
check "lib/lrama/grammar/binding.rb"
|
7
8
|
check "lib/lrama/grammar/code/printer_code.rb"
|
8
9
|
check "lib/lrama/grammar/code.rb"
|
9
10
|
check "lib/lrama/grammar/counter.rb"
|
10
11
|
check "lib/lrama/grammar/error_token.rb"
|
11
|
-
check "lib/lrama/grammar/
|
12
|
-
check "lib/lrama/grammar/parameterizing_rule_resolver.rb"
|
13
|
-
check "lib/lrama/grammar/parameterizing_rule_rhs_builder.rb"
|
12
|
+
check "lib/lrama/grammar/parameterizing_rule"
|
14
13
|
check "lib/lrama/grammar/parameterizing_rules"
|
15
14
|
check "lib/lrama/grammar/percent_code.rb"
|
16
15
|
check "lib/lrama/grammar/precedence.rb"
|
data/lib/lrama/command.rb
CHANGED
@@ -1,14 +1,36 @@
|
|
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
|
8
|
+
options = OptionParser.new.parse(argv)
|
9
|
+
rescue => e
|
10
|
+
message = e.message
|
11
|
+
message = message.gsub(/.+/, "\e[1m\\&\e[m") if Exception.to_tty?
|
12
|
+
abort message
|
13
|
+
end
|
5
14
|
|
6
15
|
Report::Duration.enable if options.trace_opts[:time]
|
7
16
|
|
8
17
|
warning = Lrama::Warning.new
|
9
18
|
text = options.y.read
|
10
19
|
options.y.close if options.y != STDIN
|
11
|
-
|
20
|
+
begin
|
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!
|
28
|
+
rescue => e
|
29
|
+
raise e if options.debug
|
30
|
+
message = e.message
|
31
|
+
message = message.gsub(/.+/, "\e[1m\\&\e[m") if Exception.to_tty?
|
32
|
+
abort message
|
33
|
+
end
|
12
34
|
states = Lrama::States.new(grammar, warning, trace_state: (options.trace_opts[:automaton] || options.trace_opts[:closure]))
|
13
35
|
states.compute
|
14
36
|
context = Lrama::Context.new(states)
|
@@ -39,7 +61,7 @@ module Lrama
|
|
39
61
|
end
|
40
62
|
|
41
63
|
if warning.has_error?
|
42
|
-
exit
|
64
|
+
exit false
|
43
65
|
end
|
44
66
|
end
|
45
67
|
end
|
data/lib/lrama/context.rb
CHANGED
@@ -9,7 +9,7 @@ module Lrama
|
|
9
9
|
BaseMin = -Float::INFINITY
|
10
10
|
|
11
11
|
# TODO: It might be better to pass `states` to Output directly?
|
12
|
-
attr_reader :states
|
12
|
+
attr_reader :states, :yylast, :yypact_ninf, :yytable_ninf, :yydefact, :yydefgoto
|
13
13
|
|
14
14
|
def initialize(states)
|
15
15
|
@states = states
|
@@ -41,15 +41,11 @@ 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
|
48
48
|
|
49
|
-
def yylast
|
50
|
-
@yylast
|
51
|
-
end
|
52
|
-
|
53
49
|
# Number of terms
|
54
50
|
def yyntokens
|
55
51
|
@states.terms.count
|
@@ -119,30 +115,14 @@ module Lrama
|
|
119
115
|
end
|
120
116
|
end
|
121
117
|
|
122
|
-
def yypact_ninf
|
123
|
-
@yypact_ninf
|
124
|
-
end
|
125
|
-
|
126
|
-
def yytable_ninf
|
127
|
-
@yytable_ninf
|
128
|
-
end
|
129
|
-
|
130
118
|
def yypact
|
131
119
|
@base[0...yynstates]
|
132
120
|
end
|
133
121
|
|
134
|
-
def yydefact
|
135
|
-
@yydefact
|
136
|
-
end
|
137
|
-
|
138
122
|
def yypgoto
|
139
123
|
@base[yynstates..-1]
|
140
124
|
end
|
141
125
|
|
142
|
-
def yydefgoto
|
143
|
-
@yydefgoto
|
144
|
-
end
|
145
|
-
|
146
126
|
def yytable
|
147
127
|
@table
|
148
128
|
end
|
@@ -241,7 +221,7 @@ module Lrama
|
|
241
221
|
|
242
222
|
if state.reduces.map(&:selected_look_ahead).any? {|la| !la.empty? }
|
243
223
|
# Iterate reduces with reverse order so that first rule is used.
|
244
|
-
state.reduces.
|
224
|
+
state.reduces.reverse_each do |reduce|
|
245
225
|
reduce.look_ahead.each do |term|
|
246
226
|
actions[term.number] = rule_id_to_action_number(reduce.rule.id)
|
247
227
|
end
|
@@ -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
|
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module Lrama
|
2
|
+
class Grammar
|
3
|
+
class Binding
|
4
|
+
attr_reader :actual_args, :count
|
5
|
+
|
6
|
+
def initialize(parameterizing_rule, actual_args)
|
7
|
+
@parameters = parameterizing_rule.parameters
|
8
|
+
@actual_args = actual_args
|
9
|
+
@parameter_to_arg = @parameters.zip(actual_args).map do |param, arg|
|
10
|
+
[param.s_value, arg]
|
11
|
+
end.to_h
|
12
|
+
end
|
13
|
+
|
14
|
+
def resolve_symbol(symbol)
|
15
|
+
if symbol.is_a?(Lexer::Token::InstantiateRule)
|
16
|
+
resolved_args = symbol.args.map { |arg| resolve_symbol(arg) }
|
17
|
+
Lrama::Lexer::Token::InstantiateRule.new(s_value: symbol.s_value, location: symbol.location, args: resolved_args, lhs_tag: symbol.lhs_tag)
|
18
|
+
else
|
19
|
+
@parameter_to_arg[symbol.s_value] || symbol
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -22,7 +22,7 @@ module Lrama
|
|
22
22
|
# For the semantic action of original rule:
|
23
23
|
#
|
24
24
|
# "Rule" class: keyword_class { $1 } tSTRING { $2 + $3 } keyword_end { $class = $1 + $keyword_end }
|
25
|
-
# "Position in grammar" $1 $2 $3 $4 $5
|
25
|
+
# "Position in grammar" $1 $2 $3 $4 $5
|
26
26
|
# "Index for yyvsp" -4 -3 -2 -1 0
|
27
27
|
#
|
28
28
|
#
|
data/lib/lrama/grammar/code.rb
CHANGED
@@ -0,0 +1,47 @@
|
|
1
|
+
module Lrama
|
2
|
+
class Grammar
|
3
|
+
class ParameterizingRule
|
4
|
+
class Resolver
|
5
|
+
attr_accessor :rules, :created_lhs_list
|
6
|
+
|
7
|
+
def initialize
|
8
|
+
@rules = []
|
9
|
+
@created_lhs_list = []
|
10
|
+
end
|
11
|
+
|
12
|
+
def add_parameterizing_rule(rule)
|
13
|
+
@rules << rule
|
14
|
+
end
|
15
|
+
|
16
|
+
def find(token)
|
17
|
+
select_rules(token).last
|
18
|
+
end
|
19
|
+
|
20
|
+
def created_lhs(lhs_s_value)
|
21
|
+
@created_lhs_list.reverse.find { |created_lhs| created_lhs.s_value == lhs_s_value }
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def select_rules(token)
|
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
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module Lrama
|
2
|
+
class Grammar
|
3
|
+
class ParameterizingRule
|
4
|
+
class Rule
|
5
|
+
attr_reader :name, :parameters, :rhs_list, :required_parameters_count
|
6
|
+
|
7
|
+
def initialize(name, parameters, rhs_list)
|
8
|
+
@name = name
|
9
|
+
@parameters = parameters
|
10
|
+
@rhs_list = rhs_list
|
11
|
+
@required_parameters_count = parameters.count
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
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
|