skeem 0.0.19 → 0.0.20
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 +11 -0
- data/README.md +64 -6
- data/lib/skeem/grammar.rb +23 -1
- data/lib/skeem/primitive/primitive_builder.rb +86 -97
- data/lib/skeem/s_expr_builder.rb +25 -0
- data/lib/skeem/s_expr_nodes.rb +40 -0
- data/lib/skeem/tokenizer.rb +32 -5
- data/lib/skeem/version.rb +1 -1
- data/spec/skeem/interpreter_spec.rb +54 -7
- data/spec/skeem/primitive/primitive_builder_spec.rb +51 -0
- data/spec/skeem/tokenizer_spec.rb +20 -0
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 91492f1d990da9deecf2146b21c13a5721cbf327
|
4
|
+
data.tar.gz: 06355e700c51894615aea3c34c7f26794a2482b4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: eb0d9f83f9816fbc881fef46b75e9587111539e3f4254772478e3e05bcb1c4fcd26d6dc16d83053f41c2fab55fe96e1d5d4cc1b02a7fc120218d09dcaa000088
|
7
|
+
data.tar.gz: cad77c7dab37087ff75202d2429d5213dd1ccf04e04d6a45993f41f9dc7ef587b897e2c677e10f1eb3d800c46f47a742f2378d95b2c020edb0f2d2da34d5171f
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,14 @@
|
|
1
|
+
## [0.0.20] - 2018-10-21
|
2
|
+
### Added
|
3
|
+
- Added support for quotation: (quote foo) or 'foo
|
4
|
+
- Added support for vectors #( 1 2 3 "foo")
|
5
|
+
- Added primitive procedures `vector?`, `vector`, `vector-length`
|
6
|
+
|
7
|
+
### Changed
|
8
|
+
- File `README.md` enhanced with detailed implementation status.
|
9
|
+
- Class `PrimitiveBuilder` in addition to new Skeem procedures, the class was vastly refactored.
|
10
|
+
- Class `Tokenizer` extended to cope with quotation and vectors.
|
11
|
+
|
1
12
|
## [0.0.19] - 2018-10-15
|
2
13
|
Added primitive procedures `list?`, `null?`, `length`
|
3
14
|
|
data/README.md
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
# Skeem
|
2
2
|
[](https://travis-ci.org/famished-tiger/Skeem)
|
3
|
+
[](https://ci.appveyor.com/project/famished-tiger/skeem)
|
3
4
|
[](https://badge.fury.io/rb/skeem)
|
4
5
|
[](https://github.com/famished-tiger/Skeem/blob/master/LICENSE.txt)
|
5
6
|
|
@@ -91,9 +92,69 @@ At this stage, the gem consists of a bare-bones interpreter.
|
|
91
92
|
SKEEM
|
92
93
|
|
93
94
|
result = schemer.run(scheme_code)
|
94
|
-
puts result.value # => 9332621544394415268169923885626670049071596826438162146859296389521759999322991560894146397615651828625369792082722375825118521091686400000000000000000000000
|
95
|
+
puts result.value # => 9332621544394415268169923885626670049071596826438162146859296389521759999322991560894146397615651828625369792082722375825118521091686400000000000000000000000
|
95
96
|
```
|
96
97
|
|
98
|
+
## Currently implemented R7RS features
|
99
|
+
### Data type literals
|
100
|
+
- Booleans: `#t`, `#true`, `#f`, `#false`
|
101
|
+
- Of the number hierarchy:
|
102
|
+
`real` (e.g. 2.718, 6.671e-11),
|
103
|
+
`integer` (42, -3)
|
104
|
+
- Strings: `"Hello, world."`
|
105
|
+
- Identifiers (symbols): `really-cool-procedure`
|
106
|
+
- Vectors: `#(1 2 "three")`
|
107
|
+
|
108
|
+
### Scheme Expressions
|
109
|
+
- Variable references
|
110
|
+
- Procedure calls
|
111
|
+
- Lambda expressions
|
112
|
+
- If conditionals
|
113
|
+
- Definitions
|
114
|
+
|
115
|
+
### Standard syntactic forms
|
116
|
+
#### define
|
117
|
+
__Purpose:__ Create a new variable and bind an expression/value to it.
|
118
|
+
__Syntax:__
|
119
|
+
* (define <identifier\> <expression\>)
|
120
|
+
|
121
|
+
#### if
|
122
|
+
__Purpose:__ Conditional evaluation based on a test expression.
|
123
|
+
__Syntax:__
|
124
|
+
* (if <test\> <consequent\> <alternate\>)
|
125
|
+
|
126
|
+
|
127
|
+
#### lambda
|
128
|
+
__Purpose:__ Definition of a procedure.
|
129
|
+
__Syntax:__
|
130
|
+
* (lambda <formals\> <body\>)
|
131
|
+
|
132
|
+
### Standard library
|
133
|
+
This section lists the implemented standard procedures
|
134
|
+
|
135
|
+
#### Boolean procedures
|
136
|
+
* `boolean?`, `not`
|
137
|
+
|
138
|
+
#### Numerical operations
|
139
|
+
* Number-level: `number?`, `real?`, `integer?`, `zero?`, `+`, `-`, `*`, `/`, `=`, `square`
|
140
|
+
* Real-level: `positive?`, `negative?`, `<`, `>`, `<=`, `>=`, `abs`, `floor-remainder`
|
141
|
+
* Integer-level: `even?`, `odd?`
|
142
|
+
|
143
|
+
#### List procedures
|
144
|
+
* `list?`, `null?`, `list`, `length`
|
145
|
+
|
146
|
+
#### String procedures
|
147
|
+
* `string?`
|
148
|
+
|
149
|
+
#### Symbol procedures
|
150
|
+
* `symbol?`
|
151
|
+
|
152
|
+
#### Vector procedures
|
153
|
+
* `vector?`, `vector`, `vector-length`
|
154
|
+
|
155
|
+
#### Input/output procedures
|
156
|
+
* `newline`
|
157
|
+
|
97
158
|
Roadmap:
|
98
159
|
- Extend language support
|
99
160
|
- Implement REPL
|
@@ -107,12 +168,9 @@ Roadmap:
|
|
107
168
|
Good to know:
|
108
169
|
Online book: [The Scheme Programming Language (4th Ed.)](https://www.scheme.com/tspl4/). Remark: covers an older version of Scheme.
|
109
170
|
|
110
|
-
## Implemented Scheme R7RS features:
|
111
171
|
|
112
|
-
|
113
|
-
|
114
|
-
* Numeric literals for integers and reals.
|
115
|
-
* `string` and `identifier` literals
|
172
|
+
|
173
|
+
|
116
174
|
|
117
175
|
## Other similar Ruby projects
|
118
176
|
__Skeem__ isn't the sole implementation of the Scheme language in Ruby.
|
data/lib/skeem/grammar.rb
CHANGED
@@ -9,9 +9,10 @@ module Skeem
|
|
9
9
|
# Names of grammar elements are based on the R7RS documentation
|
10
10
|
builder = Rley::Syntax::GrammarBuilder.new do
|
11
11
|
# Delimiters, separators...
|
12
|
-
|
12
|
+
add_terminals('APOSTROPHE') #, 'BACKQUOTE')
|
13
13
|
add_terminals('LPAREN', 'RPAREN')
|
14
14
|
add_terminals('PERIOD')
|
15
|
+
add_terminals('VECTOR_BEGIN')
|
15
16
|
|
16
17
|
# Literal values...
|
17
18
|
add_terminals('BOOLEAN', 'INTEGER', 'REAL')
|
@@ -19,6 +20,7 @@ module Skeem
|
|
19
20
|
|
20
21
|
# Keywords...
|
21
22
|
add_terminals('DEFINE', 'IF', 'LAMBDA')
|
23
|
+
add_terminals('QUOTE')
|
22
24
|
|
23
25
|
rule('program' => 'cmd_or_def_plus').as 'main'
|
24
26
|
rule('cmd_or_def_plus' => 'cmd_or_def_plus cmd_or_def').as 'multiple_cmd_def'
|
@@ -33,10 +35,30 @@ module Skeem
|
|
33
35
|
rule 'expression' => 'procedure_call'
|
34
36
|
rule 'expression' => 'lambda_expression'
|
35
37
|
rule 'expression' => 'conditional'
|
38
|
+
rule 'literal' => 'quotation'
|
36
39
|
rule 'literal' => 'self-evaluating'
|
40
|
+
rule('quotation' => 'APOSTROPHE datum').as 'quotation_abbrev'
|
41
|
+
rule('quotation' => 'LPAREN QUOTE datum RPAREN').as 'quotation'
|
37
42
|
rule 'self-evaluating' => 'BOOLEAN'
|
38
43
|
rule 'self-evaluating' => 'number'
|
39
44
|
rule 'self-evaluating' => 'STRING_LIT'
|
45
|
+
rule 'self-evaluating' => 'vector'
|
46
|
+
rule 'datum' => 'simple_datum'
|
47
|
+
rule 'datum' => 'compound_datum'
|
48
|
+
rule 'simple_datum' => 'BOOLEAN'
|
49
|
+
rule 'simple_datum' => 'number'
|
50
|
+
rule 'simple_datum' => 'STRING_LIT'
|
51
|
+
rule 'simple_datum' => 'symbol'
|
52
|
+
rule 'compound_datum' => 'list'
|
53
|
+
rule 'compound_datum' => 'vector'
|
54
|
+
rule 'list' => 'LPAREN datum_star RPAREN'
|
55
|
+
rule 'list' => 'LPAREN datum_plus PERIOD datum RPAREN'
|
56
|
+
rule('vector' => 'VECTOR_BEGIN datum_star RPAREN').as 'vector'
|
57
|
+
rule('datum_plus' => 'datum_plus datum').as 'multiple_datums'
|
58
|
+
rule('datum_plus' => 'datum').as 'last_datum'
|
59
|
+
rule('datum_star' => 'datum_star datum').as 'datum_star'
|
60
|
+
rule('datum_star' => []).as 'no_datum_yet'
|
61
|
+
rule 'symbol' => 'IDENTIFIER'
|
40
62
|
rule('procedure_call' => 'LPAREN operator RPAREN').as 'proc_call_nullary'
|
41
63
|
rule('procedure_call' => 'LPAREN operator operand_plus RPAREN').as 'proc_call_args'
|
42
64
|
rule('operand_plus' => 'operand_plus operand').as 'multiple_operands'
|
@@ -13,30 +13,31 @@ module Skeem
|
|
13
13
|
add_string_procedures(aRuntime)
|
14
14
|
add_symbol_procedures(aRuntime)
|
15
15
|
add_list_procedures(aRuntime)
|
16
|
+
add_vector_procedures(aRuntime)
|
16
17
|
add_io_procedures(aRuntime)
|
17
18
|
add_special_procedures(aRuntime)
|
18
19
|
end
|
19
20
|
|
20
21
|
private
|
21
|
-
|
22
|
+
|
22
23
|
def nullary
|
23
|
-
SkmArity.new(0, 0)
|
24
|
+
SkmArity.new(0, 0)
|
24
25
|
end
|
25
|
-
|
26
|
+
|
26
27
|
def unary
|
27
28
|
SkmArity.new(1, 1)
|
28
29
|
end
|
29
|
-
|
30
|
+
|
30
31
|
def binary
|
31
32
|
SkmArity.new(2, 2)
|
32
33
|
end
|
33
|
-
|
34
|
+
|
34
35
|
def zero_or_more
|
35
36
|
SkmArity.new(0, '*')
|
36
37
|
end
|
37
|
-
|
38
|
+
|
38
39
|
def one_or_more
|
39
|
-
SkmArity.new(1, '*')
|
40
|
+
SkmArity.new(1, '*')
|
40
41
|
end
|
41
42
|
|
42
43
|
def add_arithmetic(aRuntime)
|
@@ -56,30 +57,37 @@ module Skeem
|
|
56
57
|
end
|
57
58
|
|
58
59
|
def add_number_predicates(aRuntime)
|
59
|
-
|
60
|
-
|
61
|
-
|
60
|
+
create_object_predicate(aRuntime, 'number?')
|
61
|
+
create_object_predicate(aRuntime, 'real?')
|
62
|
+
create_object_predicate(aRuntime, 'integer?')
|
62
63
|
end
|
63
64
|
|
64
65
|
def add_boolean_procedures(aRuntime)
|
65
66
|
create_not(aRuntime)
|
66
|
-
|
67
|
+
create_object_predicate(aRuntime, 'boolean?')
|
67
68
|
end
|
68
69
|
|
69
70
|
def add_string_procedures(aRuntime)
|
70
|
-
|
71
|
+
create_object_predicate(aRuntime, 'string?')
|
71
72
|
end
|
72
73
|
|
73
74
|
def add_symbol_procedures(aRuntime)
|
74
|
-
|
75
|
+
create_object_predicate(aRuntime, 'symbol?')
|
75
76
|
end
|
76
|
-
|
77
|
+
|
77
78
|
def add_list_procedures(aRuntime)
|
78
|
-
|
79
|
-
|
79
|
+
create_object_predicate(aRuntime, 'list?')
|
80
|
+
create_object_predicate(aRuntime, 'null?')
|
80
81
|
create_length(aRuntime)
|
81
82
|
end
|
82
83
|
|
84
|
+
def add_vector_procedures(aRuntime)
|
85
|
+
create_object_predicate(aRuntime, 'vector?')
|
86
|
+
create_vector(aRuntime)
|
87
|
+
create_vector_length(aRuntime)
|
88
|
+
# create_vector_ref(aRuntime)
|
89
|
+
end
|
90
|
+
|
83
91
|
def add_io_procedures(aRuntime)(aRuntime)
|
84
92
|
create_newline(aRuntime)
|
85
93
|
end
|
@@ -135,7 +143,7 @@ module Skeem
|
|
135
143
|
define_primitive_proc(aRuntime, '*', zero_or_more, primitive)
|
136
144
|
end
|
137
145
|
|
138
|
-
|
146
|
+
|
139
147
|
def create_divide(aRuntime)
|
140
148
|
primitive = ->(runtime, first_operand, arglist) do
|
141
149
|
first_one = first_operand.evaluate(runtime)
|
@@ -155,19 +163,19 @@ module Skeem
|
|
155
163
|
end
|
156
164
|
to_skm(raw_result)
|
157
165
|
end
|
158
|
-
|
166
|
+
|
159
167
|
define_primitive_proc(aRuntime, '/', one_or_more, primitive)
|
160
168
|
end
|
161
169
|
|
162
170
|
def create_modulo(aRuntime)
|
163
|
-
primitive = ->(runtime, argument1, argument2) do
|
171
|
+
primitive = ->(runtime, argument1, argument2) do
|
164
172
|
operand_1 = argument1.evaluate(runtime)
|
165
173
|
operand_2 = argument2.evaluate(runtime)
|
166
174
|
raw_result = operand_1.value.modulo(operand_2.value)
|
167
175
|
to_skm(raw_result)
|
168
176
|
end
|
169
177
|
|
170
|
-
define_primitive_proc(aRuntime, 'floor-remainder', binary, primitive)
|
178
|
+
define_primitive_proc(aRuntime, 'floor-remainder', binary, primitive)
|
171
179
|
end
|
172
180
|
|
173
181
|
def create_equal(aRuntime)
|
@@ -176,7 +184,7 @@ module Skeem
|
|
176
184
|
if arglist.empty?
|
177
185
|
to_skm(true)
|
178
186
|
else
|
179
|
-
operands = evaluate_array(arglist, runtime)
|
187
|
+
operands = evaluate_array(arglist, runtime)
|
180
188
|
first_value = first_one.value
|
181
189
|
all_equal = operands.all? { |elem| first_value == elem.value }
|
182
190
|
to_skm(all_equal)
|
@@ -190,7 +198,7 @@ module Skeem
|
|
190
198
|
primitive = ->(runtime, first_operand, arglist) do
|
191
199
|
if arglist.empty?
|
192
200
|
to_skm(false)
|
193
|
-
else
|
201
|
+
else
|
194
202
|
operands = [first_operand.evaluate(runtime)]
|
195
203
|
operands.concat(evaluate_array(arglist, runtime))
|
196
204
|
result = true
|
@@ -208,7 +216,7 @@ module Skeem
|
|
208
216
|
primitive = ->(runtime, first_operand, arglist) do
|
209
217
|
if arglist.empty?
|
210
218
|
to_skm(false)
|
211
|
-
else
|
219
|
+
else
|
212
220
|
operands = [first_operand.evaluate(runtime)]
|
213
221
|
operands.concat(evaluate_array(arglist, runtime))
|
214
222
|
result = true
|
@@ -226,7 +234,7 @@ module Skeem
|
|
226
234
|
primitive = ->(runtime, first_operand, arglist) do
|
227
235
|
if arglist.empty?
|
228
236
|
to_skm(true)
|
229
|
-
else
|
237
|
+
else
|
230
238
|
operands = [first_operand.evaluate(runtime)]
|
231
239
|
operands.concat(evaluate_array(arglist, runtime))
|
232
240
|
result = true
|
@@ -244,7 +252,7 @@ module Skeem
|
|
244
252
|
primitive = ->(runtime, first_operand, arglist) do
|
245
253
|
if arglist.empty?
|
246
254
|
to_skm(true)
|
247
|
-
else
|
255
|
+
else
|
248
256
|
operands = [first_operand.evaluate(runtime)]
|
249
257
|
operands.concat(evaluate_array(arglist, runtime))
|
250
258
|
result = true
|
@@ -258,33 +266,6 @@ module Skeem
|
|
258
266
|
define_primitive_proc(aRuntime, '>=', one_or_more, primitive)
|
259
267
|
end
|
260
268
|
|
261
|
-
def create_number?(aRuntime)
|
262
|
-
primitive = ->(runtime, object) do
|
263
|
-
arg_evaluated = object.evaluate(runtime)
|
264
|
-
to_skm(arg_evaluated.number?)
|
265
|
-
end
|
266
|
-
|
267
|
-
define_primitive_proc(aRuntime, 'number?', unary, primitive)
|
268
|
-
end
|
269
|
-
|
270
|
-
def create_real?(aRuntime)
|
271
|
-
primitive = ->(runtime, arg) do
|
272
|
-
arg_evaluated = arg.evaluate(runtime)
|
273
|
-
to_skm(arg_evaluated.real?)
|
274
|
-
end
|
275
|
-
|
276
|
-
define_primitive_proc(aRuntime, 'real?', unary, primitive)
|
277
|
-
end
|
278
|
-
|
279
|
-
def create_integer?(aRuntime)
|
280
|
-
primitive = ->(runtime, arg) do
|
281
|
-
arg_evaluated = arg.evaluate(runtime)
|
282
|
-
to_skm(arg_evaluated.integer?)
|
283
|
-
end
|
284
|
-
|
285
|
-
define_primitive_proc(aRuntime, 'integer?', unary, primitive)
|
286
|
-
end
|
287
|
-
|
288
269
|
def create_not(aRuntime)
|
289
270
|
primitive = ->(runtime, arg) do
|
290
271
|
arg_evaluated = arg.evaluate(runtime)
|
@@ -294,67 +275,57 @@ module Skeem
|
|
294
275
|
to_skm(false)
|
295
276
|
end
|
296
277
|
end
|
297
|
-
|
278
|
+
|
298
279
|
define_primitive_proc(aRuntime, 'not', unary, primitive)
|
299
280
|
end
|
300
281
|
|
301
|
-
def
|
282
|
+
def create_length(aRuntime)
|
302
283
|
primitive = ->(runtime, arg) do
|
303
284
|
arg_evaluated = arg.evaluate(runtime)
|
304
|
-
|
285
|
+
check_argtype(arg_evaluated, SkmList, 'list', 'length')
|
286
|
+
to_skm(arg_evaluated.length)
|
305
287
|
end
|
306
288
|
|
307
|
-
define_primitive_proc(aRuntime, '
|
289
|
+
define_primitive_proc(aRuntime, 'length', unary, primitive)
|
308
290
|
end
|
309
291
|
|
310
|
-
def
|
311
|
-
primitive = ->(runtime,
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
end
|
292
|
+
def create_vector(aRuntime)
|
293
|
+
primitive = ->(runtime, arglist) do
|
294
|
+
if arglist.empty?
|
295
|
+
elements = []
|
296
|
+
else
|
297
|
+
elements = evaluate_array(arglist, aRuntime)
|
298
|
+
end
|
318
299
|
|
319
|
-
|
320
|
-
primitive = ->(runtime, arg) do
|
321
|
-
arg_evaluated = arg.evaluate(runtime)
|
322
|
-
to_skm(arg_evaluated.symbol?)
|
300
|
+
SkmVector.new(elements)
|
323
301
|
end
|
324
302
|
|
325
|
-
define_primitive_proc(aRuntime, '
|
303
|
+
define_primitive_proc(aRuntime, 'vector', zero_or_more, primitive)
|
326
304
|
end
|
327
|
-
|
328
|
-
def
|
305
|
+
|
306
|
+
def create_vector_length(aRuntime)
|
329
307
|
primitive = ->(runtime, arg) do
|
330
308
|
arg_evaluated = arg.evaluate(runtime)
|
331
|
-
|
309
|
+
check_argtype(arg_evaluated, SkmVector, 'vector', 'vector-length')
|
310
|
+
to_skm(arg_evaluated.length)
|
332
311
|
end
|
333
312
|
|
334
|
-
define_primitive_proc(aRuntime, '
|
313
|
+
define_primitive_proc(aRuntime, 'vector-length', unary, primitive)
|
335
314
|
end
|
336
315
|
|
337
|
-
def
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
primitive = ->(runtime, arg) do
|
348
|
-
arg_evaluated = arg.evaluate(runtime)
|
349
|
-
unless arg_evaluated.kind_of?(SkmList)
|
350
|
-
msg1 = "Procedure 'length': list argument required,"
|
351
|
-
msg2 = "but got #{arg_evaluated.value}"
|
352
|
-
raise StandardError, msg1 + ' ' + msg2
|
353
|
-
end
|
354
|
-
to_skm(arg_evaluated.length)
|
316
|
+
def create_vector_ref(aRuntime)
|
317
|
+
# argument 1: a vector, argument 2: an index(integer)
|
318
|
+
primitive = ->(runtime, aVector, anIndex) do
|
319
|
+
vector = aVector.evaluate(runtime)
|
320
|
+
check_argtype(vector, SkmVector, 'vector', 'vector-ref')
|
321
|
+
index = anIndex.evaluate(runtime)
|
322
|
+
check_argtype(index, SkmInteger, 'integer', 'vector-ref')
|
323
|
+
# TODO: index checking
|
324
|
+
raw_result = vector.elements[index.value]
|
325
|
+
to_skm(raw_result)
|
355
326
|
end
|
356
327
|
|
357
|
-
define_primitive_proc(aRuntime, '
|
328
|
+
define_primitive_proc(aRuntime, 'vector-ref', binary, primitive)
|
358
329
|
end
|
359
330
|
|
360
331
|
def create_newline(aRuntime)
|
@@ -374,6 +345,16 @@ module Skeem
|
|
374
345
|
define_primitive_proc(aRuntime, 'debug', nullary, primitive)
|
375
346
|
end
|
376
347
|
|
348
|
+
def create_object_predicate(aRuntime, predicate_name, msg_name = nil)
|
349
|
+
msg_name = predicate_name if msg_name.nil?
|
350
|
+
primitive = ->(runtime, arg) do
|
351
|
+
arg_evaluated = arg.evaluate(runtime)
|
352
|
+
to_skm(arg_evaluated.send(msg_name))
|
353
|
+
end
|
354
|
+
|
355
|
+
define_primitive_proc(aRuntime, predicate_name, unary, primitive)
|
356
|
+
end
|
357
|
+
|
377
358
|
|
378
359
|
def def_procedure(aRuntime, pairs)
|
379
360
|
pairs.each_slice(2) do |(name, code)|
|
@@ -381,7 +362,7 @@ module Skeem
|
|
381
362
|
define(aRuntime, func.identifier, func)
|
382
363
|
end
|
383
364
|
end
|
384
|
-
|
365
|
+
|
385
366
|
def define_primitive_proc(aRuntime, anIdentifier, anArity, aRubyLambda)
|
386
367
|
primitive = PrimitiveProcedure.new(anIdentifier, anArity, aRubyLambda)
|
387
368
|
define(aRuntime, primitive.identifier, primitive)
|
@@ -390,14 +371,22 @@ module Skeem
|
|
390
371
|
def define(aRuntime, aKey, anEntry)
|
391
372
|
aRuntime.define(aKey, anEntry)
|
392
373
|
end
|
393
|
-
|
374
|
+
|
394
375
|
def evaluate_array(anArray, aRuntime)
|
395
376
|
anArray.map { |elem| elem.evaluate(aRuntime) }
|
396
|
-
end
|
397
|
-
|
377
|
+
end
|
378
|
+
|
398
379
|
def evaluate_tail(anArray, aRuntime)
|
399
380
|
evaluate_array(anArray.drop(1), aRuntime)
|
400
|
-
end
|
381
|
+
end
|
382
|
+
|
383
|
+
def check_argtype(argument, requiredRubyClass, requiredSkmType, aProcName)
|
384
|
+
unless argument.kind_of?(requiredRubyClass)
|
385
|
+
msg1 = "Procedure '#{aProcName}': #{requiredSkmType} argument required,"
|
386
|
+
msg2 = "but got #{argument.value}"
|
387
|
+
raise StandardError, msg1 + ' ' + msg2
|
388
|
+
end
|
389
|
+
end
|
401
390
|
end # module
|
402
391
|
end # module
|
403
392
|
end # module
|
data/lib/skeem/s_expr_builder.rb
CHANGED
@@ -73,7 +73,32 @@ module Skeem
|
|
73
73
|
def reduce_variable_reference(_production, aRange, _tokens, theChildren)
|
74
74
|
SkmVariableReference.new(aRange, theChildren[0])
|
75
75
|
end
|
76
|
+
|
77
|
+
# rule('quotation' => 'APOSTROPHE datum').as 'quotation_abbrev'
|
78
|
+
def reduce_quotation_abbrev(_production, aRange, _tokens, theChildren)
|
79
|
+
SkmQuotation.new(theChildren[1])
|
80
|
+
end
|
76
81
|
|
82
|
+
# rule('quotation' => 'LPAREN QUOTE datum RPAREN').as 'quotation'
|
83
|
+
def reduce_quotation(_production, aRange, _tokens, theChildren)
|
84
|
+
SkmQuotation.new(theChildren[2])
|
85
|
+
end
|
86
|
+
|
87
|
+
# rule('vector' => 'VECTOR_BEGIN datum_star RPAREN').as 'vector'
|
88
|
+
def reduce_vector(_production, aRange, _tokens, theChildren)
|
89
|
+
SkmVector.new(theChildren[1])
|
90
|
+
end
|
91
|
+
|
92
|
+
# rule('datum_star' => 'datum_star datum').as 'datum_star'
|
93
|
+
def reduce_datum_star(_production, aRange, _tokens, theChildren)
|
94
|
+
theChildren[0] << theChildren[1]
|
95
|
+
end
|
96
|
+
|
97
|
+
# rule('datum_star' => []).as 'no_datum_yet'
|
98
|
+
def reduce_no_datum_yet(_production, aRange, _tokens, theChildren)
|
99
|
+
[]
|
100
|
+
end
|
101
|
+
|
77
102
|
# rule('procedure_call' => 'LPAREN operator RPAREN').as 'proc_call_nullary'
|
78
103
|
def reduce_proc_call_nullary(_production, aRange, _tokens, theChildren)
|
79
104
|
ProcedureCall.new(aRange, theChildren[1], [])
|
data/lib/skeem/s_expr_nodes.rb
CHANGED
@@ -56,6 +56,10 @@ module Skeem
|
|
56
56
|
def null?
|
57
57
|
false
|
58
58
|
end
|
59
|
+
|
60
|
+
def vector?
|
61
|
+
false
|
62
|
+
end
|
59
63
|
|
60
64
|
# Abstract method.
|
61
65
|
# Part of the 'visitee' role in Visitor design pattern.
|
@@ -247,6 +251,42 @@ module Skeem
|
|
247
251
|
alias to_a members
|
248
252
|
alias rest tail
|
249
253
|
end # class
|
254
|
+
|
255
|
+
class SkmVector < SkmElement
|
256
|
+
attr_accessor(:elements)
|
257
|
+
extend Forwardable
|
258
|
+
|
259
|
+
def_delegators :@elements, :each, :length, :empty?, :size
|
260
|
+
|
261
|
+
def initialize(theElements)
|
262
|
+
super(nil)
|
263
|
+
@elements = theElements.nil? ? [] : theElements
|
264
|
+
end
|
265
|
+
|
266
|
+
def vector?
|
267
|
+
true
|
268
|
+
end
|
269
|
+
|
270
|
+
def evaluate(aRuntime)
|
271
|
+
elements_evaluated = elements.map { |elem| elem.evaluate(aRuntime) }
|
272
|
+
SkmVector.new(elements_evaluated)
|
273
|
+
end
|
274
|
+
|
275
|
+
end # class
|
276
|
+
|
277
|
+
|
278
|
+
class SkmQuotation < SkmElement
|
279
|
+
attr_accessor(:datum)
|
280
|
+
|
281
|
+
def initialize(aDatum)
|
282
|
+
super(nil)
|
283
|
+
@datum = aDatum
|
284
|
+
end
|
285
|
+
|
286
|
+
def evaluate(aRuntime)
|
287
|
+
datum
|
288
|
+
end
|
289
|
+
end # class
|
250
290
|
|
251
291
|
class SkmDefinition < SkmElement
|
252
292
|
attr_reader :variable
|
data/lib/skeem/tokenizer.rb
CHANGED
@@ -23,15 +23,17 @@ module Skeem
|
|
23
23
|
'`' => 'BACKQUOTE',
|
24
24
|
'(' => 'LPAREN',
|
25
25
|
')' => 'RPAREN',
|
26
|
-
'.' => 'PERIOD'
|
26
|
+
'.' => 'PERIOD',
|
27
|
+
'#(' => 'VECTOR_BEGIN'
|
27
28
|
}.freeze
|
28
29
|
|
29
30
|
# Here are all the SRL keywords (in uppercase)
|
30
31
|
@@keywords = %w[
|
31
32
|
BEGIN
|
32
|
-
IF
|
33
33
|
DEFINE
|
34
|
+
IF
|
34
35
|
LAMBDA
|
36
|
+
QUOTE
|
35
37
|
].map { |x| [x, x] } .to_h
|
36
38
|
|
37
39
|
class ScanError < StandardError; end
|
@@ -74,10 +76,10 @@ module Skeem
|
|
74
76
|
if "()'`".include? curr_ch
|
75
77
|
# Delimiters, separators => single character token
|
76
78
|
token = build_token(@@lexeme2name[curr_ch], scanner.getch)
|
77
|
-
elsif (lexeme = scanner.scan(/(?:\.)(?=\s
|
79
|
+
elsif (lexeme = scanner.scan(/(?:\.)(?=\s)/)) # Single char occurring alone
|
78
80
|
token = build_token('PERIOD', lexeme)
|
79
|
-
elsif (lexeme = scanner.scan(/#(?:
|
80
|
-
token =
|
81
|
+
elsif (lexeme = scanner.scan(/#(?:(?:true)|(?:false)|(?:u8)|[\\\(tfeiodx]|(?:\d+[=#]))/))
|
82
|
+
token = cardinal_token(lexeme)
|
81
83
|
elsif (lexeme = scanner.scan(/[+-]?[0-9]+(?=\s|[|()";]|$)/))
|
82
84
|
token = build_token('INTEGER', lexeme) # Decimal radix
|
83
85
|
elsif (lexeme = scanner.scan(/[+-]?[0-9]+(?:\.[0-9]+)?(?:(?:e|E)[+-]?[0-9]+)?/))
|
@@ -109,6 +111,31 @@ module Skeem
|
|
109
111
|
|
110
112
|
return token
|
111
113
|
end
|
114
|
+
|
115
|
+
=begin
|
116
|
+
#t #f These are the boolean constants (section 6.3), along
|
117
|
+
with the alternatives #true and #false.
|
118
|
+
#\ This introduces a character constant (section 6.6).
|
119
|
+
#( This introduces a vector constant (section 6.8). Vector
|
120
|
+
constants are terminated by ) .
|
121
|
+
#u8( This introduces a bytevector constant (section 6.9).
|
122
|
+
Bytevector constants are terminated by ) .
|
123
|
+
#e #i #b #o #d #x These are used in the notation for
|
124
|
+
numbers (section 6.2.5).
|
125
|
+
#<n>= #<n># These are used for labeling and referencing
|
126
|
+
other literal data (section 2.4).
|
127
|
+
# token = build_token('BOOLEAN', lexeme)
|
128
|
+
=end
|
129
|
+
def cardinal_token(aLexeme)
|
130
|
+
case aLexeme
|
131
|
+
when /^#true|false|t|f$/
|
132
|
+
token = build_token('BOOLEAN', aLexeme)
|
133
|
+
when '#('
|
134
|
+
token = build_token(@@lexeme2name[aLexeme], aLexeme)
|
135
|
+
end
|
136
|
+
|
137
|
+
return token
|
138
|
+
end
|
112
139
|
|
113
140
|
def build_token(aSymbolName, aLexeme, aFormat = :default)
|
114
141
|
begin
|
data/lib/skeem/version.rb
CHANGED
@@ -83,6 +83,22 @@ module Skeem
|
|
83
83
|
expect(result.value).to eq(predicted)
|
84
84
|
end
|
85
85
|
end
|
86
|
+
|
87
|
+
it 'should evaluate vector of constants' do
|
88
|
+
source = '#(2018 10 20 "Sat")'
|
89
|
+
result = subject.run(source)
|
90
|
+
expect(result).to be_kind_of(SkmVector)
|
91
|
+
predictions = [
|
92
|
+
[SkmInteger, 2018],
|
93
|
+
[SkmInteger, 10],
|
94
|
+
[SkmInteger, 20],
|
95
|
+
[SkmString, 'Sat']
|
96
|
+
]
|
97
|
+
predictions.each_with_index do |(type, value), index|
|
98
|
+
expect(result.elements[index]).to be_kind_of(type)
|
99
|
+
expect(result.elements[index].value).to eq(value)
|
100
|
+
end
|
101
|
+
end
|
86
102
|
end # context
|
87
103
|
|
88
104
|
context 'Built-in primitives' do
|
@@ -115,7 +131,7 @@ SKEEM
|
|
115
131
|
end
|
116
132
|
|
117
133
|
it 'should implement the complete conditional form' do
|
118
|
-
|
134
|
+
checks = [
|
119
135
|
['(if (> 3 2) "yes" "no")', 'yes'],
|
120
136
|
['(if (> 2 3) "yes" "no")', 'no']
|
121
137
|
]
|
@@ -133,6 +149,39 @@ SKEEM
|
|
133
149
|
expect(result.value).to eq(1)
|
134
150
|
end
|
135
151
|
|
152
|
+
it 'should implement the quotation of constant literals' do
|
153
|
+
checks = [
|
154
|
+
['(quote a)', 'a'],
|
155
|
+
['(quote 145932)', 145932],
|
156
|
+
['(quote "abc")', 'abc'],
|
157
|
+
['(quote #t)', true],
|
158
|
+
["'a", 'a'],
|
159
|
+
["'145932", 145932],
|
160
|
+
["'\"abc\"", 'abc'],
|
161
|
+
["'#t", true]
|
162
|
+
]
|
163
|
+
checks.each do |(skeem_expr, expectation)|
|
164
|
+
result = subject.run(skeem_expr)
|
165
|
+
expect(result.value).to eq(expectation)
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
it 'should implement the quotation of vector' do
|
170
|
+
source = '(quote #(a b c))'
|
171
|
+
result = subject.run(source)
|
172
|
+
expect(result).to be_kind_of(SkmVector)
|
173
|
+
predictions = [
|
174
|
+
[SkmIdentifier, 'a'],
|
175
|
+
[SkmIdentifier, 'b'],
|
176
|
+
[SkmIdentifier, 'c']
|
177
|
+
]
|
178
|
+
predictions.each_with_index do |(type, value), index|
|
179
|
+
expect(result.elements[index]).to be_kind_of(type)
|
180
|
+
expect(result.elements[index].value).to eq(value)
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
184
|
+
|
136
185
|
it 'should implement the lambda function with one arg' do
|
137
186
|
source = <<-SKEEM
|
138
187
|
; Simplified 'abs' function implementation
|
@@ -196,7 +245,7 @@ SKEEM
|
|
196
245
|
expect(result).to be_kind_of(SkmList)
|
197
246
|
expect(result.length).to eq(4)
|
198
247
|
end
|
199
|
-
|
248
|
+
|
200
249
|
it 'should support procedures with dotted pair arguments' do
|
201
250
|
# Example from R7RS section 4.1.4
|
202
251
|
source = '((lambda (x y . z) z) 3 4 5 6)'
|
@@ -205,9 +254,7 @@ SKEEM
|
|
205
254
|
expect(result.length).to eq(2)
|
206
255
|
expect(result.head.value).to eq(5)
|
207
256
|
expect(result.last.value).to eq(6)
|
208
|
-
end
|
209
|
-
|
210
|
-
|
257
|
+
end
|
211
258
|
=begin
|
212
259
|
it 'should implement the compact define + lambda syntax' do
|
213
260
|
source = <<-SKEEM
|
@@ -349,7 +396,7 @@ SKEEM
|
|
349
396
|
expect(result.value).to eq(expectation)
|
350
397
|
end
|
351
398
|
end
|
352
|
-
|
399
|
+
|
353
400
|
it 'should implement the list procedure' do
|
354
401
|
checks = [
|
355
402
|
['(list)', []],
|
@@ -360,7 +407,7 @@ SKEEM
|
|
360
407
|
result = subject.run(skeem_expr)
|
361
408
|
expect(result.members.map(&:value)).to eq(expectation)
|
362
409
|
end
|
363
|
-
end
|
410
|
+
end
|
364
411
|
end # context
|
365
412
|
end # describe
|
366
413
|
end # module
|
@@ -285,6 +285,57 @@ module Skeem
|
|
285
285
|
expect(result.value).to eq(expectation)
|
286
286
|
end
|
287
287
|
end
|
288
|
+
end # context
|
289
|
+
|
290
|
+
context 'Vector procedures:' do
|
291
|
+
it 'should implement the vector? procedure' do
|
292
|
+
checks = [
|
293
|
+
['(vector? #f)', false],
|
294
|
+
['(vector? 1)', false],
|
295
|
+
['(vector? "bar")', false],
|
296
|
+
['(vector? (list 1 2 3))', false],
|
297
|
+
['(vector? #(1 #f "cool"))', true]
|
298
|
+
]
|
299
|
+
checks.each do |(skeem_expr, expectation)|
|
300
|
+
result = subject.run(skeem_expr)
|
301
|
+
expect(result.value).to eq(expectation)
|
302
|
+
end
|
303
|
+
end
|
304
|
+
|
305
|
+
it 'should implement the vector procedure' do
|
306
|
+
source = '(vector)'
|
307
|
+
result = subject.run(source)
|
308
|
+
expect(result).to be_kind_of(SkmVector)
|
309
|
+
expect(result).to be_empty
|
310
|
+
|
311
|
+
source = '(vector 1 2 3)'
|
312
|
+
result = subject.run(source)
|
313
|
+
expect(result).to be_kind_of(SkmVector)
|
314
|
+
expect(result.elements.map(&:value)).to eq([1, 2, 3])
|
315
|
+
end
|
316
|
+
|
317
|
+
it 'should implement the vector-length procedure' do
|
318
|
+
checks = [
|
319
|
+
['(vector-length (vector))', 0],
|
320
|
+
['(vector-length #())', 0],
|
321
|
+
['(vector-length (vector 1))', 1],
|
322
|
+
['(vector-length #(1))', 1],
|
323
|
+
['(vector-length (vector 1 2))', 2],
|
324
|
+
['(vector-length #(1 2))', 2],
|
325
|
+
['(vector-length (vector 1 2 3))', 3]
|
326
|
+
]
|
327
|
+
checks.each do |(skeem_expr, expectation)|
|
328
|
+
result = subject.run(skeem_expr)
|
329
|
+
expect(result.value).to eq(expectation)
|
330
|
+
end
|
331
|
+
end
|
332
|
+
|
333
|
+
# it 'should implement the vector-ref procedure' do
|
334
|
+
# source = "(vector-ref '#(1 1 2 3 5 8 13 21) 5)"
|
335
|
+
# result = subject.run(source)
|
336
|
+
# expect(result).to be_kind_of(SkmInteger)
|
337
|
+
# expect(result.value).to eq(8)
|
338
|
+
# end
|
288
339
|
end # context
|
289
340
|
|
290
341
|
context 'IO procedures:' do
|
@@ -144,6 +144,26 @@ module Skeem
|
|
144
144
|
end
|
145
145
|
end # context
|
146
146
|
|
147
|
+
context 'Vector recognition' do
|
148
|
+
it 'should tokenize vectors' do
|
149
|
+
input = '#(0 -2 "Sue")'
|
150
|
+
subject.reinitialize(input)
|
151
|
+
predictions = [
|
152
|
+
['VECTOR_BEGIN', '#(', 1],
|
153
|
+
['INTEGER', 0, 3],
|
154
|
+
['INTEGER', -2, 5],
|
155
|
+
['STRING_LIT', 'Sue', 8],
|
156
|
+
['RPAREN', ')', 13],
|
157
|
+
]
|
158
|
+
tokens = subject.tokens
|
159
|
+
predictions.each_with_index do |(pr_terminal, pr_lexeme, pr_position), i|
|
160
|
+
expect(tokens[i].terminal).to eq(pr_terminal)
|
161
|
+
expect(tokens[i].lexeme).to eq(pr_lexeme)
|
162
|
+
expect(tokens[i].position.column).to eq(pr_position)
|
163
|
+
end
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
147
167
|
context 'Semi-colon comments:' do
|
148
168
|
it 'should skip heading comments' do
|
149
169
|
input = "; Starting comment\n \"Some text\""
|
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.20
|
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-10-
|
11
|
+
date: 2018-10-20 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rley
|