omghax-einstein 0.1.1 → 0.2.0
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.
- data/Benchmarks.txt +48 -0
- data/History.txt +9 -0
- data/Manifest.txt +14 -21
- data/README.txt +1 -8
- data/Rakefile +26 -13
- data/einstein.gemspec +24 -0
- data/lib/einstein.rb +10 -41
- data/lib/einstein/evaluator.rb +102 -0
- data/lib/einstein/expression.rb +37 -0
- data/lib/einstein/parser.racc +41 -0
- data/lib/einstein/parser.racc.rb +333 -0
- data/lib/einstein/parser.rex +42 -0
- data/lib/einstein/parser.rex.rb +101 -0
- data/lib/einstein/pretty_printer.rb +98 -0
- data/lib/einstein/processor.rb +12 -0
- data/lib/einstein/version.rb +3 -3
- data/tasks/benchmark.rake +28 -0
- data/test/helper.rb +2 -1
- data/test/test_einstein.rb +20 -0
- data/test/test_evaluator.rb +86 -0
- data/test/test_parser.rb +35 -30
- data/test/test_pretty_printer.rb +81 -0
- metadata +31 -31
- data/config/hoe.rb +0 -62
- data/config/requirements.rb +0 -17
- data/lib/einstein/generated_parser.rb +0 -337
- data/lib/einstein/nodes.rb +0 -155
- data/lib/einstein/parser.rb +0 -60
- data/lib/einstein/tokenizer.rb +0 -112
- data/lib/einstein/visitors.rb +0 -303
- data/lib/parser.y +0 -79
- data/script/destroy +0 -14
- data/script/generate +0 -14
- data/script/txt2html +0 -74
- data/tasks/deployment.rake +0 -34
- data/tasks/environment.rake +0 -7
- data/tasks/website.rake +0 -17
- data/test/test_evaluate.rb +0 -127
- data/test/test_pretty_print.rb +0 -119
- data/test/test_tokenizer.rb +0 -68
- data/website/index.html +0 -120
- data/website/index.txt +0 -58
- data/website/javascripts/rounded_corners_lite.inc.js +0 -285
- data/website/stylesheets/screen.css +0 -138
- data/website/template.rhtml +0 -48
data/Benchmarks.txt
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
== Version 0.1.1
|
|
2
|
+
|
|
3
|
+
First version to include benchmarking. Each type of node in the AST had its
|
|
4
|
+
own Einstein::Node subclass.
|
|
5
|
+
|
|
6
|
+
Benchmarking 10000 run(s) of Einstein.parse
|
|
7
|
+
user system total real
|
|
8
|
+
1 1.020000 0.020000 1.040000 ( 1.053689)
|
|
9
|
+
2 + 5 3.150000 0.010000 3.160000 ( 3.182302)
|
|
10
|
+
x * y 2.690000 0.010000 2.700000 ( 2.712514)
|
|
11
|
+
(x ** 2) / 8 - (y << z) 10.770000 0.040000 10.810000 ( 10.866615)
|
|
12
|
+
Benchmarking 10000 run(s) of Einstein.evaluate
|
|
13
|
+
user system total real
|
|
14
|
+
1 1.710000 0.000000 1.710000 ( 1.719042)
|
|
15
|
+
2 + 5 4.110000 0.020000 4.130000 ( 4.148186)
|
|
16
|
+
x * y 3.920000 0.010000 3.930000 ( 3.949004)
|
|
17
|
+
(x ** 2) / 8 - (y << z) 13.440000 0.040000 13.480000 ( 13.548173)
|
|
18
|
+
Benchmarking 10000 run(s) of evaluate on a pre-parsed expression
|
|
19
|
+
user system total real
|
|
20
|
+
1 0.530000 0.000000 0.530000 ( 0.538565)
|
|
21
|
+
2 + 5 1.000000 0.000000 1.000000 ( 1.003800)
|
|
22
|
+
x * y 1.080000 0.010000 1.090000 ( 1.090432)
|
|
23
|
+
(x ** 2) / 8 - (y << z) 2.490000 0.010000 2.500000 ( 2.518389)
|
|
24
|
+
|
|
25
|
+
== Version 0.2.0
|
|
26
|
+
|
|
27
|
+
All node classes were removed, and Einstein was re-implemented using only
|
|
28
|
+
s-expressions. The custom Einstein::Tokenizer class was removed as well, and
|
|
29
|
+
replaced with one generated by rex.
|
|
30
|
+
|
|
31
|
+
Benchmarking 10000 run(s) of Einstein.parse
|
|
32
|
+
user system total real
|
|
33
|
+
1 0.600000 0.030000 0.630000 ( 0.629743)
|
|
34
|
+
2 + 5 1.300000 0.000000 1.300000 ( 1.310801)
|
|
35
|
+
x * y 1.280000 0.010000 1.290000 ( 1.298743)
|
|
36
|
+
(x ** 2) / 8 - (y << z) 4.570000 0.020000 4.590000 ( 4.611955)
|
|
37
|
+
Benchmarking 10000 run(s) of Einstein.evaluate
|
|
38
|
+
user system total real
|
|
39
|
+
1 0.780000 0.000000 0.780000 ( 0.794553)
|
|
40
|
+
2 + 5 1.580000 0.010000 1.590000 ( 1.595382)
|
|
41
|
+
x * y 1.600000 0.000000 1.600000 ( 1.613572)
|
|
42
|
+
(x ** 2) / 8 - (y << z) 5.380000 0.020000 5.400000 ( 5.436614)
|
|
43
|
+
Benchmarking 10000 run(s) of evaluate on a pre-parsed expression
|
|
44
|
+
user system total real
|
|
45
|
+
1 0.080000 0.000000 0.080000 ( 0.077623)
|
|
46
|
+
2 + 5 0.230000 0.000000 0.230000 ( 0.232722)
|
|
47
|
+
x * y 0.320000 0.000000 0.320000 ( 0.326163)
|
|
48
|
+
(x ** 2) / 8 - (y << z) 0.670000 0.000000 0.670000 ( 0.682789)
|
data/History.txt
CHANGED
|
@@ -1,3 +1,12 @@
|
|
|
1
|
+
== 0.2.0 2008-05-10
|
|
2
|
+
|
|
3
|
+
* 2 major enhancements:
|
|
4
|
+
* Removed the custom tokenizer in favor of using a rex-based one instead.
|
|
5
|
+
* Huge changes to the internal structure of Einstein. Eliminated the node
|
|
6
|
+
classes in favor of modeling the syntax tree as an s-expression. This
|
|
7
|
+
makes the code much simpler, and I have a feeling it will be faster as
|
|
8
|
+
well, but I need to run some benchmarks to be sure.
|
|
9
|
+
|
|
1
10
|
== 0.1.1 2008-05-08
|
|
2
11
|
|
|
3
12
|
* 1 major bugfix:
|
data/Manifest.txt
CHANGED
|
@@ -1,31 +1,24 @@
|
|
|
1
|
+
Benchmarks.txt
|
|
1
2
|
History.txt
|
|
2
3
|
License.txt
|
|
3
4
|
Manifest.txt
|
|
4
5
|
README.txt
|
|
5
6
|
Rakefile
|
|
6
|
-
|
|
7
|
-
config/requirements.rb
|
|
7
|
+
einstein.gemspec
|
|
8
8
|
lib/einstein.rb
|
|
9
|
-
lib/einstein/
|
|
10
|
-
lib/einstein/
|
|
11
|
-
lib/einstein/parser.
|
|
12
|
-
lib/einstein/
|
|
9
|
+
lib/einstein/evaluator.rb
|
|
10
|
+
lib/einstein/expression.rb
|
|
11
|
+
lib/einstein/parser.racc
|
|
12
|
+
lib/einstein/parser.racc.rb
|
|
13
|
+
lib/einstein/parser.rex
|
|
14
|
+
lib/einstein/parser.rex.rb
|
|
15
|
+
lib/einstein/pretty_printer.rb
|
|
16
|
+
lib/einstein/processor.rb
|
|
13
17
|
lib/einstein/version.rb
|
|
14
|
-
lib/einstein/visitors.rb
|
|
15
|
-
lib/parser.y
|
|
16
|
-
script/destroy
|
|
17
|
-
script/generate
|
|
18
|
-
script/txt2html
|
|
19
18
|
setup.rb
|
|
20
|
-
tasks/
|
|
21
|
-
tasks/environment.rake
|
|
22
|
-
tasks/website.rake
|
|
23
|
-
test/test_evaluate.rb
|
|
19
|
+
tasks/benchmark.rake
|
|
24
20
|
test/helper.rb
|
|
21
|
+
test/test_einstein.rb
|
|
22
|
+
test/test_evaluator.rb
|
|
25
23
|
test/test_parser.rb
|
|
26
|
-
test/
|
|
27
|
-
website/index.html
|
|
28
|
-
website/index.txt
|
|
29
|
-
website/javascripts/rounded_corners_lite.inc.js
|
|
30
|
-
website/stylesheets/screen.css
|
|
31
|
-
website/template.rhtml
|
|
24
|
+
test/test_pretty_printer.rb
|
data/README.txt
CHANGED
|
@@ -40,19 +40,12 @@ You can then evaluate this tree for different values of x:
|
|
|
40
40
|
Or you can return an s-expression representation of the tree:
|
|
41
41
|
|
|
42
42
|
>> ast.to_sexp
|
|
43
|
-
=> [:
|
|
43
|
+
=> [:exponent, [:resolve, "x"], [:lit, 2]]
|
|
44
44
|
|
|
45
45
|
== Authors
|
|
46
46
|
|
|
47
47
|
Copyright (c) 2008 by Dray Lacy
|
|
48
48
|
|
|
49
|
-
== Acknowledgements
|
|
50
|
-
|
|
51
|
-
Large portions of the parser were taken from the RKelly project by Aaron
|
|
52
|
-
Patterson, which was itself derived from rbnarcissus by Paul Sowden.
|
|
53
|
-
|
|
54
|
-
Thanks Aaron and Paul!
|
|
55
|
-
|
|
56
49
|
== License
|
|
57
50
|
|
|
58
51
|
Einstein is licensed under the MIT License.
|
data/Rakefile
CHANGED
|
@@ -1,20 +1,33 @@
|
|
|
1
|
-
require '
|
|
2
|
-
require '
|
|
1
|
+
require 'rubygems'
|
|
2
|
+
require 'hoe'
|
|
3
|
+
require './lib/einstein.rb'
|
|
3
4
|
|
|
5
|
+
# Load all tasks from /tasks.
|
|
4
6
|
Dir['tasks/**/*.rake'].each { |rake| load rake }
|
|
5
7
|
|
|
6
|
-
|
|
8
|
+
Hoe.new('einstein', Einstein::VERSION::STRING) do |p|
|
|
9
|
+
p.developer 'Dray Lacy', 'dray@izea.com'
|
|
10
|
+
p.description = p.summary = 'Safe arithmetic parser for Ruby apps'
|
|
11
|
+
p.url = 'http://github.com/omghax/einstein'
|
|
12
|
+
p.test_globs = ['test/**/test_*.rb']
|
|
13
|
+
p.changes = p.paragraphs_of('History.txt', 0..1).join("\n\n")
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
GENERATED_PARSER = 'lib/einstein/parser.racc.rb'
|
|
17
|
+
GENERATED_LEXER = 'lib/einstein/parser.rex.rb'
|
|
7
18
|
|
|
8
|
-
file
|
|
9
|
-
|
|
10
|
-
sh "racc -g -v -o #{t.name} #{t.prerequisites.first}"
|
|
11
|
-
else
|
|
12
|
-
sh "racc -o #{t.name} #{t.prerequisites.first}"
|
|
13
|
-
end
|
|
19
|
+
file GENERATED_LEXER => 'lib/einstein/parser.rex' do |t|
|
|
20
|
+
sh "rex -o #{t.name} #{t.prerequisites.first}"
|
|
14
21
|
end
|
|
15
22
|
|
|
16
|
-
|
|
23
|
+
file GENERATED_PARSER => 'lib/einstein/parser.racc' do |t|
|
|
24
|
+
sh "racc -o #{t.name} #{t.prerequisites.first}"
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
task :parser => [GENERATED_LEXER, GENERATED_PARSER]
|
|
28
|
+
|
|
29
|
+
# Make sure the parser's up-to-date when we test.
|
|
30
|
+
Rake::Task["test"].prerequisites << :parser
|
|
17
31
|
|
|
18
|
-
#
|
|
19
|
-
Rake::Task[
|
|
20
|
-
Rake::Task[:check_manifest].prerequisites << :parser
|
|
32
|
+
# Make sure the generated parser gets included in the manifest.
|
|
33
|
+
Rake::Task["check_manifest"].prerequisites << :parser
|
data/einstein.gemspec
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
Gem::Specification.new do |s|
|
|
2
|
+
s.name = %q{einstein}
|
|
3
|
+
s.version = "0.2.0"
|
|
4
|
+
|
|
5
|
+
s.specification_version = 2 if s.respond_to? :specification_version=
|
|
6
|
+
|
|
7
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
|
8
|
+
s.authors = ["Dray Lacy"]
|
|
9
|
+
s.date = %q{2008-05-15}
|
|
10
|
+
s.description = %q{Safe arithmetic parser for Ruby apps}
|
|
11
|
+
s.email = ["dray@izea.com"]
|
|
12
|
+
s.extra_rdoc_files = ["Benchmarks.txt", "History.txt", "License.txt", "Manifest.txt", "README.txt"]
|
|
13
|
+
s.files = ["Benchmarks.txt", "History.txt", "License.txt", "Manifest.txt", "README.txt", "Rakefile", "einstein.gemspec", "lib/einstein.rb", "lib/einstein/evaluator.rb", "lib/einstein/expression.rb", "lib/einstein/parser.racc", "lib/einstein/parser.racc.rb", "lib/einstein/parser.rex", "lib/einstein/parser.rex.rb", "lib/einstein/pretty_printer.rb", "lib/einstein/processor.rb", "lib/einstein/version.rb", "setup.rb", "tasks/benchmark.rake", "test/helper.rb", "test/test_einstein.rb", "test/test_evaluator.rb", "test/test_parser.rb", "test/test_pretty_printer.rb"]
|
|
14
|
+
s.has_rdoc = true
|
|
15
|
+
s.homepage = %q{http://github.com/omghax/einstein}
|
|
16
|
+
s.rdoc_options = ["--main", "README.txt"]
|
|
17
|
+
s.require_paths = ["lib"]
|
|
18
|
+
s.rubyforge_project = %q{einstein}
|
|
19
|
+
s.rubygems_version = %q{1.1.1}
|
|
20
|
+
s.summary = %q{Safe arithmetic parser for Ruby apps}
|
|
21
|
+
s.test_files = ["test/test_einstein.rb", "test/test_evaluator.rb", "test/test_parser.rb", "test/test_pretty_printer.rb"]
|
|
22
|
+
|
|
23
|
+
s.add_dependency(%q<hoe>, [">= 1.5.1"])
|
|
24
|
+
end
|
data/lib/einstein.rb
CHANGED
|
@@ -1,50 +1,19 @@
|
|
|
1
|
+
# Add ./lib to the load path.
|
|
1
2
|
$:.unshift File.dirname(__FILE__)
|
|
3
|
+
require 'einstein/parser.racc.rb'
|
|
4
|
+
require 'einstein/parser.rex.rb'
|
|
5
|
+
require 'einstein/expression'
|
|
6
|
+
require 'einstein/version'
|
|
2
7
|
|
|
3
|
-
require "einstein/parser"
|
|
4
|
-
|
|
5
|
-
# == Basic Usage
|
|
6
|
-
#
|
|
7
|
-
# If you're only interested in evaluating expressions, you can simply call
|
|
8
|
-
# Einstein.evaluate:
|
|
9
|
-
#
|
|
10
|
-
# Einstein.evaluate("1 + 2 + 3") # => 6
|
|
11
|
-
#
|
|
12
|
-
# You can use variables as well, but you must supply their values:
|
|
13
|
-
#
|
|
14
|
-
# Einstein.evaluate("x * 4", :x => 5) # => 20
|
|
15
|
-
# Einstein.evaluate("x + y + z", :x => 3, :y => 4, :z => 5) # => 12
|
|
16
|
-
#
|
|
17
|
-
# == Prepared Statements
|
|
18
|
-
#
|
|
19
|
-
# If you're going to be performing the same operation over a large set of
|
|
20
|
-
# different values, and performance is a concern, you can have Einstein parse
|
|
21
|
-
# the expression once beforehand, and return a statement object (technically,
|
|
22
|
-
# an instance of Einstein::Nodes::StatementNode). You can then pass your
|
|
23
|
-
# values directly to this statement for much faster processing, rather than
|
|
24
|
-
# making Einstein parse the same formula over and over again each time.
|
|
25
|
-
#
|
|
26
|
-
# # This will return a prepared statement.
|
|
27
|
-
# stmt = Einstein.parse("x + y")
|
|
28
|
-
#
|
|
29
|
-
# # You can then evaluate this statement against different inputs.
|
|
30
|
-
# stmt.evaluate(:x => 1, :y => 2) # => 3
|
|
31
|
-
# stmt.evaluate(:x => 25, :y => 30) # => 55
|
|
32
8
|
module Einstein
|
|
33
|
-
#
|
|
34
|
-
class
|
|
35
|
-
end
|
|
36
|
-
|
|
37
|
-
# Raised when a variable's name cannot be resolved.
|
|
38
|
-
class ResolveError < Error
|
|
39
|
-
end
|
|
40
|
-
|
|
41
|
-
# Raised when division by zero occurs.
|
|
42
|
-
class ZeroDivisionError < Error
|
|
9
|
+
# Raised when an undefined variable is referenced.
|
|
10
|
+
class ResolveError < StandardError # :nodoc:
|
|
43
11
|
end
|
|
44
12
|
|
|
45
|
-
# Parse the given +expression+ and return the AST as
|
|
13
|
+
# Parse the given +expression+ and return the AST as an instance of
|
|
14
|
+
# Einstein::Expression.
|
|
46
15
|
def self.parse(expression)
|
|
47
|
-
Parser.new.
|
|
16
|
+
Expression.new(Parser.new.scan_str(expression))
|
|
48
17
|
end
|
|
49
18
|
|
|
50
19
|
# Evaluate the given +expression+ with the given +scope+. Any variables
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
require 'einstein/processor'
|
|
2
|
+
|
|
3
|
+
module Einstein
|
|
4
|
+
# This processor walks the AST and evaluates the values of the nodes.
|
|
5
|
+
class Evaluator < Processor
|
|
6
|
+
# Initialize a new instance of this processor with the given +scope+, as a
|
|
7
|
+
# hash. This +scope+ should provide a mapping of variable names to
|
|
8
|
+
# values.
|
|
9
|
+
def initialize(scope = {})
|
|
10
|
+
# Convert symbol keys into strings.
|
|
11
|
+
@scope = scope.inject({}) do |hash, (key, value)|
|
|
12
|
+
hash[key.to_s] = value
|
|
13
|
+
hash
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
# Returns the value of +exp+.
|
|
18
|
+
def process_lit(exp)
|
|
19
|
+
exp[1]
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# Returns the value of +exp+.
|
|
23
|
+
def process_unary_plus(exp)
|
|
24
|
+
process(exp[1])
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# Returns the negated value of +exp+.
|
|
28
|
+
def process_unary_minus(exp)
|
|
29
|
+
-process(exp[1])
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# Performs a bitwise NOT operation on the value of +exp+.
|
|
33
|
+
def process_bitwise_not(exp)
|
|
34
|
+
~process(exp[1])
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# Raises the left value of +exp+ by the right value of +exp+.
|
|
38
|
+
def process_exponent(exp)
|
|
39
|
+
process(exp[1]) ** process(exp[2])
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# Multiplies the left and right values of +exp+.
|
|
43
|
+
def process_multiply(exp)
|
|
44
|
+
process(exp[1]) * process(exp[2])
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# Divides the left value of +exp+ by the right value of +exp+. Will raise
|
|
48
|
+
# ZeroDivisionError if the right value of +exp+ evaluates to 0.
|
|
49
|
+
def process_divide(exp)
|
|
50
|
+
process(exp[1]) / process(exp[2])
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# Performs a modulus operation for the left and right values of +exp+.
|
|
54
|
+
def process_modulus(exp)
|
|
55
|
+
process(exp[1]) % process(exp[2])
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
# Adds the left and right values of +exp+.
|
|
59
|
+
def process_add(exp)
|
|
60
|
+
process(exp[1]) + process(exp[2])
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
# Subtracts the right value of +exp+ by the left value of +exp+.
|
|
64
|
+
def process_subtract(exp)
|
|
65
|
+
process(exp[1]) - process(exp[2])
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# Performs a bitwise left shift of the left value of +exp+ by the number
|
|
69
|
+
# of bits specified in the right value of +exp+.
|
|
70
|
+
def process_lshift(exp)
|
|
71
|
+
process(exp[1]) << process(exp[2])
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
# Performs a bitwise right shift of the left value of +exp+ by the number
|
|
75
|
+
# of bits specified in the right value of +exp+.
|
|
76
|
+
def process_rshift(exp)
|
|
77
|
+
process(exp[1]) >> process(exp[2])
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
# Performs a bitwise AND with the left and right values of +exp+.
|
|
81
|
+
def process_bitwise_and(exp)
|
|
82
|
+
process(exp[1]) & process(exp[2])
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
# Performs a bitwise XOR with the left and right values of +exp+.
|
|
86
|
+
def process_bitwise_xor(exp)
|
|
87
|
+
process(exp[1]) ^ process(exp[2])
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
# Performs a bitwise OR with the left and right values of +exp+.
|
|
91
|
+
def process_bitwise_or(exp)
|
|
92
|
+
process(exp[1]) | process(exp[2])
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
# Performs a lookup for the value of +exp+ inside this processor's scope.
|
|
96
|
+
# Raises ResolveError if the variable is not in scope.
|
|
97
|
+
def process_resolve(exp)
|
|
98
|
+
raise ResolveError, "Undefined variable: #{exp[1]}" unless @scope.has_key?(exp[1])
|
|
99
|
+
@scope[exp[1]]
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
end
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
require 'einstein/evaluator'
|
|
2
|
+
require 'einstein/pretty_printer'
|
|
3
|
+
|
|
4
|
+
module Einstein
|
|
5
|
+
# Node representing an entire Einstein statement. This is the root-level
|
|
6
|
+
# node of the parser. This node's +exp+ is the tree of expressions that
|
|
7
|
+
# make up a single logical statement.
|
|
8
|
+
class Expression
|
|
9
|
+
# Initialize a new expression wrapping the given +sexp+, which is expected
|
|
10
|
+
# to be an s-expression represented as a set of nested arrays.
|
|
11
|
+
#
|
|
12
|
+
# Example:
|
|
13
|
+
# Expression.new([:lit, 10]) # => 10
|
|
14
|
+
# Expression.new([:multiply, [:lit, 10], [:lit, 5]]) # => (10 * 5)
|
|
15
|
+
def initialize(sexp)
|
|
16
|
+
@sexp = sexp
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# The s-expression given to this Expression's constructor.
|
|
20
|
+
attr_reader :sexp
|
|
21
|
+
alias_method :to_sexp, :sexp
|
|
22
|
+
|
|
23
|
+
# Evaluate this node against the given +scope+. Returns a numeric value
|
|
24
|
+
# calculated by walking the AST with an instance of EvaluateProcessor.
|
|
25
|
+
def evaluate(scope = {})
|
|
26
|
+
Evaluator.new(scope).process(to_sexp)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# Performs a "pretty print" of this expression.
|
|
30
|
+
def to_s
|
|
31
|
+
PrettyPrinter.new.process(to_sexp)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# Also use #to_s for inspecting a node in IRB.
|
|
35
|
+
alias_method :inspect, :to_s
|
|
36
|
+
end
|
|
37
|
+
end
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
class Einstein::Parser
|
|
2
|
+
|
|
3
|
+
rule
|
|
4
|
+
exp : bitwise_or
|
|
5
|
+
|
|
6
|
+
literal : IDENT { result = [:resolve, val.first] }
|
|
7
|
+
| NUMBER { result = [:lit, val.first] }
|
|
8
|
+
|
|
9
|
+
primary : '(' exp ')' { result = val[1] }
|
|
10
|
+
| literal
|
|
11
|
+
|
|
12
|
+
unary : primary
|
|
13
|
+
| '+' unary { result = [:unary_plus, val[1]] }
|
|
14
|
+
| '-' unary { result = [:unary_minus, val[1]] }
|
|
15
|
+
| '~' unary { result = [:bitwise_not, val[1]] }
|
|
16
|
+
|
|
17
|
+
exponent : unary
|
|
18
|
+
| exponent MULT2 unary { result = [:exponent, val[0], val[2]] }
|
|
19
|
+
|
|
20
|
+
multiplicative : exponent
|
|
21
|
+
| multiplicative '*' exponent { result = [:multiply, val[0], val[2]] }
|
|
22
|
+
| multiplicative '/' exponent { result = [:divide, val[0], val[2]] }
|
|
23
|
+
| multiplicative '%' exponent { result = [:modulus, val[0], val[2]] }
|
|
24
|
+
|
|
25
|
+
additive : multiplicative
|
|
26
|
+
| additive '+' multiplicative { result = [:add, val[0], val[2]] }
|
|
27
|
+
| additive '-' multiplicative { result = [:subtract, val[0], val[2]] }
|
|
28
|
+
|
|
29
|
+
shift : additive
|
|
30
|
+
| shift LSHIFT additive { result = [:lshift, val[0], val[2]] }
|
|
31
|
+
| shift RSHIFT additive { result = [:rshift, val[0], val[2]] }
|
|
32
|
+
|
|
33
|
+
bitwise_and : shift
|
|
34
|
+
| bitwise_and '&' shift { result = [:bitwise_and, val[0], val[2]] }
|
|
35
|
+
|
|
36
|
+
bitwise_or : bitwise_and
|
|
37
|
+
| bitwise_or '|' bitwise_and { result = [:bitwise_or, val[0], val[2]] }
|
|
38
|
+
| bitwise_or '^' bitwise_and { result = [:bitwise_xor, val[0], val[2]] }
|
|
39
|
+
|
|
40
|
+
---- header ----
|
|
41
|
+
require 'einstein/parser.rex.rb'
|