skeem 0.2.05 → 0.2.06

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
  SHA256:
3
- metadata.gz: f8c3a61e9162fe0df33e51f373614d56cac6e5bc9c4743a101b8805bad158843
4
- data.tar.gz: 52feb4bc5edf9e9cf64089ef0e3fbb6cff2d917d964ecdf4d1e00d1d1409caed
3
+ metadata.gz: f14377aa793138a9eb14281b8f72ea29a8008da684b5d462c0060f635b2b5a9b
4
+ data.tar.gz: 2e37d741b3a156c4dabf20138cc604db4e9fb59dd78e2a8b518ad59566c2ecb5
5
5
  SHA512:
6
- metadata.gz: 1e99664327739a23130244b2cff9b81e9ad9be83773efc057cdb636f8642a3a1492a8de5ed0133857582c036228f27c22c5bf3e17949bd577f162b679c7aa090
7
- data.tar.gz: 9014bdfed6b945a604ab55f940de00c9288773a0dd9a192825b5cd83e99d79c6434252e96289abbfe10b7f742ffa117034f767685c929b5dbefcb2d27e7c36d3
6
+ metadata.gz: 45a19a92c4c3e9b7033edebb197af870b20cd139db4246d14c6e9d9520105f6057435a60bdef2ad39466fa86b5179404cd19b79a604f70ea7d30abfc6ea80803
7
+ data.tar.gz: 5ecb615aef9290a912919272c6f47f372b2b9b990a3867f66205fcc2cf21005c2b0d7cfc38479dad9137c142969d36b7b856ded9cd297a426a0c6ba1f8ea0e64
data/CHANGELOG.md CHANGED
@@ -1,3 +1,22 @@
1
+ ## [0.2.06] - 2019-05-30
2
+ - NEW Special `cond` (= condional) form implemented. Supports `else` alternative and arrow (=>) syntax.
3
+ - FIX Corner case in procedure `append`.
4
+
5
+ ### Added
6
+ - Class `SkmConditional`. Internal representation of `cond`forms.
7
+
8
+ ### Changed
9
+ - Class `Skeem::Tokenizer`. Added keywords `cond`, `else` and `=>` separator.
10
+ - Method `Tokenizer#_next_token` updated to accept new keywords and arrows `=>`
11
+ - File `grammar.rb`: Added new terminals and new production rules for parsing the `cond` form
12
+ - File `s_expr_builder.rb`: Added new methods for building parse tree of `cond` forms
13
+ - File `interpreter_spec.rb`: Added tests for `cond`form.
14
+ - File `README.md` Updated for `cond` form. Added fifth example illustrating the `cond` form.
15
+
16
+ ### Fixed
17
+ - Method `Primitive#create_append`: test case (append '() 'a)) failed to return a (as identifier)
18
+
19
+
1
20
  ## [0.2.05] - 2019-05-26
2
21
  - Passing more standard Scheme tests, `append` procedure implemented.
3
22
 
data/README.md CHANGED
@@ -80,7 +80,7 @@ Here are a few pointers for the Scheme programming language:
80
80
 
81
81
  scheme_code =<<-SKEEM
82
82
  ; Let's implement the 'min' function
83
- (define min (lambda(x y) (if (< x y) x y)))
83
+ (define min (lambda (x y) (if (< x y) x y)))
84
84
 
85
85
  ; What is the minimum of 2 and 3?
86
86
  (min 2 3)
@@ -142,6 +142,30 @@ Here are a few pointers for the Scheme programming language:
142
142
  puts result.last.value # => 3
143
143
  ```
144
144
 
145
+ ### Example 5 (Conditional branching)
146
+
147
+ ```ruby
148
+ require 'skeem'
149
+
150
+ schemer = Skeem::Interpreter.new
151
+
152
+ scheme_code =<<-SKEEM
153
+ ; Let's implement the 'signum' function
154
+ (define signum (lambda (x)
155
+ (cond
156
+ ((positive? x) 1)
157
+ ((zero? x) 0)
158
+ (else -1))))
159
+
160
+ (signum -3)
161
+ SKEEM
162
+
163
+ # Ask Ruby to execute Scheme code
164
+ result = schemer.run(scheme_code)
165
+ puts result.value # => -1
166
+ ```
167
+
168
+
145
169
  ## Currently implemented R7RS features
146
170
  ### Data type literals
147
171
  - Booleans: `#t`, `#true`, `#f`, `#false`
