skeem 0.0.13 → 0.0.14
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 +15 -0
- data/README.md +29 -1
- data/lib/skeem/environment.rb +38 -6
- data/lib/skeem/grammar.rb +17 -2
- data/lib/skeem/primitive_procedure.rb +3 -2
- data/lib/skeem/runtime.rb +12 -1
- data/lib/skeem/s_expr_builder.rb +44 -1
- data/lib/skeem/s_expr_nodes.rb +167 -21
- data/lib/skeem/tokenizer.rb +1 -0
- data/lib/skeem/version.rb +1 -1
- data/spec/skeem/environment_spec.rb +47 -5
- data/spec/skeem/interpreter_spec.rb +49 -8
- data/spec/skeem/runtime_spec.rb +18 -4
- data/spec/skeem/s_expr_nodes_spec.rb +411 -0
- data/spec/skeem/tokenizer_spec.rb +5 -1
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4738bc3d483df0325cce143200b02e5c8cac7afe
|
4
|
+
data.tar.gz: 8f4560eaa88c6e754bd423bdf3c1f62a35063b75
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 99e55d28f19fe7d4cb9a2d18a066a12fa43ffc2dfe59b1e5ac3e2795202c67b122e0fce2a9862871accb936eb58320870588c7eb622b4ee714c3e7bcb22eb5bc
|
7
|
+
data.tar.gz: 12e216d00ceb2e16ca2d27711bc47449e0ff49438ad9b42d52ae677368624cf15c3c80babef8a70aa740e0cdee8b619697aae871fb2962767e87af2f633068c0
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,18 @@
|
|
1
|
+
## [0.0.14] - 2018-09-23
|
2
|
+
Added `lambda` (anonymous function). This is an initial implementation that doesn't support recursion yet.
|
3
|
+
|
4
|
+
### Added
|
5
|
+
- Class `SkmLambda` for representing a specific definition.
|
6
|
+
|
7
|
+
### Changed
|
8
|
+
- Class `Environment`. Now supports the nesting of environments. If an environment doesn't find a variable, then it forwards the serach to the outer environment.
|
9
|
+
- File `grammar.rb` Added syntax rules for lambda expression.
|
10
|
+
- Class `Runtime` added methods `nest` and `unnest` that adds or removes an nested environment.
|
11
|
+
- Class `SkmBuilder`. Added method to implement the semantic action for `lambda` expressions.
|
12
|
+
- Class `Tokenizer` Added keyword `lambda`
|
13
|
+
- File `README.md` Added demo snippet with example of lambda expression.
|
14
|
+
- Files `*_spec.rb` Added more tests. Skeem passes the 100 'RSpec examples' mark.
|
15
|
+
|
1
16
|
## [0.0.13] - 2018-09-18
|
2
17
|
Added primitive `if` (conditional form)
|
3
18
|
|
data/README.md
CHANGED
@@ -27,11 +27,13 @@ Or install it yourself as:
|
|
27
27
|
The __Skeem__ project has just started.
|
28
28
|
At this stage, the gem consists of a bare-bones interpreter.
|
29
29
|
|
30
|
+
### Example 1 (Variable definition)
|
31
|
+
|
30
32
|
```ruby
|
31
33
|
require 'skeem'
|
32
34
|
|
33
35
|
schemer = Skeem::Interpreter.new
|
34
|
-
|
36
|
+
|
35
37
|
scheme_code =<<-SKEEM
|
36
38
|
; This heredoc consists of Scheme code...
|
37
39
|
; Let's define a Scheme variable
|
@@ -52,6 +54,32 @@ At this stage, the gem consists of a bare-bones interpreter.
|
|
52
54
|
puts result.value # => 1764
|
53
55
|
```
|
54
56
|
|
57
|
+
### Example 2 (Defining a function)
|
58
|
+
Remark: Skeem 0.0.14 doesn't support recursive functions yet.
|
59
|
+
|
60
|
+
```ruby
|
61
|
+
require 'skeem'
|
62
|
+
|
63
|
+
schemer = Skeem::Interpreter.new
|
64
|
+
|
65
|
+
scheme_code =<<-SKEEM
|
66
|
+
; Let's implement the 'min' function
|
67
|
+
(define min (lambda(x y) (if (< x y) x y)))
|
68
|
+
|
69
|
+
; What is the minimum of 2 and 3?
|
70
|
+
(min 2 3)
|
71
|
+
SKEEM
|
72
|
+
|
73
|
+
# Ask Ruby to execute Scheme code
|
74
|
+
result = schemer.run(scheme_code)
|
75
|
+
puts result.value # => 2
|
76
|
+
|
77
|
+
# Let's retry with other values
|
78
|
+
scheme_code = '(min 42 3)'
|
79
|
+
result = schemer.run(scheme_code)
|
80
|
+
puts result.value # => 3
|
81
|
+
```
|
82
|
+
|
55
83
|
Roadmap:
|
56
84
|
- Extend language support
|
57
85
|
- Implement REPL
|
data/lib/skeem/environment.rb
CHANGED
@@ -1,19 +1,51 @@
|
|
1
|
-
require 'forwardable'
|
2
|
-
|
3
1
|
module Skeem
|
4
2
|
class Environment
|
5
|
-
|
6
|
-
|
7
|
-
|
3
|
+
attr_reader :outer
|
4
|
+
|
8
5
|
attr_reader(:bindings)
|
9
6
|
|
10
|
-
def initialize()
|
7
|
+
def initialize(outerEnv = nil)
|
11
8
|
@bindings = {}
|
9
|
+
@outer = outerEnv
|
12
10
|
end
|
13
11
|
|
14
12
|
def define(anIdentifier, anExpression)
|
15
13
|
raise StandardError, anIdentifier unless anIdentifier.kind_of?(String)
|
16
14
|
@bindings[anIdentifier] = anExpression
|
17
15
|
end
|
16
|
+
|
17
|
+
def fetch(anIdentifier)
|
18
|
+
found = bindings[anIdentifier]
|
19
|
+
if found.nil? && outer
|
20
|
+
found = outer.fetch(anIdentifier)
|
21
|
+
end
|
22
|
+
|
23
|
+
found
|
24
|
+
end
|
25
|
+
|
26
|
+
def empty?
|
27
|
+
my_result = bindings.empty?
|
28
|
+
if my_result && outer
|
29
|
+
my_result = outer.empty?
|
30
|
+
end
|
31
|
+
|
32
|
+
my_result
|
33
|
+
end
|
34
|
+
|
35
|
+
def size
|
36
|
+
my_result = bindings.size
|
37
|
+
my_result += outer.size if outer
|
38
|
+
|
39
|
+
my_result
|
40
|
+
end
|
41
|
+
|
42
|
+
def include?(anIdentifier)
|
43
|
+
my_result = bindings.include?(anIdentifier)
|
44
|
+
if my_result == false && outer
|
45
|
+
my_result = outer.include?(anIdentifier)
|
46
|
+
end
|
47
|
+
|
48
|
+
my_result
|
49
|
+
end
|
18
50
|
end # class
|
19
51
|
end # module
|
data/lib/skeem/grammar.rb
CHANGED
@@ -11,14 +11,14 @@ module Skeem
|
|
11
11
|
# Delimiters, separators...
|
12
12
|
# add_terminals('APOSTROPHE', 'BACKQUOTE')
|
13
13
|
add_terminals('LPAREN', 'RPAREN')
|
14
|
-
|
14
|
+
add_terminals('PERIOD')
|
15
15
|
|
16
16
|
# Literal values...
|
17
17
|
add_terminals('BOOLEAN', 'INTEGER', 'REAL')
|
18
18
|
add_terminals('STRING_LIT', 'IDENTIFIER')
|
19
19
|
|
20
20
|
# Keywords...
|
21
|
-
add_terminals('DEFINE', 'IF')
|
21
|
+
add_terminals('DEFINE', 'IF', 'LAMBDA')
|
22
22
|
|
23
23
|
rule('program' => 'cmd_or_def_plus').as 'main'
|
24
24
|
rule('cmd_or_def_plus' => 'cmd_or_def_plus cmd_or_def').as 'multiple_cmd_def'
|
@@ -30,6 +30,7 @@ module Skeem
|
|
30
30
|
rule('expression' => 'IDENTIFIER').as 'variable_reference'
|
31
31
|
rule 'expression' => 'literal'
|
32
32
|
rule 'expression' => 'procedure_call'
|
33
|
+
rule 'expression' => 'lambda_expression'
|
33
34
|
rule 'expression' => 'conditional'
|
34
35
|
rule 'literal' => 'self-evaluating'
|
35
36
|
rule 'self-evaluating' => 'BOOLEAN'
|
@@ -41,6 +42,20 @@ module Skeem
|
|
41
42
|
rule('operand_plus' => 'operand').as 'last_operand'
|
42
43
|
rule 'operator' => 'expression'
|
43
44
|
rule 'operand' => 'expression'
|
45
|
+
rule('lambda_expression' => 'LPAREN LAMBDA formals body RPAREN').as 'lambda_expression'
|
46
|
+
rule('formals' => 'LPAREN identifier_star RPAREN').as 'identifiers_as_formals'
|
47
|
+
rule 'formals' => 'IDENTIFIER'
|
48
|
+
rule 'formals' => 'LPAREN identifier_plus PERIOD IDENTIFIER RPAREN'
|
49
|
+
rule('identifier_star' => 'identifier_star IDENTIFIER').as 'identifier_star'
|
50
|
+
rule('identifier_star' => []).as 'no_identifier_yet'
|
51
|
+
rule 'identifier_plus' => 'identifier_plus IDENTIFIER'
|
52
|
+
rule 'identifier_plus' => 'IDENTIFIER'
|
53
|
+
rule('body' => 'definition_star sequence').as 'body'
|
54
|
+
rule 'definition_star' => 'definition_star definition'
|
55
|
+
rule 'definition_star' => []
|
56
|
+
rule('sequence' => 'command_star expression').as 'sequence'
|
57
|
+
rule('command_star' => 'command_star command').as 'multiple_commands'
|
58
|
+
rule('command_star' => []).as 'no_command_yet'
|
44
59
|
rule('conditional' => 'LPAREN IF test consequent alternate RPAREN').as 'conditional'
|
45
60
|
rule 'test' => 'expression'
|
46
61
|
rule 'consequent' => 'expression'
|
@@ -5,11 +5,12 @@ module Skeem
|
|
5
5
|
attr_reader(:identifier)
|
6
6
|
attr_reader(:code)
|
7
7
|
|
8
|
-
def initialize(anId,
|
8
|
+
def initialize(anId, aRubyLambda)
|
9
9
|
@identifier = anId.kind_of?(String) ? SkmIdentifier.create(anId) : anId
|
10
|
-
@code =
|
10
|
+
@code = aRubyLambda
|
11
11
|
end
|
12
12
|
|
13
|
+
# Arguments are positional in a primitive procedure.
|
13
14
|
def call(aRuntime, aProcedureCall)
|
14
15
|
args = aProcedureCall.operands
|
15
16
|
return @code.call(aRuntime, args)
|
data/lib/skeem/runtime.rb
CHANGED
@@ -7,12 +7,23 @@ module Skeem
|
|
7
7
|
end
|
8
8
|
|
9
9
|
def include?(anIdentifier)
|
10
|
-
environment.
|
10
|
+
environment.include?(normalize_key(anIdentifier))
|
11
11
|
end
|
12
12
|
|
13
13
|
def define(aKey, anEntry)
|
14
14
|
environment.define(normalize_key(aKey), anEntry)
|
15
15
|
end
|
16
|
+
|
17
|
+
def nest()
|
18
|
+
nested = Environment.new(environment)
|
19
|
+
@environment = nested
|
20
|
+
end
|
21
|
+
|
22
|
+
def unnest()
|
23
|
+
raise StandardError, 'Cannot unnest environment' unless environment.outer
|
24
|
+
environment.bindings.clear
|
25
|
+
@environment = environment.outer
|
26
|
+
end
|
16
27
|
|
17
28
|
private
|
18
29
|
|
data/lib/skeem/s_expr_builder.rb
CHANGED
@@ -82,10 +82,53 @@ module Skeem
|
|
82
82
|
[theChildren.last]
|
83
83
|
end
|
84
84
|
|
85
|
+
# rule('lambda_expression' => 'LPAREN LAMBDA formals body RPAREN').as 'lambda_expression'
|
86
|
+
def reduce_lambda_expression(_production, _range, _tokens, theChildren)
|
87
|
+
lmbd = SkmLambda.new(_range, theChildren[2], theChildren[3])
|
88
|
+
# puts lmbd.inspect
|
89
|
+
lmbd
|
90
|
+
end
|
91
|
+
|
92
|
+
# rule('formals' => 'LPAREN identifier_star RPAREN').as 'identifiers_as_formals'
|
93
|
+
def reduce_identifiers_as_formals(_production, _range, _tokens, theChildren)
|
94
|
+
theChildren[1]
|
95
|
+
end
|
96
|
+
|
97
|
+
# rule('identifier_star' => 'identifier_star IDENTIFIER').as 'identifier_star'
|
98
|
+
def reduce_identifier_star(_production, _range, _tokens, theChildren)
|
99
|
+
theChildren[0] << theChildren[1]
|
100
|
+
end
|
101
|
+
|
102
|
+
# rule('identifier_star' => []).as 'no_identifier_yet'
|
103
|
+
def reduce_no_identifier_yet(_production, _range, _tokens, theChildren)
|
104
|
+
[]
|
105
|
+
end
|
106
|
+
|
107
|
+
# rule('body' => 'definition_star sequence').as 'body'
|
108
|
+
def reduce_body(_production, _range, _tokens, theChildren)
|
109
|
+
definitions = theChildren[0].nil? ? [] : theChildren[0]
|
110
|
+
{ defs: definitions, sequence: theChildren[1] }
|
111
|
+
end
|
112
|
+
|
113
|
+
# rule('sequence' => 'command_star expression').as 'sequence'
|
114
|
+
def reduce_sequence(_production, _range, _tokens, theChildren)
|
115
|
+
SkmList.new(theChildren[0] << theChildren[1])
|
116
|
+
end
|
117
|
+
|
118
|
+
# rule('command_star' => 'command_star command').as 'multiple_commands'
|
119
|
+
def reduce_multiple_commands(_production, _range, _tokens, theChildren)
|
120
|
+
theChildren[0] << theChildren[1]
|
121
|
+
end
|
122
|
+
|
123
|
+
# rule('command_star' => []).as 'no_command_yet'
|
124
|
+
def reduce_no_command_yet(_production, _range, _tokens, theChildren)
|
125
|
+
[]
|
126
|
+
end
|
127
|
+
|
85
128
|
# rule('conditional' => 'LPAREN IF test consequent alternate RPAREN').as 'conditional'
|
86
129
|
def reduce_conditional(_production, aRange, _tokens, theChildren)
|
87
130
|
SkmCondition.new(aRange, theChildren[2], theChildren[3], theChildren[4])
|
88
|
-
end
|
131
|
+
end
|
89
132
|
end # class
|
90
133
|
end # module
|
91
134
|
# End of file
|
data/lib/skeem/s_expr_nodes.rb
CHANGED
@@ -36,18 +36,18 @@ module Skeem
|
|
36
36
|
def integer?
|
37
37
|
false
|
38
38
|
end
|
39
|
-
|
39
|
+
|
40
40
|
def boolean?
|
41
41
|
false
|
42
42
|
end
|
43
43
|
|
44
44
|
def string?
|
45
45
|
false
|
46
|
-
end
|
46
|
+
end
|
47
47
|
|
48
48
|
def symbol?
|
49
49
|
false
|
50
|
-
end
|
50
|
+
end
|
51
51
|
|
52
52
|
# Abstract method.
|
53
53
|
# Part of the 'visitee' role in Visitor design pattern.
|
@@ -55,6 +55,20 @@ module Skeem
|
|
55
55
|
def accept(_visitor)
|
56
56
|
raise NotImplementedError
|
57
57
|
end
|
58
|
+
|
59
|
+
def inspect()
|
60
|
+
raise NotImplementedError
|
61
|
+
end
|
62
|
+
|
63
|
+
protected
|
64
|
+
|
65
|
+
def inspect_prefix
|
66
|
+
"<#{self.class.name}: "
|
67
|
+
end
|
68
|
+
|
69
|
+
def inspect_suffix
|
70
|
+
'>'
|
71
|
+
end
|
58
72
|
end # struct
|
59
73
|
|
60
74
|
# Abstract class. Root of class hierarchy needed for Interpreter
|
@@ -75,11 +89,6 @@ module Skeem
|
|
75
89
|
return lightweight
|
76
90
|
end
|
77
91
|
|
78
|
-
# This method can be overriden
|
79
|
-
def init_value(aValue)
|
80
|
-
@value = aValue
|
81
|
-
end
|
82
|
-
|
83
92
|
def symbol()
|
84
93
|
token.terminal
|
85
94
|
end
|
@@ -88,6 +97,10 @@ module Skeem
|
|
88
97
|
return self
|
89
98
|
end
|
90
99
|
|
100
|
+
def inspect()
|
101
|
+
inspect_prefix + value.to_s + inspect_suffix
|
102
|
+
end
|
103
|
+
|
91
104
|
def done!()
|
92
105
|
# Do nothing
|
93
106
|
end
|
@@ -97,12 +110,17 @@ module Skeem
|
|
97
110
|
def accept(aVisitor)
|
98
111
|
aVisitor.visit_terminal(self)
|
99
112
|
end
|
113
|
+
|
114
|
+
# This method can be overriden
|
115
|
+
def init_value(aValue)
|
116
|
+
@value = aValue
|
117
|
+
end
|
100
118
|
end # class
|
101
119
|
|
102
120
|
class SkmBoolean < SkmTerminal
|
103
121
|
def boolean?
|
104
122
|
true
|
105
|
-
end
|
123
|
+
end
|
106
124
|
end # class
|
107
125
|
|
108
126
|
class SkmNumber < SkmTerminal
|
@@ -128,10 +146,10 @@ module Skeem
|
|
128
146
|
def init_value(aValue)
|
129
147
|
super(aValue.dup)
|
130
148
|
end
|
131
|
-
|
149
|
+
|
132
150
|
def string?
|
133
151
|
true
|
134
|
-
end
|
152
|
+
end
|
135
153
|
end # class
|
136
154
|
|
137
155
|
class SkmIdentifier < SkmTerminal
|
@@ -139,10 +157,10 @@ module Skeem
|
|
139
157
|
def init_value(aValue)
|
140
158
|
super(aValue.dup)
|
141
159
|
end
|
142
|
-
|
160
|
+
|
143
161
|
def symbol?
|
144
162
|
true
|
145
|
-
end
|
163
|
+
end
|
146
164
|
end # class
|
147
165
|
|
148
166
|
class SkmReserved < SkmIdentifier
|
@@ -153,7 +171,7 @@ module Skeem
|
|
153
171
|
attr_accessor(:members)
|
154
172
|
extend Forwardable
|
155
173
|
|
156
|
-
def_delegators :@members, :first, :length, :empty?
|
174
|
+
def_delegators :@members, :each, :first, :length, :empty?
|
157
175
|
|
158
176
|
def initialize(theMembers)
|
159
177
|
super(nil)
|
@@ -202,8 +220,17 @@ module Skeem
|
|
202
220
|
# Do nothing
|
203
221
|
end
|
204
222
|
|
223
|
+
def inspect()
|
224
|
+
result = inspect_prefix
|
225
|
+
members.each { |elem| result << elem.inspect + ', ' }
|
226
|
+
result.sub!(/, $/, '')
|
227
|
+
result << inspect_suffix
|
228
|
+
result
|
229
|
+
end
|
230
|
+
|
205
231
|
alias children members
|
206
232
|
alias subnodes members
|
233
|
+
alias rest tail
|
207
234
|
end # class
|
208
235
|
|
209
236
|
class SkmDefinition < SkmElement
|
@@ -219,13 +246,32 @@ module Skeem
|
|
219
246
|
def evaluate(aRuntime)
|
220
247
|
var_key = variable.evaluate(aRuntime)
|
221
248
|
aRuntime.define(var_key, self)
|
249
|
+
expression.evaluate(aRuntime) if expression.kind_of?(SkmLambda)
|
222
250
|
self
|
223
251
|
end
|
252
|
+
|
253
|
+
# call method should only invoked when the expression is a SkmLambda
|
254
|
+
def call(aRuntime, aProcedureCall)
|
255
|
+
unless expression.kind_of?(SkmLambda)
|
256
|
+
err_msg = "Expected a SkmLambda instead of #{expression.class}"
|
257
|
+
raise StandardError, err_msg
|
258
|
+
end
|
259
|
+
expression.call(aRuntime, aProcedureCall)
|
260
|
+
end
|
261
|
+
|
262
|
+
def inspect
|
263
|
+
result = inspect_prefix
|
264
|
+
result << variable.inspect
|
265
|
+
result << ', '
|
266
|
+
result << expression.inspect
|
267
|
+
result << inspect_suffix
|
268
|
+
result
|
269
|
+
end
|
224
270
|
end # class
|
225
|
-
|
271
|
+
|
226
272
|
class SkmVariableReference < SkmElement
|
227
273
|
attr_reader :variable
|
228
|
-
|
274
|
+
|
229
275
|
def initialize(aPosition, aVariable)
|
230
276
|
super(aPosition)
|
231
277
|
@variable = aVariable
|
@@ -236,10 +282,10 @@ module Skeem
|
|
236
282
|
unless aRuntime.include?(var_key.value)
|
237
283
|
err = StandardError
|
238
284
|
key = var_key.kind_of?(SkmIdentifier) ? var_key.value : var_key
|
239
|
-
err_msg = "
|
285
|
+
err_msg = "Unbound variable: '#{key}'"
|
240
286
|
raise err, err_msg
|
241
287
|
end
|
242
|
-
definition = aRuntime.environment.
|
288
|
+
definition = aRuntime.environment.fetch(var_key.value)
|
243
289
|
result = definition.expression.evaluate(aRuntime)
|
244
290
|
end
|
245
291
|
|
@@ -248,7 +294,12 @@ module Skeem
|
|
248
294
|
def value()
|
249
295
|
variable.value
|
250
296
|
end
|
251
|
-
|
297
|
+
|
298
|
+
def inspect
|
299
|
+
result = inspect_prefix + variable.inspect + inspect_suffix
|
300
|
+
result
|
301
|
+
end
|
302
|
+
end # class
|
252
303
|
|
253
304
|
class ProcedureCall < SkmElement
|
254
305
|
attr_reader :operator
|
@@ -273,13 +324,19 @@ module Skeem
|
|
273
324
|
err_msg = "Unknown procedure '#{key}'"
|
274
325
|
raise err, err_msg
|
275
326
|
end
|
276
|
-
procedure = aRuntime.environment.
|
327
|
+
procedure = aRuntime.environment.fetch(var_key.value)
|
277
328
|
result = procedure.call(aRuntime, self)
|
278
329
|
end
|
279
330
|
|
331
|
+
def inspect
|
332
|
+
result = inspect_prefix + operator.inspect + ', '
|
333
|
+
result << operands.inspect + inspect_suffix
|
334
|
+
result
|
335
|
+
end
|
336
|
+
|
280
337
|
alias children operands
|
281
338
|
end # class
|
282
|
-
|
339
|
+
|
283
340
|
class SkmCondition < SkmElement
|
284
341
|
attr_reader :test
|
285
342
|
attr_reader :consequent
|
@@ -302,6 +359,95 @@ module Skeem
|
|
302
359
|
condition_result = consequent.evaluate(aRuntime)
|
303
360
|
end
|
304
361
|
end
|
362
|
+
|
363
|
+
def inspect
|
364
|
+
result = inspect_prefix + '@test ' + test.inspect + ', '
|
365
|
+
result << '@consequent ' + consequent.inspect + ', '
|
366
|
+
result << '@alternate ' + alternate.inspect + inspect_suffix
|
367
|
+
result
|
368
|
+
end
|
369
|
+
end # class
|
370
|
+
|
371
|
+
=begin
|
372
|
+
<Skeem::SkmLambda:
|
373
|
+
@formals [<Skeem::SkmIdentifier: x>]
|
374
|
+
@definitions nil
|
375
|
+
@sequence <Skeem::SkmList:
|
376
|
+
<Skeem::SkmCondition:
|
377
|
+
@test <Skeem::ProcedureCall: <Skeem::SkmIdentifier: <>,
|
378
|
+
<Skeem::SkmList: <Skeem::SkmVariableReference: <Skeem::SkmIdentifier: x>>,
|
379
|
+
<Skeem::SkmInteger: 0>>>,
|
380
|
+
@consequent <Skeem::ProcedureCall:
|
381
|
+
<Skeem::SkmIdentifier: ->,
|
382
|
+
<Skeem::SkmList: <Skeem::SkmVariableReference: <Skeem::SkmIdentifier: x>>>>,
|
383
|
+
@alternate <Skeem::SkmVariableReference: <Skeem::SkmIdentifier: x>>>>>
|
384
|
+
=end
|
385
|
+
|
386
|
+
class SkmLambda < SkmElement
|
387
|
+
# @!attribute [r] formals
|
388
|
+
# @return [Array<SkmIdentifier>] the argument names
|
389
|
+
attr_reader :formals
|
390
|
+
attr_reader :definitions
|
391
|
+
attr_reader :sequence
|
392
|
+
|
393
|
+
def initialize(aPosition, theFormals, aBody)
|
394
|
+
super(aPosition)
|
395
|
+
@formals = theFormals
|
396
|
+
@definitions = aBody[:defs]
|
397
|
+
@sequence = aBody[:sequence]
|
398
|
+
end
|
399
|
+
|
400
|
+
def evaluate(aRuntime)
|
401
|
+
formals.map! { |param| param.evaluate(aRuntime) }
|
402
|
+
end
|
403
|
+
|
404
|
+
def call(aRuntime, aProcedureCall)
|
405
|
+
aRuntime.nest
|
406
|
+
bind_locals(aRuntime, aProcedureCall)
|
407
|
+
# TODO remove next line
|
408
|
+
# puts aRuntime.environment.inspect
|
409
|
+
result = evaluate_defs(aRuntime)
|
410
|
+
result = evaluate_sequence(aRuntime)
|
411
|
+
aRuntime.unnest
|
412
|
+
|
413
|
+
result
|
414
|
+
end
|
415
|
+
|
416
|
+
def inspect
|
417
|
+
result = inspect_prefix + '@formals ' + formals.inspect + ', '
|
418
|
+
result << '@definitions ' + definitions.inspect + ', '
|
419
|
+
result << '@sequence ' + sequence.inspect + inspect_suffix
|
420
|
+
result
|
421
|
+
end
|
422
|
+
|
423
|
+
private
|
424
|
+
|
425
|
+
def bind_locals(aRuntime, aProcedureCall)
|
426
|
+
arguments = aProcedureCall.operands.members
|
427
|
+
formals.each_with_index do |arg_name, index|
|
428
|
+
arg = arguments[index]
|
429
|
+
if arg.nil?
|
430
|
+
p aProcedureCall.operands.members
|
431
|
+
raise StandardError, "Unbound variable: '#{arg_name.value}'"
|
432
|
+
end
|
433
|
+
|
434
|
+
a_def = SkmDefinition.new(position, arg_name, arg)
|
435
|
+
a_def.evaluate(aRuntime)
|
436
|
+
end
|
437
|
+
end
|
438
|
+
|
439
|
+
def evaluate_defs(aRuntime)
|
440
|
+
definitions.each { |a_def| a_def.evaluate(runtime) }
|
441
|
+
end
|
442
|
+
|
443
|
+
def evaluate_sequence(aRuntime)
|
444
|
+
result = nil
|
445
|
+
if sequence
|
446
|
+
sequence.each { |cmd| result = cmd.evaluate(aRuntime) }
|
447
|
+
end
|
448
|
+
|
449
|
+
result
|
450
|
+
end
|
305
451
|
end # class
|
306
452
|
end # module
|
307
453
|
# End of file
|
data/lib/skeem/tokenizer.rb
CHANGED
data/lib/skeem/version.rb
CHANGED
@@ -4,12 +4,12 @@ require_relative '../../lib/skeem/environment' # Load the class under test
|
|
4
4
|
module Skeem
|
5
5
|
describe Environment do
|
6
6
|
context 'Initialization:' do
|
7
|
-
it 'should be initialized
|
7
|
+
it 'should be initialized with opional argument' do
|
8
8
|
expect { Environment.new() }.not_to raise_error
|
9
9
|
end
|
10
10
|
|
11
|
-
it 'should have no bindings' do
|
12
|
-
expect(subject
|
11
|
+
it 'should have no default bindings' do
|
12
|
+
expect(subject).to be_empty
|
13
13
|
end
|
14
14
|
end # context
|
15
15
|
|
@@ -17,11 +17,53 @@ module Skeem
|
|
17
17
|
it 'should add entries' do
|
18
18
|
entry = double('dummy')
|
19
19
|
subject.define('dummy', entry)
|
20
|
-
expect(subject.
|
20
|
+
expect(subject.size).to eq(1)
|
21
21
|
expect(subject.bindings['dummy']).not_to be_nil
|
22
22
|
expect(subject.bindings['dummy']).to eq(entry)
|
23
23
|
end
|
24
|
+
|
25
|
+
it 'should know whether it is empty' do
|
26
|
+
# Case 1: no entry
|
27
|
+
expect(subject.empty?).to be_truthy
|
28
|
+
|
29
|
+
# Case 2: existing entry
|
30
|
+
entry = double('dummy')
|
31
|
+
subject.define('dummy', entry)
|
32
|
+
expect(subject.empty?).to be_falsey
|
33
|
+
|
34
|
+
# Case 3: entry defined in outer environment
|
35
|
+
nested = Environment.new(subject)
|
36
|
+
expect(nested.empty?).to be_falsey
|
37
|
+
end
|
38
|
+
|
39
|
+
it 'should retrieve entries' do
|
40
|
+
# Case 1: non-existing entry
|
41
|
+
expect(subject.fetch('dummy')).to be_nil
|
42
|
+
|
43
|
+
# Case 2: existing entry
|
44
|
+
entry = double('dummy')
|
45
|
+
subject.define('dummy', entry)
|
46
|
+
expect(subject.fetch('dummy')).to eq(entry)
|
47
|
+
|
48
|
+
# Case 3: entry defined in outer environment
|
49
|
+
nested = Environment.new(subject)
|
50
|
+
expect(nested.fetch('dummy')).to eq(entry)
|
51
|
+
end
|
52
|
+
|
53
|
+
it 'should know the total number of bindings' do
|
54
|
+
# Case 1: non-existing entry
|
55
|
+
expect(subject.size).to be_zero
|
56
|
+
|
57
|
+
# Case 2: existing entry
|
58
|
+
entry = double('dummy')
|
59
|
+
subject.define('dummy', entry)
|
60
|
+
expect(subject.size).to eq(1)
|
61
|
+
|
62
|
+
# Case 3: entry defined in outer environment
|
63
|
+
nested = Environment.new(subject)
|
64
|
+
expect(nested.size).to eq(1)
|
65
|
+
end
|
24
66
|
end # context
|
25
|
-
|
67
|
+
|
26
68
|
end # describe
|
27
69
|
end # module
|
@@ -77,13 +77,13 @@ module Skeem
|
|
77
77
|
end
|
78
78
|
end
|
79
79
|
end # context
|
80
|
-
|
81
|
-
context 'Built-in primitives' do
|
80
|
+
|
81
|
+
context 'Built-in primitives' do
|
82
82
|
it 'should implement variable definition' do
|
83
83
|
result = subject.run('(define x 28)')
|
84
84
|
expect(result).to be_kind_of(SkmDefinition)
|
85
85
|
end
|
86
|
-
|
86
|
+
|
87
87
|
it 'should implement variable reference' do
|
88
88
|
source = <<-SKEEM
|
89
89
|
; Example from R7RS section 4.1.1
|
@@ -103,9 +103,9 @@ SKEEM
|
|
103
103
|
checks.each do |(skeem_expr, expectation)|
|
104
104
|
result = subject.run(skeem_expr)
|
105
105
|
expect(result.value).to eq(expectation)
|
106
|
-
end
|
106
|
+
end
|
107
107
|
end
|
108
|
-
|
108
|
+
|
109
109
|
it 'should implement the complete conditional form' do
|
110
110
|
checks = [
|
111
111
|
['(if (> 3 2) "yes" "no")', 'yes'],
|
@@ -122,8 +122,49 @@ SKEEM
|
|
122
122
|
(+ 3 2))
|
123
123
|
SKEEM
|
124
124
|
result = subject.run(source)
|
125
|
-
expect(result.value).to eq(1)
|
125
|
+
expect(result.value).to eq(1)
|
126
|
+
end
|
127
|
+
|
128
|
+
it 'should implement the lambda function with one arg' do
|
129
|
+
source = <<-SKEEM
|
130
|
+
; Simplified 'abs' function implementation
|
131
|
+
(define abs (lambda(x)
|
132
|
+
(if (< x 0) (- x) x)))
|
133
|
+
SKEEM
|
134
|
+
subject.run(source)
|
135
|
+
result = subject.run('(abs -3)')
|
136
|
+
expect(result.value).to eq(3)
|
137
|
+
result = subject.run('(abs 0)')
|
138
|
+
expect(result.value).to eq(0)
|
139
|
+
result = subject.run('(abs 3)')
|
140
|
+
expect(result.value).to eq(3)
|
126
141
|
end
|
142
|
+
|
143
|
+
it 'should implement the lambda function with two args' do
|
144
|
+
source = <<-SKEEM
|
145
|
+
; Simplified 'min' function implementation
|
146
|
+
(define min (lambda(x y)
|
147
|
+
(if (< x y) x y)))
|
148
|
+
SKEEM
|
149
|
+
subject.run(source)
|
150
|
+
result = subject.run('(min 1 2)')
|
151
|
+
expect(result.value).to eq(1)
|
152
|
+
result = subject.run('(min 2 1)')
|
153
|
+
expect(result.value).to eq(1)
|
154
|
+
result = subject.run('(min 2 2)')
|
155
|
+
expect(result.value).to eq(2)
|
156
|
+
end
|
157
|
+
|
158
|
+
# it 'should implement recursive functions' do
|
159
|
+
# source = <<-SKEEM
|
160
|
+
# ; Example from R7RS section 4.1.5
|
161
|
+
# (define fact (lambda (n)
|
162
|
+
# (if (<= n 1) 1 (* n (fact (- n 1))))))
|
163
|
+
# (fact 10)
|
164
|
+
# SKEEM
|
165
|
+
# subject.run(source)
|
166
|
+
# expect(result.value).to eq(3628800)
|
167
|
+
# end
|
127
168
|
end # context
|
128
169
|
|
129
170
|
context 'Built-in primitive procedures' do
|
@@ -234,7 +275,7 @@ SKEEM
|
|
234
275
|
expect(result.value).to eq(expectation)
|
235
276
|
end
|
236
277
|
end
|
237
|
-
|
278
|
+
|
238
279
|
it 'should implement the greater or equal than operator' do
|
239
280
|
checks = [
|
240
281
|
['(>= 3 2)', true],
|
@@ -248,7 +289,7 @@ SKEEM
|
|
248
289
|
result = subject.run(skeem_expr)
|
249
290
|
expect(result.value).to eq(expectation)
|
250
291
|
end
|
251
|
-
end
|
292
|
+
end
|
252
293
|
|
253
294
|
it 'should implement the number? predicate' do
|
254
295
|
checks = [
|
data/spec/skeem/runtime_spec.rb
CHANGED
@@ -6,7 +6,7 @@ module Skeem
|
|
6
6
|
describe Runtime do
|
7
7
|
let(:some_env) { Environment.new }
|
8
8
|
subject { Runtime.new(some_env) }
|
9
|
-
|
9
|
+
|
10
10
|
context 'Initialization:' do
|
11
11
|
it 'should be initialized with an environment' do
|
12
12
|
expect { Runtime.new(Environment.new) }.not_to raise_error
|
@@ -21,15 +21,29 @@ module Skeem
|
|
21
21
|
it 'should add entries to the environment' do
|
22
22
|
entry = double('dummy')
|
23
23
|
subject.define('dummy', entry)
|
24
|
-
expect(subject.environment.
|
24
|
+
expect(subject.environment.size).to eq(1)
|
25
25
|
end
|
26
|
-
|
26
|
+
|
27
27
|
it 'should know the keys in the environment' do
|
28
28
|
expect(subject.include?('dummy')).to be_falsey
|
29
29
|
entry = double('dummy')
|
30
|
-
subject.define('dummy', entry)
|
30
|
+
subject.define('dummy', entry)
|
31
31
|
expect(subject.include?('dummy')).to be_truthy
|
32
32
|
end
|
33
|
+
|
34
|
+
it 'should add nested environment' do
|
35
|
+
env_before = subject.environment
|
36
|
+
subject.nest
|
37
|
+
expect(subject.environment).not_to eq(env_before)
|
38
|
+
expect(subject.environment.outer).to eq(env_before)
|
39
|
+
end
|
40
|
+
|
41
|
+
it 'should remove nested environment' do
|
42
|
+
subject.nest
|
43
|
+
outer_before = subject.environment.outer
|
44
|
+
subject.unnest
|
45
|
+
expect(subject.environment).to eq(outer_before)
|
46
|
+
end
|
33
47
|
end # context
|
34
48
|
end # describe
|
35
49
|
end # module
|
@@ -0,0 +1,411 @@
|
|
1
|
+
require 'ostruct'
|
2
|
+
require_relative '../spec_helper' # Use the RSpec framework
|
3
|
+
require_relative '../../lib/skeem/s_expr_nodes' # Load the classes under test
|
4
|
+
|
5
|
+
module Skeem
|
6
|
+
describe SkmElement do
|
7
|
+
let(:pos) {double('fake-position') }
|
8
|
+
subject { SkmElement.new(pos) }
|
9
|
+
|
10
|
+
context 'Initialization:' do
|
11
|
+
it 'should be initialized with a position' do
|
12
|
+
expect { SkmElement.new(pos) }.not_to raise_error
|
13
|
+
end
|
14
|
+
|
15
|
+
it 'should know its position' do
|
16
|
+
expect(subject.position).to eq(pos)
|
17
|
+
end
|
18
|
+
|
19
|
+
# Default (overridable) behavior of SkmElement
|
20
|
+
it 'should react by default to predicates' do
|
21
|
+
expect(subject).not_to be_boolean
|
22
|
+
expect(subject).not_to be_number
|
23
|
+
expect(subject).not_to be_real
|
24
|
+
expect(subject).not_to be_integer
|
25
|
+
expect(subject).not_to be_string
|
26
|
+
expect(subject).not_to be_symbol
|
27
|
+
end
|
28
|
+
end # context
|
29
|
+
end # describe
|
30
|
+
|
31
|
+
describe SkmTerminal do
|
32
|
+
let(:pos) { double('fake-position') }
|
33
|
+
let(:dummy_symbol) { double('fake-symbol') }
|
34
|
+
let(:sample_value) { 'sample-value' }
|
35
|
+
let(:dummy_token) do
|
36
|
+
obj = OpenStruct.new
|
37
|
+
obj.terminal = dummy_symbol
|
38
|
+
obj.lexeme = sample_value
|
39
|
+
obj
|
40
|
+
end
|
41
|
+
subject { SkmTerminal.new(dummy_token, pos) }
|
42
|
+
|
43
|
+
context 'Initialization:' do
|
44
|
+
it 'should be initialized with a token and a position' do
|
45
|
+
expect { SkmTerminal.new(dummy_token, pos) }.not_to raise_error
|
46
|
+
end
|
47
|
+
|
48
|
+
it 'should know its token' do
|
49
|
+
expect(subject.token).to eq(dummy_token)
|
50
|
+
end
|
51
|
+
|
52
|
+
it 'should know its value' do
|
53
|
+
expect(subject.value).to eq(sample_value)
|
54
|
+
end
|
55
|
+
|
56
|
+
it "should know the token's symbol" do
|
57
|
+
expect(subject.symbol).to eq(dummy_symbol)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
context 'Provided services:' do
|
62
|
+
it 'should return its text representation' do
|
63
|
+
expect(subject.inspect).to eq("<Skeem::SkmTerminal: sample-value>")
|
64
|
+
end
|
65
|
+
end # context
|
66
|
+
end # describe
|
67
|
+
|
68
|
+
describe SkmBoolean do
|
69
|
+
let(:pos) { double('fake-position') }
|
70
|
+
let(:dummy_symbol) { double('BOOLEAN') }
|
71
|
+
let(:sample_value) { false }
|
72
|
+
let(:dummy_token) do
|
73
|
+
obj = OpenStruct.new
|
74
|
+
obj.terminal = dummy_symbol
|
75
|
+
obj.lexeme = sample_value
|
76
|
+
obj
|
77
|
+
end
|
78
|
+
subject { SkmBoolean.new(dummy_token, pos) }
|
79
|
+
|
80
|
+
context 'Initialization:' do
|
81
|
+
it 'should be initialized with a token and a position' do
|
82
|
+
expect { SkmBoolean.new(dummy_token, pos) }.not_to raise_error
|
83
|
+
end
|
84
|
+
|
85
|
+
it 'should react positively to boolean? predicate' do
|
86
|
+
expect(subject).to be_boolean
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
context 'Provided services:' do
|
91
|
+
it 'should return its text representation' do
|
92
|
+
expect(subject.inspect).to eq('<Skeem::SkmBoolean: false>')
|
93
|
+
end
|
94
|
+
end # context
|
95
|
+
end # describe
|
96
|
+
|
97
|
+
describe SkmNumber do
|
98
|
+
let(:pos) { double('fake-position') }
|
99
|
+
let(:dummy_symbol) { double('dummy') }
|
100
|
+
let(:sample_value) { 0.5100 }
|
101
|
+
let(:dummy_token) do
|
102
|
+
obj = OpenStruct.new
|
103
|
+
obj.terminal = dummy_symbol
|
104
|
+
obj.lexeme = sample_value
|
105
|
+
obj
|
106
|
+
end
|
107
|
+
subject { SkmNumber.new(dummy_token, pos) }
|
108
|
+
|
109
|
+
context 'Initialization:' do
|
110
|
+
it 'should be initialized with a token and a position' do
|
111
|
+
expect { SkmNumber.new(dummy_token, pos) }.not_to raise_error
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
context 'Provided services:' do
|
116
|
+
it 'should react positively to number? predicate' do
|
117
|
+
expect(subject).to be_number
|
118
|
+
end
|
119
|
+
|
120
|
+
it 'should return its text representation' do
|
121
|
+
expect(subject.inspect).to eq('<Skeem::SkmNumber: 0.51>')
|
122
|
+
end
|
123
|
+
end # context
|
124
|
+
end # describe
|
125
|
+
|
126
|
+
describe SkmReal do
|
127
|
+
let(:pos) { double('fake-position') }
|
128
|
+
let(:dummy_symbol) { double('dummy') }
|
129
|
+
let(:sample_value) { 0.5100 }
|
130
|
+
let(:dummy_token) do
|
131
|
+
obj = OpenStruct.new
|
132
|
+
obj.terminal = dummy_symbol
|
133
|
+
obj.lexeme = sample_value
|
134
|
+
obj
|
135
|
+
end
|
136
|
+
subject { SkmReal.new(dummy_token, pos) }
|
137
|
+
|
138
|
+
context 'Provided services:' do
|
139
|
+
it 'should react positively to number? predicate' do
|
140
|
+
expect(subject).to be_number
|
141
|
+
end
|
142
|
+
|
143
|
+
it 'should react positively to real? predicate' do
|
144
|
+
expect(subject).to be_real
|
145
|
+
end
|
146
|
+
end
|
147
|
+
end # describe
|
148
|
+
|
149
|
+
describe SkmInteger do
|
150
|
+
let(:pos) { double('fake-position') }
|
151
|
+
let(:dummy_symbol) { double('dummy') }
|
152
|
+
let(:sample_value) { 3 }
|
153
|
+
let(:dummy_token) do
|
154
|
+
obj = OpenStruct.new
|
155
|
+
obj.terminal = dummy_symbol
|
156
|
+
obj.lexeme = sample_value
|
157
|
+
obj
|
158
|
+
end
|
159
|
+
subject { SkmInteger.new(dummy_token, pos) }
|
160
|
+
|
161
|
+
context 'Provided services:' do
|
162
|
+
it 'should react positively to number? predicate' do
|
163
|
+
expect(subject).to be_number
|
164
|
+
end
|
165
|
+
|
166
|
+
it 'should react positively to real? predicate' do
|
167
|
+
expect(subject).to be_real
|
168
|
+
end
|
169
|
+
|
170
|
+
it 'should react positively to integer? predicate' do
|
171
|
+
expect(subject).to be_real
|
172
|
+
end
|
173
|
+
end
|
174
|
+
end # describe
|
175
|
+
|
176
|
+
describe SkmString do
|
177
|
+
let(:pos) { double('fake-position') }
|
178
|
+
let(:dummy_symbol) { double('dummy') }
|
179
|
+
let(:sample_value) { 'Hello' }
|
180
|
+
let(:dummy_token) do
|
181
|
+
obj = OpenStruct.new
|
182
|
+
obj.terminal = dummy_symbol
|
183
|
+
obj.lexeme = sample_value
|
184
|
+
obj
|
185
|
+
end
|
186
|
+
subject { SkmString.new(dummy_token, pos) }
|
187
|
+
|
188
|
+
context 'Provided services:' do
|
189
|
+
it 'should react positively to string? predicate' do
|
190
|
+
expect(subject).to be_string
|
191
|
+
end
|
192
|
+
|
193
|
+
it 'should return its text representation' do
|
194
|
+
expect(subject.inspect).to eq('<Skeem::SkmString: Hello>')
|
195
|
+
end
|
196
|
+
end
|
197
|
+
end # describe
|
198
|
+
|
199
|
+
describe SkmIdentifier do
|
200
|
+
let(:pos) { double('fake-position') }
|
201
|
+
let(:dummy_symbol) { double('dummy') }
|
202
|
+
let(:sample_value) { 'this-is-it!' }
|
203
|
+
let(:dummy_token) do
|
204
|
+
obj = OpenStruct.new
|
205
|
+
obj.terminal = dummy_symbol
|
206
|
+
obj.lexeme = sample_value
|
207
|
+
obj
|
208
|
+
end
|
209
|
+
subject { SkmIdentifier.new(dummy_token, pos) }
|
210
|
+
|
211
|
+
context 'Provided services:' do
|
212
|
+
it 'should react positively to symbol? predicate' do
|
213
|
+
expect(subject).to be_symbol
|
214
|
+
end
|
215
|
+
|
216
|
+
it 'should return its text representation' do
|
217
|
+
expect(subject.inspect).to eq('<Skeem::SkmIdentifier: this-is-it!>')
|
218
|
+
end
|
219
|
+
end # context
|
220
|
+
end # describe
|
221
|
+
|
222
|
+
describe SkmList do
|
223
|
+
let(:sample_members) { [1, 2, 3] }
|
224
|
+
|
225
|
+
subject { SkmList.new(sample_members) }
|
226
|
+
|
227
|
+
context 'Initialization:' do
|
228
|
+
it 'should be initialized with its members' do
|
229
|
+
expect{ SkmList.new(sample_members) }.not_to raise_error
|
230
|
+
end
|
231
|
+
|
232
|
+
it 'should know its members' do
|
233
|
+
expect(subject.members).to eq(sample_members)
|
234
|
+
end
|
235
|
+
end # context
|
236
|
+
|
237
|
+
context 'Provided services:' do
|
238
|
+
it 'should retrieve its first member' do
|
239
|
+
expect(subject.first).to eq(1)
|
240
|
+
expect(subject.head).to eq(1)
|
241
|
+
end
|
242
|
+
|
243
|
+
it 'should retrieve its tail members' do
|
244
|
+
expect(subject.tail.inspect).to eq('<Skeem::SkmList: 2, 3>')
|
245
|
+
expect(subject.rest.inspect).to eq('<Skeem::SkmList: 2, 3>')
|
246
|
+
end
|
247
|
+
|
248
|
+
it 'should return its text representation' do
|
249
|
+
expect(subject.inspect).to eq('<Skeem::SkmList: 1, 2, 3>')
|
250
|
+
end
|
251
|
+
end # context
|
252
|
+
end # describe
|
253
|
+
|
254
|
+
|
255
|
+
describe SkmDefinition do
|
256
|
+
let(:pos) { double('fake-position') }
|
257
|
+
let(:sample_symbol) { SkmIdentifier.create('this-is-it!') }
|
258
|
+
let(:sample_expr) { SkmInteger.create(10) }
|
259
|
+
|
260
|
+
subject { SkmDefinition.new(pos, sample_symbol, sample_expr) }
|
261
|
+
|
262
|
+
context 'Initialization:' do
|
263
|
+
it 'should be initialized with a symbol and an expression' do
|
264
|
+
expect{ SkmDefinition.new(pos, sample_symbol, sample_expr) }.not_to raise_error
|
265
|
+
end
|
266
|
+
|
267
|
+
it 'should know its variable' do
|
268
|
+
expect(subject.variable).to eq(sample_symbol)
|
269
|
+
end
|
270
|
+
|
271
|
+
it 'should know its expression' do
|
272
|
+
expect(subject.expression).to eq(sample_expr)
|
273
|
+
end
|
274
|
+
end # context
|
275
|
+
|
276
|
+
context 'Provided services:' do
|
277
|
+
it 'should return its text representation' do
|
278
|
+
txt1 = '<Skeem::SkmDefinition: <Skeem::SkmIdentifier: this-is-it!>,'
|
279
|
+
txt2 = ' <Skeem::SkmInteger: 10>>'
|
280
|
+
expect(subject.inspect).to eq(txt1 + txt2)
|
281
|
+
end
|
282
|
+
end # context
|
283
|
+
end # describe
|
284
|
+
|
285
|
+
describe SkmVariableReference do
|
286
|
+
let(:pos) { double('fake-position') }
|
287
|
+
let(:sample_symbol) { SkmIdentifier.create('this-is-it!') }
|
288
|
+
|
289
|
+
subject { SkmVariableReference.new(pos, sample_symbol) }
|
290
|
+
|
291
|
+
context 'Initialization:' do
|
292
|
+
it 'should be initialized with a position and a symbol' do
|
293
|
+
expect{ SkmVariableReference.new(pos, sample_symbol) }.not_to raise_error
|
294
|
+
end
|
295
|
+
|
296
|
+
it 'should know its variable' do
|
297
|
+
expect(subject.variable).to eq(sample_symbol)
|
298
|
+
end
|
299
|
+
end # context
|
300
|
+
|
301
|
+
context 'Provided services:' do
|
302
|
+
it 'should return its text representation' do
|
303
|
+
txt1 = '<Skeem::SkmVariableReference: <Skeem::SkmIdentifier: this-is-it!>>'
|
304
|
+
expect(subject.inspect).to eq(txt1)
|
305
|
+
end
|
306
|
+
end # context
|
307
|
+
end # describe
|
308
|
+
|
309
|
+
describe ProcedureCall do
|
310
|
+
let(:pos) { double('fake-position') }
|
311
|
+
let(:operator) { SkmIdentifier.create('+') }
|
312
|
+
let(:operands) { [1, 2, 3] }
|
313
|
+
|
314
|
+
subject { ProcedureCall.new(pos, operator, operands) }
|
315
|
+
|
316
|
+
context 'Initialization:' do
|
317
|
+
it 'should be initialized with an operator symbol and its operands' do
|
318
|
+
expect{ ProcedureCall.new(pos, operator, operands) }.not_to raise_error
|
319
|
+
end
|
320
|
+
|
321
|
+
it 'should know its operator' do
|
322
|
+
expect(subject.operator).to eq(operator)
|
323
|
+
end
|
324
|
+
|
325
|
+
it 'should know its operands' do
|
326
|
+
expect(subject.operands.inspect).to eq('<Skeem::SkmList: 1, 2, 3>')
|
327
|
+
end
|
328
|
+
end # context
|
329
|
+
|
330
|
+
context 'Provided services:' do
|
331
|
+
it 'should return its text representation' do
|
332
|
+
txt1 = '<Skeem::ProcedureCall: <Skeem::SkmIdentifier: +>, '
|
333
|
+
txt2 = '<Skeem::SkmList: 1, 2, 3>>'
|
334
|
+
expect(subject.inspect).to eq(txt1 + txt2)
|
335
|
+
end
|
336
|
+
end # context
|
337
|
+
end # describe
|
338
|
+
|
339
|
+
describe SkmCondition do
|
340
|
+
let(:pos) { double('fake-position') }
|
341
|
+
let(:s_test) { double('fake-test') }
|
342
|
+
let(:s_consequent) { double('fake-consequent') }
|
343
|
+
let(:s_alt) { double('fake-alternate') }
|
344
|
+
|
345
|
+
subject { SkmCondition.new(pos, s_test, s_consequent, s_alt) }
|
346
|
+
|
347
|
+
context 'Initialization:' do
|
348
|
+
it 'should be initialized with a pos and 3 expressions' do
|
349
|
+
expect{ SkmCondition.new(pos, s_test, s_consequent, s_alt) }.not_to raise_error
|
350
|
+
end
|
351
|
+
|
352
|
+
it 'should know its test' do
|
353
|
+
expect(subject.test).to eq(s_test)
|
354
|
+
end
|
355
|
+
|
356
|
+
it 'should know its consequent' do
|
357
|
+
expect(subject.consequent).to eq(s_consequent)
|
358
|
+
end
|
359
|
+
|
360
|
+
it 'should know its alternate' do
|
361
|
+
expect(subject.alternate).to eq(s_alt)
|
362
|
+
end
|
363
|
+
end # context
|
364
|
+
|
365
|
+
context 'Provided services:' do
|
366
|
+
it 'should return its text representation' do
|
367
|
+
txt1 = '<Skeem::SkmCondition: @test #<Double "fake-test">, '
|
368
|
+
txt2 = '@consequent #<Double "fake-consequent">, '
|
369
|
+
txt3 = '@alternate #<Double "fake-alternate">>'
|
370
|
+
expect(subject.inspect).to eq(txt1 + txt2 + txt3)
|
371
|
+
end
|
372
|
+
end # context
|
373
|
+
end # describe
|
374
|
+
|
375
|
+
describe SkmLambda do
|
376
|
+
let(:pos) { double('fake-position') }
|
377
|
+
let(:s_formals) { double('fake-formals') }
|
378
|
+
let(:s_defs) { double('fake-definitions') }
|
379
|
+
let(:s_sequence) { double('fake-sequence') }
|
380
|
+
let(:s_body) do { defs: s_defs, sequence: s_sequence } end
|
381
|
+
|
382
|
+
subject { SkmLambda.new(pos, s_formals, s_body) }
|
383
|
+
|
384
|
+
context 'Initialization:' do
|
385
|
+
it 'should be initialized with a pos and 3 expressions' do
|
386
|
+
expect{ SkmLambda.new(pos, s_formals, s_body) }.not_to raise_error
|
387
|
+
end
|
388
|
+
|
389
|
+
it 'should know its formals' do
|
390
|
+
expect(subject.formals).to eq(s_formals)
|
391
|
+
end
|
392
|
+
|
393
|
+
it 'should know its definitions' do
|
394
|
+
expect(subject.definitions).to eq(s_defs)
|
395
|
+
end
|
396
|
+
|
397
|
+
it 'should know its sequence' do
|
398
|
+
expect(subject.sequence).to eq(s_sequence)
|
399
|
+
end
|
400
|
+
end # context
|
401
|
+
|
402
|
+
context 'Provided services:' do
|
403
|
+
it 'should return its text representation' do
|
404
|
+
txt1 = '<Skeem::SkmLambda: @formals #<Double "fake-formals">, '
|
405
|
+
txt2 = '@definitions #<Double "fake-definitions">, '
|
406
|
+
txt3 = '@sequence #<Double "fake-sequence">>'
|
407
|
+
expect(subject.inspect).to eq(txt1 + txt2 + txt3)
|
408
|
+
end
|
409
|
+
end # context
|
410
|
+
end # describe
|
411
|
+
end # module
|
@@ -134,7 +134,11 @@ module Skeem
|
|
134
134
|
examples.each do |input|
|
135
135
|
subject.reinitialize(input)
|
136
136
|
token = subject.tokens.first
|
137
|
-
|
137
|
+
if token.lexeme == 'lambda'
|
138
|
+
expect(token.terminal).to eq('LAMBDA')
|
139
|
+
else
|
140
|
+
expect(token.terminal).to eq('IDENTIFIER')
|
141
|
+
end
|
138
142
|
expect(token.lexeme).to eq(input)
|
139
143
|
end
|
140
144
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: skeem
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.14
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Dimitri Geshef
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2018-09-
|
11
|
+
date: 2018-09-23 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rley
|
@@ -104,6 +104,7 @@ files:
|
|
104
104
|
- spec/skeem/parser_spec.rb
|
105
105
|
- spec/skeem/primitive/primitive_builder_spec.rb
|
106
106
|
- spec/skeem/runtime_spec.rb
|
107
|
+
- spec/skeem/s_expr_nodes_spec.rb
|
107
108
|
- spec/skeem/tokenizer_spec.rb
|
108
109
|
- spec/skeem_spec.rb
|
109
110
|
- spec/spec_helper.rb
|
@@ -139,5 +140,6 @@ test_files:
|
|
139
140
|
- spec/skeem/parser_spec.rb
|
140
141
|
- spec/skeem/primitive/primitive_builder_spec.rb
|
141
142
|
- spec/skeem/runtime_spec.rb
|
143
|
+
- spec/skeem/s_expr_nodes_spec.rb
|
142
144
|
- spec/skeem/tokenizer_spec.rb
|
143
145
|
- spec/skeem_spec.rb
|