skeem 0.0.23 → 0.0.24

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.
@@ -1,309 +1,44 @@
1
1
  # Classes that implement nodes of Abstract Syntax Trees (AST) representing
2
2
  # Skeem parse results.
3
3
 
4
- require 'forwardable'
4
+ require_relative 'skm_simple_datum'
5
+ require_relative 'skm_compound_datum'
6
+ require_relative 'skm_unary_expression'
5
7
 
6
8
  module Skeem
7
9
  class SkmUndefined
8
10
  def value
9
11
  :UNDEFINED
10
12
  end
11
- end # class
12
-
13
-
14
- # Abstract class. Generalization of any S-expr element.
15
- SkmElement = Struct.new(:position) do
16
- def initialize(aPosition)
17
- self.position = aPosition
18
- end
19
-
20
- def evaluate(_runtime)
21
- raise NotImplementedError, "Missing implementation of #{self.class.name}"
22
- end
23
-
24
- def done!()
25
- # Do nothing
26
- end
27
-
28
- def number?
29
- false
30
- end
31
-
32
- def real?
33
- false
34
- end
35
-
36
- def integer?
37
- false
38
- end
39
-
40
- def boolean?
41
- false
42
- end
43
-
44
- def string?
45
- false
46
- end
47
-
48
- def symbol?
49
- false
50
- end
51
-
52
- def list?
53
- false
54
- end
55
-
56
- def null?
57
- false
58
- end
59
-
60
- def vector?
61
- false
62
- end
63
-
64
- # Abstract method.
65
- # Part of the 'visitee' role in Visitor design pattern.
66
- # @param _visitor[ParseTreeVisitor] the visitor
67
- def accept(_visitor)
68
- raise NotImplementedError
69
- end
70
-
71
- def inspect
72
- raise NotImplementedError, "Missing #{self.class}#inspect method."
73
- end
74
-
75
- protected
76
-
77
- def inspect_prefix
78
- "<#{self.class.name}: "
79
- end
80
-
81
- def inspect_suffix
82
- '>'
83
- end
84
- end # struct
85
-
86
- # Abstract class. Root of class hierarchy needed for Interpreter
87
- # design pattern
88
- class SkmTerminal < SkmElement
89
- attr_reader :token
90
- attr_reader :value
91
-
92
- def initialize(aToken, aPosition)
93
- super(aPosition)
94
- @token = aToken
95
- init_value(aToken.lexeme)
96
- end
97
-
98
- def self.create(aValue)
99
- lightweight = self.allocate
100
- lightweight.init_value(aValue)
101
- return lightweight
102
- end
103
-
104
- def symbol()
105
- token.terminal
106
- end
107
-
108
- def evaluate(_runtime)
109
- return self
110
- end
111
-
112
- def inspect()
113
- inspect_prefix + value.to_s + inspect_suffix
114
- end
115
-
116
- def done!()
117
- # Do nothing
118
- end
119
-
120
- # Part of the 'visitee' role in Visitor design pattern.
121
- # @param aVisitor[ParseTreeVisitor] the visitor
122
- def accept(aVisitor)
123
- aVisitor.visit_terminal(self)
124
- end
125
-
126
- # This method can be overriden
127
- def init_value(aValue)
128
- @value = aValue
129
- end
130
- end # class
131
-
132
- class SkmBoolean < SkmTerminal
133
- def boolean?
134
- true
135
- end
136
- end # class
137
-
138
- class SkmNumber < SkmTerminal
139
- def number?
140
- true
141
- end
142
- end # class
143
-
144
- class SkmReal < SkmNumber
145
- def real?
146
- true
147
- end
148
- end # class
149
-
150
- class SkmInteger < SkmReal
151
- def integer?
152
- true
153
- end
154
- end # class
155
-
156
- class SkmString < SkmTerminal
157
- # Override
158
- def init_value(aValue)
159
- super(aValue.dup)
160
- end
161
-
162
- def string?
163
- true
164
- end
165
- end # class
166
-
167
- class SkmIdentifier < SkmTerminal
168
- # Override
169
- def init_value(aValue)
170
- super(aValue.dup)
171
- end
172
-
173
- def symbol?
174
- true
175
- end
176
- end # class
177
-
178
- class SkmReserved < SkmIdentifier
179
- end # class
180
-
181
-
182
- class SkmList < SkmElement
183
- attr_accessor(:members)
184
- extend Forwardable
185
-
186
- def_delegators :@members, :each, :first, :last, :length, :empty?, :size
187
-
188
- def initialize(theMembers)
189
- super(nil)
190
- @members = theMembers.nil? ? [] : theMembers
191
- end
192
-
193
- def head()
194
- return members.first
195
- end
196
-
197
- def tail()
198
- SkmList.new(members.slice(1..-1))
199
- end
200
-
201
- def list?
202
- true
203
- end
204
13
 
