skeem 0.2.02 → 0.2.03

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