rubylisp 0.1.1 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,238 @@
1
+ # Copyright 2014 David R. Astels. All rights reserved.
2
+ # Use of this source code is governed by a BSD-style
3
+ # license that can be found in the LICENSE file.
4
+
5
+
6
+ module Lisp
7
+
8
+ class Debug
9
+
10
+ class <<self
11
+ attr_accessor :trace, :on_error, :on_entry, :single_step, :interactive, :target_env, :eval_in_debug_repl
12
+ end
13
+
14
+
15
+ def self.register
16
+ self.trace = false
17
+ self.on_error = false
18
+ self.on_entry = Set.new
19
+ self.single_step = false
20
+ self.interactive = false
21
+
22
+ Primitive.register("debug-trace") {|args, env| Lisp::Debug::debug_trace_impl(args, env) }
23
+ Primitive.register("debug-on-error") {|args, env| Lisp::Debug::debug_on_error_impl(args, env) }
24
+ Primitive.register("debug-on-entry") {|args, env| Lisp::Debug::debug_on_entry_impl(args, env) }
25
+ Primitive.register("add-debug-on-entry") {|args, env| Lisp::Debug::add_debug_on_entry_impl(args, env) }
26
+ Primitive.register("remove-debug-on-entry") {|args, env| Lisp::Debug::remove_debug_on_entry_impl(args, env) }
27
+ Primitive.register("debug") {|args, env| Lisp::Debug::debug_impl(args, env) }
28
+ Primitive.register("dump") {|args, env| Lisp::Debug::dump_imp2l(args, env) }
29
+ end
30
+
31
+
32
+ def self.debug_trace_impl(args, env)
33
+ return Lisp::Debug.process_error("debug-trace requires 1 argument", env) unless args.length == 1
34
+ flag = args.car.evaluate(env)
35
+ return Lisp::Debug.process_error("the argument to debug-trace has to be a boolean", env) unless flag.boolean?
36
+ self.trace = flag.value
37
+ flag
38
+ end
39
+
40
+ def self.debug_on_error_impl(args, env)
41
+ return Lisp::Debug.process_error("debug-on-error requires 1 argument", env) unless args.length == 1
42
+ flag = args.car.evaluate(env)
43
+ return Lisp::Debug.process_error("the argument to debug-on-error has to be a boolean", env) unless flag.boolean?
44
+ self.on_error = flag.value
45
+ flag
46
+ end
47
+
48
+ def self.debug_on_entry_impl(args, env)
49
+ Lisp::ConsCell.array_to_list(self.on_entry.to_a.sort.map {|s| Lisp::String.with_value(s) })
50
+ end
51
+
52
+ def self.add_debug_on_entry_impl(args, env)
53
+ return Lisp::Debug.process_error("add-debug-on-error requires 1 argument", env) unless args.length == 1
54
+ f = args.car.evaluate(env)
55
+ return Lisp::Debug.process_error("the argument to add-debug-on-error has to be a function", env) unless f.function? || f.primitive?
56
+
57
+ self.on_entry.add(f.name)
58
+ f
59
+ end
60
+
61
+ def self.remove_debug_on_entry_impl(args, env)
62
+ return Lisp::Debug.process_error("remove-debug-on-error requires 1 argument", env) unless args.length == 1
63
+ f = args.car.evaluate(env)
64
+ return Lisp::Debug.process_error("the argument to remove-debug-on-error has to be a function", env) unless f.function?
65
+
66
+ self.on_entry.remove(f.name)
67
+ f
68
+ end
69
+
70
+ def self.debug_impl(args, env)
71
+ end
72
+
73
+ def self.dump_impl(args, env)
74
+ env.dump()
75
+ end
76
+
77
+
78
+ def self.process_state(tokens)
79
+ if tokens.size != 2
80
+ puts "Missing on/off"
81
+ [false, false]
82
+ else
83
+ case tokens[1]
84
+ when 'on'
85
+ [true, true]
86
+ when 'off'
87
+ [true, false]
88
+ else
89
+ puts "on/off expected."
90
+ [false, false]
91
+ end
92
+ end
93
+ end
94
+
95
+
96
+ def func_or_nil(fname, env)
97
+ f = env.value_of(Lisp::Symbol.named(fname))
98
+ if f.nil? || !f.function?
99
+ puts "No such function."
100
+ nil
101
+ else
102
+ f
103
+ end
104
+ end
105
+
106
+
107
+ def self.debug_repl(env)
108
+ parser = Lisp::Parser.new
109
+ puts("Debugging: #{env.current_code[0]}")
110
+ while line = Readline.readline('D> ', true)
111
+ if !line.empty?
112
+ if line[0] == ':'
113
+ tokens = line[1..-1].split
114
+ case tokens[0]
115
+ when '(+'
116
+ f = func_or_nil(tokens[1], env)
117
+ self.on_entry.add(f.name) unless f.nil?
118
+ when '(-'
119
+ f = func_or_nil(tokens[1], env)
120
+ self.on_entry.delete(f.name) unless f.nil?
121
+ when '('
122
+ self.on_entry.to_a.sort.each {|f| puts f}
123
+ when '?'
124
+ puts "RubyLisp Debugger"
125
+ puts "-----------------"
126
+ puts ":(+ func - debug on entry to func"
127
+ puts ":(- func - don't debug on entry to func"
128
+ puts ":( - show functions marked as debug on entry"
129
+ puts ":? - show this command summary"
130
+ puts ":b - show the environment stack"
131
+ puts ":c - continue, exiting the debugger"
132
+ puts ":d - do a full of the environment stack"
133
+ puts ":e on/off - Enable/disable debug on error"
134
+ puts ":f frame# - do a full dump of a single environment frame"
135
+ puts ":q - quit GoLisp"
136
+ puts ":r sexpr - return from the current evaluation with the specified value"
137
+ puts ":s - single step (run to the next evaluation)"
138
+ puts ":t on/off - Enable/disable tracing"
139
+ puts ":u - continue until the enclosing environment frame is returned to"
140
+ puts
141
+ when 'b'
142
+ env.dump_headers()
143
+ puts
144
+ when 'c'
145
+ self.target_env = nil
146
+ self.single_step = false
147
+ self.eval_in_debug_repl = false
148
+ return
149
+ when 'd'
150
+ env.dump
151
+ when 'e'
152
+ ok, state = process_state(tokens)
153
+ self.on_error = state if ok
154
+ when 'f'
155
+ if tokens.size != 2
156
+ puts "Missing frame number."
157
+ else
158
+ fnum = tokens[1].to_i
159
+ env.dump_single_frame(fnum)
160
+ end
161
+ when 'q'
162
+ exit()
163
+ when 'r'
164
+ self.eval_in_debug_repl = true
165
+ code = parser.parse(tokens[1..-1].join(' '))
166
+ return_value = code.evaluate(env)
167
+ self.eval_in_debug_repl = false
168
+ self.target_env = nil
169
+ self.single_step = false
170
+ self.eval_in_debug_repl = false
171
+ return return_value
172
+ when 's'
173
+ self.single_step = true
174
+ return
175
+ when 't'
176
+ ok, state = process_state(tokens)
177
+ self.trace = state if ok
178
+ when 'u'
179
+ if env.previous.nil?
180
+ puts "Already at top frame."
181
+ else
182
+ self.target_env = env
183
+ return
184
+ end
185
+ end
186
+ else
187
+ begin
188
+ self.eval_in_debug_repl = true
189
+ code = parser.parse(line)
190
+ value = code.evaluate(env)
191
+ self.eval_in_debug_repl = false
192
+ puts value.to_s
193
+ rescue Exception => ex
194
+ puts "ERROR: #{ex}"
195
+ puts ex.backtrace
196
+ end
197
+ end
198
+ end
199
+ end
200
+ end
201
+
202
+ def self.process_error(error_message, env)
203
+ if self.on_error && self.interactive
204
+ puts "ERROR: #{error_message}"
205
+ self.debug_repl(env)
206
+ else
207
+ raise error_message
208
+ end
209
+ end
210
+
211
+
212
+ def self.print_dashes(level)
213
+ print("-" * level)
214
+ end
215
+
216
+
217
+ def self.log_eval(sexpr, env)
218
+ if !self.eval_in_debug_repl && self.trace
219
+ depth = env.depth
220
+ print("% #d: " % depth)
221
+ print_dashes(depth)
222
+ puts("> #{sexpr.to_s}")
223
+ end
224
+ end
225
+
226
+
227
+ def self.log_result(result, env)
228
+ if !self.eval_in_debug_repl && self.trace
229
+ depth = env.depth
230
+ print("% #d: <" % depth)
231
+ print_dashes(depth)
232
+ puts(" #{result.to_s}")
233
+ end
234
+ end
235
+
236
+
237
+ end
238
+ end
@@ -2,7 +2,7 @@ module Lisp
2
2
 
