rubylisp 0.1.1 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- 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)
|