skeem 0.0.23 → 0.0.24

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ require_relative 'skm_element'
2
+
3
+ module Skeem
4
+ # Abstract class.
5
+ class SkmExpression < SkmElement
6
+ end # class
7
+ end # module
@@ -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
@@ -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))
@@ -20,10 +20,12 @@ module Skeem
20
20
 
21
21
  @@lexeme2name = {
22
22
  "'" => 'APOSTROPHE',
23
- '`' => 'BACKQUOTE',
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
@@ -1,3 +1,3 @@
1
1
  module Skeem
2
- VERSION = '0.0.23'.freeze
2
+ VERSION = '0.0.24'.freeze
3
3
  end
@@ -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