205
- def null?
206
- empty?
207
- end
208
-
209
- def evaluate(aRuntime)
210
- list_evaluated = members.map { |elem| elem.evaluate(aRuntime) }
211
- SkmList.new(list_evaluated)
212
- end
213
-
214
- # Factory method.
215
- # Construct an Enumerator that will return iteratively the result
216
- # of 'evaluate' method of each members of self.
217
- def to_eval_enum(aRuntime)
218
- =begin
219
- elements = self.members
220
-
221
- new_enum = Enumerator.new do |result|
222
- context = aRuntime
223
- elements.each { |elem| result << elem.evaluate(context) }
14
+ def ==(other)
15
+ return true if other.kind_of?(SkmUndefined)
16
+
17
+ result = case other
18
+ when Symbol
19
+ self.value == other
20
+ when String
21
+ self.value.to_s == other
22
+ else
23
+ raise StandardError, other.inspect
224
24
  end
225
-
226
- new_enum
227
- =end
228
- members.map { |elem| elem.evaluate(aRuntime) }
229
- end
230
-
231
- # Part of the 'visitee' role in Visitor design pattern.
232
- # @param aVisitor[ParseTreeVisitor] the visitor
233
- def accept(aVisitor)
234
- aVisitor.visit_nonterminal(self)
235
- end
236
-
237
- def done!()
238
- # Do nothing
239
- end
240
-
241
- def inspect()
242
- result = inspect_prefix
243
- members.each { |elem| result << elem.inspect + ', ' }
244
- result.sub!(/, $/, '')
245
- result << inspect_suffix
246
- result
247
- end
248
-
249
- alias children members
250
- alias subnodes members
251
- alias to_a members
252
- alias rest tail
253
- end # class
254
-
255
- class SkmVector < SkmElement
256
- attr_accessor(:elements)
257
- extend Forwardable
258
-
259
- def_delegators :@elements, :each, :length, :empty?, :size
260
-
261
- def initialize(theElements)
262
- super(nil)
263
- @elements = theElements.nil? ? [] : theElements
264
- end
265
-
266
- def vector?
267
- true
268
- end
269
-
270
- def evaluate(aRuntime)
271
- elements_evaluated = elements.map { |elem| elem.evaluate(aRuntime) }
272
- SkmVector.new(elements_evaluated)
273
25
  end
274
-
275
- def inspect()
276
- result = inspect_prefix
277
- elements.each { |elem| result << elem.inspect + ', ' }
278
- result.sub!(/, $/, '')
279
- result << inspect_suffix
280
- result
281
- end
282
-
283
26
  end # class
284
27
 
285
-
286
- class SkmQuotation < SkmElement
287
- attr_accessor(:datum)
288
-
289
- def initialize(aDatum)
290
- super(nil)
291
- @datum = aDatum
292
- end
293
-
294
- def evaluate(aRuntime)
295
- datum
28
+ class SkmMultiExpression < SkmExpression
29
+ # Part of the 'visitee' role in Visitor design pattern.
30
+ # @param _visitor [SkmElementVisitor] the visitor
31
+ def accept(aVisitor)
32
+ aVisitor.visit_multi_expression(self)
296
33
  end
297
34
 
298
- def inspect
299
- result = inspect_prefix
300
- result << datum.inspect
301
- result << inspect_suffix
302
- result
35
+ # @return [Array] the names of attributes referencing child SkmElement.
36
+ def associations
37
+ raise NotImplementedError
303
38
  end
304
- end # class
39
+ end
305
40
 
306
- class SkmDefinition < SkmElement
41
+ class SkmDefinition < SkmMultiExpression
307
42
  attr_reader :variable
308
43
  attr_reader :expression
