Skip to content

Commit 4655d21

Browse files
committed
Lrama v0.5.6
1 parent c75d54a commit 4655d21

File tree

8 files changed

+188
-141
lines changed

8 files changed

+188
-141
lines changed

tool/lrama/exe/lrama

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,4 @@
33
$LOAD_PATH << File.join(__dir__, "../lib")
44
require "lrama"
55

6-
Lrama::Command.new(ARGV.dup).run
6+
Lrama::Command.new.run(ARGV.dup)

tool/lrama/lib/lrama.rb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
require "lrama/digraph"
66
require "lrama/grammar"
77
require "lrama/lexer"
8+
require "lrama/option_parser"
9+
require "lrama/options"
810
require "lrama/output"
911
require "lrama/parser"
1012
require "lrama/report"

tool/lrama/lib/lrama/command.rb

Lines changed: 15 additions & 137 deletions
Original file line numberDiff line numberDiff line change
@@ -1,162 +1,40 @@
1-
require 'optparse'
2-
31
module Lrama
42
class Command
5-
def initialize(argv)
6-
@argv = argv
7-
8-
@skeleton = "bison/yacc.c"
9-
@header = false
10-
@header_file = nil
11-
@report = []
12-
@report_file = nil
13-
@outfile = "y.tab.c"
14-
@trace = []
15-
@error_recovery = false
16-
@grammar_file = nil
17-
@report_file = nil
18-
@trace_opts = nil
19-
@report_opts = nil
20-
end
3+
def run(argv)
4+
options = OptionParser.new.parse(argv)
215

22-
def run
23-
parse_option
24-
25-
Report::Duration.enable if @trace_opts[:time]
6+
Report::Duration.enable if options.trace_opts[:time]
267

278
warning = Lrama::Warning.new
28-
grammar = Lrama::Parser.new(@y.read).parse
29-
@y.close if @y != STDIN
30-
states = Lrama::States.new(grammar, warning, trace_state: (@trace_opts[:automaton] || @trace_opts[:closure]))
9+
grammar = Lrama::Parser.new(options.y.read).parse
10+
options.y.close if options.y != STDIN
11+
states = Lrama::States.new(grammar, warning, trace_state: (options.trace_opts[:automaton] || options.trace_opts[:closure]))
3112
states.compute
3213
context = Lrama::Context.new(states)
3314

34-
if @report_file
15+
if options.report_file
3516
reporter = Lrama::StatesReporter.new(states)
36-
File.open(@report_file, "w+") do |f|
37-
reporter.report(f, **@report_opts)
17+
File.open(options.report_file, "w+") do |f|
18+
reporter.report(f, **options.report_opts)
3819
end
3920
end
4021

41-
File.open(@outfile, "w+") do |f|
22+
File.open(options.outfile, "w+") do |f|
4223
Lrama::Output.new(
4324
out: f,
44-
output_file_path: @outfile,
45-
template_name: @skeleton,
46-
grammar_file_path: @grammar_file,
47-
header_file_path: @header_file,
25+
output_file_path: options.outfile,
26+
template_name: options.skeleton,
27+
grammar_file_path: options.grammar_file,
28+
header_file_path: options.header_file,
4829
context: context,
4930
grammar: grammar,
50-
error_recovery: @error_recovery,
31+
error_recovery: options.error_recovery,
5132
).render
5233
end
5334

