skeem 0.2.05 → 0.2.06

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