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