rubylisp 0.2.1 → 1.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (59) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +129 -2
  3. data/bin/rubylisp +87 -12
  4. data/lib/rubylisp/atom.rb +25 -6
  5. data/lib/rubylisp/boolean.rb +9 -6
  6. data/lib/rubylisp/builtins.rb +19 -18
  7. data/lib/rubylisp/character.rb +14 -275
  8. data/lib/rubylisp/class_object.rb +56 -0
  9. data/lib/rubylisp/cons_cell.rb +56 -25
  10. data/lib/rubylisp/debug.rb +15 -19
  11. data/lib/rubylisp/environment.rb +27 -0
  12. data/lib/rubylisp/environment_frame.rb +31 -6
  13. data/lib/rubylisp/eof_object.rb +26 -0
  14. data/lib/rubylisp/exception.rb +61 -61
  15. data/lib/rubylisp/ext.rb +32 -6
  16. data/lib/rubylisp/ffi_new.rb +2 -1
  17. data/lib/rubylisp/ffi_send.rb +15 -5
  18. data/lib/rubylisp/frame.rb +5 -164
  19. data/lib/rubylisp/function.rb +4 -3
  20. data/lib/rubylisp/macro.rb +13 -8
  21. data/lib/rubylisp/{object.rb → native_object.rb} +0 -15
  22. data/lib/rubylisp/number.rb +5 -0
  23. data/lib/rubylisp/parser.rb +81 -52
  24. data/lib/rubylisp/port.rb +27 -0
  25. data/lib/rubylisp/prim_alist.rb +115 -0
  26. data/lib/rubylisp/prim_assignment.rb +61 -0
  27. data/lib/rubylisp/prim_character.rb +273 -0
  28. data/lib/rubylisp/{ffi_class.rb → prim_class_object.rb} +16 -69
  29. data/lib/rubylisp/prim_environment.rb +203 -0
  30. data/lib/rubylisp/prim_equivalence.rb +93 -0
  31. data/lib/rubylisp/prim_frame.rb +166 -0
  32. data/lib/rubylisp/prim_io.rb +266 -0
  33. data/lib/rubylisp/prim_list_support.rb +496 -0
  34. data/lib/rubylisp/{logical.rb → prim_logical.rb} +9 -14
  35. data/lib/rubylisp/prim_math.rb +397 -0
  36. data/lib/rubylisp/prim_native_object.rb +21 -0
  37. data/lib/rubylisp/prim_relational.rb +42 -0
  38. data/lib/rubylisp/{special_forms.rb → prim_special_forms.rb} +98 -85
  39. data/lib/rubylisp/prim_string.rb +792 -0
  40. data/lib/rubylisp/prim_system.rb +55 -0
  41. data/lib/rubylisp/prim_type_checks.rb +58 -0
  42. data/lib/rubylisp/prim_vector.rb +497 -0
  43. data/lib/rubylisp/primitive.rb +51 -6
  44. data/lib/rubylisp/string.rb +4 -803
  45. data/lib/rubylisp/symbol.rb +0 -1
  46. data/lib/rubylisp/tokenizer.rb +161 -137
  47. data/lib/rubylisp/vector.rb +10 -31
  48. data/lib/rubylisp.rb +1 -0
  49. metadata +46 -17
  50. data/lib/rubylisp/alist.rb +0 -230
  51. data/lib/rubylisp/assignment.rb +0 -65
  52. data/lib/rubylisp/equivalence.rb +0 -118
  53. data/lib/rubylisp/io.rb +0 -74
  54. data/lib/rubylisp/list_support.rb +0 -526
  55. data/lib/rubylisp/math.rb +0 -405
  56. data/lib/rubylisp/relational.rb +0 -46
  57. data/lib/rubylisp/system.rb +0 -20
  58. data/lib/rubylisp/testing.rb +0 -136
  59. data/lib/rubylisp/type_checks.rb +0 -60
