rubylisp 0.1.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 +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 }
|