3
3
  class EnvironmentFrame
4
4
 
5
- attr_accessor :frame
5
+ attr_accessor :frame, :current_code, :previous
6
6
 
7
7
  def self.global
8
8
  @@global_frame ||= EnvironmentFrame.new(nil)
@@ -18,6 +18,7 @@ module Lisp
18
18
  @bindings = []
19
19
  @parent = parent
20
20
  @frame = f
21
+ @current_code = []
21
22
  end
22
23
 
23
24
  def has_frame?
@@ -103,14 +104,56 @@ module Lisp
103
104
  b = binding_for(Symbol.new(symbol_name))
104
105
  b.nil? ? nil : b.value
105
106
  end
106
-
107
107
 
108
- def dump
108
+
109
+ def dump_bindings
109
110
  @bindings.each do |b|
110
- puts b.to_s
111
+ puts b.to_s if b.value.nil? || !b.value.primitive?
111
112
  end
113
+ puts
114
+ end
115
+
116
+
117
+ def dump(frame_number=0)
118
+ puts "Frame #{frame_number}: #{@current_code[0]}"
119
+ dump_bindings
120
+ @previous.dump(frame_number + 1) unless @previous.nil?
121
+ end
122
+
123
+
124
+ def dump_single_frame(frame_number)
125
+ if frame_number == 0
126
+ puts "Evaling: #{@current_code[0]}"
127
+ dump_bindings
128
+ elsif !@previous.nil?
129
+ @previous.dump_single_frame(frame_number - 1)
130
+ else
131
+ puts "Invalid frame selected."
132
+ end
133
+ end
134
+
135
+ def internal_dump_headers(frame_number)
136
+ puts "Frame #{frame_number}: #{@current_code[0]}"
137
+ @previous.internal_dump_headers(frame_number + 1) unless @previous.nil?
138
+ end
139
+
140
+
141
+
142
+ def dump_headers
143
+ puts
144
+ internal_dump_headers(0)
112
145
  end
