skeem 0.2.02 → 0.2.03

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: ff26a0c0a06cb583052e17627fd7c620e26dc5c595613f1f6d5716a368c154b7
4
- data.tar.gz: e7697be919dc2624c449ff001524d426ff914b2e09f8bc69f581599bd3961148
3
+ metadata.gz: 6d5dc31ad0db5984772c42897c9ce30f51b5d97b44e7c1b9e65a277f4294c767
4
+ data.tar.gz: cdb6ad6e66f703a65327fee0523be67a898215d2efa0e91371ec5b75b5262daa
5
5
  SHA512:
6
- metadata.gz: 3a2ec84eec62e1bd007b480f89a843af69c023259dcfd37ae5808c8f7266f904ac0a6ab6ee8eb4f1877d3fbe16735c39c281c18ca16ee27c09307ae48f677287
7
- data.tar.gz: e58e2a58401d9b8d6c17c9394029a4e2438917682e767e0541262c98ff4c0011dd8fd6b2fe998c019c96c7ce0a6007d2697d5987271858fba7c80d053242ba41
6
+ metadata.gz: ca03f6c15d90bbebae93cea809aaadd0dae35099bdcf6f7562182963e4c86c703d82a1955b3dde8ccd31369f85072a5e7a38cecaf8b8f4494775f3cf28c71611
7
+ data.tar.gz: 8167282d129ecf2e18563b5d6cceb1de387dab5ed4f8e63fc56655f33fc593fc79eeb8a650c72e88520d790b69d967e369e03552b261ce28052c709072867bbe
data/CHANGELOG.md CHANGED
@@ -1,3 +1,10 @@
1
+ ## [0.2.03] - 2019-04-26
2
+ ### Changed
3
+ - File `README.md` added new example with procedures holding each their local states.
4
+
5
+ ### Fixed
6
+ - The nesting of a lambda expression nested in a let construct caused an aliasing of the bindings.
7
+
1
8
  ## [0.2.02] - 2019-04-20
2
9
  ### Added
3
10
  - Skeem now supports 'let*' local binding construct
@@ -23,7 +30,7 @@
23
30
 
24
31
  ### Changed
25
32
  - File `.travis.yml` Added newer Ruby versions and more environments in "Allowed failures" because of Bundler issue.
26
- - File `README.md` udpated to reflect currently implemented features.
33
+ - File `README.md` updated to reflect currently implemented features.
27
34
  - Class `SkmUndefined` uses now the Singleton pattern.
28
35
 
29
36
  ### Fixed
data/README.md CHANGED
@@ -108,6 +108,37 @@ Here are a few pointers for the Scheme programming language:
108
108
  result = schemer.run(scheme_code)
109
109
  puts result.value # => 9332621544394415268169923885626670049071596826438162146859296389521759999322991560894146397615651828625369792082722375825118521091686400000000000000000000000
