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/lib/einstein/nodes.rb
DELETED
|
@@ -1,155 +0,0 @@
|
|
|
1
|
-
require "einstein/visitors"
|
|
2
|
-
|
|
3
|
-
module Einstein
|
|
4
|
-
module Nodes
|
|
5
|
-
# Base class for all Einstein nodes.
|
|
6
|
-
class Node
|
|
7
|
-
include Einstein::Visitors
|
|
8
|
-
|
|
9
|
-
# Initializes a new instance of this node with the given +value+.
|
|
10
|
-
def initialize(value)
|
|
11
|
-
@value = value
|
|
12
|
-
end
|
|
13
|
-
|
|
14
|
-
# The value of this node.
|
|
15
|
-
attr_accessor :value
|
|
16
|
-
|
|
17
|
-
# Implements the visitor pattern by calling a method named
|
|
18
|
-
# visit_SomeNode when visiting a class named SomeNode. This way we
|
|
19
|
-
# separate the tree traversal logic from the nodes themselves, which
|
|
20
|
-
# makes it easier to modify the visitors, or to add new ones.
|
|
21
|
-
def accept(visitor, &block)
|
|
22
|
-
klass = self.class.ancestors.find do |ancestor|
|
|
23
|
-
visitor.respond_to?("visit_#{ancestor.name.split(/::/)[-1]}")
|
|
24
|
-
end
|
|
25
|
-
|
|
26
|
-
if klass
|
|
27
|
-
visitor.send("visit_#{klass.name.split(/::/)[-1]}", self, &block)
|
|
28
|
-
else
|
|
29
|
-
raise "No visitor for '#{self.class}'"
|
|
30
|
-
end
|
|
31
|
-
end
|
|
32
|
-
|
|
33
|
-
# Evaluate this node against the given +scope+. Returns a numeric value
|
|
34
|
-
# calculated by walking the AST with an instance of EvaluateVisitor.
|
|
35
|
-
def evaluate(scope = {})
|
|
36
|
-
EvaluateVisitor.new(scope).accept(self)
|
|
37
|
-
end
|
|
38
|
-
|
|
39
|
-
# Performs a "pretty print" of this node.
|
|
40
|
-
def to_s
|
|
41
|
-
PrettyPrintVisitor.new.accept(self)
|
|
42
|
-
end
|
|
43
|
-
|
|
44
|
-
# Also use #to_s for inspecting a node in IRB.
|
|
45
|
-
alias_method :inspect, :to_s
|
|
46
|
-
|
|
47
|
-
# Returns this node as an s-expression. Built by walking the AST with
|
|
48
|
-
# an instance of SexpVisitor.
|
|
49
|
-
def to_sexp
|
|
50
|
-
SexpVisitor.new.accept(self)
|
|
51
|
-
end
|
|
52
|
-
end
|
|
53
|
-
|
|
54
|
-
# Node representing an entire Einstein statement. This is the root-level
|
|
55
|
-
# node of the parser. This node's +value+ is the tree of expressions that
|
|
56
|
-
# make up a single logical statement.
|
|
57
|
-
class StatementNode < Node
|
|
58
|
-
end
|
|
59
|
-
|
|
60
|
-
# Node representing a number. This is a terminal node.
|
|
61
|
-
class NumberNode < Node
|
|
62
|
-
end
|
|
63
|
-
|
|
64
|
-
# Node representing a unary + (plus) operation. Not to be confused with
|
|
65
|
-
# the addition operator, this node is generated when you specify a number
|
|
66
|
-
# with an explicit positive sign.
|
|
67
|
-
#
|
|
68
|
-
# Example:
|
|
69
|
-
# # This would generate a UnaryPlusNode with the value 1.25.
|
|
70
|
-
# +1.25
|
|
71
|
-
class UnaryPlusNode < Node
|
|
72
|
-
end
|
|
73
|
-
|
|
74
|
-
# Node representing a unary - (minus) operation. Not to be confused with
|
|
75
|
-
# the subtraction operator, this node is generated when you specify a
|
|
76
|
-
# negative number.
|
|
77
|
-
#
|
|
78
|
-
# Example:
|
|
79
|
-
# # This would generate a UnaryMinusNode with the value 3.3.
|
|
80
|
-
# -3.3
|
|
81
|
-
class UnaryMinusNode < Node
|
|
82
|
-
end
|
|
83
|
-
|
|
84
|
-
# Node representing a bitwise NOT operation.
|
|
85
|
-
class BitwiseNotNode < Node
|
|
86
|
-
end
|
|
87
|
-
|
|
88
|
-
# Base class for all binary nodes. Binary nodes operate on two values.
|
|
89
|
-
class BinaryNode < Node
|
|
90
|
-
# Initializes a new instance of this node with the given +left+ and
|
|
91
|
-
# +right+ values.
|
|
92
|
-
def initialize(left, right)
|
|
93
|
-
super(right)
|
|
94
|
-
@left = left
|
|
95
|
-
end
|
|
96
|
-
|
|
97
|
-
# The secondary value given to #initialize.
|
|
98
|
-
attr_reader :left
|
|
99
|
-
end
|
|
100
|
-
|
|
101
|
-
# Node representing an exponential raise. This node's +left+ value is the
|
|
102
|
-
# base, and the +right+ value is the exponent.
|
|
103
|
-
class ExponentNode < BinaryNode
|
|
104
|
-
end
|
|
105
|
-
|
|
106
|
-
# Node representing multiplication of two values.
|
|
107
|
-
class MultiplyNode < BinaryNode
|
|
108
|
-
end
|
|
109
|
-
|
|
110
|
-
# Node representing division of two values.
|
|
111
|
-
class DivideNode < BinaryNode
|
|
112
|
-
end
|
|
113
|
-
|
|
114
|
-
# Node representing modulus of two values.
|
|
115
|
-
class ModulusNode < BinaryNode
|
|
116
|
-
end
|
|
117
|
-
|
|
118
|
-
# Node representing addition of two nodes.
|
|
119
|
-
class AddNode < BinaryNode
|
|
120
|
-
end
|
|
121
|
-
|
|
122
|
-
# Node representing subtraction of two nodes.
|
|
123
|
-
class SubtractNode < BinaryNode
|
|
124
|
-
end
|
|
125
|
-
|
|
126
|
-
# Node representing an LSHIFT (left shift) operation. This node's +left+
|
|
127
|
-
# value is the number to be shifted, and the +right+ value is the number
|
|
128
|
-
# of bits to shift the value by.
|
|
129
|
-
class LeftShiftNode < BinaryNode
|
|
130
|
-
end
|
|
131
|
-
|
|
132
|
-
# Node representing an RSHIFT (right shift) operation. This node's +left+
|
|
133
|
-
# value is the number to be shifted, and the +right+ value is the number
|
|
134
|
-
# of bits to shift the value by.
|
|
135
|
-
class RightShiftNode < BinaryNode
|
|
136
|
-
end
|
|
137
|
-
|
|
138
|
-
# Node representing a bitwise AND operation.
|
|
139
|
-
class BitwiseAndNode < BinaryNode
|
|
140
|
-
end
|
|
141
|
-
|
|
142
|
-
# Node representing a bitwise XOR operation.
|
|
143
|
-
class BitwiseXorNode < BinaryNode
|
|
144
|
-
end
|
|
145
|
-
|
|
146
|
-
# Node representing a bitwise OR operation.
|
|
147
|
-
class BitwiseOrNode < BinaryNode
|
|
148
|
-
end
|
|
149
|
-
|
|
150
|
-
# Node representing a variable lookup. This node's +value+ is the
|
|
151
|
-
# variable's name, as a string.
|
|
152
|
-
class ResolveNode < Node
|
|
153
|
-
end
|
|
154
|
-
end
|
|
155
|
-
end
|
data/lib/einstein/parser.rb
DELETED
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
require "einstein/generated_parser"
|
|
2
|
-
require "einstein/tokenizer"
|
|
3
|
-
|
|
4
|
-
module Einstein
|
|
5
|
-
class Parser < GeneratedParser
|
|
6
|
-
# Tokenizer instance that is shared between instances of Parser.
|
|
7
|
-
TOKENIZER = Tokenizer.new
|
|
8
|
-
|
|
9
|
-
def initialize
|
|
10
|
-
@tokens = []
|
|
11
|
-
@logger = nil
|
|
12
|
-
@terminator = false
|
|
13
|
-
end
|
|
14
|
-
|
|
15
|
-
# Logger object to receive debugging information when a parsing error
|
|
16
|
-
# occurs. If this is nil, no information will be output.
|
|
17
|
-
attr_accessor :logger
|
|
18
|
-
|
|
19
|
-
# Parses the given +expression+ using TOKENIZER, and returns an instance
|
|
20
|
-
# of StatementNode, which represents an AST. You can take this AST and
|
|
21
|
-
# perform evaluations on it using #evaluate, or transform it into an
|
|
22
|
-
# s-expression using #to_sexp.
|
|
23
|
-
#
|
|
24
|
-
# Example:
|
|
25
|
-
# # Parse the expression "x + 3".
|
|
26
|
-
# ast = Einstein::Parser.new.parse("x + 3")
|
|
27
|
-
#
|
|
28
|
-
# # Evaluate the expression with a given scope.
|
|
29
|
-
# ast.evaluate(:x => 5) # => 8
|
|
30
|
-
#
|
|
31
|
-
# # Return the expression as an s-expression.
|
|
32
|
-
# ast.to_sexp # => [:add, [:resolve, "x"], [:lit, 3]]
|
|
33
|
-
def parse(expression)
|
|
34
|
-
@tokens = TOKENIZER.tokenize(expression)
|
|
35
|
-
@position = 0
|
|
36
|
-
StatementNode.new(do_parse)
|
|
37
|
-
end
|
|
38
|
-
|
|
39
|
-
private
|
|
40
|
-
|
|
41
|
-
def on_error(error_token_id, error_value, value_stack)
|
|
42
|
-
if logger
|
|
43
|
-
logger.error(token_to_str(error_token_id))
|
|
44
|
-
logger.error("error value: #{error_value}")
|
|
45
|
-
logger.error("error stack: #{value_stack.inspect}")
|
|
46
|
-
end
|
|
47
|
-
super
|
|
48
|
-
end
|
|
49
|
-
|
|
50
|
-
# Used by Racc::Parser to step through tokens.
|
|
51
|
-
def next_token
|
|
52
|
-
begin
|
|
53
|
-
return [false, false] if @position >= @tokens.length
|
|
54
|
-
n_token = @tokens[@position]
|
|
55
|
-
@position += 1
|
|
56
|
-
end while [:COMMENT, :WS].include?(n_token[0])
|
|
57
|
-
n_token
|
|
58
|
-
end
|
|
59
|
-
end
|
|
60
|
-
end
|
data/lib/einstein/tokenizer.rb
DELETED
|
@@ -1,112 +0,0 @@
|
|
|
1
|
-
module Einstein
|
|
2
|
-
class Token
|
|
3
|
-
# The default transformer used by instances of Token when no block is
|
|
4
|
-
# given to #initialize.
|
|
5
|
-
DEFAULT_TRANSFORMER = lambda { |name, value| [name, value] }
|
|
6
|
-
|
|
7
|
-
def initialize(name, value, &transformer)
|
|
8
|
-
@name = name
|
|
9
|
-
@value = value
|
|
10
|
-
@transformer = transformer || DEFAULT_TRANSFORMER
|
|
11
|
-
end
|
|
12
|
-
|
|
13
|
-
# This token's name (eg. :NUMBER, :IDENT).
|
|
14
|
-
attr_accessor :name
|
|
15
|
-
|
|
16
|
-
# This token's value (eg. 1, "x").
|
|
17
|
-
attr_accessor :value
|
|
18
|
-
|
|
19
|
-
# The block given to this token's #initialize method. This block is
|
|
20
|
-
# called by #to_racc_token.
|
|
21
|
-
attr_accessor :transformer
|
|
22
|
-
|
|
23
|
-
# Converts this token to a format that Racc expects. This is an array of
|
|
24
|
-
# the format [name, value].
|
|
25
|
-
#
|
|
26
|
-
# Examples:
|
|
27
|
-
# [:NUMBER, 2.20]
|
|
28
|
-
# [:IDENT, "x"]
|
|
29
|
-
def to_racc_token
|
|
30
|
-
@transformer.call(name, value)
|
|
31
|
-
end
|
|
32
|
-
end
|
|
33
|
-
|
|
34
|
-
class Lexeme
|
|
35
|
-
def initialize(name, pattern, &block)
|
|
36
|
-
@name = name
|
|
37
|
-
@pattern = pattern
|
|
38
|
-
@block = block
|
|
39
|
-
end
|
|
40
|
-
|
|
41
|
-
def match(string)
|
|
42
|
-
match = @pattern.match(string)
|
|
43
|
-
return Token.new(@name, match.to_s, &@block) if match
|
|
44
|
-
match
|
|
45
|
-
end
|
|
46
|
-
end
|
|
47
|
-
|
|
48
|
-
class Tokenizer
|
|
49
|
-
LITERALS = {
|
|
50
|
-
# Punctuators
|
|
51
|
-
"**" => :RAISE,
|
|
52
|
-
"<<" => :LSHIFT,
|
|
53
|
-
">>" => :RSHIFT
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
def initialize(&block)
|
|
57
|
-
@lexemes = []
|
|
58
|
-
|
|
59
|
-
token(:COMMENT, /\A\/(?:\*(?:.)*?\*\/|\/[^\n]*)/m)
|
|
60
|
-
|
|
61
|
-
# A regexp to match floating point literals (but not integer literals).
|
|
62
|
-
token(:NUMBER, /\A\d+\.\d*(?:[eE][-+]?\d+)?|\A\d+(?:\.\d*)?[eE][-+]?\d+|\A\.\d+(?:[eE][-+]?\d+)?/m) do |type, value|
|
|
63
|
-
value.gsub!(/\.(\D)/, '.0\1') if value =~ /\.\w/
|
|
64
|
-
value.gsub!(/\.$/, '.0') if value =~ /\.$/
|
|
65
|
-
value.gsub!(/^\./, '0.') if value =~ /^\./
|
|
66
|
-
[type, eval(value)]
|
|
67
|
-
end
|
|
68
|
-
token(:NUMBER, /\A0[bBxX][\da-fA-F]+|\A0[0-7]*|\A\d+/) do |type, value|
|
|
69
|
-
[type, eval(value)]
|
|
70
|
-
end
|
|
71
|
-
|
|
72
|
-
token(:LITERALS,
|
|
73
|
-
Regexp.new(LITERALS.keys.sort_by { |x| x.length }.reverse.map { |x| "\\A#{x.gsub(/([|+*^])/, '\\\\\1')}" }.join('|')
|
|
74
|
-
)) do |type, value|
|
|
75
|
-
[LITERALS[value], value]
|
|
76
|
-
end
|
|
77
|
-
|
|
78
|
-
token(:IDENT, /\A(\w|\$)+/)
|
|
79
|
-
|
|
80
|
-
token(:WS, /\A[\s\r\n]*/m)
|
|
81
|
-
|
|
82
|
-
token(:SINGLE_CHAR, /\A./) do |type, value|
|
|
83
|
-
[value, value]
|
|
84
|
-
end
|
|
85
|
-
end
|
|
86
|
-
|
|
87
|
-
def tokenize(string)
|
|
88
|
-
tokens = []
|
|
89
|
-
while string.length > 0
|
|
90
|
-
longest_token = nil
|
|
91
|
-
|
|
92
|
-
@lexemes.each { |lexeme|
|
|
93
|
-
match = lexeme.match(string)
|
|
94
|
-
next if match.nil?
|
|
95
|
-
longest_token = match if longest_token.nil?
|
|
96
|
-
next if longest_token.value.length >= match.value.length
|
|
97
|
-
longest_token = match
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
string = string.slice(Range.new(longest_token.value.length, -1))
|
|
101
|
-
tokens << longest_token
|
|
102
|
-
end
|
|
103
|
-
tokens.map { |x| x.to_racc_token }
|
|
104
|
-
end
|
|
105
|
-
|
|
106
|
-
private
|
|
107
|
-
|
|
108
|
-
def token(name, pattern = nil, &block)
|
|
109
|
-
@lexemes << Lexeme.new(name, pattern, &block)
|
|
110
|
-
end
|
|
111
|
-
end
|
|
112
|
-
end
|
data/lib/einstein/visitors.rb
DELETED
|
@@ -1,303 +0,0 @@
|
|
|
1
|
-
module Einstein
|
|
2
|
-
module Visitors
|
|
3
|
-
class Visitor
|
|
4
|
-
TERMINAL_NODES = %w{
|
|
5
|
-
Number Resolve
|
|
6
|
-
}
|
|
7
|
-
SINGLE_VALUE_NODES = %w{
|
|
8
|
-
BitwiseNot Statement UnaryMinus UnaryPlus
|
|
9
|
-
}
|
|
10
|
-
BINARY_NODES = %w{
|
|
11
|
-
Add BitwiseAnd BitwiseOr BitwiseXor Divide Exponent LeftShift Modulus
|
|
12
|
-
Multiply RightShift Subtract
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
def accept(target)
|
|
16
|
-
target.accept(self)
|
|
17
|
-
end
|
|
18
|
-
|
|
19
|
-
TERMINAL_NODES.each do |type|
|
|
20
|
-
define_method("visit_#{type}Node") { |o| }
|
|
21
|
-
end
|
|
22
|
-
|
|
23
|
-
SINGLE_VALUE_NODES.each do |type|
|
|
24
|
-
define_method("visit_#{type}Node") do |o|
|
|
25
|
-
o.value.accept(self)
|
|
26
|
-
end
|
|
27
|
-
end
|
|
28
|
-
|
|
29
|
-
BINARY_NODES.each do |type|
|
|
30
|
-
define_method("visit_#{type}Node") do |o|
|
|
31
|
-
[o.left && o.left.accept(self), o.value && o.value.accept(self)]
|
|
32
|
-
end
|
|
33
|
-
end
|
|
34
|
-
end
|
|
35
|
-
|
|
36
|
-
# This visitor walks the AST and evaluates the values of the nodes.
|
|
37
|
-
class EvaluateVisitor < Visitor
|
|
38
|
-
# Initialize a new instance of this visitor with the given +scope+, as a
|
|
39
|
-
# hash. This +scope+ should provide a mapping of variable names to
|
|
40
|
-
# values.
|
|
41
|
-
def initialize(scope)
|
|
42
|
-
super()
|
|
43
|
-
|
|
44
|
-
# Convert the scope hash keys from symbols to strings.
|
|
45
|
-
@scope = scope.inject({}) do |hash, (key, value)|
|
|
46
|
-
hash[key.to_s] = value
|
|
47
|
-
hash
|
|
48
|
-
end
|
|
49
|
-
end
|
|
50
|
-
|
|
51
|
-
# Returns the value of +o+.
|
|
52
|
-
def visit_NumberNode(o)
|
|
53
|
-
o.value
|
|
54
|
-
end
|
|
55
|
-
|
|
56
|
-
# Returns the value of +o+.
|
|
57
|
-
def visit_UnaryPlusNode(o)
|
|
58
|
-
o.value.accept(self)
|
|
59
|
-
end
|
|
60
|
-
|
|
61
|
-
# Returns the negated value of +o+.
|
|
62
|
-
def visit_UnaryMinusNode(o)
|
|
63
|
-
-o.value.accept(self)
|
|
64
|
-
end
|
|
65
|
-
|
|
66
|
-
# Raises the left value of +o+ by the right value of +o+.
|
|
67
|
-
def visit_ExponentNode(o)
|
|
68
|
-
o.left.accept(self) ** o.value.accept(self)
|
|
69
|
-
end
|
|
70
|
-
|
|
71
|
-
# Multiplies the left and right values of +o+.
|
|
72
|
-
def visit_MultiplyNode(o)
|
|
73
|
-
o.left.accept(self) * o.value.accept(self)
|
|
74
|
-
end
|
|
75
|
-
|
|
76
|
-
# Divides the left value of +o+ by the right value of +o+. Raises
|
|
77
|
-
# ZeroDivisionError if the right value of +o+ is zero.
|
|
78
|
-
def visit_DivideNode(o)
|
|
79
|
-
dividend = o.value.accept(self)
|
|
80
|
-
raise ZeroDivisionError, "divided by zero" if dividend == 0
|
|
81
|
-
o.left.accept(self) / dividend
|
|
82
|
-
end
|
|
83
|
-
|
|
84
|
-
# Performs a modulus operation for the left and right values of +o+.
|
|
85
|
-
def visit_ModulusNode(o)
|
|
86
|
-
o.left.accept(self) % o.value.accept(self)
|
|
87
|
-
end
|
|
88
|
-
|
|
89
|
-
# Adds the left and right values of +o+.
|
|
90
|
-
def visit_AddNode(o)
|
|
91
|
-
o.left.accept(self) + o.value.accept(self)
|
|
92
|
-
end
|
|
93
|
-
|
|
94
|
-
# Subtracts the right value of +o+ by the left value of +o+.
|
|
95
|
-
def visit_SubtractNode(o)
|
|
96
|
-
o.left.accept(self) - o.value.accept(self)
|
|
97
|
-
end
|
|
98
|
-
|
|
99
|
-
# Performs a bitwise left shift of the left value of +o+ by the number
|
|
100
|
-
# of bits specified in the right value of +o+.
|
|
101
|
-
def visit_LeftShiftNode(o)
|
|
102
|
-
o.left.accept(self) << o.value.accept(self)
|
|
103
|
-
end
|
|
104
|
-
|
|
105
|
-
# Performs a bitwise right shift of the left value of +o+ by the number
|
|
106
|
-
# of bits specified in the right value of +o+.
|
|
107
|
-
def visit_RightShiftNode(o)
|
|
108
|
-
o.left.accept(self) >> o.value.accept(self)
|
|
109
|
-
end
|
|
110
|
-
|
|
111
|
-
# Performs a bitwise AND with the left and right values of +o+.
|
|
112
|
-
def visit_BitwiseAndNode(o)
|
|
113
|
-
o.left.accept(self) & o.value.accept(self)
|
|
114
|
-
end
|
|
115
|
-
|
|
116
|
-
# Performs a bitwise XOR with the left and right values of +o+.
|
|
117
|
-
def visit_BitwiseXorNode(o)
|
|
118
|
-
o.left.accept(self) ^ o.value.accept(self)
|
|
119
|
-
end
|
|
120
|
-
|
|
121
|
-
# Performs a bitwise OR with the left and right values of +o+.
|
|
122
|
-
def visit_BitwiseOrNode(o)
|
|
123
|
-
o.left.accept(self) | o.value.accept(self)
|
|
124
|
-
end
|
|
125
|
-
|
|
126
|
-
# Performs a lookup for the value of +o+ inside this visitor's scope.
|
|
127
|
-
# Raises ResolveError if the variable is not in scope.
|
|
128
|
-
def visit_ResolveNode(o)
|
|
129
|
-
raise ResolveError, "undefined variable: #{o.value}" unless @scope.has_key?(o.value)
|
|
130
|
-
@scope[o.value]
|
|
131
|
-
end
|
|
132
|
-
end
|
|
133
|
-
|
|
134
|
-
# This visitor walks the AST and builds a "pretty print" of the values.
|
|
135
|
-
# This means that it returns an unambiguous string representation of the
|
|
136
|
-
# tree. All binary expressions are wrapped in parentheses. This visitor
|
|
137
|
-
# is used when calling #inspect on a node.
|
|
138
|
-
class PrettyPrintVisitor < Visitor
|
|
139
|
-
# Example: 4
|
|
140
|
-
def visit_NumberNode(o)
|
|
141
|
-
o.value.inspect
|
|
142
|
-
end
|
|
143
|
-
|
|
144
|
-
# Example: +4
|
|
145
|
-
def visit_UnaryPlusNode(o)
|
|
146
|
-
"+#{o.value.accept(self)}"
|
|
147
|
-
end
|
|
148
|
-
|
|
149
|
-
# Example: -4
|
|
150
|
-
def visit_UnaryMinusNode(o)
|
|
151
|
-
"-#{o.value.accept(self)}"
|
|
152
|
-
end
|
|
153
|
-
|
|
154
|
-
# Example: ~4
|
|
155
|
-
def visit_BitwiseNotNode(o)
|
|
156
|
-
"~#{o.value.accept(self)}"
|
|
157
|
-
end
|
|
158
|
-
|
|
159
|
-
# Example: (2 ** 3)
|
|
160
|
-
def visit_ExponentNode(o)
|
|
161
|
-
"(#{o.left.accept(self)} ** #{o.value.accept(self)})"
|
|
162
|
-
end
|
|
163
|
-
|
|
164
|
-
# Example: (2 * 3)
|
|
165
|
-
def visit_MultiplyNode(o)
|
|
166
|
-
"(#{o.left.accept(self)} * #{o.value.accept(self)})"
|
|
167
|
-
end
|
|
168
|
-
|
|
169
|
-
# Example: (6 / 3)
|
|
170
|
-
def visit_DivideNode(o)
|
|
171
|
-
"(#{o.left.accept(self)} / #{o.value.accept(self)})"
|
|
172
|
-
end
|
|
173
|
-
|
|
174
|
-
# Example: (7 % 3)
|
|
175
|
-
def visit_ModulusNode(o)
|
|
176
|
-
"(#{o.left.accept(self)} % #{o.value.accept(self)})"
|
|
177
|
-
end
|
|
178
|
-
|
|
179
|
-
# Example: (5 + 8)
|
|
180
|
-
def visit_AddNode(o)
|
|
181
|
-
"(#{o.left.accept(self)} + #{o.value.accept(self)})"
|
|
182
|
-
end
|
|
183
|
-
|
|
184
|
-
# Example: (6 - 3)
|
|
185
|
-
def visit_SubtractNode(o)
|
|
186
|
-
"(#{o.left.accept(self)} - #{o.value.accept(self)})"
|
|
187
|
-
end
|
|
188
|
-
|
|
189
|
-
# Example: (8 << 2)
|
|
190
|
-
def visit_LeftShiftNode(o)
|
|
191
|
-
"(#{o.left.accept(self)} << #{o.value.accept(self)})"
|
|
192
|
-
end
|
|
193
|
-
|
|
194
|
-
# Example: (8 >> 2)
|
|
195
|
-
def visit_RightShiftNode(o)
|
|
196
|
-
"(#{o.left.accept(self)} >> #{o.value.accept(self)})"
|
|
197
|
-
end
|
|
198
|
-
|
|
199
|
-
# Example: (4 & 16)
|
|
200
|
-
def visit_BitwiseAndNode(o)
|
|
201
|
-
"(#{o.left.accept(self)} & #{o.value.accept(self)})"
|
|
202
|
-
end
|
|
203
|
-
|
|
204
|
-
# Example: (4 ^ 6)
|
|
205
|
-
def visit_BitwiseXorNode(o)
|
|
206
|
-
"(#{o.left.accept(self)} ^ #{o.value.accept(self)})"
|
|
207
|
-
end
|
|
208
|
-
|
|
209
|
-
# Example: (4 | 6)
|
|
210
|
-
def visit_BitwiseOrNode(o)
|
|
211
|
-
"(#{o.left.accept(self)} | #{o.value.accept(self)})"
|
|
212
|
-
end
|
|
213
|
-
|
|
214
|
-
# Example: x
|
|
215
|
-
def visit_ResolveNode(o)
|
|
216
|
-
o.value
|
|
217
|
-
end
|
|
218
|
-
end
|
|
219
|
-
|
|
220
|
-
# This visitor walks the AST and returns an s-expression.
|
|
221
|
-
class SexpVisitor < Visitor
|
|
222
|
-
# Example: [:lit, 3]
|
|
223
|
-
def visit_NumberNode(o)
|
|
224
|
-
[:lit, o.value]
|
|
225
|
-
end
|
|
226
|
-
|
|
227
|
-
# Example: [:u_plus, [:lit, 3]]
|
|
228
|
-
def visit_UnaryPlusNode(o)
|
|
229
|
-
[:u_plus, super]
|
|
230
|
-
end
|
|
231
|
-
|
|
232
|
-
# Example: [:u_minus, [:lit, 3]]
|
|
233
|
-
def visit_UnaryMinusNode(o)
|
|
234
|
-
[:u_minus, super]
|
|
235
|
-
end
|
|
236
|
-
|
|
237
|
-
# Example: [:bitwise_not, [:lit, 3]]
|
|
238
|
-
def visit_BitwiseNotNode(o)
|
|
239
|
-
[:bitwise_not, super]
|
|
240
|
-
end
|
|
241
|
-
|
|
242
|
-
# Example: [:raise, [:lit, 2], [:lit, 3]]
|
|
243
|
-
def visit_ExponentNode(o)
|
|
244
|
-
[:raise, *super]
|
|
245
|
-
end
|
|
246
|
-
|
|
247
|
-
# Example: [:multiply, [:lit, 2], [:lit, 3]]
|
|
248
|
-
def visit_MultiplyNode(o)
|
|
249
|
-
[:multiply, *super]
|
|
250
|
-
end
|
|
251
|
-
|
|
252
|
-
# Example: [:divide, [:lit, 4], [:lit, 2]]
|
|
253
|
-
def visit_DivideNode(o)
|
|
254
|
-
[:divide, *super]
|
|
255
|
-
end
|
|
256
|
-
|
|
257
|
-
# Example: [:modulus, [:lit, 3], [:lit, 5]]
|
|
258
|
-
def visit_ModulusNode(o)
|
|
259
|
-
[:modulus, *super]
|
|
260
|
-
end
|
|
261
|
-
|
|
262
|
-
# Example: [:add, [:lit, 2], [:lit, 2]]
|
|
263
|
-
def visit_AddNode(o)
|
|
264
|
-
[:add, *super]
|
|
265
|
-
end
|
|
266
|
-
|
|
267
|
-
# Example: [:subtract, [:lit, 5], [:lit, 2]]
|
|
268
|
-
def visit_SubtractNode(o)
|
|
269
|
-
[:subtract, *super]
|
|
270
|
-
end
|
|
271
|
-
|
|
272
|
-
# Example: [:lshift, [:lit, 2], [:lit, 3]]
|
|
273
|
-
def visit_LeftShiftNode(o)
|
|
274
|
-
[:lshift, *super]
|
|
275
|
-
end
|
|
276
|
-
|
|
277
|
-
# Example: [:rshift, [:lit, 8], [:lit, 2]]
|
|
278
|
-
def visit_RightShiftNode(o)
|
|
279
|
-
[:rshift, *super]
|
|
280
|
-
end
|
|
281
|
-
|
|
282
|
-
# Example: [:bitwise_and, [:lit, 4], [:lit, 2]]
|
|
283
|
-
def visit_BitwiseAndNode(o)
|
|
284
|
-
[:bitwise_and, *super]
|
|
285
|
-
end
|
|
286
|
-
|
|
287
|
-
# Example: [:bitwise_xor, [:lit, 4], [:lit, 2]]
|
|
288
|
-
def visit_BitwiseXorNode(o)
|
|
289
|
-
[:bitwise_xor, *super]
|
|
290
|
-
end
|
|
291
|
-
|
|
292
|
-
# Example: [:bitwise_or, [:lit, 4], [:lit, 2]]
|
|
293
|
-
def visit_BitwiseOrNode(o)
|
|
294
|
-
[:bitwise_or, *super]
|
|
295
|
-
end
|
|
296
|
-
|
|
297
|
-
# Example: [:resolve, "x"]
|
|
298
|
-
def visit_ResolveNode(o)
|
|
299
|
-
[:resolve, o.value]
|
|
300
|
-
end
|
|
301
|
-
end
|
|
302
|
-
end
|
|
303
|
-
end
|