113
146
 
147
+
148
+ def depth
149
+ if @previous.nil?
150
+ 1
151
+ else
152
+ 1 + @previous.depth
153
+ end
154
+ end
155
+
114
156
  end
115
157
 
158
+
116
159
  end
@@ -37,20 +37,20 @@ module Lisp
37
37
  frame = frame.parent
38
38
  if frame.nil?
39
39
  exception_message = message.empty? ? "" : ": #{message}"
40
- raise "Unhandled Exception: #{exception_name}#{exception_message}"
40
+ return Lisp::Debug.process_error("Unhandled Exception: #{exception_name}#{exception_message}", env)
41
41
  end
42
42
  end
43
43
  end
44
44
 
45
45
  def self.raise_impl(args, env)
46
- raise "'raise' requires at least one argument." unless args.length > 0
46
+ return Lisp::Debug.process_error("'raise' requires at least one argument.", env) unless args.length > 0
47
47
  exception_name = args.car.evaluate(env)
48
- raise "'raise' requires an exception name as it's first argument." unless exception_name.string? || class_name.symbol?
48
+ return Lisp::Debug.process_error("'raise' requires an exception name as it's first argument.", env) unless exception_name.string? || class_name.symbol?
49
49
 
50
50
  message = ""
51
51
  if args.length > 1
52
52
  message = args.cadr.evaluate(env)
53
- raise "The message parameter to 'raise' must be a string." unless message.string?
53
+ return Lisp::Debug.process_error("The message parameter to 'raise' must be a string.", env) unless message.string?
54
54
  end
55
55
 
56
56
  handler_args = args.length > 2 ? Lisp::ConsCell.array_to_list(args.cdr.to_a.collect {|a| a.evaluate(env)}) : Lisp::ConsCell.new
@@ -61,9 +61,9 @@ module Lisp
61
61
  raw_handlers = args.car
62
62
  body = args.cdr
63
63
 
