eleetscript 0.0.2a

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 (37) hide show
  1. checksums.yaml +7 -0
  2. data/bin/eleet +89 -0
  3. data/lib/eleetscript.rb +18 -0
  4. data/lib/engine/eleet_engine.rb +58 -0
  5. data/lib/engine/eleet_to_ruby_wrapper.rb +25 -0
  6. data/lib/engine/ruby_to_eleet_wrapper.rb +22 -0
  7. data/lib/engine/values.rb +55 -0
  8. data/lib/lang/grammar.y +350 -0
  9. data/lib/lang/interpreter.rb +400 -0
  10. data/lib/lang/lexer.rb +92 -0
  11. data/lib/lang/nodes.rb +197 -0
  12. data/lib/lang/parser.output +11953 -0
  13. data/lib/lang/parser.rb +2300 -0
  14. data/lib/lang/runtime/array.rb +23 -0
  15. data/lib/lang/runtime/base_classes.rb +31 -0
  16. data/lib/lang/runtime/bootstrap.rb +3 -0
  17. data/lib/lang/runtime/class.rb +113 -0
  18. data/lib/lang/runtime/class_instance.rb +53 -0
  19. data/lib/lang/runtime/class_skeleton.rb +57 -0
  20. data/lib/lang/runtime/context.rb +263 -0
  21. data/lib/lang/runtime/eleetscript/enumerable.es +36 -0
  22. data/lib/lang/runtime/eleetscript/falseclass.es +25 -0
  23. data/lib/lang/runtime/eleetscript/integer.es +11 -0
  24. data/lib/lang/runtime/eleetscript/list.es +44 -0
  25. data/lib/lang/runtime/eleetscript/nilclass.es +5 -0
  26. data/lib/lang/runtime/eleetscript/number.es +13 -0
  27. data/lib/lang/runtime/eleetscript/object.es +49 -0
  28. data/lib/lang/runtime/eleetscript/pair.es +12 -0
  29. data/lib/lang/runtime/eleetscript/que.es +13 -0
  30. data/lib/lang/runtime/eleetscript/stack.es +14 -0
  31. data/lib/lang/runtime/eleetscript/string.es +34 -0
  32. data/lib/lang/runtime/eleetscript/trueclass.es +25 -0
  33. data/lib/lang/runtime/memory.rb +553 -0
  34. data/lib/lang/runtime/method.rb +32 -0
  35. data/lib/lang/runtime/method_hash.rb +40 -0
  36. data/lib/util/processed_key_hash.rb +34 -0
  37. metadata +79 -0
