rubylisp 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/bin/rubylisp +14 -0
- data/lib/rubylisp/alist.rb +230 -0
- data/lib/rubylisp/assignment.rb +65 -0
- data/lib/rubylisp/atom.rb +149 -0
- data/lib/rubylisp/binding.rb +17 -0
- data/lib/rubylisp/boolean.rb +49 -0
- data/lib/rubylisp/builtins.rb +31 -0
- data/lib/rubylisp/character.rb +383 -0
- data/lib/rubylisp/cons_cell.rb +255 -0
- data/lib/rubylisp/environment_frame.rb +116 -0
- data/lib/rubylisp/equivalence.rb +118 -0
- data/lib/rubylisp/exception.rb +98 -0
- data/lib/rubylisp/ext.rb +122 -0
- data/lib/rubylisp/ffi_class.rb +162 -0
- data/lib/rubylisp/ffi_new.rb +32 -0
- data/lib/rubylisp/ffi_send.rb +83 -0
- data/lib/rubylisp/ffi_static.rb +22 -0
- data/lib/rubylisp/frame.rb +284 -0
- data/lib/rubylisp/function.rb +92 -0
- data/lib/rubylisp/io.rb +74 -0
- data/lib/rubylisp/list_support.rb +527 -0
- data/lib/rubylisp/logical.rb +38 -0
- data/lib/rubylisp/macro.rb +95 -0
- data/lib/rubylisp/math.rb +403 -0
- data/lib/rubylisp/number.rb +63 -0
- data/lib/rubylisp/object.rb +62 -0
- data/lib/rubylisp/parser.rb +184 -0
- data/lib/rubylisp/primitive.rb +45 -0
- data/lib/rubylisp/relational.rb +46 -0
- data/lib/rubylisp/special_forms.rb +454 -0
- data/lib/rubylisp/string.rb +841 -0
- data/lib/rubylisp/symbol.rb +56 -0
- data/lib/rubylisp/system.rb +19 -0
- data/lib/rubylisp/testing.rb +136 -0
- data/lib/rubylisp/tokenizer.rb +292 -0
- data/lib/rubylisp/type_checks.rb +58 -0
- data/lib/rubylisp/vector.rb +114 -0
- data/lib/rubylisp.rb +1 -0
- metadata +82 -0
@@ -0,0 +1,56 @@
|
|
1
|
+
module Lisp
|
2
|
+
|
3
|
+
class Symbol < Atom
|
4
|
+
|
5
|
+
def self.named(n)
|
6
|
+
name = n.to_s
|
7
|
+
@@symbols ||= {}
|
8
|
+
return @@symbols[n] if @@symbols.has_key?(n)
|
9
|
+
s = self.new(n)
|
10
|
+
@@symbols[n] = s
|
11
|
+
s
|
12
|
+
end
|
13
|
+
|
14
|
+
def initialize(n)
|
15
|
+
@value = n
|
16
|
+
@naked = (n[-1] == ?:)
|
17
|
+
end
|
18
|
+
|
19
|
+
def to_naked
|
20
|
+
Lisp::Symbol.named("#{@value}:")
|
21
|
+
end
|
22
|
+
|
23
|
+
|
24
|
+
def name
|
25
|
+
@value
|
26
|
+
end
|
27
|
+
|
28
|
+
def symbol?
|
29
|
+
true
|
30
|
+
end
|
31
|
+
|
32
|
+
def naked?
|
33
|
+
@naked
|
34
|
+
end
|
35
|
+
|
36
|
+
|
37
|
+
def type
|
38
|
+
:symbol
|
39
|
+
end
|
40
|
+
|
41
|
+
def evaluate(env)
|
42
|
+
return self if @naked
|
43
|
+
env.value_of(self)
|
44
|
+
end
|
45
|
+
|
46
|
+
def to_s
|
47
|
+
@value
|
48
|
+
end
|
49
|
+
|
50
|
+
def to_sym
|
51
|
+
@value.to_sym
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
55
|
+
|
56
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Lisp
|
2
|
+
|
3
|
+
class System
|
4
|
+
|
5
|
+
def self.register
|
6
|
+
Primitive.register("sleep") {|args, env| Lisp::System.sleep_impl(args, env) }
|
7
|
+
end
|
8
|
+
|
9
|
+
|
10
|
+
def self.sleep_impl(args, env)
|
11
|
+
raise "sleep needs 1 argument" if args.length != 1
|
12
|
+
arg = args.car.evaluate(env)
|
13
|
+
raise "sleep needs a numeric argument" unless arg.number?
|
14
|
+
sleep(arg.value)
|
15
|
+
end
|
16
|
+
|
17
|
+
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,136 @@
|
|
1
|
+
module Lisp
|
2
|
+
|
3
|
+
class Testing
|
4
|
+
|
5
|
+
|
6
|
+
def self.register
|
7
|
+
Primitive.register("describe") {|args, env| Lisp::Testing::describe_impl(args, env) }
|
8
|
+
Primitive.register("check") {|args, env| Lisp::Testing::check_impl(args, env) }
|
9
|
+
Primitive.register("check!") {|args, env| Lisp::Testing::check_not_impl(args, env) }
|
10
|
+
Primitive.register("check*") {|args, env| Lisp::Testing::check_star_impl(args, env) }
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.describe_impl(args, env)
|
14
|
+
raise "First arg to describe must be a string or symbol" if !args.car.symbol? && !args.car.string?
|
15
|
+
return if args.cdr.nil?
|
16
|
+
puts
|
17
|
+
puts " #{args.car.to_s}"
|
18
|
+
args.cdr.each do |clause|
|
19
|
+
clause.evaluate(env)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.do_comparison(c1, c2)
|
24
|
+
return Lisp::TRUE if c1.nil? && c2.nil?
|
25
|
+
return Lisp::FALSE if c1.nil? || c2.nil?
|
26
|
+
Lisp::Boolean.with_value(c1.eq?(c2))
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.eval_check(code, c1, c2, inverted)
|
30
|
+
check_result = self.do_comparison(c1, c2)
|
31
|
+
passed = inverted ? check_result.negate : check_result
|
32
|
+
|
33
|
+
if passed.true?
|
34
|
+
@@number_of_passes += 1
|
35
|
+
puts "ok"
|
36
|
+
else
|
37
|
+
@@number_of_fails += 1
|
38
|
+
message = "failed: #{code.print_string} is #{c1.print_string}, expected #{inverted ? 'not ' : ''}#{c2.print_string}"
|
39
|
+
puts message
|
40
|
+
@@failure_messages << message
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
|
45
|
+
def self.unary_check(name,sexpr, env, inverted)
|
46
|
+
print " (#{name} #{sexpr.print_string}) - "
|
47
|
+
c1 = sexpr.evaluate(env)
|
48
|
+
self.eval_check(sexpr, c1, inverted ? Lisp::FALSE : Lisp::TRUE, false)
|
49
|
+
end
|
50
|
+
|
51
|
+
|
52
|
+
def self.binary_check(name, sexpr_1, sexpr_2, env, inverted)
|
53
|
+
print " (#{name} "
|
54
|
+
print "#{sexpr_1.print_string} "
|
55
|
+
print "#{sexpr_2.print_string}) - "
|
56
|
+
c1 = sexpr_1.evaluate(env)
|
57
|
+
c2 = sexpr_2.evaluate(env)
|
58
|
+
self.eval_check(sexpr_1, c1, c2, inverted)
|
59
|
+
end
|
60
|
+
|
61
|
+
|
62
|
+
def self.check_impl(args, env, inverted=false)
|
63
|
+
@@number_of_tests += 1
|
64
|
+
name = inverted ? "check!" : "check"
|
65
|
+
Lisp::Boolean.with_value(case (args.length)
|
66
|
+
when 1
|
67
|
+
self.unary_check(name, args.car, env, inverted)
|
68
|
+
when 2
|
69
|
+
self.binary_check(name, args.car, args.cadr, env, inverted)
|
70
|
+
else
|
71
|
+
raise "check takes 1 or 2 arguments, received #{args.length}"
|
72
|
+
end)
|
73
|
+
end
|
74
|
+
|
75
|
+
|
76
|
+
def self.check_not_impl(args, env)
|
77
|
+
self.check_impl(args, env, true)
|
78
|
+
end
|
79
|
+
|
80
|
+
|
81
|
+
def self.check_star_impl(args, env)
|
82
|
+
raise "check* needs 2 arguments, received #{args.length}" if args.length != 2
|
83
|
+
@@number_of_tests += 1
|
84
|
+
print " (check* #{args.car.print_string} #{args.cadr.print_string}) - "
|
85
|
+
|
86
|
+
c1 = args.car.evaluate(env)
|
87
|
+
c2 = args.cadr
|
88
|
+
self.eval_check(args.car, c1, c2, false)
|
89
|
+
end
|
90
|
+
|
91
|
+
def self.init
|
92
|
+
@@number_of_tests = 0
|
93
|
+
@@number_of_fails = 0
|
94
|
+
@@number_of_passes = 0
|
95
|
+
@@number_of_errors = 0
|
96
|
+
@@failure_messages = []
|
97
|
+
@@error_messages = []
|
98
|
+
end
|
99
|
+
|
100
|
+
def self.dump_messages(header, messages)
|
101
|
+
return if messages.empty?
|
102
|
+
puts " #{header}:"
|
103
|
+
messages.each do |message|
|
104
|
+
puts " #{message}"
|
105
|
+
end
|
106
|
+
puts ""
|
107
|
+
end
|
108
|
+
|
109
|
+
def self.print_test_results
|
110
|
+
puts ""
|
111
|
+
puts " Done."
|
112
|
+
puts ""
|
113
|
+
|
114
|
+
dump_messages("Errors", @@error_messages)
|
115
|
+
dump_messages("Failures", @@failure_messages)
|
116
|
+
|
117
|
+
puts " #{@@number_of_tests} Lisp tests"
|
118
|
+
puts " #{@@number_of_passes} passes, #{@@number_of_fails} fails, #{@@number_of_errors} errors"
|
119
|
+
end
|
120
|
+
|
121
|
+
def self.run_tests
|
122
|
+
register
|
123
|
+
init
|
124
|
+
Dir[File.dirname(__FILE__) + '/../../lisptest/*_test.lsp'].each do |test_filename|
|
125
|
+
puts "\nLoading #{test_filename}"
|
126
|
+
File.open(test_filename) do |f|
|
127
|
+
code_string = f.read()
|
128
|
+
Parser.new.parse_and_eval_all(code_string)
|
129
|
+
end
|
130
|
+
end
|
131
|
+
print_test_results
|
132
|
+
@@number_of_errors == 0 && @@number_of_fails == 0
|
133
|
+
end
|
134
|
+
|
135
|
+
end
|
136
|
+
end
|
@@ -0,0 +1,292 @@
|
|
1
|
+
module Lisp
|
2
|
+
|
3
|
+
class Tokenizer
|
4
|
+
|
5
|
+
attr_reader :line_number
|
6
|
+
|
7
|
+
def initialize(src)
|
8
|
+
@lookahead_token = 0
|
9
|
+
@lookahead_literal = ''
|
10
|
+
@source = src
|
11
|
+
@position = 0
|
12
|
+
end
|
13
|
+
|
14
|
+
def next_token
|
15
|
+
return @lookahead_token, @lookahead_literal
|
16
|
+
end
|
17
|
+
|
18
|
+
def eof?
|
19
|
+
@position >= @source.length
|
20
|
+
end
|
21
|
+
|
22
|
+
def almost_eof?
|
23
|
+
@position == @source.length - 1
|
24
|
+
end
|
25
|
+
|
26
|
+
def next_char
|
27
|
+
almost_eof? ? nil : @source[@position + 1]
|
28
|
+
end
|
29
|
+
|
30
|
+
def letter?(ch)
|
31
|
+
ch =~ /[[:alpha:]]/
|
32
|
+
end
|
33
|
+
|
34
|
+
def hex?(ch)
|
35
|
+
ch =~ /[abcdefABCDEF]/
|
36
|
+
end
|
37
|
+
|
38
|
+
def digit?(ch)
|
39
|
+
ch =~ /[[:digit:]]/
|
40
|
+
end
|
41
|
+
|
42
|
+
def number?(ch)
|
43
|
+
digit?(ch) || (ch == '-' && digit?(next_char))
|
44
|
+
end
|
45
|
+
|
46
|
+
|
47
|
+
def space?(ch)
|
48
|
+
ch =~ /[[:space:]]/
|
49
|
+
end
|
50
|
+
|
51
|
+
|
52
|
+
def symbol_character?(ch)
|
53
|
+
return true if letter?(ch)
|
54
|
+
return true if (?0..?9).include?(ch)
|
55
|
+
return "-_?!:*=<>".include?(ch)
|
56
|
+
end
|
57
|
+
|
58
|
+
def read_symbol
|
59
|
+
start = @position
|
60
|
+
tok = nil
|
61
|
+
if @source[@position] == '.'
|
62
|
+
@position += 1
|
63
|
+
tok = :FFI_SEND_SYMBOL
|
64
|
+
end
|
65
|
+
|
66
|
+
while !eof? && (symbol_character?(@source[@position]) ||
|
67
|
+
(@source[@position] == '.' && !symbol_character?(@source[@position+1]) && tok.nil?) ||
|
68
|
+
(@source[@position] == '/' && symbol_character?(@source[@position+1])))
|
69
|
+
tok ||= :FFI_NEW_SYMBOL if @source[@position] == '.'
|
70
|
+
tok = :FFI_STATIC_SYMBOL if @source[@position] == '/'
|
71
|
+
@position += 1
|
72
|
+
end
|
73
|
+
|
74
|
+
tok ||= :SYMBOL
|
75
|
+
return tok, case tok
|
76
|
+
when :SYMBOL, :FFI_STATIC_SYMBOL
|
77
|
+
@source[start...@position]
|
78
|
+
when :FFI_SEND_SYMBOL
|
79
|
+
@source[start+1...@position]
|
80
|
+
when :FFI_NEW_SYMBOL
|
81
|
+
@source[start...@position-1]
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
def read_number
|
86
|
+
start = @position
|
87
|
+
@position += 1 if @source[@position] == '-'
|
88
|
+
hex = @source[@position, 2] == "#x"
|
89
|
+
is_float = false
|
90
|
+
@position += 2 if hex
|
91
|
+
ch = @source[@position]
|
92
|
+
while !eof? && (digit?(ch) || (hex && hex?(ch)) || (!hex && !is_float && ch == ?.))
|
93
|
+
is_float ||= (ch == ?.)
|
94
|
+
@position += 1
|
95
|
+
ch = @source[@position]
|
96
|
+
end
|
97
|
+
|
98
|
+
tok = if hex
|
99
|
+
:HEXNUMBER
|
100
|
+
elsif is_float
|
101
|
+
:FLOAT
|
102
|
+
else
|
103
|
+
:NUMBER
|
104
|
+
end
|
105
|
+
|
106
|
+
return tok, @source[start...@position]
|
107
|
+
end
|
108
|
+
|
109
|
+
|
110
|
+
def process_escapes(str)
|
111
|
+
i = 0
|
112
|
+
processed_str = ""
|
113
|
+
while i < str.length
|
114
|
+
if str[i] == ?\\
|
115
|
+
processed_str << if i < (str.length - 1)
|
116
|
+
i += 1
|
117
|
+
case (str[i])
|
118
|
+
when ?n
|
119
|
+
"\n"
|
120
|
+
when ?t
|
121
|
+
"\t"
|
122
|
+
when ?\\
|
123
|
+
"\\"
|
124
|
+
else
|
125
|
+
"\\#{str[i]}"
|
126
|
+
end
|
127
|
+
else
|
128
|
+
"\\"
|
129
|
+
end
|
130
|
+
else
|
131
|
+
processed_str << str[i]
|
132
|
+
end
|
133
|
+
i += 1
|
134
|
+
end
|
135
|
+
processed_str
|
136
|
+
end
|
137
|
+
|
138
|
+
|
139
|
+
def read_string
|
140
|
+
start = @position
|
141
|
+
@position += 1
|
142
|
+
while !eof? && @source[@position] != ?"
|
143
|
+
@position += 1
|
144
|
+
end
|
145
|
+
|
146
|
+
return :EOF, '' if eof?
|
147
|
+
@position += 1
|
148
|
+
return :STRING, process_escapes(@source[start...@position])
|
149
|
+
end
|
150
|
+
|
151
|
+
|
152
|
+
def divider?(ch)
|
153
|
+
ch =~ /[[[:space:]]\(\)\{\}<>\[\]]/
|
154
|
+
end
|
155
|
+
|
156
|
+
|
157
|
+
def read_character
|
158
|
+
@position += 2
|
159
|
+
start = @position
|
160
|
+
@position += 1
|
161
|
+
while !eof? && !divider?(@source[@position])
|
162
|
+
@position += 1
|
163
|
+
end
|
164
|
+
|
165
|
+
return :CHARACTER, @source[start...@position]
|
166
|
+
end
|
167
|
+
|
168
|
+
|
169
|
+
def read_next_token
|
170
|
+
return :EOF, '' if eof?
|
171
|
+
|
172
|
+
while space?(@source[@position])
|
173
|
+
@line_number += 1 if @source[@position] == ?\n
|
174
|
+
@position += 1
|
175
|
+
return :EOF, '' if eof?
|
176
|
+
end
|
177
|
+
|
178
|
+
current_ch = @source[@position]
|
179
|
+
next_ch = @source[@position + 1] unless almost_eof?
|
180
|
+
|
181
|
+
if letter?(current_ch) || ('*._'.include?(current_ch) && letter?(next_ch))
|
182
|
+
return read_symbol
|
183
|
+
elsif number?(current_ch)
|
184
|
+
return read_number
|
185
|
+
elsif current_ch == ?- && number?(next_ch)
|
186
|
+
return read_number
|
187
|
+
elsif current_ch == ?# && next_ch == ?x
|
188
|
+
return read_number
|
189
|
+
elsif current_ch == ?"
|
190
|
+
return read_string
|
191
|
+
elsif current_ch == ?# && next_ch == ?\\
|
192
|
+
return read_character
|
193
|
+
elsif current_ch == ?'
|
194
|
+
@position += 1
|
195
|
+
return :QUOTE, "'"
|
196
|
+
elsif current_ch == ?`
|
197
|
+
@position += 1
|
198
|
+
return :BACKQUOTE, "`"
|
199
|
+
elsif current_ch == ?, && next_ch == ?@
|
200
|
+
@position += 2
|
201
|
+
return :COMMAAT, ",@"
|
202
|
+
elsif current_ch == ?,
|
203
|
+
@position += 1
|
204
|
+
return :COMMA, ","
|
205
|
+
elsif current_ch == ?(
|
206
|
+
@position += 1
|
207
|
+
return :LPAREN, "("
|
208
|
+
elsif current_ch == ?)
|
209
|
+
@position += 1
|
210
|
+
return :RPAREN, ")"
|
211
|
+
elsif current_ch == ?{
|
212
|
+
@position += 1
|
213
|
+
return :LBRACE, "{"
|
214
|
+
elsif current_ch == ?}
|
215
|
+
@position += 1
|
216
|
+
return :RBRACE, "}"
|
217
|
+
elsif current_ch == ?[
|
218
|
+
@position += 1
|
219
|
+
return :LBRACKET, "["
|
220
|
+
elsif current_ch == ?]
|
221
|
+
@position += 1
|
222
|
+
return :RBRACKET, "]"
|
223
|
+
elsif current_ch == ?.
|
224
|
+
@position += 1
|
225
|
+
return :PERIOD, "."
|
226
|
+
elsif current_ch == ?/ && next_ch == ?=
|
227
|
+
@position += 2
|
228
|
+
return :SYMBOL, "!="
|
229
|
+
elsif current_ch == ?- && next_ch == ?>
|
230
|
+
@position += 2
|
231
|
+
return :SYMBOL, "->"
|
232
|
+
elsif current_ch == ?= && next_ch == ?>
|
233
|
+
@position += 2
|
234
|
+
return :SYMBOL, "=>"
|
235
|
+
elsif "+-*/%".include?(current_ch)
|
236
|
+
@position += 1
|
237
|
+
return :SYMBOL, current_ch.to_s
|
238
|
+
elsif current_ch == ?< && next_ch == ?=
|
239
|
+
@position += 2
|
240
|
+
return :SYMBOL, "<="
|
241
|
+
elsif current_ch == ?<
|
242
|
+
@position += 1
|
243
|
+
return :SYMBOL, "<"
|
244
|
+
elsif current_ch == ?> && next_ch == ?=
|
245
|
+
@position += 2
|
246
|
+
return :SYMBOL, ">="
|
247
|
+
elsif current_ch == ?>
|
248
|
+
@position += 1
|
249
|
+
return :SYMBOL, ">"
|
250
|
+
elsif current_ch == ?= && next_ch == ?=
|
251
|
+
@position += 2
|
252
|
+
return :SYMBOL, "="
|
253
|
+
elsif current_ch == ?=
|
254
|
+
@position += 1
|
255
|
+
return :SYMBOL, "="
|
256
|
+
elsif current_ch == ?! && next_ch == ?=
|
257
|
+
@position += 2
|
258
|
+
return :SYMBOL, "!="
|
259
|
+
elsif current_ch == ?!
|
260
|
+
@position += 1
|
261
|
+
return :SYMBOL, "!"
|
262
|
+
elsif current_ch == ?# && next_ch == ?t
|
263
|
+
@position += 2
|
264
|
+
return :TRUE, "#t"
|
265
|
+
elsif current_ch == ?# && next_ch == ?f
|
266
|
+
@position += 2
|
267
|
+
return :FALSE, "#f"
|
268
|
+
elsif current_ch == ?;
|
269
|
+
start = @position
|
270
|
+
while true
|
271
|
+
return :COMMENT, @source[start..-1] if eof?
|
272
|
+
return :COMMENT, @source[start...@position] if @source[@position] == ?\n
|
273
|
+
@position += 1
|
274
|
+
end
|
275
|
+
else
|
276
|
+
return :ILLEGAL, ''
|
277
|
+
end
|
278
|
+
end
|
279
|
+
|
280
|
+
def consume_token
|
281
|
+
@lookahead_token, @lookahead_literal = read_next_token
|
282
|
+
consume_token if @lookahead_token == :COMMENT
|
283
|
+
end
|
284
|
+
|
285
|
+
def init
|
286
|
+
@line_number = 0
|
287
|
+
consume_token
|
288
|
+
end
|
289
|
+
|
290
|
+
end
|
291
|
+
|
292
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
module Lisp
|
2
|
+
|
3
|
+
class TypeChecks
|
4
|
+
|
5
|
+
def self.register
|
6
|
+
Primitive.register("list?") {|args, env| Lisp::TypeChecks::typep_impl("list?", :pair, args, env) }
|
7
|
+
Primitive.register("pair?") {|args, env| Lisp::TypeChecks::typep_impl("pair?", :pair, args, env) }
|
8
|
+
Primitive.register("symbol?") {|args, env| Lisp::TypeChecks::typep_impl("symbol?", :symbol, args, env) }
|
9
|
+
Primitive.register("number?") {|args, env| Lisp::TypeChecks::typep_impl("number?", :number, args, env) }
|
10
|
+
Primitive.register("integer?") {|args, env| Lisp::TypeChecks::integerp_impl(args, env) }
|
11
|
+
Primitive.register("float?") {|args, env| Lisp::TypeChecks::floatp_impl(args, env) }
|
12
|
+
Primitive.register("function?") {|args, env| Lisp::TypeChecks::functionp_impl(args, env) }
|
13
|
+
|
14
|
+
Primitive.register("nil?") {|args, env| Lisp::TypeChecks::nilp_impl(args, env) }
|
15
|
+
Primitive.register("not-nil?") {|args, env| Lisp::TypeChecks::not_nilp_impl(args, env) }
|
16
|
+
end
|
17
|
+
|
18
|
+
|
19
|
+
def self.typep_impl(name, sym, args, env)
|
20
|
+
raise "#{name} needs 1 argument" unless args.length == 1
|
21
|
+
return Lisp::Boolean.with_value(args.car.evaluate(env).type == sym)
|
22
|
+
end
|
23
|
+
|
24
|
+
|
25
|
+
def self.integerp_impl(args, env)
|
26
|
+
raise "integer? needs 1 argument" unless args.length == 1
|
27
|
+
val = args.car.evaluate(env)
|
28
|
+
return Lisp::Boolean.with_value(val.type == :number && val.integer?)
|
29
|
+
end
|
30
|
+
|
31
|
+
|
32
|
+
def self.floatp_impl(args, env)
|
33
|
+
raise "float? needs 1 argument" unless args.length == 1
|
34
|
+
val = args.car.evaluate(env)
|
35
|
+
return Lisp::Boolean.with_value(val.type == :number && val.float?)
|
36
|
+
end
|
37
|
+
|
38
|
+
|
39
|
+
def self.functionp_impl(args, env)
|
40
|
+
raise "function? needs 1 argument" unless args.length == 1
|
41
|
+
val = args.car.evaluate(env)
|
42
|
+
return Lisp::Boolean.with_value(val.type == :function || val.type == :primitive)
|
43
|
+
end
|
44
|
+
|
45
|
+
|
46
|
+
def self.nilp_impl(args, env)
|
47
|
+
raise "nil? needs 1 argument" unless args.length == 1
|
48
|
+
return Lisp::Boolean.with_value(args.car.evaluate(env).nil?)
|
49
|
+
end
|
50
|
+
|
51
|
+
|
52
|
+
def self.not_nilp_impl(args, env)
|
53
|
+
raise "not-nil? needs 1 argument" unless args.length == 1
|
54
|
+
return Lisp::Boolean.with_value(!args.car.evaluate(env).nil?)
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,114 @@
|
|
1
|
+
module Lisp
|
2
|
+
|
3
|
+
class Vector < Atom
|
4
|
+
|
5
|
+
def self.register
|
6
|
+
Primitive.register("make-vector") {|args, env| Lisp::Vector::make_vector_impl(args, env) }
|
7
|
+
Primitive.register("vector") {|args, env| Lisp::Vector::vector_impl(args, env) }
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.make_vector_impl(args, env)
|
11
|
+
c = args
|
12
|
+
a = []
|
13
|
+
while !c.nil?
|
14
|
+
a << c.car.evaluate(env)
|
15
|
+
c = c.cdr
|
16
|
+
end
|
17
|
+
|
18
|
+
Lisp::Vector.new(a)
|
19
|
+
end
|
20
|
+
|
21
|
+
|
22
|
+
def self.vector_impl(args, env)
|
23
|
+
raise "vector requires a single list argument." unless args.length == 1
|
24
|
+
|
25
|
+
c = args.car.evaluate(env)
|
26
|
+
Lisp::Vector.new(c.to_a)
|
27
|
+
end
|
28
|
+
|
29
|
+
|
30
|
+
def self.with_array(a)
|
31
|
+
self.new(a)
|
32
|
+
end
|
33
|
+
|
34
|
+
|
35
|
+
def initialize(a = [])
|
36
|
+
@value = a
|
37
|
+
self
|
38
|
+
end
|
39
|
+
|
40
|
+
|
41
|
+
def type
|
42
|
+
:vector
|
43
|
+
end
|
44
|
+
|
45
|
+
|
46
|
+
def vector?
|
47
|
+
true
|
48
|
+
end
|
49
|
+
|
50
|
+
|
51
|
+
|
52
|
+
def empty?
|
53
|
+
@value.empty?
|
54
|
+
end
|
55
|
+
|
56
|
+
|
57
|
+
def length
|
58
|
+
@value.size
|
59
|
+
end
|
60
|
+
|
61
|
+
|
62
|
+
def add(e)
|
63
|
+
@value << e
|
64
|
+
end
|
65
|
+
|
66
|
+
|
67
|
+
def to_a
|
68
|
+
@value
|
69
|
+
end
|
70
|
+
|
71
|
+
|
72
|
+
def to_s
|
73
|
+
"[#{@value.join(' ')}]"
|
74
|
+
end
|
75
|
+
|
76
|
+
|
77
|
+
def at(n)
|
78
|
+
@value[n - 1]
|
79
|
+
end
|
80
|
+
alias_method :nth, :at
|
81
|
+
|
82
|
+
|
83
|
+
def nth_tail(n)
|
84
|
+
return Lisp::Vector.new if n > @value.size
|
85
|
+
Lisp::Vector.new(@value[(n - 1)..-1])
|
86
|
+
end
|
87
|
+
|
88
|
+
|
89
|
+
def at_put(n, d)
|
90
|
+
@value[n - 1] = d
|
91
|
+
end
|
92
|
+
|
93
|
+
|
94
|
+
def set_nth!(n, d)
|
95
|
+
at_put(n, d)
|
96
|
+
end
|
97
|
+
|
98
|
+
|
99
|
+
def eq?(other)
|
100
|
+
return false unless other.vector?
|
101
|
+
return false unless @value.size == other.value.size
|
102
|
+
(0..@value.size).each do |i|
|
103
|
+
return false unless Lisp::Equivalence.equal_check(other.value[i], value[i]).value
|
104
|
+
end
|
105
|
+
true
|
106
|
+
end
|
107
|
+
|
108
|
+
def each &block
|
109
|
+
@value.each &block
|
110
|
+
end
|
111
|
+
|
112
|
+
end
|
113
|
+
|
114
|
+
end
|
data/lib/rubylisp.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
Dir[File.dirname(__FILE__) + '/rubylisp/*.rb'].each {|file| require file }
|