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 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