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