64
- raise "Exception handlers must be a list." unless raw_handlers.list?
65
- raise "Exception handlers must be a list of pairs." unless raw_handlers.all? {|h| h.list?}
66
- raise "Exception clause must be a symbol/string or a list of symbol/string." unless raw_handlers.all? do |h|
64
+ return Lisp::Debug.process_error("Exception handlers must be a list.", env) unless raw_handlers.list?
65
+ return Lisp::Debug.process_error("Exception handlers must be a list of pairs.", env) unless raw_handlers.all? {|h| h.list?}
66
+ return Lisp::Debug.process_error("Exception clause must be a symbol/string or a list of symbol/string.", env) unless raw_handlers.all? do |h|
67
67
  ex = h.car
68
68
  ex.symbol? || ex.string? || (ex.list? && ex.all? {|h2| h2.symbol? || h2.string?})
69
69
  end
@@ -71,7 +71,7 @@ module Lisp
71
71
  array_of_handlers = raw_handlers.to_a.collect do |h|
72
72
  ex = h.car
73
73
  f = h.cadr.evaluate(env)
74
- raise "Exception handler has to be a function." unless f.function?
74
+ return Lisp::Debug.process_error("Exception handler has to be a function.", env) unless f.function?
75
75
  Lisp::ConsCell.cons(ex, f)
76
76
  end
77
77
  handlers = Lisp::ConsCell.array_to_list(array_of_handlers)
data/lib/rubylisp/ext.rb CHANGED
@@ -20,6 +20,10 @@ class NilClass
20
20
  self.to_s
21
21
  end
22
22
 
23
+ def print_string_helper
24
+ ""
25
+ end
26
+
23
27
  def evaluate(env)
24
28
  nil
25
29
  end
@@ -24,17 +24,17 @@ module Lisp
24
24
 
25
25
 
26
26
  def self.extend_impl(args, env)
27
- raise "'extend' requires 2 arguments." if args.length != 2
27
+ return Lisp::Debug.process_error("'extend' requires 2 arguments.", env) if args.length != 2
28
28
 
29
29
  class_name = args.car.evaluate(env)
30
- raise "'extend' requires a name as it's first argument." unless class_name.string? || class_name.symbol?
30
+ return Lisp::Debug.process_error("'extend' requires a name as it's first argument.", env) unless class_name.string? || class_name.symbol?
31
31
  super_class = Object.const_get(class_name.to_s)
32
- raise "'extend' requires the name of an existing class as it's first argument." if super_class.nil?
32
+ return Lisp::Debug.process_error("'extend' requires the name of an existing class as it's first argument.", env) if super_class.nil?
33
33
 
34
34
  new_class_name = args.cadr.evaluate(env)
35
- raise "'extend' requires a name as it's second argument." unless new_class_name.string? || new_class_name.symbol?
35
+ return Lisp::Debug.process_error("'extend' requires a name as it's second argument.", env) unless new_class_name.string? || new_class_name.symbol?
36
36
  new_class = Class.new(super_class)
37
- raise "'extend' requires the name of a new (i.e. nonexistant) class as it's second argument." if Object.const_defined?(new_class_name.to_s)
37
+ return Lisp::Debug.process_error("'extend' requires the name of a new (i.e. nonexistant) class as it's second argument.", env) if Object.const_defined?(new_class_name.to_s)
38
38
 
39
39
  Object.const_set(new_class_name.to_s, new_class)
40
40
  ClassObject.with_class(new_class)
@@ -77,17 +77,17 @@ module Lisp
77
77
 
78
78
 
79
79
  def self.add_method_impl(args, env)
80
- raise "'add-method' requires 3 arguments." if args.length != 3
80
+ return Lisp::Debug.process_error("'add-method' requires 3 arguments.", env) if args.length != 3
81
81
  class_name = args.car.evaluate(env)
82
- raise "'add-method' requires a class name as it's first argument." unless class_name.string? || class_name.symbol?
82
+ return Lisp::Debug.process_error("'add-method' requires a class name as it's first argument.", env) unless class_name.string? || class_name.symbol?
83
83
  target_class = Object.const_get(class_name.to_s)
84
- raise "'add-method' requires the name of an existing class." if target_class.nil?
84
+ return Lisp::Debug.process_error("'add-method' requires the name of an existing class.", env) if target_class.nil?
85
85
 
86
86
  method_name = args.cadr.evaluate(env)
