skeem 0.1.03 → 0.2.00
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +7 -0
- data/README.md +1 -1
- data/lib/skeem/grammar.rb +4 -2
- data/lib/skeem/interpreter.rb +24 -9
- data/lib/skeem/primitive/primitive_builder.rb +18 -18
- data/lib/skeem/primitive/primitive_procedure.rb +32 -6
- data/lib/skeem/runtime.rb +34 -32
- data/lib/skeem/s_expr_builder.rb +9 -3
- data/lib/skeem/s_expr_nodes.rb +203 -188
- data/lib/skeem/skm_binding.rb +103 -0
- data/lib/skeem/skm_element.rb +20 -10
- data/lib/skeem/skm_frame.rb +137 -0
- data/lib/skeem/skm_pair.rb +2 -2
- data/lib/skeem/skm_procedure_exec.rb +31 -0
- data/lib/skeem/standard/base.skm +15 -1
- data/lib/skeem/tokenizer.rb +2 -1
- data/lib/skeem/version.rb +1 -1
- data/spec/skeem/interpreter_spec.rb +30 -16
- data/spec/skeem/lambda_spec.rb +114 -0
- data/spec/skeem/primitive/primitive_builder_spec.rb +16 -12
- data/spec/skeem/primitive/primitive_procedure_spec.rb +14 -21
- data/spec/skeem/runtime_spec.rb +14 -12
- data/spec/skeem/s_expr_nodes_spec.rb +5 -81
- data/spec/skeem/skm_frame_spec.rb +119 -0
- data/spec/skeem/skm_procedure_exec_spec.rb +63 -0
- data/spec/skeem/skm_unary_expression_spec.rb +2 -3
- metadata +11 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4f8a75ad0a0ead374dc7ec59e94abc9edd04826901be7f2e8230c9047c256845
|
4
|
+
data.tar.gz: 854f4e2e64dff8224046420ee7ccd035b23cf409c016ecec667206c02a5aae87
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ea925e74fbbab68290d6b6cb13e7af3caa735e9f5709e5d9f50423410695d5db0a9c50d077e594e2683e16c7b4f6b85c02b1d72629d97dc6e9348f7a1e32533c
|
7
|
+
data.tar.gz: 4930db824c351c76d56f006491f8838e88b64e2e2200fb13412c848c0bc90c6f9effa839983ac3e5bd926e0c0ab162bfb87e3a8217f8d2f14410ce35a4ff2d46
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,10 @@
|
|
1
|
+
## [0.1.04] - 2019-01-19
|
2
|
+
### Added
|
3
|
+
- Method `SkmDefinition#redefine`: a given variable can point to another expression.
|
4
|
+
|
5
|
+
### Fixed
|
6
|
+
- Procedure `set!` caused infinite loops when redefinition involved a procedure call.
|
7
|
+
|
1
8
|
## [0.1.03] - 2019-01-15
|
2
9
|
### Added
|
3
10
|
- File `base.skm` implementation of list procedures: `caar`, `cadr`, `cdar`, `cddr`
|
data/README.md
CHANGED
@@ -222,7 +222,7 @@ This project is intended to be a safe, welcoming space for collaboration, and co
|
|
222
222
|
|
223
223
|
Copyright
|
224
224
|
---------
|
225
|
-
Copyright (c) 2018, Dimitri Geshef.
|
225
|
+
Copyright (c) 2018-2019, Dimitri Geshef.
|
226
226
|
__Skeem__ is released under the MIT License see [LICENSE.txt](https://github.com/famished-tiger/Skeem/blob/master/LICENSE.txt) for details.
|
227
227
|
|
228
228
|
## Code of Conduct
|
data/lib/skeem/grammar.rb
CHANGED
@@ -20,8 +20,8 @@ module Skeem
|
|
20
20
|
|
21
21
|
# Keywords...
|
22
22
|
add_terminals('DEFINE', 'IF', 'LAMBDA')
|
23
|
-
add_terminals('QUOTE', 'QUASIQUOTE', '
|
24
|
-
add_terminals('UNQUOTE-SPLICING')
|
23
|
+
add_terminals('QUOTE', 'QUASIQUOTE', 'SET!')
|
24
|
+
add_terminals('UNQUOTE', 'UNQUOTE-SPLICING')
|
25
25
|
|
26
26
|
rule('program' => 'cmd_or_def_plus').as 'main'
|
27
27
|
rule('cmd_or_def_plus' => 'cmd_or_def_plus cmd_or_def').as 'multiple_cmd_def'
|
@@ -36,6 +36,7 @@ module Skeem
|
|
36
36
|
rule 'expression' => 'procedure_call'
|
37
37
|
rule 'expression' => 'lambda_expression'
|
38
38
|
rule 'expression' => 'conditional'
|
39
|
+
rule 'expression' => 'assignment'
|
39
40
|
rule 'expression' => 'derived_expression'
|
40
41
|
rule 'literal' => 'quotation'
|
41
42
|
rule 'literal' => 'self-evaluating'
|
@@ -90,6 +91,7 @@ module Skeem
|
|
90
91
|
rule 'alternate' => []
|
91
92
|
rule 'number' => 'INTEGER'
|
92
93
|
rule 'number' => 'REAL'
|
94
|
+
rule('assignment' => 'LPAREN SET! IDENTIFIER expression RPAREN').as 'assignment'
|
93
95
|
rule 'derived_expression' => 'quasiquotation'
|
94
96
|
rule('quasiquotation' => 'LPAREN QUASIQUOTE qq_template RPAREN').as 'quasiquotation'
|
95
97
|
rule('quasiquotation' => 'GRAVE_ACCENT qq_template').as 'quasiquotation_short'
|
data/lib/skeem/interpreter.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
require_relative 'parser'
|
2
|
-
require_relative '
|
2
|
+
require_relative 'skm_frame'
|
3
3
|
require_relative 'runtime'
|
4
4
|
require_relative './primitive/primitive_builder'
|
5
5
|
|
@@ -9,31 +9,46 @@ module Skeem
|
|
9
9
|
attr_reader(:parser)
|
10
10
|
attr_reader(:runtime)
|
11
11
|
|
12
|
-
def initialize()
|
13
|
-
@runtime = Runtime.new(
|
12
|
+
def initialize(&aBlock)
|
13
|
+
@runtime = Runtime.new(SkmFrame.new)
|
14
|
+
@parser = Parser.new
|
15
|
+
|
16
|
+
if block_given?
|
17
|
+
yield self
|
18
|
+
else
|
19
|
+
add_default_procedures
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def add_default_procedures()
|
14
24
|
add_primitives(runtime)
|
15
25
|
add_standard(runtime)
|
16
26
|
end
|
17
27
|
|
18
|
-
def
|
28
|
+
def parse(source, mode = nil)
|
19
29
|
@parser ||= Parser.new
|
20
30
|
@ptree = parser.parse(source)
|
21
31
|
# $stderr.puts @ptree.root.inspect if mode.nil?
|
22
|
-
# require 'debug'
|
23
|
-
|
32
|
+
# require 'debug' unless mode.nil?
|
33
|
+
end
|
34
|
+
|
35
|
+
def run(source, mode = nil)
|
36
|
+
parse(source, mode)
|
37
|
+
# require 'debug' unless mode.nil?
|
38
|
+
@ptree.root.evaluate(runtime)
|
24
39
|
end
|
25
40
|
|
26
41
|
def fetch(anIdentifier)
|
27
42
|
runtime.environment.fetch(anIdentifier)
|
28
43
|
end
|
29
44
|
|
30
|
-
|
31
|
-
|
32
|
-
def add_standard(aRuntime)
|
45
|
+
def add_standard(_runtime)
|
33
46
|
std_pathname = File.dirname(__FILE__) + '/standard/base.skm'
|
34
47
|
load_lib(std_pathname)
|
35
48
|
end
|
36
49
|
|
50
|
+
private
|
51
|
+
|
37
52
|
def load_lib(aPathname)
|
38
53
|
lib_source = nil
|
39
54
|
File.open(aPathname, 'r') do |lib|
|
@@ -48,7 +48,6 @@ module Skeem
|
|
48
48
|
end
|
49
49
|
|
50
50
|
def add_binding(aRuntime)
|
51
|
-
create_set!(aRuntime)
|
52
51
|
end
|
53
52
|
|
54
53
|
def add_arithmetic(aRuntime)
|
@@ -123,21 +122,9 @@ module Skeem
|
|
123
122
|
end
|
124
123
|
|
125
124
|
def add_special_procedures(aRuntime)
|
126
|
-
|
125
|
+
create_test_assert(aRuntime)
|
127
126
|
create_debug(aRuntime)
|
128
|
-
|
129
|
-
|
130
|
-
def create_set!(aRuntime)
|
131
|
-
primitive = ->(runtime, var_ref, expr) do
|
132
|
-
if runtime.include?(var_ref.child)
|
133
|
-
redefinition = SkmDefinition.new(nil, var_ref.child, expr)
|
134
|
-
redefinition.evaluate(runtime)
|
135
|
-
else
|
136
|
-
raise StandardError, "Unbound variable: '#{var.value}'"
|
137
|
-
end
|
138
|
-
end
|
139
|
-
|
140
|
-
define_primitive_proc(aRuntime, 'set!', binary, primitive)
|
127
|
+
create_inspect(aRuntime)
|
141
128
|
end
|
142
129
|
|
143
130
|
def create_plus(aRuntime)
|
@@ -622,7 +609,7 @@ module Skeem
|
|
622
609
|
define_primitive_proc(aRuntime, 'newline', nullary, primitive)
|
623
610
|
end
|
624
611
|
|
625
|
-
def
|
612
|
+
def create_test_assert(aRuntime)
|
626
613
|
primitive = ->(runtime, arg) do
|
627
614
|
arg_evaluated = arg.evaluate(runtime)
|
628
615
|
if arg_evaluated.boolean? && arg_evaluated.value == false
|
@@ -636,9 +623,11 @@ module Skeem
|
|
636
623
|
end
|
637
624
|
end
|
638
625
|
|
639
|
-
define_primitive_proc(aRuntime, 'assert', unary, primitive)
|
626
|
+
define_primitive_proc(aRuntime, 'test-assert', unary, primitive)
|
640
627
|
end
|
641
628
|
|
629
|
+
# DON'T USE IT
|
630
|
+
# Non-standard procedure reserved for internal testing/debugging purposes.
|
642
631
|
def create_debug(aRuntime)
|
643
632
|
primitive = ->(runtime) do
|
644
633
|
require 'debug'
|
@@ -646,6 +635,17 @@ module Skeem
|
|
646
635
|
|
647
636
|
define_primitive_proc(aRuntime, 'debug', nullary, primitive)
|
648
637
|
end
|
638
|
+
|
639
|
+
# DON'T USE IT
|
640
|
+
# Non-standard procedure reserved for internal testing/debugging purposes.
|
641
|
+
def create_inspect(aRuntime)
|
642
|
+
primitive = ->(runtime, arg) do
|
643
|
+
arg_evaluated = arg.evaluate(runtime)
|
644
|
+
$stderr.puts 'INSPECT>' + arg_evaluated.inspect
|
645
|
+
Skeem::SkmUndefined.instance
|
646
|
+
end
|
647
|
+
define_primitive_proc(aRuntime, '_inspect', unary, primitive)
|
648
|
+
end
|
649
649
|
|
650
650
|
def create_object_predicate(aRuntime, predicate_name, msg_name = nil)
|
651
651
|
msg_name = predicate_name if msg_name.nil?
|
@@ -670,7 +670,7 @@ module Skeem
|
|
670
670
|
end
|
671
671
|
|
672
672
|
def define(aRuntime, aKey, anEntry)
|
673
|
-
aRuntime.
|
673
|
+
aRuntime.add_binding(aKey, anEntry)
|
674
674
|
end
|
675
675
|
|
676
676
|
def evaluate_arguments(arglist, aRuntime)
|
@@ -14,18 +14,44 @@ module Skeem
|
|
14
14
|
@arity = arity_validated(anArity)
|
15
15
|
end
|
16
16
|
|
17
|
+
def callable?
|
18
|
+
true
|
19
|
+
end
|
20
|
+
|
21
|
+
# This method should be invoked when the procedure isn't
|
22
|
+
# explicitly called (with arguments). In this case, the name
|
23
|
+
# of procedure just returns the procedure object itself.
|
24
|
+
# @param _runtime [Runtime]
|
25
|
+
# @return [PrimitiveProcedure]
|
26
|
+
def evaluate(_runtime)
|
27
|
+
self
|
28
|
+
end
|
29
|
+
|
17
30
|
# Arguments are positional in a primitive procedure.
|
18
|
-
|
19
|
-
|
31
|
+
# @param theActuals [Array<SkmElement>]
|
32
|
+
def call(aRuntime, theActuals)
|
33
|
+
actuals = theActuals
|
34
|
+
# $stderr.puts "--- Start of procedure #{identifier}"
|
35
|
+
# actuals.each { |actual| $stderr.puts ' Actual: ' + actual.inspect }
|
20
36
|
check_actual_count(actuals)
|
21
|
-
|
22
|
-
|
37
|
+
# TODO: check that next line became useless
|
38
|
+
# aProcedureCall.operands_consumed = true
|
39
|
+
result = do_call(aRuntime, actuals)
|
40
|
+
# $stderr.puts " Result: #{result.inspect}"
|
41
|
+
# $stderr.puts "--- End of procedure #{identifier}"
|
42
|
+
result
|
23
43
|
end
|
24
|
-
|
44
|
+
|
25
45
|
def skm_equal?(other)
|
26
46
|
equal?(other)
|
27
47
|
end
|
28
48
|
|
49
|
+
# Notification that this procedure is bound to a variable
|
50
|
+
# @param [Skemm::SkmFrame]
|
51
|
+
def bound!(_frame)
|
52
|
+
# Do nothing
|
53
|
+
end
|
54
|
+
|
29
55
|
private
|
30
56
|
|
31
57
|
def code_validated(aRubyLambda)
|
@@ -94,7 +120,7 @@ module Skeem
|
|
94
120
|
#p count_delta
|
95
121
|
#p arguments.inspect
|
96
122
|
result = code.send(:call, aRuntime, *arguments.flatten)
|
97
|
-
end
|
123
|
+
end
|
98
124
|
else # Fixed arity...
|
99
125
|
result = code.send(:call, aRuntime, *operands)
|
100
126
|
end
|
data/lib/skeem/runtime.rb
CHANGED
@@ -1,30 +1,37 @@
|
|
1
1
|
require_relative 's_expr_nodes'
|
2
|
-
require_relative 'environment'
|
3
2
|
|
4
3
|
module Skeem
|
5
4
|
class Runtime
|
6
|
-
# @return [
|
7
|
-
attr_reader(:
|
5
|
+
# @return [Array<SkmFrame>] The current active frame
|
6
|
+
attr_reader(:env_stack)
|
8
7
|
|
9
8
|
# @return [Array<ProcedureCall>] The call stack
|
10
9
|
attr_reader(:call_stack)
|
11
10
|
|
12
|
-
def initialize(anEnvironment)
|
13
|
-
@
|
14
|
-
|
11
|
+
def initialize(anEnvironment, parent = nil)
|
12
|
+
@env_stack = []
|
13
|
+
push(anEnvironment)
|
14
|
+
@call_stack = parent.nil? ? [] : parent.call_stack
|
15
|
+
end
|
16
|
+
|
17
|
+
def environment
|
18
|
+
env_stack.last
|
15
19
|
end
|
16
20
|
|
17
21
|
def include?(anIdentifier)
|
18
|
-
environment.include?(
|
22
|
+
environment.include?(anIdentifier)
|
19
23
|
end
|
20
24
|
|
21
25
|
def fetch(aKey)
|
22
|
-
|
23
|
-
include?(key_value) ? environment.fetch(key_value) : nil
|
26
|
+
include?(aKey) ? environment.fetch(aKey) : nil
|
24
27
|
end
|
25
28
|
|
26
|
-
def
|
27
|
-
environment.
|
29
|
+
def add_binding(aKey, anEntry)
|
30
|
+
environment.add_binding(aKey, anEntry)
|
31
|
+
end
|
32
|
+
|
33
|
+
def update_binding(aKey, anEntry)
|
34
|
+
environment.update_binding(aKey, anEntry)
|
28
35
|
end
|
29
36
|
|
30
37
|
def evaluate(aKey)
|
@@ -33,14 +40,8 @@ module Skeem
|
|
33
40
|
entry = environment.fetch(key_value)
|
34
41
|
result = nil
|
35
42
|
begin
|
36
|
-
|
37
|
-
|
38
|
-
result = entry
|
39
|
-
when SkmDefinition
|
40
|
-
result = entry.expression.evaluate(self)
|
41
|
-
else
|
42
|
-
raise StandardError, entry.inspect
|
43
|
-
end
|
43
|
+
result = entry.evaluate(self)
|
44
|
+
|
44
45
|
rescue NoMethodError => exc
|
45
46
|
# $stderr.puts 'In rescue block'
|
46
47
|
# $stderr.puts key_value.inspect
|
@@ -66,29 +67,30 @@ module Skeem
|
|
66
67
|
end
|
67
68
|
|
68
69
|
def nest()
|
69
|
-
nested =
|
70
|
-
|
70
|
+
nested = SkmFrame.new(environment)
|
71
|
+
push(nested)
|
71
72
|
end
|
72
73
|
|
73
|
-
def unnest
|
74
|
-
raise StandardError, 'Cannot unnest environment' unless environment.
|
74
|
+
def unnest
|
75
|
+
raise StandardError, 'Cannot unnest environment' unless environment.parent
|
75
76
|
environment.bindings.clear
|
76
|
-
|
77
|
+
pop
|
77
78
|
end
|
78
79
|
|
79
|
-
def depth
|
80
|
-
return
|
80
|
+
def depth
|
81
|
+
return env_stack.size
|
81
82
|
end
|
82
83
|
|
83
84
|
def push(anEnvironment)
|
84
|
-
|
85
|
+
env_stack.push(anEnvironment)
|
85
86
|
end
|
86
87
|
|
87
|
-
# Make the
|
88
|
+
# Make the parent frame the current one inside the provided block
|
88
89
|
def pop
|
89
|
-
|
90
|
-
|
91
|
-
|
90
|
+
if env_stack.empty?
|
91
|
+
raise StandardError, 'Skeem environment stack empty!'
|
92
|
+
end
|
93
|
+
env_stack.pop
|
92
94
|
end
|
93
95
|
|
94
96
|
def push_call(aProcCall)
|
@@ -104,7 +106,7 @@ module Skeem
|
|
104
106
|
# $stderr.puts 'CALL STACK ^^^^'
|
105
107
|
end
|
106
108
|
|
107
|
-
def pop_call
|
109
|
+
def pop_call
|
108
110
|
if call_stack.empty?
|
109
111
|
raise StandardError, 'Skeem call stack empty!'
|
110
112
|
end
|
data/lib/skeem/s_expr_builder.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
require 'stringio'
|
2
2
|
require_relative 'skm_pair'
|
3
|
+
require_relative 'skm_binding'
|
3
4
|
require_relative 's_expr_nodes'
|
4
5
|
|
5
6
|
module Skeem
|
@@ -60,8 +61,8 @@ module Skeem
|
|
60
61
|
|
61
62
|
# rule('definition' => 'LPAREN DEFINE IDENTIFIER expression RPAREN')
|
62
63
|
# .as 'definition'
|
63
|
-
def reduce_definition(_production,
|
64
|
-
|
64
|
+
def reduce_definition(_production, _range, _tokens, theChildren)
|
65
|
+
SkmBinding.new(theChildren[2], theChildren[3])
|
65
66
|
end
|
66
67
|
|
67
68
|
# rule('definition' => 'LPAREN DEFINE LPAREN IDENTIFIER def_formals RPAREN body RPAREN').as 'alt_definition'
|
@@ -69,7 +70,7 @@ module Skeem
|
|
69
70
|
def reduce_alt_definition(_production, aRange, _tokens, theChildren)
|
70
71
|
lmbd = SkmLambda.new(aRange, theChildren[4], theChildren[6])
|
71
72
|
# $stderr.puts lmbd.inspect
|
72
|
-
|
73
|
+
SkmBinding.new(theChildren[3], lmbd)
|
73
74
|
end
|
74
75
|
|
75
76
|
# rule('expression' => 'IDENTIFIER').as 'variable_reference'
|
@@ -245,6 +246,11 @@ module Skeem
|
|
245
246
|
def reduce_conditional(_production, aRange, _tokens, theChildren)
|
246
247
|
SkmCondition.new(aRange, theChildren[2], theChildren[3], theChildren[4])
|
247
248
|
end
|
249
|
+
|
250
|
+
# rule('assignment' => 'LPAREN SET! IDENTIFIER expression RPAREN').as 'assignment'
|
251
|
+
def reduce_assignment(_production, _range, _tokens, theChildren)
|
252
|
+
SkmUpdateBinding.new(theChildren[2], theChildren[3])
|
253
|
+
end
|
248
254
|
|
249
255
|
# rule('quasiquotation' => 'LPAREN QUASIQUOTE qq_template RPAREN').as 'quasiquotation'
|
250
256
|
def reduce_quasiquotation(_production, aRange, _tokens, theChildren)
|
data/lib/skeem/s_expr_nodes.rb
CHANGED
@@ -8,26 +8,17 @@ require_relative 'skm_unary_expression'
|
|
8
8
|
module Skeem
|
9
9
|
class SkmUndefined
|
10
10
|
include Singleton
|
11
|
-
|
11
|
+
|
12
12
|
def value
|
13
|
-
|
13
|
+
self
|
14
14
|
end
|
15
15
|
|
16
16
|
def ==(other)
|
17
|
-
|
18
|
-
|
19
|
-
result = case other
|
20
|
-
when Symbol
|
21
|
-
self.value == other
|
22
|
-
when String
|
23
|
-
self.value.to_s == other
|
24
|
-
else
|
25
|
-
raise StandardError, other.inspect
|
26
|
-
end
|
17
|
+
equal?(other)
|
27
18
|
end
|
28
|
-
|
19
|
+
|
29
20
|
private
|
30
|
-
|
21
|
+
|
31
22
|
def initialize
|
32
23
|
self.freeze
|
33
24
|
end
|
@@ -46,95 +37,13 @@ module Skeem
|
|
46
37
|
end
|
47
38
|
end
|
48
39
|
|
49
|
-
class SkmDefinition < SkmMultiExpression
|
50
|
-
attr_reader :variable
|
51
|
-
attr_reader :expression
|
52
|
-
|
53
|
-
def initialize(aPosition, aVariable, theExpression)
|
54
|
-
super(aPosition)
|
55
|
-
@variable = aVariable
|
56
|
-
@expression = theExpression
|
57
|
-
unless expression.kind_of?(SkmElement)
|
58
|
-
raise StandardError, "Bad definition"
|
59
|
-
end
|
60
|
-
end
|
61
|
-
|
62
|
-
def evaluate(aRuntime)
|
63
|
-
var_key = variable.evaluate(aRuntime)
|
64
|
-
aRuntime.define(var_key, self)
|
65
|
-
case expression
|
66
|
-
when SkmLambda
|
67
|
-
result = expression.evaluate(aRuntime)
|
68
|
-
|
69
|
-
when SkmVariableReference
|
70
|
-
other_key = expression.variable.evaluate(aRuntime)
|
71
|
-
if var_key.value != other_key.value
|
72
|
-
entry = aRuntime.fetch(other_key)
|
73
|
-
result = expression.evaluate(aRuntime)
|
74
|
-
if entry.kind_of?(Primitive::PrimitiveProcedure)
|
75
|
-
@expression = entry
|
76
|
-
elsif entry.kind_of?(SkmDefinition)
|
77
|
-
if entry.expression.kind_of?(SkmLambda)
|
78
|
-
@expression = entry.expression
|
79
|
-
end
|
80
|
-
end
|
81
|
-
else
|
82
|
-
# INFINITE LOOP DANGER: definition of 'x' has a reference to 'x'!
|
83
|
-
# Way out: the lookup for the reference should start from outer
|
84
|
-
# environment.
|
85
|
-
env = aRuntime.pop
|
86
|
-
@expression = expression.evaluate(aRuntime)
|
87
|
-
aRuntime.push(env)
|
88
|
-
result = expression
|
89
|
-
end
|
90
|
-
else
|
91
|
-
result = self
|
92
|
-
end
|
93
|
-
|
94
|
-
result
|
95
|
-
end
|
96
|
-
|
97
|
-
def quasiquote(aRuntime)
|
98
|
-
quasi_var = variable.quasiquote(aRuntime)
|
99
|
-
quasi_expression = variable.quasiquote(aRuntime)
|
100
|
-
|
101
|
-
if quasi_var.equal?(variable) && quasi_expression.equal?(expression)
|
102
|
-
self
|
103
|
-
else
|
104
|
-
self.class.new(position, quasi_var, quasi_expression)
|
105
|
-
end
|
106
|
-
end
|
107
|
-
|
108
|
-
# call method should only invoked when the expression is a SkmLambda
|
109
|
-
def call(aRuntime, aProcedureCall)
|
110
|
-
expr = expression
|
111
|
-
expr = expr.evaluate(aRuntime) if expr.kind_of?(SkmVariableReference)
|
112
|
-
unless [SkmLambda, Primitive::PrimitiveProcedure].include?(expr.class)
|
113
|
-
err_msg = "Expected a SkmLambda instead of #{expr.class}"
|
114
|
-
raise StandardError, err_msg
|
115
|
-
end
|
116
|
-
# $stderr.puts "SkmDefinition#call #{aProcedureCall.inspect}"
|
117
|
-
expr.call(aRuntime, aProcedureCall)
|
118
|
-
end
|
119
|
-
|
120
|
-
def inspect
|
121
|
-
result = inspect_prefix
|
122
|
-
result << variable.inspect
|
123
|
-
result << ', '
|
124
|
-
result << expression.inspect
|
125
|
-
result << inspect_suffix
|
126
|
-
result
|
127
|
-
end
|
128
|
-
|
129
|
-
def associations
|
130
|
-
[:variable, :expression]
|
131
|
-
end
|
132
|
-
end # class
|
133
|
-
|
134
40
|
class ProcedureCall < SkmMultiExpression
|
135
41
|
attr_reader :operator
|
136
42
|
attr_reader :operands
|
43
|
+
|
137
44
|
attr_accessor :call_site
|
45
|
+
|
46
|
+
# @return [FalseClass, TrueClass] True if all arguments are used by callee.
|
138
47
|
attr_accessor :operands_consumed
|
139
48
|
|
140
49
|
def initialize(aPosition, anOperator, theOperands)
|
@@ -154,48 +63,32 @@ module Skeem
|
|
154
63
|
end
|
155
64
|
|
156
65
|
def evaluate(aRuntime)
|
66
|
+
frame_change = false
|
157
67
|
aRuntime.push_call(self)
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
unless aRuntime.include?(var_key.value)
|
180
|
-
err = StandardError
|
181
|
-
key = var_key.kind_of?(SkmIdentifier) ? var_key.value : var_key
|
182
|
-
err_msg = "Unknown procedure '#{key}'"
|
183
|
-
raise err, err_msg
|
184
|
-
end
|
185
|
-
callee = aRuntime.environment.fetch(var_key.value)
|
186
|
-
end
|
187
|
-
unless var_key.nil?
|
188
|
-
# $stderr.puts "## In ProcCall #{var_key.value} #############"
|
189
|
-
# $stderr.puts 'operator: ' + operator.inspect
|
190
|
-
# $stderr.puts 'operands: ' + operands.inspect
|
191
|
-
# $stderr.puts "## CALL(#{var_key.value}) ###################"
|
192
|
-
# $stderr.puts 'callee: ' + callee.inspect
|
193
|
-
end
|
68
|
+
# $stderr.puts "\n Start of ProcedureCall#evaluate #{object_id.to_s(16)}"
|
69
|
+
# $stderr.puts " environment: #{aRuntime.environment.object_id.to_s(16)}, "
|
70
|
+
if aRuntime.environment && aRuntime.environment.parent
|
71
|
+
# $stderr.puts "Parent environment #{aRuntime.environment.parent.object_id.to_s(16)}, "
|
72
|
+
# $stderr.puts aRuntime.environment.inspect
|
73
|
+
end
|
74
|
+
#$stderr.puts ' operator: ' + operator.inspect
|
75
|
+
#$stderr.puts ' original operands: ' + operands.inspect
|
76
|
+
actuals = transform_operands(aRuntime)
|
77
|
+
#$stderr.puts ' transformed operands: ' + actuals.inspect
|
78
|
+
outcome, result = determine_callee(aRuntime)
|
79
|
+
if outcome == :callee
|
80
|
+
callee = result
|
81
|
+
# if callee.kind_of?(SkmLambda)
|
82
|
+
# aRuntime.push(callee.environment)
|
83
|
+
# frame_change = true
|
84
|
+
# end
|
85
|
+
# $stderr.puts ' callee: ' + callee.inspect
|
86
|
+
result = callee.call(aRuntime, actuals)
|
87
|
+
operands_consumed = true
|
88
|
+
# aRuntime.pop if frame_change
|
194
89
|
end
|
195
|
-
result = callee.call(aRuntime, self)
|
196
|
-
operands_consumed = true
|
197
90
|
aRuntime.pop_call
|
198
|
-
# $stderr.puts "
|
91
|
+
# $stderr.puts "\n End of ProcedureCall#evaluate #{object_id.to_s(16)}"
|
199
92
|
result
|
200
93
|
end
|
201
94
|
|
@@ -217,6 +110,78 @@ module Skeem
|
|
217
110
|
end
|
218
111
|
|
219
112
|
alias children operands
|
113
|
+
|
114
|
+
private
|
115
|
+
|
116
|
+
def determine_callee(aRuntime)
|
117
|
+
case operator
|
118
|
+
when SkmIdentifier
|
119
|
+
callee = fetch_callee(aRuntime, operator)
|
120
|
+
when ProcedureCall
|
121
|
+
result = operator.evaluate(aRuntime)
|
122
|
+
# If child proc call consumes the parent's operand, then we're done
|
123
|
+
return [:result, result] unless result.callable? # if operands_consumed
|
124
|
+
callee = result
|
125
|
+
# callee = fetch_callee(aRuntime, result)
|
126
|
+
when Primitive::PrimitiveProcedure
|
127
|
+
callee = operator
|
128
|
+
when SkmLambda
|
129
|
+
callee = operator
|
130
|
+
else
|
131
|
+
result = operator.evaluate(aRuntime)
|
132
|
+
callee = fetch_callee(aRuntime, result)
|
133
|
+
end
|
134
|
+
|
135
|
+
[:callee, callee]
|
136
|
+
end
|
137
|
+
|
138
|
+
def fetch_callee(aRuntime, var_key)
|
139
|
+
begin
|
140
|
+
aRuntime.include?(var_key.value)
|
141
|
+
rescue NoMethodError => exc
|
142
|
+
# $stderr.puts "VVVVVVVVVVVVVVV"
|
143
|
+
# $stderr.puts 'var_key: ' + var_key.inspect
|
144
|
+
# $stderr.puts 'operator: ' + operator.inspect
|
145
|
+
# $stderr.puts 'operands: ' + operands.inspect
|
146
|
+
# $stderr.puts 'operands_consumed: ' + operands_consumed.inspect
|
147
|
+
# $stderr.puts "^^^^^^^^^^^^^^^"
|
148
|
+
raise exc
|
149
|
+
end
|
150
|
+
unless aRuntime.include?(var_key.value)
|
151
|
+
err = StandardError
|
152
|
+
key = var_key.kind_of?(SkmIdentifier) ? var_key.value : var_key
|
153
|
+
err_msg = "Unknown procedure '#{key}'"
|
154
|
+
# $stderr.puts aRuntime.inspect
|
155
|
+
# $stderr.puts aRuntime.environment.size.inspect
|
156
|
+
raise err, err_msg
|
157
|
+
end
|
158
|
+
callee = aRuntime.environment.fetch(var_key.value)
|
159
|
+
# $stderr.puts "## CALL(#{var_key.value}) ###################"
|
160
|
+
# $stderr.puts 'callee: ' + callee.inspect
|
161
|
+
# $stderr.puts 'operator: ' + operator.inspect
|
162
|
+
# $stderr.puts 'operands: ' + operands.inspect
|
163
|
+
|
164
|
+
callee
|
165
|
+
end
|
166
|
+
|
167
|
+
def transform_operands(aRuntime)
|
168
|
+
return [] if operands == SkmEmptyList.instance
|
169
|
+
actuals = operands.to_a
|
170
|
+
|
171
|
+
result = actuals.map do |actual|
|
172
|
+
case actual
|
173
|
+
when SkmVariableReference
|
174
|
+
aRuntime.fetch(actual.child)
|
175
|
+
else
|
176
|
+
actual.evaluate(aRuntime)
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
180
|
+
result.nil? ? [] : result
|
181
|
+
end
|
182
|
+
|
183
|
+
|
184
|
+
|
220
185
|
end # class
|
221
186
|
|
222
187
|
class SkmCondition < SkmMultiExpression
|
@@ -331,6 +296,10 @@ module Skeem
|
|
331
296
|
end
|
332
297
|
end # class
|
333
298
|
|
299
|
+
|
300
|
+
|
301
|
+
require_relative 'skm_procedure_exec'
|
302
|
+
|
334
303
|
class SkmLambda < SkmMultiExpression
|
335
304
|
include DatumDSL
|
336
305
|
|
@@ -339,6 +308,7 @@ module Skeem
|
|
339
308
|
attr_reader :formals
|
340
309
|
attr_reader :definitions
|
341
310
|
attr_reader :sequence
|
311
|
+
attr_reader :environment
|
342
312
|
|
343
313
|
def initialize(aPosition, theFormals, aBody)
|
344
314
|
super(aPosition)
|
@@ -348,9 +318,12 @@ module Skeem
|
|
348
318
|
end
|
349
319
|
|
350
320
|
def evaluate(aRuntime)
|
351
|
-
# formals.evaluate(aRuntime)
|
352
321
|
self
|
353
322
|
end
|
323
|
+
|
324
|
+
def callable?
|
325
|
+
true
|
326
|
+
end
|
354
327
|
=begin
|
355
328
|
TODO
|
356
329
|
def quasiquote(aRuntime)
|
@@ -361,17 +334,28 @@ module Skeem
|
|
361
334
|
self.class.new(position, quasi_test, quasi_consequent, quasi_alternate)
|
362
335
|
end
|
363
336
|
=end
|
364
|
-
def call(aRuntime,
|
337
|
+
def call(aRuntime, theActuals)
|
338
|
+
set_cond_environment(aRuntime.environment) # Last chance for anonymous lambda
|
339
|
+
application = SkmProcedureExec.new(self)
|
340
|
+
application.run!(aRuntime, theActuals)
|
341
|
+
=begin
|
342
|
+
# $stderr.puts "Start of lambda #{object_id.to_s(16)}"
|
365
343
|
aRuntime.nest
|
344
|
+
# $stderr.puts ' Before bind_locals'
|
366
345
|
bind_locals(aRuntime, aProcedureCall)
|
346
|
+
# $stderr.puts ' After bind_locals'
|
367
347
|
# $stderr.puts 'LOCALS vvvvvvvvvvv'
|
368
348
|
# $stderr.puts aRuntime.environment.inspect
|
369
349
|
# $stderr.puts 'LOCALS ^^^^^^^^^^^'
|
370
350
|
result = evaluate_defs(aRuntime)
|
351
|
+
# $stderr.puts ' Before evaluate_sequence'
|
371
352
|
result = evaluate_sequence(aRuntime)
|
353
|
+
# $stderr.puts ' After evaluate_sequence'
|
372
354
|
aRuntime.unnest
|
373
|
-
|
355
|
+
# $stderr.puts " Result: #{result.inspect}"
|
356
|
+
# $stderr.puts "End of lambda #{object_id.to_s(16)}"
|
374
357
|
result
|
358
|
+
=end
|
375
359
|
end
|
376
360
|
|
377
361
|
def arity
|
@@ -385,10 +369,14 @@ module Skeem
|
|
385
369
|
alias eqv? equal?
|
386
370
|
alias skm_equal? equal?
|
387
371
|
|
372
|
+
def bound!(aFrame)
|
373
|
+
set_cond_environment(aFrame)
|
374
|
+
end
|
375
|
+
|
388
376
|
def inspect
|
389
|
-
result = inspect_prefix +
|
390
|
-
result <<
|
391
|
-
result <<
|
377
|
+
result = inspect_prefix + "@object_id=#{object_id.to_s(16)}, "
|
378
|
+
result << inspect_specific
|
379
|
+
result << inspect_suffix
|
392
380
|
result
|
393
381
|
end
|
394
382
|
|
@@ -396,18 +384,18 @@ module Skeem
|
|
396
384
|
[:formals, :definitions, :sequence]
|
397
385
|
end
|
398
386
|
|
399
|
-
|
400
|
-
|
401
|
-
def bind_locals(aRuntime, aProcedureCall)
|
402
|
-
actuals = aProcedureCall.operands.to_a
|
387
|
+
def bind_locals(aRuntime, theActuals)
|
388
|
+
actuals = theActuals
|
403
389
|
count_actuals = actuals.size
|
404
390
|
|
405
391
|
if (count_actuals < required_arity) ||
|
406
392
|
((count_actuals > required_arity) && !formals.variadic?)
|
407
|
-
|
393
|
+
# $stderr.puts "Error"
|
394
|
+
# $stderr.puts self.inspect
|
395
|
+
raise StandardError, msg_arity_mismatch(theActuals)
|
408
396
|
end
|
409
397
|
return if count_actuals.zero? && !formals.variadic?
|
410
|
-
bind_required_locals(aRuntime,
|
398
|
+
bind_required_locals(aRuntime, theActuals)
|
411
399
|
if formals.variadic?
|
412
400
|
variadic_part_raw = actuals.drop(required_arity)
|
413
401
|
variadic_part = variadic_part_raw.map do |actual|
|
@@ -422,14 +410,17 @@ module Skeem
|
|
422
410
|
end
|
423
411
|
variadic_arg_name = formals.formals.last
|
424
412
|
args_coll = SkmPair.create_from_a(variadic_part)
|
425
|
-
a_def = SkmDefinition.new(position, variadic_arg_name, args_coll)
|
413
|
+
# a_def = SkmDefinition.new(position, variadic_arg_name, args_coll)
|
414
|
+
a_def = SkmBinding.new(variadic_arg_name, args_coll)
|
415
|
+
a_def.evaluate(aRuntime)
|
416
|
+
aRuntime.add_binding(a_def.variable, a_def.value)
|
426
417
|
# $stderr.puts "Tef #{a_def.inspect}"
|
427
418
|
# $stderr.puts "Tef #{actuals.inspect}"
|
428
419
|
# $stderr.puts "Tef #{variadic_part.inspect}"
|
429
420
|
# $stderr.puts "Tef #{aProcedureCall.inspect}"
|
430
|
-
a_def.evaluate(aRuntime)
|
421
|
+
# a_def.evaluate(aRuntime)
|
431
422
|
end
|
432
|
-
aProcedureCall.operands_consumed = true
|
423
|
+
#aProcedureCall.operands_consumed = true
|
433
424
|
end
|
434
425
|
|
435
426
|
def evaluate_defs(aRuntime)
|
@@ -440,45 +431,54 @@ module Skeem
|
|
440
431
|
result = nil
|
441
432
|
if sequence
|
442
433
|
sequence.each do |cmd|
|
443
|
-
|
444
|
-
|
445
|
-
|
446
|
-
|
447
|
-
unless aRuntime.caller(caller_index).operands_consumed
|
448
|
-
aRuntime.caller(caller_index).operands_consumed = true
|
449
|
-
result = cmd.call(aRuntime, aRuntime.caller(caller_index))
|
450
|
-
break
|
451
|
-
end
|
452
|
-
caller_index -= 1
|
453
|
-
end
|
454
|
-
else
|
455
|
-
begin
|
434
|
+
begin
|
435
|
+
if cmd.kind_of?(SkmLambda)
|
436
|
+
result = cmd.dup_cond(aRuntime)
|
437
|
+
else
|
456
438
|
result = cmd.evaluate(aRuntime)
|
457
|
-
rescue NoMethodError => exc
|
458
|
-
$stderr.puts self.inspect
|
459
|
-
$stderr.puts sequence.inspect
|
460
|
-
$stderr.puts cmd.inspect
|
461
|
-
raise exc
|
462
439
|
end
|
440
|
+
rescue NoMethodError => exc
|
441
|
+
$stderr.puts self.inspect
|
442
|
+
$stderr.puts sequence.inspect
|
443
|
+
$stderr.puts cmd.inspect
|
444
|
+
raise exc
|
463
445
|
end
|
464
446
|
end
|
465
447
|
end
|
466
448
|
|
467
449
|
result
|
468
450
|
end
|
451
|
+
|
452
|
+
def dup_cond(aRuntime)
|
453
|
+
if environment
|
454
|
+
result = self
|
455
|
+
else
|
456
|
+
twin = self.dup
|
457
|
+
twin.set_cond_environment(aRuntime.environment)
|
458
|
+
result = twin
|
459
|
+
end
|
460
|
+
|
461
|
+
result
|
462
|
+
end
|
463
|
+
|
464
|
+
def set_cond_environment(theFrame)
|
465
|
+
# $stderr.puts "Lambda #{object_id.to_s(16)}, env [#{environment.object_id.to_s(16)}]"
|
466
|
+
# $stderr.puts " Runtime environment: #{theFrame.object_id.to_s(16)}"
|
467
|
+
# $stderr.puts " Called from #{caller(1, 1)}"
|
468
|
+
raise StandardError unless theFrame.kind_of?(SkmFrame)
|
469
|
+
unless environment
|
470
|
+
@environment = theFrame
|
471
|
+
self.freeze
|
472
|
+
# $stderr.puts " Lambda's environment updated!"
|
473
|
+
end
|
474
|
+
end
|
475
|
+
|
476
|
+
private
|
469
477
|
|
470
478
|
# Purpose: bind each formal from lambda to an actual value from the call
|
471
|
-
def bind_required_locals(aRuntime,
|
479
|
+
def bind_required_locals(aRuntime, theActuals)
|
472
480
|
max_index = required_arity - 1
|
473
|
-
|
474
|
-
when SkmPair
|
475
|
-
actuals = aProcedureCall.operands.to_a
|
476
|
-
when SkmEmptyList
|
477
|
-
actuals = []
|
478
|
-
else
|
479
|
-
raise StandardError, "Unsupported type of operand list #{aProcedureCall.operands.inspect}"
|
480
|
-
actuals = aProcedureCall.operands.members
|
481
|
-
end
|
481
|
+
actuals = theActuals
|
482
482
|
formal_names = formals.formals.map(&:value)
|
483
483
|
|
484
484
|
formals.formals.each_with_index do |arg_name, index|
|
@@ -496,25 +496,40 @@ module Skeem
|
|
496
496
|
unless arg.kind_of?(SkmElement)
|
497
497
|
arg = to_datum(arg)
|
498
498
|
end
|
499
|
-
a_def = SkmDefinition.new(position, arg_name, arg)
|
500
|
-
|
499
|
+
# a_def = SkmDefinition.new(position, arg_name, arg)
|
500
|
+
a_def = SkmBinding.new(arg_name, arg)
|
501
|
+
# $stderr.puts "Lambda #{object_id.to_s(16)}"
|
501
502
|
# $stderr.puts "LOCAL #{arg_name.value} #{arg.inspect}"
|
502
503
|
if arg.kind_of?(SkmVariableReference) && !formal_names.include?(arg.value)
|
503
|
-
aRuntime.
|
504
|
+
aRuntime.add_binding(arg_name, a_def)
|
504
505
|
else
|
505
|
-
a_def.evaluate(aRuntime)
|
506
|
+
aRuntime.add_binding(a_def.variable, a_def.evaluate(aRuntime))
|
506
507
|
end
|
507
508
|
break if index >= max_index
|
508
509
|
end
|
509
510
|
end
|
510
511
|
|
511
|
-
def msg_arity_mismatch(
|
512
|
+
def msg_arity_mismatch(actuals)
|
512
513
|
# *** ERROR: wrong number of arguments for #<closure morph> (required 2, got 1)
|
513
|
-
msg1 = "Wrong number of arguments for procedure
|
514
|
-
count_actuals =
|
514
|
+
msg1 = "Wrong number of arguments for procedure "
|
515
|
+
count_actuals = actuals.size
|
515
516
|
msg2 = "(required #{required_arity}, got #{count_actuals})"
|
516
517
|
msg1 + msg2
|
517
518
|
end
|
519
|
+
|
520
|
+
def inspect_specific
|
521
|
+
#result = "@environment #{environment.object_id.to_s(16)}, "
|
522
|
+
result = ''
|
523
|
+
if environment && environment.parent
|
524
|
+
result << "Parent environment #{environment.parent.object_id.to_s(16)}, "
|
525
|
+
result << environment.inspect
|
526
|
+
end
|
527
|
+
result << '@formals ' + formals.inspect + ', '
|
528
|
+
result << '@definitions ' + definitions.inspect + ', '
|
529
|
+
result << '@sequence ' + sequence.inspect + inspect_suffix
|
530
|
+
|
531
|
+
result
|
532
|
+
end
|
518
533
|
end # class
|
519
534
|
end # module
|
520
535
|
# End of file
|