yadriggy 1.0.0

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,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