@@ -1,230 +0,0 @@
1
- module Lisp
2
-
3
- class AList < Object
4
- include Enumerable
5
-
6
- def self.register
7
- Primitive.register("acons") {|args, env| Lisp::AList::acons_impl(args, env) }
8
- Primitive.register("assoc") {|args, env| Lisp::AList::assoc_impl(args, env) }
9
- Primitive.register("rassoc") {|args, env| Lisp::AList::rassoc_impl(args, env) }
10
- Primitive.register("dissoc") {|args, env| Lisp::AList::dissoc_impl(args, env) }
11
- Primitive.register("zip") {|args, env| Lisp::AList::zip_impl(args, env) }
12
- Primitive.register("alist-to-list") {|args, env| Lisp::AList::alist_to_list_impl(args, env) }
13
- Primitive.register("list-to-alist") {|args, env| Lisp::AList::list_to_alist_impl(args, env) }
14
- end
15
-
16
- def self.acons_impl(args, env)
17
- return Lisp::Debug.process_error("acons require at least 2 or 3 arguments", env) unless args.length == 2 || args.length == 3
18
- key = args.car.evaluate(env)
19
- value = args.cadr.evaluate(env)
20
- alist = args.length == 2 ? nil : args.caddr.evaluate(env)
21
- alist = Lisp::AList.from_list(alist) if !alist.nil? && alist.list? && !alist.alist?
22
- return Lisp::Debug.process_error("the last argument to acons has to be an association list", env) unless alist.nil? || alist.alist?
23
-
24
- if alist.nil?
25
- Lisp::AList.new({key => value})
26
- else
27
- alist.acons(key, value)
28
- end
29
- end
30
-
31
- def self.assoc_impl(args, env)
32
- return Lisp::Debug.process_error("assoc require 2 arguments", env) unless args.length == 2
33
- key = args.car.evaluate(env)
34
- alist = args.cadr.evaluate(env)
35
- alist = Lisp::AList.from_list(alist) if alist.list? && !alist.alist?
36
- return Lisp::Debug.process_error("the last argument to assoc has to be an association list", env) unless alist.alist?
37
- alist.assoc(key)
38
- end
39
-
40
- def self.rassoc_impl(args, env)
41
- return Lisp::Debug.process_error("assoc require 2 arguments", env) unless args.length == 2
42
- value = args.car.evaluate(env)
43
- alist = args.cadr.evaluate(env)
44
- alist = Lisp::AList.from_list(alist) if alist.list? && !alist.alist?
45
- return Lisp::Debug.process_error("the last argument to rassoc has to be an association list", env) unless alist.alist?
46
- alist.rassoc(value)
47
- end
48
-
49
- def self.dissoc_impl(args, env)
50
- return Lisp::Debug.process_error("assoc require 2 arguments", env) unless args.length == 2
51
- key = args.car.evaluate(env)
52
- alist = args.cadr.evaluate(env)
53
- alist = Lisp::AList.from_list(alist) if alist.list? && !alist.alist?
54
- return Lisp::Debug.process_error("the last argument to dissoc has to be an association list", env) unless alist.alist?
55
- alist.dissoc(key)
56
- end
57
-
58
- def self.zip_impl(args, env)
59
- return Lisp::Debug.process_error("assoc require 2 or 3arguments", env) unless args.length == 2 || args.length == 3
60
- key_list = args.car.evaluate(env)
61
- return Lisp::Debug.process_error("the keys supplied to zip has to be a list", env) unless key_list.list?
62
- value_list = args.cadr.evaluate(env)
63
- return Lisp::Debug.process_error("the values supplied to zip has to be a list", env) unless value_list.list?
64
- return Lisp::Debug.process_error("zip requires the same number of keys and values", env) unless key_list.length == value_list.length
65
-
66
- if args.length == 3
67
- alist = args.caddr.evaluate(env)
68
- alist = Lisp::AList.from_list(alist) if alist.list? && !alist.alist?
69
- return Lisp::Debug.process_error("the third argument to zip has to be an association list", env) unless alist.alist?
70
- alist.zip(key_list.to_a, value_list.to_a)
71
- else
72
- Lisp::AList.zip(key_list.to_a, value_list.to_a)
73
- end
74
- end
75
-
76
- def self.alist_to_list_impl(args, env)
77
- return Lisp::Debug.process_error("alist-to-list requires 1 arguments", env) unless args.length == 1
78
- alist = args.car.evaluate(env)
79
- return alist if alist.list? && !alist.alist?
80
- return Lisp::Debug.process_error("the argument to alist-to-list has to be an association list", env) unless alist.alist?
81
-
82
- alist.to_list
83
- end
84
-
85
- def self.list_to_alist_impl(args, env)
86
- return Lisp::Debug.process_error("list-to-alist requires 1 arguments", env) unless args.length == 1
87
- list = args.car.evaluate(env)
88
- return Lisp::Debug.process_error("the argument to list-to-alist has to be a list", env) unless list.list?
89
-
90
- Lisp::AList.from_list(list)
91
- end
92
-
93
-
94
- def initialize(h=nil)
95
- @value = h || {}
96
- end
97
-
98
- def lisp_object?
99
- true
100
- end
101
-
102
- def eq?(sexpr)
103
- return false unless sexpr.alist?
104
- @value == sexpr.value
105
- end
106
-
107
- def empty?
108
- @value.empty?
109
- end
110
-
111
- def string?
112
- false
113
- end
114
-
115
- def character?
116
- false
117
- end
118
-
119
- def number?
120
- false
121
- end
122
-
123
- def positive?
124
- false
125
- end
126
-
127
- def zero?
128
- false
129
- end
130
-
131
- def negative?
132
- false
133
- end
134
-
135
- def symbol?
136
- false
137
- end
138
-
139
- def primitive?
140
- false
141
- end
142
-
143
- def function?
144
- false
145
- end
146
-
147
- def macro?
148
- false
149
- end
150
-
151
- def pair?
152
- true
153
- end
154
-
155
- def list?
156
- true
157
- end
158
-
159
- def alist?
160
- true
161
- end
162
-
163
- def frame?
164
- false
165
- end
166
-
167
- def length
168
- return @value.length
169
- end
170
-
171
- def car
172
- @value.first
173
- end
174
-
175
- def cdr
176
- end
177
-
178
- def acons(k, v)
179
- @value[k] = v
180
- self
181
- end
182
-
183
- def assoc(k)
184
- v = @value[k]
185
- Lisp::ConsCell.cons(k, v)
186
- end
187
-
188
- def rassoc(value)
189
- @value.each do |k, v|
190
- return Lisp::ConsCell.cons(k, v) if value == v || value.eq?(v)
191
- end
192
- end
193
-
194
- def dissoc(k)
195
- @value.delete(k)
196
- self
197
- end
198
-
199
- def zip(keys, values)
200
- keys.zip(values).each {|k, v| @value[k] = v}
201
- self
202
- end
203
-
204
- def self.zip(keys, values)
205
- self.new.zip(keys, values)
206
- end
207
-
208
- def self.from_list(list)
209
- h = {}
210
- list.each do |pair|
211
- h[pair.car] = pair.cdr
212
- end
213
- self.new(h)
214
- end
215
-
216
- def to_list
217
- Lisp::ConsCell.array_to_list(@value.map {|k, v| Lisp::ConsCell.cons(k, v)})
218
- end
219
-
220
- def to_s
221
- to_list.to_s
222
- end
223
-
224
- def print_string
225
- self.to_s
226
- end
227
-
228
- end
229
-
230
- end
@@ -1,65 +0,0 @@
1
- module Lisp
2
-
3
- class Assignment
4
-
5
- def self.register
6
- Primitive.register("set!", "(set! _name_ _new-value_)\n\nThe way to assign (i.e. rebind) a symbol. `name` is the symbol to be rebound.
7
- The `new-value` sexpr is evaluated to arrive at the new value to be bound to. Use of `set!` is frowned upon, and should not be used without thought.") do |args, env|
8
- Lisp::Assignment::setbang_impl(args, env)
9
- end
10
-
11
- Primitive.register("set-car!", "(set-car! _cons-cell_ _new-value_)\n\nSet the `car` pointer of `cons-cell`.") do |args, env|
12
- Lisp::Assignment::setcarbang_impl(args, env)
13
- end
14
-
15
- Primitive.register("set-cdr!", "(set-cdr! _cons-cell_ _new-value_)\n\nSet the `cdr` pointer of `cons-cell`.") do |args, env|
16
- Lisp::Assignment::setcdrbang_impl(args, env)
17
- end
18
-
19
- Primitive.register("set-nth!", "(set-nth! _n_ _list-or-vector_ _new-value_)\n\nSets the `n`th element of `list-or-vector` to `new-value`.") do |args, env|
20
- Lisp::Assignment::setnthbang_impl(args, env)
21
- end
22
-
23
- end
24
-
25
-
26
- def self.setbang_impl(args, env)
27
- sym = args.car
28
- return Lisp::Debug.process_error("set! requires a raw (unevaluated) symbol as it's first argument.", env) unless sym.symbol?
29
- value = args.cadr.evaluate(env)
30
- env.set(sym, value)
31
- end
32
-
33
-
34
- def self.setcarbang_impl(args, env)
35
- pair = args.car.evaluate(env)
36
- return Lisp::Debug.process_error("set-car! requires a pair as it's first argument.", env) unless pair.pair?
37
- value = args.cadr.evaluate(env)
38
- pair.set_car!(value)
39
- end
40
-
41
-
42
- def self.setcdrbang_impl(args, env)
43
- pair = args.car.evaluate(env)
44
- return Lisp::Debug.process_error("set-cdr! requires a pair as it's first argument.", env) unless pair.pair?
45
- value = args.cadr.evaluate(env)
46
- pair.set_cdr!(value)
47
- end
48
-
49
-
50
- def self.setnthbang_impl(args, env)
51
- return Lisp::Debug.process_error("set-nth! requires 3 arguments.", env) unless args.length == 3
52
- n = args.car.evaluate(env)
53
- return Lisp::Debug.process_error("The first argument of set-nth! has to be an number.", env) unless n.number?
54
- return Lisp::Debug.process_error("The first argument of set-nth! has to be positive.", env) unless n.value > 0
55
-
56
- l = args.cadr.evaluate(env)
57
- return Lisp::Debug.process_error("set-nth! requires a list or vector as it's first argument.", env) unless l.list? || l.vector?
58
- value = args.caddr.evaluate(env)
59
- l.set_nth!(n.value, value)
60
- l
61
- end
62
-
63
-
64
- end
65
- end
@@ -1,118 +0,0 @@
1
- module Lisp
2
-
3
- class Equivalence
4
-
5
- def self.register
6
- Primitive.register("=", "(= number number)\n\nReturns whether the first numeric argument is equal to the second numeric argument.") do |args, env|
7
- Lisp::Equivalence::num_eq_impl(args, env)
8
- end
9
-
10
- Primitive.register("==", "(== number number)\n\nReturns whether the first numeric argument is equal to the second numeric argument.") do |args, env|
11
- Lisp::Equivalence::num_eq_impl(args, env)
12
- end
13
-
14
- Primitive.register("!=", "(!= number number)\n\nReturns whether the first numeric argument is not equal to the second numeric argument.") do |args, env|
15
- Lisp::Equivalence::num_neq_impl(args, env)
16
- end
17
-
18
- Primitive.register("/=", "(/= number number)\n\nReturns whether the first numeric argument is not equal to the second numeric argument.") do |args, env|
19
- Lisp::Equivalence::num_neq_impl(args, env)
20
- end
21
-
22
- Primitive.register("eq?", "(eq? sexpr sexpr)\n\nReturns whether the first argument is the same type as the second argument, and the same object, except in the case of numbers where the values are compared.") do |args, env|
23
- Lisp::Equivalence::eq_impl(args, env)
24
- end
25
-
26
- Primitive.register("eqv?", "(eqv? sexpr sexpr)\n\nReturns whether the first argument is the same type as the second argument, and the same object, except in the case of numbers where the values are compared.") do |args, env|
27
- Lisp::Equivalence::eqv_impl(args, env)
28
- end
29
-
30
- Primitive.register("equal?", "(equal? sexpr sexpr)\n\nReturns whether the first argument is the same type as the second argument, and are lists containing the same elements, are identical strings, are frames with matching slots, are numbers with the same value, or are the same object (if none of the above).") do |args, env|
31
- Lisp::Equivalence::equal_impl(args, env)
32
- end
33
-
34
- end
35
-
36
- # == - Check two integers for equivalence
37
-
38
- def self.num_eq_impl(args, env)
39
- raise "= and == need 2 arguments, received #{args.length}" if args.length != 2
40
- c1 = args.car.evaluate(env)
41
- c2 = args.cadr.evaluate(env)
42
- return Lisp::FALSE unless c1.integer? && c2.integer?
43
- Lisp::Boolean.with_value(c1.value == c2.value)
44
- end
45
-
46
- def self.num_neq_impl(args, env)
47
- raise "!= and /= needs 2 arguments, received #{args.length}" if args.length != 2
48
- c1 = args.car.evaluate(env)
49
- c2 = args.cadr.evaluate(env)
50
- return Lisp::TRUE unless c1.integer? && c2.integer?
51
- Lisp::Boolean.with_value(c1.value != c2.value)
52
- end
53
-
54
-
55
- # eq? - identity, typically used for symbols
56
-
57
- def self.eq_check(o1, o2)
58
- return Lisp::FALSE unless o1.type == o2.type
59
- Lisp::Boolean.with_value(case o1.type
60
- when :number
61
- (o1.integer? == o2.integer?) && (o1.value == o2.value)
62
- else
63
- o1.equal?(o2)
64
- end)
65
- end
66
-
67
-
68
- def self.eq_impl(args, env)
69
- raise "eq? needs 2 arguments, received #{args.length}" if args.length != 2
70
- o1 = args.car.evaluate(env)
71
- o2 = args.cadr.evaluate(env)
72
- eq_check(o1, o2)
73
- end
74
-
75
-
76
- # eqv? - same as eq?
77
-
78
- def self.eqv_check(o1, o2)
79
- eq_check(o1, o2)
80
- end
81
-
82
-
83
- def self.eqv_impl(args, env)
84
- raise "eq? needs 2 arguments, received #{args.length}" if args.length != 2
85
- o1 = args.car.evaluate(env)
86
- o2 = args.cadr.evaluate(env)
87
- eqv_check(o1, o2)
88
- end
89
-
90
-
91
- # equal? - object equality: same value
92
-
93
- def self.equal_check(o1, o2)
94
- return Lisp::FALSE unless o1.type == o2.type
95
- Lisp::Boolean.with_value(case o1.type
96
- when :pair
97
- o1.eq?(o2)
98
- when :string
99
- o1.value.eql?(o2.value)
100
- when :frame
101
- o1.eq?(o2)
102
- when :number
103
- (o1.integer? == o2.integer?) && (o1.value == o2.value)
104
- else
105
- o1.equal?(o2)
106
- end)
107
- end
108
-
109
-
110
- def self.equal_impl(args, env)
111
- raise "equal? needs 2 arguments, received #{args.length}" if args.length != 2
112
- o1 = args.car.evaluate(env)
113
- o2 = args.cadr.evaluate(env)
114
- self.equal_check(o1, o2)
115
- end
116
- end
117
-
118
- end
data/lib/rubylisp/io.rb DELETED
@@ -1,74 +0,0 @@
1
- module Lisp
2
-
3
- class IO
4
-
5
- def self.register
6
- Primitive.register("load") {|args, env| Lisp::IO::load_impl(args, env) }
7
- Primitive.register("load-library") {|args, env| Lisp::IO::load_library_impl(args, env) }
8
- Primitive.register("load-project") {|args, env| Lisp::IO::load_project_impl(args, env) }
9
- Primitive.register("trace") {|args, env| puts "Trace: #{(args.to_a.map {|a| a.evaluate(env).value.to_s}).join(' ')}"; nil}
10
- Primitive.register("error") {|args, env| App.alert((args.to_a.map {|a| a.evaluate(env).value.to_s}).join(' '))}
11
- Primitive.register("alert") {|args, env| App.alert((args.to_a.map {|a| a.evaluate(env).value.to_s}).join(' '))}
12
- end
13
-
14
-
15
- def self.load_impl(args, env)
16
- return Lisp::Debug.process_error("'load' requires 1 argument.", env) if args.empty?
17
- fname = args.car.evaluate(env)
18
- return Lisp::Debug.process_error("'load' requires a string argument.", env) unless fname.string?
19
- filename = fname.value.end_with?(".lsp") ? fname.value : "#{fname.value}.lsp"
20
- File.open(filename) do |f|
21
- contents = f.read()
22
- Lisp::Parser.new.parse_and_eval_all(contents)
23
- end
24
- Lisp::String.with_value("OK")
25
- end
26
-
27
-
28
- def self.load_library_impl(args, env)
29
- return Lisp::Debug.process_error("'load-library' requires 1 argument.", env) if args.empty?
30
- library_name = args.car.evaluate(env)
31
- return Lisp::Debug.process_error("'load-library' requires a string or symbol argument.", env) unless library_name.string? || library_name.symbol?
32
- Dir.chdir(File.join(App.documents_path, "libraries", "#{library_name}.lib")) do |d|
33
- if File.exists?("load.lsp")
34
- File.open("load.lsp") do |f|
35
- contents = f.read()
36
- Lisp::Parser.new.parse_and_eval_all(contents)
37
- end
38
- else
39
- Dir.glob("*.lsp") do |filename|
40
- File.open(filename) do |f|
41
- contents = f.read()
42
- Lisp::Parser.new.parse_and_eval_all(contents)
43
- end
44
- end
45
- end
46
- end
47
- Lisp::String.with_value("OK")
48
- end
49
-
50
-
51
- def self.load_project_impl(args, env)
52
- return Lisp::Debug.process_error("'load-project' requires 1 argument.", env) if args.empty?
53
- project_name = args.car.evaluate(env)
54
- return Lisp::Debug.process_error("'load-project' requires a string or symbol argument.", env) unless project_name.string? || project_name.symbol?
55
- Dir.chdir(File.join(App.documents_path, "projects", "#{project_name}.prj")) do |d|
56
- if File.exists?("load.lsp")
57
- File.open("load.lsp") do |f|
58
- contents = f.read()
59
- Lisp::Parser.new.parse_and_eval_all(contents)
60
- end
61
- else
62
- Dir.glob("*.lsp") do |filename|
63
- File.open(filename) do |f|
64
- contents = f.read()
65
- Lisp::Parser.new.parse_and_eval_all(contents)
66
- end
67
- end
68
- end
69
- end
70
- Lisp::String.with_value("OK")
71
- end
72
-
73
- end
74
- end