skeem 0.1.03 → 0.2.00

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: 378523b0249fe580ce581ba771a6fb89e1b9e8845489629b81930adecccf2907
4
- data.tar.gz: de3a44c40bb82810c3dbaa7b97d70de40d8d0149c157bc97fdd7e74c8e4e44cb
3
+ metadata.gz: 4f8a75ad0a0ead374dc7ec59e94abc9edd04826901be7f2e8230c9047c256845
4
+ data.tar.gz: 854f4e2e64dff8224046420ee7ccd035b23cf409c016ecec667206c02a5aae87
5
5
  SHA512:
6
- metadata.gz: 6189770ed0e2cd852d30a80d87ff9947c43a7c43afa64f4673ca1ce7208d5967b864a6e62dc9a20fd9ad0ca3115e6f31e77058038bf7c4f523f110dcae50a162
7
- data.tar.gz: 6fe2efbf1e8618df9af68c08ea62abbb4cb66d52a45019be4f6e5961be418044a60091e41e1c7fad4da27d1d396e23ae4bb2e6fb6b52dca89b8db0f1f395c748
6
+ metadata.gz: ea925e74fbbab68290d6b6cb13e7af3caa735e9f5709e5d9f50423410695d5db0a9c50d077e594e2683e16c7b4f6b85c02b1d72629d97dc6e9348f7a1e32533c
7
+ data.tar.gz: 4930db824c351c76d56f006491f8838e88b64e2e2200fb13412c848c0bc90c6f9effa839983ac3e5bd926e0c0ab162bfb87e3a8217f8d2f14410ce35a4ff2d46
@@ -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
@@ -20,8 +20,8 @@ module Skeem
20
20
 
21
21
  # Keywords...
22
22
  add_terminals('DEFINE', 'IF', 'LAMBDA')
23
- add_terminals('QUOTE', 'QUASIQUOTE', 'UNQUOTE')
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'
@@ -1,5 +1,5 @@
1
1
  require_relative 'parser'
2
- require_relative 'environment'
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(Environment.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 run(source, mode = nil)
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' if mode.nil?
23
- return @ptree.root.evaluate(runtime)
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
- private
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
- create_assert(aRuntime)
125
+ create_test_assert(aRuntime)
127
126
  create_debug(aRuntime)
128
- end
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 create_assert(aRuntime)
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.define(aKey, anEntry)
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
- def call(aRuntime, aProcedureCall)
19
- actuals = aProcedureCall.operands.to_a
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
- aProcedureCall.operands_consumed = true
22
- do_call(aRuntime, actuals)
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
@@ -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 [Environment]
7
- attr_reader(:environment)
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
- @environment = anEnvironment
14
- @call_stack = []
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?(normalize_key(anIdentifier))
22
+ environment.include?(anIdentifier)
19
23
  end
20
24
 
21
25
  def fetch(aKey)
22
- key_value = normalize_key(aKey)
23
- include?(key_value) ? environment.fetch(key_value) : nil
26
+ include?(aKey) ? environment.fetch(aKey) : nil
24
27
  end
25
28
 
26
- def define(aKey, anEntry)
27
- environment.define(normalize_key(aKey), anEntry)
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
- case entry
37
- when Primitive::PrimitiveProcedure
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 = Environment.new(environment)
70
- @environment = nested
70
+ nested = SkmFrame.new(environment)
71
+ push(nested)
71
72
  end
72
73
 
73
- def unnest()
74
- raise StandardError, 'Cannot unnest environment' unless environment.outer
74
+ def unnest
75
+ raise StandardError, 'Cannot unnest environment' unless environment.parent
75
76
  environment.bindings.clear
76
- @environment = environment.outer
77
+ pop
77
78
  end
78
79
 
79
- def depth()
80
- return environment.depth
80
+ def depth
81
+ return env_stack.size
81
82
  end
82
83
 
83
84
  def push(anEnvironment)
84
- @environment = anEnvironment
85
+ env_stack.push(anEnvironment)
85
86
  end
86
87
 
87
- # Make the outer enviromnent thecurrent one inside the provided block
88
+ # Make the parent frame the current one inside the provided block
88
89
  def pop
89
- env = environment
90
- @environment = environment.outer
91
- env
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
@@ -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, aRange, _tokens, theChildren)
64
- SkmDefinition.new(aRange, theChildren[2], theChildren[3])
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
- SkmDefinition.new(aRange, theChildren[3], lmbd)
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)
@@ -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
- :UNDEFINED
13
+ self
14
14
  end
15
15
 
16
16
  def ==(other)
17
- return true if other.kind_of?(SkmUndefined)
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
- if operator.kind_of?(SkmLambda)
159
- callee = operator
160
- else
161
- var_key = operator.evaluate(aRuntime)
162
- if operator.kind_of?(ProcedureCall)
163
- return var_key if operands_consumed
164
- end
165
- if var_key.kind_of?(Primitive::PrimitiveProcedure)
166
- callee = var_key
167
- else
168
- begin
169
- aRuntime.include?(var_key.value)
170
- rescue NoMethodError => exc
171
- # $stderr.puts "VVVVVVVVVVVVVVV"
172
- # $stderr.puts 'var_key: ' + var_key.inspect
173
- # $stderr.puts 'operator: ' + operator.inspect
174
- # $stderr.puts 'operands: ' + operands.inspect
175
- # $stderr.puts 'operands_consumed: ' + operands_consumed.inspect
176
- # $stderr.puts "^^^^^^^^^^^^^^^"
177
- raise exc
178
- end
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 "## RETURN #{result.inspect} from #{var_key.value}"
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, aProcedureCall)
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 + '@formals ' + formals.inspect + ', '
390
- result << '@definitions ' + definitions.inspect + ', '
391
- result << '@sequence ' + sequence.inspect + inspect_suffix
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
- private
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
- raise StandardError, msg_arity_mismatch(aProcedureCall)
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, aProcedureCall)
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
- if cmd.kind_of?(SkmLambda)
444
- caller_index = -2
445
- loop do
446
- raise StandardError, "Missing argument" unless aRuntime.caller(caller_index)
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, aProcedureCall)
479
+ def bind_required_locals(aRuntime, theActuals)
472
480
  max_index = required_arity - 1
473
- case aProcedureCall.operands
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
- # $stderr.puts "Procedure call #{aProcedureCall.operator.inspect}"
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.define(arg_name, a_def)
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(aProcCall)
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 #{aProcCall.operator} "
514
- count_actuals = aProcCall.operands.members.size
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