eggshell 1.1.1 → 1.1.2
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.
- checksums.yaml +4 -4
- data/lib/eggshell/bundles/basics.rb +14 -5
- data/lib/eggshell/expression-evaluator.rb +305 -89
- data/lib/eggshell/expression-evaluator/lexer.rb +68 -66
- data/lib/eggshell/expression-evaluator/parser.rb +53 -10
- data/lib/eggshell/parse-tree.rb +3 -3
- data/lib/eggshell/processor.rb +15 -8
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d333cbc627c5af623979142276a316f4cc249026
|
4
|
+
data.tar.gz: b4abb4e1c883a743b702ae184790a96164b6a207
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: cf06dc8012c489ee0387f6338cab00067660f671b2aaf0aa7042a5ea1dc152c1ae4dc5eecdcb64232e1d7065ffdb70efde18ac21a58e5dff1cfeb735c9854fa0
|
7
|
+
data.tar.gz: a1bb8fee382b92f2f8788a80c44fd094634d28db437e3f0a9c305017f98e3e2570ef2488131bb1d0fa10682b57e3552304061218413d82501fac6009e14e0741
|
@@ -64,9 +64,8 @@ module Eggshell::Bundles::Basic
|
|
64
64
|
html = line.match(HTML_BLOCK)
|
65
65
|
if html
|
66
66
|
@block_type = 'html_block'
|
67
|
-
|
68
67
|
end_html = HTML_BLOCK_END["<#{html[1]}"]
|
69
|
-
end_html = "</#{html[1]}
|
68
|
+
end_html = "</#{html[1]}>" if !end_html
|
70
69
|
if line.match(end_html)
|
71
70
|
return BH::DONE
|
72
71
|
end
|
@@ -387,8 +386,14 @@ module Eggshell::Bundles::Basic
|
|
387
386
|
end
|
388
387
|
|
389
388
|
lines.each do |line_obj|
|
390
|
-
line =
|
391
|
-
indent =
|
389
|
+
line = nil
|
390
|
+
indent = 0
|
391
|
+
if line_obj.is_a?(Eggshell::Line)
|
392
|
+
line = line_obj.line
|
393
|
+
indent = line_obj.indent_lvl
|
394
|
+
else
|
395
|
+
line = line_obj
|
396
|
+
end
|
392
397
|
ltype = line[0] == '-' ? 'ul' : 'ol'
|
393
398
|
line = line[1..line.length].strip
|
394
399
|
|
@@ -663,6 +668,10 @@ module Eggshell::Bundles::Basic
|
|
663
668
|
end
|
664
669
|
end
|
665
670
|
|
671
|
+
def collection_type(macro)
|
672
|
+
macro == 'raw' ? MH::COLLECT_RAW_MACRO : MH::COLLECT_NORMAL
|
673
|
+
end
|
674
|
+
|
666
675
|
def process(name, args, lines, out, call_depth = 0)
|
667
676
|
if name == '=' || name == 'var'
|
668
677
|
# @todo expand args[0]?
|
@@ -709,7 +718,7 @@ module Eggshell::Bundles::Basic
|
|
709
718
|
out << @eggshell.assemble(unit, call_depth + 1)
|
710
719
|
end
|
711
720
|
else
|
712
|
-
out <<
|
721
|
+
out << unit
|
713
722
|
end
|
714
723
|
end
|
715
724
|
elsif name == 'pipe'
|
@@ -15,11 +15,26 @@ module Eggshell; class ExpressionEvaluator
|
|
15
15
|
"\\'" => "'",
|
16
16
|
'\\"' => '"'
|
17
17
|
}.freeze
|
18
|
+
|
19
|
+
OP_ASSIGN = '='.to_sym
|
20
|
+
OP_EQQ = '==='.to_sym
|
21
|
+
OP_EQ = '=='.to_sym
|
22
|
+
OP_NEQ = '!='.to_sym
|
23
|
+
OP_MATCH = '=~'.to_sym
|
24
|
+
OP_NMATCH = '!=~'.to_sym
|
25
|
+
OP_MULTIPLY = '*'.to_sym
|
26
|
+
OP_DIVIDE = '/'.to_sym
|
27
|
+
OP_ADD = '+'.to_sym
|
28
|
+
OP_SUBTRACT = '-'.to_sym
|
18
29
|
|
30
|
+
# Values give corresponding order of precedence
|
19
31
|
OPERATOR_MAP = {
|
20
|
-
|
21
|
-
|
22
|
-
|
32
|
+
OP_ASSIGN => 1000,
|
33
|
+
OP_EQQ => 999,
|
34
|
+
OP_EQ => 998,
|
35
|
+
OP_NEQ => 998,
|
36
|
+
OP_MATCH => 997,
|
37
|
+
OP_NMATCH => 997,
|
23
38
|
'<'.to_sym => 990,
|
24
39
|
'<='.to_sym => 990,
|
25
40
|
'>'.to_sym => 990,
|
@@ -31,12 +46,12 @@ module Eggshell; class ExpressionEvaluator
|
|
31
46
|
'~'.to_sym => 200,
|
32
47
|
'&'.to_sym => 200,
|
33
48
|
'|'.to_sym => 199,
|
34
|
-
|
35
|
-
|
49
|
+
OP_MULTIPLY => 150,
|
50
|
+
OP_DIVIDE => 150,
|
36
51
|
'%'.to_sym => 150,
|
37
52
|
'^'.to_sym => 150,
|
38
|
-
|
39
|
-
|
53
|
+
OP_ADD => 100,
|
54
|
+
OP_SUBTRACT => 100,
|
40
55
|
'&&'.to_sym => 99,
|
41
56
|
'||'.to_sym => 99
|
42
57
|
}.freeze
|
@@ -47,6 +62,8 @@ module Eggshell; class ExpressionEvaluator
|
|
47
62
|
@cache = {}
|
48
63
|
@parser = Parser::DefaultParser.new
|
49
64
|
#@evaluator = Evaluator.new(@vars, @funcs)
|
65
|
+
@func_whitelist = {}
|
66
|
+
@func_wl_alias = {}
|
50
67
|
end
|
51
68
|
|
52
69
|
# Maps 1 or more virtual function names to a handler.
|
@@ -75,42 +92,147 @@ module Eggshell; class ExpressionEvaluator
|
|
75
92
|
@funcs["#{ns}:"] = handler
|
76
93
|
end
|
77
94
|
end
|
95
|
+
|
96
|
+
# Registers 1+ getters and/or setters for a given class/module, avoiding
|
97
|
+
# need to alias or proxy methods to use `get_` and `set_` names.
|
98
|
+
def register_function_whitelist(clz, getters, setters = nil)
|
99
|
+
if !clz.is_a?(String)
|
100
|
+
clz = clz.name if clz.is_a?(Class)
|
101
|
+
#clz = clz.name if clz.is_a?(Class)
|
102
|
+
end
|
103
|
+
@func_whitelist[clz] = {:get => {}, :set => {}}
|
104
|
+
if getters.is_a?(Array)
|
105
|
+
getters.each do |get|
|
106
|
+
@func_whitelist[clz][:get][get] = true
|
107
|
+
end
|
108
|
+
end
|
109
|
+
if setters.is_a?(Array)
|
110
|
+
setters.each do |set|
|
111
|
+
@func_whitelist[clz][:set][set] = true
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
# The function whitelist expects a given object's class to be a direct
|
117
|
+
# mapping into the whitelist. Use this method to register all super classes
|
118
|
+
# and interfaces of the given object that can be looked up if a mapping
|
119
|
+
# fails.
|
120
|
+
def register_function_alias(obj)
|
121
|
+
clz = obj.is_a?(Class) ? obj : obj.class
|
122
|
+
ptr = []
|
123
|
+
clz.ancestors.each do |anc|
|
124
|
+
next if anc == clz
|
125
|
+
ptr << anc.name
|
126
|
+
end
|
127
|
+
@func_wl_alias[clz.name] = ptr
|
128
|
+
end
|
129
|
+
|
130
|
+
def resolve_function_alias(obj)
|
131
|
+
clz = obj.is_a?(Class) ? obj.name : obj.class.name
|
132
|
+
return @func_wl_alias[clz] || []
|
133
|
+
end
|
134
|
+
|
135
|
+
def has_function_alias(obj, func_name, type = :get)
|
136
|
+
clz = obj.is_a?(Class) ? obj.name : obj.class.name
|
137
|
+
if @func_whitelist[clz] && @func_whitelist[clz][type][func_name]
|
138
|
+
return true
|
139
|
+
end
|
140
|
+
|
141
|
+
ret = false
|
142
|
+
if @func_wl_alias[clz]
|
143
|
+
@func_wl_alias[clz].each do |ali|
|
144
|
+
ali = @func_whitelist[ali]
|
145
|
+
if ali && ali[type][func_name]
|
146
|
+
ret = true
|
147
|
+
break
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
return ret
|
153
|
+
end
|
154
|
+
|
155
|
+
def get_function_aliases
|
156
|
+
ret = []
|
157
|
+
@func_whitelist.each do |clz, map|
|
158
|
+
ret << clz
|
159
|
+
ret << "\tgetters: #{map[:get].keys.join(', ')}"
|
160
|
+
ret << "\tsetters: #{map[:set].keys.join(', ')}"
|
161
|
+
end
|
162
|
+
@func_wl_alias.each do |clz, arr|
|
163
|
+
ret << clz
|
164
|
+
arr.each do |ali|
|
165
|
+
star = @func_whitelist[ali] ? ' *' : ''
|
166
|
+
ret << "\t=> #{ali}#{star}"
|
167
|
+
end
|
168
|
+
end
|
169
|
+
ret
|
170
|
+
end
|
78
171
|
|
79
172
|
attr_reader :vars, :funcs, :parser
|
80
173
|
|
81
174
|
def parse(statement, cache = true)
|
82
|
-
|
83
|
-
|
175
|
+
if cache
|
176
|
+
parsed = @cache[statement]
|
177
|
+
return parsed if parsed
|
178
|
+
end
|
84
179
|
|
85
|
-
|
86
|
-
@cache[statement]
|
87
|
-
return parsed
|
180
|
+
@cache[statement] = @parser.parse(statement)
|
181
|
+
return @cache[statement].clone
|
88
182
|
end
|
89
183
|
|
90
184
|
def evaluate(statement, do_parse = true, cache = true, vtable = nil, ftable = nil)
|
91
185
|
vtable = @vars if !vtable.is_a?(Hash)
|
92
186
|
ftable = @funcs if !ftable.is_a?(Hash)
|
93
187
|
parsed = statement
|
94
|
-
|
188
|
+
#$stderr.write "!!! #{vtable.inspect} // #{@vars.inspect} !!!\n"
|
189
|
+
#$stderr.write "parsed = #{parsed.inspect}||vtable=#{vtable.inspect}\n"
|
95
190
|
if !statement.is_a?(Array)
|
96
191
|
return statement if !do_parse || !statement.is_a?(String)
|
97
192
|
parsed = parse(statement, cache)
|
193
|
+
elsif parsed[0].is_a?(Symbol)
|
194
|
+
parsed = [parsed]
|
195
|
+
#$stderr.write "^^ fixed up\n"
|
98
196
|
end
|
99
197
|
|
100
198
|
ret = nil
|
101
199
|
parsed.each do |frag|
|
102
200
|
ftype = frag[0]
|
103
201
|
if ftype == :op
|
104
|
-
|
105
|
-
z =
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
202
|
+
# [:op, [operand, operator, operand(, operator, operand, ...)]]
|
203
|
+
# z contains the start index of the operator-operand list. if the first operator is '=',
|
204
|
+
# reserve first operand as var reference and evaluate everything after '=' before storing
|
205
|
+
# into var ref
|
206
|
+
z = 0
|
207
|
+
oplist = frag[1]
|
208
|
+
if oplist[1] == OP_ASSIGN
|
209
|
+
if !oplist[0].is_a?(Array) || oplist[0][0] != :var
|
210
|
+
raise Exception.new("Illegal assignment to non-variable: #{frag[1][0].inspect}")
|
211
|
+
end
|
212
|
+
z = 2
|
213
|
+
end
|
214
|
+
|
215
|
+
op_val = oplist[z]
|
216
|
+
if op_val.is_a?(Array)
|
217
|
+
op_val = op_val[0] == :var ? var_access(op_val) : evaluate([oplist[z]], false)
|
218
|
+
end
|
219
|
+
|
220
|
+
z += 1
|
221
|
+
while z < oplist.length
|
222
|
+
op = oplist[z]
|
223
|
+
rop = oplist[z+1]
|
224
|
+
if rop.is_a?(Array)
|
225
|
+
rop = rop[0] == :var ? var_access(rop) : evaluate([rop])
|
226
|
+
end
|
227
|
+
op_val = self.class.op_eval(op_val, op.to_sym, rop)
|
111
228
|
z += 2
|
112
229
|
end
|
113
230
|
ret = op_val
|
231
|
+
|
232
|
+
# var assignment; return true for successful set or false otherwise
|
233
|
+
if oplist[1] == OP_ASSIGN
|
234
|
+
ret = var_access(oplist[0], nil, ret)
|
235
|
+
end
|
114
236
|
elsif ftype == :op_tern
|
115
237
|
cond = frag[1].is_a?(Array) ? evaluate(frag[1]) : frag[1]
|
116
238
|
if cond
|
@@ -147,7 +269,8 @@ module Eggshell; class ExpressionEvaluator
|
|
147
269
|
ret = handler.exec_func(name, frag[2])
|
148
270
|
end
|
149
271
|
elsif ftype == :var
|
150
|
-
|
272
|
+
#$stderr.write "<<< VAR\n"
|
273
|
+
ret = var_access(frag, vtable)[0]
|
151
274
|
elsif ftype == :group
|
152
275
|
ret = evaluate(frag[1], false, cache, vtable)
|
153
276
|
elsif ftype == :array
|
@@ -179,121 +302,178 @@ module Eggshell; class ExpressionEvaluator
|
|
179
302
|
ret
|
180
303
|
end
|
181
304
|
|
182
|
-
# @todo have one set for strings, one set for numbers, and a general set for everything else
|
183
|
-
# @todo what to do with unsupported ops?
|
184
305
|
def self.op_eval(lop, op, rop)
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
306
|
+
#$stderr.write "op_eval:\n\t#{lop.inspect} #{op.inspect} #{rop.inspect}\n"
|
307
|
+
if (lop.is_a?(Numeric) && rop.is_a?(Numeric))
|
308
|
+
if op == OP_MATCH || op == OP_NMATCH
|
309
|
+
raise Exception.new("'#{op}' can only be used with strings -- #{lop} #{op} #{rop}")
|
310
|
+
end
|
311
|
+
return lop.send(op, rop)
|
312
|
+
elsif op == OP_EQQ
|
313
|
+
return lop === rop
|
314
|
+
elsif rop == :empty && (op == OP_EQ || op == OP_NEQ)
|
315
|
+
# essentially (lop == false || lop == nil || lop == '') [==|!=] true
|
316
|
+
# @todo other conditions to check empty?
|
317
|
+
return (lop == false || lop == nil || lop == '').send(op, true)
|
318
|
+
elsif lop.is_a?(String)
|
319
|
+
e = nil
|
320
|
+
if op == OP_MULTIPLY
|
321
|
+
if rop.is_a?(Numeric)
|
322
|
+
return lop * rop
|
323
|
+
else
|
324
|
+
e = "multiply string with numeric, #{rop.class} given"
|
325
|
+
end
|
326
|
+
elsif op == OP_MATCH || op == OP_NMATCH
|
327
|
+
if rop.is_a?(String)
|
328
|
+
m = lop.match(rop)
|
329
|
+
if op == OP_NMATCH
|
330
|
+
m = m == nil ? true : false
|
331
|
+
end
|
332
|
+
return m
|
333
|
+
else
|
334
|
+
e = "'#{op}' expects right operand to be string, #{rop.class} given"
|
335
|
+
end
|
336
|
+
elsif op == OP_ADD
|
195
337
|
return lop + rop
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
338
|
+
end
|
339
|
+
|
340
|
+
raise Exception.new(e) if e
|
341
|
+
elsif lop.is_a?(Array)
|
342
|
+
if op == OP_MULTIPLY && rop.is_a?(Numeric)
|
201
343
|
return lop * rop
|
202
|
-
|
203
|
-
return lop
|
204
|
-
when '>>'
|
205
|
-
return lop >> rop
|
206
|
-
when '<'
|
207
|
-
return lop < rop
|
208
|
-
when '<='
|
209
|
-
return lop <= rop
|
210
|
-
when '>'
|
211
|
-
return lop > rop
|
212
|
-
when '>='
|
213
|
-
return lop >= rop
|
214
|
-
when '|'
|
215
|
-
return lop | rop
|
216
|
-
when '||'
|
217
|
-
return lop || rop
|
218
|
-
when '&'
|
219
|
-
return lop & rop
|
220
|
-
when '&&'
|
221
|
-
return lop && rop
|
222
|
-
when '%'
|
223
|
-
return lop % rop
|
224
|
-
when '^'
|
225
|
-
return lop ^ rop
|
226
|
-
when '='
|
227
|
-
# @todo assign rop to lop
|
344
|
+
elsif (op == OP_ADD || op == OP_SUBTRACT) && rop.is_a?(Array)
|
345
|
+
return lop.send(op, rop)
|
228
346
|
end
|
347
|
+
raise Exception.new("unsupported operation: #{lop.class} #{op} #{rop.class}")
|
229
348
|
else
|
230
349
|
|
231
350
|
end
|
232
351
|
end
|
233
352
|
|
234
|
-
|
353
|
+
# Gets or sets a variable reference. If reference is a string, an exact match is looked for
|
354
|
+
# in vars table, otherwise an array (complex reference) is expected. If a value is given
|
355
|
+
# in the `set_var` parameter, an attempt is made to assign the value (provided there's
|
356
|
+
# a suitable method to handle setting).
|
357
|
+
#
|
358
|
+
# For a complex variable reference, iteratively chains a parent var with a child variable
|
359
|
+
# identifier. The return value is an array that returns the resolved value, its parent var,
|
360
|
+
# and the final identifier. In the case of setting, the resolved value is {{true}} if a setter
|
361
|
+
# was found and {{false}} otherwise.
|
362
|
+
#
|
363
|
+
# @param String|Array var If a string, an exact match is attempted in vars table,
|
364
|
+
# otherwise an array with a parsed variable expression is expected.
|
365
|
+
# @param Hash vtable Optional vars table. If `nil`, uses `@vars`.
|
366
|
+
# @param Object set_var If not equivalent to `:nope`, assumes some sort of assignment.
|
367
|
+
# A symbol is used as a check since symbols are not supported in expressions.
|
368
|
+
# @return Array A 3-element array in the following format: `[value, parent_var, child_var_ident]
|
369
|
+
def var_access(var, vtable = nil, set_var = :nope)
|
235
370
|
vtable = @vars if !vtable
|
236
371
|
if var.is_a?(String)
|
372
|
+
return :empty if var == 'empty'
|
237
373
|
if var.match(/^[a-zA-Z_][a-zA-Z0-9_\.]*$/)
|
238
|
-
|
374
|
+
if vtable.has_key?(var)
|
375
|
+
if set_var == :nope
|
376
|
+
return [vtable[var], nil, nil]
|
377
|
+
else
|
378
|
+
vtable[var] = set_var
|
379
|
+
return [true, nil, nil]
|
380
|
+
end
|
381
|
+
end
|
239
382
|
end
|
240
383
|
var = @parser.parse(var)
|
241
384
|
end
|
385
|
+
|
386
|
+
return :empty if var[1] == 'empty'
|
242
387
|
|
243
388
|
ptr_lst = nil
|
244
389
|
ptr = vtable
|
245
|
-
|
246
390
|
i = 0
|
247
391
|
parts = var
|
248
|
-
|
249
|
-
while (i < parts.length)
|
392
|
+
set_point = set_var == :nope ? parts.length + 1 : parts.length - 1
|
393
|
+
while (i < parts.length && ptr != nil)
|
250
394
|
part = parts[i]
|
251
395
|
ptr_lst = ptr
|
252
396
|
if part == :var
|
253
397
|
key = parts[i+1]
|
254
|
-
if ptr.is_a?(Hash)
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
398
|
+
if ptr.is_a?(Hash)
|
399
|
+
if i == set_point
|
400
|
+
ptr[key] = set_var
|
401
|
+
ptr = true
|
402
|
+
elsif ptr[key]
|
403
|
+
ptr = ptr[key]
|
404
|
+
if ptr != nil
|
405
|
+
i += 2
|
406
|
+
next
|
407
|
+
end
|
259
408
|
else
|
260
|
-
|
409
|
+
ptr = nil
|
261
410
|
end
|
262
411
|
else
|
263
412
|
ptr = nil
|
264
|
-
break
|
265
413
|
end
|
266
414
|
elsif part[0] == :index_access
|
267
415
|
idx = part[1]
|
268
416
|
if idx.is_a?(Array)
|
269
|
-
idx =
|
417
|
+
idx = var_access(idx)[0]
|
270
418
|
if idx == nil
|
271
419
|
ptr = nil
|
272
420
|
break
|
273
421
|
end
|
274
422
|
end
|
275
|
-
if (ptr.is_a?(Array) || ptr.is_a?(Hash))
|
276
|
-
|
423
|
+
if (ptr.is_a?(Array) || ptr.is_a?(Hash))
|
424
|
+
if i == set_point
|
425
|
+
ptr[idx] = set_var
|
426
|
+
ptr = true
|
427
|
+
else
|
428
|
+
ptr = ptr[idx]
|
429
|
+
end
|
277
430
|
else
|
278
431
|
ptr = nil
|
279
|
-
break
|
280
432
|
end
|
281
433
|
elsif part[0] == :member_access
|
282
|
-
|
283
|
-
|
284
|
-
|
434
|
+
if ptr.is_a?(Hash)
|
435
|
+
if i == set_point
|
436
|
+
ptr[part[1]] = set_var
|
437
|
+
ptr = true
|
438
|
+
else
|
439
|
+
ptr = ptr[part[1]]
|
440
|
+
end
|
285
441
|
else
|
286
|
-
|
287
|
-
|
442
|
+
prefix = 'get_'
|
443
|
+
type = :get
|
444
|
+
if i == set_point
|
445
|
+
prefix = 'set_'
|
446
|
+
type = :set
|
447
|
+
end
|
448
|
+
|
449
|
+
mname = (prefix + part[1]).to_sym
|
450
|
+
tgt = nil
|
451
|
+
|
452
|
+
if ptr.respond_to?(mname)
|
453
|
+
tgt = mname
|
454
|
+
elsif has_function_alias(ptr, part[1], type)
|
455
|
+
tgt = part[1].to_sym
|
456
|
+
end
|
457
|
+
if tgt
|
458
|
+
if type == :set
|
459
|
+
ptr.send(tgt, set_var)
|
460
|
+
ptr = true
|
461
|
+
else
|
462
|
+
ptr = ptr.send(tgt)
|
463
|
+
end
|
464
|
+
else
|
465
|
+
ptr = nil
|
466
|
+
end
|
288
467
|
end
|
289
468
|
elsif part[0] == :func
|
469
|
+
# @todo throw exception if this is set_point?
|
470
|
+
# @todo what happens when we have `var[something](fn_args)`
|
290
471
|
ptr = evaluate(part, false)
|
291
|
-
break if ptr == nil
|
292
472
|
end
|
293
473
|
i += 1
|
294
474
|
end
|
295
|
-
|
296
|
-
return do_ptr ? ptr_lst : ptr
|
475
|
+
return [ptr, ptr_lst, parts[-1]]
|
476
|
+
#return do_ptr ? ptr_lst : ptr
|
297
477
|
end
|
298
478
|
|
299
479
|
def var_set(var, val)
|
@@ -306,4 +486,40 @@ end; end
|
|
306
486
|
|
307
487
|
require_relative './expression-evaluator/lexer.rb'
|
308
488
|
require_relative './expression-evaluator/parser.rb'
|
309
|
-
require_relative './expression-evaluator/evaluator.rb'
|
489
|
+
require_relative './expression-evaluator/evaluator.rb'
|
490
|
+
|
491
|
+
# --- move this to a test! --
|
492
|
+
# class AClass < String
|
493
|
+
# include Eggshell
|
494
|
+
|
495
|
+
# def normal_getter()
|
496
|
+
# end
|
497
|
+
|
498
|
+
# def another_method()
|
499
|
+
# end
|
500
|
+
|
501
|
+
# def normal_setter(val)
|
502
|
+
# end
|
503
|
+
|
504
|
+
# def get_test()
|
505
|
+
# end
|
506
|
+
# end
|
507
|
+
|
508
|
+
# class BClass < AClass
|
509
|
+
# def scroopy_noopers
|
510
|
+
# end
|
511
|
+
# end
|
512
|
+
|
513
|
+
# ee = Eggshell::ExpressionEvaluator.new
|
514
|
+
# ee.register_function_whitelist(String, ['length'])
|
515
|
+
# ee.register_function_whitelist(AClass, ['normal_getter'], ['normal_setter'])
|
516
|
+
# ee.register_function_alias(AClass)
|
517
|
+
# ee.register_function_alias(BClass)
|
518
|
+
# aclass = AClass.new
|
519
|
+
# bclass = BClass.new
|
520
|
+
|
521
|
+
# puts "aclass.length ? #{ee.has_function_alias(aclass, 'length')}"
|
522
|
+
# puts "aclass.normal_setter ? #{ee.has_function_alias(aclass, 'normal_setter', :set)}"
|
523
|
+
# puts "bclass.normal_getter ? #{ee.has_function_alias(bclass, 'normal_getter', :get)}"
|
524
|
+
# puts "bclass.another_method ? #{ee.has_function_alias(aclass, 'another_method', :get)}"
|
525
|
+
# puts ee.get_function_aliases.join("\n")
|
@@ -1,7 +1,7 @@
|
|
1
1
|
|
2
|
-
# line 1 "lexer.ragel"
|
2
|
+
# line 1 "lib/eggshell/expression-evaluator/lexer.ragel"
|
3
3
|
|
4
|
-
# line 71 "lexer.ragel"
|
4
|
+
# line 71 "lib/eggshell/expression-evaluator/lexer.ragel"
|
5
5
|
|
6
6
|
# %
|
7
7
|
|
@@ -11,7 +11,7 @@ class Eggshell::ExpressionEvaluator::Lexer
|
|
11
11
|
def initialize(parser)
|
12
12
|
@parser = parser
|
13
13
|
|
14
|
-
# line 15 "lexer.rb"
|
14
|
+
# line 15 "lib/eggshell/expression-evaluator/lexer.rb"
|
15
15
|
class << self
|
16
16
|
attr_accessor :_eggshell_actions
|
17
17
|
private :_eggshell_actions, :_eggshell_actions=
|
@@ -30,8 +30,8 @@ class << self
|
|
30
30
|
private :_eggshell_key_offsets, :_eggshell_key_offsets=
|
31
31
|
end
|
32
32
|
self._eggshell_key_offsets = [
|
33
|
-
0, 2,
|
34
|
-
|
33
|
+
0, 2, 39, 40, 41, 48, 49, 51,
|
34
|
+
54, 56, 57, 59, 61, 62, 64, 74
|
35
35
|
]
|
36
36
|
|
37
37
|
class << self
|
@@ -39,15 +39,16 @@ class << self
|
|
39
39
|
private :_eggshell_trans_keys, :_eggshell_trans_keys=
|
40
40
|
end
|
41
41
|
self._eggshell_trans_keys = [
|
42
|
-
48, 57, 13, 32, 34, 36, 38,
|
43
|
-
43, 44, 45, 46, 58, 59, 60,
|
44
|
-
62, 63, 64, 91, 92, 93, 94,
|
45
|
-
9, 10, 40, 41, 42, 47, 48,
|
46
|
-
65, 95, 97, 122, 123, 125,
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
42
|
+
48, 57, 13, 32, 33, 34, 36, 38,
|
43
|
+
39, 43, 44, 45, 46, 58, 59, 60,
|
44
|
+
61, 62, 63, 64, 91, 92, 93, 94,
|
45
|
+
124, 9, 10, 40, 41, 42, 47, 48,
|
46
|
+
57, 65, 95, 97, 122, 123, 125, 61,
|
47
|
+
126, 95, 48, 57, 65, 90, 97, 122,
|
48
|
+
38, 48, 57, 46, 48, 57, 48, 57,
|
49
|
+
58, 60, 61, 61, 126, 61, 61, 62,
|
50
|
+
34, 91, 93, 110, 114, 116, 123, 125,
|
51
|
+
39, 41, 124, 0
|
51
52
|
]
|
52
53
|
|
53
54
|
class << self
|
@@ -55,8 +56,8 @@ class << self
|
|
55
56
|
private :_eggshell_single_lengths, :_eggshell_single_lengths=
|
56
57
|
end
|
57
58
|
self._eggshell_single_lengths = [
|
58
|
-
0,
|
59
|
-
0, 1, 1, 0, 8, 1
|
59
|
+
0, 23, 1, 1, 1, 1, 0, 1,
|
60
|
+
0, 1, 0, 2, 1, 0, 8, 1
|
60
61
|
]
|
61
62
|
|
62
63
|
class << self
|
@@ -64,8 +65,8 @@ class << self
|
|
64
65
|
private :_eggshell_range_lengths, :_eggshell_range_lengths=
|
65
66
|
end
|
66
67
|
self._eggshell_range_lengths = [
|
67
|
-
1, 7,
|
68
|
-
1, 0, 0, 1, 1, 0
|
68
|
+
1, 7, 0, 0, 3, 0, 1, 1,
|
69
|
+
1, 0, 1, 0, 0, 1, 1, 0
|
69
70
|
]
|
70
71
|
|
71
72
|
class << self
|
@@ -73,8 +74,8 @@ class << self
|
|
73
74
|
private :_eggshell_index_offsets, :_eggshell_index_offsets=
|
74
75
|
end
|
75
76
|
self._eggshell_index_offsets = [
|
76
|
-
0, 2,
|
77
|
-
|
77
|
+
0, 2, 33, 35, 37, 42, 44, 46,
|
78
|
+
49, 51, 53, 55, 58, 60, 62, 72
|
78
79
|
]
|
79
80
|
|
80
81
|
class << self
|
@@ -82,15 +83,16 @@ class << self
|
|
82
83
|
private :_eggshell_indicies, :_eggshell_indicies=
|
83
84
|
end
|
84
85
|
self._eggshell_indicies = [
|
85
|
-
1, 0, 3, 3, 4, 5, 6,
|
86
|
-
|
87
|
-
15,
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
86
|
+
1, 0, 3, 3, 4, 5, 6, 7,
|
87
|
+
5, 10, 11, 10, 11, 13, 11, 14,
|
88
|
+
15, 16, 9, 17, 18, 19, 18, 9,
|
89
|
+
21, 3, 8, 9, 12, 6, 6, 20,
|
90
|
+
2, 23, 22, 9, 24, 6, 6, 6,
|
91
|
+
6, 25, 9, 22, 12, 24, 27, 12,
|
92
|
+
26, 1, 28, 17, 29, 9, 24, 30,
|
93
|
+
9, 24, 9, 24, 9, 24, 31, 31,
|
94
|
+
31, 31, 31, 31, 31, 31, 31, 22,
|
95
|
+
9, 22, 0
|
94
96
|
]
|
95
97
|
|
96
98
|
class << self
|
@@ -98,10 +100,10 @@ class << self
|
|
98
100
|
private :_eggshell_trans_targs, :_eggshell_trans_targs=
|
99
101
|
end
|
100
102
|
self._eggshell_trans_targs = [
|
101
|
-
1,
|
102
|
-
1,
|
103
|
-
1, 1,
|
104
|
-
1, 0, 1, 1,
|
103
|
+
1, 8, 1, 1, 2, 1, 4, 5,
|
104
|
+
1, 1, 6, 1, 7, 9, 10, 11,
|
105
|
+
13, 1, 1, 14, 1, 15, 1, 3,
|
106
|
+
1, 1, 1, 0, 1, 1, 12, 1
|
105
107
|
]
|
106
108
|
|
107
109
|
class << self
|
@@ -109,10 +111,10 @@ class << self
|
|
109
111
|
private :_eggshell_trans_actions, :_eggshell_trans_actions=
|
110
112
|
end
|
111
113
|
self._eggshell_trans_actions = [
|
112
|
-
39, 0, 25, 23, 15, 0, 0,
|
113
|
-
7, 0, 19, 5, 0, 0, 0,
|
114
|
-
17, 9, 0, 11, 0,
|
115
|
-
27, 0, 29, 35, 0, 21
|
114
|
+
39, 0, 25, 23, 0, 15, 0, 0,
|
115
|
+
13, 7, 0, 19, 5, 0, 0, 0,
|
116
|
+
0, 17, 9, 0, 11, 0, 37, 0,
|
117
|
+
33, 31, 27, 0, 29, 35, 0, 21
|
116
118
|
]
|
117
119
|
|
118
120
|
class << self
|
@@ -121,7 +123,7 @@ class << self
|
|
121
123
|
end
|
122
124
|
self._eggshell_to_state_actions = [
|
123
125
|
0, 1, 0, 0, 0, 0, 0, 0,
|
124
|
-
0, 0, 0, 0, 0, 0
|
126
|
+
0, 0, 0, 0, 0, 0, 0, 0
|
125
127
|
]
|
126
128
|
|
127
129
|
class << self
|
@@ -130,7 +132,7 @@ class << self
|
|
130
132
|
end
|
131
133
|
self._eggshell_from_state_actions = [
|
132
134
|
0, 3, 0, 0, 0, 0, 0, 0,
|
133
|
-
0, 0, 0, 0, 0, 0
|
135
|
+
0, 0, 0, 0, 0, 0, 0, 0
|
134
136
|
]
|
135
137
|
|
136
138
|
class << self
|
@@ -138,8 +140,8 @@ class << self
|
|
138
140
|
private :_eggshell_eof_trans, :_eggshell_eof_trans=
|
139
141
|
end
|
140
142
|
self._eggshell_eof_trans = [
|
141
|
-
1, 0,
|
142
|
-
|
143
|
+
1, 0, 23, 25, 26, 23, 25, 27,
|
144
|
+
29, 30, 25, 25, 25, 25, 23, 23
|
143
145
|
]
|
144
146
|
|
145
147
|
class << self
|
@@ -161,7 +163,7 @@ end
|
|
161
163
|
self.eggshell_en_main = 1;
|
162
164
|
|
163
165
|
|
164
|
-
# line 80 "lexer.ragel"
|
166
|
+
# line 80 "lib/eggshell/expression-evaluator/lexer.ragel"
|
165
167
|
# %
|
166
168
|
end
|
167
169
|
|
@@ -169,7 +171,7 @@ self.eggshell_en_main = 1;
|
|
169
171
|
data = source.is_a?(String) ? source.unpack("c*") : source
|
170
172
|
eof = data.length
|
171
173
|
|
172
|
-
# line
|
174
|
+
# line 175 "lib/eggshell/expression-evaluator/lexer.rb"
|
173
175
|
begin
|
174
176
|
p ||= 0
|
175
177
|
pe ||= data.length
|
@@ -179,9 +181,9 @@ begin
|
|
179
181
|
act = 0
|
180
182
|
end
|
181
183
|
|
182
|
-
# line 87 "lexer.ragel"
|
184
|
+
# line 87 "lib/eggshell/expression-evaluator/lexer.ragel"
|
183
185
|
|
184
|
-
# line
|
186
|
+
# line 187 "lib/eggshell/expression-evaluator/lexer.rb"
|
185
187
|
begin
|
186
188
|
_klen, _trans, _keys, _acts, _nacts = nil
|
187
189
|
_goto_level = 0
|
@@ -211,7 +213,7 @@ begin
|
|
211
213
|
begin
|
212
214
|
ts = p
|
213
215
|
end
|
214
|
-
# line
|
216
|
+
# line 217 "lib/eggshell/expression-evaluator/lexer.rb"
|
215
217
|
end # from state action switch
|
216
218
|
end
|
217
219
|
if _trigger_goto
|
@@ -284,7 +286,7 @@ when 2 then
|
|
284
286
|
te = p+1
|
285
287
|
end
|
286
288
|
when 3 then
|
287
|
-
# line 31 "lexer.ragel"
|
289
|
+
# line 31 "lib/eggshell/expression-evaluator/lexer.ragel"
|
288
290
|
begin
|
289
291
|
te = p+1
|
290
292
|
begin
|
@@ -292,7 +294,7 @@ te = p+1
|
|
292
294
|
end
|
293
295
|
end
|
294
296
|
when 4 then
|
295
|
-
# line 35 "lexer.ragel"
|
297
|
+
# line 35 "lib/eggshell/expression-evaluator/lexer.ragel"
|
296
298
|
begin
|
297
299
|
te = p+1
|
298
300
|
begin
|
@@ -300,7 +302,7 @@ te = p+1
|
|
300
302
|
end
|
301
303
|
end
|
302
304
|
when 5 then
|
303
|
-
# line 39 "lexer.ragel"
|
305
|
+
# line 39 "lib/eggshell/expression-evaluator/lexer.ragel"
|
304
306
|
begin
|
305
307
|
te = p+1
|
306
308
|
begin
|
@@ -308,7 +310,7 @@ te = p+1
|
|
308
310
|
end
|
309
311
|
end
|
310
312
|
when 6 then
|
311
|
-
# line 43 "lexer.ragel"
|
313
|
+
# line 43 "lib/eggshell/expression-evaluator/lexer.ragel"
|
312
314
|
begin
|
313
315
|
te = p+1
|
314
316
|
begin
|
@@ -316,7 +318,7 @@ te = p+1
|
|
316
318
|
end
|
317
319
|
end
|
318
320
|
when 7 then
|
319
|
-
# line 47 "lexer.ragel"
|
321
|
+
# line 47 "lib/eggshell/expression-evaluator/lexer.ragel"
|
320
322
|
begin
|
321
323
|
te = p+1
|
322
324
|
begin
|
@@ -324,7 +326,7 @@ te = p+1
|
|
324
326
|
end
|
325
327
|
end
|
326
328
|
when 8 then
|
327
|
-
# line 51 "lexer.ragel"
|
329
|
+
# line 51 "lib/eggshell/expression-evaluator/lexer.ragel"
|
328
330
|
begin
|
329
331
|
te = p+1
|
330
332
|
begin
|
@@ -332,7 +334,7 @@ te = p+1
|
|
332
334
|
end
|
333
335
|
end
|
334
336
|
when 9 then
|
335
|
-
# line 55 "lexer.ragel"
|
337
|
+
# line 55 "lib/eggshell/expression-evaluator/lexer.ragel"
|
336
338
|
begin
|
337
339
|
te = p+1
|
338
340
|
begin
|
@@ -340,7 +342,7 @@ te = p+1
|
|
340
342
|
end
|
341
343
|
end
|
342
344
|
when 10 then
|
343
|
-
# line 59 "lexer.ragel"
|
345
|
+
# line 59 "lib/eggshell/expression-evaluator/lexer.ragel"
|
344
346
|
begin
|
345
347
|
te = p+1
|
346
348
|
begin
|
@@ -348,7 +350,7 @@ te = p+1
|
|
348
350
|
end
|
349
351
|
end
|
350
352
|
when 11 then
|
351
|
-
# line 63 "lexer.ragel"
|
353
|
+
# line 63 "lib/eggshell/expression-evaluator/lexer.ragel"
|
352
354
|
begin
|
353
355
|
te = p+1
|
354
356
|
begin
|
@@ -356,7 +358,7 @@ te = p+1
|
|
356
358
|
end
|
357
359
|
end
|
358
360
|
when 12 then
|
359
|
-
# line 67 "lexer.ragel"
|
361
|
+
# line 67 "lib/eggshell/expression-evaluator/lexer.ragel"
|
360
362
|
begin
|
361
363
|
te = p+1
|
362
364
|
begin
|
@@ -364,7 +366,7 @@ te = p+1
|
|
364
366
|
end
|
365
367
|
end
|
366
368
|
when 13 then
|
367
|
-
# line 19 "lexer.ragel"
|
369
|
+
# line 19 "lib/eggshell/expression-evaluator/lexer.ragel"
|
368
370
|
begin
|
369
371
|
te = p
|
370
372
|
p = p - 1; begin
|
@@ -372,7 +374,7 @@ p = p - 1; begin
|
|
372
374
|
end
|
373
375
|
end
|
374
376
|
when 14 then
|
375
|
-
# line 23 "lexer.ragel"
|
377
|
+
# line 23 "lib/eggshell/expression-evaluator/lexer.ragel"
|
376
378
|
begin
|
377
379
|
te = p
|
378
380
|
p = p - 1; begin
|
@@ -380,7 +382,7 @@ p = p - 1; begin
|
|
380
382
|
end
|
381
383
|
end
|
382
384
|
when 15 then
|
383
|
-
# line 27 "lexer.ragel"
|
385
|
+
# line 27 "lib/eggshell/expression-evaluator/lexer.ragel"
|
384
386
|
begin
|
385
387
|
te = p
|
386
388
|
p = p - 1; begin
|
@@ -388,7 +390,7 @@ p = p - 1; begin
|
|
388
390
|
end
|
389
391
|
end
|
390
392
|
when 16 then
|
391
|
-
# line 31 "lexer.ragel"
|
393
|
+
# line 31 "lib/eggshell/expression-evaluator/lexer.ragel"
|
392
394
|
begin
|
393
395
|
te = p
|
394
396
|
p = p - 1; begin
|
@@ -396,7 +398,7 @@ p = p - 1; begin
|
|
396
398
|
end
|
397
399
|
end
|
398
400
|
when 17 then
|
399
|
-
# line 51 "lexer.ragel"
|
401
|
+
# line 51 "lib/eggshell/expression-evaluator/lexer.ragel"
|
400
402
|
begin
|
401
403
|
te = p
|
402
404
|
p = p - 1; begin
|
@@ -404,7 +406,7 @@ p = p - 1; begin
|
|
404
406
|
end
|
405
407
|
end
|
406
408
|
when 18 then
|
407
|
-
# line 67 "lexer.ragel"
|
409
|
+
# line 67 "lib/eggshell/expression-evaluator/lexer.ragel"
|
408
410
|
begin
|
409
411
|
te = p
|
410
412
|
p = p - 1; begin
|
@@ -412,14 +414,14 @@ p = p - 1; begin
|
|
412
414
|
end
|
413
415
|
end
|
414
416
|
when 19 then
|
415
|
-
# line 19 "lexer.ragel"
|
417
|
+
# line 19 "lib/eggshell/expression-evaluator/lexer.ragel"
|
416
418
|
begin
|
417
419
|
begin p = ((te))-1; end
|
418
420
|
begin
|
419
421
|
@parser.emit(:number_literal, data, ts, te)
|
420
422
|
end
|
421
423
|
end
|
422
|
-
# line
|
424
|
+
# line 425 "lib/eggshell/expression-evaluator/lexer.rb"
|
423
425
|
end # action switch
|
424
426
|
end
|
425
427
|
end
|
@@ -439,7 +441,7 @@ when 0 then
|
|
439
441
|
# line 1 "NONE"
|
440
442
|
begin
|
441
443
|
ts = nil; end
|
442
|
-
# line
|
444
|
+
# line 445 "lib/eggshell/expression-evaluator/lexer.rb"
|
443
445
|
end # to state action switch
|
444
446
|
end
|
445
447
|
if _trigger_goto
|
@@ -466,7 +468,7 @@ end
|
|
466
468
|
end
|
467
469
|
end
|
468
470
|
|
469
|
-
# line 88 "lexer.ragel"
|
471
|
+
# line 88 "lib/eggshell/expression-evaluator/lexer.ragel"
|
470
472
|
# %
|
471
473
|
end
|
472
474
|
end
|
@@ -15,6 +15,23 @@ module Parser
|
|
15
15
|
ST_ARRAY = 256
|
16
16
|
ST_INDEX_ACCESS = ST_ARRAY|ST_LABEL
|
17
17
|
|
18
|
+
STATE_NAMES = {
|
19
|
+
0 => 'null',
|
20
|
+
1 => 'number',
|
21
|
+
2 => 'string',
|
22
|
+
4 => 'string_expression',
|
23
|
+
8 => 'string_block',
|
24
|
+
16 => 'label',
|
25
|
+
17 => 'label_call',
|
26
|
+
19 => 'label_member',
|
27
|
+
32 => 'operator',
|
28
|
+
33 => 'operator_tern',
|
29
|
+
64 => 'group',
|
30
|
+
128 => 'hash',
|
31
|
+
256 => 'array',
|
32
|
+
ST_INDEX_ACCESS => 'index_access'
|
33
|
+
}.freeze
|
34
|
+
|
18
35
|
CONSTS = {
|
19
36
|
'true' => true,
|
20
37
|
'false' => false,
|
@@ -50,9 +67,17 @@ module Parser
|
|
50
67
|
end
|
51
68
|
|
52
69
|
def emit(type, data = nil, ts = nil, te = nil)
|
70
|
+
lst = @state[-1]
|
71
|
+
# pop last state if appropriate
|
72
|
+
if type == :end
|
73
|
+
while @state[-1] == ST_OPERATOR || @state[-1] == ST_OPERATOR_TERN
|
74
|
+
@state.pop
|
75
|
+
end
|
76
|
+
return
|
77
|
+
end
|
78
|
+
|
53
79
|
word = data[ts...te].pack('c*')
|
54
80
|
@tokens << word # if type != :space
|
55
|
-
lst = @state[-1]
|
56
81
|
|
57
82
|
# before inserting term, need to make sure it follows semantics of function/array
|
58
83
|
# @todo look out for case of '(,' or '[,'
|
@@ -143,7 +168,10 @@ module Parser
|
|
143
168
|
elsif type == :logical_op
|
144
169
|
# STORED AS: `[:op, [operand1, operator1, operand2, operator2, ...]]
|
145
170
|
# > @last_ptr holds entire structure, @ptr holds structure[1]
|
146
|
-
# @todo deal with - prefix
|
171
|
+
# @todo deal with '-' prefix
|
172
|
+
|
173
|
+
# direct assignment unsupported for now, cast to equivalence
|
174
|
+
word = '==' if word == '='
|
147
175
|
@term_last.pop
|
148
176
|
raise Exception.new("expecting identifier after '#{@ptr[-1][1]}'") if @expect_label
|
149
177
|
if word == '?'
|
@@ -349,7 +377,7 @@ module Parser
|
|
349
377
|
end
|
350
378
|
elsif type == :space
|
351
379
|
else
|
352
|
-
|
380
|
+
$stderr.write "! parser else: #{word}\n"
|
353
381
|
end
|
354
382
|
end
|
355
383
|
|
@@ -357,19 +385,34 @@ module Parser
|
|
357
385
|
reset
|
358
386
|
begin
|
359
387
|
@lexer.process(src)
|
388
|
+
emit(:end)
|
389
|
+
if @state[-1] != ST_NULL
|
390
|
+
err = []
|
391
|
+
@state[1..-1].each do |st|
|
392
|
+
err << STATE_NAMES[st]
|
393
|
+
end
|
394
|
+
raise Exception.new("Unbalanced state: #{err.join(' -> ')}")
|
395
|
+
end
|
360
396
|
@tree
|
361
|
-
rescue => ex
|
362
|
-
$stderr.write("parse exception: #{ex}\n")
|
397
|
+
rescue Exception => ex
|
398
|
+
$stderr.write("WARN: returning nil, parse exception for: #{src}\n#{ex}\n")
|
363
399
|
$stderr.write("\t#{ex.backtrace.join("\n\t")}\n")
|
400
|
+
debug
|
364
401
|
nil
|
365
402
|
end
|
366
403
|
end
|
367
404
|
|
368
|
-
def debug
|
369
|
-
|
370
|
-
|
371
|
-
|
372
|
-
|
405
|
+
def debug(io = nil)
|
406
|
+
io = $stderr if !io
|
407
|
+
io.write "STATE : "
|
408
|
+
@state.each do |st|
|
409
|
+
io.write STATE_NAMES[st]
|
410
|
+
io.write ", "
|
411
|
+
end
|
412
|
+
io.write "\n"
|
413
|
+
io.write "TREE : #{@tree.inspect}\n"
|
414
|
+
io.write " PTR : #{@ptr.inspect}\n"
|
415
|
+
io.write "TOKENS: #{@tokens.inspect}\n"
|
373
416
|
end
|
374
417
|
end
|
375
418
|
|
data/lib/eggshell/parse-tree.rb
CHANGED
@@ -39,7 +39,7 @@ class Eggshell::ParseTree
|
|
39
39
|
|
40
40
|
if delim
|
41
41
|
@modes << (mode == MH::COLLECT_RAW_MACRO ? :macro_raw : macro)
|
42
|
-
@macro_delims << delim.reverse.gsub('[', ']').gsub('(', ')').gsub('{', '}')
|
42
|
+
@macro_delims << delim #delim.reverse.gsub('[', ']').gsub('(', ')').gsub('{', '}')
|
43
43
|
@macro_open << line
|
44
44
|
@macro_ptr << @ptr
|
45
45
|
# set ptr to entry's tree
|
@@ -59,7 +59,7 @@ class Eggshell::ParseTree
|
|
59
59
|
@macro_delims.pop
|
60
60
|
@macro_open.pop
|
61
61
|
@ptr = @macro_ptr.pop
|
62
|
-
@ptr[-1]
|
62
|
+
@ptr[-1][-1] = line_num
|
63
63
|
last_mode = @modes.pop
|
64
64
|
return true
|
65
65
|
end
|
@@ -90,7 +90,7 @@ class Eggshell::ParseTree
|
|
90
90
|
end
|
91
91
|
|
92
92
|
def push_block
|
93
|
-
if @modes[-1] == :block
|
93
|
+
if @modes[-1] == :block || @modes[-1] == :raw
|
94
94
|
if @cur_block
|
95
95
|
line_end = @cur_block[3]
|
96
96
|
line_end = @lines[-1].line_num if @lines[-1]
|
data/lib/eggshell/processor.rb
CHANGED
@@ -302,7 +302,7 @@ module Eggshell
|
|
302
302
|
|
303
303
|
line_norm = Line.new(line, tab_str, _ind, line_start, oline.chomp)
|
304
304
|
line_start = line_count
|
305
|
-
|
305
|
+
#$stderr.write ">> mode(#{parse_tree.mode}): #{line}\n"
|
306
306
|
if parse_tree.mode == :raw
|
307
307
|
stat = parse_tree.collect(line_norm)
|
308
308
|
next if stat != BH::RETRY
|
@@ -317,6 +317,7 @@ module Eggshell
|
|
317
317
|
# macro processing
|
318
318
|
if line[0] == '@'
|
319
319
|
macro, args, delim = parse_macro_start(line)
|
320
|
+
#$stderr.write "-- macro: #{macro} (#{line})\n"
|
320
321
|
mhandler = get_macro_handler(macro)
|
321
322
|
parse_tree.new_macro(line_norm, line_count, macro, args, delim, mhandler ? mhandler.collection_type(macro) : nil)
|
322
323
|
next
|
@@ -400,7 +401,6 @@ module Eggshell
|
|
400
401
|
last_line = 0
|
401
402
|
last_macro = nil
|
402
403
|
deferred = nil
|
403
|
-
|
404
404
|
parse_tree.each do |unit|
|
405
405
|
if unit.is_a?(String)
|
406
406
|
out << unit
|
@@ -418,11 +418,11 @@ module Eggshell
|
|
418
418
|
_warn "handler not found: #{unit[0]} -> #{unit[1]}"
|
419
419
|
next
|
420
420
|
end
|
421
|
-
|
421
|
+
#$stderr.write "#{unit[0]}:#{unit[1]}\n\t#{unit[2].inspect}\n"
|
422
422
|
args_o = unit[2] || []
|
423
423
|
args = []
|
424
424
|
args_o.each do |arg|
|
425
|
-
args << (arg.is_a?(Array) ?
|
425
|
+
args << (arg.is_a?(Array) ? @ee.evaluate([arg]) : arg)
|
426
426
|
end
|
427
427
|
|
428
428
|
lines = unit[ParseTree::IDX_LINES]
|
@@ -621,6 +621,8 @@ module Eggshell
|
|
621
621
|
[block_type, args, line]
|
622
622
|
end
|
623
623
|
|
624
|
+
# @todo enhancement #1: allow same-line nesting of macros like `@macro(...) { @macro2 {` (and
|
625
|
+
# make sure to handle closing on same line like `} }`)
|
624
626
|
def parse_macro_start(line)
|
625
627
|
macro = nil
|
626
628
|
args = []
|
@@ -628,21 +630,26 @@ module Eggshell
|
|
628
630
|
|
629
631
|
# either macro is a plain '@macro' or it has parameters/opening brace
|
630
632
|
if line.index(' ') || line.index('(') || line.index('{')
|
633
|
+
# remove the end delimiter
|
634
|
+
m = line.match(/(\{[\/\(\[a-z]*)\s*$/)
|
635
|
+
if m
|
636
|
+
line = line[0...line.rindex(m[1])]
|
637
|
+
end
|
638
|
+
line = line[1..-1]
|
631
639
|
# since the macro statement is essentially a function call, parse the line as an expression to get components
|
632
640
|
expr_struct = @ee.parse(line)
|
633
641
|
fn = expr_struct.shift
|
634
|
-
|
635
642
|
if fn.is_a?(Array) && (fn[0] == :func || fn[0] == :var)
|
636
643
|
macro = fn[1]
|
637
644
|
args = fn[2] # @@ee.evaluate([:array, fn[2]])
|
638
|
-
if
|
639
|
-
delim =
|
645
|
+
if m
|
646
|
+
delim = m[1].reverse.gsub('{', '}').gsub('[', ']').gsub('(', ')')
|
640
647
|
end
|
641
648
|
end
|
642
649
|
else
|
643
650
|
macro = line[1..line.length]
|
644
651
|
end
|
645
|
-
|
652
|
+
|
646
653
|
[macro, args, delim]
|
647
654
|
end
|
648
655
|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: eggshell
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.1.
|
4
|
+
version: 1.1.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Kaiser Shahid
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-
|
11
|
+
date: 2017-09-13 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: From fast and basic HTML to complex decouments and more, Eggshell aims
|
14
14
|
to provide you with all the document generation power you need through simple markup.
|