trxl 0.1.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,1394 @@
1
+ grammar Trxl
2
+
3
+ rule program
4
+ space statement_list space {
5
+ def eval(env = Environment.new)
6
+ statement_list.eval(env)
7
+ end
8
+ }
9
+ /
10
+ space statement_separator* {
11
+ def eval(env = Environment.new)
12
+ nil
13
+ end
14
+ }
15
+ end
16
+
17
+ rule require_directive
18
+ require_keyword space string_literal <RequireDirective>
19
+ end
20
+
21
+ rule statement_list
22
+ expression more_expressions:(statement_separator expression)* statement_separator* {
23
+ def eval(env = Environment.new)
24
+ last_eval = nil
25
+ #env.enter_scope
26
+ expressions.each do |e|
27
+ last_eval = e.eval(env)
28
+ end
29
+ #env.exit_scope
30
+ last_eval
31
+ end
32
+
33
+ def expressions
34
+ [ expression ] + more_expressions.elements.map { |e| e.expression }
35
+ end
36
+
37
+ def to_s(env = Environment.new)
38
+ expressions.map { |e| e.to_s(env) }.join(' ')
39
+ end
40
+ }
41
+ end
42
+
43
+ rule statement_separator
44
+ (space ';' space) {
45
+ def to_s(env = Environment.new)
46
+ text_value
47
+ end
48
+ }
49
+ end
50
+
51
+
52
+ rule expression
53
+ if_expression
54
+ /
55
+ case_expression
56
+ /
57
+ binary_expression
58
+ /
59
+ negated_expression
60
+ /
61
+ unary_expression
62
+ end
63
+
64
+ rule binary_expression
65
+ operand_1:unary_expression space operator:binary_expression_op space operand_2:unary_expression binary_expression <BinaryOperation>
66
+ /
67
+ operand_1:unary_expression space operator:binary_expression_op space operand_2:unary_expression <BinaryOperation>
68
+ end
69
+
70
+ rule negated_expression
71
+ "!" expression {
72
+ def eval(env = Environment.new)
73
+ !expression.eval(env)
74
+ end
75
+ }
76
+ end
77
+
78
+ rule unary_expression
79
+ require_directive
80
+ /
81
+ definition
82
+ /
83
+ comparative
84
+ /
85
+ additive
86
+ end
87
+
88
+
89
+ rule definition
90
+ variable space '=' space expression {
91
+ def eval(env = Environment.new)
92
+ env[variable.name] = expression.eval(env)
93
+ end
94
+
95
+ def to_s(env = Environment.new)
96
+ "#{variable.name} = #{expression.eval(env)}"
97
+ end
98
+ }
99
+ end
100
+
101
+
102
+ rule if_expression
103
+
104
+ 'if' space '(' space if_exp:expression space ')' SPACE
105
+ if_branch:statement_list space
106
+ elsif_branches:(elsif_expression_list SPACE)?
107
+ else_branch:('else' SPACE statement_list SPACE)?
108
+ 'end'
109
+
110
+ {
111
+ def eval(env = Environment.new)
112
+ return if_branch.eval(env) if if_exp.eval(env)
113
+ elsif_expressions.each do |e|
114
+ return e.statement_list.eval(env) if e.elsif_exp.eval(env)
115
+ end
116
+ (else_branch && !else_branch.empty?) ? else_branch.statement_list.eval(env) : nil
117
+ end
118
+
119
+ def elsif_expressions(env = Environment.new)
120
+ (elsif_branches && !elsif_branches.empty?) ? elsif_branches.elsif_expression_list.elsif_expressions : []
121
+ end
122
+ }
123
+
124
+ end
125
+
126
+ rule elsif_expression
127
+ 'elsif' space '(' space elsif_exp:expression space ')' space statement_list {
128
+
129
+ def eval(env = Environment.new)
130
+ statement_list.eval(env)
131
+ end
132
+ }
133
+ end
134
+
135
+ rule elsif_expression_list
136
+ elsif_expression tail:(SPACE elsif_expression)* {
137
+ def eval(env = Environment.new)
138
+ elsif_expressions.inject([]) do |exprs, expr|
139
+ exprs << expr.eval(env)
140
+ end
141
+ end
142
+
143
+ def elsif_expressions
144
+ [ elsif_expression ] + tail.elements.map { |e| e.elsif_expression }
145
+ end
146
+
147
+ }
148
+ end
149
+
150
+ rule case_expression
151
+
152
+ case_keyword SPACE case_exp:expression SPACE
153
+ when_expression_list SPACE
154
+ 'else' SPACE else_exp:statement_list SPACE
155
+ end_keyword
156
+
157
+ {
158
+ def eval(env = Environment.new)
159
+ case_val = case_exp.eval(env)
160
+ else_val = else_exp.eval(env)
161
+ Kernel.eval <<-CASE_STMT
162
+ lambda do
163
+ case #{case_val.is_a?(String) ? "'#{case_val}'" : case_val}
164
+ #{ruby_when_expressions(env)}
165
+ else #{else_val.is_a?(String) ? "'#{else_val}'" : else_val}
166
+ end
167
+ end [] # call this lambda immediately
168
+ CASE_STMT
169
+ end
170
+
171
+ def ruby_when_expressions(env = Environment.new)
172
+ when_expression_list.eval(env).inject('') do |ruby, e|
173
+ # possible string values have been wrapped in '' already
174
+ ruby << "when #{e[:condition]} then #{e[:expression]} "
175
+ end
176
+ end
177
+ }
178
+
179
+ end
180
+
181
+ rule when_expression
182
+ when_keyword SPACE when_exp:expression SPACE then_keyword SPACE statement_list {
183
+ def eval(env = Environment.new)
184
+ condition = when_exp.eval(env)
185
+ expression = statement_list.eval(env)
186
+ {
187
+ # use '' instead of "" since we don't care about var replacement now
188
+ :condition => (condition.is_a?(String) ? "'#{condition}'" : condition),
189
+ :expression => (expression.is_a?(String) ? "'#{expression}'" : expression)
190
+ }
191
+ end
192
+ }
193
+ end
194
+
195
+ rule when_expression_list
196
+ when_expression more_when_expressions:(SPACE when_expression)* {
197
+ def eval(env = Environment.new)
198
+ when_expressions.inject([]) do |exprs, expr|
199
+ exprs << expr.eval(env)
200
+ end
201
+ end
202
+
203
+ def when_expressions
204
+ [ when_expression ] + more_when_expressions.elements.map { |e| e.when_expression }
205
+ end
206
+ }
207
+ end
208
+
209
+ rule application
210
+ operator space first_application:actual_parameter_list more_applications:( space actual_parameter_list )* {
211
+ def eval(env = Environment.new)
212
+ left_associative_apply(operator, env)
213
+ end
214
+
215
+ def left_associative_apply(operator, env)
216
+ applications.each do |actual_parameter_list|
217
+ actuals = actual_parameter_list.eval(env)
218
+ unless operator.instance_of?(Trxl::Function::Closure)
219
+ operator = operator.eval(env)
220
+ end
221
+ operator = operator.apply(actuals)
222
+ end
223
+ operator
224
+ end
225
+
226
+ def applications
227
+ [ first_application ] + more_applications.elements.map { |e| e.actual_parameter_list }
228
+ end
229
+
230
+ def to_s(env = Environment.new)
231
+ text_value
232
+ end
233
+ }
234
+ end
235
+
236
+ rule operator
237
+ function / variable
238
+ end
239
+
240
+ rule function
241
+ 'fun' formal_parameter_list space '{' space body:statement_list space '}' <Function>
242
+ end
243
+
244
+ rule formal_parameter_list
245
+ '(' variable more_variables:(space ',' space variable)* space ')' {
246
+ def bind(args, env = Environment.new)
247
+ if (a = args.length) < (f = variables.length)
248
+ raise WrongNumberOfArgumentsException, "#{a} instead of #{f}"
249
+ end
250
+ env.merge!(variables.zip(args).inject({}) do |bindings, param|
251
+ bindings.merge(param.first.name => param.last)
252
+ end)
253
+ # store arguments array in scope, javascript like
254
+ env.merge!(:arguments => args)
255
+ end
256
+
257
+ def variables
258
+ [variable] + more_variables.elements.map { |e| e.variable }
259
+ end
260
+
261
+ def length
262
+ variables.length
263
+ end
264
+
265
+ def to_s(env = Environment.new)
266
+ "(#{variables.map { |var| var.text_value }.join(',')})"
267
+ end
268
+ }
269
+ /
270
+ '(' space ')' {
271
+ def bind(args, env)
272
+ # store arguments array in scope, javascript like
273
+ env.merge!(:arguments => args)
274
+ end
275
+
276
+ def to_s(env = Environment.new)
277
+ '()'
278
+ end
279
+ }
280
+ end
281
+
282
+ rule actual_parameter_list
283
+ '(' space expression_list space ')' {
284
+
285
+ def eval(env = Environment.new)
286
+ expression_list.eval(env)
287
+ end
288
+
289
+ def to_s(env = Environment.new)
290
+ "(#{expression_list.to_s(env)})"
291
+ end
292
+ }
293
+ /
294
+ '(' space ')' {
295
+ def eval(env = Environment.new)
296
+ []
297
+ end
298
+
299
+ def to_s(env = Environment.new)
300
+ '()'
301
+ end
302
+ }
303
+ end
304
+
305
+ rule string_literal
306
+ single_quoted_string / double_quoted_string
307
+ end
308
+
309
+ rule double_quoted_string
310
+ '"' string:(!'"' ("\\\\" / '\"' / .))* '"' {
311
+ def eval(env = Environment.new)
312
+ string.text_value
313
+ end
314
+ }
315
+ end
316
+
317
+ rule single_quoted_string
318
+ "'" string:(!"'" ("\\\\" / "\\'" / .))* "'" {
319
+ def eval(env = Environment.new)
320
+ string.text_value
321
+ end
322
+ }
323
+ end
324
+
325
+ rule hash_literal
326
+ '{' space hash_entry_list space '}' {
327
+
328
+ def eval(env = Environment.new)
329
+ hash_entry_list.eval(env)
330
+ end
331
+
332
+ def to_s(env = Environment.new)
333
+ "(#{hash_entry_list.to_s(env)})"
334
+ end
335
+ }
336
+ /
337
+ '{}' {
338
+ def eval(env = Environment.new)
339
+ {}
340
+ end
341
+
342
+ def to_s(env = Environment.new)
343
+ text_value
344
+ end
345
+ }
346
+ end
347
+
348
+
349
+ rule hash_entry_list
350
+ hash_entry tail:(space ',' space hash_entry)* ','? {
351
+ def eval(env = Environment.new)
352
+ hash_entries.inject({}) do |hash, entry|
353
+ hash.merge(entry.eval(env))
354
+ end
355
+ end
356
+
357
+ def hash_entries
358
+ [ hash_entry ] + tail.elements.map { |e| e.hash_entry }
359
+ end
360
+ }
361
+ end
362
+
363
+ rule hash_entry
364
+ key:expression space '=>' space value:expression {
365
+ def eval(env = Environment.new)
366
+ { key.eval(env) => value.eval(env) }
367
+ end
368
+
369
+ def to_s(env = Environment.new)
370
+ text_value
371
+ end
372
+ }
373
+ end
374
+
375
+ rule array_literal
376
+ '[' space expression_list space ']' {
377
+
378
+ def eval(env = Environment.new)
379
+ expression_list.eval(env)
380
+ end
381
+
382
+ def to_s(env = Environment.new)
383
+ "(#{expression_list.to_s(env)})"
384
+ end
385
+ }
386
+ /
387
+ '[]' {
388
+ def eval(env = Environment.new)
389
+ []
390
+ end
391
+
392
+ def to_s(env = Environment.new)
393
+ text_value
394
+ end
395
+ }
396
+ end
397
+
398
+ rule range_literal
399
+ lower:(variable/integer_number/string_literal) space ('...' / '..') space upper:(variable/integer_number/string_literal) {
400
+ def eval(env = Environment.new)
401
+ lower_bound = lower.eval(env)
402
+ upper_bound = upper.eval(env)
403
+ if lower_bound.class == upper_bound.class && !lower_bound.is_a?(Array)
404
+ range_op = elements[2].text_value
405
+ omit_upper = (range_op == '...') ? true : false
406
+ Range.new(lower.eval(env), upper.eval(env), omit_upper).to_a
407
+ else
408
+ raise Trxl::InvalidOperationException, "Range boundary is not of type String or Integer"
409
+ end
410
+ end
411
+
412
+ def range_type(env = Environment.new)
413
+ case elements[0].eval(env)
414
+ when Fixnum then :numeric
415
+ when String then :string
416
+ else :unknown
417
+ end
418
+ end
419
+
420
+ def to_s(env = Environment.new)
421
+ text_value
422
+ end
423
+ }
424
+ end
425
+
426
+ rule expression_list
427
+ expression more_expressions:(space ',' space expression)* {
428
+
429
+ def eval(env = Environment.new)
430
+ expressions.inject([]) { |arr, exp| arr << exp.eval(env) }
431
+ end
432
+
433
+ def expressions
434
+ [ expression ] + more_expressions.elements.map { |e| e.expression }
435
+ end
436
+
437
+ def length
438
+ expressions.length
439
+ end
440
+
441
+ def to_s(env = Environment.new)
442
+ "#{expressions.map { |p| p.text_value }.join(',')}"
443
+ end
444
+ }
445
+ end
446
+
447
+ rule comparative
448
+ operand_1:additive space operator:equality_op space operand_2:additive <BinaryOperation>
449
+ end
450
+
451
+ rule binary_expression_op
452
+ '&&' <NilAcceptingOperator>
453
+ /
454
+ '||' <NilAcceptingOperator>
455
+ /
456
+ '<<' <NilAcceptingOperator> {
457
+
458
+ def apply(a, b)
459
+ if a.is_a?(Array)
460
+ super
461
+ else
462
+ raise Trxl::InvalidOperationException, "Left operand is not an Array"
463
+ end
464
+ end
465
+
466
+ # override default behavior since it's not possible to push into nil
467
+ def lhs_nil_allowed?
468
+ false
469
+ end
470
+ }
471
+ end
472
+
473
+ rule equality_op
474
+ '==' <NilAcceptingOperator>
475
+ /
476
+ '!=' <NilAcceptingOperator>
477
+ /
478
+ '<=' <NilRejectingOperator>
479
+ /
480
+ '>=' <NilRejectingOperator>
481
+ /
482
+ '<' <NilRejectingOperator>
483
+ /
484
+ '>' <NilRejectingOperator>
485
+ end
486
+
487
+ rule additive
488
+ multitive tail:(space additive_op space multitive)* {
489
+ def eval(env = Environment.new)
490
+ # left associative evaluation
491
+ additives(env).inject(multitive.eval(env)) do |result, next_op|
492
+ next_op[0].apply(result, next_op[1])
493
+ end
494
+ end
495
+
496
+ def additives(env = Environment.new)
497
+ tail.elements.map { |e| [ e.additive_op, e.multitive.eval(env) ] }
498
+ end
499
+ }
500
+ end
501
+
502
+ rule additive_op
503
+ '+' <NilRejectingOperator>
504
+ /
505
+ '-' <NilRejectingOperator>
506
+ end
507
+
508
+ rule multitive
509
+ exponential tail:(space multitive_op space exponential)* {
510
+ def eval(env = Environment.new)
511
+ # left associative evaluation
512
+ multitives(env).inject(exponential.eval(env)) do |operand, next_op|
513
+ op = (next_op[0].text_value == '/' ? operand.to_f : operand)
514
+ next_op[0].apply(op, next_op[1])
515
+ end
516
+ end
517
+
518
+ def multitives(env = Environment.new)
519
+ tail.elements.map { |e| [ e.multitive_op, e.exponential.eval(env) ] }
520
+ end
521
+ }
522
+ end
523
+
524
+ rule multitive_op
525
+ '*' <NilRejectingOperator>
526
+ /
527
+ '/' <NilRejectingOperator> {
528
+
529
+ def apply(a, b)
530
+ begin
531
+ result = super
532
+ if result == 1.0 / 0 || (result.respond_to?(:nan?) && result.nan?)
533
+ raise Trxl::DivisionByZeroError, "Division by zero: '#{a} / #{b}'"
534
+ end
535
+ result
536
+ rescue ZeroDivisionError
537
+ raise Trxl::DivisionByZeroError, "Division by zero: '#{a} / #{b}'"
538
+ end
539
+ end
540
+ }
541
+ /
542
+ '%' <NilRejectingOperator> {
543
+
544
+ def apply(a, b)
545
+ begin
546
+ result = super
547
+ if result.respond_to?(:nan?) && result.nan?
548
+ raise Trxl::DivisionByZeroError, "Division by zero: '#{a} % #{b}'"
549
+ end
550
+ result
551
+ rescue ZeroDivisionError
552
+ raise Trxl::DivisionByZeroError, "Division by zero: '#{a} % #{b}'"
553
+ end
554
+ end
555
+ }
556
+ end
557
+
558
+ rule exponential
559
+ operand_1:primary space operator:exponential_op space operand_2:exponential <BinaryOperation>
560
+ /
561
+ primary
562
+ end
563
+
564
+ rule exponential_op
565
+ '^' <NilRejectingOperator> {
566
+ def ruby_operator
567
+ :**
568
+ end
569
+ }
570
+ end
571
+
572
+ rule primary
573
+ predefined_function
574
+ /
575
+ application
576
+ /
577
+ function
578
+ /
579
+ 'TRUE' {
580
+ def eval(env = Environment.new)
581
+ true
582
+ end
583
+ }
584
+ /
585
+ 'FALSE' {
586
+ def eval(env = Environment.new)
587
+ false
588
+ end
589
+ }
590
+ /
591
+ 'NULL' {
592
+ def eval(env = Environment.new)
593
+ nil
594
+ end
595
+ }
596
+ /
597
+ offset_access_exp
598
+ /
599
+ pattern_match_exp
600
+ /
601
+ array_literal
602
+ /
603
+ hash_literal
604
+ /
605
+ range_literal
606
+ /
607
+ string_literal
608
+ /
609
+ variable
610
+ /
611
+ number
612
+ /
613
+ '(' space expression space ')' {
614
+ def eval(env = Environment.new)
615
+ expression.eval(env)
616
+ end
617
+ }
618
+ end
619
+
620
+
621
+ rule offset_access_exp
622
+ variable offset_specifier_exp <OffsetAccessExpression> {
623
+ def eval(env = Environment.new)
624
+ var = variable.eval(env)
625
+ if var.is_a?(Array) || var.is_a?(Hash) || var.is_a?(String)
626
+ result = left_associative_apply(var, offset_specifier_exp.eval(env))
627
+ var.is_a?(String) ? result.chr : result
628
+ else
629
+ msg = "Indexing is not possible for #{var.class} (only Arrays and Strings allowed)"
630
+ raise Trxl::InvalidOperationException, msg
631
+ end
632
+ end
633
+ }
634
+ /
635
+ pattern_match_exp offset_specifier_exp <OffsetAccessExpression> {
636
+ def eval(env = Environment.new)
637
+ offsets = offset_specifier_exp.eval(env)
638
+ ruby_array = pattern_match_exp.eval(env)
639
+ left_associative_apply(ruby_array, offsets)
640
+ end
641
+ }
642
+ /
643
+ array_literal offset_specifier_exp <OffsetAccessExpression> {
644
+ def eval(env = Environment.new)
645
+ offsets = offset_specifier_exp.eval(env)
646
+ ruby_array = array_literal.eval(env)
647
+ left_associative_apply(ruby_array, offsets)
648
+ end
649
+ }
650
+ end
651
+
652
+ rule offset_specifier_exp
653
+ '[' expression ']' offset_specifier_exp {
654
+ def eval(env = Environment.new)
655
+ [ expression.eval(env) ] + offset_specifier_exp.eval(env)
656
+ end
657
+ }
658
+ /
659
+ '[' expression ']' {
660
+ def eval(env = Environment.new)
661
+ [ expression.eval(env) ]
662
+ end
663
+ }
664
+ end
665
+
666
+
667
+
668
+ rule pattern_match_exp
669
+ exact_match_exp / regex_match_exp
670
+ end
671
+
672
+ rule pattern_match_exp
673
+ variable (exact_match_exp / regex_match_exp) {
674
+ def eval(env = Environment.new)
675
+ match_op = elements[1].match_op
676
+ pattern = elements[1].pattern(env)
677
+ enumerable = variable.eval(env)
678
+ if enumerable.is_a?(Array)
679
+ enumerable.find_all { |e| e.send(match_op, pattern) }
680
+ elsif enumerable.is_a?(Hash)
681
+ enumerable.select { |k, v| v.send(match_op, pattern) }
682
+ else
683
+ msg = "Pattern matching is not possible for #{enumerable.class} (only Arrays and Hashes allowed)"
684
+ raise Trxl::InvalidOperationException, msg
685
+ end
686
+ end
687
+ }
688
+ end
689
+
690
+ rule exact_match_exp
691
+ '[=' primary ']' {
692
+ def pattern(env = Environment.new)
693
+ primary.eval(env)
694
+ end
695
+
696
+ def match_op
697
+ '=='
698
+ end
699
+ }
700
+ end
701
+
702
+ rule regex_match_exp
703
+ '[' regexp ']' {
704
+ def pattern(env = Environment.new)
705
+ regexp.eval(env)
706
+ end
707
+
708
+ def match_op
709
+ '=~'
710
+ end
711
+ }
712
+ end
713
+
714
+ rule regexp
715
+ "/" regexp_body "/" {
716
+ def eval(env = Environment.new)
717
+ regexp_body.eval(env)
718
+ end
719
+ }
720
+ end
721
+
722
+ rule regexp_body
723
+ .+ {
724
+ def eval(env = Environment.new)
725
+ text_value # allow anything for now
726
+ end
727
+ }
728
+ end
729
+
730
+
731
+ rule variable
732
+ [a-zA-Z_]+ ([0-9] / [a-zA-Z_])* {
733
+
734
+ def eval(env = Environment.new)
735
+ if env.has_key?(name)
736
+ env[name]
737
+ else
738
+ raise(Trxl::MissingVariableException, "variable #{name} is not defined")
739
+ end
740
+ end
741
+
742
+ def bind(value, env)
743
+ env.merge(text_value.to_sym => value)
744
+ end
745
+
746
+ def to_s(env = Environment.new)
747
+ if env.has_key?(name)
748
+ value = env[name]
749
+ (value.is_a?(Array) || value.is_a?(Hash)) ? value.inspect : value.to_s
750
+ else
751
+ text_value
752
+ end
753
+ end
754
+
755
+ def name
756
+ text_value.to_sym
757
+ end
758
+ }
759
+ end
760
+
761
+ rule number
762
+ real_number / integer_number {
763
+ def to_s(env = Environment.new)
764
+ text_value
765
+ end
766
+ }
767
+ end
768
+
769
+ rule integer_number
770
+ '-'? ([1-9] [0-9]* / '0') {
771
+ def eval(env = Environment.new)
772
+ text_value.to_i
773
+ end
774
+ }
775
+ end
776
+
777
+ rule real_number
778
+ '-'? [0-9]* '.' [0-9]* {
779
+ def eval(env = Environment.new)
780
+ text_value.to_f
781
+ end
782
+ }
783
+ end
784
+
785
+
786
+
787
+ rule predefined_function
788
+ help_function
789
+ /
790
+ env_function
791
+ /
792
+ print_line_function
793
+ /
794
+ print_function
795
+ /
796
+ size_function
797
+ /
798
+ split_function
799
+ /
800
+ to_int_function
801
+ /
802
+ to_float_function
803
+ /
804
+ to_array_function
805
+ /
806
+ round_function
807
+ /
808
+ min_function
809
+ /
810
+ max_function
811
+ /
812
+ sum_function
813
+ /
814
+ mult_function
815
+ /
816
+ avg_sum_function
817
+ /
818
+ avg_function
819
+ /
820
+ matching_ids_function
821
+ /
822
+ values_of_type_function
823
+ end
824
+
825
+ rule help_function
826
+ 'HELP' {
827
+ def eval(env = Environment.new)
828
+ to_s(env)
829
+ end
830
+
831
+ def to_s(env = Environment.new)
832
+ help = "-----------------------------------------\n"
833
+ help = " TRXL Language HELP \n"
834
+ help = "-----------------------------------------\n"
835
+ help << "1) Built in operators:\n"
836
+ help << " +,-,*,/,%,==,!=,<=,>=,<,>,;\n"
837
+ help << "-----------------------------------------\n"
838
+ help << "2) Integers and floats in arithmetics:\n"
839
+ help << " 1 or 2.33333 or 0.34 or .34\n"
840
+ help << "-----------------------------------------\n"
841
+ help << "3) Arbitrary nesting of parentheses:\n"
842
+ help << " (1+2*(5+((3+4)*3)-6/2)+7*2)\n"
843
+ help << " => 61\n"
844
+ help << "-----------------------------------------\n"
845
+ help << "4) Comments:\n"
846
+ help << " # A comment until the end of the line\n"
847
+ help << " /* A longer comment that\n"
848
+ help << " spans multiple lines\n"
849
+ help << " */\n"
850
+ help << "-----------------------------------------\n"
851
+ help << "5) Built in keywords:\n"
852
+ help << " TRUE,FALSE,NULL,IF,ELSE,END\n"
853
+ help << "-----------------------------------------\n"
854
+ help << "6) Built in functions:\n"
855
+ help << " HELP,ENV,SIZE,SPLIT,ROUND,MIN,MAX\n"
856
+ help << " SUM,MULT,AVG, PRINT, PRINT_LINE\n"
857
+ help << " TO_INT, TO_FLOAT, TO_ARRAY\n"
858
+ help << "-----------------------------------------\n"
859
+ help << "7) Standard library functions:\n"
860
+ help << " Use to iterate over Arrays or Strings\n"
861
+ help << " FOREACH_IN, INJECT\n"
862
+ help << "-----------------------------------------\n"
863
+ help << "8) Access the current environment:\n"
864
+ help << " ENV; (your output may differ)\n"
865
+ help << " => { :a => 3, :foo => 5 }\n"
866
+ help << " Given the following environment:\n"
867
+ help << " { :a => 1, :b => 2, :c => 3 }\n"
868
+ help << " ENV['a']\n"
869
+ help << " => 1\n"
870
+ help << " ENV['a'..'b']\n"
871
+ help << " => { :a => 1, :b => 2 }\n"
872
+ help << "-----------------------------------------\n"
873
+ help << "9) Numeric variables and literals\n"
874
+ help << " 3;\n"
875
+ help << " => 3\n"
876
+ help << " a = 3;\n"
877
+ help << " => 3\n"
878
+ help << " a;\n"
879
+ help << " => 3\n"
880
+ help << "-----------------------------------------\n"
881
+ help << "10) String variables and literals\n"
882
+ help << " \"This is a string\";\n"
883
+ help << " => \"This is a string\";\n"
884
+ help << " 'This is a string';\n"
885
+ help << " => \"This is a string\";\n"
886
+ help << " s1 = \"This is a string\"; s1;\n"
887
+ help << " => \"This is a string\"\n"
888
+ help << " s2 = 'This is a string'; s2;\n"
889
+ help << " => \"This is a string\"\n"
890
+ help << " SIZE(s1);\n"
891
+ help << " => 16\n"
892
+ help << " SIZE(\"foo\");\n"
893
+ help << " => 3\n"
894
+ help << "-----------------------------------------\n"
895
+ help << "11) Variables and closure applications\n"
896
+ help << " a = 3; foo = 5;\n"
897
+ help << " calc = fun(x,y) { (x + y) * a + foo };\n"
898
+ help << " calc(2,2);\n"
899
+ help << " => 17\n"
900
+ help << "-----------------------------------------\n"
901
+ help << "12) Array variables and literals\n"
902
+ help << " arr = [1, [fun(){2}()], fun(x){x}(3)]\n"
903
+ help << " SIZE(arr);\n"
904
+ help << " => 3\n"
905
+ help << " SIZE([1,2,3]);\n"
906
+ help << " => 3\n"
907
+ help << " [1,2,3] + [4,[5,6]];\n"
908
+ help << " => [1,2,3,4,[5,6]]\n"
909
+ help << " [1,2,3] - [[1],2,3];\n"
910
+ help << " => [1]\n"
911
+ help << "-----------------------------------------\n"
912
+ help << "13) Hash variables and literals\n"
913
+ help << " h = { 1 => fun(){2}(), 'a' => 'foo' }\n"
914
+ help << " SIZE(h);\n"
915
+ help << " => 2\n"
916
+ help << " h[1];\n"
917
+ help << " => 'fun(){2}()'\n"
918
+ help << " h['a'];\n"
919
+ help << " => 'foo'\n"
920
+ help << " SIZE({ 1 => 2});\n"
921
+ help << " => 1\n"
922
+ help << "-----------------------------------------\n"
923
+ help << "14) Range variables and literals\n"
924
+ help << " range_including_upper = 1..5\n"
925
+ help << " => [ 1, 2, 3, 4, 5 ]\n"
926
+ help << " SIZE(range_including_upper);\n"
927
+ help << " => 5\n"
928
+ help << " range_excluding_upper = 1...5\n"
929
+ help << " => [ 1, 2, 3, 4 ]\n"
930
+ help << " SIZE(range_excluding_upper);\n"
931
+ help << " => 4\n"
932
+ help << " SIZE([1..5);\n"
933
+ help << " => 5\n"
934
+ help << "-----------------------------------------\n"
935
+ help << "15) Conditional branching and recursion:\n"
936
+ help << " factorial = fun(x) {\n"
937
+ help << " if(x == 0)\n"
938
+ help << " 1\n"
939
+ help << " else\n"
940
+ help << " x * factorial(x - 1)\n"
941
+ help << " end\n"
942
+ help << " }\n"
943
+ help << " factorial(5);\n"
944
+ help << " => 120\n"
945
+ help << "-----------------------------------------\n"
946
+ help << "16) Conditional branching:\n"
947
+ help << " foo = fun(x) {\n"
948
+ help << " if(x == 0)\n"
949
+ help << " 0\n"
950
+ help << " elsif(x == 1)\n"
951
+ help << " 1\n"
952
+ help << " else\n"
953
+ help << " 2\n"
954
+ help << " end\n"
955
+ help << " }\n"
956
+ help << " foo(0);\n"
957
+ help << " => 0\n"
958
+ help << " foo(1);\n"
959
+ help << " => 1\n"
960
+ help << " foo(2);\n"
961
+ help << " => 2\n"
962
+ help << "-----------------------------------------\n"
963
+ help << "17) case expressions:\n"
964
+ help << " foo = fun(x) {\n"
965
+ help << " case x\n"
966
+ help << " when 0 then 0\n"
967
+ help << " when 1 then 1\n"
968
+ help << " when 2 then 2\n"
969
+ help << " else 3\n"
970
+ help << " end\n"
971
+ help << " }\n"
972
+ help << " foo(1);\n"
973
+ help << " => 1\n"
974
+ help << " foo(3);\n"
975
+ help << " => 3\n"
976
+ help << "-----------------------------------------\n"
977
+ help
978
+ end
979
+ }
980
+ end
981
+
982
+ rule env_function
983
+ 'ENV' space '[' space range_literal space ']' {
984
+ def eval(env = Environment.new)
985
+ if range_literal.range_type(env) == :string
986
+ env_range = range_literal.eval(env)
987
+ #Hash[*(env.select{ |k,v| env_range.include?(k.to_s) }).flatten]
988
+ env.select{ |k,v| env_range.include?(k.to_s) }.map { |pair| pair[1] }
989
+ else
990
+ raise Trxl::InvalidOperationException, "ENV range not of type String"
991
+ end
992
+ end
993
+ }
994
+ /
995
+ 'ENV' space '[' space expression space ']' {
996
+ def eval(env = Environment.new)
997
+ env[expression.eval(env).to_sym]
998
+ end
999
+ }
1000
+ /
1001
+ 'ENV' {
1002
+ def eval(env = Environment.new)
1003
+ env
1004
+ end
1005
+ }
1006
+ end
1007
+
1008
+ rule print_line_function
1009
+ 'PRINT_LINE' space '(' space expression space ')' {
1010
+ def eval(env = Environment.new)
1011
+ result = expression.eval(env)
1012
+ puts (result.is_a?(Array) || result.is_a?(Hash)) ? result.inspect : result.to_s
1013
+ end
1014
+ }
1015
+ /
1016
+ 'PRINT_LINE' space '(' space ')' {
1017
+ def eval(env = Environment.new)
1018
+ puts
1019
+ end
1020
+ }
1021
+ end
1022
+
1023
+ rule print_function
1024
+ 'PRINT' space '(' space expression space ')' {
1025
+ def eval(env = Environment.new)
1026
+ result = expression.eval(env)
1027
+ print (result.is_a?(Array) || result.is_a?(Hash)) ? result.inspect : result.to_s
1028
+ end
1029
+ }
1030
+ end
1031
+
1032
+ rule size_function
1033
+ 'SIZE' space '(' space expression space ')' {
1034
+ def eval(env = Environment.new)
1035
+ result = expression.eval(env)
1036
+ if result.respond_to?(:length)
1037
+ result.length
1038
+ else
1039
+ raise Trxl::InvalidOperationException, "Argument is not Enumerable"
1040
+ end
1041
+ end
1042
+ }
1043
+ end
1044
+
1045
+ rule split_function
1046
+ 'SPLIT' space '(' space split_string:expression space ',' space split_char:expression space ')' {
1047
+ def eval(env = Environment.new)
1048
+ string, char = split_string.eval(env), split_char.eval(env)
1049
+ if string.is_a?(String) && char.is_a?(String)
1050
+ string.split(char)
1051
+ else
1052
+ raise Trxl::InvalidArgumentException, "Both arguments must be of type String"
1053
+ end
1054
+ end
1055
+ }
1056
+ end
1057
+
1058
+ rule to_int_function
1059
+ 'TO_INT' space '(' space expression space ')' {
1060
+ def eval(env = Environment.new)
1061
+ expression.eval(env).to_i
1062
+ end
1063
+ }
1064
+ end
1065
+
1066
+ rule to_float_function
1067
+ 'TO_FLOAT' space '(' space expression space ')' {
1068
+ def eval(env = Environment.new)
1069
+ expression.eval(env).to_f
1070
+ end
1071
+ }
1072
+ end
1073
+
1074
+ rule to_array_function
1075
+ 'TO_ARRAY' space '(' space expression space ')' {
1076
+ def eval(env = Environment.new)
1077
+ result = expression.eval(env)
1078
+ if result.is_a?(Array)
1079
+ result
1080
+ elsif result.is_a?(Hash)
1081
+ result.to_a
1082
+ else
1083
+ [ result ]
1084
+ end
1085
+ end
1086
+ }
1087
+ end
1088
+
1089
+ rule round_function
1090
+ 'ROUND' space '(' space value:expression space ',' space digits:expression space ')' {
1091
+ def eval(env = Environment.new)
1092
+ if ((v = value.eval(env)) && !v.is_a?(TrueClass))
1093
+ format("%0.#{digits.eval(env)}f", v).to_f
1094
+ else
1095
+ nil
1096
+ end
1097
+ end
1098
+ }
1099
+ end
1100
+
1101
+ rule sum_function
1102
+ 'SUM' space '(' space expression more_expressions:( space ',' space expression)* space ')' {
1103
+ def eval(env = Environment.new)
1104
+ evaluated_expressions(env).compact.inject(0) do |sum, val|
1105
+ sum + if val.is_a?(Array)
1106
+ val.flatten.compact.inject(0) { |next_sum, v| next_sum + v }
1107
+ else
1108
+ val
1109
+ end
1110
+ end
1111
+ end
1112
+
1113
+ def evaluated_expressions(env = Environment.new)
1114
+ expressions.map { |e| e.eval(env) }
1115
+ end
1116
+
1117
+ def expressions
1118
+ [ expression ] + more_expressions.elements.map { |e| e.expression }
1119
+ end
1120
+ }
1121
+ /
1122
+ 'SUM' space '(' space ')' {
1123
+ def eval(env = Environment.new)
1124
+ 0
1125
+ end
1126
+ }
1127
+ end
1128
+
1129
+ rule mult_function
1130
+ 'MULT' space '(' space expression more_expressions:( space ',' space expression)* space ')' {
1131
+ def eval(env = Environment.new)
1132
+ values = evaluated_expressions(env).compact
1133
+ return 0 if values.size == 0
1134
+ values.inject(1) do |sum, val|
1135
+ sum * if val.is_a?(Array)
1136
+ val.flatten.compact.inject(1) { |next_sum, v| next_sum * v }
1137
+ else
1138
+ val
1139
+ end
1140
+ end
1141
+ end
1142
+
1143
+ def evaluated_expressions(env = Environment.new)
1144
+ expressions.map { |e| e.eval(env) }
1145
+ end
1146
+
1147
+ def expressions
1148
+ [ expression ] + more_expressions.elements.map { |e| e.expression }
1149
+ end
1150
+ }
1151
+ /
1152
+ 'MULT' space '(' space ')' {
1153
+ def eval(env = Environment.new)
1154
+ 0
1155
+ end
1156
+ }
1157
+ end
1158
+
1159
+ rule avg_function
1160
+ 'AVG' space '(' space expression more_expressions:( space ',' space expression)* space ')' {
1161
+ def eval(env = Environment.new)
1162
+ strict = true
1163
+ nr_of_vals = 0
1164
+ values = expressions
1165
+ strict_flag = values[0].eval(env)
1166
+ if strict_flag.is_a?(TrueClass) || strict_flag.is_a?(FalseClass)
1167
+ values.shift
1168
+ strict = strict_flag
1169
+ end
1170
+
1171
+ # if all values are nil return nil
1172
+ values = values.map { |v| v.eval(env) }
1173
+ return nil if values.compact.size == 0
1174
+
1175
+ s = values.inject(0) do |sum, next_val|
1176
+ sum + if next_val.is_a?(Array)
1177
+ next_val.flatten.inject(0) do |next_sum, val|
1178
+ nr_of_vals += 1 if val && (strict || (!strict && val != 0))
1179
+ next_sum + (val || 0)
1180
+ end
1181
+ else
1182
+ nr_of_vals += 1 if next_val && (strict || (!strict && next_val != 0))
1183
+ next_val || 0
1184
+ end
1185
+ end
1186
+ (s != 0 && nr_of_vals != 0) ? s.to_f / nr_of_vals : 0
1187
+ end
1188
+
1189
+ def expressions
1190
+ [ expression ] + more_expressions.elements.map { |e| e.expression }
1191
+ end
1192
+ }
1193
+ /
1194
+ 'AVG' space '(' space ')' {
1195
+ def eval(env = Environment.new)
1196
+ 0
1197
+ end
1198
+ }
1199
+ end
1200
+
1201
+ rule avg_sum_function
1202
+ 'AVG_SUM' space '(' space expression more_expressions:( space ',' space expression)* space ')' {
1203
+ def eval(env = Environment.new)
1204
+ strict = true
1205
+ nr_of_vals = 0
1206
+ values = expressions
1207
+ strict_flag = values[0].eval(env)
1208
+ if strict_flag.is_a?(TrueClass) || strict_flag.is_a?(FalseClass)
1209
+ values.shift
1210
+ strict = strict_flag
1211
+ end
1212
+ values.inject(0) do |sum, e|
1213
+ next_val = e.eval(env)
1214
+ sum + if next_val.is_a?(Array)
1215
+ nr_of_vals = 0
1216
+ res = next_val.inject(0) do |next_sum, val|
1217
+ if val.is_a?(Array)
1218
+ next_sum + val.inject(0) { |s, v| s + (v || 0) } / val.compact.size
1219
+ else
1220
+ nr_of_vals += 1 if val && (strict || (!strict && val != 0))
1221
+ next_sum + (val || 0)
1222
+ end
1223
+ end
1224
+ nr_of_vals != 0 ? res / nr_of_vals : res
1225
+ else
1226
+ next_val || 0
1227
+ end
1228
+ end
1229
+ end
1230
+
1231
+ def expressions
1232
+ [ expression ] + more_expressions.elements.map { |e| e.expression }
1233
+ end
1234
+ }
1235
+ /
1236
+ 'AVG_SUM' space '(' space ')' {
1237
+ def eval(env = Environment.new)
1238
+ 0
1239
+ end
1240
+ }
1241
+ end
1242
+
1243
+ rule min_function
1244
+ 'MIN' space '(' space expression more_expressions:( space ',' space expression)* space ')' {
1245
+ def eval(env = Environment.new)
1246
+ expressions.map { |e| e.eval(env) }.min
1247
+ end
1248
+
1249
+ def expressions
1250
+ [ expression ] + more_expressions.elements.map { |e| e.expression }
1251
+ end
1252
+ }
1253
+ /
1254
+ 'MIN' space '(' space ')' {
1255
+ def eval(env = Environment.new)
1256
+ 0
1257
+ end
1258
+ }
1259
+ end
1260
+
1261
+ rule max_function
1262
+ 'MAX' space '(' space expression more_expressions:( space ',' space expression)* space ')' {
1263
+ def eval(env = Environment.new)
1264
+ expressions.map { |e| e.eval(env) }.max
1265
+ end
1266
+
1267
+ def expressions
1268
+ [ expression ] + more_expressions.elements.map { |e| e.expression }
1269
+ end
1270
+ }
1271
+ /
1272
+ 'MAX' space '(' space ')' {
1273
+ def eval(env = Environment.new)
1274
+ 0
1275
+ end
1276
+ }
1277
+ end
1278
+
1279
+ rule matching_ids_function
1280
+ 'MATCHING_IDS' space '(' space match_exp:expression space ',' space hash:expression space ')' {
1281
+ def eval(env = Environment.new)
1282
+ if(h = hash.eval(env)).is_a?(Hash)
1283
+ h.select { |k, v| v == match_exp.eval(env) }.map { |entry| entry[0] }
1284
+ else
1285
+ []
1286
+ end
1287
+ end
1288
+ }
1289
+ /
1290
+ 'MATCHING_IDS' space '(' space ')' {
1291
+ def eval(env = Environment.new)
1292
+ []
1293
+ end
1294
+ }
1295
+ end
1296
+
1297
+ rule values_of_type_function
1298
+ 'VALUES_OF_TYPE' space '('
1299
+ space match_exp:expression space ','
1300
+ space all_types:expression space ','
1301
+ space all_values:expression space
1302
+ ')' {
1303
+ def eval(env = Environment.new)
1304
+ types = all_types.eval(env)
1305
+ if types.is_a?(Hash)
1306
+ values = all_values.eval(env)
1307
+ if values.is_a?(Hash)
1308
+ types.select { |k, v| v == match_exp.eval(env) }.map do |entry|
1309
+ values[entry[0]]
1310
+ end
1311
+ else
1312
+ raise Trxl::InvalidArgumentException, "Third parameter must be a Hash"
1313
+ end
1314
+ else
1315
+ raise Trxl::InvalidArgumentException, "Second parameter must be a Hash"
1316
+ end
1317
+ end
1318
+ }
1319
+ /
1320
+ 'VALUES_OF_TYPE' space '(' space ')' {
1321
+ def eval(env = Environment.new)
1322
+ []
1323
+ end
1324
+ }
1325
+ end
1326
+
1327
+
1328
+
1329
+ rule non_space_char
1330
+ !white .
1331
+ end
1332
+
1333
+
1334
+ rule require_keyword
1335
+ 'require' !non_space_char
1336
+ end
1337
+
1338
+ rule case_keyword
1339
+ 'case' !non_space_char
1340
+ end
1341
+
1342
+ rule when_keyword
1343
+ 'when' !non_space_char
1344
+ end
1345
+
1346
+ rule then_keyword
1347
+ 'then' !non_space_char
1348
+ end
1349
+
1350
+ rule if_keyword
1351
+ 'if' &('(' / SPACE)
1352
+ end
1353
+
1354
+ rule else_keyword
1355
+ 'else' SPACE
1356
+ end
1357
+
1358
+ rule end_keyword
1359
+ 'end' &( ';' / '}' / space)
1360
+ end
1361
+
1362
+
1363
+ rule comment
1364
+ comment_to_eol / multiline_comment
1365
+ end
1366
+
1367
+ rule multiline_comment
1368
+ '/*' (!'*/' . )* '*/'
1369
+ end
1370
+
1371
+ rule comment_to_eol
1372
+ # TODO find out why this doesn't work in specs
1373
+ #'#' (!"\n" .)+ "\n"
1374
+
1375
+ '#' (!"\n" .)*
1376
+ end
1377
+
1378
+
1379
+ # whitespace
1380
+ rule white
1381
+ [ \r\t\n]+
1382
+ end
1383
+
1384
+ # mandatory space
1385
+ rule SPACE
1386
+ (white / comment)+
1387
+ end
1388
+
1389
+ # optional space
1390
+ rule space
1391
+ SPACE?
1392
+ end
1393
+
1394
+ end