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