@@ -160,7 +184,7 @@ Here are a few pointers for the Scheme programming language:
160
184
  - Variable references
161
185
  - Procedure calls
162
186
  - Lambda expressions
163
- - If conditionals
187
+ - Conditionals (if, cond)
164
188
  - Definitions
165
189
  - Assignments
166
190
 
@@ -195,6 +219,12 @@ __Syntax:__
195
219
  * (set! <identifier\> <expression\>)
196
220
 
197
221
  ### Derived expressions
222
+ #### cond
223
+ __Purpose:__ Define one or more branchings.
224
+ __Syntax:__
225
+ * (cond (<test\> <consequent\>)\+)
226
+ * (cond (<test\><consequent\>)* (else <alternate\>))
227
+
198
228
  #### let
199
229
  __Purpose:__ Define one or more variable local to the block.
200
230
  __Syntax:__
data/lib/skeem/grammar.rb CHANGED
@@ -11,7 +11,7 @@ module Skeem
11
11
  # Delimiters, separators...
12
12
  add_terminals('APOSTROPHE', 'COMMA', 'COMMA_AT_SIGN')
13
13
  add_terminals('GRAVE_ACCENT', 'LPAREN', 'RPAREN')
14
- add_terminals('PERIOD')
14
+ add_terminals('PERIOD', 'ARROW')
15
15
  add_terminals('VECTOR_BEGIN')
16
16
 
17
17
  # Literal values...
@@ -19,8 +19,8 @@ module Skeem
19
19
  add_terminals('STRING_LIT', 'IDENTIFIER')
20
20
 
21
21
  # Keywords...
22
- add_terminals('BEGIN', 'DEFINE', 'IF', 'LAMBDA')
23
- add_terminals('LET', 'LET*')
22
+ add_terminals('BEGIN', 'COND', 'DEFINE', 'ELSE')
23
+ add_terminals('IF', 'LAMBDA', 'LET', 'LET*')
24
24
  add_terminals('QUOTE', 'QUASIQUOTE', 'SET!')
25
25
  add_terminals('UNQUOTE', 'UNQUOTE-SPLICING')
26
26
 
@@ -95,6 +95,8 @@ module Skeem
95
95
  rule 'number' => 'INTEGER'
96
96
  rule 'number' => 'REAL'
97
97
  rule('assignment' => 'LPAREN SET! IDENTIFIER expression RPAREN').as 'assignment'
98
+ rule('derived_expression' => 'LPAREN COND cond_clause_plus RPAREN').as 'cond_form'
99
+ rule('derived_expression' => 'LPAREN COND cond_clause_star LPAREN ELSE sequence RPAREN RPAREN').as 'cond_else_form'
98
100
  rule('derived_expression' => 'LPAREN LET LPAREN binding_spec_star RPAREN body RPAREN').as 'short_let_form'
99
101
  rule('derived_expression' => 'LPAREN LET* LPAREN binding_spec_star RPAREN body RPAREN').as 'let_star_form'
100
102
 
@@ -102,6 +104,14 @@ module Skeem
102
104
  # the next rule was made more general than its standard counterpart
103
105
  rule('derived_expression' => 'LPAREN BEGIN body RPAREN').as 'begin_expression'
104
106
  rule 'derived_expression' => 'quasiquotation'
107
+ rule('cond_clause_plus' => 'cond_clause_plus cond_clause').as 'multiple_cond_clauses'
108
+ rule('cond_clause_plus' => 'cond_clause').as 'last_cond_clauses'
109
+ rule('cond_clause_star' => 'cond_clause_star cond_clause').as 'cond_clauses_star'
110
+ rule('cond_clause_star' => []).as 'last_cond_clauses_star'
111
+ rule('cond_clause' => 'LPAREN test sequence RPAREN').as 'cond_clause'
112
+ rule('cond_clause' => 'LPAREN test RPAREN')
113
+ rule('cond_clause' => 'LPAREN test ARROW recipient RPAREN').as 'cond_arrow_clause'
114
+ rule('recipient' => 'expression')
105
115
  rule('quasiquotation' => 'LPAREN QUASIQUOTE qq_template RPAREN').as 'quasiquotation'
