plasma 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,97 @@
1
+ module Plasma
2
+ module Interpreter
3
+ grammar PlasmaGrammar
4
+ rule plasma
5
+ decl / seq / quote / defun / def / fun / if / apply / bool / sym / hash / list / date / time / str / num
6
+ end
7
+
8
+ rule decl
9
+ '|' x first:plasma? rest:(s '|' s plasma)* x '|' <DeclNode>
10
+ end
11
+
12
+ rule seq
13
+ '(' x first:plasma? rest:(s '|' s plasma)* x ')' <SeqNode>
14
+ end
15
+
16
+ rule quote
17
+ "'" plasma <QuoteNode>
18
+ end
19
+
20
+ rule defun
21
+ '(' x 'defun' s params s plasma x ')' <DefunNode>
22
+ end
23
+
24
+ rule def
25
+ '(' x 'def' s sym s plasma x ')' <DefNode>
26
+ end
27
+
28
+ rule fun
29
+ '(' x 'fun' s params s plasma x ')' <FunNode>
30
+ end
31
+
32
+ rule params
33
+ '(' x first:sym rest:(s sym)* x ')' <ParamsNode>
34
+ end
35
+
36
+ rule if
37
+ '(' x 'if' s pred:plasma s 'then' s body:plasma maybe:(s 'else' s other:plasma)? x ')' <IfNode>
38
+ end
39
+
40
+ rule apply
41
+ '(' x applied:(fun / sym) s first:plasma rest:(s plasma)* x ')' <ApplyNode>
42
+ end
43
+
44
+ rule bool
45
+ 'true' <TrueNode> / 'false' <FalseNode>
46
+ end
47
+
48
+ rule sym
49
+ [a-z+!/\^&@?*%<>=_-]+ <SymNode>
50
+ end
51
+
52
+ rule hash
53
+ '{' x first:relation? rest:(s relation)* x '}' <HashNode>
54
+ end
55
+
56
+ rule relation
57
+ sym x ':' x plasma <RelationNode>
58
+ end
59
+
60
+ rule list
61
+ '[' x first:plasma? rest:(s plasma)* x ']' <ListNode>
62
+ end
63
+
64
+ rule date
65
+ year [-] month:dual [-] day:dual (s time)? <DateNode>
66
+ end
67
+
68
+ rule year
69
+ [0-9] [0-9] [0-9] [0-9]
70
+ end
71
+
72
+ rule dual
73
+ [0-9] [0-9]
74
+ end
75
+
76
+ rule time
77
+ hours:dual ':' minutes:dual ':' seconds:dual <TimeNode>
78
+ end
79
+
80
+ rule str
81
+ '"' first:([^"])? rest:([^"])* '"' <StrNode>
82
+ end
83
+
84
+ rule num
85
+ whole:([0-9]+) expansion:('.' [0-9]+)? <NumNode>
86
+ end
87
+
88
+ rule x
89
+ s?
90
+ end
91
+
92
+ rule s
93
+ [ \n]+
94
+ end
95
+ end
96
+ end
97
+ end
@@ -0,0 +1,331 @@
1
+ module Plasma
2
+ module Interpreter
3
+ class Env
4
+ attr_accessor :state
5
+ attr_accessor :default
6
+
7
+ def initialize(init={}, default=nil)
8
+ @state = [init]
9
+ @default = default
10
+ end
11
+
12
+ def to_s
13
+ @state.map {|s| s.inspect}.join("\n")
14
+ end
15
+
16
+ def to_plasma
17
+ @state.to_plasma
18
+ end
19
+
20
+ def bind!(key, value)
21
+ @state.last[key] = value
22
+ self
23
+ end
24
+
25
+ def merge!(hash)
26
+ @state.last.merge!(hash)
27
+ self
28
+ end
29
+
30
+ def scope!(inner={})
31
+ @state.push(inner)
32
+ self
33
+ end
34
+
35
+ def release!()
36
+ @state.pop
37
+ self
38
+ end
39
+
40
+ def resolve(key)
41
+ @state.reverse_each do |layer|
42
+ return layer[key] if layer.include?(key)
43
+ end
44
+
45
+ # nothing to find
46
+ return default
47
+ end
48
+ end
49
+
50
+ class Quote
51
+ attr_reader :unevaluated
52
+
53
+ def initialize(plasma)
54
+ @unevaluated = plasma
55
+ end
56
+
57
+ def unquote(env)
58
+ @unevaluated.eval(env)
59
+ end
60
+
61
+ def to_plasma
62
+ "'#{@unevaluated.text_value}"
63
+ end
64
+ end
65
+
66
+ class Closure
67
+ attr_reader :env, :params, :body
68
+
69
+ def initialize(env, params, body)
70
+ @env = env.dup
71
+ @params = params
72
+ @body = body
73
+ @proc = nil
74
+ end
75
+
76
+ def apply(*args)
77
+ left = params.dup
78
+ args = args.slice(0...left.length) if left.length < args.length
79
+ zipped = args.inject({}){|hash, arg| hash.merge(left.shift => arg)}
80
+ zipped.merge!(:env => @env)
81
+
82
+ if left.empty?
83
+ @env.scope!(zipped)
84
+ value = @body.evaluate(@env)
85
+ @env.release!()
86
+
87
+ return value
88
+ else
89
+ return Closure.new(@env.dup.merge!(zipped), left, body)
90
+ end
91
+ end
92
+
93
+ def to_proc
94
+ @proc ||= Proc.new do |*args|
95
+ self.apply *args
96
+ end
97
+ end
98
+
99
+ def to_plasma
100
+ p = params.map{|p| p.to_s}.join(' ')
101
+ "fun (#{p}) #{@body.text_value}"
102
+ end
103
+ end
104
+
105
+ class RubyClosure
106
+ def initialize(name, interp, &body)
107
+ @name = name
108
+ @interp = interp
109
+ @body = Proc.new(&body)
110
+ end
111
+
112
+ def apply(*args)
113
+ args = args.slice(0..@body.arity) if @body.arity < args.length
114
+ diff = @body.arity - args.length
115
+ if diff == 0
116
+ value = @body.call(*args)
117
+ else
118
+ fresh = {}
119
+ args.each_with_index {|arg, index| fresh.merge(index => arg)}
120
+ params = (args.length..args.length+diff).join(' ')
121
+ keys = fresh.keys.join(' ')
122
+ env = Env.new(fresh.merge(@name => self))
123
+
124
+ interp.interpret("fun (#{params}) ((#{@name} #{keys} #{params}))", env)
125
+ end
126
+ end
127
+ end
128
+
129
+ class UnresolvedSymbolException < Exception
130
+ attr_accessor :symbol
131
+
132
+ def initialize(symbol)
133
+ @symbol = symbol
134
+ end
135
+ end
136
+
137
+ class NoSuchSourceException < Exception
138
+ attr_accessor :plasma
139
+
140
+ def initialize(plasma)
141
+ @plasma = plasma
142
+ end
143
+ end
144
+
145
+ class TooManyArgumentsException < Exception
146
+ end
147
+
148
+ class FailedToParseException < Exception
149
+ end
150
+
151
+ class PlasmaNode < Treetop::Runtime::SyntaxNode
152
+ def evaluate(env)
153
+ orb.evaluate(env)
154
+ end
155
+
156
+ def empty?
157
+ false
158
+ end
159
+ end
160
+
161
+ class ColNode < PlasmaNode
162
+ def col
163
+ if first.empty?
164
+ []
165
+ else
166
+ if rest.empty?
167
+ [first]
168
+ else
169
+ rest.elements.map do |el|
170
+ el.elements.select{|subel| subel.is_a? PlasmaNode}
171
+ end.flatten.unshift(first)
172
+ end
173
+ end
174
+ end
175
+ end
176
+
177
+ class DeclNode < ColNode
178
+ def evaluate(env)
179
+ value = col.inject(nil) do |value, statement|
180
+ statement.evaluate(env)
181
+ end
182
+ value
183
+ end
184
+ end
185
+
186
+ class SeqNode < ColNode
187
+ def evaluate(env)
188
+ env.scope!
189
+ value = col.inject(nil) do |value, statement|
190
+ statement.evaluate(env)
191
+ end
192
+ env.release!
193
+
194
+ value
195
+ end
196
+ end
197
+
198
+ class QuoteNode < PlasmaNode
199
+ def evaluate(env)
200
+ plasma
201
+ end
202
+ end
203
+
204
+ class DefunNode < PlasmaNode
205
+ def evaluate(env)
206
+ syms = params.syms
207
+ closure = Closure.new(env, syms.slice(1..syms.length), plasma)
208
+ closure.env.merge!(syms[0] => closure)
209
+ env.bind!(syms[0], closure)
210
+ closure
211
+ end
212
+ end
213
+
214
+ class DefNode < PlasmaNode
215
+ def evaluate(env)
216
+ value = plasma.evaluate(env)
217
+ env.bind!(sym.text_value.to_sym, value)
218
+ value
219
+ end
220
+ end
221
+
222
+ class FunNode < PlasmaNode
223
+ def evaluate(env)
224
+ Closure.new(env, params.syms, plasma)
225
+ end
226
+ end
227
+
228
+ class ParamsNode < ColNode
229
+ def syms
230
+ col.map{|node| node.text_value.to_sym}
231
+ end
232
+ end
233
+
234
+ class IfNode < PlasmaNode
235
+ def evaluate(env)
236
+ if pred.evaluate(env)
237
+ body.evaluate(env)
238
+ elsif respond_to?(:maybe)
239
+ maybe.other.evaluate(env)
240
+ else
241
+ nil
242
+ end
243
+ end
244
+ end
245
+
246
+ class ApplyNode < ColNode
247
+ def evaluate(env)
248
+ args = col.map{|arg| arg.evaluate(env)}
249
+
250
+ begin
251
+ closure = applied.evaluate(env)
252
+ return closure.apply(*args)
253
+ rescue UnresolvedSymbolException => detail
254
+ begin
255
+ return args.first.send(detail.symbol, *args.slice(1..args.length))
256
+ rescue ArgumentError => arg
257
+ begin
258
+ slice = args.slice(1...args.length-1)
259
+ proc = args.last.to_proc
260
+
261
+ return args.first.send(detail.symbol, *slice, &proc)
262
+ rescue Exception => brg
263
+ raise TooManyArgumentsException.new(self.text_value), "too many arguments in #{self.text_value}", caller
264
+ end
265
+ end
266
+ end
267
+ end
268
+ end
269
+
270
+ class TrueNode < PlasmaNode
271
+ def evaluate(env)
272
+ true
273
+ end
274
+ end
275
+
276
+ class FalseNode < PlasmaNode
277
+ def evaluate(env)
278
+ false
279
+ end
280
+ end
281
+
282
+ class SymNode < PlasmaNode
283
+ def evaluate(env)
284
+ result = env.resolve(self.text_value.to_sym)
285
+ raise UnresolvedSymbolException.new(self.text_value), "unresolved symbol #{self.text_value}", caller if result.nil?
286
+ result
287
+ end
288
+ end
289
+
290
+ class HashNode < ColNode
291
+ def evaluate(env)
292
+ col.inject({}) {|hash, el| hash.merge(el.evaluate(env))}
293
+ end
294
+ end
295
+
296
+ class RelationNode < PlasmaNode
297
+ def evaluate(env)
298
+ {sym.text_value.to_sym => plasma.evaluate(env)}
299
+ end
300
+ end
301
+
302
+ class ListNode < ColNode
303
+ def evaluate(env)
304
+ col.map{|el| el.evaluate(env)}
305
+ end
306
+ end
307
+
308
+ class DateNode < PlasmaNode
309
+ def evaluate(env)
310
+ end
311
+ end
312
+
313
+ class TimeNode < PlasmaNode
314
+ def evaluate(env)
315
+ end
316
+ end
317
+
318
+ class StrNode < ColNode
319
+ def evaluate(env)
320
+ self.text_value.slice(1...self.text_value.length-1)
321
+ end
322
+ end
323
+
324
+ class NumNode < PlasmaNode
325
+ def evaluate(env)
326
+ text_value.to_f
327
+ end
328
+ end
329
+ end
330
+ end
331
+
@@ -0,0 +1,104 @@
1
+ module Plasma
2
+ module Interpreter
3
+ class PlasmaInterpreter
4
+ attr_accessor :env
5
+
6
+ def initialize
7
+ @prompt = "-----| "
8
+ @dir = File.dirname(__FILE__)
9
+ @load_path = [File.join(PLASMA_ROOT, 'include'), PLASMA_PACKAGE_ROOT, @dir]
10
+
11
+ @env = Env.new
12
+ @env.bind!(:mu, self)
13
+ @env.bind!(:env, @env)
14
+
15
+ @plasma = PlasmaGrammarParser.new
16
+
17
+ import 'plasma_core'
18
+ merge 'core'
19
+ end
20
+
21
+ def to_s
22
+ "plasma -- #{@env.keys}"
23
+ end
24
+
25
+ def to_plasma
26
+ "eval"
27
+ end
28
+
29
+ def path
30
+ @load_path
31
+ end
32
+
33
+ def import(rb)
34
+ name = rb.split('/').last
35
+ file = "#{rb}.rb"
36
+ @load_path.each do |p|
37
+ package = File.join(p, file)
38
+ if File.exist? package
39
+ load package
40
+ @env.merge!(Plasma::Interpreter.const_get(name.classify).plasma(self))
41
+
42
+ return true
43
+ end
44
+ end
45
+
46
+ return false
47
+ end
48
+
49
+ def merge(plasma)
50
+ name = plasma.split('/').last
51
+ file = "#{plasma}.plasma"
52
+ found = false
53
+ value = nil
54
+
55
+ @load_path.each do |p|
56
+ package = File.join(p, file)
57
+ if File.exist? package
58
+ source = File.open(package, 'r')
59
+ code = source.read.strip
60
+
61
+ value = self.interpret(code)
62
+ found = true
63
+ end
64
+ end
65
+
66
+ return value if found
67
+ raise NoSuchSourceException.new(plasma), "no such source #{plasma}", caller
68
+ end
69
+
70
+ def parse(code)
71
+ tree = @plasma.parse(code)
72
+ raise FailedToParseException.new, "failed to parse #{code}", caller if tree.nil?
73
+
74
+ return tree
75
+ end
76
+
77
+ def evaluate(tree, env=nil)
78
+ env = @env if env.nil?
79
+ tree.evaluate(env)
80
+ end
81
+
82
+ def interpret(code, env=nil)
83
+ tree = parse(code)
84
+ evaluate(tree, env)
85
+ end
86
+
87
+ def repl
88
+ while true
89
+ begin
90
+ STDOUT.write(@prompt)
91
+ input = STDIN.readline.strip
92
+ break if input == 'quit' or input == 'exit'
93
+
94
+ value = interpret input
95
+
96
+ STDOUT.write(" #{value.to_plasma}\n")
97
+ rescue Exception => e
98
+ STDOUT.write(" #{e.to_s}\n")
99
+ end
100
+ end
101
+ end
102
+ end
103
+ end
104
+ end