87
- raise "'add-method' requires a method name as it's second argument." unless class_name.string? || class_name.symbol?
87
+ return Lisp::Debug.process_error("'add-method' requires a method name as it's second argument.", env) unless class_name.string? || class_name.symbol?
88
88
 
89
89
  body = args.caddr.evaluate(env)
90
- raise "'add-method' requires a function as it's third argument." unless body.function?
90
+ return Lisp::Debug.process_error("'add-method' requires a function as it's third argument.", env) unless body.function?
91
91
 
92
92
  target_class.send(:define_method, method_name.to_s) do |*args|
93
93
  local_env = Lisp::EnvironmentFrame.extending(env)
@@ -46,8 +46,8 @@ module Lisp
46
46
 
47
47
  def apply_to_without_evaluating(args, env)
48
48
  target = args.car
49
- raise "Send target of '#{@value}' evaluated to nil." if target.nil?
50
- raise "Target of an FFI send of '#{@value}' must be a wrapped ObjC object, was #{target}" unless target.object?
49
+ return Lisp::Debug.process_error("Send target of '#{@value}' evaluated to nil.", env) if target.nil?
50
+ return Lisp::Debug.process_error("Target of an FFI send of '#{@value}' must be a wrapped ObjC object, was #{target}", env) unless target.object?
51
51
 
52
52
  arguments = args.cdr.nil? ? [] : args.cdr.to_a.map {|a| process_arg(a, env)}
53
53
  result = nil
@@ -60,7 +60,7 @@ module Lisp
60
60
  target.value.send(@value, *arguments)
61
61
  end
62
62
  rescue Exception => e
63
- raise "Exception sending #{@value}: #{e}"
63
+ return Lisp::Debug.process_error("Exception sending #{@value}: #{e}", env)
64
64
  end
65
65
 
66
66
  convert_value(result)
@@ -39,6 +39,10 @@ module Lisp
39
39
  Lisp::Frame::clone_impl(args, env)
40
40
  end
41
41
 
42
+ Primitive.register("keys", "(keys frame)\n\nReturn a list of the keys in the frame.") do |args, env|
43
+ Lisp::Frame::keys_impl(args, env)
44
+ end
45
+
42
46
  end
43
47
 
44
48
 
@@ -47,8 +51,8 @@ module Lisp
47
51
  m = {}
48
52
  while !c.nil?
49
53
  k = c.car
50
- raise "Slot names must be a symbol, found a {k.type}." unless k.symbol?
51
- raise "Slot names must end in a colon, found '#{k}'." unless k.naked?
54
+ return Lisp::Debug.process_error("Slot names must be a symbol, found a {k.type}.", env) unless k.symbol?
55
+ return Lisp::Debug.process_error("Slot names must end in a colon, found '#{k}'.", env) unless k.naked?
52
56
  v = c.cadr.evaluate(env)
53
57
  m[k] = v
54
58
  c = c.cddr
@@ -60,9 +64,9 @@ module Lisp
60
64
 
61
65
  def self.has_slot_impl(args, env)
62
66
  frame = args.car.evaluate(env)
63
- raise "Frame data must be a frame but was #{frame.type}." unless frame.frame?
67
+ return Lisp::Debug.process_error("Frame data must be a frame but was #{frame.type}.", env) unless frame.frame?
64
68
  key = args.cadr.evaluate(env)
65
- raise "Frame key must be a symbol but was #{key.type}." unless key.symbol?
69
+ return Lisp::Debug.process_error("Frame key must be a symbol but was #{key.type}.", env) unless key.symbol?
66
70
  return Lisp::TRUE if frame.has_slot?(key)
67
71
  Lisp::FALSE
68
72
  end
@@ -70,28 +74,28 @@ module Lisp
70
74
 
71
75
  def self.get_slot_impl(args, env)
72
76
  frame = args.car.evaluate(env)
73
- raise "Frame data must be a frame but was #{frame.type}." unless frame.frame?
77
+ return Lisp::Debug.process_error("Frame data must be a frame but was #{frame.type}.", env) unless frame.frame?
74
78
  key = args.cadr.evaluate(env)
75
- raise "Frame key (#{key.to_s}) must be a symbol but was #{key.type}." unless key.symbol?
76
- raise "Frame key (#{key.to_s}) must name an existing slot." unless frame.has_slot?(key)
79
+ return Lisp::Debug.process_error("Frame key (#{key.to_s}) must be a symbol but was #{key.type}.", env) unless key.symbol?
80
+ return Lisp::Debug.process_error("Frame key (#{key.to_s}) must name an existing slot.", env) unless frame.has_slot?(key)
77
81
  frame.get(key)
