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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 9cb4724d17594667921a2ccd39d5c39891edf55b
4
- data.tar.gz: e5ac55b88a2c71fb84e5752881e11c36546e5c98
3
+ metadata.gz: 91492f1d990da9deecf2146b21c13a5721cbf327
4
+ data.tar.gz: 06355e700c51894615aea3c34c7f26794a2482b4
5
5
  SHA512:
6
- metadata.gz: f70733db86074f7828e19bfb069f957372bfc10e95d2fa0d85484de418b147d6392ff357f533977d6d10db4fa08a29372f916274dd2d192fa6bdd8887561f608
7
- data.tar.gz: 5ed2a9db63d57adb2d93cb1c99d4b5d40a452f5174531d3c06522c80c6f1914c34fd4e97ca02059e3c733a227d3dfb22c9eeba363b01dadb7d75786ad90f3c48
6
+ metadata.gz: eb0d9f83f9816fbc881fef46b75e9587111539e3f4254772478e3e05bcb1c4fcd26d6dc16d83053f41c2fab55fe96e1d5d4cc1b02a7fc120218d09dcaa000088
7
+ data.tar.gz: cad77c7dab37087ff75202d2429d5213dd1ccf04e04d6a45993f41f9dc7ef587b897e2c677e10f1eb3d800c46f47a742f2378d95b2c020edb0f2d2da34d5171f
@@ -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
- ### Literals
113
- * Boolean literals: `#t`, `#true`, `#f` and `#false`
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.
@@ -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
- # add_terminals('APOSTROPHE', 'BACKQUOTE')
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
- create_number?(aRuntime)
60
- create_real?(aRuntime)
61
- create_integer?(aRuntime)
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
- create_boolean?(aRuntime)
67
+ create_object_predicate(aRuntime, 'boolean?')
67
68
  end
68
69
 
69
70
  def add_string_procedures(aRuntime)
70
- create_string?(aRuntime)
71
+ create_object_predicate(aRuntime, 'string?')
71
72
  end
72
73
 
73
74
  def add_symbol_procedures(aRuntime)
74
- create_symbol?(aRuntime)
75
+ create_object_predicate(aRuntime, 'symbol?')
75
76
  end
76
-
77
+
77
78
  def add_list_procedures(aRuntime)
78
- create_list?(aRuntime)
79
- create_null?(aRuntime)
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 create_boolean?(aRuntime)
282
+ def create_length(aRuntime)
302
283
  primitive = ->(runtime, arg) do
303
284
  arg_evaluated = arg.evaluate(runtime)
304
- to_skm(arg_evaluated.boolean?)
285
+ check_argtype(arg_evaluated, SkmList, 'list', 'length')
286
+ to_skm(arg_evaluated.length)
305
287
  end
306
288
 
307
- define_primitive_proc(aRuntime, 'boolean?', unary, primitive)
289
+ define_primitive_proc(aRuntime, 'length', unary, primitive)
308
290
  end
309
291
 
310
- def create_string?(aRuntime)
311
- primitive = ->(runtime, arg) do
312
- arg_evaluated = arg.evaluate(runtime)
313
- to_skm(arg_evaluated.string?)
314
- end
315
-
316
- define_primitive_proc(aRuntime, 'string?', unary, primitive)
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
- def create_symbol?(aRuntime)
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, 'symbol?', unary, primitive)
303
+ define_primitive_proc(aRuntime, 'vector', zero_or_more, primitive)
326
304
  end
327
-
328
- def create_list?(aRuntime)
305
+
306
+ def create_vector_length(aRuntime)
329
307
  primitive = ->(runtime, arg) do
330
308
  arg_evaluated = arg.evaluate(runtime)
331
- to_skm(arg_evaluated.list?)
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, 'list?', unary, primitive)
313
+ define_primitive_proc(aRuntime, 'vector-length', unary, primitive)
335
314
  end
336
315
 
337
- def create_null?(aRuntime)
338
- primitive = ->(runtime, arg) do
339
- arg_evaluated = arg.evaluate(runtime)
340
- to_skm(arg_evaluated.null?)
341
- end
342
-
343
- define_primitive_proc(aRuntime, 'null?', unary, primitive)
344
- end
345
-
346
- def create_length(aRuntime)
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, 'length', unary, primitive)
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
@@ -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], [])
@@ -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
@@ -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|[|()";]|$)/)) # Single char occurring alone
79
+ elsif (lexeme = scanner.scan(/(?:\.)(?=\s)/)) # Single char occurring alone
78
80
  token = build_token('PERIOD', lexeme)
79
- elsif (lexeme = scanner.scan(/#(?:t|f|true|false)(?=\s|[|()";]|$)/))
80
- token = build_token('BOOLEAN', lexeme)
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
@@ -1,3 +1,3 @@
1
1
  module Skeem
2
- VERSION = '0.0.19'.freeze
2
+ VERSION = '0.0.20'.freeze
3
3
  end
@@ -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
- checks = [
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.19
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-15 00:00:00.000000000 Z
11
+ date: 2018-10-20 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rley