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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +11 -0
- data/README.md +11 -6
- data/lib/skeem/datum_dsl.rb +139 -0
- data/lib/skeem/element_visitor.rb +91 -0
- data/lib/skeem/grammar.rb +31 -8
- data/lib/skeem/interpreter.rb +1 -1
- data/lib/skeem/primitive/primitive_builder.rb +47 -37
- data/lib/skeem/runtime.rb +28 -4
- data/lib/skeem/s_expr_builder.rb +64 -25
- data/lib/skeem/s_expr_nodes.rb +81 -327
- data/lib/skeem/skm_compound_datum.rb +118 -0
- data/lib/skeem/skm_element.rb +85 -0
- data/lib/skeem/skm_expression.rb +7 -0
- data/lib/skeem/skm_simple_datum.rb +132 -0
- data/lib/skeem/skm_unary_expression.rb +107 -0
- data/lib/skeem/standard/base.skm +3 -3
- data/lib/skeem/tokenizer.rb +8 -1
- data/lib/skeem/version.rb +1 -1
- data/spec/skeem/datum_dsl_spec.rb +191 -0
- data/spec/skeem/element_visitor_spec.rb +170 -0
- data/spec/skeem/interpreter_spec.rb +126 -36
- data/spec/skeem/primitive/primitive_builder_spec.rb +48 -36
- data/spec/skeem/runtime_spec.rb +28 -2
- data/spec/skeem/s_expr_nodes_spec.rb +15 -277
- data/spec/skeem/skm_compound_datum_spec.rb +132 -0
- data/spec/skeem/skm_element_spec.rb +50 -0
- data/spec/skeem/skm_simple_datum_spec.rb +233 -0
- data/spec/skeem/skm_unary_expression_spec.rb +201 -0
- data/spec/skeem/tokenizer_spec.rb +7 -35
- metadata +21 -3
- data/lib/skeem/convertible.rb +0 -23
@@ -0,0 +1,132 @@
|
|
1
|
+
require_relative 'skm_element'
|
2
|
+
|
3
|
+
module Skeem
|
4
|
+
# Abstract class. Root of class hierarchy needed for Interpreter
|
5
|
+
# design pattern
|
6
|
+
class SkmSimpleDatum < SkmElement
|
7
|
+
attr_reader :token
|
8
|
+
attr_reader :value
|
9
|
+
|
10
|
+
def initialize(aToken, aPosition)
|
11
|
+
super(aPosition)
|
12
|
+
@token = aToken
|
13
|
+
init_value(aToken.lexeme)
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.create(aValue)
|
17
|
+
lightweight = self.allocate
|
18
|
+
lightweight.init_value(aValue)
|
19
|
+
return lightweight
|
20
|
+
end
|
21
|
+
|
22
|
+
def symbol()
|
23
|
+
token.terminal
|
24
|
+
end
|
25
|
+
|
26
|
+
# Equality operator.
|
27
|
+
# Returns true when: self and 'other' are identical, or
|
28
|
+
# when they have same value
|
29
|
+
# @param other [SkmSimpleDatum, Object] object to compare with.
|
30
|
+
# @return [TrueClass, FalseClass]
|
31
|
+
def ==(other)
|
32
|
+
return true if self.equal?(other)
|
33
|
+
|
34
|
+
result = if other.kind_of?(SkmSimpleDatum)
|
35
|
+
self.value == other.value
|
36
|
+
else
|
37
|
+
self.value == other
|
38
|
+
end
|
39
|
+
|
40
|
+
result
|
41
|
+
end
|
42
|
+
|
43
|
+
def done!()
|
44
|
+
# Do nothing
|
45
|
+
end
|
46
|
+
|
47
|
+
# Evaluate the Skeem expression represented by this object.
|
48
|
+
# Reminder: terminals evaluate to themselves.
|
49
|
+
# @param _runtime [Skeem::Runtime]
|
50
|
+
def evaluate(_runtime)
|
51
|
+
return self
|
52
|
+
end
|
53
|
+
|
54
|
+
# Return this object un-evaluated.
|
55
|
+
# Reminder: As terminals are atomic, there is no need to launch a visitor.
|
56
|
+
# @param _runtime [Skeem::Runtime]
|
57
|
+
def quasiquote(runtime)
|
58
|
+
evaluate(runtime)
|
59
|
+
end
|
60
|
+
|
61
|
+
# Part of the 'visitee' role in Visitor design pattern.
|
62
|
+
# @param _visitor [SkmElementVisitor] the visitor
|
63
|
+
def accept(aVisitor)
|
64
|
+
aVisitor.visit_simple_datum(self)
|
65
|
+
end
|
66
|
+
|
67
|
+
# This method can be overriden
|
68
|
+
def init_value(aValue)
|
69
|
+
@value = aValue
|
70
|
+
end
|
71
|
+
|
72
|
+
protected
|
73
|
+
|
74
|
+
def inspect_specific
|
75
|
+
value.to_s
|
76
|
+
end
|
77
|
+
end # class
|
78
|
+
|
79
|
+
|
80
|
+
class SkmBoolean < SkmSimpleDatum
|
81
|
+
def boolean?
|
82
|
+
true
|
83
|
+
end
|
84
|
+
end # class
|
85
|
+
|
86
|
+
class SkmNumber < SkmSimpleDatum
|
87
|
+
def number?
|
88
|
+
true
|
89
|
+
end
|
90
|
+
end # class
|
91
|
+
|
92
|
+
class SkmReal < SkmNumber
|
93
|
+
def real?
|
94
|
+
true
|
95
|
+
end
|
96
|
+
end # class
|
97
|
+
|
98
|
+
class SkmInteger < SkmReal
|
99
|
+
def integer?
|
100
|
+
true
|
101
|
+
end
|
102
|
+
end # class
|
103
|
+
|
104
|
+
class SkmString < SkmSimpleDatum
|
105
|
+
# Override
|
106
|
+
def init_value(aValue)
|
107
|
+
super(aValue.dup)
|
108
|
+
end
|
109
|
+
|
110
|
+
def string?
|
111
|
+
true
|
112
|
+
end
|
113
|
+
|
114
|
+
def length
|
115
|
+
value.length
|
116
|
+
end
|
117
|
+
end # class
|
118
|
+
|
119
|
+
class SkmIdentifier < SkmSimpleDatum
|
120
|
+
# Override
|
121
|
+
def init_value(aValue)
|
122
|
+
super(aValue.dup)
|
123
|
+
end
|
124
|
+
|
125
|
+
def symbol?
|
126
|
+
true
|
127
|
+
end
|
128
|
+
end # class
|
129
|
+
|
130
|
+
class SkmReserved < SkmIdentifier
|
131
|
+
end # class
|
132
|
+
end # module
|
@@ -0,0 +1,107 @@
|
|
1
|
+
require_relative 'skm_expression'
|
2
|
+
|
3
|
+
module Skeem
|
4
|
+
class SkmUnaryExpression < SkmExpression
|
5
|
+
attr_reader :child
|
6
|
+
|
7
|
+
def initialize(aPosition, aChild)
|
8
|
+
super(aPosition)
|
9
|
+
@child = aChild
|
10
|
+
end
|
11
|
+
|
12
|
+
# Part of the 'visitee' role in Visitor design pattern.
|
13
|
+
# @param _visitor [SkmElementVisitor] the visitor
|
14
|
+
def accept(aVisitor)
|
15
|
+
aVisitor.visit_unary_expression(self)
|
16
|
+
end
|
17
|
+
|
18
|
+
protected
|
19
|
+
|
20
|
+
def inspect_specific
|
21
|
+
child.inspect
|
22
|
+
end
|
23
|
+
end # class
|
24
|
+
|
25
|
+
class SkmQuotation < SkmUnaryExpression
|
26
|
+
alias datum child
|
27
|
+
|
28
|
+
def initialize(aDatum)
|
29
|
+
super(nil, aDatum)
|
30
|
+
end
|
31
|
+
|
32
|
+
def evaluate(aRuntime)
|
33
|
+
datum
|
34
|
+
end
|
35
|
+
|
36
|
+
def quasiquote(aRuntime)
|
37
|
+
quasi_child = child.quasiquote(aRuntime)
|
38
|
+
if quasi_child.equal?(child)
|
39
|
+
self
|
40
|
+
else
|
41
|
+
self.class.new(quasi_child)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
protected
|
46
|
+
|
47
|
+
def inspect_specific
|
48
|
+
datum.inspect
|
49
|
+
end
|
50
|
+
|
51
|
+
end # class
|
52
|
+
|
53
|
+
class SkmQuasiquotation < SkmQuotation
|
54
|
+
alias template child
|
55
|
+
|
56
|
+
def evaluate(aRuntime)
|
57
|
+
quasiquote(aRuntime)
|
58
|
+
end
|
59
|
+
|
60
|
+
def quasiquote(aRuntime)
|
61
|
+
child.quasiquote(aRuntime)
|
62
|
+
end
|
63
|
+
|
64
|
+
end # class
|
65
|
+
|
66
|
+
class SkmUnquotation < SkmUnaryExpression
|
67
|
+
alias template child
|
68
|
+
|
69
|
+
def initialize(aTemplate)
|
70
|
+
super(nil, aTemplate)
|
71
|
+
end
|
72
|
+
|
73
|
+
def evaluate(aRuntime)
|
74
|
+
template.evaluate(aRuntime)
|
75
|
+
end
|
76
|
+
|
77
|
+
def quasiquote(aRuntime)
|
78
|
+
result = evaluate(aRuntime)
|
79
|
+
result
|
80
|
+
end
|
81
|
+
|
82
|
+
protected
|
83
|
+
|
84
|
+
def inspect_specific
|
85
|
+
template.inspect
|
86
|
+
end
|
87
|
+
end # class
|
88
|
+
|
89
|
+
class SkmVariableReference < SkmUnaryExpression
|
90
|
+
alias variable child
|
91
|
+
|
92
|
+
def evaluate(aRuntime)
|
93
|
+
var_key = variable.evaluate(aRuntime)
|
94
|
+
aRuntime.evaluate(var_key)
|
95
|
+
end
|
96
|
+
|
97
|
+
def quasiquote(aRuntime)
|
98
|
+
self
|
99
|
+
end
|
100
|
+
|
101
|
+
# Confusing!
|
102
|
+
# Value, here, means the value of the identifier (the variable's name).
|
103
|
+
def value()
|
104
|
+
variable.value
|
105
|
+
end
|
106
|
+
end # class
|
107
|
+
end # module
|
data/lib/skeem/standard/base.skm
CHANGED
@@ -18,11 +18,11 @@
|
|
18
18
|
#t
|
19
19
|
#f)))
|
20
20
|
|
21
|
-
; For backwards compatibility
|
21
|
+
; For backwards compatibility
|
22
22
|
(define modulo
|
23
23
|
(lambda (x y)
|
24
24
|
(floor-remainder x y)))
|
25
|
-
|
25
|
+
|
26
26
|
(define odd?
|
27
27
|
(lambda (n)
|
28
28
|
(if (= (modulo n 2) 1)
|
@@ -44,6 +44,6 @@
|
|
44
44
|
(define square
|
45
45
|
(lambda (z)
|
46
46
|
(* z z)))
|
47
|
-
|
47
|
+
|
48
48
|
(define list
|
49
49
|
(lambda args args))
|
data/lib/skeem/tokenizer.rb
CHANGED
@@ -20,10 +20,12 @@ module Skeem
|
|
20
20
|
|
21
21
|
@@lexeme2name = {
|
22
22
|
"'" => 'APOSTROPHE',
|
23
|
-
'`' => '
|
23
|
+
'`' => 'GRAVE_ACCENT',
|
24
24
|
'(' => 'LPAREN',
|
25
25
|
')' => 'RPAREN',
|
26
26
|
'.' => 'PERIOD',
|
27
|
+
',' => 'COMMA',
|
28
|
+
',@' => 'COMMA_AT_SIGN',
|
27
29
|
'#(' => 'VECTOR_BEGIN'
|
28
30
|
}.freeze
|
29
31
|
|
@@ -33,7 +35,10 @@ module Skeem
|
|
33
35
|
DEFINE
|
34
36
|
IF
|
35
37
|
LAMBDA
|
38
|
+
QUASIQUOTE
|
36
39
|
QUOTE
|
40
|
+
UNQUOTE
|
41
|
+
UNQUOTE-SPLICING
|
37
42
|
].map { |x| [x, x] } .to_h
|
38
43
|
|
39
44
|
class ScanError < StandardError; end
|
@@ -78,6 +83,8 @@ module Skeem
|
|
78
83
|
token = build_token(@@lexeme2name[curr_ch], scanner.getch)
|
79
84
|
elsif (lexeme = scanner.scan(/(?:\.)(?=\s)/)) # Single char occurring alone
|
80
85
|
token = build_token('PERIOD', lexeme)
|
86
|
+
elsif (lexeme = scanner.scan(/,@?/))
|
87
|
+
token = build_token(@@lexeme2name[lexeme], lexeme)
|
81
88
|
elsif (lexeme = scanner.scan(/#(?:(?:true)|(?:false)|(?:u8)|[\\\(tfeiodx]|(?:\d+[=#]))/))
|
82
89
|
token = cardinal_token(lexeme)
|
83
90
|
elsif (lexeme = scanner.scan(/[+-]?[0-9]+(?=\s|[|()";]|$)/))
|
data/lib/skeem/version.rb
CHANGED
@@ -0,0 +1,191 @@
|
|
1
|
+
require_relative '../spec_helper'
|
2
|
+
|
3
|
+
|
4
|
+
# Load the module under test
|
5
|
+
require_relative '../../lib/skeem/datum_dsl'
|
6
|
+
|
7
|
+
module Skeem
|
8
|
+
describe DatumDSL do
|
9
|
+
subject do
|
10
|
+
obj = Object.new
|
11
|
+
obj.extend(DatumDSL) # use the mixin module
|
12
|
+
obj
|
13
|
+
end
|
14
|
+
|
15
|
+
let(:boolean_tests) do
|
16
|
+
[
|
17
|
+
[true, true],
|
18
|
+
['true', true],
|
19
|
+
['#t', true],
|
20
|
+
['#true', true],
|
21
|
+
[false, false],
|
22
|
+
['false', false],
|
23
|
+
['#f', false],
|
24
|
+
['false', false]
|
25
|
+
]
|
26
|
+
end
|
27
|
+
|
28
|
+
let(:integer_tests) do
|
29
|
+
[
|
30
|
+
[0, 0],
|
31
|
+
[-123, -123],
|
32
|
+
[+456, 456],
|
33
|
+
['0', 0],
|
34
|
+
['-123', -123],
|
35
|
+
['+456', 456]
|
36
|
+
]
|
37
|
+
end
|
38
|
+
|
39
|
+
let(:real_tests) do
|
40
|
+
[
|
41
|
+
[0, 0],
|
42
|
+
[-123.4, -123.4],
|
43
|
+
[+456.7, 456.7],
|
44
|
+
[-1.234e+3, -1234],
|
45
|
+
['0', 0],
|
46
|
+
['-123.4', -123.4],
|
47
|
+
['+456.7', 456.7],
|
48
|
+
['-1.234e+3', -1234]
|
49
|
+
]
|
50
|
+
end
|
51
|
+
|
52
|
+
let(:string_tests) do
|
53
|
+
[
|
54
|
+
['hello', 'hello']
|
55
|
+
]
|
56
|
+
end
|
57
|
+
|
58
|
+
let(:identifier_tests) do
|
59
|
+
[
|
60
|
+
['define', 'define'],
|
61
|
+
[SkmString.create('positive?'), 'positive?']
|
62
|
+
]
|
63
|
+
end
|
64
|
+
|
65
|
+
let(:simple_datum_tests) do
|
66
|
+
[
|
67
|
+
['#t', SkmBoolean.create(true)],
|
68
|
+
[-1, SkmInteger.create(-1)],
|
69
|
+
[1.41, 1.41],
|
70
|
+
['foo', 'foo']
|
71
|
+
]
|
72
|
+
end
|
73
|
+
|
74
|
+
context 'Simple datums:' do
|
75
|
+
it 'should convert boolean literals' do
|
76
|
+
boolean_tests.each do |(literal, predicted)|
|
77
|
+
expect(subject.boolean(literal)).to eq(predicted)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
it 'should convert integer literals' do
|
82
|
+
integer_tests.each do |(literal, predicted)|
|
83
|
+
expect(subject.integer(literal)).to eq(predicted)
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
it 'should convert real number literals' do
|
88
|
+
real_tests.each do |(literal, predicted)|
|
89
|
+
expect(subject.real(literal)).to eq(predicted)
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
it 'should convert string literals' do
|
94
|
+
string_tests.each do |(literal, predicted)|
|
95
|
+
expect(subject.string(literal)).to eq(predicted)
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
it 'should convert identifier literals' do
|
100
|
+
identifier_tests.each do |(literal, predicted)|
|
101
|
+
expect(subject.identifier(literal)).to eq(predicted)
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end # context
|
105
|
+
|
106
|
+
|
107
|
+
context 'Compound datums:' do
|
108
|
+
it 'should convert empty array into one-member list' do
|
109
|
+
result = subject.list([])
|
110
|
+
expect(result).to be_kind_of(SkmList)
|
111
|
+
expect(result).to be_null
|
112
|
+
end
|
113
|
+
|
114
|
+
it 'should convert array of simple datums into list' do
|
115
|
+
literals = simple_datum_tests.map { |(datum, _predicted)| datum }
|
116
|
+
predictions = simple_datum_tests.map { |(_datum, predicted)| predicted }
|
117
|
+
list_result = subject.list(literals)
|
118
|
+
expect(list_result).to be_kind_of(SkmList)
|
119
|
+
list_result.members.each_with_index do |member, index|
|
120
|
+
expect(member).to eq(predictions[index])
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
it 'should convert a single datum into one-member list' do
|
125
|
+
result = subject.list('123')
|
126
|
+
expect(result).to be_kind_of(SkmList)
|
127
|
+
expect(result.members.first).to eq(123)
|
128
|
+
end
|
129
|
+
|
130
|
+
it 'should convert empty array into one-member list' do
|
131
|
+
result = subject.vector([])
|
132
|
+
expect(result).to be_kind_of(SkmVector)
|
133
|
+
expect(result.members).to be_empty
|
134
|
+
end
|
135
|
+
|
136
|
+
|
137
|
+
it 'should convert array of simple datums into vector' do
|
138
|
+
literals = simple_datum_tests.map { |(datum, _predicted)| datum }
|
139
|
+
predictions = simple_datum_tests.map { |(_datum, predicted)| predicted }
|
140
|
+
vector_result = subject.vector(literals)
|
141
|
+
expect(vector_result).to be_kind_of(SkmVector)
|
142
|
+
vector_result.members.each_with_index do |member, index|
|
143
|
+
expect(member).to eq(predictions[index])
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
it 'should convert a single datum into one-member vector' do
|
148
|
+
result = subject.vector('123')
|
149
|
+
expect(result).to be_kind_of(SkmVector)
|
150
|
+
expect(result.members.first).to eq(123)
|
151
|
+
end
|
152
|
+
end # context
|
153
|
+
|
154
|
+
context 'Arbitrary datums:' do
|
155
|
+
it 'should recognize & convert booleans' do
|
156
|
+
boolean_tests.each do |(literal, predicted)|
|
157
|
+
expect(subject.to_datum(literal)).to eq(predicted)
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
it 'should recognize & convert integer literals' do
|
162
|
+
integer_tests.each do |(literal, predicted)|
|
163
|
+
expect(subject.to_datum(literal)).to eq(predicted)
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
it 'should recognize & convert real number literals' do
|
168
|
+
real_tests.each do |(literal, predicted)|
|
169
|
+
expect(subject.to_datum(literal)).to eq(predicted)
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
it 'should recognize & convert string literals' do
|
174
|
+
string_tests.each do |(literal, predicted)|
|
175
|
+
expect(subject.to_datum(literal)).to eq(predicted)
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
it 'should convert nested compound datums' do
|
180
|
+
literals = [
|
181
|
+
'false', '123', '-1.41',
|
182
|
+
'foo', SkmVector.new(['uno', '2', 3.0]), 'bar'
|
183
|
+
]
|
184
|
+
result = subject.list(literals)
|
185
|
+
expect(result).to be_kind_of(SkmList)
|
186
|
+
expect(result).to eq([false, 123, -1.41, 'foo', ['uno', 2, 3], 'bar'])
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
end # describe
|
191
|
+
end # module
|
@@ -0,0 +1,170 @@
|
|
1
|
+
require_relative '../spec_helper'
|
2
|
+
require_relative '../../lib/skeem/datum_dsl'
|
3
|
+
|
4
|
+
# Load the class under test
|
5
|
+
require_relative '../../lib/skeem/element_visitor'
|
6
|
+
|
7
|
+
module Skeem
|
8
|
+
describe SkmElementVisitor do
|
9
|
+
include DatumDSL
|
10
|
+
let(:simple_datum) { integer 42 }
|
11
|
+
let(:listener) do
|
12
|
+
fake = double('fake-subscriber')
|
13
|
+
fake.define_singleton_method(:accept_all) {}
|
14
|
+
fake
|
15
|
+
end
|
16
|
+
|
17
|
+
# Default instantiation rule
|
18
|
+
subject { SkmElementVisitor.new(simple_datum) }
|
19
|
+
|
20
|
+
context 'Standard creation & initialization:' do
|
21
|
+
it 'should be initialized with a parse tree argument' do
|
22
|
+
expect { SkmElementVisitor.new(simple_datum) }.not_to raise_error
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'should know the parse tree to visit' do
|
26
|
+
expect(subject.root).to eq(simple_datum)
|
27
|
+
end
|
28
|
+
|
29
|
+
it "shouldn't have subscribers at start" do
|
30
|
+
expect(subject.subscribers).to be_empty
|
31
|
+
end
|
32
|
+
end # context
|
33
|
+
|
34
|
+
context 'Subscribing:' do
|
35
|
+
let(:listener1) { double('fake-subscriber1') }
|
36
|
+
let(:listener2) { double('fake-subscriber2') }
|
37
|
+
|
38
|
+
it 'should allow subscriptions' do
|
39
|
+
subject.subscribe(listener1)
|
40
|
+
expect(subject.subscribers.size).to eq(1)
|
41
|
+
expect(subject.subscribers).to eq([listener1])
|
42
|
+
|
43
|
+
subject.subscribe(listener2)
|
44
|
+
expect(subject.subscribers.size).to eq(2)
|
45
|
+
expect(subject.subscribers).to eq([listener1, listener2])
|
46
|
+
end
|
47
|
+
|
48
|
+
it 'should allow un-subcriptions' do
|
49
|
+
subject.subscribe(listener1)
|
50
|
+
subject.subscribe(listener2)
|
51
|
+
subject.unsubscribe(listener2)
|
52
|
+
expect(subject.subscribers.size).to eq(1)
|
53
|
+
expect(subject.subscribers).to eq([listener1])
|
54
|
+
subject.unsubscribe(listener1)
|
55
|
+
expect(subject.subscribers).to be_empty
|
56
|
+
end
|
57
|
+
end # context
|
58
|
+
|
59
|
+
context 'Visiting simple datum:' do
|
60
|
+
let(:runtime) { double('fake-runtime') }
|
61
|
+
|
62
|
+
it 'should allow the visit of simple datum object' do
|
63
|
+
subject.subscribe(listener)
|
64
|
+
expect(listener).to receive(:before_simple_datum).with(runtime, simple_datum)
|
65
|
+
expect(listener).to receive(:after_simple_datum).with(runtime, simple_datum)
|
66
|
+
subject.start(runtime)
|
67
|
+
expect(subject.runtime).to eq(runtime)
|
68
|
+
end
|
69
|
+
end # context
|
70
|
+
|
71
|
+
context 'Visiting compound datum:' do
|
72
|
+
let(:runtime) { double('fake-runtime') }
|
73
|
+
|
74
|
+
it 'should allow the visit of a flat list' do
|
75
|
+
ls = list ['#false', 3, 'foo']
|
76
|
+
instance = SkmElementVisitor.new(ls)
|
77
|
+
instance.subscribe(listener)
|
78
|
+
expect(listener).to receive(:before_compound_datum).with(runtime, ls).ordered
|
79
|
+
expect(listener).to receive(:before_children).with(runtime, ls, ls.members).ordered
|
80
|
+
expect(listener).to receive(:before_simple_datum).with(runtime, ls.members[0]).ordered
|
81
|
+
expect(listener).to receive(:after_simple_datum).with(runtime, ls.members[0]).ordered
|
82
|
+
expect(listener).to receive(:before_simple_datum).with(runtime, ls.members[1]).ordered
|
83
|
+
expect(listener).to receive(:after_simple_datum).with(runtime, ls.members[1]).ordered
|
84
|
+
expect(listener).to receive(:before_simple_datum).with(runtime, ls.members[2]).ordered
|
85
|
+
expect(listener).to receive(:after_simple_datum).with(runtime, ls.members[2]).ordered
|
86
|
+
expect(listener).to receive(:after_children).with(runtime, ls, ls.members).ordered
|
87
|
+
expect(listener).to receive(:after_compound_datum).with(runtime, ls).ordered
|
88
|
+
instance.start(runtime)
|
89
|
+
end
|
90
|
+
|
91
|
+
it 'should allow the visit of a flat vector' do
|
92
|
+
vec = vector ['#false', 3, 'foo']
|
93
|
+
instance = SkmElementVisitor.new(vec)
|
94
|
+
instance.subscribe(listener)
|
95
|
+
expect(listener).to receive(:before_compound_datum).with(runtime, vec).ordered
|
96
|
+
expect(listener).to receive(:before_children).with(runtime, vec, vec.members).ordered
|
97
|
+
expect(listener).to receive(:before_simple_datum).with(runtime, vec.members[0]).ordered
|
98
|
+
expect(listener).to receive(:after_simple_datum).with(runtime, vec.members[0]).ordered
|
99
|
+
expect(listener).to receive(:before_simple_datum).with(runtime, vec.members[1]).ordered
|
100
|
+
expect(listener).to receive(:after_simple_datum).with(runtime, vec.members[1]).ordered
|
101
|
+
expect(listener).to receive(:before_simple_datum).with(runtime, vec.members[2]).ordered
|
102
|
+
expect(listener).to receive(:after_simple_datum).with(runtime, vec.members[2]).ordered
|
103
|
+
expect(listener).to receive(:after_children).with(runtime, vec, vec.members).ordered
|
104
|
+
expect(listener).to receive(:after_compound_datum).with(runtime, vec).ordered
|
105
|
+
instance.start(runtime)
|
106
|
+
end
|
107
|
+
|
108
|
+
it 'should allow the visit of a nested compound datum' do
|
109
|
+
nested_list = list ['uno', 'twei', 'three']
|
110
|
+
vec = vector ['#false', 3, nested_list, 'foo']
|
111
|
+
instance = SkmElementVisitor.new(vec)
|
112
|
+
instance.subscribe(listener)
|
113
|
+
expect(listener).to receive(:before_compound_datum).with(runtime, vec).ordered
|
114
|
+
expect(listener).to receive(:before_children).with(runtime, vec, vec.members).ordered
|
115
|
+
expect(listener).to receive(:before_simple_datum).with(runtime, vec.members[0]).ordered
|
116
|
+
expect(listener).to receive(:after_simple_datum).with(runtime, vec.members[0]).ordered
|
117
|
+
expect(listener).to receive(:before_simple_datum).with(runtime, vec.members[1]).ordered
|
118
|
+
expect(listener).to receive(:after_simple_datum).with(runtime, vec.members[1]).ordered
|
119
|
+
expect(listener).to receive(:before_compound_datum).with(runtime, vec.members[2]).ordered
|
120
|
+
expect(listener).to receive(:before_children).with(runtime, nested_list, nested_list.members).ordered
|
121
|
+
expect(listener).to receive(:before_simple_datum).with(runtime, nested_list.members[0]).ordered
|
122
|
+
expect(listener).to receive(:after_simple_datum).with(runtime, nested_list.members[0]).ordered
|
123
|
+
expect(listener).to receive(:before_simple_datum).with(runtime, nested_list.members[1]).ordered
|
124
|
+
expect(listener).to receive(:after_simple_datum).with(runtime, nested_list.members[1]).ordered
|
125
|
+
expect(listener).to receive(:before_simple_datum).with(runtime, nested_list.members[2]).ordered
|
126
|
+
expect(listener).to receive(:after_simple_datum).with(runtime, nested_list.members[2]).ordered
|
127
|
+
expect(listener).to receive(:after_children).with(runtime, nested_list, nested_list.members).ordered
|
128
|
+
expect(listener).to receive(:after_compound_datum).with(runtime, nested_list).ordered
|
129
|
+
expect(listener).to receive(:before_simple_datum).with(runtime, vec.members[3]).ordered
|
130
|
+
expect(listener).to receive(:after_simple_datum).with(runtime, vec.members[3]).ordered
|
131
|
+
expect(listener).to receive(:after_children).with(runtime, vec, vec.members).ordered
|
132
|
+
expect(listener).to receive(:after_compound_datum).with(runtime, vec).ordered
|
133
|
+
instance.start(runtime)
|
134
|
+
end
|
135
|
+
|
136
|
+
# it 'should allow the visit of list datum object' do
|
137
|
+
# subject.subscribe(listener)
|
138
|
+
# expect(listener).to receive(:before_compound_datum).with(runtime, simple_datum)
|
139
|
+
# expect(listener).to receive(:after_compound_datum).with(runtime, simple_datum)
|
140
|
+
# subject.start(runtime)
|
141
|
+
# expect(subject.runtime).to eq(runtime)
|
142
|
+
# end
|
143
|
+
end # context
|
144
|
+
|
145
|
+
=begin
|
146
|
+
it 'should react to the start_visit_nonterminal message' do
|
147
|
+
# Notify subscribers when start the visit of a non-terminal node
|
148
|
+
expect(listener1).to receive(:before_non_terminal).with(nterm_node)
|
149
|
+
subject.visit_nonterminal(nterm_node)
|
150
|
+
end
|
151
|
+
|
152
|
+
it 'should react to the visit_children message' do
|
153
|
+
# Notify subscribers when start the visit of children nodes
|
154
|
+
children = nterm_node.subnodes
|
155
|
+
args = [nterm_node, children]
|
156
|
+
expect(listener1).to receive(:before_subnodes).with(*args)
|
157
|
+
expect(listener1).to receive(:before_terminal).with(children[0])
|
158
|
+
expect(listener1).to receive(:after_terminal).with(children[0])
|
159
|
+
expect(listener1).to receive(:after_subnodes).with(nterm_node, children)
|
160
|
+
subject.send(:traverse_subnodes, nterm_node)
|
161
|
+
end
|
162
|
+
|
163
|
+
it 'should react to the end_visit_nonterminal message' do
|
164
|
+
# Notify subscribers when ending the visit of a non-terminal node
|
165
|
+
expect(listener1).to receive(:after_non_terminal).with(nterm_node)
|
166
|
+
subject.end_visit_nonterminal(nterm_node)
|
167
|
+
end
|
168
|
+
=end
|
169
|
+
end # describe
|
170
|
+
end # module
|