78
82
  end
79
83
 
80
84
 
81
85
  def self.get_slot_if_impl(args, env)
82
86
  frame = args.car.evaluate(env)
83
- raise "Frame data must be a frame but was #{frame.type}." unless frame.frame?
87
+ return Lisp::Debug.process_error("Frame data must be a frame but was #{frame.type}.", env) unless frame.frame?
84
88
  key = args.cadr.evaluate(env)
85
- raise "Frame key (#{key.to_s}) must be a symbol but was #{key.type}." unless key.symbol?
89
+ return Lisp::Debug.process_error("Frame key (#{key.to_s}) must be a symbol but was #{key.type}.", env) unless key.symbol?
86
90
  frame.get(key)
87
91
  end
88
92
 
89
93
 
90
94
  def self.remove_slot_impl(args, env)
91
95
  frame = args.car.evaluate(env)
92
- raise "Frame data must be a frame but was #{frame.type}." unless frame.frame?
96
+ return Lisp::Debug.process_error("Frame data must be a frame but was #{frame.type}.", env) unless frame.frame?
93
97
  key = args.cadr.evaluate(env)
94
- raise "Frame key (#{key.to_s}) must be a symbol but was #{key.type}." unless key.symbol?
98
+ return Lisp::Debug.process_error("Frame key (#{key.to_s}) must be a symbol but was #{key.type}.", env) unless key.symbol?
95
99
  return Lisp::TRUE if frame.remove(key)
96
100
  Lisp::FALSE
97
101
  end
@@ -99,9 +103,9 @@ module Lisp
99
103
 
100
104
  def self.set_slot_impl(args, env)
101
105
  frame = args.car.evaluate(env)
102
- raise "Frame data must be a frame but was #{frame.type}." unless frame.frame?
106
+ return Lisp::Debug.process_error("Frame data must be a frame but was #{frame.type}.", env) unless frame.frame?
103
107
  key = args.cadr.evaluate(env)
104
- raise "Frame key (#{key.to_s}) must be a symbol but was #{key.type}." unless key.symbol?
108
+ return Lisp::Debug.process_error("Frame key (#{key.to_s}) must be a symbol but was #{key.type}.", env) unless key.symbol?
105
109
  value = args.caddr.evaluate(env)
106
110
  frame.at_put(key, value)
107
111
  end
@@ -109,12 +113,12 @@ module Lisp
109
113
 
110
114
  def self.send_impl(args, env)
111
115
  frame = args.car.evaluate(env)
112
- raise "Frame data must be a frame but was #{frame.type}." unless frame.frame?
116
+ return Lisp::Debug.process_error("Frame data must be a frame but was #{frame.type}.", env) unless frame.frame?
113
117
  selector = args.cadr.evaluate(env)
114
- raise "Selector must be a symbol but was #{selector.type}." unless selector.symbol?
115
- raise "Message sent must name an existing slot in the receiver." unless frame.has_slot?(selector)
118
+ return Lisp::Debug.process_error("Selector must be a symbol but was #{selector.type}.", env) unless selector.symbol?
119
+ return Lisp::Debug.process_error("Message sent must name an existing slot in the receiver.", env) unless frame.has_slot?(selector)
116
120
  func = frame.get(selector)
117
- raise "Message sent must select a function slot but was #{func.type}." unless func.function?
121
+ return Lisp::Debug.process_error("Message sent must select a function slot but was #{func.type}.", env) unless func.function?
118
122
  params = args.cddr
119
123
  frame_env = Lisp::EnvironmentFrame.extending(env, frame)
120
124
  frame_env.bind_locally(Symbol.named("self"), frame)
@@ -132,11 +136,11 @@ module Lisp
132
136
  end
133
137
 
134
138
  def self.send_super_impl(args, env)
135
- raise "super can only be used within the context of a frame." unless env.frame
139
+ return Lisp::Debug.process_error("super can only be used within the context of a frame.", env) unless env.frame
136
140
  selector = args.car.evaluate(env)
137
- raise "Selector must be a symbol but was #{selector.type}." unless selector.symbol?
141
+ return Lisp::Debug.process_error("Selector must be a symbol but was #{selector.type}.", env) unless selector.symbol?
138
142
  func = get_super_function(selector, env)