5435
if warning.has_error?
5536
exit 1
5637
end
5738
end
58-
59-
private
60-
61-
def validate_report(report)
62-
bison_list = %w[states itemsets lookaheads solved counterexamples cex all none]
63-
others = %w[verbose]
64-
list = bison_list + others
65-
not_supported = %w[cex none]
66-
h = { grammar: true }
67-
68-
report.each do |r|
69-
if list.include?(r) && !not_supported.include?(r)
70-
h[r.to_sym] = true
71-
else
72-
raise "Invalid report option \"#{r}\"."
73-
end
74-
end
75-
76-
if h[:all]
77-
(bison_list - not_supported).each do |r|
78-
h[r.to_sym] = true
79-
end
80-
81-
h.delete(:all)
82-
end
83-
84-
return h
85-
end
86-
87-
def validate_trace(trace)
88-
list = %w[
89-
none locations scan parse automaton bitsets
90-
closure grammar resource sets muscles tools
91-
m4-early m4 skeleton time ielr cex all
92-
]
93-
h = {}
94-
95-
trace.each do |t|
96-
if list.include?(t)
97-
h[t.to_sym] = true
98-
else
99-
raise "Invalid trace option \"#{t}\"."
100-
end
101-
end
102-
103-
return h
104-
end
105-
106-
def parse_option
107-
opt = OptionParser.new
108-
109-
# opt.on('-h') {|v| p v }
110-
opt.on('-V', '--version') {|v| puts "lrama #{Lrama::VERSION}"; exit 0 }
111-
112-
# Tuning the Parser
113-
opt.on('-S', '--skeleton=FILE') {|v| @skeleton = v }
114-
opt.on('-t') { } # Do nothing
115-
116-
# Output Files:
117-
opt.on('-h', '--header=[FILE]') {|v| @header = true; @header_file = v }
118-
opt.on('-d') { @header = true }
119-
opt.on('-r', '--report=THINGS', Array) {|v| @report = v }
120-
opt.on('--report-file=FILE') {|v| @report_file = v }
121-
opt.on('-v') { } # Do nothing
122-
opt.on('-o', '--output=FILE') {|v| @outfile = v }
123-
124-
# Hidden
125-
opt.on('--trace=THINGS', Array) {|v| @trace = v }
126-
127-
# Error Recovery
128-
opt.on('-e') {|v| @error_recovery = true }
129-
130-
opt.parse!(@argv)
131-
132-
@trace_opts = validate_trace(@trace)
133-
@report_opts = validate_report(@report)
134-
135-
@grammar_file = @argv.shift
136-
137-
if !@grammar_file
138-
abort "File should be specified\n"
139-
end
140-
141-
if @grammar_file == '-'
142-
@grammar_file = @argv.shift or abort "File name for STDIN should be specified\n"
143-
@y = STDIN
144-
else
145-
@y = File.open(@grammar_file, 'r')
146-
end
147-
148-
if !@report.empty? && @report_file.nil? && @grammar_file
149-
@report_file = File.dirname(@grammar_file) + "/" + File.basename(@grammar_file, ".*") + ".output"
150-
end
151-
152-
if !@header_file && @header
153-
case
154-
when @outfile
155-
@header_file = File.dirname(@outfile) + "/" + File.basename(@outfile, ".*") + ".h"
156-
when @grammar_file
157-
@header_file = File.dirname(@grammar_file) + "/" + File.basename(@grammar_file, ".*") + ".h"
158-
end
159-
end
160-
end
16139
end
16240
end

