rockit 0.7.1
Sign up to get free protection for your applications and to get access to all the features.
- data/BUGS +13 -0
- data/LICENSE +280 -0
- data/README +172 -0
- data/TODO +53 -0
- data/VERSION +1 -0
- data/lib/packrat/grammar.rb +537 -0
- data/lib/rockit/prettyprint/box.rb +60 -0
- data/lib/rockit/prettyprint/renderer.rb +41 -0
- data/lib/rockit/prettyprint/text_renderer.rb +47 -0
- data/lib/rockit/tree/base.rb +223 -0
- data/lib/rockit/tree/enter_leave_visitor.rb +12 -0
- data/lib/rockit/tree/graphviz.rb +69 -0
- data/lib/rockit/tree/visitor.rb +12 -0
- data/lib/util/array_alternatives.rb +20 -0
- data/lib/util/enter_leave_visitor.rb +69 -0
- data/lib/util/graphviz_dot.rb +182 -0
- data/lib/util/string_location.rb +42 -0
- data/lib/util/visitor.rb +49 -0
- data/lib/util/visitor_combinators.rb +14 -0
- data/rakefile +200 -0
- data/tests/acceptance/packrat/minibasic/atest_minibasic.rb +45 -0
- data/tests/acceptance/packrat/minibasic/minibasic.rb +137 -0
- data/tests/acceptance/rockit/dparser/atest_any_operator.rb +33 -0
- data/tests/acceptance/rockit/dparser/atest_arithmetic_grammar.rb +30 -0
- data/tests/acceptance/rockit/dparser/atest_list_operator.rb +57 -0
- data/tests/acceptance/rockit/dparser/atest_mult_operator.rb +60 -0
- data/tests/acceptance/rockit/dparser/atest_operator_grammar.rb +61 -0
- data/tests/acceptance/rockit/dparser/atest_plus_operator.rb +55 -0
- data/tests/acceptance/rockit/dparser/atest_samples_calculator.rb +14 -0
- data/tests/acceptance/rockit/dparser/atest_samples_minibasic.rb +20 -0
- data/tests/acceptance/rockit/dparser/atest_samples_multifunccalculator.rb +36 -0
- data/tests/acceptance/rockit/dparser/atest_simple_grammar.rb +34 -0
- data/tests/acceptance/rockit/dparser/atest_speculative_code_action.rb +128 -0
- data/tests/acceptance/rockit/dparser/calc_tests_common.rb +103 -0
- data/tests/unit/packrat/test_interpreting_parser.rb +296 -0
- data/tests/unit/parse/utest_ebnf_grammar.rb +50 -0
- data/tests/unit/parse/utest_expand_grammar.rb +23 -0
- data/tests/unit/parse/utest_grammar.rb +160 -0
- data/tests/unit/rockit/assembler/llvm/utest_instructions.rb +41 -0
- data/tests/unit/rockit/assembler/llvm/utest_module.rb +19 -0
- data/tests/unit/rockit/prettyprint/utest_box.rb +44 -0
- data/tests/unit/rockit/tree/utest_tree_base.rb +301 -0
- data/tests/unit/rockit/tree/utest_tree_enter_leave_visitor.rb +69 -0
- data/tests/unit/rockit/tree/utest_tree_visitor.rb +63 -0
- data/tests/unit/rockit/utest_grammar.rb +145 -0
- data/tests/unit/rockit/utest_grammar_symbol.rb +11 -0
- data/tests/unit/rockit/utest_maybe_operator.rb +12 -0
- data/tests/unit/rockit/utest_regexp_terminal.rb +45 -0
- data/tests/unit/rockit/utest_repetition_operators.rb +35 -0
- data/tests/unit/rockit/utest_rule.rb +23 -0
- data/tests/unit/rockit/utest_string_terminal.rb +40 -0
- data/tests/unit/util/utest_array_alternatives.rb +23 -0
- data/tests/unit/util/utest_enter_leave_visitor.rb +89 -0
- data/tests/unit/util/utest_string_location.rb +42 -0
- data/tests/unit/util/utest_visitor.rb +92 -0
- data/tests/unit/util/utest_visitor_combinators.rb +64 -0
- metadata +112 -0
data/rakefile
ADDED
@@ -0,0 +1,200 @@
|
|
1
|
+
require 'rake'
|
2
|
+
require 'rake/clean'
|
3
|
+
require 'rake/packagetask'
|
4
|
+
require 'rake/gempackagetask'
|
5
|
+
|
6
|
+
#require 'rubygems'
|
7
|
+
|
8
|
+
#require 'rbconfig'
|
9
|
+
#require 'find'
|
10
|
+
#require 'ftools'
|
11
|
+
#require 'fileutils'
|
12
|
+
#include Config
|
13
|
+
|
14
|
+
PROJECT = "rockit"
|
15
|
+
|
16
|
+
VERSION_FILE = "VERSION"
|
17
|
+
MANIFEST_FILE = "Manifest"
|
18
|
+
README_FILE = "README"
|
19
|
+
TODO_FILE = "TODO"
|
20
|
+
RAKE_FILE = "rakefile"
|
21
|
+
CHANGELOG = "Changelog"
|
22
|
+
|
23
|
+
|
24
|
+
task :default => :ptest
|
25
|
+
|
26
|
+
|
27
|
+
#############################################################################
|
28
|
+
# Version related
|
29
|
+
#############################################################################
|
30
|
+
|
31
|
+
# Read the version from the version file
|
32
|
+
def Version()
|
33
|
+
File.open(VERSION_FILE) {|fh| fh.read.strip}
|
34
|
+
end
|
35
|
+
|
36
|
+
# Update the version num on disc
|
37
|
+
def update_version
|
38
|
+
v = Version().split(".").map {|vp| vp.to_i}
|
39
|
+
new_v = yield(v)
|
40
|
+
File.open(VERSION_FILE, "w") {|fh| fh.write(new_v)}
|
41
|
+
puts Version()
|
42
|
+
end
|
43
|
+
|
44
|
+
def inc_tiny; update_version {|v| "#{v[0]}.#{v[1]}.#{v[2]+1}\n"}; end
|
45
|
+
def inc_minor; update_version {|v| "#{v[0]}.#{v[1]+1}.0\n"}; end
|
46
|
+
def inc_major; update_version {|v| "#{v[0]+1}.0.0\n"}; end
|
47
|
+
|
48
|
+
task :version do
|
49
|
+
puts Version()
|
50
|
+
end
|
51
|
+
task :inc_tiny do
|
52
|
+
inc_tiny()
|
53
|
+
end
|
54
|
+
task :inc_minor do
|
55
|
+
inc_minor()
|
56
|
+
end
|
57
|
+
task :inc_major do
|
58
|
+
inc_major()
|
59
|
+
end
|
60
|
+
|
61
|
+
#############################################################################
|
62
|
+
# Test related
|
63
|
+
#############################################################################
|
64
|
+
|
65
|
+
PackratTestFiles = FileList["tests/unit/packrat/test*.rb"]
|
66
|
+
PackratAcceptanceTestFiles = FileList["tests/acceptance/packrat/**/atest*.rb"]
|
67
|
+
|
68
|
+
task :setup_for_test do
|
69
|
+
$: << "lib"
|
70
|
+
end
|
71
|
+
|
72
|
+
def load_files(list)
|
73
|
+
list.each {|f| puts "Loading #{f}"; require f}
|
74
|
+
end
|
75
|
+
|
76
|
+
task :ptest => [:setup_for_test] do
|
77
|
+
load_files PackratTestFiles
|
78
|
+
end
|
79
|
+
|
80
|
+
task :patest => [:setup_for_test] do
|
81
|
+
load_files PackratAcceptanceTestFiles
|
82
|
+
end
|
83
|
+
|
84
|
+
task :unit_tests => [:setup_for_test] do
|
85
|
+
load_files FileList["tests/unit/**/test*.rb"]
|
86
|
+
end
|
87
|
+
|
88
|
+
task :acceptance_tests do
|
89
|
+
load_files FileList["tests/acceptance/**/test*.rb"]
|
90
|
+
end
|
91
|
+
|
92
|
+
task :utest => :unit_tests
|
93
|
+
task :ut => :unit_tests
|
94
|
+
task :atest => :acceptance_tests
|
95
|
+
task :at => :acceptance_tests
|
96
|
+
|
97
|
+
#############################################################################
|
98
|
+
# Gem and package related
|
99
|
+
#############################################################################
|
100
|
+
|
101
|
+
BaseFileToIncludeGlobs = [
|
102
|
+
"README", "TODO", "BUGS", "LICENSE", "VERSION",
|
103
|
+
"rakefile", "Manifest",
|
104
|
+
"lib/packrat/**/*",
|
105
|
+
"lib/rockit/prettyprint/**/*",
|
106
|
+
"lib/rockit/tree/**/*",
|
107
|
+
"lib/util/**/*",
|
108
|
+
]
|
109
|
+
|
110
|
+
TestFilesToIncludeGlobs = [
|
111
|
+
"tests/acceptance/**/*",
|
112
|
+
"tests/unit/**/*",
|
113
|
+
]
|
114
|
+
|
115
|
+
PkgFileGlobs = BaseFileToIncludeGlobs + TestFilesToIncludeGlobs
|
116
|
+
|
117
|
+
GemSpec = Gem::Specification.new do |s|
|
118
|
+
s.name = PROJECT
|
119
|
+
s.version = Version()
|
120
|
+
s.platform = Gem::Platform::RUBY
|
121
|
+
s.summary = "Rockit is the Ruby Object-oriented Compiler construction toolKIT"
|
122
|
+
s.author = "Robert Feldt"
|
123
|
+
s.email = "Robert.Feldt@gmail.com"
|
124
|
+
s.homepage = "http://www.pronovomundo.com/projects/ruby/rockit"
|
125
|
+
|
126
|
+
s.description = <<EOS
|
127
|
+
Rockit is a potent parser generator and gives you AST's (Abstract Syntax Tree's) which you can pattern match and pretty-print. Rockit does not distinghuish between lexing and parsing so the generated parsers are scanner-/lexer-less. The vision is to extend Rockit with more advanced compiler-related abilities including back-ends and code generation. However, currently the focus is on parsing and AST-related tasks such as transformation.
|
128
|
+
EOS
|
129
|
+
|
130
|
+
s.require_path = "lib"
|
131
|
+
s.requirements << "none"
|
132
|
+
|
133
|
+
s.has_rdoc = false
|
134
|
+
# Dependency on rake?
|
135
|
+
|
136
|
+
s.files = []
|
137
|
+
PkgFileGlobs.each do |globpat|
|
138
|
+
fs = Dir.glob(globpat).delete_if {|i| i.include?(".svn")}
|
139
|
+
s.files += fs
|
140
|
+
end
|
141
|
+
|
142
|
+
s.extensions = []
|
143
|
+
end
|
144
|
+
|
145
|
+
Rake::GemPackageTask.new(GemSpec) do |pkg|
|
146
|
+
pkg.need_zip = true
|
147
|
+
pkg.need_tar = true
|
148
|
+
end
|
149
|
+
|
150
|
+
# Reformat the 'svn log' output to become the Changelog file. We assume
|
151
|
+
# log entries are single lines with each log event starting with an asterisk
|
152
|
+
# "*" and use those as split markers. We also insert newlines before the
|
153
|
+
# svn log event separators "-----------------------...---".
|
154
|
+
def reformat_svn_log_to_changelog(svnLogAsString)
|
155
|
+
s = svnLogAsString.split(/, \*/).join(",\n\n* ")
|
156
|
+
s.split(/^------/).join("\n------")
|
157
|
+
end
|
158
|
+
|
159
|
+
task :changelog do
|
160
|
+
# We must run 'svn up' first to make sure the log is recent
|
161
|
+
system "svn up"
|
162
|
+
File.open("Changelog", "w") do |fh|
|
163
|
+
fh.write reformat_svn_log_to_changelog(`svn log`)
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
#############################################################################
|
168
|
+
# Rubyforge related
|
169
|
+
#############################################################################
|
170
|
+
|
171
|
+
# Create a package (a name for a top-level "item" which can then have multiple
|
172
|
+
# releases where each release can have multiple files)
|
173
|
+
def create_package(packageName = "rockit")
|
174
|
+
system "rubyforge create_package #{PROJECT} #{packageName}"
|
175
|
+
end
|
176
|
+
|
177
|
+
# Release a <file> with a given <release_name> in the given <package>.
|
178
|
+
def release_file(file, release_name, package = PROJECT)
|
179
|
+
group_id = PROJECT
|
180
|
+
system "rubyforge add_release #{group_id} #{package} #{release_name} #{file}"
|
181
|
+
end
|
182
|
+
|
183
|
+
# Delete a package (and all its files)
|
184
|
+
def delete_package(packageName)
|
185
|
+
system "rubyforge create_package rockit #{ARGV.last}"
|
186
|
+
end
|
187
|
+
|
188
|
+
# We use a temporary package name for now, should be PROJECT later...
|
189
|
+
PACKAGE_NAME = "1799" # if of the test package in rockit group
|
190
|
+
|
191
|
+
desc "Create package and release to rubyforge"
|
192
|
+
task :rubyforge_release => [:package] do
|
193
|
+
system "rubyforge login"
|
194
|
+
release_name = "#{PROJECT}-#{Version()}"
|
195
|
+
files_to_release = FileList["pkg/#{release_name}.*"]
|
196
|
+
files_to_release.each do |file|
|
197
|
+
puts "Releasing #{file} to Rubyforge"
|
198
|
+
release_file(file, release_name, PACKAGE_NAME)
|
199
|
+
end
|
200
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
|
3
|
+
require File.join(File.dirname(__FILE__), "minibasic")
|
4
|
+
|
5
|
+
class ATestMiniBasic < Test::Unit::TestCase
|
6
|
+
def assert_parse(exp, str)
|
7
|
+
res = MiniBasic::Parser.parse_string(str)
|
8
|
+
assert_equal(exp, res)
|
9
|
+
end
|
10
|
+
|
11
|
+
def assert_stmt(expStmt, str)
|
12
|
+
assert_parse([expStmt], str)
|
13
|
+
end
|
14
|
+
|
15
|
+
include MiniBasic::Grammar::ASTs
|
16
|
+
|
17
|
+
def test_01
|
18
|
+
assert_stmt(PrintLn["PRINTLN"], "PRINTLN")
|
19
|
+
end
|
20
|
+
|
21
|
+
def test_02
|
22
|
+
assert_stmt(Read["READ", :A], "READ A")
|
23
|
+
end
|
24
|
+
|
25
|
+
def test_03
|
26
|
+
assert_stmt(Print["PRINT", :BC], "PRINT BC")
|
27
|
+
end
|
28
|
+
|
29
|
+
def test_04
|
30
|
+
assert_stmt(Assign[:D, ":=", 1], "D := 1")
|
31
|
+
assert_stmt(Assign[:D, ":=", 1], "D :=1")
|
32
|
+
assert_stmt(Assign[:D, ":=", 1], "D:=1")
|
33
|
+
end
|
34
|
+
|
35
|
+
def test_05
|
36
|
+
assert_stmt(For["FOR", :A, ":=", 1, "TO", 3,
|
37
|
+
[Assign[:S, ":=", OpExpr[:S, "+", :A]],
|
38
|
+
Print["PRINT", :S],
|
39
|
+
],
|
40
|
+
"NEXT"], "FOR A := 1 TO 3 S:= S + A PRINT S NEXT")
|
41
|
+
end
|
42
|
+
|
43
|
+
def atest_06
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,137 @@
|
|
1
|
+
#############################################################################
|
2
|
+
# Grammar and interpreter for a mini version of Basic.
|
3
|
+
# Author: Robert Feldt
|
4
|
+
#
|
5
|
+
# The grammar is based on the minibasic example in SableCC.
|
6
|
+
#
|
7
|
+
# Here's the copyright notice from the original file SableCC file:
|
8
|
+
#
|
9
|
+
# Copyright (C) 1997, 1998, 1999 J-Meg inc. All rights reserved.
|
10
|
+
#
|
11
|
+
# This file is free software; you can redistribute it and/or modify it
|
12
|
+
# under the terms of the GNU Lesser General Public License as published
|
13
|
+
# by the Free Software Foundation; either version 2.1 of the License, or
|
14
|
+
# (at your option) any later version.
|
15
|
+
#
|
16
|
+
# This file is distributed in the hope that it will be useful, but WITHOUT
|
17
|
+
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
18
|
+
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
|
19
|
+
# License for more details.
|
20
|
+
#
|
21
|
+
# You should have received a copy of the GNU Lesser General Public
|
22
|
+
# License along with this file (in the file "COPYING-LESSER"); if not,
|
23
|
+
# write to the Free Software Foundation, Inc., 59 Temple Place,
|
24
|
+
# Suite 330, Boston, MA 02111-1307 USA
|
25
|
+
#
|
26
|
+
# If you have any question, send an electronic message to
|
27
|
+
# Etienne M. Gagnon, M.Sc. (egagnon@j-meg.com), or write to:
|
28
|
+
#
|
29
|
+
# J-Meg inc.
|
30
|
+
# 11348 Brunet
|
31
|
+
# Montreal-Nord (Quebec)
|
32
|
+
# H1G 5G1 Canada
|
33
|
+
#############################################################################
|
34
|
+
require 'packrat/grammar'
|
35
|
+
|
36
|
+
module MiniBasic
|
37
|
+
class Interpreter
|
38
|
+
def initialize
|
39
|
+
# For variables and their values. Default value is 0.
|
40
|
+
@vars = Hash.new(0)
|
41
|
+
end
|
42
|
+
|
43
|
+
def eval(sexpr)
|
44
|
+
case sexpr.first
|
45
|
+
when :Statements
|
46
|
+
sexpr.statements.each {|stmt| mb_eval(stmt)}
|
47
|
+
when "If"
|
48
|
+
if mb_eval(sexpr.condition) # What is true and false in basic?
|
49
|
+
mb_eval(sexpr.statements)
|
50
|
+
elsif sexpr.optelse
|
51
|
+
mb_eval(sexpr.optelse[2])
|
52
|
+
end
|
53
|
+
when "For"
|
54
|
+
for i in (mb_eval(sexpr.from)..mb_eval(sexpr.to))
|
55
|
+
$vars[sexpr.ident.id] = i
|
56
|
+
mb_eval(sexpr.statements)
|
57
|
+
end
|
58
|
+
when "Read"
|
59
|
+
print "? "; STDOUT.flush
|
60
|
+
$vars[sexpr.ident.id] = STDIN.gets.to_i # Error catching?!
|
61
|
+
when "Print"
|
62
|
+
print mb_eval(sexpr.message); STDOUT.flush
|
63
|
+
when "PrintLn"
|
64
|
+
print "\n"; STDOUT.flush
|
65
|
+
when "Assignment"
|
66
|
+
$vars[sexpr.ident.id] = mb_eval(sexpr.expression)
|
67
|
+
when "Condition"
|
68
|
+
map = {">" => :>, "<" => :<, "=" => :==}
|
69
|
+
mb_eval(sexpr.left).send(map[sexpr.op], mb_eval(sexpr.right))
|
70
|
+
when "BinExpr"
|
71
|
+
map = {"+"=>:+, "-"=>:-, "*"=>:*, "/"=>"/".intern, "MOD"=>"%".intern }
|
72
|
+
mb_eval(sexpr.left).send(map[sexpr.op], mb_eval(sexpr.right))
|
73
|
+
when "String"
|
74
|
+
sexpr.value[1..-2] # Skip leading and trailing double quotes
|
75
|
+
when "Identifier"
|
76
|
+
$vars[sexpr.id]
|
77
|
+
when "Number"
|
78
|
+
sexpr.lexeme.to_i
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
Grammar = Packrat::Grammar.new do
|
84
|
+
start_symbol :Program
|
85
|
+
|
86
|
+
# Spacing (S) and Forced Spacing (FS)
|
87
|
+
S = hidden(/\s*/)
|
88
|
+
FS = hidden(/\s\s*/)
|
89
|
+
|
90
|
+
prod :Program, [S, :Statements, eos(), lift(0)]
|
91
|
+
|
92
|
+
prod :Statements, [plus(:Statement), lift(0)]
|
93
|
+
|
94
|
+
rule :Statement, [
|
95
|
+
['IF', FS, :Condition, FS, 'THEN', FS,
|
96
|
+
:Statements, FS,
|
97
|
+
maybe(:OptElse), S,
|
98
|
+
'ENDIF', S, ast(:If)],
|
99
|
+
|
100
|
+
['FOR', FS, :Identifier, S, ':=', S, :Expr, FS, 'TO', FS, :Expr, S,
|
101
|
+
:Statements, S,
|
102
|
+
'NEXT', S, ast(:For)],
|
103
|
+
|
104
|
+
['READ', FS, :Identifier, S, ast(:Read)],
|
105
|
+
|
106
|
+
['PRINTLN', S, ast(:PrintLn)],
|
107
|
+
|
108
|
+
['PRINT', FS, any(:Expr, :String), S, ast(:Print)],
|
109
|
+
|
110
|
+
[:Identifier, S, ':=', S, :Expr, S, ast(:Assign)],
|
111
|
+
]
|
112
|
+
|
113
|
+
prod :OptElse, ['ELSE', FS, :Statements, lift(1)]
|
114
|
+
|
115
|
+
prod :Condition, [:Expr, S, any('<', '>', '='), S, :Expr, ast(:Cond)]
|
116
|
+
|
117
|
+
# This is crude! No precedence levels or handling of associativity.
|
118
|
+
rule :Expr, [
|
119
|
+
[:BaseExpr, S, any('+', '-', '*', '/', 'MOD'), S, :BaseExpr,
|
120
|
+
ast(:OpExpr)
|
121
|
+
],
|
122
|
+
[:BaseExpr, lift(0)],
|
123
|
+
]
|
124
|
+
|
125
|
+
rule :BaseExpr, [
|
126
|
+
[:Number, lift(0)],
|
127
|
+
[:Identifier, lift(0)],
|
128
|
+
['(', S, :Expr, S, ')', lift(2)],
|
129
|
+
]
|
130
|
+
|
131
|
+
prod :String, [/"[^"]*"/, lift(0)]
|
132
|
+
prod :Identifier, [/[A-Z]([A-Z0-9])*/, lift(0) {|r| r.intern}]
|
133
|
+
prod :Number, [/[0-9]+/, lift(0) {|r| r.to_i}]
|
134
|
+
end
|
135
|
+
|
136
|
+
Parser = Grammar.interpreting_parser
|
137
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
require 'rockit/dparser'
|
3
|
+
|
4
|
+
class ATestAnyOperator < Test::Unit::TestCase
|
5
|
+
AnyTestGrammar = Rockit::DParser::Grammar.new do
|
6
|
+
start :S, [["a", any("b", "c"), any(:D, :E), ast(:S, 0, 1, 2)]]
|
7
|
+
("d".."e").map {|s| term(s.upcase.intern, s)}
|
8
|
+
end
|
9
|
+
|
10
|
+
Parser = AnyTestGrammar.new_parser
|
11
|
+
|
12
|
+
def test_01
|
13
|
+
ast = Parser.parse "abd"
|
14
|
+
assert_equal("a", ast[0])
|
15
|
+
assert_equal("b", ast[1])
|
16
|
+
assert_equal("d", ast[2])
|
17
|
+
|
18
|
+
ast = Parser.parse "abe"
|
19
|
+
assert_equal("a", ast[0])
|
20
|
+
assert_equal("b", ast[1])
|
21
|
+
assert_equal("e", ast[2])
|
22
|
+
|
23
|
+
ast = Parser.parse "acd"
|
24
|
+
assert_equal("a", ast[0])
|
25
|
+
assert_equal("c", ast[1])
|
26
|
+
assert_equal("d", ast[2])
|
27
|
+
|
28
|
+
ast = Parser.parse "ace"
|
29
|
+
assert_equal("a", ast[0])
|
30
|
+
assert_equal("c", ast[1])
|
31
|
+
assert_equal("e", ast[2])
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
require 'rockit/dparser'
|
3
|
+
|
4
|
+
class ATestArithmeticGrammar < Test::Unit::TestCase
|
5
|
+
ArithmeticTestGrammar = Rockit::DParser::Grammar.new do
|
6
|
+
start :Expr, [
|
7
|
+
[:Digit, "+", :Digit, ast(:Add, 0, 2)],
|
8
|
+
[:Digit, "*", :Digit, ast(:Mult, 0, 2)],
|
9
|
+
]
|
10
|
+
term :Digit, /[0-9]+/
|
11
|
+
end
|
12
|
+
|
13
|
+
def test_03_use_parser
|
14
|
+
parser = ArithmeticTestGrammar.new_parser
|
15
|
+
|
16
|
+
ast = parser.parse "12+34"
|
17
|
+
assert_equal("Add", ast.name)
|
18
|
+
assert_kind_of(parser.astclass_of_name(:Add), ast)
|
19
|
+
assert_equal(2, ast.num_children)
|
20
|
+
assert_equal("12", ast[0])
|
21
|
+
assert_equal("34", ast[1])
|
22
|
+
|
23
|
+
ast = parser.parse "567*980"
|
24
|
+
assert_equal("Mult", ast.name)
|
25
|
+
assert_kind_of(parser.astclass_of_name(:Mult), ast)
|
26
|
+
assert_equal(2, ast.num_children)
|
27
|
+
assert_equal("567", ast[0])
|
28
|
+
assert_equal("980", ast[1])
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
require 'rockit/dparser'
|
3
|
+
|
4
|
+
class ATestListOperator < Test::Unit::TestCase
|
5
|
+
ListTestGrammar = Rockit::DParser::Grammar.new do
|
6
|
+
start :S, [list(:Number, ",")], value(0)
|
7
|
+
term :Number, [/[0-9]+/]
|
8
|
+
end
|
9
|
+
|
10
|
+
Parser = ListTestGrammar.new_parser
|
11
|
+
|
12
|
+
def test_01_two_value_list
|
13
|
+
ary = Parser.parse("1, 2")
|
14
|
+
assert_equal(["1", "2"], ary)
|
15
|
+
end
|
16
|
+
|
17
|
+
def test_02_multi_value_list
|
18
|
+
ary = Parser.parse("1, 2, 34, 567,890")
|
19
|
+
assert_equal(["1", "2", "34", "567", "890"], ary)
|
20
|
+
end
|
21
|
+
|
22
|
+
def test_03_single_value_list
|
23
|
+
ary = Parser.parse("9731")
|
24
|
+
assert_equal(["9731"], ary)
|
25
|
+
end
|
26
|
+
|
27
|
+
ListAndAstTestGrammar = Rockit::DParser::Grammar.new do
|
28
|
+
start :S, [list(:Digit)], ast(:S, 0)
|
29
|
+
term :Digit, [/[0-9]+/]
|
30
|
+
end
|
31
|
+
|
32
|
+
AstParser = ListAndAstTestGrammar.new_parser
|
33
|
+
|
34
|
+
def test_04_multiple_occurences_and_ast
|
35
|
+
ast = AstParser.parse("7,5,6,9")
|
36
|
+
assert_equal("S", ast.name)
|
37
|
+
assert_kind_of(AstParser.astclass_of_name(:S), ast)
|
38
|
+
assert_equal(1, ast.num_children)
|
39
|
+
assert_equal(["7", "5", "6", "9"], ast[0])
|
40
|
+
end
|
41
|
+
|
42
|
+
def test_05_single_occurence_and_ast
|
43
|
+
ast = AstParser.parse("745")
|
44
|
+
assert_equal("S", ast.name)
|
45
|
+
assert_kind_of(AstParser.astclass_of_name(:S), ast)
|
46
|
+
assert_equal(1, ast.num_children)
|
47
|
+
assert_equal(["745"], ast[0])
|
48
|
+
end
|
49
|
+
|
50
|
+
def atest_06_no_occurence_and_ast
|
51
|
+
ast = AstParser.parse("")
|
52
|
+
assert_equal("S", ast.name)
|
53
|
+
assert_kind_of(AstParser.astclass_of_name(:S), ast)
|
54
|
+
assert_equal(1, ast.num_children)
|
55
|
+
assert_equal([], ast[0])
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
require 'rockit/dparser'
|
3
|
+
|
4
|
+
class ATestMultOperator < Test::Unit::TestCase
|
5
|
+
MultTestGrammar = Rockit::DParser::Grammar.new do
|
6
|
+
start :S, [mult(:Digit)], value(0)
|
7
|
+
term :Digit, [/[0-9]/]
|
8
|
+
end
|
9
|
+
|
10
|
+
Parser = MultTestGrammar.new_parser
|
11
|
+
|
12
|
+
def test_01_multiple_occurences
|
13
|
+
(2..20).each do |i|
|
14
|
+
digit = rand(10).to_s
|
15
|
+
ary = Parser.parse(digit * i)
|
16
|
+
assert_equal((1..i).map {digit}, ary)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def test_02_single_occurence
|
21
|
+
ary = Parser.parse("7")
|
22
|
+
assert_equal(["7"], ary)
|
23
|
+
end
|
24
|
+
|
25
|
+
def test_03_no_occurence
|
26
|
+
ary = Parser.parse("")
|
27
|
+
assert_equal([], ary)
|
28
|
+
end
|
29
|
+
|
30
|
+
MultAndAstTestGrammar = Rockit::DParser::Grammar.new do
|
31
|
+
start :S, [mult(:Digit)], ast(:S, 0)
|
32
|
+
term :Digit, [/[0-9]/]
|
33
|
+
end
|
34
|
+
|
35
|
+
AstParser = MultAndAstTestGrammar.new_parser
|
36
|
+
|
37
|
+
def test_04_multiple_occurences_and_ast
|
38
|
+
ast = AstParser.parse("7 5 6 9")
|
39
|
+
assert_equal("S", ast.name)
|
40
|
+
assert_kind_of(AstParser.astclass_of_name(:S), ast)
|
41
|
+
assert_equal(1, ast.num_children)
|
42
|
+
assert_equal(["7", "5", "6", "9"], ast[0])
|
43
|
+
end
|
44
|
+
|
45
|
+
def test_05_single_occurence_and_ast
|
46
|
+
ast = AstParser.parse("7")
|
47
|
+
assert_equal("S", ast.name)
|
48
|
+
assert_kind_of(AstParser.astclass_of_name(:S), ast)
|
49
|
+
assert_equal(1, ast.num_children)
|
50
|
+
assert_equal(["7"], ast[0])
|
51
|
+
end
|
52
|
+
|
53
|
+
def test_06_no_occurence_and_ast
|
54
|
+
ast = AstParser.parse("")
|
55
|
+
assert_equal("S", ast.name)
|
56
|
+
assert_kind_of(AstParser.astclass_of_name(:S), ast)
|
57
|
+
assert_equal(1, ast.num_children)
|
58
|
+
assert_equal([], ast[0])
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
require 'rockit/dparser'
|
3
|
+
|
4
|
+
class ATestOperatorGrammar < Test::Unit::TestCase
|
5
|
+
MaybeTestGrammar = Rockit::DParser::Grammar.new do
|
6
|
+
start :M, [[:A, maybe(:Digit)]], ast(:M, 0, 1)
|
7
|
+
term :A, ["a"]
|
8
|
+
term :Digit, [/[0-9]+/]
|
9
|
+
end
|
10
|
+
|
11
|
+
def test_01_use_maybe_parser
|
12
|
+
parser = MaybeTestGrammar.new_parser
|
13
|
+
|
14
|
+
ast = parser.parse "a1"
|
15
|
+
assert_equal("M", ast.name)
|
16
|
+
assert_kind_of(parser.astclass_of_name(:M), ast)
|
17
|
+
assert_equal(2, ast.num_children)
|
18
|
+
assert_equal("a", ast[0])
|
19
|
+
assert_equal("1", ast[1])
|
20
|
+
|
21
|
+
ast = parser.parse "a"
|
22
|
+
assert_equal("M", ast.name)
|
23
|
+
assert_kind_of(parser.astclass_of_name(:M), ast)
|
24
|
+
assert_equal(2, ast.num_children)
|
25
|
+
assert_equal("a", ast[0])
|
26
|
+
assert_equal(nil, ast[1])
|
27
|
+
end
|
28
|
+
|
29
|
+
OperatorTestGrammar = Rockit::DParser::Grammar.new do
|
30
|
+
start :Str, [[:A, plus(:B), plus(:A), mult(:C), maybe(:D)]],
|
31
|
+
ast(:Str, 0, 1, 3, 4)
|
32
|
+
term :A, ["a"]
|
33
|
+
term :B, ["b"]
|
34
|
+
term :C, ["c"]
|
35
|
+
term :D, ["d"]
|
36
|
+
end
|
37
|
+
|
38
|
+
def test_02_use_parser
|
39
|
+
parser = OperatorTestGrammar.new_parser
|
40
|
+
ast = parser.parse "abbbac"
|
41
|
+
assert_equal("Str", ast.name)
|
42
|
+
assert_kind_of(parser.astclass_of_name(:Str), ast)
|
43
|
+
assert_equal(4, ast.num_children)
|
44
|
+
assert_equal("a", ast[0])
|
45
|
+
assert_equal(["b", "b", "b"], ast[1])
|
46
|
+
assert_equal(["c"], ast[2])
|
47
|
+
assert_equal(nil, ast[3])
|
48
|
+
end
|
49
|
+
|
50
|
+
def test_03_use_parser
|
51
|
+
parser = OperatorTestGrammar.new_parser
|
52
|
+
ast = parser.parse "a b a c c d"
|
53
|
+
assert_equal("Str", ast.name)
|
54
|
+
assert_kind_of(parser.astclass_of_name(:Str), ast)
|
55
|
+
assert_equal(4, ast.num_children)
|
56
|
+
assert_equal("a", ast[0])
|
57
|
+
assert_equal(["b"], ast[1])
|
58
|
+
assert_equal(["c", "c"], ast[2])
|
59
|
+
assert_equal("d", ast[3])
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
require 'rockit/dparser'
|
3
|
+
|
4
|
+
class ATestPlusOperator < Test::Unit::TestCase
|
5
|
+
PlusTestGrammar = Rockit::DParser::Grammar.new do
|
6
|
+
start :S, [plus(:A)], value(0)
|
7
|
+
term :A, ["a"]
|
8
|
+
end
|
9
|
+
|
10
|
+
Parser = PlusTestGrammar.new_parser
|
11
|
+
|
12
|
+
|
13
|
+
def test_01_multiple_occurences
|
14
|
+
(2..20).each do |i|
|
15
|
+
ary = Parser.parse("a" * i)
|
16
|
+
assert_equal((1..i).map {"a"}, ary)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def test_02_single_occurence
|
21
|
+
ary = Parser.parse("a")
|
22
|
+
assert_equal(["a"], ary)
|
23
|
+
end
|
24
|
+
|
25
|
+
def test_03_no_occurence
|
26
|
+
assert_raises(RuntimeError) {Parser.parse("b")}
|
27
|
+
end
|
28
|
+
|
29
|
+
PlusAndAstTestGrammar = Rockit::DParser::Grammar.new do
|
30
|
+
start :S, [plus(:Digit)], ast(:S, 0)
|
31
|
+
term :Digit, [/[0-9]/]
|
32
|
+
end
|
33
|
+
|
34
|
+
AstParser = PlusAndAstTestGrammar.new_parser
|
35
|
+
|
36
|
+
def test_04_multiple_occurences_and_ast
|
37
|
+
ast = AstParser.parse("7 5 6 9")
|
38
|
+
assert_equal("S", ast.name)
|
39
|
+
assert_kind_of(AstParser.astclass_of_name(:S), ast)
|
40
|
+
assert_equal(1, ast.num_children)
|
41
|
+
assert_equal(["7", "5", "6", "9"], ast[0])
|
42
|
+
end
|
43
|
+
|
44
|
+
def test_05_single_occurence_and_ast
|
45
|
+
ast = AstParser.parse("7")
|
46
|
+
assert_equal("S", ast.name)
|
47
|
+
assert_kind_of(AstParser.astclass_of_name(:S), ast)
|
48
|
+
assert_equal(1, ast.num_children)
|
49
|
+
assert_equal(["7"], ast[0])
|
50
|
+
end
|
51
|
+
|
52
|
+
def test_06_no_occurence_and_ast
|
53
|
+
assert_raises(RuntimeError) {AstParser.parse("b")}
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
require 'rockit/dparser'
|
3
|
+
require 'calculator/calculator'
|
4
|
+
|
5
|
+
require File.join(File.dirname(__FILE__), "calc_tests_common")
|
6
|
+
|
7
|
+
class ATestSamplesCalculator < Test::Unit::TestCase
|
8
|
+
include CalcTestsCommon
|
9
|
+
|
10
|
+
def setup
|
11
|
+
@grammar = Samples::CalculatorGrammar
|
12
|
+
@calculator = Samples::Calculator.new
|
13
|
+
end
|
14
|
+
end
|