106
116
  rule('quasiquotation' => 'GRAVE_ACCENT qq_template').as 'quasiquotation_short'
107
117
  rule('binding_spec_star' => 'binding_spec_star binding_spec').as 'multiple_binding_specs'
@@ -514,7 +514,11 @@ module Skeem
514
514
  when SkmEmptyList
515
515
  # Do nothing
516
516
  else
517
- result.append(arg)
517
+ if result.kind_of?(SkmEmptyList)
518
+ result = arg
519
+ else
520
+ result.append(arg)
521
+ end
518
522
  end
519
523
  end
520
524
  end
@@ -56,13 +56,13 @@ module Skeem
56
56
 
57
57
  # rule('cmd_or_def_plus' => 'cmd_or_def').as 'last_cmd_def'
58
58
  def reduce_last_cmd_def(_production, _range, _tokens, theChildren)
59
- SkmPair.create_from_a([theChildren.last])
59
+ SkmPair.new(theChildren.last, SkmEmptyList.instance)
60
60
  end
61
-
61
+
62
62
  # rule('cmd_or_def' => 'LPAREN BEGIN cmd_or_def_plus RPAREN').as 'begin_cmd'
63
63
  def reduce_begin_cmd(_production, _range, _tokens, theChildren)
64
- SkmSequencingBlock.new(theChildren[2])
65
- end
64
+ SkmSequencingBlock.new(theChildren[2])
65
+ end
66
66
 
67
67
  # rule('definition' => 'LPAREN DEFINE IDENTIFIER expression RPAREN')
68
68
  # .as 'definition'
@@ -77,10 +77,10 @@ module Skeem
77
77
  # $stderr.puts lmbd.inspect
78
78
  SkmBinding.new(theChildren[3], lmbd)
79
79
  end
80
-
80
+
81
81
  # rule('definition' => 'LPAREN BEGIN definition_star RPAREN').as 'definitions_within_begin'
82
82
  def reduce_definitions_within_begin(_production, aRange, _tokens, theChildren)
83
- SkmSequencingBlock.new(SkmPair.create_from_a(theChildren[2]))
83
+ SkmSequencingBlock.new(SkmPair.create_from_a(theChildren[2]))
84
84
  end
85
85
 
86
86
  # rule('expression' => 'IDENTIFIER').as 'variable_reference'
@@ -236,22 +236,21 @@ module Skeem
236
236
  definitions = theChildren[0].nil? ? [] : theChildren[0]
237
237
  { defs: definitions, sequence: theChildren[1] }
238
238
  end
239
-
239
+
240
240
  # rule('definition_star' => 'definition_star definition').as 'definition_star'
241
241
  def reduce_definition_star(_production, _range, _tokens, theChildren)
242
242
  theChildren[0] << theChildren[1]
243
243
  end
244
-
245
-
246
- # rule('definition_star' => []).as 'no_definition_yet'
244
+
245
+
246
+ # rule('definition_star' => []).as 'no_definition_yet'
247
247
  def reduce_no_definition_yet(_production, _range, _tokens, theChildren)
248
248
  []
249
- end
249
+ end
250
250
 
251
251
  # rule('sequence' => 'command_star expression').as 'sequence'
252
252
  def reduce_sequence(_production, _range, _tokens, theChildren)
253
253
  SkmPair.create_from_a(theChildren[0] << theChildren[1])
254
-
255
254
  end
256
255
 
257
256
  # rule('command_star' => 'command_star command').as 'multiple_commands'
@@ -274,20 +273,60 @@ module Skeem
274
273
  SkmUpdateBinding.new(theChildren[2], theChildren[3])
275
274
  end
276
275
 
