yadriggy 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: aa5e75505e86183390b717e3559e79528a06dab1
4
+ data.tar.gz: 6103b4d010e666693876526d6e425ae1bccf6a21
5
+ SHA512:
6
+ metadata.gz: cc3de4fdd6886598cefb8cfac86c8423cd5dda187d124b94495feae4bfd998d60f0f4d269cb659026bfa62625199ab284de67ec3690cc497004f0f7ff0fb0679
7
+ data.tar.gz: cd047b260fe5e66c1775f1532a51cfaf321304b309187ac7214d3d5f5110b05eead8f8806f91a6a810e84a4a361fd76778b36164eff4dbc9cee395c61f120522
@@ -0,0 +1,13 @@
1
+ *~
2
+ .idea
3
+ .DS_Store
4
+ /.bundle/
5
+ /.yardoc
6
+ /Gemfile.lock
7
+ /_yardoc/
8
+ /coverage/
9
+ /doc/
10
+ /pkg/
11
+ /spec/reports/
12
+ /testbin/
13
+ /tmp/
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in yadriggy.gemspec
4
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2018- Shigeru Chiba
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,108 @@
1
+ # Yadriggy
2
+
3
+ Yadriggy (mistletoe in Japanese) is a library for building a
4
+ domain-specific language (DSL) embedded in Ruby. It was developed for
5
+ a particular kind of embedded DSLs, which we call hemiparasitic DSLs.
6
+ These DSLs borrow the syntax from the host language, Ruby, and the
7
+ code written in the DSLs is embedded in normal Ruby code. However,
8
+ the execution of the DSL code is independent of Ruby. Its semantics
9
+ can be totally different from Ruby and the code can be run out of the
10
+ Ruby VM. Hemiparasitic DSLs look like Ruby but they are different
11
+ languages except their syntax.
12
+ They parasitize Ruby by borrowing the syntax but their parasitism is
13
+ hemi; their execution engines are their own.
14
+
15
+ For details, the documentation is available from [Wiki](https://github.com/csg-tokyo/yadriggy/wiki).
16
+
17
+ ## Hemiparasitic DSLs
18
+
19
+ A typical example of hemiparasitic DSL is computation offloading from
20
+ Ruby. For example, Yadriggy provides a simple DSL to offload
21
+ from Ruby to native C language.
22
+
23
+ ```ruby
24
+ require 'yadriggy/c'
25
+
26
+ include Yadriggy::C::CType
27
+
28
+ def fib(n) ! Integer
29
+ typedecl n: Integer
30
+ if n > 1
31
+ return fib(n - 1) + fib(n - 2)
32
+ else
33
+ return 1
34
+ end
35
+ end
36
+
37
+ puts Yadriggy::C.run { return fib(32) }
38
+ ```
39
+
40
+ When this code is run, the block given to `Yadriggy::C.run` is
41
+ translated into C code with the definition of `fib` method.
42
+ Then the C code is compiled into a dynamic library, loaded the
43
+ library through ruby-ffi, and executed. Since the block given to
44
+ `run` calls `fib`, the definition of `fib` is also translated
45
+ into C.
46
+
47
+ An external variable is accessible from the compiled block:
48
+
49
+ ```ruby
50
+ n = 32
51
+ puts Yadriggy::C.run { return fib(n) }
52
+ ```
53
+
54
+ The argument to `fib` is take from the variable `n` that exists
55
+ outside of the block. `n` is passed to the compiled block by _copying_.
56
+ It is not passed by _reference_. Thus, when a new value is assigned to
57
+ `n` within the compiled block, it is not visible from the Ruby code.
58
+ The variable `n` in the Ruby code keeps the old value.
59
+
60
+ Note that the definition of `fib` contains type declarations
61
+ since this DSL is not Ruby.
62
+ A hemiparasitic DSL looks like Ruby but it is a different language.
63
+ `! Integer` following `def fib(n)` specifies the return type.
64
+ `typedecl` specifies the types of the parameters (and local variables
65
+ if any). In this DSL, most types have to be statically given
66
+ although the DSL performs simple type inference and some types
67
+ can be omitted.
68
+
69
+ ## Library
70
+
71
+ Yadriggy provides a method for obtaining the abstract syntax tree (AST)
72
+ of the given method, lambda, or Proc.
73
+ It also provides a syntax checker that determines whether or not an AST
74
+ satisfies the syntax described in the BNF-like language, which is
75
+ a DSL embedded in Ruby by Yadriggy.
76
+
77
+ You can even obtain the AST of a piece of source code:
78
+
79
+ ```ruby
80
+ require 'yadriggy'
81
+ ast = Yadriggy.reify {|a| a + 1 }
82
+ ```
83
+
84
+ `reify` returns the AST of the given block `{|a| a + 1 }`.
85
+ It takes not only a block but also a `Method` or `Proc` object.
86
+
87
+ The idea of `reify` was proposed in the following paper:
88
+
89
+ - Shigeru Chiba, YungYu Zhuang, Maximilian Scherr, "Deeply Reifying Running Code for Constructing a Domain-Specific Language", PPPJ'16, Article No. 1, ACM, August 2016.
90
+
91
+ ## Installation
92
+
93
+ To install, run:
94
+
95
+ $ gem install yadriggy
96
+
97
+ or, download this repository and run:
98
+
99
+ $ bundle exec rake install
100
+
101
+ ## Contributing
102
+
103
+ Bug reports and pull requests are welcome on GitHub at https://github.com/csg-tokyo/yadriggy.
104
+
105
+
106
+ ## License
107
+
108
+ The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
@@ -0,0 +1,10 @@
1
+ require "bundler/gem_tasks"
2
+ require "rake/testtask"
3
+
4
+ Rake::TestTask.new(:test) do |t|
5
+ t.libs << "test"
6
+ t.libs << "lib"
7
+ t.test_files = FileList['test/**/*_test.rb']
8
+ end
9
+
10
+ task :default => :test
@@ -0,0 +1,32 @@
1
+ # Copyright (C) 2017- Shigeru Chiba. All rights reserved.
2
+
3
+ require 'yadriggy/version'
4
+
5
+ require 'yadriggy/source_code'
6
+ require 'yadriggy/ast'
7
+ require 'yadriggy/ast_value'
8
+ require 'yadriggy/ast_location'
9
+ require 'yadriggy/eval'
10
+ require 'yadriggy/eval_all'
11
+ require 'yadriggy/algebra'
12
+ require 'yadriggy/syntax'
13
+ require 'yadriggy/checker'
14
+ require 'yadriggy/typecheck'
15
+ require 'yadriggy/ruby_typecheck'
16
+ require 'yadriggy/ruby_typeinfer'
17
+ require 'yadriggy/printer'
18
+ require 'yadriggy/pretty_print'
19
+
20
+ module Yadriggy
21
+ @@debug = 0
22
+
23
+ # Current debug level (0, 1, or 2).
24
+ # @return [Integer] the current level.
25
+ def self.debug() @@debug end
26
+
27
+ # Sets the current debug level.
28
+ # @param [Integer] level.
29
+ def self.debug=(level)
30
+ @@debug = level
31
+ end
32
+ end
@@ -0,0 +1,497 @@
1
+ # Copyright (C) 2017- Shigeru Chiba. All rights reserved.
2
+
3
+ require 'yadriggy/eval'
4
+
5
+ module Yadriggy
6
+
7
+ # An interface inspired by object algebra (and tag-less final)
8
+ #
9
+ class Algebra
10
+
11
+ def initialize(expr)
12
+ EvalAlgebra.new(self).evaluate(expr)
13
+ end
14
+
15
+ def nil_value()
16
+ raise NotImplementedError.new('nil_value')
17
+ end
18
+
19
+ def name(name, line_no, column)
20
+ raise NotImplementedError.new('name')
21
+ end
22
+
23
+ def identifier_or_call(name, line_no, column)
24
+ name(name, line_no, column)
25
+ end
26
+
27
+ def identifier(name, line_no, column)
28
+ identifier_or_call(name, line_no, column)
29
+ end
30
+
31
+ def reserved(name, line_no, column)
32
+ name(name, line_no, column)
33
+ end
34
+
35
+ def const(name, line_no, column)
36
+ name(name, line_no, column)
37
+ end
38
+
39
+ def label(name, line_no, column)
40
+ name(name, line_no, column)
41
+ end
42
+
43
+ def symbol(name, line_no, column)
44
+ raise NotImplementedError.new('symbol')
45
+ end
46
+
47
+ def global_variable(name, line_no, column)
48
+ name(name, line_no, column)
49
+ end
50
+
51
+ def instance_variable(name, line_no, column)
52
+ name(name, line_no, column)
53
+ end
54
+
55
+ def variable_call(name, line_no, column)
56
+ identifier_or_call(name, line_no, column)
57
+ end
58
+
59
+ def super_method(expr)
60
+ raise NotImplementedError.new('super_method')
61
+ end
62
+
63
+ def number(value, line_no, column)
64
+ raise NotImplementedError.new('number')
65
+ end
66
+
67
+ # exprs() sequentially processes each expression in a series
68
+ # of expressions.
69
+ # So, for example, { e1, e2, ..., e_n } is
70
+ # processed by exprs(.. exprs(exprs(nil, e1), e2).., e_n).
71
+ # In other words, it is by
72
+ # [e1, e2, ..., e_n].inject(nil) {|r,e| exprs(r, evaluate(e))}.
73
+ #
74
+ # RESULT specifies the result of the previous expression.
75
+ # It is nil if EXPR is the first expression.
76
+ #
77
+ def exprs(result, expr)
78
+ raise NotImplementedError.new('exprs')
79
+ end
80
+
81
+ # CONTENTS is an array of the results of evaluating
82
+ # each component.
83
+ #
84
+ def string_interpolation(contents)
85
+ raise NotImplementedError.new('string_interpolation')
86
+ end
87
+
88
+ def string_literal(value, line_no, column)
89
+ raise NotImplementedError.new('string_literal')
90
+ end
91
+
92
+ # A constant value in a scope, such as Foo::Bar.
93
+ #
94
+ def const_path_ref(scope, name)
95
+ raise NotImplementedError.new('const_path_ref')
96
+ end
97
+
98
+ # A constant value in a scope as a L-value.
99
+ #
100
+ def const_path_field(scope, name)
101
+ const_path_ref(scope, name)
102
+ end
103
+
104
+ def unary(op, expr)
105
+ raise NotImplementedError.new('unary')
106
+ end
107
+
108
+ def binary(left, op, right)
109
+ raise NotImplementedError.new('binary')
110
+ end
111
+
112
+ def dots(left, op, right)
113
+ binary(left, op, right)
114
+ end
115
+
116
+ def assign(left, op, right)
117
+ binary(left, op, right)
118
+ end
119
+
120
+ def array_ref(array, index)
121
+ raise NotImplementedError.new('array')
122
+ end
123
+
124
+ # Array reference as L-value
125
+ #
126
+ def array_ref_field(array, index)
127
+ array_ref(array, index)
128
+ end
129
+
130
+ # An expression surrounded with ().
131
+ #
132
+ def paren(element)
133
+ raise NotImplementedError.new('paren')
134
+ end
135
+
136
+ # An array literal.
137
+ # ELEMENTS is an array.
138
+ #
139
+ def array(elements)
140
+ raise NotImplementedError.new('array')
141
+ end
142
+
143
+ # PAIRS is an array of pairs. Each pair is
144
+ # an array where the first element is a key
145
+ # and the second element is a value.
146
+ #
147
+ def hash(pairs)
148
+ raise NotImplementedError.new('hash')
149
+ end
150
+
151
+ # Method call.
152
+ # ARGS is an array. BLOCK is a thunk.
153
+ #
154
+ def call(receiver, op, name, args, block_arg, block)
155
+ raise NotImplementedError.new('call')
156
+ end
157
+
158
+ # Method call without parentheses.
159
+ # ARGS is an array. BLOCK is a thunk.
160
+ #
161
+ def command(receiver, op, name, args, block_arg, block)
162
+ call(receiver, op, name, args, block_arg, block)
163
+ end
164
+
165
+ # if, unless, modifier if/unless, and ternary if (?:)
166
+ # THEN_EXPRS is a thunk.
167
+ # ALL_ELSIF is an array of pairs of elsif condition and its body.
168
+ # Both are thunks.
169
+ # ELSE_EXPRS is a thiuk or nil.
170
+ #
171
+ def conditional(op, cond, then_exprs, all_elsif, else_exprs)
172
+ raise NotImplementedError.new('conditional')
173
+ end
174
+
175
+ # while, until, and modifier while/until.
176
+ # COND and BODY are thunks.
177
+ #
178
+ def loop(op, cond, body)
179
+ raise NotImplementedError.new('loop')
180
+ end
181
+
182
+ # for loop.
183
+ # BODY is a thunk.
184
+ #
185
+ def for_loop(var, set, body)
186
+ raise NotImplementedError.new('for_loop')
187
+ end
188
+
189
+ # break, next, redo, retry.
190
+ #
191
+ def break_out(op, values)
192
+ raise NotImplementedError.new('break_out')
193
+ end
194
+
195
+ # An expression with return.
196
+ #
197
+ def return_values(values)
198
+ raise NotImplementedError.new('return_values')
199
+ end
200
+
201
+ # A block.
202
+ # BODY is a thunk.
203
+ #
204
+ def block(params, optionals, rest_params, params_after_rest, keywords,
205
+ rest_of_keywords, block_param, body)
206
+ raise NotImplementedError.new('block')
207
+ end
208
+
209
+ # A lambda expression.
210
+ # BODY is a thunk.
211
+ #
212
+ def lambda_expr(params, optionals, rest_params, params_after_rest,
213
+ keywords, rest_of_keywords, block_param, body)
214
+ block(params, optionals, rest_params, params_after_rest,
215
+ keywords, rest_of_keywords, block_param, body)
216
+ end
217
+
218
+ # rescue-else-ensure-end.
219
+ # BODY, NESTED_RESCUE, ELSE_CLAUSE, and ENSURE_CLAUSE are
220
+ # thunks.
221
+ #
222
+ def rescue_end(types, parameter, body, nested_rescue,
223
+ else_clause, ensure_clause)
224
+ raise NotImplementedError.new('rescue_end')
225
+ end
226
+
227
+ def begin_end(body, rescue_clause)
228
+ raise NotImplementedError.new('begin_end')
229
+ end
230
+
231
+ # def.
232
+ #
233
+ # BODY and RESCUE_CLAUSE are thunks.
234
+ #
235
+ def define(name, params, optionals, rest_of_params, params_after_rest,
236
+ keywords, rest_of_keywords, block_param,
237
+ body, rescue_clause)
238
+ raise NotImplementedError.new('define')
239
+ end
240
+
241
+ def module_def(name, body, rescue_clause)
242
+ raise NotImplementedError.new('module_def')
243
+ end
244
+
245
+ def class_def(name, superclass, body, rescue_clause)
246
+ raise NotImplementedError.new('class_def')
247
+ end
248
+
249
+ def singular_class_def(name, body, rescue_clause)
250
+ raise NotImplementedError.new('singular_class_def')
251
+ end
252
+
253
+ # A whole program.
254
+ #
255
+ # ELEMENTS is the result of processing the program elements.
256
+ #
257
+ def program(elements)
258
+ raise NotImplementedError.new('program')
259
+ end
260
+ end
261
+
262
+ # evaluator for Algebra
263
+ #
264
+ class EvalAlgebra < Eval
265
+ def initialize(algebra)
266
+ @algebra = algebra
267
+ end
268
+
269
+ def nil_value(expr)
270
+ @algebra.nil_value
271
+ end
272
+
273
+ def name(expr)
274
+ raise 'should never happen'
275
+ end
276
+
277
+ def identifier(expr)
278
+ @algebra.identifier(expr.name, expr.line_no, expr.column)
279
+ end
280
+
281
+ def reserved(expr)
282
+ @algebra.reserved(expr.name, expr.line_no, expr.column)
283
+ end
284
+
285
+ def const(expr)
286
+ @algebra.const(expr.name, expr.line_no, expr.column)
287
+ end
288
+
289
+ def label(expr)
290
+ @algebra.label(expr.name, expr.line_no, expr.column)
291
+ end
292
+
293
+ def symbol(expr)
294
+ @algebra.symbol(expr.name, expr.line_no, expr.column)
295
+ end
296
+
297
+ def global_variable(expr)
298
+ @algebra.global_variable(expr.name, expr.line_no, expr.column)
299
+ end
300
+
301
+ def instance_variable(expr)
302
+ @algebra.instance_variable(expr.name, expr.line_no, expr.column)
303
+ end
304
+
305
+ def variable_call(expr)
306
+ @algebra.variable_call(expr.name, expr.line_no, expr.column)
307
+ end
308
+
309
+ def super_method(expr) @algebra.super_method end
310
+
311
+ def number(expr)
312
+ @algebra.number(expr.value, expr.line_no, expr.column)
313
+ end
314
+
315
+ def exprs(expr)
316
+ expr.expressions.inject(nil) do |result, e|
317
+ @algebra.exprs(result, evaluate(e))
318
+ end
319
+ end
320
+
321
+ def paren(expr)
322
+ @algebra.paren(evaluate(expr.expression))
323
+ end
324
+
325
+ def array(expr)
326
+ @algebra.array(expr.elements.map {|e| evaluate(e)})
327
+ end
328
+
329
+ def string_interpolation(expr)
330
+ @algebra.string_interpolation(expr.contents.map {|e| evaluate(e) })
331
+ end
332
+
333
+ def string_literal(expr)
334
+ @algebra.string_literal(expr.value, expr.line_no, expr.column)
335
+ end
336
+
337
+ def const_path_ref(expr)
338
+ @algebra.const_path_ref(expr.scope, expr.name)
339
+ end
340
+
341
+ def const_path_field(expr)
342
+ @algebra.const_path_field(expr.scope, expr.name)
343
+ end
344
+
345
+ def unary(expr)
346
+ @algebra.unary(expr.op, evaluate(expr.expr))
347
+ end
348
+
349
+ def binary(expr)
350
+ @algebra.binary(evaluate(expr.left), expr.op, evaluate(expr.right))
351
+ end
352
+
353
+ def dots(expr)
354
+ @algebra.dots(evaluate(expr.left), expr.op, evaluate(expr.right))
355
+ end
356
+
357
+ def assign(expr)
358
+ @algebra.assign(evaluate(expr.left), expr.op, evaluate(expr.right))
359
+ end
360
+
361
+ def array_ref(expr)
362
+ @algebra.array_ref(evaluate(expr.array),
363
+ expr.indexes.map {|e| evaluate(e) })
364
+ end
365
+
366
+ def array_ref_field(expr)
367
+ @algebra.array_ref_field(evaluate(expr.array),
368
+ expr.indexes.map {|e| evaluate(e) })
369
+ end
370
+
371
+ def hash(expr)
372
+ @algebra.hash(expr.pairs.map {|p| p.map {|e| evaluate(e) }})
373
+ end
374
+
375
+ def call(expr)
376
+ @algebra.call(evaluate(expr.receiver), expr.op, expr.name,
377
+ expr.args.map {|e| evaluate(e) },
378
+ evaluate(expr.block_arg),
379
+ lambda { evaluate(expr.block) })
380
+ end
381
+
382
+ def command(expr)
383
+ @algebra.command(evaluate(expr.receiver), expr.op, expr.name,
384
+ expr.args.map {|e| evaluate(e) },
385
+ evaluate(expr.block_arg),
386
+ lambda { evaluate(expr.block) })
387
+ end
388
+
389
+ def conditional(expr)
390
+ @algebra.conditional(expr.op, evaluate(expr.cond),
391
+ lambda { evaluate(expr.then) },
392
+ expr.all_elsif.map do |e|
393
+ [lambda { evaluate(e[0]) },
394
+ lambda { evaluate(e[1]) }]
395
+ end,
396
+ if expr.else.nil?
397
+ nil
398
+ else
399
+ lambda { evaluate(expr.else) }
400
+ end)
401
+ end
402
+
403
+ def loop(expr)
404
+ @algebra.loop(expr.op, lambda { evaluate(expr.cond) },
405
+ lambda { evaluate(expr.body) })
406
+ end
407
+
408
+ def for_loop(expr)
409
+ @algebra.for_loop(expr.vars, evaluate(expr.set),
410
+ lambda { evaluate(expr.body) })
411
+ end
412
+
413
+ def break_out(expr)
414
+ @algebra.break_out(expr.op, expr.values.map {|e| evaluate(e) })
415
+ end
416
+
417
+ def return_values(expr)
418
+ @algebra.return_values(expr.values.map {|e| evaluate(e) })
419
+ end
420
+
421
+ def block(expr)
422
+ @algebra.block(expr.params, expr.optionals, expr.rest_of_params,
423
+ expr.params_after_rest, expr.keywords,
424
+ expr.rest_of_keywords,
425
+ expr.block_param, lambda { evaluate(expr.body) })
426
+ end
427
+
428
+ def lambda_expr(expr)
429
+ @algebra.lambda_expr(expr.params, expr.optionals, expr.rest_of_params,
430
+ expr.params_after_rest, expr.keywords,
431
+ expr.rest_of_keywords,
432
+ expr.block_param, lambda { evaluate(expr.body) })
433
+ end
434
+
435
+ def rescue_end(expr)
436
+ @algebra.rescue_end(expr.types, expr.parameter,
437
+ lambda { evaluate(expr.body) },
438
+ lambda { evaluate(expr.nested_rescue) },
439
+ lambda { evaluate(expr.else) },
440
+ lambda { evaluate(expr.ensure) })
441
+ end
442
+
443
+ def begin_end(expr)
444
+ @algebra.begin_end(lambda { evaluate(expr.body) },
445
+ if expr.rescue.nil?
446
+ nil
447
+ else
448
+ lambda { evaluate(expr.rescue) }
449
+ end)
450
+ end
451
+
452
+ def define(expr)
453
+ @algebra.define(expr.name, expr.params, expr.optionals,
454
+ expr.rest_of_params, expr.params_after_rest,
455
+ expr.keywords, expr.rest_of_keywords,
456
+ expr.block_param, lambda { evaluate(expr.body) },
457
+ if expr.rescue.nil?
458
+ nil
459
+ else
460
+ lambda { evaluate(expr.rescue) }
461
+ end)
462
+ end
463
+
464
+ def module_def(expr)
465
+ @algebra.module_def(expr.name, lambda { evaluate(expr.body) },
466
+ if expr.rescue.nil?
467
+ nil
468
+ else
469
+ lambda { evaluate(expr.rescue) }
470
+ end)
471
+ end
472
+
473
+ def class_def(expr)
474
+ @algebra.class_def(expr.name, expr.superclass,
475
+ lambda { evaluate(expr.body) },
476
+ if expr.rescue.nil?
477
+ nil
478
+ else
479
+ lambda { evaluate(expr.rescue) }
480
+ end)
481
+ end
482
+
483
+ def singular_class_def(expr)
484
+ @algebra.singular_class_def(expr.name,
485
+ lambda { evaluate(expr.body) },
486
+ if expr.rescue.nil?
487
+ nil
488
+ else
489
+ lambda { evaluate(expr.rescue) }
490
+ end)
491
+ end
492
+
493
+ def program(expr)
494
+ @algebra.program(evaluate(expr.elements))
495
+ end
496
+ end
497
+ end