tool/lrama/lib/lrama/lexer.rb

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -213,19 +213,33 @@ def lex_user_code(ss, line, column, lines)
213213
string, line = lex_string(ss, "'", line, lines)
214214
str << string
215215
next
216+
217+
# $ references
218+
# It need to wrap an identifier with brackets to use ".-" for identifiers
216219
when ss.scan(/\$(<[a-zA-Z0-9_]+>)?\$/) # $$, $<long>$
217220
tag = ss[1] ? create_token(Token::Tag, ss[1], line, str.length) : nil
218221
references << [:dollar, "$", tag, str.length, str.length + ss[0].length - 1]
219222
when ss.scan(/\$(<[a-zA-Z0-9_]+>)?(\d+)/) # $1, $2, $<long>1
220223
tag = ss[1] ? create_token(Token::Tag, ss[1], line, str.length) : nil
221224
references << [:dollar, Integer(ss[2]), tag, str.length, str.length + ss[0].length - 1]
222-
when ss.scan(/\$(<[a-zA-Z0-9_]+>)?([a-zA-Z_.][-a-zA-Z0-9_.]*)/) # $foo, $expr, $<long>program
225+
when ss.scan(/\$(<[a-zA-Z0-9_]+>)?([a-zA-Z_][a-zA-Z0-9_]*)/) # $foo, $expr, $<long>program (named reference without brackets)
226+
tag = ss[1] ? create_token(Token::Tag, ss[1], line, str.length) : nil
227+
references << [:dollar, ss[2], tag, str.length, str.length + ss[0].length - 1]
228+
when ss.scan(/\$(<[a-zA-Z0-9_]+>)?\[([a-zA-Z_.][-a-zA-Z0-9_.]*)\]/) # $expr.right, $expr-right, $<long>program (named reference with brackets)
223229
tag = ss[1] ? create_token(Token::Tag, ss[1], line, str.length) : nil
224230
references << [:dollar, ss[2], tag, str.length, str.length + ss[0].length - 1]
231+
232+
# @ references
233+
# It need to wrap an identifier with brackets to use ".-" for identifiers
225234
when ss.scan(/@\$/) # @$
226235
references << [:at, "$", nil, str.length, str.length + ss[0].length - 1]
227236
when ss.scan(/@(\d+)/) # @1
228237
references << [:at, Integer(ss[1]), nil, str.length, str.length + ss[0].length - 1]
238+
when ss.scan(/@([a-zA-Z][a-zA-Z0-9_]*)/) # @foo, @expr (named reference without brackets)
239+
references << [:at, ss[1], nil, str.length, str.length + ss[0].length - 1]
240+
when ss.scan(/@\[([a-zA-Z_.][-a-zA-Z0-9_.]*)\]/) # @expr.right, @expr-right (named reference with brackets)
241+
references << [:at, ss[1], nil, str.length, str.length + ss[0].length - 1]
242+
229243
when ss.scan(/{/)
230244
brace_count += 1
231245
when ss.scan(/}/)

tool/lrama/lib/lrama/lexer/token.rb

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,13 @@ def numberize_references(lhs, rhs)
2828
if lhs.referred_by?(ref_name)
2929
'$'
3030
else
31-
rhs.find_index {|token| token.referred_by?(ref_name) } + 1
31+
index = rhs.find_index {|token| token.referred_by?(ref_name) }
32+
33+
if index
34+
index + 1
35+
else
36+
raise "'#{ref_name}' is invalid name."
37+
end
3238
end
3339
[ref[0], value, ref[2], ref[3], ref[4]]
3440
else