309
44
 
@@ -339,6 +74,17 @@ module Skeem
339
74
 
340
75
  result
341
76
  end
77
+
78
+ def quasiquote(aRuntime)
79
+ quasi_var = variable.quasiquote(aRuntime)
80
+ quasi_expression = variable.quasiquote(aRuntime)
81
+
82
+ if quasi_var.equal?(variable) && quasi_expression.equal?(expression)
83
+ self
84
+ else
85
+ self.class.new(position, quasi_var, quasi_expression)
86
+ end
87
+ end
342
88
 
343
89
  # call method should only invoked when the expression is a SkmLambda
344
90
  def call(aRuntime, aProcedureCall)
@@ -357,41 +103,13 @@ module Skeem
357
103
  result << inspect_suffix
358
104
  result
359
105
  end
360
- end # class
361
-
362
- class SkmVariableReference < SkmElement
363
- attr_reader :variable
364
-
365
- def initialize(aPosition, aVariable)
366
- super(aPosition)
367
- @variable = aVariable
368
- end
369
-
370
- def evaluate(aRuntime)
371
- var_key = variable.evaluate(aRuntime)
372
- unless aRuntime.include?(var_key.value)
373
- err = StandardError
374
- key = var_key.kind_of?(SkmIdentifier) ? var_key.value : var_key
375
- err_msg = "Unbound variable: '#{key}'"
376
- raise err, err_msg
377
- end
378
- definition = aRuntime.environment.fetch(var_key.value)
379
- result = definition.expression.evaluate(aRuntime)
380
- end
381
-
382
- # Confusing!
383
- # Value, here, means the value of the identifier (the variable's name).
384
- def value()
385
- variable.value
386
- end
387
-
388
- def inspect
389
- result = inspect_prefix + variable.inspect + inspect_suffix
390
- result
106
+
107
+ def associations
108
+ [:variable, :expression]
391
109
  end
392
110
  end # class
393
111
 
394
- class ProcedureCall < SkmElement
112
+ class ProcedureCall < SkmMultiExpression
395
113
  attr_reader :operator
396
114
  attr_reader :operands
397
115
 
@@ -425,17 +143,28 @@ module Skeem
425
143
  # $stderr.puts "## RETURN #{result.inspect}"
426
144
  result
427
145
  end
146
+
147
+ def quasiquote(aRuntime)
148
+ quasi_operator = operator.quasiquote(aRuntime)
149
+ quasi_operands = operands.map { |oper | oper.quasiquote(aRuntime) }
150
+
151
+ self.class.new(position, quasi_operator, quasi_operands)
152
+ end
428
153
 
429
154
  def inspect
430
155
  result = inspect_prefix + operator.inspect + ', '
431
156
  result << '@operands ' + operands.inspect + inspect_suffix
432
157
  result
433
158
  end
159
+
160
+ def associations
161
+ [:operator, :operands]
162
+ end
434
163
 
435
164
  alias children operands
436
165
  end # class
437
166
 
438
- class SkmCondition < SkmElement
167
+ class SkmCondition < SkmMultiExpression
439
168
  attr_reader :test
440
169
  attr_reader :consequent
441
170
  attr_reader :alternate
@@ -457,6 +186,14 @@ module Skeem
457
186
  condition_result = consequent.evaluate(aRuntime)
458
187
  end
459
188
  end
189
+
190
+ def quasiquote(aRuntime)
191
+ quasi_test = test.quasiquote(aRuntime)
192
+ quasi_consequent = consequent.quasiquote(aRuntime)
193
+ quasi_alternate = alternate.quasiquote(aRuntime)
194
+
195
+ self.class.new(position, quasi_test, quasi_consequent, quasi_alternate)
196
+ end
460
197
 
461
198
  def inspect
462
199
  result = inspect_prefix + '@test ' + test.inspect + ', '
@@ -464,6 +201,10 @@ module Skeem
464
201
  result << '@alternate ' + alternate.inspect + inspect_suffix
465
202
  result
466
203
  end
204
+
205
+ def associations
206
+ [:test, :consequent, :alternate]
207
+ end
467
208
  end # class
468
209
 
469
210
  SkmArity = Struct.new(:low, :high) do
@@ -535,7 +276,7 @@ module Skeem
535
276
  end
