delorean_lang 0.0.33

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.
@@ -0,0 +1,45 @@
1
+ module Delorean
2
+ ######################################################################
3
+ # Parse Errors
4
+
5
+ class ParseError < StandardError
6
+ attr_reader :line, :module_name
7
+
8
+ def initialize(message, module_name, line)
9
+ super(message)
10
+ @line = line
11
+ @module_name = module_name
12
+ end
13
+ end
14
+
15
+ class UndefinedError < ParseError
16
+ end
17
+
18
+ class RedefinedError < ParseError
19
+ end
20
+
21
+ class UndefinedFunctionError < ParseError
22
+ end
23
+
24
+ class UndefinedNodeError < ParseError
25
+ end
26
+
27
+ class RecursionError < ParseError
28
+ end
29
+
30
+ class BadCallError < ParseError
31
+ end
32
+
33
+ class ImportError < ParseError
34
+ end
35
+
36
+ ######################################################################
37
+ # Runtime Errors
38
+
39
+ class InvalidGetAttribute < StandardError
40
+ end
41
+
42
+ class UndefinedParamError < StandardError
43
+ end
44
+
45
+ end
@@ -0,0 +1,134 @@
1
+ module Delorean
2
+ module Functions
3
+ ######################################################################
4
+
5
+ def MAX(*args)
6
+ args.max
7
+ end
8
+
9
+ MAX_SIG = [ 2, Float::INFINITY ]
10
+
11
+ ######################################################################
12
+
13
+ def MIN(*args)
14
+ args.min
15
+ end
16
+
17
+ MIN_SIG = MAX_SIG
18
+
19
+ ######################################################################
20
+
21
+ def MAXLIST(arg)
22
+ raise "argument must be list" unless arg.is_a? Array
23
+ arg.max
24
+ end
25
+
26
+ MAXLIST_SIG = [ 1, 1 ]
27
+
28
+ def MINLIST(arg)
29
+ raise "argument must be list" unless arg.is_a? Array
30
+ arg.min
31
+ end
32
+
33
+ MINLIST_SIG = [ 1, 1 ]
34
+
35
+ ######################################################################
36
+
37
+ def ROUND(number, *args)
38
+ number.round(*args)
39
+ end
40
+
41
+ ROUND_SIG = [ 1, 2 ]
42
+
43
+ ######################################################################
44
+
45
+ def TIMEPART(time, part)
46
+ if time == Float::INFINITY
47
+ return time if part == "d"
48
+ raise "Can only access date part of Infinity"
49
+ end
50
+
51
+ raise "non-time arg to TIMEPART" unless time.is_a?(Time)
52
+
53
+ return time.hour if part == "h"
54
+ return time.min if part == "m"
55
+ return time.sec if part == "s"
56
+ return time.to_date if part == "d"
57
+
58
+ raise "unknown part arg to TIMEPART"
59
+ end
60
+
61
+ TIMEPART_SIG = [ 2, 2 ]
62
+
63
+ ######################################################################
64
+
65
+ def DATEPART(date, part)
66
+ raise "non-date arg to DATEPART" unless date.is_a?(Date)
67
+
68
+ return date.month if part == "m"
69
+ return date.day if part == "d"
70
+ return date.year if part == "y"
71
+
72
+ raise "unknown part arg to DATEPART"
73
+ end
74
+
75
+ DATEPART_SIG = [ 2, 2 ]
76
+
77
+ ######################################################################
78
+
79
+ def DATEADD(date, interval, part)
80
+ raise "non-date arg to DATEADD" unless date.is_a?(Date)
81
+ raise "non-integer interval arg to DATEADD" unless interval.is_a?(Fixnum)
82
+
83
+ return date >> interval if part == "m"
84
+ return date + interval if part == "d"
85
+ return date >> (interval * 12) if part == "y"
86
+
87
+ raise "unknown part arg to DATEADD"
88
+ end
89
+
90
+ DATEADD_SIG = [ 3, 3 ]
91
+
92
+ ######################################################################
93
+
94
+ def INDEX(array, i)
95
+ raise "non-array arg to INDEX" unless array.is_a?(Array)
96
+ raise "non-integer index on call to INDEX" unless i.is_a?(Fixnum)
97
+ array.at(i)
98
+ end
99
+
100
+ INDEX_SIG = [ 2, 2 ]
101
+
102
+ ######################################################################
103
+
104
+ def FLATTEN(array, *args)
105
+ raise "non-array arg to FLATTEN" unless array.is_a?(Array)
106
+ raise "non-integer flatten on call to FLATTEN" unless
107
+ (args.empty? || args[0].is_a?(Fixnum))
108
+ array.flatten(*args)
109
+ end
110
+
111
+ FLATTEN_SIG = [ 1, 2 ]
112
+
113
+ ######################################################################
114
+
115
+ def ERR(*args)
116
+ str = args.map(&:to_s).join(", ")
117
+ raise str
118
+ end
119
+
120
+ ERR_SIG = [ 1, Float::INFINITY ]
121
+
122
+ ######################################################################
123
+
124
+ def TOSYM(s)
125
+ # raise "non-String arg to SYM" unless s.is_a?(String)
126
+ s.to_sym
127
+ end
128
+
129
+ TOSYM_SIG = [ 1, 1 ]
130
+
131
+ ######################################################################
132
+
133
+ end
134
+ end
@@ -0,0 +1,25 @@
1
+ module Delorean
2
+ module Model
3
+ def self.included(base)
4
+ base.send :extend, ClassMethods
5
+ end
6
+
7
+ module ClassMethods
8
+ def delorean_fn(name, options = {}, &block)
9
+ define_singleton_method(name) do |*args|
10
+ block.call(*args)
11
+ end
12
+
13
+ sig = options[:sig]
14
+
15
+ raise "no signature" unless sig
16
+
17
+ if sig
18
+ sig = [sig, sig] if sig.is_a? Fixnum
19
+ raise "Bad signature" unless (sig.is_a? Array and sig.length==2)
20
+ self.const_set(name.to_s.upcase+SIG, sig)
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,401 @@
1
+ module Delorean
2
+ class SNode < Treetop::Runtime::SyntaxNode
3
+ end
4
+
5
+ class Line < SNode
6
+ def check(context, *a)
7
+ f.check(context, *a)
8
+ end
9
+ def rewrite(context)
10
+ f.rewrite(context)
11
+ end
12
+ end
13
+
14
+ class Parameter < SNode
15
+ def check(context, *)
16
+ context.parse_define_param(i.text_value, [])
17
+ end
18
+
19
+ def rewrite(context)
20
+ # Adds a parameter to the current node. Parameters are
21
+ # implemented as functions (just like attrs). The environment
22
+ # arg (_e) is a Hash. To find a param (aname) in node (cname),
23
+ # we first check to see if cname.aname has already been computed
24
+ # in _e. If not, to compute it we check for the value in _e
25
+ # (i.e. check for aname). Otherwise, we use the default value
26
+ # if any.
27
+ aname, cname = i.text_value, context.last_node
28
+ exc = "raise UndefinedParamError, 'undefined parameter #{aname}'"
29
+ <<eos
30
+ class #{cname}
31
+ def self.#{aname}#{POST}(_e)
32
+ _e['#{cname}.#{aname}'] ||=
33
+ begin
34
+ _e.fetch('#{aname}')
35
+ rescue KeyError
36
+ #{defined?(e) ? e.rewrite(context) : exc}
37
+ end
38
+ end
39
+ end
40
+ eos
41
+ end
42
+ end
43
+
44
+ class ParameterDefault < Parameter
45
+ def check(context, *)
46
+ # The check function returns the list of attrs used in the
47
+ # default expression. This is then used to make check if the
48
+ # attrs are available in the param's context. NOTE: in a
49
+ # previous implementation, spec used to include attr type
50
+ # information so that we could perform static type checking.
51
+ # This mechanism has been removed.
52
+ spec = e.check(context)
53
+ context.parse_define_param(i.text_value, spec)
54
+ end
55
+ end
56
+
57
+ class Import < SNode
58
+ def check(context, sset)
59
+ context.parse_import(sset, n.text_value, v.text_value)
60
+ end
61
+
62
+ def rewrite(context)
63
+ context.gen_import(n.text_value, v.text_value)
64
+ ""
65
+ end
66
+ end
67
+
68
+ class BaseNode < SNode
69
+ # defines a base node
70
+ def check(context, *)
71
+ context.parse_define_node(n.text_value, nil)
72
+ end
73
+
74
+ def rewrite(context)
75
+ # Nodes are simply translated to classes.
76
+ "class #{n.text_value} < BaseClass; end"
77
+ end
78
+ end
79
+
80
+ class SubNode < SNode
81
+ def check(context, *)
82
+ mname = mod.m.text_value if defined?(mod.m)
83
+
84
+ context.parse_define_node(n.text_value, p.text_value, mname)
85
+ end
86
+
87
+ def rewrite(context)
88
+ mname = mod.m.text_value if defined?(mod.m)
89
+ sname = context.super_name(p.text_value, mname)
90
+
91
+ # A sub-node (derived node) is just a subclass.
92
+ "class #{n.text_value} < #{sname}; end"
93
+ end
94
+ end
95
+
96
+ class Formula < SNode
97
+ def check(context, *)
98
+ context.parse_define_attr(i.text_value, e.check(context))
99
+ end
100
+
101
+ def rewrite(context)
102
+ # an attr is defined as a class function on the node class.
103
+ "class #{context.last_node}; " +
104
+ "def self.#{i.text_value}#{POST}(_e); " +
105
+ "_e['#{context.last_node}.#{i.text_value}'] ||= " +
106
+ e.rewrite(context) + "; end; end;"
107
+ end
108
+ end
109
+
110
+ class Expr < SNode
111
+ def check(context, *)
112
+ e.check(context)
113
+ end
114
+
115
+ def rewrite(context)
116
+ "(" + e.rewrite(context) + ")"
117
+ end
118
+ end
119
+
120
+ # unary operator
121
+ class UnOp < SNode
122
+ def check(context, *)
123
+ e.check(context)
124
+ end
125
+
126
+ def rewrite(context)
127
+ op.text_value + e.rewrite(context)
128
+ end
129
+ end
130
+
131
+ class BinOp < SNode
132
+ def check(context, *)
133
+ vc, ec = v.check(context), e.check(context)
134
+ # returns list of attrs used in RHS and LHS
135
+ ec + vc
136
+ end
137
+
138
+ def rewrite(context)
139
+ v.rewrite(context) + " " + op.text_value + " " + e.rewrite(context)
140
+ end
141
+ end
142
+
143
+ class Literal < SNode
144
+ def check(context, *)
145
+ []
146
+ end
147
+
148
+ # Delorean literals have same syntax as Ruby
149
+ def rewrite(context)
150
+ text_value
151
+ end
152
+ end
153
+
154
+ class String < Literal
155
+ def rewrite(context)
156
+ # remove the quotes and requote. We don't want the likes of #{}
157
+ # evals to just pass through.
158
+ text_value[1..-2].inspect
159
+ end
160
+ end
161
+
162
+ class Identifier < SNode
163
+ def check(context, *)
164
+ context.parse_call_last_node_attr(text_value)
165
+ [text_value]
166
+ end
167
+
168
+ def rewrite(context)
169
+ # Identifiers are just attr accesses. These are translated to
170
+ # class method calls. POST is used in mangling the attr names.
171
+ # _e is the environment. Comprehension vars (in comp_set) are
172
+ # not passed the env arg.
173
+ arg = context.comp_set.member?(text_value) ? "" : '(_e)'
174
+ text_value + POST + arg
175
+ end
176
+ end
177
+
178
+ class NodeGetAttr < SNode
179
+ def check(context, *)
180
+ context.parse_call_attr(n.text_value, i.text_value)
181
+ [text_value]
182
+ end
183
+
184
+ def rewrite(context)
185
+ text_value + POST + '(_e)'
186
+ end
187
+ end
188
+
189
+ class GetAttr < SNode
190
+ def check(context, *)
191
+ i.check(context)
192
+ end
193
+
194
+ def rewrite(context)
195
+ attr_list = ga.text_value.split('.')
196
+ attr_list.inject(i.rewrite(context)) {|x, y| "_get_attr(#{x}, '#{y}')"}
197
+ end
198
+ end
199
+
200
+ class ModelFnGetAttr < SNode
201
+ def check(context, *)
202
+ mfn.check(context)
203
+ end
204
+
205
+ def rewrite(context)
206
+ x = mfn.rewrite(context)
207
+ "_get_attr(#{x}, '#{i.text_value}')"
208
+ end
209
+ end
210
+
211
+ class Fn < SNode
212
+ def check(context, *)
213
+ acount, res =
214
+ defined?(args) ? [args.arg_count, args.check(context)] : [0, []]
215
+
216
+ context.parse_check_call_fn(fn.text_value, acount)
217
+ res
218
+ end
219
+
220
+ def rewrite(context)
221
+ fn.text_value + "(" + (defined?(args) ? args.rewrite(context) : "") + ")"
222
+ end
223
+ end
224
+
225
+ class FnArgs < SNode
226
+ def check(context, *)
227
+ arg0.check(context) +
228
+ (defined?(args_rest.args) && !args_rest.args.text_value.empty? ?
229
+ args_rest.args.check(context) : [])
230
+ end
231
+
232
+ def rewrite(context)
233
+ arg0.rewrite(context) +
234
+ (defined?(args_rest.args) && !args_rest.args.text_value.empty? ?
235
+ ", " + args_rest.args.rewrite(context) : "")
236
+ end
237
+
238
+ def arg_count
239
+ defined?(args_rest.args) ? 1 + args_rest.args.arg_count : 1
240
+ end
241
+ end
242
+
243
+ class ModelFn < SNode
244
+ def check(context, *)
245
+ acount, res =
246
+ defined?(args) ? [args.arg_count, args.check(context)] : [0, []]
247
+
248
+ context.parse_check_call_fn(fn.text_value, acount, m.text_value)
249
+ res
250
+ end
251
+
252
+ def rewrite(context)
253
+ m.text_value + "." + fn.text_value +
254
+ "(" + (defined?(args) ? args.rewrite(context) : "") + ")"
255
+ end
256
+ end
257
+
258
+ class IfElse < SNode
259
+ def check(context, *)
260
+ vc, e1c, e2c =
261
+ v.check(context), e1.check(context), e2.check(context)
262
+ vc + e1c + e2c
263
+ end
264
+
265
+ def rewrite(context)
266
+ "(" + v.rewrite(context) + ") ? (" +
267
+ e1.rewrite(context) + ") : (" + e2.rewrite(context) + ")"
268
+ end
269
+ end
270
+
271
+ class ListExpr < SNode
272
+ def check(context, *)
273
+ defined?(args) ? args.check(context) : []
274
+ end
275
+
276
+ def rewrite(context)
277
+ "[" + (defined?(args) ? args.rewrite(context) : "") + "]"
278
+ end
279
+ end
280
+
281
+ class ListComprehension < SNode
282
+ def check(context, *)
283
+ vname = i.text_value
284
+
285
+ e1c = e1.check(context)
286
+ context.parse_define_var(vname)
287
+ # need to check e2/e3 in a context where the comprehension var
288
+ # is defined.
289
+ e2c = e2.check(context)
290
+ e3c = defined?(ifexp.e3) ? ifexp.e3.check(context) : []
291
+
292
+ context.parse_undef_var(vname)
293
+ e2c.delete(vname)
294
+ e3c.delete(vname)
295
+
296
+ e1c + e2c + e3c
297
+ end
298
+
299
+ def rewrite(context)
300
+ res = "(#{e1.rewrite(context)}).map{"
301
+ context.parse_define_var(i.text_value)
302
+ res += "|#{i.rewrite(context)}| (#{e2.rewrite(context)}) }"
303
+
304
+ res += ".select{|#{i.rewrite(context)}| (#{ifexp.e3.rewrite(context)}) }" if
305
+ defined?(ifexp.e3)
306
+
307
+ context.parse_undef_var(i.text_value)
308
+ res
309
+ end
310
+ end
311
+
312
+ class HashExpr < SNode
313
+ def check(context, *)
314
+ defined?(args) ? args.check(context) : {}
315
+ end
316
+
317
+ def rewrite(context)
318
+ "{" + (defined?(args) ? args.rewrite(context) : "") + "}"
319
+ end
320
+ end
321
+
322
+ class KwArgs < SNode
323
+ def check(context, *)
324
+ arg0.check(context) + (defined?(args_rest.args) ?
325
+ args_rest.args.check(context) : [])
326
+ end
327
+
328
+ def rewrite(context)
329
+ arg0_rw = arg0.rewrite(context)
330
+
331
+ if defined?(args_rest.al)
332
+ args, kw = args_rest.al.rewrite(context)
333
+ else
334
+ args, kw = [], {}
335
+ end
336
+
337
+ if defined?(k.i)
338
+ kw[k.i.text_value] = arg0_rw
339
+ else
340
+ args << arg0_rw
341
+ end
342
+
343
+ [args, kw]
344
+ end
345
+ end
346
+
347
+ class HashArgs < SNode
348
+ def check(context, *)
349
+ e0.check(context) + e1.check(context) +
350
+ (defined?(args_rest.args) ? args_rest.args.check(context) : [])
351
+ end
352
+
353
+ def rewrite(context)
354
+ e0.rewrite(context) + " => " + e1.rewrite(context) +
355
+ (defined?(args_rest.al) && !args_rest.al.text_value.empty? ?
356
+ ", " + args_rest.al.rewrite(context) : "")
357
+ end
358
+ end
359
+
360
+ class ScriptCall < SNode
361
+ def check(context, *)
362
+ i.check(context) unless i.text_value.empty?
363
+ al.check(context) if defined?(al)
364
+ []
365
+ end
366
+
367
+ def rewrite(context)
368
+ node_name = i.text_value.empty? ? "nil" : i.rewrite(context)
369
+ do_rewrite(context, node_name)
370
+ end
371
+
372
+ def do_rewrite(context, node_name, mname="nil")
373
+ args, kw = al.rewrite(context)
374
+
375
+ args_str = '[' + args.reverse.join(',') + ']'
376
+ kw_str = '{' + kw.map {|k, v| "'#{k}' => #{v}" }.join(',') + '}'
377
+
378
+ "_script_call(#{node_name}, #{mname}, _e, #{args_str}, #{kw_str})"
379
+ end
380
+ end
381
+
382
+ class ScriptCallNode < ScriptCall
383
+ def check(context, *)
384
+ # FIXME: for both this and when node_name is nil, should check
385
+ # to see if attributes exist on the node before allowing the
386
+ # call. Also, can check parameters.
387
+
388
+ mname = mod.m.text_value if defined?(mod.m)
389
+ context.parse_check_defined_mod_node(c.text_value, mname)
390
+
391
+ al.check(context) if defined?(al)
392
+ []
393
+ end
394
+
395
+ def rewrite(context)
396
+ node_name = c.text_value.inspect
397
+ mname = defined?(mod.m) ? mod.m.text_value.inspect : "nil"
398
+ do_rewrite(context, node_name, mname)
399
+ end
400
+ end
401
+ end