delorean_lang 0.0.33

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,172 @@
1
+ grammar Delorean
2
+ rule line
3
+ f:formula sp? ('#' .*)? <Line>
4
+ end
5
+
6
+ rule formula
7
+ sp i:identifier sp? '=?' sp? e:expression <ParameterDefault>
8
+ /
9
+ sp i:identifier sp? '=?' <Parameter>
10
+ /
11
+ sp i:identifier sp? '=' sp? e:expression <Formula>
12
+ /
13
+ n:node_name ':' sp? mod:(m:node_name '::')? p:node_name <SubNode>
14
+ /
15
+ n:node_name ':' <BaseNode>
16
+ /
17
+ 'import' sp n:node_name sp v:integer <Import>
18
+ end
19
+
20
+ rule node_name
21
+ [A-Z] [a-zA-Z0-9_]*
22
+ end
23
+
24
+ rule expression
25
+ op:unary_op sp? e:expression <UnOp>
26
+ /
27
+ 'if' sp? v:expression sp?
28
+ 'then' sp? e1:expression sp?
29
+ 'else' sp? e2:expression <IfElse>
30
+ /
31
+ v:value sp? op:binary_op sp? e:expression <BinOp>
32
+ /
33
+ value
34
+ end
35
+
36
+ rule list_expr
37
+ '[]' <ListExpr>
38
+ /
39
+ '[' sp? e2:expression sp
40
+ 'for' sp i:identifier sp 'in' sp e1:expression sp?
41
+ ifexp:('if' sp e3:expression sp?)?
42
+ ']' <ListComprehension>
43
+ /
44
+ '[' sp? args:fn_args sp? ']' <ListExpr>
45
+ end
46
+
47
+ rule hash_expr
48
+ '{}' <HashExpr>
49
+ /
50
+ '{' sp? args:hash_args sp? '}' <HashExpr>
51
+ end
52
+
53
+ # NOTE: some operations such as << have side-effects (e.g. on
54
+ # Arrays). So, be cautious about which opertaions are added.
55
+ rule binary_op
56
+ '+' / '-' / '*' / '/' / '%' / '==' / '!=' / '>=' / '<=' / '>' / '<' / '&&' / '||'
57
+ end
58
+
59
+ rule unary_op
60
+ '!' / '-'
61
+ end
62
+
63
+ rule value
64
+ number /
65
+ string /
66
+ boolean /
67
+ nil_val /
68
+ script_call /
69
+ fn /
70
+ model_fn_getattr /
71
+ model_fn /
72
+ node_getattr /
73
+ getattr /
74
+ list_expr /
75
+ hash_expr /
76
+ '(' sp? e:expression sp? ')' <Expr>
77
+ end
78
+
79
+ # built-in functions
80
+ rule fn
81
+ fn:fn_name '(' sp? ')' <Fn>
82
+ /
83
+ fn:fn_name '(' sp? args:fn_args sp? ')' <Fn>
84
+ end
85
+
86
+ rule fn_args
87
+ arg0:expression args_rest:(sp? ',' sp? args:fn_args?)? <FnArgs>
88
+ end
89
+
90
+ rule model_fn
91
+ m:model_name '.' fn:identifier '(' sp? ')' <ModelFn>
92
+ /
93
+ m:model_name '.' fn:identifier '(' sp? args:fn_args sp? ')' <ModelFn>
94
+ end
95
+
96
+ rule model_name
97
+ class_name '::' model_name
98
+ /
99
+ class_name
100
+ end
101
+
102
+ rule fn_name
103
+ [A-Z] [A-Z0-9]*
104
+ end
105
+
106
+ rule class_name
107
+ [A-Z] [a-zA-Z0-9]*
108
+ end
109
+
110
+ # script calling
111
+ rule script_call
112
+ '@' mod:(m:node_name '::')? c:class_name '(' sp? al:kw_args sp? ')' <ScriptCallNode>
113
+ /
114
+ '@' i:identifier? '(' sp? al:kw_args sp? ')' <ScriptCall>
115
+ end
116
+
117
+ rule hash_args
118
+ e0:expression sp? ':' sp? e1:expression args_rest:(sp? ',' sp? al:hash_args?)? <HashArgs>
119
+ end
120
+
121
+ rule kw_args
122
+ k:(i:identifier ':' sp?)? arg0:expression args_rest:(sp? ',' sp? al:kw_args)? <KwArgs>
123
+ end
124
+
125
+ rule node_getattr
126
+ n:node_name '.' i:identifier <NodeGetAttr>
127
+ end
128
+
129
+ rule model_fn_getattr
130
+ mfn:model_fn '.' i:identifier <ModelFnGetAttr>
131
+ end
132
+
133
+ rule getattr
134
+ i:identifier '.' ga:getattr <GetAttr>
135
+ /
136
+ identifier
137
+ end
138
+
139
+ rule number
140
+ decimal / integer
141
+ end
142
+
143
+ rule decimal
144
+ [0-9]+ '.' [0-9]+ <Literal>
145
+ end
146
+
147
+ rule integer
148
+ [0-9]+ <Literal>
149
+ end
150
+
151
+ rule identifier
152
+ [a-z] [a-zA-Z0-9_]* <Identifier>
153
+ end
154
+
155
+ rule boolean
156
+ 'true' <Literal> / 'false' <Literal>
157
+ end
158
+
159
+ rule nil_val
160
+ 'nil' <Literal>
161
+ end
162
+
163
+ rule sp
164
+ [\s]+
165
+ end
166
+
167
+ rule string
168
+ '"' ('\"' / !'"' .)* '"' <String>
169
+ /
170
+ "'" [^']* "'" <String>
171
+ end
172
+ end
@@ -0,0 +1,359 @@
1
+ require 'delorean/base'
2
+
3
+ module Delorean
4
+ SIG = "_SIG"
5
+ MOD = "DELOREAN__"
6
+ POST = "__D"
7
+
8
+ class Engine
9
+ attr_reader :last_node, :module_name, :line_no, :comp_set, :pm, :m, :imports
10
+
11
+ def initialize(module_name)
12
+ # name of current module
13
+ @module_name = module_name
14
+ reset
15
+ end
16
+
17
+ def reset
18
+ @m, @pm = nil, nil
19
+ @last_node, @node_attrs = nil, {}
20
+ @line_no, @multi_no = 0, nil
21
+
22
+ # set of comprehension vars
23
+ @comp_set = Set.new
24
+
25
+ # set of all params
26
+ @param_set = Set.new
27
+
28
+ @imports = {}
29
+ end
30
+
31
+ def curr_line
32
+ @multi_no || @line_no
33
+ end
34
+
35
+ def parse_import(sset, name, version)
36
+ err(ParseError, "No script set") unless sset
37
+
38
+ err(ParseError, "Module #{name} importing itself") if
39
+ name == module_name
40
+
41
+ begin
42
+ @imports[name] = sset.import(name, version)
43
+ rescue => exc
44
+ err(ImportError, exc.to_s)
45
+ end
46
+
47
+ @pm.const_set("#{MOD}#{name}", @imports[name].pm)
48
+ end
49
+
50
+ def gen_import(name, version)
51
+ @imports.merge!(@imports[name].imports)
52
+
53
+ @m.const_set("#{MOD}#{name}", @imports[name].m)
54
+ end
55
+
56
+ def get_import_engine(name)
57
+ @imports[name] || err(ParseError, "#{name} not imported")
58
+ end
59
+
60
+ # Check to see if node with given name is defined. flag tell the
61
+ # method about our expectation. flag=true means that we make sure
62
+ # that name is defined. flag=false is the opposite.
63
+ def parse_check_defined_node(name, flag)
64
+ isdef = @pm.constants.member? name.to_sym
65
+
66
+ if isdef != flag
67
+ isdef ? err(RedefinedError, "#{name} already defined") :
68
+ err(UndefinedError, "#{name} not defined yet")
69
+ end
70
+ end
71
+
72
+ def super_name(pname, mname)
73
+ mname ? "#{MOD}#{mname}::#{pname}" : pname
74
+ end
75
+
76
+ def parse_check_defined_mod_node(pname, mname)
77
+ engine = mname ? get_import_engine(mname) : self
78
+ engine.parse_check_defined_node(pname, true)
79
+ end
80
+
81
+ def parse_define_node(name, pname, mname=nil)
82
+ parse_check_defined_node(name, false)
83
+ parse_check_defined_mod_node(pname, mname) if pname
84
+
85
+ sname = pname ? super_name(pname, mname) : 'Object'
86
+
87
+ code = "class #{name} < #{sname}; end"
88
+ @pm.module_eval(code)
89
+
90
+ # latest defined node
91
+ @last_node = name
92
+
93
+ # mapping of node name to list of attrs it defines
94
+ @node_attrs[name] = []
95
+ end
96
+
97
+ # Parse-time check to see if attr is available. If not, error is
98
+ # raised.
99
+ def parse_call_attr(node_name, attr_name)
100
+ return [] if comp_set.member?(attr_name)
101
+
102
+ # get the class associated with node
103
+ klass = @pm.module_eval(node_name)
104
+
105
+ # puts attr_name, "#{attr_name}#{POST}".to_sym, klass.methods.inspect
106
+
107
+ begin
108
+ klass.send("#{attr_name}#{POST}".to_sym, [])
109
+ rescue NoMethodError
110
+ err(UndefinedError, "'#{attr_name}' not defined in #{node_name}")
111
+ end
112
+ end
113
+
114
+ # Parse-time check to see if attr is available on current node.
115
+ def parse_call_last_node_attr(attr_name)
116
+ err(ParseError, "Not inside a node") unless @last_node
117
+ parse_call_attr(@last_node, attr_name)
118
+ end
119
+
120
+ def parse_define_var(var_name)
121
+ err(RedefinedError,
122
+ "List comprehension can't redefine variable '#{var_name}'") if
123
+ comp_set.member? var_name
124
+
125
+ comp_set.add var_name
126
+ end
127
+
128
+ def parse_undef_var(var_name)
129
+ err(ParseError, "internal error") unless comp_set.member? var_name
130
+ comp_set.delete var_name
131
+ end
132
+
133
+ # parse-time attr definition
134
+ def parse_define_attr(name, spec)
135
+ err(ParseError, "Can't define '#{name}' outside a node") unless
136
+ @last_node
137
+
138
+ err(RedefinedError, "Can't redefine '#{name}' in node #{@last_node}") if
139
+ @node_attrs[@last_node].member? name
140
+
141
+ @node_attrs[@last_node] << name
142
+
143
+ checks = spec.map { |a|
144
+ n = a.index('.') ? a : (@last_node + "." + a)
145
+ "_x.member?('#{n}') ? raise('#{n}') : #{a}#{POST}(_x + ['#{n}'])"
146
+ }.join(';')
147
+
148
+ code = "class #{@last_node}; def self.#{name}#{POST}(_x); #{checks}; end; end"
149
+
150
+ # pp code
151
+
152
+ @pm.module_eval(code)
153
+
154
+ begin
155
+ parse_call_attr(@last_node, name)
156
+ rescue RuntimeError
157
+ err(RecursionError, "'#{name}' is recursive")
158
+ end
159
+ end
160
+
161
+ def parse_define_param(name, spec)
162
+ parse_define_attr(name, spec)
163
+ @param_set.add(name)
164
+ end
165
+
166
+ def parse_class(class_name)
167
+ begin
168
+ # need the runtime module here (@m) since we need to
169
+ # introspect methods/attrs.
170
+ klass = @m.module_eval(class_name)
171
+ rescue NoMethodError, NameError
172
+ err(UndefinedError, "Can't find class: #{class_name}")
173
+ end
174
+
175
+ err(UndefinedError, "Access to non-class: #{class_name}") unless
176
+ klass.instance_of?(Class)
177
+
178
+ klass
179
+ end
180
+
181
+ def err(exc, msg)
182
+ raise exc.new(msg, @module_name, curr_line)
183
+ end
184
+
185
+ def parse_check_call_fn(fn, argcount, class_name=nil)
186
+ klass = class_name ? parse_class(class_name) : (@m::BaseClass)
187
+
188
+ err(UndefinedFunctionError, "Function #{fn} not found") unless
189
+ klass.methods.member? fn.to_sym
190
+
191
+ # signature methods must be named FUNCTION_NAME_SIG
192
+ sig = "#{fn}#{SIG}".upcase.to_sym
193
+
194
+ err(UndefinedFunctionError, "Signature #{sig} not found") unless
195
+ klass.constants.member? sig
196
+
197
+ min, max = klass.const_get(sig)
198
+
199
+ err(BadCallError, "Too many args to #{fn} (#{argcount} > #{max})") if
200
+ argcount > max
201
+
202
+ err(BadCallError, "Too few args to #{fn} (#{argcount} < #{min})") if
203
+ argcount < min
204
+ end
205
+
206
+ def parser
207
+ @@parser ||= DeloreanParser.new
208
+ end
209
+
210
+ def generate(t, sset=nil)
211
+ t.check(self, sset)
212
+
213
+ # generate ruby code
214
+ gen = t.rewrite(self)
215
+
216
+ # puts gen
217
+
218
+ begin
219
+ # evaluate generated code in @m
220
+ @m.module_eval(gen, "#{MOD}#{module_name}", curr_line)
221
+ rescue => exc
222
+ # bad ruby code generated, shoudn't happen
223
+ err(ParseError, "codegen error: " + exc.message)
224
+ end
225
+ end
226
+
227
+ def parse(source, sset=nil)
228
+ raise "can't call parse again without reset" if @pm
229
+
230
+ # @m module is used at runtime for code evaluation. @pm module
231
+ # is only used during parsing to check for errors.
232
+ @m, @pm = BaseModule.clone, Module.new
233
+
234
+ multi_line, @multi_no = nil, nil
235
+
236
+ source.each_line do |line|
237
+ @line_no += 1
238
+
239
+ # skip comments
240
+ next if line.match(/^\s*\#/)
241
+
242
+ # remove trailing blanks
243
+ line.rstrip!
244
+
245
+ next if line.length == 0
246
+
247
+ if multi_line
248
+ # Inside a multiline and next line doesn't look like a
249
+ # continuation => syntax error.
250
+ err(ParseError, "syntax error") unless line =~ /^\s+/
251
+
252
+ multi_line += line
253
+ t = parser.parse(multi_line)
254
+
255
+ if t
256
+ multi_line, @multi_no = nil, nil
257
+ generate(t, sset)
258
+ end
259
+
260
+ else
261
+ t = parser.parse(line)
262
+
263
+ if !t
264
+ err(ParseError, "syntax error") unless line =~ /^\s+/
265
+
266
+ multi_line = line
267
+ @multi_no = @line_no
268
+ else
269
+ generate(t, sset)
270
+ end
271
+ end
272
+ end
273
+
274
+ # left over multi_line
275
+ err(ParseError, "syntax error") if multi_line
276
+ end
277
+
278
+ ######################################################################
279
+ # Script development/testing
280
+ ######################################################################
281
+
282
+ # enumerate all nodes
283
+ def enumerate_nodes
284
+ n = SortedSet.new
285
+ @node_attrs.keys.each {|k| n.add(k) }
286
+ n
287
+ end
288
+
289
+ # enumerate qualified list of all attrs
290
+ def enumerate_attrs
291
+ enumerate_attrs_by_node(nil)
292
+ end
293
+
294
+ # enumerate qualified list of attrs by node (or all if nil is passed in)
295
+ def enumerate_attrs_by_node(node)
296
+ @node_attrs.keys.inject({}) { |h, n|
297
+ klass = @m.module_eval(n)
298
+ h[n] = klass.methods.map(&:to_s).select {|x| x.end_with?(POST)}.map {|x|
299
+ x.sub(/#{POST}$/, '')
300
+ } if node == n || node.nil?
301
+ h
302
+ }
303
+ end
304
+
305
+ # enumerate all params
306
+ def enumerate_params
307
+ @param_set
308
+ end
309
+
310
+ # enumerate params by a single node
311
+ def enumerate_params_by_node(node)
312
+ attrs = enumerate_attrs_by_node(node)
313
+ ps = Set.new
314
+ attrs.each_value {|v| v.map {|p| ps.add(p) if @param_set.include?(p.to_s)}}
315
+ ps
316
+ end
317
+
318
+ ######################################################################
319
+ # Runtime
320
+ ######################################################################
321
+
322
+ def evaluate(node, attr, params={})
323
+ evaluate_attrs(node, [attr], params)[0]
324
+ end
325
+
326
+ def evaluate_attrs(node, attrs, params={})
327
+ raise "bad node '#{node}'" unless node =~ /^[A-Z][a-zA-Z0-9_]*$/
328
+
329
+ begin
330
+ klass = @m.module_eval(node)
331
+ rescue NameError
332
+ err(UndefinedNodeError, "node #{node} is undefined")
333
+ end
334
+
335
+ params[:_engine] = self
336
+
337
+ attrs.map {|attr|
338
+ raise "bad attribute '#{attr}'" unless attr =~ /^[a-z][A-Za-z0-9_]*$/
339
+ klass.send("#{attr}#{POST}".to_sym, params)
340
+ }
341
+ end
342
+
343
+ # FIXME: should be renamed to grok_runtime_exception so as to not
344
+ # be confused with other parse_* calls which occur at parse time.
345
+ def parse_runtime_exception(exc)
346
+ # parse out the delorean-related backtrace records
347
+ bt = exc.backtrace.map{ |x|
348
+ x.match(/^#{MOD}(.+?):(\d+)(|:in `(.+)')$/);
349
+ $1 && [$1, $2.to_i, $4.sub(/#{POST}$/, '')]
350
+ }.reject(&:!)
351
+
352
+ [exc.message, bt]
353
+ end
354
+
355
+ ######################################################################
356
+
357
+ end
358
+
359
+ end