536
277
  end # class
537
278
 
538
- class SkmLambda < SkmElement
279
+ class SkmLambda < SkmMultiExpression
539
280
  # @!attribute [r] formals
540
281
  # @return [Array<SkmIdentifier>] the argument names
541
282
  attr_reader :formals
@@ -552,7 +293,16 @@ module Skeem
552
293
  def evaluate(aRuntime)
553
294
  formals.evaluate(aRuntime)
554
295
  end
555
-
296
+ =begin
297
+ TODO
298
+ def quasiquote(aRuntime)
299
+ quasi_test = test.quasiquote(aRuntime)
300
+ quasi_consequent = consequent.quasiquote(aRuntime)
301
+ quasi_alternate = alternate.quasiquote(aRuntime)
302
+
303
+ self.class.new(position, quasi_test, quasi_consequent, quasi_alternate)
304
+ end
305
+ =end
556
306
  def call(aRuntime, aProcedureCall)
557
307
  aRuntime.nest
558
308
  bind_locals(aRuntime, aProcedureCall)
@@ -579,13 +329,17 @@ module Skeem
579
329
  result << '@sequence ' + sequence.inspect + inspect_suffix
580
330
  result
581
331
  end
332
+
333
+ def associations
334
+ [:formals, :definitions, :sequence]
335
+ end
582
336
 
583
337
  private
584
338
 
585
339
  def bind_locals(aRuntime, aProcedureCall)
586
340
  actuals = aProcedureCall.operands.members
587
341
  count_actuals = actuals.size
588
-
342
+
589
343
  if (count_actuals < required_arity) ||
590
344
  ((count_actuals > required_arity) && !formals.variadic?)
591
345
  raise StandardError, msg_arity_mismatch(aProcedureCall)
@@ -596,7 +350,7 @@ module Skeem
596
350
  variadic_part_raw = actuals.drop(required_arity)
597
351
  variadic_part = variadic_part_raw.map do |actual|
598
352
  if actual.kind_of?(ProcedureCall)
599
- actual.evaluate(aRuntime)
353
+ actual.evaluate(aRuntime)
600
354
  else
601
355
  actual
602
356
  end
@@ -604,7 +358,7 @@ module Skeem
604
358
  variadic_arg_name = formals.formals.last
605
359
  args_coll = SkmList.new(variadic_part)
606
360
  a_def = SkmDefinition.new(position, variadic_arg_name, args_coll)
607
- a_def.evaluate(aRuntime)
361
+ a_def.evaluate(aRuntime)
608
362
  end
609
363
  end
610
364
 
@@ -643,7 +397,7 @@ module Skeem
643
397
  break if index >= max_index
644
398
  end
645
399
  end
646
-
400
+
647
401
  def msg_arity_mismatch(aProcedureCall)
648
402
  # *** ERROR: wrong number of arguments for #<closure morph> (required 2, got 1)
649
403
  msg1 = "Wrong number of arguments for procedure #{operator} "
