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 +4 -4
- data/CHANGELOG.md +8 -1
- data/README.md +31 -0
- data/lib/skeem/grammar.rb +7 -4
- data/lib/skeem/primitive/primitive_procedure.rb +3 -3
- data/lib/skeem/s_expr_builder.rb +21 -0
- data/lib/skeem/s_expr_nodes.rb +11 -3
- data/lib/skeem/skm_frame.rb +16 -9
- data/lib/skeem/skm_unary_expression.rb +55 -10
- data/lib/skeem/version.rb +1 -1
- data/spec/skeem/interpreter_spec.rb +47 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6d5dc31ad0db5984772c42897c9ce30f51b5d97b44e7c1b9e65a277f4294c767
|
4
|
+
data.tar.gz: cdb6ad6e66f703a65327fee0523be67a898215d2efa0e91371ec5b75b5262daa
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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`
|
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('
|
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
|
84
|
-
rule
|
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...
|
data/lib/skeem/s_expr_builder.rb
CHANGED
@@ -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'
|
data/lib/skeem/s_expr_nodes.rb
CHANGED
@@ -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
|
-
|
75
|
-
|
74
|
+
# $stderr.puts ' operator: ' + operator.inspect
|
75
|
+
# $stderr.puts ' original operands: ' + operands.inspect
|
76
76
|
actuals = transform_operands(aRuntime)
|
77
|
-
|
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)}]"
|
data/lib/skeem/skm_frame.rb
CHANGED
@@ -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
|
-
|
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
|
-
|
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
@@ -330,7 +330,7 @@ SKEEM
|
|
330
330
|
expect(result.last).to eq(66)
|
331
331
|
end
|
332
332
|
|
333
|
-
|
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.
|
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-
|
11
|
+
date: 2019-04-26 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rley
|