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