276
+ # rule('derived_expression' => 'LPAREN COND cond_clause_plus RPAREN').as 'cond_form'
277
+ def reduce_cond_form(_production, aRange, _tokens, theChildren)
278
+ SkmConditional.new(aRange.low, theChildren[2], nil)
279
+ end
280
+
281
+ # rule('derived_expression' => 'LPAREN COND cond_clause_star LPAREN ELSE sequence RPAREN RPAREN').as 'cond_else_form'
282
+ def reduce_cond_else_form(_production, aRange, _tokens, theChildren)
283
+ SkmConditional.new(aRange.low, theChildren[2], SkmSequencingBlock.new(theChildren[5]))
284
+ end
285
+
277
286
  # rule('derived_expression' => 'LPAREN LET LPAREN binding_spec_star RPAREN body RPAREN').as 'short_let_form'
278
287
  def reduce_short_let_form(_production, aRange, _tokens, theChildren)
279
288
  SkmBindingBlock.new(:let, theChildren[3], theChildren[5])
280
289
  end
281
-
290
+
282
291
  # rule('derived_expression' => 'LPAREN LET* LPAREN binding_spec_star RPAREN body RPAREN').as 'let_star_form'
283
- def reduce_let_star_form(_production, aRange, _tokens, theChildren)
292
+ def reduce_let_star_form(_production, _range, _tokens, theChildren)
284
293
  SkmBindingBlock.new(:let_star, theChildren[3], theChildren[5])
285
294
  end
286
295
 
287
296
  # rule('derived_expression' => 'LPAREN BEGIN body RPAREN').as 'begin_expression'
288
- def reduce_begin_expression(_production, aRange, _tokens, theChildren)
289
- SkmSequencingBlock.new(theChildren[2])
290
- end
297
+ def reduce_begin_expression(_production, _range, _tokens, theChildren)
298
+ SkmSequencingBlock.new(theChildren[2])
299
+ end
300
+
301
+ # rule('cond_clause_plus' => 'cond_clause_plus cond_clause').as 'multiple_cond_clauses'
302
+ def reduce_multiple_cond_clauses(_production, _range, _tokens, theChildren)
303
+ theChildren[0] << theChildren[1]
304
+ end
305
+
306
+ # rule('cond_clause_plus' => 'cond_clause').as 'last_cond_clauses'
307
+ def reduce_last_cond_clauses(_production, _range, _tokens, theChildren)
308
+ [theChildren[0]]
309
+ end
310
+
311
+ # rule('cond_clause_star' => 'cond_clause_star cond_clause').as 'cond_clauses_star'
312
+ def reduce_cond_clauses_star(_production, _range, _tokens, theChildren)
313
+ theChildren[0] << theChildren[1]
314
+ end
315
+
316
+ # rule('cond_clause_star' => []).as 'last_cond_clauses_star'
317
+ def reduce_last_cond_clauses_star(_production, _range, _tokens, _children)
318
+ []
319
+ end
320
+
321
+ # rule('cond_clause' => 'LPAREN test sequence RPAREN').as 'cond_clause'
322
+ def reduce_cond_clause(_production, _range, _tokens, theChildren)
323
+ [theChildren[1], SkmSequencingBlock.new(SkmPair.create_from_a(theChildren[2]))]
324
+ end
325
+
326
+ # rule('cond_clause' => 'LPAREN test ARROW recipient RPAREN').as 'cond_arrow_clause'
327
+ def reduce_cond_arrow_clause(_production, _range, _tokens, theChildren)
328
+ [theChildren[1], theChildren[3]]
329
+ end
291
330
 
292
331
  # rule('quasiquotation' => 'LPAREN QUASIQUOTE qq_template RPAREN').as 'quasiquotation'
293
332
  def reduce_quasiquotation(_production, aRange, _tokens, theChildren)
@@ -233,6 +233,60 @@ module Skeem
233
233
  end
234
234
  end # class
235
235
 
