lrama 0.5.1 → 0.5.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 +2 -3
- data/.gitignore +2 -0
- data/Gemfile +1 -0
- data/LEGAL.md +1 -16
- data/README.md +1 -1
- data/Steepfile +3 -0
- data/doc/TODO.md +4 -3
- data/exe/lrama +1 -1
- data/lib/lrama/command.rb +90 -71
- data/lib/lrama/context.rb +11 -1
- data/lib/lrama/grammar/code.rb +123 -0
- data/lib/lrama/grammar/error_token.rb +9 -0
- data/lib/lrama/grammar/precedence.rb +11 -0
- data/lib/lrama/grammar/printer.rb +9 -0
- data/lib/lrama/grammar/reference.rb +22 -0
- data/lib/lrama/grammar/rule.rb +33 -0
- data/lib/lrama/grammar/symbol.rb +94 -0
- data/lib/lrama/grammar/union.rb +10 -0
- data/lib/lrama/grammar.rb +67 -285
- data/lib/lrama/lexer/token.rb +76 -0
- data/lib/lrama/lexer.rb +12 -48
- data/lib/lrama/output.rb +31 -3
- data/lib/lrama/parser/token_scanner.rb +4 -0
- data/lib/lrama/parser.rb +19 -5
- data/lib/lrama/report/duration.rb +25 -0
- data/lib/lrama/report/profile.rb +25 -0
- data/lib/lrama/report.rb +2 -47
- data/lib/lrama/state/resolved_conflict.rb +29 -0
- data/lib/lrama/state.rb +3 -28
- data/lib/lrama/states/item.rb +43 -0
- data/lib/lrama/states.rb +3 -41
- data/lib/lrama/version.rb +1 -1
- data/lrama.gemspec +2 -2
- data/rbs_collection.lock.yaml +26 -0
- data/rbs_collection.yaml +22 -0
- data/sig/lrama/report/duration.rbs +11 -0
- data/sig/lrama/report/profile.rbs +7 -0
- data/sig/lrama/warning.rbs +16 -0
- data/template/bison/yacc.c +397 -32
- metadata +23 -5
data/lib/lrama/parser.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
require "lrama/report"
|
1
|
+
require "lrama/report/duration"
|
2
2
|
require "lrama/parser/token_scanner"
|
3
3
|
|
4
4
|
module Lrama
|
@@ -59,6 +59,13 @@ module Lrama
|
|
59
59
|
code = grammar.build_code(:printer, code)
|
60
60
|
ident_or_tags = ts.consume_multi(T::Ident, T::Tag)
|
61
61
|
grammar.add_printer(ident_or_tags: ident_or_tags, code: code, lineno: lineno)
|
62
|
+
when T::P_error_token
|
63
|
+
lineno = ts.current_token.line
|
64
|
+
ts.next
|
65
|
+
code = ts.consume!(T::User_code)
|
66
|
+
code = grammar.build_code(:printer, code)
|
67
|
+
ident_or_tags = ts.consume_multi(T::Ident, T::Tag)
|
68
|
+
grammar.add_error_token(ident_or_tags: ident_or_tags, code: code, lineno: lineno)
|
62
69
|
when T::P_lex_param
|
63
70
|
ts.next
|
64
71
|
code = ts.consume!(T::User_code)
|
@@ -175,8 +182,11 @@ module Lrama
|
|
175
182
|
# LHS
|
176
183
|
lhs = ts.consume!(T::Ident_Colon) # class:
|
177
184
|
lhs.type = T::Ident
|
185
|
+
if named_ref = ts.consume(T::Named_Ref)
|
186
|
+
lhs.alias = named_ref.s_value
|
187
|
+
end
|
178
188
|
|
179
|
-
rhs = parse_grammar_rule_rhs(ts, grammar)
|
189
|
+
rhs = parse_grammar_rule_rhs(ts, grammar, lhs)
|
180
190
|
|
181
191
|
grammar.add_rule(lhs: lhs, rhs: rhs, lineno: rhs.first ? rhs.first.line : lhs.line)
|
182
192
|
|
@@ -186,7 +196,7 @@ module Lrama
|
|
186
196
|
# |
|
187
197
|
bar_lineno = ts.current_token.line
|
188
198
|
ts.next
|
189
|
-
rhs = parse_grammar_rule_rhs(ts, grammar)
|
199
|
+
rhs = parse_grammar_rule_rhs(ts, grammar, lhs)
|
190
200
|
grammar.add_rule(lhs: lhs, rhs: rhs, lineno: rhs.first ? rhs.first.line : bar_lineno)
|
191
201
|
when T::Semicolon
|
192
202
|
# ;
|
@@ -205,13 +215,13 @@ module Lrama
|
|
205
215
|
end
|
206
216
|
end
|
207
217
|
|
208
|
-
def parse_grammar_rule_rhs(ts, grammar)
|
218
|
+
def parse_grammar_rule_rhs(ts, grammar, lhs)
|
209
219
|
a = []
|
210
220
|
prec_seen = false
|
211
221
|
code_after_prec = false
|
212
222
|
|
213
223
|
while true do
|
214
|
-
# TODO:
|
224
|
+
# TODO: String can be here
|
215
225
|
case ts.current_type
|
216
226
|
when T::Ident
|
217
227
|
# keyword_class
|
@@ -244,9 +254,13 @@ module Lrama
|
|
244
254
|
end
|
245
255
|
|
246
256
|
code = ts.current_token
|
257
|
+
code.numberize_references(lhs, a)
|
247
258
|
grammar.build_references(code)
|
248
259
|
a << code
|
249
260
|
ts.next
|
261
|
+
when T::Named_Ref
|
262
|
+
ts.previous_token.alias = ts.current_token.s_value
|
263
|
+
ts.next
|
250
264
|
when T::Bar
|
251
265
|
# |
|
252
266
|
break
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Lrama
|
2
|
+
class Report
|
3
|
+
module Duration
|
4
|
+
def self.enable
|
5
|
+
@_report_duration_enabled = true
|
6
|
+
end
|
7
|
+
|
8
|
+
def self.enabled?
|
9
|
+
!!@_report_duration_enabled
|
10
|
+
end
|
11
|
+
|
12
|
+
def report_duration(method_name)
|
13
|
+
time1 = Time.now.to_f
|
14
|
+
result = yield
|
15
|
+
time2 = Time.now.to_f
|
16
|
+
|
17
|
+
if Duration.enabled?
|
18
|
+
puts sprintf("%s %10.5f s", method_name, time2 - time1)
|
19
|
+
end
|
20
|
+
|
21
|
+
return result
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Lrama
|
2
|
+
class Report
|
3
|
+
module Profile
|
4
|
+
# 1. Wrap target method with Profile.report_profile like below:
|
5
|
+
#
|
6
|
+
# Lrama::Report::Profile.report_profile { method }
|
7
|
+
#
|
8
|
+
# 2. Run lrama command, for example
|
9
|
+
#
|
10
|
+
# $ ./exe/lrama --trace=time spec/fixtures/integration/ruby_3_2_0/parse.tmp.y
|
11
|
+
#
|
12
|
+
# 3. Generate html file
|
13
|
+
#
|
14
|
+
# $ stackprof --d3-flamegraph tmp/stackprof-cpu-myapp.dump > tmp/flamegraph.html
|
15
|
+
#
|
16
|
+
def self.report_profile
|
17
|
+
require "stackprof"
|
18
|
+
|
19
|
+
StackProf.run(mode: :cpu, raw: true, out: 'tmp/stackprof-cpu-myapp.dump') do
|
20
|
+
yield
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
data/lib/lrama/report.rb
CHANGED
@@ -1,47 +1,2 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
module Profile
|
4
|
-
# 1. Wrap target method with Profile.report_profile like below:
|
5
|
-
#
|
6
|
-
# Lrama::Report::Profile.report_profile { method }
|
7
|
-
#
|
8
|
-
# 2. Run lrama command, for example
|
9
|
-
#
|
10
|
-
# $ ./exe/lrama --trace=time spec/fixtures/integration/ruby_3_2_0/parse.tmp.y
|
11
|
-
#
|
12
|
-
# 3. Generate html file
|
13
|
-
#
|
14
|
-
# $ stackprof --d3-flamegraph tmp/stackprof-cpu-myapp.dump > tmp/flamegraph.html
|
15
|
-
#
|
16
|
-
def self.report_profile
|
17
|
-
require "stackprof"
|
18
|
-
|
19
|
-
StackProf.run(mode: :cpu, raw: true, out: 'tmp/stackprof-cpu-myapp.dump') do
|
20
|
-
yield
|
21
|
-
end
|
22
|
-
end
|
23
|
-
end
|
24
|
-
|
25
|
-
module Duration
|
26
|
-
def self.enable
|
27
|
-
@_report_duration_enabled = true
|
28
|
-
end
|
29
|
-
|
30
|
-
def self.enabled?
|
31
|
-
!!@_report_duration_enabled
|
32
|
-
end
|
33
|
-
|
34
|
-
def report_duration(method_name)
|
35
|
-
time1 = Time.now.to_f
|
36
|
-
result = yield
|
37
|
-
time2 = Time.now.to_f
|
38
|
-
|
39
|
-
if Duration.enabled?
|
40
|
-
puts sprintf("%s %10.5f s", method_name, time2 - time1)
|
41
|
-
end
|
42
|
-
|
43
|
-
return result
|
44
|
-
end
|
45
|
-
end
|
46
|
-
end
|
47
|
-
end
|
1
|
+
require 'lrama/report/duration'
|
2
|
+
require 'lrama/report/profile'
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module Lrama
|
2
|
+
class State
|
3
|
+
# * symbol: A symbol under discussion
|
4
|
+
# * reduce: A reduce under discussion
|
5
|
+
# * which: For which a conflict is resolved. :shift, :reduce or :error (for nonassociative)
|
6
|
+
class ResolvedConflict < Struct.new(:symbol, :reduce, :which, :same_prec, keyword_init: true)
|
7
|
+
def report_message
|
8
|
+
s = symbol.display_name
|
9
|
+
r = reduce.rule.precedence_sym.display_name
|
10
|
+
case
|
11
|
+
when which == :shift && same_prec
|
12
|
+
msg = "resolved as #{which} (%right #{s})"
|
13
|
+
when which == :shift
|
14
|
+
msg = "resolved as #{which} (#{r} < #{s})"
|
15
|
+
when which == :reduce && same_prec
|
16
|
+
msg = "resolved as #{which} (%left #{s})"
|
17
|
+
when which == :reduce
|
18
|
+
msg = "resolved as #{which} (#{s} < #{r})"
|
19
|
+
when which == :error
|
20
|
+
msg = "resolved as an #{which} (%nonassoc #{s})"
|
21
|
+
else
|
22
|
+
raise "Unknown direction. #{self}"
|
23
|
+
end
|
24
|
+
|
25
|
+
"Conflict between rule #{reduce.rule.id} and token #{s} #{msg}."
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
data/lib/lrama/state.rb
CHANGED
@@ -1,34 +1,9 @@
|
|
1
1
|
require "lrama/state/reduce"
|
2
2
|
require "lrama/state/shift"
|
3
|
+
require "lrama/state/resolved_conflict"
|
3
4
|
|
4
5
|
module Lrama
|
5
6
|
class State
|
6
|
-
# * symbol: A symbol under discussion
|
7
|
-
# * reduce: A reduce under discussion
|
8
|
-
# * which: For which a conflict is resolved. :shift, :reduce or :error (for nonassociative)
|
9
|
-
ResolvedConflict = Struct.new(:symbol, :reduce, :which, :same_prec, keyword_init: true) do
|
10
|
-
def report_message
|
11
|
-
s = symbol.display_name
|
12
|
-
r = reduce.rule.precedence_sym.display_name
|
13
|
-
case
|
14
|
-
when which == :shift && same_prec
|
15
|
-
msg = "resolved as #{which} (%right #{s})"
|
16
|
-
when which == :shift
|
17
|
-
msg = "resolved as #{which} (#{r} < #{s})"
|
18
|
-
when which == :reduce && same_prec
|
19
|
-
msg = "resolved as #{which} (%left #{s})"
|
20
|
-
when which == :reduce
|
21
|
-
msg = "resolved as #{which} (#{s} < #{r})"
|
22
|
-
when which == :error
|
23
|
-
msg = "resolved as an #{which} (%nonassoc #{s})"
|
24
|
-
else
|
25
|
-
raise "Unknown direction. #{self}"
|
26
|
-
end
|
27
|
-
|
28
|
-
"Conflict between rule #{reduce.rule.id} and token #{s} #{msg}."
|
29
|
-
end
|
30
|
-
end
|
31
|
-
|
32
7
|
Conflict = Struct.new(:symbols, :reduce, :type, keyword_init: true)
|
33
8
|
|
34
9
|
attr_reader :id, :accessing_symbol, :kernels, :conflicts, :resolved_conflicts,
|
@@ -96,7 +71,7 @@ module Lrama
|
|
96
71
|
reduce.look_ahead = look_ahead
|
97
72
|
end
|
98
73
|
|
99
|
-
# Returns array of [
|
74
|
+
# Returns array of [Shift, next_state]
|
100
75
|
def nterm_transitions
|
101
76
|
return @nterm_transitions if @nterm_transitions
|
102
77
|
|
@@ -111,7 +86,7 @@ module Lrama
|
|
111
86
|
@nterm_transitions
|
112
87
|
end
|
113
88
|
|
114
|
-
# Returns array of [
|
89
|
+
# Returns array of [Shift, next_state]
|
115
90
|
def term_transitions
|
116
91
|
return @term_transitions if @term_transitions
|
117
92
|
|
@@ -0,0 +1,43 @@
|
|
1
|
+
# TODO: Validate position is not over rule rhs
|
2
|
+
|
3
|
+
module Lrama
|
4
|
+
class States
|
5
|
+
class Item < Struct.new(:rule, :position, keyword_init: true)
|
6
|
+
# Optimization for States#setup_state
|
7
|
+
def hash
|
8
|
+
[rule.id, position].hash
|
9
|
+
end
|
10
|
+
|
11
|
+
def rule_id
|
12
|
+
rule.id
|
13
|
+
end
|
14
|
+
|
15
|
+
def next_sym
|
16
|
+
rule.rhs[position]
|
17
|
+
end
|
18
|
+
|
19
|
+
def end_of_rule?
|
20
|
+
rule.rhs.count == position
|
21
|
+
end
|
22
|
+
|
23
|
+
def new_by_next_position
|
24
|
+
Item.new(rule: rule, position: position + 1)
|
25
|
+
end
|
26
|
+
|
27
|
+
def previous_sym
|
28
|
+
rule.rhs[position - 1]
|
29
|
+
end
|
30
|
+
|
31
|
+
def display_name
|
32
|
+
r = rule.rhs.map(&:display_name).insert(position, "•").join(" ")
|
33
|
+
"#{r} (rule #{rule.id})"
|
34
|
+
end
|
35
|
+
|
36
|
+
# Right after position
|
37
|
+
def display_rest
|
38
|
+
r = rule.rhs[position..-1].map(&:display_name).join(" ")
|
39
|
+
". #{r} (rule #{rule.id})"
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
data/lib/lrama/states.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
require "forwardable"
|
2
|
-
require "lrama/report"
|
2
|
+
require "lrama/report/duration"
|
3
|
+
require "lrama/states/item"
|
3
4
|
|
4
5
|
module Lrama
|
5
6
|
# States is passed to a template file
|
@@ -11,46 +12,7 @@ module Lrama
|
|
11
12
|
include Lrama::Report::Duration
|
12
13
|
|
13
14
|
def_delegators "@grammar", :symbols, :terms, :nterms, :rules,
|
14
|
-
:accept_symbol, :eof_symbol, :find_symbol_by_s_value!
|
15
|
-
|
16
|
-
# TODO: Validate position is not over rule rhs
|
17
|
-
Item = Struct.new(:rule, :position, keyword_init: true) do
|
18
|
-
# Optimization for States#setup_state
|
19
|
-
def hash
|
20
|
-
[rule.id, position].hash
|
21
|
-
end
|
22
|
-
|
23
|
-
def rule_id
|
24
|
-
rule.id
|
25
|
-
end
|
26
|
-
|
27
|
-
def next_sym
|
28
|
-
rule.rhs[position]
|
29
|
-
end
|
30
|
-
|
31
|
-
def end_of_rule?
|
32
|
-
rule.rhs.count == position
|
33
|
-
end
|
34
|
-
|
35
|
-
def new_by_next_position
|
36
|
-
Item.new(rule: rule, position: position + 1)
|
37
|
-
end
|
38
|
-
|
39
|
-
def previous_sym
|
40
|
-
rule.rhs[position - 1]
|
41
|
-
end
|
42
|
-
|
43
|
-
def display_name
|
44
|
-
r = rule.rhs.map(&:display_name).insert(position, "•").join(" ")
|
45
|
-
"#{r} (rule #{rule.id})"
|
46
|
-
end
|
47
|
-
|
48
|
-
# Right after position
|
49
|
-
def display_rest
|
50
|
-
r = rule.rhs[position..-1].map(&:display_name).join(" ")
|
51
|
-
". #{r} (rule #{rule.id})"
|
52
|
-
end
|
53
|
-
end
|
15
|
+
:accept_symbol, :eof_symbol, :undef_symbol, :find_symbol_by_s_value!
|
54
16
|
|
55
17
|
attr_reader :states, :reads_relation, :includes_relation, :lookback_relation
|
56
18
|
|
data/lib/lrama/version.rb
CHANGED
data/lrama.gemspec
CHANGED
@@ -8,9 +8,9 @@ Gem::Specification.new do |spec|
|
|
8
8
|
|
9
9
|
spec.summary = "LALR (1) parser generator written by Ruby"
|
10
10
|
spec.description = "LALR (1) parser generator written by Ruby"
|
11
|
-
spec.homepage = "https://github.com/
|
11
|
+
spec.homepage = "https://github.com/ruby/lrama"
|
12
12
|
# See LEGAL.md file for detail
|
13
|
-
spec.license = "
|
13
|
+
spec.license = "GPL-3.0-or-later"
|
14
14
|
spec.required_ruby_version = Gem::Requirement.new(">= 2.5.0")
|
15
15
|
|
16
16
|
spec.files = Dir.chdir(File.expand_path(__dir__)) do
|
@@ -0,0 +1,26 @@
|
|
1
|
+
---
|
2
|
+
sources:
|
3
|
+
- type: git
|
4
|
+
name: ruby/gem_rbs_collection
|
5
|
+
revision: 28208148c7e64a25e9b86b9723b4c3a2cef14e81
|
6
|
+
remote: https://github.com/ruby/gem_rbs_collection.git
|
7
|
+
repo_dir: gems
|
8
|
+
path: ".gem_rbs_collection"
|
9
|
+
gems:
|
10
|
+
- name: erb
|
11
|
+
version: '0'
|
12
|
+
source:
|
13
|
+
type: stdlib
|
14
|
+
- name: stackprof
|
15
|
+
version: '0.2'
|
16
|
+
source:
|
17
|
+
type: git
|
18
|
+
name: ruby/gem_rbs_collection
|
19
|
+
revision: 28208148c7e64a25e9b86b9723b4c3a2cef14e81
|
20
|
+
remote: https://github.com/ruby/gem_rbs_collection.git
|
21
|
+
repo_dir: gems
|
22
|
+
- name: strscan
|
23
|
+
version: '0'
|
24
|
+
source:
|
25
|
+
type: stdlib
|
26
|
+
gemfile_lock_path: Gemfile.lock
|
data/rbs_collection.yaml
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
# Download sources
|
2
|
+
sources:
|
3
|
+
- type: git
|
4
|
+
name: ruby/gem_rbs_collection
|
5
|
+
remote: https://github.com/ruby/gem_rbs_collection.git
|
6
|
+
revision: main
|
7
|
+
repo_dir: gems
|
8
|
+
|
9
|
+
# You can specify local directories as sources also.
|
10
|
+
# - type: local
|
11
|
+
# path: path/to/your/local/repository
|
12
|
+
|
13
|
+
# A directory to install the downloaded RBSs
|
14
|
+
path: .gem_rbs_collection
|
15
|
+
|
16
|
+
gems:
|
17
|
+
- name: erb
|
18
|
+
- name: strscan
|
19
|
+
# Skip loading rbs gem's RBS.
|
20
|
+
# It's unnecessary if you don't use rbs as a library.
|
21
|
+
- name: rbs
|
22
|
+
ignore: true
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module Lrama
|
2
|
+
class Warning
|
3
|
+
interface _Appendable
|
4
|
+
def <<: (String message) -> self
|
5
|
+
end
|
6
|
+
|
7
|
+
@out: _Appendable
|
8
|
+
|
9
|
+
attr_reader errors: Array[String]
|
10
|
+
attr_reader warns: Array[String]
|
11
|
+
def initialize: (?_Appendable out) -> void
|
12
|
+
def error: (String message) -> void
|
13
|
+
def warn: (String message) -> void
|
14
|
+
def has_error?: -> bool
|
15
|
+
end
|
16
|
+
end
|