110
110
  ```
111
+ ### Example 4 (Defining a procedure that holds its own environment)
112
+ ```ruby
113
+ require 'skeem'
114
+
115
+ schemer = Skeem::Interpreter.new
116
+ scheme_code = <<-SKEEM
117
+ (define make-counter
118
+ ;; create a procedure that will bind count and
119
+ ;; return a new procedure that will iself increment the
120
+ ;; variable and return its newest value
121
+ (lambda ()
122
+ (let ((count 0))
123
+ (lambda ()
124
+ (set! count (+ count 1))
125
+ count))))
126
+
127
+ (define c1 (make-counter))
128
+ (define c2 (make-counter))
129
+ (define c3 (make-counter))
130
+
131
+ ;; Notice how each procedure keeps track of its "own" counter.
132
+ (c1) ; => 1
133
+ (c2) ; => 1
134
+ (c1) ; => 2
135
+ (c3) ; => 1
136
+ (c1) ; => 3
137
+ SKEEM
138
+
139
+ result = schemer.run(scheme_code)
140
+ puts result.last.value # => 3
141
+ ```
111
142
 
112
143
  ## Currently implemented R7RS features
113
144
  ### Data type literals
data/lib/skeem/grammar.rb CHANGED
@@ -19,8 +19,8 @@ module Skeem
19
19
  add_terminals('STRING_LIT', 'IDENTIFIER')
20
20
 
21
21
  # Keywords...
22
- add_terminals('DEFINE', 'IF', 'LAMBDA', 'LET')
23
- add_terminals('LET*')
22
+ add_terminals('BEGIN', 'DEFINE', 'IF', 'LAMBDA')
23
+ add_terminals('LET', 'LET*')
24
24
  add_terminals('QUOTE', 'QUASIQUOTE', 'SET!')
25
25
  add_terminals('UNQUOTE', 'UNQUOTE-SPLICING')
26
26
 
@@ -29,9 +29,11 @@ module Skeem
29
29
  rule('cmd_or_def_plus' => 'cmd_or_def').as 'last_cmd_def'
30
30
  rule 'cmd_or_def' => 'command'
31
31
  rule 'cmd_or_def' => 'definition'
32
+ # TODO: add BEGIN here p. 64 R7RS
32
33
  rule 'command' => 'expression'
33
34
  rule('definition' => 'LPAREN DEFINE IDENTIFIER expression RPAREN').as 'definition'
34
35
  rule('definition' => 'LPAREN DEFINE LPAREN IDENTIFIER def_formals RPAREN body RPAREN').as 'alt_definition'
36
+ rule('definition' => 'LPAREN BEGIN definition_star RPAREN').as 'definitions_within_begin'
35
37
  rule('expression' => 'IDENTIFIER').as 'variable_reference'
36
38
  rule 'expression' => 'literal'
37
39
  rule 'expression' => 'procedure_call'
@@ -80,8 +82,8 @@ module Skeem
80
82
  rule('identifier_plus' => 'identifier_plus IDENTIFIER').as 'multiple_identifiers'
81
83
  rule('identifier_plus' => 'IDENTIFIER').as 'last_identifier'
82
84
  rule('body' => 'definition_star sequence').as 'body'
83
- rule 'definition_star' => 'definition_star definition'
84
- rule 'definition_star' => []
85
+ rule('definition_star' => 'definition_star definition').as 'definition_star'
86
+ rule('definition_star' => []).as 'no_definition_yet'
85
87
  rule('sequence' => 'command_star expression').as 'sequence'
86
88
  rule('command_star' => 'command_star command').as 'multiple_commands'
87
89
  rule('command_star' => []).as 'no_command_yet'
@@ -95,6 +97,7 @@ module Skeem
95
97
  rule('assignment' => 'LPAREN SET! IDENTIFIER expression RPAREN').as 'assignment'
96
98
  rule('derived_expression' => 'LPAREN LET LPAREN binding_spec_star RPAREN body RPAREN').as 'short_let_form'
97
99
  rule('derived_expression' => 'LPAREN LET* LPAREN binding_spec_star RPAREN body RPAREN').as 'let_star_form'
100
+ rule('derived_expression' => 'LPAREN BEGIN sequence RPAREN').as 'begin_expression'
98
101
  rule 'derived_expression' => 'quasiquotation'
99
102
  rule('quasiquotation' => 'LPAREN QUASIQUOTE qq_template RPAREN').as 'quasiquotation'
100
103
  rule('quasiquotation' => 'GRAVE_ACCENT qq_template').as 'quasiquotation_short'
@@ -116,9 +116,9 @@ module Skeem
116
116
  arguments << operands.take(arity.low).flatten
117
117
  count_delta = operands.size - arity.low
118
118
  arguments << SkmPair.create_from_a(operands.slice(-count_delta, count_delta))
119
- #p operands.size
120
- #p count_delta
121
- #p arguments.inspect
119
+ # p operands.size
120
+ # p count_delta
121
+ # p arguments.inspect
122
122
  result = code.send(:call, aRuntime, *arguments.flatten)
123
123
  end
124
124
  else # Fixed arity...
@@ -72,6 +72,11 @@ module Skeem
72
72
  # $stderr.puts lmbd.inspect
73
73
  SkmBinding.new(theChildren[3], lmbd)
74
74
  end
75
+
76
+ # rule('definition' => 'LPAREN BEGIN definition_star RPAREN').as 'definitions_within_begin'
77
+ def reduce_definitions_within_begin(_production, aRange, _tokens, theChildren)
78
+ SkmSequencingBlock.new(SkmPair.create_from_a(theChildren[2]))
79
+ end
75
80
 
76
81
  # rule('expression' => 'IDENTIFIER').as 'variable_reference'
77
82
  def reduce_variable_reference(_production, aRange, _tokens, theChildren)
@@ -226,6 +231,17 @@ module Skeem
226
231
  definitions = theChildren[0].nil? ? [] : theChildren[0]
227
232
  { defs: definitions, sequence: theChildren[1] }
228
233
  end
234
+
235
+ # rule('definition_star' => 'definition_star definition').as 'definition_star'
236
+ def reduce_definition_star(_production, _range, _tokens, theChildren)
237
+ theChildren[0] << theChildren[1]
238
+ end
239
+
240
+
241
+ # rule('definition_star' => []).as 'no_definition_yet'
242
+ def reduce_no_definition_yet(_production, _range, _tokens, theChildren)
243
+ []
244
+ end
229
245
 
230
246
  # rule('sequence' => 'command_star expression').as 'sequence'
231
247
  def reduce_sequence(_production, _range, _tokens, theChildren)
@@ -260,6 +276,11 @@ module Skeem
260
276
  # rule('derived_expression' => 'LPAREN LET* LPAREN binding_spec_star RPAREN body RPAREN').as 'let_star_form'
261
277
  def reduce_let_star_form(_production, aRange, _tokens, theChildren)
262
278
  SkmBindingBlock.new(:let_star, theChildren[3], theChildren[5])
279
+ end
280
+
281
+ # rule('derived_expression' => 'LPAREN BEGIN sequence RPAREN').as 'begin_expression'
282
+ def reduce_begin_expression(_production, aRange, _tokens, theChildren)
283
+ SkmSequencingBlock.new(theChildren[2])
263
284
  end
264
285
 
265
286
  # rule('quasiquotation' => 'LPAREN QUASIQUOTE qq_template RPAREN').as 'quasiquotation'
@@ -71,10 +71,10 @@ module Skeem
71
71
  # $stderr.puts "Parent environment #{aRuntime.environment.parent.object_id.to_s(16)}, "
72
72
  # $stderr.puts aRuntime.environment.inspect
73
73
  end
74
- #$stderr.puts ' operator: ' + operator.inspect
75
- #$stderr.puts ' original operands: ' + operands.inspect
74
+ # $stderr.puts ' operator: ' + operator.inspect
75
+ # $stderr.puts ' original operands: ' + operands.inspect
76
76
  actuals = transform_operands(aRuntime)
77
- #$stderr.puts ' transformed operands: ' + actuals.inspect
77
+ # $stderr.puts ' transformed operands: ' + actuals.inspect
78
78
  outcome, result = determine_callee(aRuntime)
79
79
  if outcome == :callee
80
80
  callee = result
@@ -464,6 +464,14 @@ require_relative 'skm_procedure_exec'
464
464
 
465
465
  result
466
466
  end
467
+
468
+ def doppelganger(aRuntime)
469
+ twin = self.dup
470
+ twin.set_cond_environment(aRuntime.environment.dup)
471
+ result = twin
472
+
473
+ result
474
+ end
467
475
 
468
476
  def set_cond_environment(theFrame)
469
477
  # $stderr.puts "Lambda #{object_id.to_s(16)}, env [#{environment.object_id.to_s(16)}]"
@@ -16,7 +16,14 @@ module Skeem
16
16
  @bindings = {}
17
17
  @parent = parentFrame
18
18
  end
19
-
19
+
20
+ def initialize_copy(original)
21
+ @bindings = {}
22
+ original.bindings.each_pair do |var, val|
23
+ add_binding(var.dup, val.dup)
24
+ end
25
+ end
26
+
20
27
  # Add a binding to this frame.
21
28
  # There is no check that the variable name is already in use.
22
29
  # @param anIdentifier [String, SkmIdentifier] The variable name
@@ -29,7 +36,7 @@ module Skeem
29
36
  # @param anIdentifier [String, SkmIdentifier] The variable name.
30
37
  # @param anExpression [SkmElement] The Skeem expression to bind
31
38
  def update_binding(anIdentifier, anExpression)
32
- variable = valid_identifier(anIdentifier)
39
+ variable = valid_identifier(anIdentifier)
33
40
  if bindings.include?(variable)
34
41
  bind(variable, anExpression)
35
42
  elsif parent
@@ -52,7 +59,7 @@ module Skeem
52
59
 
53
60
  # Tell the value of an existing variable.
54
61
  # @param anIdentifier [String, SkmIdentifier] The variable name.
55
- # @return [Boolean]
62
+ # @return [Boolean]
56
63
  def include?(anIdentifier)
57
64
  variable = valid_identifier(anIdentifier)
58
65
  my_result = bindings.include?(variable)
@@ -110,9 +117,9 @@ module Skeem
110
117
 
111
118
  result
112
119
  end
113
-
120
+
114
121
  private
115
-
122
+
116
123
  def valid_identifier(anIdentifier)
117
124
  case anIdentifier
118
125
  when String
@@ -125,13 +132,13 @@ module Skeem
125
132
  raise StandardError, err_msg
126
133
  end
127
134
  end
128
-
135
+
129
136
  def bind(anIdentifier, anExpression)
130
137
  @bindings[anIdentifier] = anExpression
131
-
138
+
132
139
  # Notify the value that it is bound to a variable from this frame.
133
- anExpression.bound!(self)
140
+ anExpression.bound!(self)
134
141
  end
135
-
142
+
136
143
  end # class
137
144
  end # module
@@ -110,20 +110,20 @@ module Skeem
110
110
  variable.value
111
111
  end
112
112
  end # class
113
-
113
+
114
114
  # Used to represent local binding constructs (let, let*, letrec, letrec*)
115
115
  class SkmBindingBlock < SkmUnaryExpression
116
116
  alias body child
117
-
117
+
118
118
  attr_reader :kind
119
119
  attr_reader :bindings
120
-
120
+
121
121
  def initialize(theKind, theBindings, aBody)
122
122
  @kind = theKind
123
123
  @bindings = theBindings
124
124
  super(nil, aBody)
125
125
  end
126
-
126
+
127
127
  def evaluate(aRuntime)
128
128
  aRuntime.push(SkmFrame.new(aRuntime.environment))
129
129
  if kind == :let
@@ -137,14 +137,59 @@ module Skeem
137
137
  bindings.each do |bnd|
138
138
  val = bnd.value.evaluate(aRuntime)
139
139
  aRuntime.add_binding(bnd.variable.evaluate(aRuntime), val)
140
- end
140
+ end
141
+ end
142
+
143
+ unless body[:defs].empty?
144
+ body[:defs].each do |dfn|
145
+ dfn.evaluate(aRuntime)
146
+ end
141
147
  end
142
-
143
- result = body[:sequence].evaluate(aRuntime)
148
+
149
+ # $stderr.puts "Environment SkmBindingBlock#evaluate:" + aRuntime.environment.inspect
150
+ raw_result = body[:sequence].evaluate(aRuntime)
151
+ result = raw_result.kind_of?(SkmPair) ? raw_result.last : raw_result
152
+ result = result.doppelganger(aRuntime) if result.kind_of?(SkmLambda)
153
+
144
154
  aRuntime.pop
145
- result.kind_of?(SkmPair) ? result.last : result
155
+ # $stderr.puts "Result SkmBindingBlock#evaluate: " + result.inspect
156
+ result
146
157
  end
147
-
158
+
148
159
  end # class
149
-
160
+
161
+ # Sequencing construct
162
+ class SkmSequencingBlock < SkmUnaryExpression
163
+ alias sequence child
164
+
165
+ def initialize(aSequence)
166
+ super(nil, aSequence)
167
+ end
168
+
169
+ def evaluate(aRuntime)
170
+ result = nil
171
+ if sequence
172
+ sequence.kind_of?(SkmPair)
173
+ sequence.to_a.each do |cmd|
174
+ begin
175
+ if cmd.kind_of?(SkmLambda)
176
+ result = cmd.dup_cond(aRuntime)
177
+ else
178
+ result = cmd.evaluate(aRuntime)
179
+ end
180
+ rescue NoMethodError => exc
181
+ $stderr.puts self.inspect
182
+ $stderr.puts sequence.inspect
183
+ $stderr.puts cmd.inspect
184
+ raise exc
185
+ end
186
+ end
187
+ elsif
188
+ result = sequence.evaluate(aRuntime)
189
+ end
190
+
191
+ result
192
+ end
193
+ end # class
194
+
150
195
  end # module
data/lib/skeem/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Skeem
2
- VERSION = '0.2.02'.freeze
2
+ VERSION = '0.2.03'.freeze
3
3
  end
@@ -330,7 +330,7 @@ SKEEM
330
330
  expect(result.last).to eq(66)
331
331
  end
332
332
 
333
- it 'should support the nesting of local bindings' do
333
+ it 'should support the nesting of local bindings' do
334
334
  source = <<-SKEEM
335
335
  (let ((x 2) (y 3))
336
336
  (let ((x 7)
@@ -341,6 +341,29 @@ SKEEM
341
341
  expect(result).to eq(35)
342
342
  end
343
343
 
344
+ it 'should support the nesting of a lambda in a let expression' do
345
+ source =<<-SKEEM
346
+ (define make-counter
347
+ (lambda ()
348
+ (let ((count 0))
349
+ (lambda ()
350
+ (set! count (+ count 1))
351
+ count))))
352
+
353
+ (define c1 (make-counter))
354
+ (define c2 (make-counter))
355
+ (c1)
356
+ (c2)
357
+ (c1)
358
+ (c2)
359
+ (c1)
360
+ SKEEM
361
+ result = subject.run(source)
362
+ expect(result.last).to eq(3)
363
+ end
364
+
365
+
366
+
344
367
  it 'should implement let* expression' do
345
368
  source = <<-SKEEM
346
369
  (let ((x 2) (y 3))
@@ -353,6 +376,29 @@ SKEEM
353
376
  end
354
377
  end # context
355
378
 
379
+ context 'Sequencing constructs:' do
380
+ it 'should implement begin as a sequence of expressions' do
381
+ source = <<-SKEEM
382
+ (define x 0)
383
+ (and (= x 0)
384
+ (begin (set! x 5)
385
+ (+ x 1))) ; => 6
386
+ SKEEM
387
+ result = subject.run(source)
388
+ expect(result.last).to eq(6)
389
+ end
390
+
391
+ it 'should implement begin as a sequence of expressions' do
392
+ source = <<-SKEEM
393
+ (let ()
394
+ (begin (define x 3) (define y 4))
395
+ (+ x y)) ; => 7
396
+ SKEEM
397
+ result = subject.run(source)
398
+ expect(result).to eq(7)
399
+ end
400
+ end # context
401
+
356
402
  context 'Quasiquotation:' do
357
403
  it 'should implement the quasiquotation of constant literals' do
358
404
  checks = [
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.02
4
+ version: 0.2.03
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-04-20 00:00:00.000000000 Z
11
+ date: 2019-04-26 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rley