236
+
237
+ class SkmConditional < SkmMultiExpression
238
+ # An array of couples [test, sequence]
239
+ attr_reader :clauses
240
+ attr_reader :alternate
241
+
242
+ def initialize(aPosition, theClauses, anAlternate)
243
+ super(aPosition)
244
+ @clauses = theClauses
245
+ @alternate = anAlternate
246
+ end
247
+
248
+ def evaluate(aRuntime)
249
+ clause_matching = false
250
+ result = nil
251
+
252
+ clauses.each do |(test, consequent)|
253
+ test_result = test.evaluate(aRuntime)
254
+ next if test_result.boolean? && test_result.value == false
255
+
256
+ clause_matching = true
257
+ result = consequent.evaluate(aRuntime)
258
+ break
259
+ end
260
+
261
+ unless clause_matching
262
+ result = alternate ? alternate.evaluate(aRuntime) : SkmUndefined.instance
263
+ end
264
+
265
+ result
266
+ end
267
+
268
+ def quasiquote(aRuntime)
269
+ quasi_clauses = clauses.map do |(test, consequent)|
270
+ test_qq = test.quasiquote(aRuntime)
271
+ consequent_qq = consequent.quasiquote(aRuntime)
272
+ [test_qq, consequent_qq]
273
+ end
274
+ quasi_alternate = alternate ? alternate.quasiquote(aRuntime) : nil
275
+
276
+ self.class.new(position, quasi_clauses, quasi_alternate)
277
+ end
278
+
279
+ def inspect
280
+ result = inspect_prefix + '@test ' + test.inspect + ', '
281
+ result << "@clauses \n"
282
+ clauses.each do |(test, consequent)|
283
+ result << ' ' << test.inspect << ' ' << consequent.inspect << "\n"
284
+ end
285
+ result << '@alternate ' + alternate.inspect + inspect_suffix
286
+ result
287
+ end
288
+ end # class
289
+
236
290
  SkmArity = Struct.new(:low, :high) do
237
291
  def nullary?
238
292
  low.zero? && high == 0
@@ -466,8 +520,8 @@ require_relative 'skm_procedure_exec'
466
520
  extend Forwardable
467
521
 
468
522
  attr_reader :representation
469
- attr_reader :environment
470
-
523
+ attr_reader :environment
524
+
471
525
  def_delegators(:@representation, :formals, :definitions, :sequence)
472
526
 
473
527
  def initialize(aRepresentation, aRuntime)
@@ -20,6 +20,7 @@ module Skeem
20
20
 