tool/lrama/lib/lrama/option_parser.rb

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
require 'optparse'
2+
3+
module Lrama
4+
# Handle option parsing for the command line interface.
5+
class OptionParser
6+
def initialize
7+
@options = Options.new
8+
@trace = []
9+
@report = []
10+
end
11+
12+
def parse(argv)
13+
parse_by_option_parser(argv)
14+
15+
@options.trace_opts = validate_trace(@trace)
16+
@options.report_opts = validate_report(@report)
17+
@options.grammar_file = argv.shift
18+
19+
if !@options.grammar_file
20+
abort "File should be specified\n"
21+
end
22+
23+
if @options.grammar_file == '-'
24+
@options.grammar_file = argv.shift or abort "File name for STDIN should be specified\n"
25+
else
26+
@options.y = File.open(@options.grammar_file, 'r')
27+
end
28+
29+
if !@report.empty? && @options.report_file.nil? && @options.grammar_file
30+
@options.report_file = File.dirname(@options.grammar_file) + "/" + File.basename(@options.grammar_file, ".*") + ".output"
31+
end
32+
33+
if !@options.header_file && @options.header
34+
case
35+
when @options.outfile
36+
@options.header_file = File.dirname(@options.outfile) + "/" + File.basename(@options.outfile, ".*") + ".h"
37+
when @options.grammar_file
38+
@options.header_file = File.dirname(@options.grammar_file) + "/" + File.basename(@options.grammar_file, ".*") + ".h"
39+
end
40+
end
41+
42+
@options
43+
end
44+
45+
private
46+
47+
def parse_by_option_parser(argv)
48+
::OptionParser.new do |o|
49+
o.banner = <<~BANNER
50+
Lrama is LALR (1) parser generator written by Ruby.
51+
52+
Usage: lrama [options] FILE
53+
BANNER
54+
o.separator ''
55+
o.separator 'Tuning the Parser:'
56+
o.on('-S', '--skeleton=FILE', 'specify the skeleton to use') {|v| @options.skeleton = v }
57+
o.on('-t', 'reserved, do nothing') { }
58+
o.separator ''
59+
o.separator 'Output:'
60+
o.on('-h', '--header=[FILE]', 'also produce a header file named FILE') {|v| @options.header = true; @options.header_file = v }
61+
o.on('-d', 'also produce a header file') { @options.header = true }
62+
o.on('-r', '--report=THINGS', Array, 'also produce details on the automaton') {|v| @report = v }
63+
o.on('--report-file=FILE', 'also produce details on the automaton output to a file named FILE') {|v| @options.report_file = v }
64+
o.on('-o', '--output=FILE', 'leave output to FILE') {|v| @options.outfile = v }
65+
o.on('--trace=THINGS', Array, 'also output trace logs at runtime') {|v| @trace = v }
66+
o.on('-v', 'reserved, do nothing') { }
67+
o.separator ''
68+
o.separator 'Error Recovery:'
69+
o.on('-e', 'enable error recovery') {|v| @options.error_recovery = true }
70+
o.separator ''
71+
o.separator 'Other options:'
72+
o.on('-V', '--version', "output version information and exit") {|v| puts "lrama #{Lrama::VERSION}"; exit 0 }
73+
o.on('--help', "display this help and exit") {|v| puts o; exit 0 }
74+
o.separator ''
75+
o.parse!(argv)
76+
end
77+
end
78+
79+
def validate_report(report)
80+
bison_list = %w[states itemsets lookaheads solved counterexamples cex all none]
81+
others = %w[verbose]
82+
list = bison_list + others
83+
not_supported = %w[cex none]
84+
h = { grammar: true }
85+
86+
report.each do |r|
87+
if list.include?(r) && !not_supported.include?(r)
88+
h[r.to_sym] = true
89+
else
90+
raise "Invalid report option \"#{r}\"."
91+
end
92+
end
93+
94+
if h[:all]
95+
(bison_list - not_supported).each do |r|
96+
h[r.to_sym] = true
97+
end
98+
99+
h.delete(:all)
100+
end
101+
102+
return h
103+
end
104+
105+
def validate_trace(trace)
106+
list = %w[
107+
none locations scan parse automaton bitsets
108+
closure grammar resource sets muscles tools
109+
m4-early m4 skeleton time ielr cex all
110+
]
111+
h = {}
112+
113+
trace.each do |t|
114+
if list.include?(t)
115+
h[t.to_sym] = true
116+
else
117+
raise "Invalid trace option \"#{t}\"."
118+
end
119+
end
120+
121+
return h
122+
end
123+
end
124+
end

tool/lrama/lib/lrama/options.rb

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
module Lrama
2+
# Command line options.
3+
class Options
4+
attr_accessor :skeleton, :header, :header_file,
5+
:report_file, :outfile,
6+
:error_recovery, :grammar_file,
7+
:report_file, :trace_opts, :report_opts, :y
8+
9+
def initialize
10+
@skeleton = "bison/yacc.c"
11+
@header = false
12+
@header_file = nil
13+
@report_file = nil
14+
@outfile = "y.tab.c"
15+
@error_recovery = false
16+
@grammar_file = nil
17+
@report_file = nil
18+
@trace_opts = nil
19+
@report_opts = nil
20+
@y = STDIN
21+
end
22+
end
23+
end

tool/lrama/lib/lrama/version.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
module Lrama
2-
VERSION = "0.5.5".freeze
2+
VERSION = "0.5.6".freeze
33
end

0 commit comments

Comments
 (0)