@@ -0,0 +1,118 @@
1
+ require 'forwardable'
2
+ require_relative 'skm_element'
3
+
4
+ module Skeem
5
+ # Abstract class.
6
+ class SkmCompoundDatum < SkmElement
7
+ extend Forwardable
8
+
9
+ attr_accessor(:members)
10
+ alias children members
11
+
12
+ def_delegators :@members, :each, :first, :last, :length, :empty?, :size
13
+ alias to_a members
14
+
15
+ def initialize(theMembers)
16
+ super(nil)
17
+ @members = theMembers.nil? ? [] : theMembers
18
+ end
19
+
20
+ def ==(other)
21
+ return true if self.equal?(other)
22
+ result = case other
23
+ when SkmCompoundDatum
24
+ self.class == other.class && self.members == other.members
25
+ when Array
26
+ members == other
27
+ end
28
+ end
29
+
30
+ def evaluate(aRuntime)
31
+ members_eval = members.map { |elem| elem.evaluate(aRuntime) }
32
+ self.class.new(members_eval)
33
+ end
34
+
35
+ # Return this object un-evaluated.
36
+ # Reminder: As terminals are atomic, there is no need to launch a visitor.
37
+ # @param aRuntime [Skeem::Runtime]
38
+ def quasiquote(aRuntime)
39
+ quasi_members = members.map { |elem| elem.quasiquote(aRuntime) }
40
+ self.class.new(quasi_members)
41
+ end
42
+
43
+ # Part of the 'visitee' role in Visitor design pattern.
44
+ # @param aVisitor [SkmElementVisitor] the visitor
45
+ def accept(aVisitor)
46
+ aVisitor.visit_compound_datum(self)
47
+ end
48
+
49
+ protected
50
+
51
+ def inspect_specific
52
+ result = ''
53
+ members.each { |elem| result << elem.inspect + ', ' }
54
+ result.sub!(/, $/, '')
55
+ result
56
+ end
57
+ end # class
58
+
59
+ class SkmList < SkmCompoundDatum
60
+ def tail()
61
+ SkmList.new(members.slice(1..-1))
62
+ end
63
+
64
+ def list?
65
+ true
66
+ end
67
+
68
+ def null?
69
+ empty?
70
+ end
71
+
72
+ def evaluate(aRuntime)
73
+ if empty?
74
+ self.class.new(nil)
75
+ else
76
+ first_evaluated = members.first.evaluate(aRuntime)
77
+
78
+ if first_evaluated.kind_of?(SkmIdentifier)
79
+ aRuntime.evaluate_form(self)
80
+ else
81
+ members_eval = members.map { |elem| elem.evaluate(aRuntime) }
82
+ self.class.new(members_eval)
83
+ end
84
+ end
85
+ end
86
+
87
+
88
+ # Factory method.
89
+ # Construct an Enumerator that will return iteratively the result
90
+ # of 'evaluate' method of each members of self.
91
+ def to_eval_enum(aRuntime)
92
+ =begin
93
+ elements = self.members
94
+
95
+ new_enum = Enumerator.new do |result|
96
+ context = aRuntime
97
+ elements.each { |elem| result << elem.evaluate(context) }
98
+ end
99
+
100
+ new_enum
101
+ =end
102
+ members.map { |elem| elem.evaluate(aRuntime) }
103
+ end
104
+
105
+ def done!()
106
+ # Do nothing
107
+ end
108
+
109
+ alias head first
110
+ alias rest tail
111
+ end # class
112
+
113
+ class SkmVector < SkmCompoundDatum
114
+ def vector?
115
+ true
116
+ end
117
+ end # class
118
+ end # module
@@ -0,0 +1,85 @@
1
+ module Skeem
2
+ # Abstract class. Generalization of any S-expr element.
3
+ SkmElement = Struct.new(:position) do
4
+ def initialize(aPosition)
5
+ self.position = aPosition
6
+ end
7
+
8
+ def number?
9
+ false
10
+ end
11
+
12
+ def real?
13
+ false
14
+ end
15
+
16
+ def integer?
17
+ false
18
+ end
19
+
20
+ def boolean?
21
+ false
22
+ end
23
+
24
+ def string?
25
+ false
26
+ end
27
+
28
+ def symbol?
29
+ false
30
+ end
31
+
32
+ def list?
33
+ false
34
+ end
35
+
36
+ def null?
37
+ false
38
+ end
39
+
40
+ def vector?
41
+ false
42
+ end
43
+
44
+ def evaluate(_runtime)
45
+ raise NotImplementedError, "Missing implementation of #{self.class.name}"
46
+ end
47
+
48
+ def quasiquote(_runtime)
49
+ raise NotImplementedError, "Missing implementation of #{self.class.name}"
50
+ end
51
+
52
+ # Abstract method.
53
+ # Part of the 'visitee' role in Visitor design pattern.
54
+ # @param _visitor [SkmElementVisitor] the visitor
55
+ def accept(_visitor)
56
+ raise NotImplementedError
57
+ end
58
+
59
+ def done!()
60
+ # Do nothing
61
+ end
62
+
63
+ def inspect
64
+ result = inspect_prefix
65
+ result << inspect_specific
66
+ result << inspect_suffix
67
+
68
+ result
69
+ end
70
+
71
+ protected
72
+
73
+ def inspect_prefix
74
+ "<#{self.class.name}: "
75
+ end
76
+
77
+ def inspect_suffix
78
+ '>'
79
+ end
80
+
81
+ def inspect_specific
82
+ raise NotImplementedError
83
+ end
84
+ end # struct
85
+ end # module