21
21
  @@lexeme2name = {
22
22
  "'" => 'APOSTROPHE',
23
+ '=>' => 'ARROW',
23
24
  '`' => 'GRAVE_ACCENT',
24
25
  '(' => 'LPAREN',
25
26
  ')' => 'RPAREN',
@@ -32,7 +33,9 @@ module Skeem
32
33
  # Here are all the implemented Scheme keywords (in uppercase)
33
34
  @@keywords = %w[
34
35
  BEGIN
36
+ COND
35
37
  DEFINE
38
+ ELSE
36
39
  IF
37
40
  LAMBDA
38
41
  LET
@@ -86,7 +89,7 @@ module Skeem
86
89
  token = build_token(@@lexeme2name[curr_ch], scanner.getch)
87
90
  elsif (lexeme = scanner.scan(/(?:\.)(?=\s)/)) # Single char occurring alone
88
91
  token = build_token('PERIOD', lexeme)
89
- elsif (lexeme = scanner.scan(/,@?/))
92
+ elsif (lexeme = scanner.scan(/(?:,@?)|(?:=>)/))
90
93
  token = build_token(@@lexeme2name[lexeme], lexeme)
91
94
  elsif (lexeme = scanner.scan(/#(?:(?:true)|(?:false)|(?:u8)|[\\\(tfeiodx]|(?:\d+[=#]))/))
92
95
  token = cardinal_token(lexeme)
data/lib/skeem/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Skeem
2
- VERSION = '0.2.05'.freeze
2
+ VERSION = '0.2.06'.freeze
3
3
  end
@@ -156,6 +156,66 @@ SKEEM
156
156
  result = subject.run(source)
157
157
  expect(result).to eq(1)
158
158
  end
159
+
160
+ it 'should implement the cond form' do
161
+ source = <<-SKEEM
162
+ (define signum (lambda (x)
163
+ (cond
164
+ ((> x 0) 1)
165
+ ((= x 0) 0)
166
+ ((< x 0) -1)
167
+ )))
168
+ SKEEM
169
+ result = subject.run(source)
170
+ result = subject.run('(signum 3)')
171
+ expect(result).to eq(1)
172
+
173
+ result = subject.run('(signum 0)')
174
+ expect(result).to eq(0)
175
+
176
+ result = subject.run('(signum -3)')
177
+ expect(result).to eq(-1)
178
+ end
179
+
180
+ it 'should implement the cond form with arrows' do
181
+ source = <<-SKEEM
182
+ (define signum (lambda (x)
183
+ (cond
184
+ ((> x 0) => 1)
185
+ ((= x 0) => 0)
186
+ ((< x 0) => -1)
187
+ )))
188
+ SKEEM
189
+ result = subject.run(source)
190
+ result = subject.run('(signum 3)')
191
+ expect(result).to eq(1)
192
+
193
+ result = subject.run('(signum 0)')
194
+ expect(result).to eq(0)
195
+
196
+ result = subject.run('(signum -3)')
197
+ expect(result).to eq(-1)
198
+ end
199
+
200
+ it 'should implement the cond ... else form' do
201
+ source = <<-SKEEM
202
+ (define signum (lambda (x)
203
+ (cond
204
+ ((> x 0) 1)
205
+ ((= x 0) 0)
206
+ (else -1)
207
+ )))
208
+ SKEEM
209
+ result = subject.run(source)
210
+ result = subject.run('(signum 3)')
211
+ expect(result).to eq(1)
212
+
213
+ result = subject.run('(signum 0)')
214
+ expect(result).to eq(0)
215
+
216
+ result = subject.run('(signum -3)')
217
+ expect(result).to eq(-1)
218
+ end
159
219
 
160
220
  it 'should implement the quotation of constant literals' do
161
221
  checks = [
@@ -718,7 +778,8 @@ SKEEM
718
778
  checks = [
719
779
  ['(list)', []],
720
780
  ['(list 1)', [1]],
721
- ['(list 1 2 3 4)', [1, 2, 3, 4]]
781
+ ['(list 1 2 3 4)', [1, 2, 3, 4]],
782
+ ["(list 'a (+ 3 4) 'c)", [identifier('a'), 7, identifier('c')]]
722
783
  ]
723
784
  checks.each do |(skeem_expr, expectation)|
724
785
  result = subject.run(skeem_expr)
@@ -558,8 +558,10 @@ SKEEM
558
558
  ["(length '())", 0],
559
559
  ["(length '(1))", 1],
560
560
  ["(length '(1 2))", 2],
561
- ["(length '(1 2 3))", 3]
561
+ ["(length '(1 2 3))", 3],
562
+ ["(length '(a (b) (c d e)))", 3]
562
563
  ]
564
+
563
565
  checks.each do |(skeem_expr, expectation)|
564
566
  result = subject.run(skeem_expr)
565
567
  expect(result).to eq(expectation)
@@ -580,11 +582,16 @@ SKEEM
580
582
  ["(append '(a b) '(c) 'd)", array2list_ids(['a', 'b', 'c', 'd'])],
581
583
  ["(append '(a (b)) '((c)))", [SkmIdentifier.create('a'),
582
584
  SkmPair.create_from_a(array2list_ids(['b'])),
583
- SkmPair.create_from_a(array2list_ids(['c']))]]
585
+ SkmPair.create_from_a(array2list_ids(['c']))]],
586
+ [ "(append '() 'a)", SkmIdentifier.create('a')]
584
587
  ]
585
588
  checks.each do |(skeem_expr, expectation)|
586
589
  result = subject.run(skeem_expr)
587
- expect(result.to_a).to eq(expectation)
590
+ if result.kind_of?(SkmPair)
591
+ expect(result.to_a).to eq(expectation)
592
+ else
593
+ expect(result).to eq(expectation)
594
+ end
588
595
  end
589
596
  end
590
597
 
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.2.05
4
+ version: 0.2.06
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dimitri Geshef
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-05-26 00:00:00.000000000 Z
11
+ date: 2019-05-30 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rley