skeem 0.0.19 → 0.0.20
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
[![Linux Build Status](https://travis-ci.org/famished-tiger/Skeem.svg?branch=master)](https://travis-ci.org/famished-tiger/Skeem)
|
3
|
+
[![Build status](https://ci.appveyor.com/api/projects/status/qs19wn6o6bpo8lm6?svg=true)](https://ci.appveyor.com/project/famished-tiger/skeem)
|
3
4
|
[![Gem Version](https://badge.fury.io/rb/skeem.svg)](https://badge.fury.io/rb/skeem)
|
4
5
|
[![License](https://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat)](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
|