139
- raise "Message sent must select a function slot but was #{func.type}." unless func && func.function?
143
+ return Lisp::Debug.process_error("Message sent must select a function slot but was #{func.type}.", env) unless func && func.function?
140
144
  params = args.cdr
141
145
  frame_env = Lisp::EnvironmentFrame.extending(env, env.frame)
142
146
  frame_env.bind_locally(Symbol.named("self"), env.frame)
@@ -146,11 +150,18 @@ module Lisp
146
150
 
147
151
  def self.clone_impl(args, env)
148
152
  frame = args.car.evaluate(env)
149
- raise "Frame data must be a frame but was #{frame.type}." unless frame.frame?
153
+ return Lisp::Debug.process_error("Frame data must be a frame but was #{frame.type}.", env) unless frame.frame?
150
154
  frame.clone
151
155
  end
152
156
 
153
157
 
158
+ def self.keys_impl(args, env)
159
+ frame = args.car.evaluate(env)
160
+ return Lisp::Debug.process_error("Frame data must be a frame but was #{frame.type}.", env) unless frame.frame?
161
+ ConsCell.array_to_list(frame.value.keys)
162
+ end
163
+
164
+
154
165
  def self.with_map(m)
155
166
  self.new(m)
156
167
  end
@@ -203,23 +214,37 @@ module Lisp
203
214
  end
204
215
 
205
216
 
206
- def has_slot?(n)
217
+ def has_slot_helper(n, v)
218
+ return false if v.include?(self)
219
+ v << self
207
220
  return true if has_slot_locally?(n)
208
221
  return false unless has_parent_slots?
209
- return parents.any? {|p| p.has_slot?(n)}
222
+ return parents.any? {|p| p.has_slot_helper(n, v)}
210
223
  end
211
224
 
212
225
 
213
- def get(key)
226
+ def has_slot?(n)
227
+ has_slot_helper(n, Set.new)
228
+ end
229
+
230
+
231
+ def get_helper(key, v)
232
+ return nil if v.include?(self)
233
+ v << self
214
234
  return @value[key] if has_slot_locally?(key)
215
235
  parents.each do |p|
216
- value = p.get(key)
236
+ value = p.get_helper(key, v)
217
237
  return value unless value.nil?
218
238
  end
219
239
  nil
220
240
  end
221
241
 
222
242
 
243
+ def get(key)
244
+ get_helper(key, Set.new)
245
+ end
246
+
247
+
223
248
  def remove(key)
224
249
  return false unless has_slot_locally?(key)
225
250
  @value.delete(key)
@@ -228,12 +253,7 @@ module Lisp
228
253
 
229
254
 
230
255
  def at_put(key, value)
231
- return @value[key] = value if !has_slot?(key) || has_slot_locally?(key)
232
- parents.each do |p|
233
- v = p.at_put(key, value)
234
- return v unless v.nil?
235
- end
236
- nil
256
+ return @value[key] = value
237
257
  end
238
258
 
239
259
 
@@ -2,7 +2,7 @@ module Lisp
2
2
 
3
3
  class Function < Atom
4
4
 
5
- attr_reader :doc, :arity
5
+ attr_reader :doc, :arity, :name
6
6
 
7
7
  def compute_required_argument_count(args)
8
8
  a = args
@@ -35,12 +35,13 @@ module Lisp
35
35
 
36
36
  def internal_apply_to(parameters, env, should_eval)
37
37
  if @var_args
38
- raise "#{@name} expected at least #{@arity} parameters, received #{parameters.length}." if parameters.length < @arity
38
+ return Lisp::Debug.process_error("#{@name} expected at least #{@arity} parameters, received #{parameters.length}.", env) if parameters.length < @arity
39
39
  else
40
- raise "#{@name} expected #{@arity} parameters, received #{parameters.length}." unless parameters.length == @arity
40
+ return Lisp::Debug.process_error("#{@name} expected #{@arity} parameters, received #{parameters.length}.", env) unless parameters.length == @arity
41
41
  end
42
42
 
43
43
  local_env = EnvironmentFrame.extending(@env, env.frame)
44
+ local_env.previous = env
44
45
  self_sym = Symbol.named("self")
45
46
  if env.frame
46
47
  local_env.bind_locally(self_sym, env.frame)