rubylisp 0.1.1 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +82 -0
- data/bin/rubylisp +6 -0
- data/lib/rubylisp/alist.rb +17 -17
- data/lib/rubylisp/assignment.rb +7 -7
- data/lib/rubylisp/builtins.rb +1 -0
- data/lib/rubylisp/character.rb +21 -20
- data/lib/rubylisp/cons_cell.rb +46 -3
- data/lib/rubylisp/debug.rb +238 -0
- data/lib/rubylisp/environment_frame.rb +47 -4
- data/lib/rubylisp/exception.rb +8 -8
- data/lib/rubylisp/ext.rb +4 -0
- data/lib/rubylisp/ffi_class.rb +10 -10
- data/lib/rubylisp/ffi_send.rb +3 -3
- data/lib/rubylisp/frame.rb +51 -31
- data/lib/rubylisp/function.rb +4 -3
- data/lib/rubylisp/io.rb +6 -6
- data/lib/rubylisp/list_support.rb +93 -94
- data/lib/rubylisp/logical.rb +3 -3
- data/lib/rubylisp/macro.rb +4 -2
- data/lib/rubylisp/math.rb +58 -56
- data/lib/rubylisp/object.rb +1 -1
- data/lib/rubylisp/parser.rb +5 -5
- data/lib/rubylisp/primitive.rb +1 -1
- data/lib/rubylisp/relational.rb +4 -4
- data/lib/rubylisp/special_forms.rb +29 -27
- data/lib/rubylisp/string.rb +75 -75
- data/lib/rubylisp/system.rb +3 -2
- data/lib/rubylisp/testing.rb +3 -3
- data/lib/rubylisp/type_checks.rb +10 -8
- data/lib/rubylisp/vector.rb +1 -1
- data/lib/rubylisp.rb +1 -0
- metadata +6 -4
@@ -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
|
-
|
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
|
data/lib/rubylisp/exception.rb
CHANGED
@@ -37,20 +37,20 @@ module Lisp
|
|
37
37
|
frame = frame.parent
|
38
38
|
if frame.nil?
|
39
39
|
exception_message = message.empty? ? "" : ": #{message}"
|
40
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
65
|
-
|
66
|
-
|
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
|
-
|
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
data/lib/rubylisp/ffi_class.rb
CHANGED
@@ -24,17 +24,17 @@ module Lisp
|
|
24
24
|
|
25
25
|
|
26
26
|
def self.extend_impl(args, env)
|
27
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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)
|
data/lib/rubylisp/ffi_send.rb
CHANGED
@@ -46,8 +46,8 @@ module Lisp
|
|
46
46
|
|
47
47
|
def apply_to_without_evaluating(args, env)
|
48
48
|
target = args.car
|
49
|
-
|
50
|
-
|
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
|
-
|
63
|
+
return Lisp::Debug.process_error("Exception sending #{@value}: #{e}", env)
|
64
64
|
end
|
65
65
|
|
66
66
|
convert_value(result)
|
data/lib/rubylisp/frame.rb
CHANGED
@@ -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
|
-
|
51
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
76
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
115
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
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.
|
222
|
+
return parents.any? {|p| p.has_slot_helper(n, v)}
|
210
223
|
end
|
211
224
|
|
212
225
|
|
213
|
-
def
|
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.
|
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
|
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
|
|
data/lib/rubylisp/function.rb
CHANGED
@@ -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
|
-
|
38
|
+
return Lisp::Debug.process_error("#{@name} expected at least #{@arity} parameters, received #{parameters.length}.", env) if parameters.length < @arity
|
39
39
|
else
|
40
|
-
|
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)
|