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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: e8f0c6e2c0d46c6a179f5ec5b92860f89191cb49
4
- data.tar.gz: 6a2796788c613161da16ea59f2fbee7e3287e116
3
+ metadata.gz: d333cbc627c5af623979142276a316f4cc249026
4
+ data.tar.gz: b4abb4e1c883a743b702ae184790a96164b6a207
5
5
  SHA512:
6
- metadata.gz: 0c0c82ea72213e521a12dba7aa595f3ccedb7dee48141df53c30ffae2fb49de2afc5eba567a1fb608fbdd844316aacb03c547145aa3d1d5082343d27541a11de
7
- data.tar.gz: 36468590248a2d9c2d4638915166e56985d59ba989c00f3934dd88089705a6f85a7b80d6af39fd1f5f92df340f69b17e6d2906bf3449951d5288d0aba2e588c0
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]}>$" if !end_html
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 = line_obj.is_a?(Eggshell::Line) ? line_obj.line : line_obj
391
- indent = line_obj.indent_lvl
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 << line
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
- '='.to_sym => 1000,
21
- '=='.to_sym => 999,
22
- '!='.to_sym => 999,
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
- '*'.to_sym => 150,
35
- '/'.to_sym => 150,
49
+ OP_MULTIPLY => 150,
50
+ OP_DIVIDE => 150,
36
51
  '%'.to_sym => 150,
37
52
  '^'.to_sym => 150,
38
- '+'.to_sym => 100,
39
- '-'.to_sym => 100,
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
- parsed = @cache[statement]
83
- return parsed if cache && parsed
175
+ if cache
176
+ parsed = @cache[statement]
177
+ return parsed if parsed
178
+ end
84
179
 
85
- parsed = @parser.parse(statement)
86
- @cache[statement] = parsed if cache
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
- op_val = frag[1][0].is_a?(Array) ? evaluate([frag[1][0]], false) : frag[1][0]
105
- z = 1
106
- while z < frag[1].length
107
- op = frag[1][z]
108
- rop = frag[1][z+1]
109
- rop = evaluate([rop]) if rop.is_a?(Array)
110
- op_val = self.class.op_eval(op_val, op, rop)
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
- ret = var_get(frag, false, vtable)
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
- if (lop.is_a?(Numeric) && rop.is_a?(Numeric)) || lop.is_a?(rop.class)
186
- op = op.to_s
187
- case op
188
- when '==='
189
- return lop === rop
190
- when '=='
191
- return lop == rop
192
- when '!='
193
- return lop != rop
194
- when '+'
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
- when '-'
197
- return lop - rop
198
- when '/'
199
- return lop / rop
200
- when '*'
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
- when '<<'
203
- return lop << rop
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
- def var_get(var, do_ptr = false, vtable = nil)
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
- return vtable[var] if vtable.has_key?(var)
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
- # @todo if do_ptr, set it as parts.length - 1? (and avoid having to save ptr_lst)
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) && ptr[key]
255
- ptr = ptr[key]
256
- if ptr != nil
257
- i += 2
258
- next
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
- break
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 = var_get(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)) && ptr[idx]
276
- ptr = ptr[idx]
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
- mname = 'get_' + part[1]
283
- if ptr.respond_to?(mname.to_sym)
284
- ptr = ptr.send(mname.to_sym)
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
- ptr = nil
287
- break
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, 38, 43, 44, 46, 49, 51,
34
- 52, 54, 55, 56, 58, 68
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, 39,
43
- 43, 44, 45, 46, 58, 59, 60, 61,
44
- 62, 63, 64, 91, 92, 93, 94, 124,
45
- 9, 10, 40, 41, 42, 47, 48, 57,
46
- 65, 95, 97, 122, 123, 125, 95, 65,
47
- 90, 97, 122, 38, 48, 57, 46, 48,
48
- 57, 48, 57, 58, 60, 61, 61, 61,
49
- 61, 62, 34, 91, 93, 110, 114, 116,
50
- 123, 125, 39, 41, 124, 0
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, 22, 1, 1, 0, 1, 0, 1,
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, 2, 0, 1, 1, 1, 0,
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, 32, 36, 38, 40, 43, 45,
77
- 47, 49, 51, 53, 55, 65
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, 4,
86
- 9, 10, 9, 10, 12, 10, 13, 14,
87
- 15, 8, 16, 17, 18, 17, 8, 20,
88
- 3, 7, 8, 11, 5, 5, 19, 2,
89
- 5, 5, 5, 21, 8, 22, 11, 23,
90
- 25, 11, 24, 1, 26, 16, 27, 8,
91
- 23, 28, 23, 8, 23, 8, 23, 29,
92
- 29, 29, 29, 29, 29, 29, 29, 29,
93
- 22, 8, 22, 0
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, 6, 1, 1, 1, 2, 3, 1,
102
- 1, 4, 1, 5, 7, 8, 9, 11,
103
- 1, 1, 12, 1, 13, 1, 1, 1,
104
- 1, 0, 1, 1, 10, 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, 13,
113
- 7, 0, 19, 5, 0, 0, 0, 0,
114
- 17, 9, 0, 11, 0, 31, 37, 33,
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, 22, 23, 24, 25, 27, 28,
142
- 24, 24, 24, 24, 23, 23
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 173 "lexer.rb"
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 185 "lexer.rb"
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 215 "lexer.rb"
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 423 "lexer.rb"
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 443 "lexer.rb"
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
- puts "woooo"
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
- puts "STATE : #{@state.inspect}"
370
- puts "TREE : #{@tree.inspect}"
371
- puts " PTR : #{@ptr.inspect}"
372
- puts "TOKENS: #{@tokens.inspect}"
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
 
@@ -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] << line_num
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]
@@ -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) ? @@ee.evaluate([:array, arg]) : arg)
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 expr_struct[-1].is_a?(Array) && expr_struct[-1][0] == :brace_op
639
- delim = expr_struct[-1][1]
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.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-08-16 00:00:00.000000000 Z
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.