@@ -0,0 +1,400 @@
1
+ require "lang/parser"
2
+ require "lang/runtime/memory"
3
+
4
+ module EleetScript
5
+ class Interpreter
6
+ attr_reader :memory
7
+ def initialize(memory = nil)
8
+ @parser = Parser.new
9
+ @memory = memory || Memory.new
10
+ @memory.bootstrap(self)
11
+ end
12
+
13
+ def eval(code, show_nodes = false)
14
+ nodes = @parser.parse(code)
15
+ puts nodes if show_nodes
16
+ nodes.eval(@memory.root_namespace)
17
+ end
18
+
19
+ def load(file_name)
20
+ if File.exists?(file_name)
21
+ eval(File.read(file_name))
22
+ end
23
+ end
24
+ end
25
+
26
+ module Helpers
27
+ def self.throw_eleet_error(context, error)
28
+ context["Errors"].call("<", [context["String"].new_with_value(error)])
29
+ end
30
+ end
31
+
32
+ module Returnable
33
+ def returned
34
+ @returned = true
35
+ end
36
+
37
+ def returned?
38
+ @returned
39
+ end
40
+
41
+ def reset_returned
42
+ @returned = false
43
+ end
44
+ end
45
+
46
+ module Nextable
47
+ def nexted
48
+ @nexted = true
49
+ end
50
+
51
+ def nexted?
52
+ @nexted
53
+ end
54
+
55
+ def reset_nexted
56
+ @nexted = false
57
+ end
58
+ end
59
+
60
+ module NodeMethods
61
+ def returnable?
62
+ self.class.included_modules.include?(Returnable)
63
+ end
64
+
65
+ def nextable?
66
+ self.class.included_modules.include?(Nextable)
67
+ end
68
+ end
69
+
70
+ class Nodes
71
+ include Returnable
72
+ include Nextable
73
+
74
+ def eval(context)
75
+ return_value = nil
76
+ nodes.each do |node|
77
+ if node.kind_of?(ReturnNode)
78
+ returned
79
+ break return_value = node.eval(context)
80
+ elsif node.kind_of?(NextNode)
81
+ nexted
82
+ break
83
+ else
84
+ return_value = node.eval(context)
85
+ end
86
+ if node.returnable? && node.returned?
87
+ returned
88
+ node.reset_returned
89
+ break
90
+ elsif node.nextable? && node.nexted?
91
+ node.reset_nexted
92
+ nexted
93
+ break
94
+ end
95
+ end
96
+ return_value || context.es_nil
97
+ end
98
+ end
99
+
100
+ class StringNode
101
+ INTERPOLATE_RX = /[\\]?%(?:@|@@|\$)?[\w]+?(?=\W|$)/
102
+
103
+ def eval(context)
104
+ context["String"].new_with_value(interpolate(context))
105
+ end
106
+
107
+ def interpolate(context)
108
+ new_val = value.dup
109
+ matches = value.scan(INTERPOLATE_RX)
110
+ matches.each do |match|
111
+ next if match.nil? || match == "%" || match == ""
112
+ if match.start_with?("\\")
113
+ next new_val.sub!(match, match[1..-1])
114
+ end
115
+ var = match[1..-1]
116
+ new_val.sub!(match, context[var].call(:to_string).ruby_value)
117
+ end
118
+ new_val
119
+ end
120
+ end
121
+
122
+ class IntegerNode
123
+ def eval(context)
124
+ context["Integer"].new_with_value(value)
125
+ end
126
+ end
127
+
128
+ class FloatNode
129
+ def eval(context)
130
+ context["Float"].new_with_value(value)
131
+ end
132
+ end
133
+
134
+ class SetGlobalNode
135
+ def eval(context)
136
+ context[name] = value.eval(context)
137
+ end
138
+ end
139
+
140
+ class GetGlobalNode
141
+ def eval(context)
142
+ context[name]
143
+ end
144
+ end
145
+
146
+ class GetLocalNode
147
+ def eval(context)
148
+ val = context.local_var(name)
149
+ val != context.es_nil ? val : context.current_self.call(name, [])
150
+ end
151
+ end
152
+
153
+ class SetLocalNode
154
+ def eval(context)
155
+ if Lexer::RESERVED_WORDS.include?(name)
156
+ context["Errors"].call("<", [context["String"].new_with_value("Cannot assign a value to reserved word \"name\"")])
157
+ else
158
+ context.local_var(name, value.eval(context))
159
+ end
160
+ end
161
+ end
162
+
163
+ class GetConstantNode
164
+ def eval(context)
165
+ context.constants[name] || context[name]
166
+ end
167
+ end
168
+
169
+ class SetConstantNode
170
+ def eval(context)
171
+ if !context.constants.has_key?(name)
172
+ context[name] = value.eval(context)
173
+ else
174
+ Helpers.throw_eleet_error(context, "Cannot reassign constant \"#{name}\" after it's already been defined!")
175
+ end
176
+ end
177
+ end
178
+
179
+ class SetInstanceVarNode
180
+ def eval(context)
181
+ context.instance_vars[name] = value.eval(context)
182
+ end
183
+ end
184
+
185
+ class GetInstanceVarNode
186
+ def eval(context)
187
+ context.current_self.instance_vars[name]
188
+ end
189
+ end
190
+
191
+ class SetClassVarNode
192
+ def eval(context)
193
+ context.current_class.class_vars[name] = value.eval(context)
194
+ end
195
+ end
196
+
197
+ class GetClassVarNode
198
+ def eval(context)
199
+ context.current_class.class_vars[name]
200
+ end
201
+ end
202
+
203
+ class TrueNode
204
+ def eval(context)
205
+ context["true"]
206
+ end
207
+ end
208
+
209
+ class FalseNode
210
+ def eval(context)
211
+ context["false"]
212
+ end
213
+ end
214
+
215
+ class NilNode
216
+ def eval(context)
217
+ context.es_nil
218
+ end
219
+ end
220
+
221
+ class ClassNode
222
+ def eval(context)
223
+ cls = context[name]
224
+ if cls == context.es_nil
225
+ cls = if parent
226
+ parent_cls = context[parent]
227
+ throw "Cannot extend undefined class \"#{parent}\"." if parent_cls == context.es_nil
228
+ EleetScriptClass.create(context, name, parent_cls)
229
+ else
230
+ EleetScriptClass.create(context, name)
231
+ end
232
+ context[name] = cls
233
+ end
234
+
235
+ body.eval(cls.context)
236
+ cls
237
+ end
238
+ end
239
+
240
+ class PropertyNode
241
+ def eval(context)
242
+ cls = context.current_class
243
+ properties.each do |prop_name|
244
+ cls.def "#{prop_name}=" do |receiver, arguments|
245
+ receiver.instance_vars[prop_name] = arguments.first
246
+ end
247
+
248
+ cls.def prop_name do |receiver, arguments|
249
+ receiver.instance_vars[prop_name]
250
+ end
251
+ end
252
+ end
253
+ end
254
+
255
+ class CallNode
256
+ def eval(context)
257
+ value = if receiver
258
+ receiver.eval(context)
259
+ else
260
+ context.current_self
261
+ end
262
+ evaled_args = arguments.map { |a| a.eval(context) }
263
+ evaled_args << lambda.eval(context) if lambda
264
+ value.call(method_name, evaled_args)
265
+ end
266
+ end
267
+
268
+ class LambdaNode
269
+ def eval(context)
270
+ context["Lambda"].new_with_value(EleetScriptMethod.new(params, body, context))
271
+ end
272
+ end
273
+
274
+ class DefMethodNode
275
+ def eval(context)
276
+ method_obj = EleetScriptMethod.new(method.params, method.body)
277
+ context.current_class.methods[method_name] = method_obj
278
+ context.es_nil
279
+ end
280
+ end
281
+
282
+ class SelfNode
283
+ def eval(context)
284
+ context.current_self
285
+ end
286
+ end
287
+
288
+ class IfNode
289
+ include Returnable
290
+ include Nextable
291
+
292
+ def eval(context)
293
+ cond = condition.eval(context)
294
+ cond = (cond.class? ? cond : cond.ruby_value)
295
+ if cond
296
+ ret = body.eval(context)
297
+ if body.returnable? && body.returned?
298
+ body.reset_returned
299
+ returned
300
+ elsif body.nextable? && body.nexted?
301
+ body.reset_nexted
302
+ nexted
303
+ return context.es_nil
304
+ end
305
+ ret
306
+ else
307
+ unless else_node.nil?
308
+ ret = else_node.eval(context)
309
+ if else_node.returned?
310
+ else_node.reset_returned
311
+ returned
312
+ elsif else_node.nexted?
313
+ else_node.reset_nexted
314
+ nexted
315
+ return context.es_nil
316
+ end
317
+ ret
318
+ end
319
+ end
320
+ end
321
+ end
322
+
323
+ class ElseNode
324
+ include Returnable
325
+ include Nextable
326
+
327
+ def eval(context)
328
+ ret = body.eval(context)
329
+ if body.returnable? and body.returned?
330
+ body.reset_returned
331
+ returned
332
+ elsif body.nextable? && body.nexted?
333
+ body.reset_nexted
334
+ nexted
335
+ return context.es_nil
336
+ end
337
+ ret
338
+ end
339
+ end
340
+
341
+ class ReturnNode
342
+ def eval(context)
343
+ if expression
344
+ expression.eval(context)
345
+ else
346
+ context.es_nil
347
+ end
348
+ end
349
+ end
350
+
351
+ class WhileNode
352
+ include Returnable
353
+
354
+ def eval(context)
355
+ val = condition.eval(context)
356
+ ret = nil
357
+ while val.ruby_value
358
+ ret = body.eval(context)
359
+ if body.returnable? && body.returned?
360
+ body.reset_returned
361
+ returned
362
+ return ret
363
+ elsif body.nextable? && body.nexted?
364
+ body.reset_nexted
365
+ next
366
+ end
367
+ val = condition.eval(context)
368
+ end
369
+ ret || context.es_nil
370
+ end
371
+ end
372
+
373
+ class NamespaceNode
374
+ def eval(context)
375
+ ns_ctx = context.namespace(name)
376
+ if ns_ctx
377
+ body.eval(ns_ctx)
378
+ else
379
+ ns_ctx = context.new_namespace_context
380
+ context.add_namespace(name, ns_ctx)
381
+ body.eval(ns_ctx)
382
+ end
383
+ end
384
+ end
385
+
386
+ class NamespaceAccessNode
387
+ def eval(context)
388
+ ns_ctx = if namespace.nil?
389
+ context.root_ns
390
+ else
391
+ context.namespace(namespace)
392
+ end
393
+ if ns_ctx
394
+ expression.eval(ns_ctx)
395
+ else
396
+ Helpers.throw_eleet_error(context, "Namespace \"#{namespace}\" does not exist.")
397
+ end
398
+ end
399
+ end
400
+ end
data/lib/lang/lexer.rb ADDED
@@ -0,0 +1,92 @@
1
+ require "eleetscript"
2
+
3
+ module EleetScript
4
+ class Lexer
5
+ RESERVED_WORDS = [
6
+ "lambda?", "lambda", "self", "nil", "true", "yes", "on", "false", "no",
7
+ "off", "next", "break", "return"
8
+ ]
9
+
10
+ KEYWORDS = [
11
+ "do", "end", "class", "load", "if", "while", "namespace", "else", "elsif",
12
+ "return", "break", "next", "true", "yes", "on", "false", "no", "off",
13
+ "nil", "self", "defined?", "property"
14
+ ]
15
+
16
+ TOKEN_RX = {
17
+ identifiers: /\A([a-z_][\w\d]*[?!]?)/,
18
+ constants: /\A([A-Z][\w\d]*[?!]?)/,
19
+ globals: /\A(\$[a-z][\w\d]*[?!]?)/i,
20
+ class_var: /\A(\@\@[a-z][\w\d]*[!?]?)/i,
21
+ instance_var: /\A(\@[a-z][\w\d]*[!?]?)/i,
22
+ operator: /\A(->|=>|[.+\-\*\/%<>=!]=|\*\*=|\*\*|[+\-\*\/%=><]|or|and|not|isnt|is|\||\(|\)|\[|\]|\{|\}|::|[.,])/,
23
+ whitespace: /\A([ \t]+)/,
24
+ terminator: /\A([;\n])/,
25
+ integer: /\A([\d_]+)/,
26
+ float: /\A([\d_]*?\.[\d_]+)/,
27
+ string: /\A\"(.*?)(?<!\\)\"/m,
28
+ comment: /\A#.*?(?=\n|$)/m
29
+ }
30
+
31
+ def tokenize(code)
32
+ tokens = []
33
+ if code.length == 0
34
+ return tokens
35
+ end
36
+ line = 1
37
+ i = 0
38
+ while i < code.length
39
+ chunk = code[i..-1]
40
+ if operator = chunk[TOKEN_RX[:operator]]
41
+ tokens << [operator, operator]
42
+ i += operator.length
43
+ elsif constant = chunk[TOKEN_RX[:constants]]
44
+ tokens << [:CONSTANT, constant]
45
+ i += constant.length
46
+ elsif global = chunk[TOKEN_RX[:globals]]
47
+ tokens << [:GLOBAL, $1]
48
+ i += global.length
49
+ elsif class_var = chunk[TOKEN_RX[:class_var]]
50
+ tokens << [:CLASS_IDENTIFIER, $1]
51
+ i += class_var.length
52
+ elsif instance_var = chunk[TOKEN_RX[:instance_var]]
53
+ tokens << [:INSTANCE_IDENTIFIER, $1]
54
+ i += instance_var.length
55
+ elsif identifier = chunk[TOKEN_RX[:identifiers]]
56
+ if KEYWORDS.include? identifier
57
+ tokens << [identifier.upcase.gsub(/\?\!/, "").to_sym, identifier]
58
+ else
59
+ tokens << [:IDENTIFIER, identifier]
60
+ end
61
+ i += identifier.length
62
+ elsif float = chunk[TOKEN_RX[:float]]
63
+ tokens << [:FLOAT, float.to_f]
64
+ i += float.length
65
+ elsif integer = chunk[TOKEN_RX[:integer]]
66
+ tokens << [:NUMBER, integer.to_i]
67
+ i += integer.length
68
+ elsif string = chunk[TOKEN_RX[:string]]
69
+ tokens << [:STRING, $1.gsub('\"', '"')]
70
+ i += string.length
71
+ elsif comment = chunk[TOKEN_RX[:comment]]
72
+ i += comment.length # Ignore comments
73
+ elsif terminator = chunk[TOKEN_RX[:terminator]]
74
+ tokens << [:TERMINATOR, terminator]
75
+ if terminator == "\n"
76
+ line += 1
77
+ end
78
+ i += 1
79
+ elsif space = chunk[TOKEN_RX[:whitespace]]
80
+ i += space.length # ignore spaces and tab characters
81
+ else
82
+ raise LexicalError.new(code[i], line)
83
+ end
84
+ end
85
+ if tokens.length > 0 && tokens.last != [:TERMINATOR, "\n"]
86
+ tokens << [:TERMINATOR, "\n"]
87
+ end
88
+ tokens << [:EOF, :eof] if tokens.length > 0
89
+ tokens
90
+ end
91
+ end
92
+ end
data/lib/lang/nodes.rb ADDED
@@ -0,0 +1,197 @@
1
+ require "eleetscript"
2
+
3
+ module EleetScript
4
+ module NodeMethods
5
+ def spaces(level)
6
+ " " * level
7
+ end
8
+
9
+ def to_s(level = 0)
10
+ "#{spaces(level)}<#{self.class.name}>\n"
11
+ end
12
+
13
+ def is_node?
14
+ true
15
+ end
16
+ end
17
+
18
+ class Node < Struct
19
+ include NodeMethods
20
+ end
21
+
22
+ class NodeType
23
+ include NodeMethods
24
+ end
25
+
26
+ class Nodes < Node.new(:nodes)
27
+ def <<(node)
28
+ nodes << node
29
+ self
30
+ end
31
+
32
+ def to_s(level = 0)
33
+ str = "#{spaces(level)}<EleetScript::Nodes\n"
34
+ nodes.each do |node|
35
+ str += node.to_s(level + 1)
36
+ end
37
+ str + "#{spaces(level)}>\n"
38
+ end
39
+ end
40
+
41
+ class LiteralNode < Node.new(:value)
42
+ def to_s(level = 0)
43
+ "#{spaces(level)}<#{self.class.name} #{value}>\n"
44
+ end
45
+ end
46
+
47
+ class IntegerNode < LiteralNode; end
48
+ class FloatNode < LiteralNode; end
49
+ class StringNode < LiteralNode; end
50
+
51
+ class TrueNode < LiteralNode
52
+ def value
53
+ true
54
+ end
55
+ end
56
+
57
+ class FalseNode < LiteralNode
58
+ def value
59
+ false
60
+ end
61
+ end
62
+
63
+ class NilNode < LiteralNode
64
+ def value
65
+ nil
66
+ end
67
+ end
68
+
69
+ class CallNode < Node.new(:receiver, :method_name, :arguments, :lambda)
70
+ def to_s(level = 0)
71
+ tabs = spaces(level)
72
+ str = "#{tabs}<EleetScript::CallNode\n"
73
+ if receiver.nil?
74
+ str += "#{tabs} @receiver=nil\n"
75
+ else
76
+ str += "#{tabs} @receiver=#{receiver.to_s[0..-2]}\n"
77
+ end
78
+ str += "#{tabs} @method_name=#{method_name}\n"
79
+ if arguments.empty?
80
+ str += "#{tabs} @arguemtns=empty\n"
81
+ else
82
+ str += "#{tabs} @arguments=(\n"
83
+ arguments.each do |arg|
84
+ str += arg.to_s(level + 2)
85
+ end
86
+ str += "#{tabs} )\n"
87
+ end
88
+ if lambda.nil?
89
+ str + "#{tabs} @lambda=nil\n>\n"
90
+ else
91
+ str += "#{tabs} @lambda=(\n"
92
+ str += lambda.to_s(level + 2)
93
+ end
94
+ str + "#{tabs}>\n"
95
+ end
96
+ end
97
+
98
+ class SetVarNode < Node.new(:name, :value)
99
+ def to_s(level = 0)
100
+ tabs = spaces(level)
101
+ str = "#{tabs}<#{self.class.name}\n"
102
+ str += "#{tabs} @name=#{name}\n"
103
+ str += "#{tabs} @value=(\n"
104
+ str += value.to_s(level + 2)
105
+ str += "#{tabs}>\n"
106
+ end
107
+ end
108
+
109
+ class GetVarNode < Node.new(:name)
110
+ def to_s(level = 0)
111
+ str = "<#{self.class.name} \"#{name}\">\n"
112
+ end
113
+ end
114
+
115
+ class SetConstantNode < SetVarNode; end
116
+ class GetConstantNode < GetVarNode; end
117
+ class SetGlobalNode < SetVarNode; end
118
+ class GetGlobalNode < GetVarNode; end
119
+ class SetClassVarNode < SetVarNode; end
120
+ class GetClassVarNode < GetVarNode; end
121
+ class SetInstanceVarNode < SetVarNode; end
122
+ class GetInstanceVarNode < GetVarNode; end
123
+ class SetLocalNode < SetVarNode; end
124
+ class GetLocalNode < GetVarNode; end
125
+
126
+ class DefMethodNode < Node.new(:method_name, :method)
127
+ def to_s(level = 0)
128
+ tabs = spaces(level)
129
+ str = "#{tabs}<EleetScript::DefMethodNode \"#{method_name}\"\n"
130
+ str += "#{tabs} @method=(\n"
131
+ str += method.to_s(level + 2)
132
+ str + "#{tabs} )\n#{tabs}>\n"
133
+ end
134
+ end
135
+
136
+ class LambdaNode < Node.new(:params, :body)
137
+ def to_s(level = 0)
138
+ tabs = spaces(level)
139
+ str = "#{tabs}<EleetScript::LambdaNode\n"
140
+ str += "#{tabs} @params=#{params.inspect}\n"
141
+ if body.nodes.length > 0
142
+ str += "#{tabs} @body=(\n"
143
+ str += body.to_s(level + 2)
144
+ str += "#{tabs} )\n#{tabs}>\n"
145
+ else
146
+ str += "#{tabs} @body=nil\n#{tabs}>\n"
147
+ end
148
+ str
149
+ end
150
+ end
151
+
152
+ class MethodNode < Node.new(:params, :body)
153
+ def to_s(level = 0)
154
+ tabs = spaces(level)
155
+ str = "#{tabs}<EleetScript::MethodNode\n"
156
+ str += "#{tabs} @params=#{params.inspect}\n"
157
+ if body.nodes.length > 0
158
+ str += "#{tabs} @body=(\n"
159
+ str += body.to_s(level + 2)
160
+ str += "#{tabs} )\n#{tabs}>\n"
161
+ else
162
+ str += "#{tabs} @body=nil\n#{tabs}>\n"
163
+ end
164
+ str
165
+ end
166
+ end
167
+
168
+ class IfNode < Node.new(:condition, :body, :else_node); end
169
+ class ElseNode < Node.new(:body); end
170
+ class NotNode < Node.new(:value); end
171
+ class WhileNode < Node.new(:condition, :body); end
172
+ class SelfNode < NodeType; end
173
+ class DefinedNode < Node.new(:value); end
174
+ class NamespaceNode < Node.new(:name, :body); end
175
+ class NamespaceAccessNode < Node.new(:namespace, :expression); end
176
+
177
+ class ClassNode < Node.new(:name, :parent, :body)
178
+ def to_s(level = 0)
179
+ tabs = spaces(level)
180
+ str = "#{tabs}<EleetScript::ClassNode \"#{name}\"\n"
181
+ str += "#{tabs} @extends=\"#{parent || "Object"}\"\n"
182
+ str += "#{tabs} @body=(\n"
183
+ str += body.to_s(level + 2)
184
+ str + "#{tabs} )\n#{tabs}>\n"
185
+ end
186
+ end
187
+
188
+ class ReturnNode < Node.new(:expression); end
189
+ class PropertyNode < Node.new(:properties); end
190
+
191
+ class NextNode < NodeType
192
+ def eval(context, memory); end
193
+ def ==(o)
194
+ o.kind_of?(NextNode)
195
+ end
196
+ end
197
+ end