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.
- data/.gitignore +20 -0
- data/.rspec +1 -0
- data/Gemfile +4 -0
- data/LICENSE +22 -0
- data/README.md +145 -0
- data/Rakefile +2 -0
- data/delorean.gemspec +21 -0
- data/lib/delorean/base.rb +54 -0
- data/lib/delorean/container.rb +40 -0
- data/lib/delorean/delorean.rb +3157 -0
- data/lib/delorean/delorean.treetop +172 -0
- data/lib/delorean/engine.rb +359 -0
- data/lib/delorean/error.rb +45 -0
- data/lib/delorean/functions.rb +134 -0
- data/lib/delorean/model.rb +25 -0
- data/lib/delorean/nodes.rb +401 -0
- data/lib/delorean/version.rb +3 -0
- data/lib/delorean_lang.rb +12 -0
- data/spec/dev_spec.rb +98 -0
- data/spec/eval_spec.rb +609 -0
- data/spec/func_spec.rb +192 -0
- data/spec/parse_spec.rb +688 -0
- data/spec/spec_helper.rb +100 -0
- metadata +138 -0
@@ -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
|