skeem 0.0.25 → 0.0.26
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 +21 -0
- data/README.md +6 -0
- data/lib/skeem/primitive/primitive_builder.rb +32 -15
- data/lib/skeem/runtime.rb +24 -3
- data/lib/skeem/s_expr_builder.rb +6 -1
- data/lib/skeem/s_expr_nodes.rb +30 -25
- data/lib/skeem/skm_compound_datum.rb +2 -0
- data/lib/skeem/skm_simple_datum.rb +33 -1
- data/lib/skeem/skm_unary_expression.rb +4 -0
- data/lib/skeem/standard/base.skm +29 -3
- data/lib/skeem/tokenizer.rb +4 -4
- data/lib/skeem/version.rb +1 -1
- data/skeem.gemspec +1 -1
- data/spec/skeem/interpreter_spec.rb +22 -6
- data/spec/skeem/primitive/primitive_builder_spec.rb +44 -13
- data/spec/skeem/runtime_spec.rb +32 -1
- data/spec/skeem/skm_simple_datum_spec.rb +27 -0
- data/spec/skeem/tokenizer_spec.rb +1 -1
- metadata +4 -5
- data/lib/skeem/stoken.rb +0 -22
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: dac0bc87694eef9365c7c7ba6dab9b1827eab533
|
4
|
+
data.tar.gz: 5097264ce99da2c1e9a8e662e2276144a31b1660
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 25d09cd405ff9463eba60c098933df4804ff28051a663af4b1bed8217aee280ebfba0a4ff86391d7500b606ccbede4f3cb2021e9713c0535600ce0c30291f72d
|
7
|
+
data.tar.gz: 5e36e23fe874f945b394adb866b4ffb85b62a014a7afacd5653c279d4d7d9902f2b012ddf19b680259e8cc73e69385a0a9bd556b47f2c4ae46b7a35297d2974b
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,24 @@
|
|
1
|
+
## [0.0.26] - 2018-11-25
|
2
|
+
|
3
|
+
### Added
|
4
|
+
- Procedure `eqv?`
|
5
|
+
- Procedure `assert`
|
6
|
+
- Class `Runtime`, call stack added.
|
7
|
+
- Class `ProcedureCall`, attribute `call_site` added. It contains the location of the call (line and column)
|
8
|
+
|
9
|
+
### Changed
|
10
|
+
- File `primitive_builder.rb` implementation of: not procedure removed, `not` is now implemented in `base.skm`.
|
11
|
+
- Method `Tokenizer#build_token` updated to remain compatible with new Rley (> 0.7.x))
|
12
|
+
|
13
|
+
### Removed
|
14
|
+
- File `stoken.rb`: Class `SkmToken` is no more necessary.
|
15
|
+
|
16
|
+
## [0.0.25] - 2018-11-11
|
17
|
+
Aliasing of procedures with 'set!' is supported.
|
18
|
+
|
19
|
+
### Added
|
20
|
+
- Procedures `string=?`, `symbol=?`
|
21
|
+
|
1
22
|
## [0.0.24] - 2018-11-08
|
2
23
|
Many internal refactoring, augmented spec files base, initial quasiquotation implementation.
|
3
24
|
|
data/README.md
CHANGED
@@ -166,6 +166,9 @@ __Syntax:__
|
|
166
166
|
### Standard library
|
167
167
|
This section lists the implemented standard procedures
|
168
168
|
|
169
|
+
#### Equivalence predicates
|
170
|
+
* `eqv?`
|
171
|
+
|
169
172
|
#### Boolean procedures
|
170
173
|
* `boolean?`, `and`, `or`, `not`
|
171
174
|
|
@@ -189,6 +192,9 @@ This section lists the implemented standard procedures
|
|
189
192
|
#### Input/output procedures
|
190
193
|
* `newline`
|
191
194
|
|
195
|
+
#### Special procedures
|
196
|
+
* `assert`
|
197
|
+
|
192
198
|
Roadmap:
|
193
199
|
- Extend language support
|
194
200
|
- Implement REPL
|
@@ -55,6 +55,7 @@ module Skeem
|
|
55
55
|
end
|
56
56
|
|
57
57
|
def add_comparison(aRuntime)
|
58
|
+
create_eqv?(aRuntime)
|
58
59
|
create_equal(aRuntime)
|
59
60
|
create_lt(aRuntime)
|
60
61
|
create_gt(aRuntime)
|
@@ -66,11 +67,11 @@ module Skeem
|
|
66
67
|
create_object_predicate(aRuntime, 'number?')
|
67
68
|
create_object_predicate(aRuntime, 'real?')
|
68
69
|
create_object_predicate(aRuntime, 'integer?')
|
70
|
+
create_object_predicate(aRuntime, 'exact?')
|
69
71
|
create_number2string(aRuntime)
|
70
72
|
end
|
71
73
|
|
72
74
|
def add_boolean_procedures(aRuntime)
|
73
|
-
create_not(aRuntime)
|
74
75
|
create_and(aRuntime)
|
75
76
|
create_or(aRuntime)
|
76
77
|
create_object_predicate(aRuntime, 'boolean?')
|
@@ -106,6 +107,7 @@ module Skeem
|
|
106
107
|
end
|
107
108
|
|
108
109
|
def add_special_procedures(aRuntime)
|
110
|
+
create_assert(aRuntime)
|
109
111
|
create_debug(aRuntime)
|
110
112
|
end
|
111
113
|
|
@@ -194,7 +196,7 @@ module Skeem
|
|
194
196
|
end
|
195
197
|
|
196
198
|
def create_modulo(aRuntime)
|
197
|
-
|
199
|
+
primitive = ->(runtime, argument1, argument2) do
|
198
200
|
operand_1 = argument1.evaluate(runtime)
|
199
201
|
operand_2 = argument2.evaluate(runtime)
|
200
202
|
raw_result = operand_1.value.modulo(operand_2.value)
|
@@ -203,6 +205,17 @@ module Skeem
|
|
203
205
|
|
204
206
|
define_primitive_proc(aRuntime, 'floor-remainder', binary, primitive)
|
205
207
|
end
|
208
|
+
|
209
|
+
def create_eqv?(aRuntime)
|
210
|
+
primitive = ->(runtime, argument1, argument2) do
|
211
|
+
operand_1 = argument1.evaluate(runtime)
|
212
|
+
operand_2 = argument2.evaluate(runtime)
|
213
|
+
raw_result = operand_1.eqv?(operand_2)
|
214
|
+
to_datum(raw_result)
|
215
|
+
end
|
216
|
+
|
217
|
+
define_primitive_proc(aRuntime, 'eqv?', binary, primitive)
|
218
|
+
end
|
206
219
|
|
207
220
|
def create_equal(aRuntime)
|
208
221
|
primitive = ->(runtime, first_operand, arglist) do
|
@@ -303,19 +316,6 @@ module Skeem
|
|
303
316
|
define_primitive_proc(aRuntime, 'number->string', unary, primitive)
|
304
317
|
end
|
305
318
|
|
306
|
-
def create_not(aRuntime)
|
307
|
-
primitive = ->(runtime, arg) do
|
308
|
-
arg_evaluated = arg.evaluate(runtime)
|
309
|
-
if arg_evaluated.boolean? && arg_evaluated.value == false
|
310
|
-
boolean(true)
|
311
|
-
else
|
312
|
-
boolean(false)
|
313
|
-
end
|
314
|
-
end
|
315
|
-
|
316
|
-
define_primitive_proc(aRuntime, 'not', unary, primitive)
|
317
|
-
end
|
318
|
-
|
319
319
|
def create_and(aRuntime)
|
320
320
|
# arglist should be a Ruby Array
|
321
321
|
primitive = ->(runtime, arglist) do
|
@@ -466,6 +466,23 @@ module Skeem
|
|
466
466
|
|
467
467
|
define_primitive_proc(aRuntime, 'newline', nullary, primitive)
|
468
468
|
end
|
469
|
+
|
470
|
+
def create_assert(aRuntime)
|
471
|
+
primitive = ->(runtime, arg) do
|
472
|
+
arg_evaluated = arg.evaluate(runtime)
|
473
|
+
if arg_evaluated.boolean? && arg_evaluated.value == false
|
474
|
+
assert_call = aRuntime.caller
|
475
|
+
pos = assert_call.call_site
|
476
|
+
# Error: assertion failed: (> 1 2)
|
477
|
+
msg = "assertion failed on line #{pos.line}, column #{pos.column}"
|
478
|
+
raise StandardError, 'Error: ' + msg
|
479
|
+
else
|
480
|
+
boolean(true)
|
481
|
+
end
|
482
|
+
end
|
483
|
+
|
484
|
+
define_primitive_proc(aRuntime, 'assert', unary, primitive)
|
485
|
+
end
|
469
486
|
|
470
487
|
def create_debug(aRuntime)
|
471
488
|
primitive = ->(runtime) do
|
data/lib/skeem/runtime.rb
CHANGED
@@ -4,9 +4,11 @@ require_relative 'environment'
|
|
4
4
|
module Skeem
|
5
5
|
class Runtime
|
6
6
|
attr_reader(:environment)
|
7
|
+
attr_reader(:call_stack)
|
7
8
|
|
8
9
|
def initialize(anEnvironment)
|
9
10
|
@environment = anEnvironment
|
11
|
+
@call_stack = []
|
10
12
|
end
|
11
13
|
|
12
14
|
def include?(anIdentifier)
|
@@ -65,15 +67,34 @@ module Skeem
|
|
65
67
|
return environment.depth
|
66
68
|
end
|
67
69
|
|
70
|
+
def push(anEnvironment)
|
71
|
+
@environment = anEnvironment
|
72
|
+
end
|
73
|
+
|
68
74
|
# Make the outer enviromnent thecurrent one inside the provided block
|
69
75
|
def pop
|
70
76
|
env = environment
|
71
77
|
@environment = environment.outer
|
72
78
|
env
|
73
79
|
end
|
74
|
-
|
75
|
-
def
|
76
|
-
|
80
|
+
|
81
|
+
def push_call(aProcCall)
|
82
|
+
if aProcCall.kind_of?(ProcedureCall)
|
83
|
+
call_stack.push(aProcCall)
|
84
|
+
else
|
85
|
+
raise StandardError, "Invalid call object #{aProcCall.inspect}"
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
def pop_call()
|
90
|
+
if call_stack.empty?
|
91
|
+
raise StandardError, 'Skeem call stack empty!'
|
92
|
+
end
|
93
|
+
call_stack.pop
|
94
|
+
end
|
95
|
+
|
96
|
+
def caller
|
97
|
+
call_stack.last
|
77
98
|
end
|
78
99
|
|
79
100
|
private
|
data/lib/skeem/s_expr_builder.rb
CHANGED
@@ -112,7 +112,12 @@ module Skeem
|
|
112
112
|
|
113
113
|
# rule('proc_call_args' => 'LPAREN operator operand_plus RPAREN')
|
114
114
|
def reduce_proc_call_args(_production, aRange, _tokens, theChildren)
|
115
|
-
ProcedureCall.new(aRange, theChildren[1], theChildren[2])
|
115
|
+
pcall = ProcedureCall.new(aRange, theChildren[1], theChildren[2])
|
116
|
+
if theChildren[1].kind_of?(SkmVariableReference)
|
117
|
+
pcall.call_site = theChildren[1].child.token.position
|
118
|
+
end
|
119
|
+
|
120
|
+
pcall
|
116
121
|
end
|
117
122
|
|
118
123
|
# rule('operand_plus' => 'operand_plus operand').as 'multiple_operands'
|
data/lib/skeem/s_expr_nodes.rb
CHANGED
@@ -10,28 +10,28 @@ module Skeem
|
|
10
10
|
def value
|
11
11
|
:UNDEFINED
|
12
12
|
end
|
13
|
-
|
13
|
+
|
14
14
|
def ==(other)
|
15
15
|
return true if other.kind_of?(SkmUndefined)
|
16
|
-
|
16
|
+
|
17
17
|
result = case other
|
18
18
|
when Symbol
|
19
19
|
self.value == other
|
20
20
|
when String
|
21
21
|
self.value.to_s == other
|
22
|
-
else
|
22
|
+
else
|
23
23
|
raise StandardError, other.inspect
|
24
24
|
end
|
25
25
|
end
|
26
26
|
end # class
|
27
|
-
|
27
|
+
|
28
28
|
class SkmMultiExpression < SkmExpression
|
29
29
|
# Part of the 'visitee' role in Visitor design pattern.
|
30
30
|
# @param _visitor [SkmElementVisitor] the visitor
|
31
31
|
def accept(aVisitor)
|
32
32
|
aVisitor.visit_multi_expression(self)
|
33
33
|
end
|
34
|
-
|
34
|
+
|
35
35
|
# @return [Array] the names of attributes referencing child SkmElement.
|
36
36
|
def associations
|
37
37
|
raise NotImplementedError
|
@@ -66,7 +66,7 @@ module Skeem
|
|
66
66
|
if entry.expression.kind_of?(SkmLambda)
|
67
67
|
@expression = entry.expression
|
68
68
|
end
|
69
|
-
end
|
69
|
+
end
|
70
70
|
else
|
71
71
|
# INFINITE LOOP DANGER: definition of 'x' is a reference to 'x'!
|
72
72
|
# Way out: the lookup for the reference should start from outer
|
@@ -82,11 +82,11 @@ module Skeem
|
|
82
82
|
|
83
83
|
result
|
84
84
|
end
|
85
|
-
|
85
|
+
|
86
86
|
def quasiquote(aRuntime)
|
87
87
|
quasi_var = variable.quasiquote(aRuntime)
|
88
88
|
quasi_expression = variable.quasiquote(aRuntime)
|
89
|
-
|
89
|
+
|
90
90
|
if quasi_var.equal?(variable) && quasi_expression.equal?(expression)
|
91
91
|
self
|
92
92
|
else
|
@@ -96,7 +96,7 @@ module Skeem
|
|
96
96
|
|
97
97
|
# call method should only invoked when the expression is a SkmLambda
|
98
98
|
def call(aRuntime, aProcedureCall)
|
99
|
-
unless [SkmLambda, Primitive::PrimitiveProcedure].include?(expression.class)
|
99
|
+
unless [SkmLambda, Primitive::PrimitiveProcedure].include?(expression.class)
|
100
100
|
err_msg = "Expected a SkmLambda instead of #{expression.class}"
|
101
101
|
raise StandardError, err_msg
|
102
102
|
end
|
@@ -111,7 +111,7 @@ module Skeem
|
|
111
111
|
result << inspect_suffix
|
112
112
|
result
|
113
113
|
end
|
114
|
-
|
114
|
+
|
115
115
|
def associations
|
116
116
|
[:variable, :expression]
|
117
117
|
end
|
@@ -120,6 +120,7 @@ module Skeem
|
|
120
120
|
class ProcedureCall < SkmMultiExpression
|
121
121
|
attr_reader :operator
|
122
122
|
attr_reader :operands
|
123
|
+
attr_accessor :call_site
|
123
124
|
|
124
125
|
def initialize(aPosition, anOperator, theOperands)
|
125
126
|
super(aPosition)
|
@@ -147,16 +148,18 @@ module Skeem
|
|
147
148
|
# $stderr.puts "## CALL(#{var_key.value}) ###################"
|
148
149
|
# $stderr.puts operands.inspect
|
149
150
|
end
|
151
|
+
aRuntime.push_call(self)
|
150
152
|
result = procedure.call(aRuntime, self)
|
153
|
+
aRuntime.pop_call
|
151
154
|
# $stderr.puts "## RETURN #{result.inspect}"
|
152
155
|
result
|
153
156
|
end
|
154
|
-
|
157
|
+
|
155
158
|
def quasiquote(aRuntime)
|
156
159
|
quasi_operator = operator.quasiquote(aRuntime)
|
157
160
|
quasi_operands = operands.map { |oper | oper.quasiquote(aRuntime) }
|
158
|
-
|
159
|
-
self.class.new(position, quasi_operator, quasi_operands)
|
161
|
+
|
162
|
+
self.class.new(position, quasi_operator, quasi_operands)
|
160
163
|
end
|
161
164
|
|
162
165
|
def inspect
|
@@ -164,7 +167,7 @@ module Skeem
|
|
164
167
|
result << '@operands ' + operands.inspect + inspect_suffix
|
165
168
|
result
|
166
169
|
end
|
167
|
-
|
170
|
+
|
168
171
|
def associations
|
169
172
|
[:operator, :operands]
|
170
173
|
end
|
@@ -194,14 +197,14 @@ module Skeem
|
|
194
197
|
condition_result = consequent.evaluate(aRuntime)
|
195
198
|
end
|
196
199
|
end
|
197
|
-
|
200
|
+
|
198
201
|
def quasiquote(aRuntime)
|
199
202
|
quasi_test = test.quasiquote(aRuntime)
|
200
203
|
quasi_consequent = consequent.quasiquote(aRuntime)
|
201
204
|
quasi_alternate = alternate.quasiquote(aRuntime)
|
202
|
-
|
203
|
-
self.class.new(position, quasi_test, quasi_consequent, quasi_alternate)
|
204
|
-
end
|
205
|
+
|
206
|
+
self.class.new(position, quasi_test, quasi_consequent, quasi_alternate)
|
207
|
+
end
|
205
208
|
|
206
209
|
def inspect
|
207
210
|
result = inspect_prefix + '@test ' + test.inspect + ', '
|
@@ -209,10 +212,10 @@ module Skeem
|
|
209
212
|
result << '@alternate ' + alternate.inspect + inspect_suffix
|
210
213
|
result
|
211
214
|
end
|
212
|
-
|
215
|
+
|
213
216
|
def associations
|
214
217
|
[:test, :consequent, :alternate]
|
215
|
-
end
|
218
|
+
end
|
216
219
|
end # class
|
217
220
|
|
218
221
|
SkmArity = Struct.new(:low, :high) do
|
@@ -301,15 +304,15 @@ module Skeem
|
|
301
304
|
def evaluate(aRuntime)
|
302
305
|
formals.evaluate(aRuntime)
|
303
306
|
end
|
304
|
-
=begin
|
307
|
+
=begin
|
305
308
|
TODO
|
306
309
|
def quasiquote(aRuntime)
|
307
310
|
quasi_test = test.quasiquote(aRuntime)
|
308
311
|
quasi_consequent = consequent.quasiquote(aRuntime)
|
309
312
|
quasi_alternate = alternate.quasiquote(aRuntime)
|
310
|
-
|
311
|
-
self.class.new(position, quasi_test, quasi_consequent, quasi_alternate)
|
312
|
-
end
|
313
|
+
|
314
|
+
self.class.new(position, quasi_test, quasi_consequent, quasi_alternate)
|
315
|
+
end
|
313
316
|
=end
|
314
317
|
def call(aRuntime, aProcedureCall)
|
315
318
|
aRuntime.nest
|
@@ -331,13 +334,15 @@ module Skeem
|
|
331
334
|
formals.required_arity
|
332
335
|
end
|
333
336
|
|
337
|
+
alias eqv? equal?
|
338
|
+
|
334
339
|
def inspect
|
335
340
|
result = inspect_prefix + '@formals ' + formals.inspect + ', '
|
336
341
|
result << '@definitions ' + definitions.inspect + ', '
|
337
342
|
result << '@sequence ' + sequence.inspect + inspect_suffix
|
338
343
|
result
|
339
344
|
end
|
340
|
-
|
345
|
+
|
341
346
|
def associations
|
342
347
|
[:formals, :definitions, :sequence]
|
343
348
|
end
|
@@ -19,9 +19,13 @@ module Skeem
|
|
19
19
|
return lightweight
|
20
20
|
end
|
21
21
|
|
22
|
-
def symbol
|
22
|
+
def symbol
|
23
23
|
token.terminal
|
24
24
|
end
|
25
|
+
|
26
|
+
def position
|
27
|
+
token.position
|
28
|
+
end
|
25
29
|
|
26
30
|
# Equality operator.
|
27
31
|
# Returns true when: self and 'other' are identical, or
|
@@ -39,6 +43,8 @@ module Skeem
|
|
39
43
|
|
40
44
|
result
|
41
45
|
end
|
46
|
+
|
47
|
+
alias eqv? ==
|
42
48
|
|
43
49
|
def done!()
|
44
50
|
# Do nothing
|
@@ -87,18 +93,42 @@ module Skeem
|
|
87
93
|
def number?
|
88
94
|
true
|
89
95
|
end
|
96
|
+
|
97
|
+
def eqv?(other)
|
98
|
+
return true if self.equal?(other)
|
99
|
+
|
100
|
+
result = if other.kind_of?(SkmNumber)
|
101
|
+
if self.exact? != other.exact?
|
102
|
+
false
|
103
|
+
else
|
104
|
+
self.value == other.value
|
105
|
+
end
|
106
|
+
else
|
107
|
+
self.value == other
|
108
|
+
end
|
109
|
+
|
110
|
+
result
|
111
|
+
end
|
90
112
|
end # class
|
91
113
|
|
92
114
|
class SkmReal < SkmNumber
|
93
115
|
def real?
|
94
116
|
true
|
95
117
|
end
|
118
|
+
|
119
|
+
def exact?
|
120
|
+
false
|
121
|
+
end
|
96
122
|
end # class
|
97
123
|
|
98
124
|
class SkmInteger < SkmReal
|
99
125
|
def integer?
|
100
126
|
true
|
101
127
|
end
|
128
|
+
|
129
|
+
def exact?
|
130
|
+
true
|
131
|
+
end
|
102
132
|
end # class
|
103
133
|
|
104
134
|
class SkmString < SkmSimpleDatum
|
@@ -111,6 +141,8 @@ module Skeem
|
|
111
141
|
true
|
112
142
|
end
|
113
143
|
|
144
|
+
alias eqv? equal?
|
145
|
+
|
114
146
|
def length
|
115
147
|
value.length
|
116
148
|
end
|
data/lib/skeem/standard/base.skm
CHANGED
@@ -1,10 +1,14 @@
|
|
1
1
|
; Standard R7RS procedures from section 6.2.6
|
2
|
+
|
3
|
+
; (zero? z)
|
4
|
+
; Returns true iff z is zero
|
2
5
|
(define zero?
|
3
6
|
(lambda (z)
|
4
7
|
(if (= z 0)
|
5
8
|
#t
|
6
9
|
#f)))
|
7
10
|
|
11
|
+
; (positive? x)
|
8
12
|
; Return true if x greater or equal to zero; false otherwise
|
9
13
|
(define positive?
|
10
14
|
(lambda (x)
|
@@ -12,29 +16,43 @@
|
|
12
16
|
#t
|
13
17
|
#f)))
|
14
18
|
|
19
|
+
; (negative? x)
|
20
|
+
; Returns true iff x < 0
|
15
21
|
(define negative?
|
16
22
|
(lambda (x)
|
17
23
|
(if (< x 0)
|
18
24
|
#t
|
19
25
|
#f)))
|
20
26
|
|
27
|
+
; (inexact? z)
|
28
|
+
; Returns true iff z is not an exact number
|
29
|
+
(define inexact?
|
30
|
+
(lambda (z)
|
31
|
+
(not (exact? z))))
|
32
|
+
|
21
33
|
; For backwards compatibility
|
22
34
|
(define modulo
|
23
35
|
(lambda (x y)
|
24
36
|
(floor-remainder x y)))
|
25
37
|
|
38
|
+
; (odd? n)
|
39
|
+
; Returns true iff n is odd
|
26
40
|
(define odd?
|
27
41
|
(lambda (n)
|
28
42
|
(if (= (modulo n 2) 1)
|
29
43
|
#t
|
30
44
|
#f)))
|
31
45
|
|
46
|
+
; (even? n)
|
47
|
+
; Returns true iff n is even
|
32
48
|
(define even?
|
33
49
|
(lambda (n)
|
34
50
|
(if (= (modulo n 2) 0)
|
35
51
|
#t
|
36
52
|
#f)))
|
37
53
|
|
54
|
+
; (abs x)
|
55
|
+
; Returns the absolute value of a number
|
38
56
|
(define abs
|
39
57
|
(lambda (x)
|
40
58
|
(if (positive? x)
|
@@ -45,7 +63,15 @@
|
|
45
63
|
(lambda (z)
|
46
64
|
(* z z)))
|
47
65
|
|
48
|
-
(
|
49
|
-
|
66
|
+
; (not obj)
|
67
|
+
; Logical inverse of obj
|
68
|
+
(define (not obj)
|
69
|
+
(if obj #f #t))
|
70
|
+
|
71
|
+
; (list arg ...)
|
72
|
+
; Allocates and returns a new list from its arguments
|
50
73
|
(define list
|
51
|
-
(lambda args args))
|
74
|
+
(lambda args args))
|
75
|
+
|
76
|
+
|
77
|
+
(define symbol=? string=?)
|
data/lib/skeem/tokenizer.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
# File: tokenizer.rb
|
2
2
|
# Tokenizer for Skeem language (a small subset of Scheme)
|
3
3
|
require 'strscan'
|
4
|
-
|
4
|
+
require 'rley'
|
5
5
|
|
6
6
|
module Skeem
|
7
7
|
# A tokenizer for the Skeem dialect.
|
@@ -58,7 +58,7 @@ module Skeem
|
|
58
58
|
@line_start = 0
|
59
59
|
end
|
60
60
|
|
61
|
-
# @return [Array<
|
61
|
+
# @return [Array<SkmToken>] | Returns a sequence of tokens
|
62
62
|
def tokens
|
63
63
|
tok_sequence = []
|
64
64
|
until @scanner.eos?
|
@@ -148,8 +148,8 @@ other literal data (section 2.4).
|
|
148
148
|
begin
|
149
149
|
value = convert_to(aLexeme, aSymbolName, aFormat)
|
150
150
|
col = scanner.pos - aLexeme.size - @line_start + 1
|
151
|
-
pos = Position.new(@lineno, col)
|
152
|
-
token =
|
151
|
+
pos = Rley::Lexical::Position.new(@lineno, col)
|
152
|
+
token = Rley::Lexical::Token.new(value, aSymbolName, pos)
|
153
153
|
rescue StandardError => exc
|
154
154
|
puts "Failing with '#{aSymbolName}' and '#{aLexeme}'"
|
155
155
|
raise exc
|
data/lib/skeem/version.rb
CHANGED
data/skeem.gemspec
CHANGED
@@ -55,7 +55,7 @@ SUMMARY
|
|
55
55
|
PkgExtending.pkg_files(spec)
|
56
56
|
PkgExtending.pkg_documentation(spec)
|
57
57
|
# Runtime dependencies
|
58
|
-
spec.add_dependency 'rley', '~> 0.
|
58
|
+
spec.add_dependency 'rley', '~> 0.7'
|
59
59
|
|
60
60
|
# Development dependencies
|
61
61
|
spec.add_development_dependency 'bundler', '~> 1.16'
|
@@ -568,18 +568,22 @@ SKEEM
|
|
568
568
|
end
|
569
569
|
end
|
570
570
|
|
571
|
-
it 'should implement the
|
571
|
+
it 'should implement the not procedure' do
|
572
572
|
checks = [
|
573
|
-
[
|
574
|
-
[
|
575
|
-
[
|
573
|
+
['(not #t)', false],
|
574
|
+
['(not 3)', false],
|
575
|
+
['(not (list 3))', false],
|
576
|
+
['(not #f)', true],
|
577
|
+
["(not '())", false],
|
578
|
+
['(not (list))', false],
|
579
|
+
["(not 'nil)", false]
|
576
580
|
]
|
577
581
|
checks.each do |(skeem_expr, expectation)|
|
578
582
|
result = subject.run(skeem_expr)
|
579
583
|
expect(result).to eq(expectation)
|
580
584
|
end
|
581
|
-
end
|
582
|
-
|
585
|
+
end
|
586
|
+
|
583
587
|
it 'should implement the list procedure' do
|
584
588
|
checks = [
|
585
589
|
['(list)', []],
|
@@ -591,6 +595,18 @@ SKEEM
|
|
591
595
|
expect(result.members).to eq(expectation)
|
592
596
|
end
|
593
597
|
end
|
598
|
+
|
599
|
+
it 'should implement the symbol=? procedure' do
|
600
|
+
checks = [
|
601
|
+
["(symbol=? 'a 'a)", true],
|
602
|
+
["(symbol=? 'a (string->symbol \"a\"))", true],
|
603
|
+
["(symbol=? 'a 'b)", false]
|
604
|
+
]
|
605
|
+
checks.each do |(skeem_expr, expectation)|
|
606
|
+
result = subject.run(skeem_expr)
|
607
|
+
expect(result).to eq(expectation)
|
608
|
+
end
|
609
|
+
end
|
594
610
|
end # context
|
595
611
|
end # describe
|
596
612
|
end # module
|
@@ -88,6 +88,26 @@ SKEEM
|
|
88
88
|
end # context
|
89
89
|
|
90
90
|
context 'Comparison operators' do
|
91
|
+
it 'should implement the eqv? procedure' do
|
92
|
+
checks = [
|
93
|
+
['(eqv? #f #f)', true],
|
94
|
+
['(eqv? #t #t)', true],
|
95
|
+
['(eqv? #f #t)', false],
|
96
|
+
["(eqv? 'a 'a)", true],
|
97
|
+
["(eqv? 'a 'b)", false],
|
98
|
+
['(eqv? 2 2)', true],
|
99
|
+
['(eqv? 2 2.0)', false],
|
100
|
+
['(eqv? 3 2)', false],
|
101
|
+
['(eqv? 100000000 100000000)', true],
|
102
|
+
['(eqv? "a" "a")', false],
|
103
|
+
['(eqv? "a" "b")', false]
|
104
|
+
]
|
105
|
+
checks.each do |(skeem_expr, expectation)|
|
106
|
+
result = subject.run(skeem_expr)
|
107
|
+
expect(result).to eq(expectation)
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
91
111
|
it 'should implement the equality operator' do
|
92
112
|
checks = [
|
93
113
|
['(= 3)', true], # '=' as unary operator
|
@@ -222,18 +242,6 @@ SKEEM
|
|
222
242
|
end # context
|
223
243
|
|
224
244
|
context 'Boolean procedures:' do
|
225
|
-
it 'should implement the not procedure' do
|
226
|
-
checks = [
|
227
|
-
['(not #t)', false],
|
228
|
-
['(not 3)', false],
|
229
|
-
['(not #f)', true]
|
230
|
-
]
|
231
|
-
checks.each do |(skeem_expr, expectation)|
|
232
|
-
result = subject.run(skeem_expr)
|
233
|
-
expect(result).to eq(expectation)
|
234
|
-
end
|
235
|
-
end
|
236
|
-
|
237
245
|
it 'should implement the and procedure' do
|
238
246
|
checks = [
|
239
247
|
['(and (= 2 2) (> 2 1))', true],
|
@@ -280,7 +288,8 @@ SKEEM
|
|
280
288
|
it 'should implement the boolean? procedure' do
|
281
289
|
checks = [
|
282
290
|
['(boolean? #f)', true],
|
283
|
-
['(boolean? 0)', false]
|
291
|
+
['(boolean? 0)', false],
|
292
|
+
["(boolean? '())", false]
|
284
293
|
]
|
285
294
|
checks.each do |(skeem_expr, expectation)|
|
286
295
|
result = subject.run(skeem_expr)
|
@@ -469,6 +478,28 @@ SKEEM
|
|
469
478
|
$stdout = default_stdout
|
470
479
|
end
|
471
480
|
end # context
|
481
|
+
|
482
|
+
context 'Miscellaneous procedures' do
|
483
|
+
it 'should return true when an assertion succeeds' do
|
484
|
+
source = <<-SKEEM
|
485
|
+
(define x 2)
|
486
|
+
(define y 1)
|
487
|
+
(assert (> x y))
|
488
|
+
SKEEM
|
489
|
+
expect(subject.run(source).last).to eq(true)
|
490
|
+
end
|
491
|
+
|
492
|
+
it 'should raise an error when an assertion fails' do
|
493
|
+
source = <<-SKEEM
|
494
|
+
(define x 1)
|
495
|
+
(define y 2)
|
496
|
+
(assert (> x y))
|
497
|
+
SKEEM
|
498
|
+
err = StandardError
|
499
|
+
msg = 'Error: assertion failed on line 3, column 4'
|
500
|
+
expect { subject.run(source) }.to raise_error(err, msg)
|
501
|
+
end
|
502
|
+
end # context
|
472
503
|
end # describe
|
473
504
|
end # module
|
474
505
|
end # module
|
data/spec/skeem/runtime_spec.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
require_relative '../spec_helper' # Use the RSpec framework
|
2
2
|
require_relative '../../lib/skeem/datum_dsl'
|
3
|
+
require_relative '../../lib/skeem/s_expr_nodes'
|
3
4
|
require_relative '../../lib/skeem/primitive/primitive_builder'
|
4
5
|
|
5
6
|
require_relative '../../lib/skeem/runtime' # Load the class under test
|
@@ -19,6 +20,10 @@ module Skeem
|
|
19
20
|
it 'should know the environment' do
|
20
21
|
expect(subject.environment).to eq(some_env)
|
21
22
|
end
|
23
|
+
|
24
|
+
it 'should have an empty call stack' do
|
25
|
+
expect(subject.call_stack).to be_empty
|
26
|
+
end
|
22
27
|
end # context
|
23
28
|
|
24
29
|
context 'Provided services:' do
|
@@ -56,7 +61,7 @@ module Skeem
|
|
56
61
|
end
|
57
62
|
end # context
|
58
63
|
|
59
|
-
context 'Environment nesting' do
|
64
|
+
context 'Environment nesting:' do
|
60
65
|
it 'should add nested environment' do
|
61
66
|
expect(subject.depth).to be_zero
|
62
67
|
env_before = subject.environment
|
@@ -78,5 +83,31 @@ module Skeem
|
|
78
83
|
expect(subject.depth).to be_zero
|
79
84
|
end
|
80
85
|
end # context
|
86
|
+
|
87
|
+
context 'Call stack operations:' do
|
88
|
+
let(:sample_call) do
|
89
|
+
pos = double('fake-position')
|
90
|
+
ProcedureCall.new(pos, identifier('boolean?'), [integer(42)])
|
91
|
+
end
|
92
|
+
|
93
|
+
it 'should push a call to the stack call' do
|
94
|
+
expect { subject.push_call(sample_call) }.not_to raise_error
|
95
|
+
expect(subject.call_stack.size). to eq(1)
|
96
|
+
expect(subject.caller).to eq(sample_call)
|
97
|
+
|
98
|
+
subject.push_call(sample_call.clone)
|
99
|
+
expect(subject.call_stack.size). to eq(2)
|
100
|
+
end
|
101
|
+
|
102
|
+
it 'should pop a call from the call stack' do
|
103
|
+
subject.push_call(sample_call)
|
104
|
+
expect { subject.pop_call }.not_to raise_error
|
105
|
+
expect(subject.call_stack).to be_empty
|
106
|
+
|
107
|
+
err = StandardError
|
108
|
+
msg = 'Skeem call stack empty!'
|
109
|
+
expect { subject.pop_call }.to raise_error(err, msg)
|
110
|
+
end
|
111
|
+
end # context
|
81
112
|
end # describe
|
82
113
|
end # module
|
@@ -155,6 +155,19 @@ module Skeem
|
|
155
155
|
it 'should react positively to real? predicate' do
|
156
156
|
expect(subject).to be_real
|
157
157
|
end
|
158
|
+
|
159
|
+
it 'should react negatively to exact? predicate' do
|
160
|
+
expect(subject).not_to be_exact
|
161
|
+
end
|
162
|
+
|
163
|
+
it 'should implement the eqv? predicate' do
|
164
|
+
same = SkmReal.create(0.51)
|
165
|
+
different = SkmReal.create(1.21)
|
166
|
+
|
167
|
+
expect(subject).to be_eqv(subject)
|
168
|
+
expect(subject).to be_eqv(same)
|
169
|
+
expect(subject).not_to be_eqv(different)
|
170
|
+
end
|
158
171
|
end # context
|
159
172
|
end # describe
|
160
173
|
|
@@ -182,6 +195,20 @@ module Skeem
|
|
182
195
|
it 'should react positively to integer? predicate' do
|
183
196
|
expect(subject).to be_real
|
184
197
|
end
|
198
|
+
|
199
|
+
it 'should react positively to exact? predicate' do
|
200
|
+
expect(subject).to be_exact
|
201
|
+
end
|
202
|
+
|
203
|
+
it 'should implement the eqv? predicate' do
|
204
|
+
three = SkmInteger.create(3)
|
205
|
+
real_3 = SkmReal.create(3.0)
|
206
|
+
four = SkmInteger.create(4)
|
207
|
+
|
208
|
+
expect(subject).to be_eqv(three)
|
209
|
+
expect(subject).not_to be_eqv(real_3)
|
210
|
+
expect(subject).not_to be_eqv(four)
|
211
|
+
end
|
185
212
|
end # context
|
186
213
|
end # describe
|
187
214
|
|
@@ -32,7 +32,7 @@ module Skeem
|
|
32
32
|
it 'should tokenize single char delimiters' do
|
33
33
|
subject.reinitialize("( ) ' ` . , ,@")
|
34
34
|
tokens = subject.tokens
|
35
|
-
tokens.each { |token| expect(token).to be_kind_of(
|
35
|
+
tokens.each { |token| expect(token).to be_kind_of(Rley::Lexical::Token) }
|
36
36
|
terminals = tokens.map(&:terminal)
|
37
37
|
prediction = %w[LPAREN RPAREN APOSTROPHE
|
38
38
|
GRAVE_ACCENT PERIOD
|
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.26
|
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-11-
|
11
|
+
date: 2018-11-25 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rley
|
@@ -16,14 +16,14 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: '0.
|
19
|
+
version: '0.7'
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: '0.
|
26
|
+
version: '0.7'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: bundler
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -102,7 +102,6 @@ files:
|
|
102
102
|
- lib/skeem/skm_simple_datum.rb
|
103
103
|
- lib/skeem/skm_unary_expression.rb
|
104
104
|
- lib/skeem/standard/base.skm
|
105
|
-
- lib/skeem/stoken.rb
|
106
105
|
- lib/skeem/tokenizer.rb
|
107
106
|
- lib/skeem/version.rb
|
108
107
|
- skeem.gemspec
|
data/lib/skeem/stoken.rb
DELETED
@@ -1,22 +0,0 @@
|
|
1
|
-
require 'rley' # Load the Rley gem
|
2
|
-
|
3
|
-
module Skeem
|
4
|
-
Position = Struct.new(:line, :column) do
|
5
|
-
def to_s
|
6
|
-
"line #{line}, column #{column}"
|
7
|
-
end
|
8
|
-
end
|
9
|
-
|
10
|
-
# Specialization of Token class.
|
11
|
-
# It stores the position in (line, row) of the token
|
12
|
-
class SToken < Rley::Lexical::Token
|
13
|
-
attr_reader(:position)
|
14
|
-
|
15
|
-
def initialize(theLexeme, aTerminal, aPosition)
|
16
|
-
super(theLexeme, aTerminal)
|
17
|
-
@position = aPosition
|
18
|
-
end
|
19
|
-
end # class
|
20
|
-
end # module
|
21
|
-
|
22
|
-
# End of file
|