trxl 0.1.5

Sign up to get free protection for your applications and to get access to all the features.
@@ -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