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.
Files changed (45) hide show
  1. data/Benchmarks.txt +48 -0
  2. data/History.txt +9 -0
  3. data/Manifest.txt +14 -21
  4. data/README.txt +1 -8
  5. data/Rakefile +26 -13
  6. data/einstein.gemspec +24 -0
  7. data/lib/einstein.rb +10 -41
  8. data/lib/einstein/evaluator.rb +102 -0
  9. data/lib/einstein/expression.rb +37 -0
  10. data/lib/einstein/parser.racc +41 -0
  11. data/lib/einstein/parser.racc.rb +333 -0
  12. data/lib/einstein/parser.rex +42 -0
  13. data/lib/einstein/parser.rex.rb +101 -0
  14. data/lib/einstein/pretty_printer.rb +98 -0
  15. data/lib/einstein/processor.rb +12 -0
  16. data/lib/einstein/version.rb +3 -3
  17. data/tasks/benchmark.rake +28 -0
  18. data/test/helper.rb +2 -1
  19. data/test/test_einstein.rb +20 -0
  20. data/test/test_evaluator.rb +86 -0
  21. data/test/test_parser.rb +35 -30
  22. data/test/test_pretty_printer.rb +81 -0
  23. metadata +31 -31
  24. data/config/hoe.rb +0 -62
  25. data/config/requirements.rb +0 -17
  26. data/lib/einstein/generated_parser.rb +0 -337
  27. data/lib/einstein/nodes.rb +0 -155
  28. data/lib/einstein/parser.rb +0 -60
  29. data/lib/einstein/tokenizer.rb +0 -112
  30. data/lib/einstein/visitors.rb +0 -303
  31. data/lib/parser.y +0 -79
  32. data/script/destroy +0 -14
  33. data/script/generate +0 -14
  34. data/script/txt2html +0 -74
  35. data/tasks/deployment.rake +0 -34
  36. data/tasks/environment.rake +0 -7
  37. data/tasks/website.rake +0 -17
  38. data/test/test_evaluate.rb +0 -127
  39. data/test/test_pretty_print.rb +0 -119
  40. data/test/test_tokenizer.rb +0 -68
  41. data/website/index.html +0 -120
  42. data/website/index.txt +0 -58
  43. data/website/javascripts/rounded_corners_lite.inc.js +0 -285
  44. data/website/stylesheets/screen.css +0 -138
  45. data/website/template.rhtml +0 -48
@@ -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
@@ -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
@@ -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
@@ -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