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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: bb83eafd57c37cbb7ef8e45248b31ec8aed7b124
4
- data.tar.gz: e9660c1b0bf3b359b18d0ff10af7b21790ef32cd
3
+ metadata.gz: ed25cdabf9d69adf654828879d800947d2d69624
4
+ data.tar.gz: cc25906946d3020ead7a82284a65f47796cc16f5
5
5
  SHA512:
6
- metadata.gz: 6023b05ca6246ea48b013ff1449063925b77d164aa938682d4601a8a7197d00260a582e9bc3d4acc71b14e9517b70259bc09d0557641d1c1722c9cd4050d85cb
7
- data.tar.gz: 8a4dcc8eec4c6c0bf60c17a15ee195aa08ca8a4a18fd0080622a0cf0676d56b64be890efd9eff4e781bce4d576f445112b718008ef8a6bd875bba1d43cb23afb
6
+ metadata.gz: 7984fd29e98ac9c002d515954630d894802c9c2a1e161a3e6cc880033f959b761845a23d9f198b98303bfb10a40c91660c51045d89db09fbb1f9eefe400752a5
7
+ data.tar.gz: 4f675b07e8f92ac250eeef84eeaf1a7cb8cecd56ca810d9d38b2bd4fa7797a3fd8e417376a7d682b5908569d64bc64532f52bd21e8c3e04782a91f3bb024ed6e
data/README.md ADDED
@@ -0,0 +1,82 @@
1
+ ## Overview
2
+
3
+ RubyLisp is a relatively simple Lisp dialect based on Scheme
4
+ implimented using Ruby.
5
+
6
+ RubyLisp is a lexically scoped Lisp-1:
7
+
8
+ **Lexically scoped:**
9
+ The scope of definitions are determined by where they are made in the
10
+ lexical structure of the code. Each `lambda`, `let`, and `do` creates
11
+ a new lexical scope. From [2]:
12
+
13
+ > Here references to the established entity can occur only within
14
+ > certain program portions that are lexically (that is, textually)
15
+ > contained within the establishing construct. Typically the construct
16
+ > will have a part designated the body, and the scope of all entities
17
+ > established will be (or include) the body.
18
+
19
+ **Lisp-1:**
20
+ A Lisp where functions and variables share a single namespace. This
21
+ differs from a Lisp-2 in which functions and variables have separate
22
+ namespaces.
23
+
24
+ ## Installation
25
+
26
+ Add this line to your application's Gemfile:
27
+
28
+ gem 'rubylisp'
29
+
30
+ And then execute:
31
+
32
+ $ bundle
33
+
34
+ Or install it yourself as:
35
+
36
+ $ gem install rubylisp
37
+
38
+ ## Contributing
39
+
40
+ 1. Fork it
41
+ 2. Create your feature branch (git checkout -b my-new-feature)
42
+ 3. Commit your changes (git commit -am 'Add some feature')
43
+ 4. Push to the branch (git push origin my-new-feature)
44
+ 5. Create new Pull Request
45
+
46
+ ## REPL
47
+
48
+ RubyLisp includes a very basic REPL that, as expected, lets you type
49
+ in snippets (it does not support multiline snippets) of Lisp code,
50
+ evaluates them, and prints the result.
51
+
52
+ >: rubylisp
53
+
54
+ RubyLisp REPL
55
+ > 4
56
+ 4
57
+ > (+ 2 3)
58
+ 5
59
+ > (define (fib x) (if (eq? x 0) 1 (* x (fib (- x 1)))))
60
+ <function: fib>
61
+ > (fib 4)
62
+ 24
63
+
64
+ ## Integrating
65
+
66
+
67
+ The simplist way to integrate RubyLisp is to have it parse and
68
+ evaluate strings of lisp code.
69
+
70
+ >: irb
71
+ 2.0.0-p247 :001 > require 'rubylisp'
72
+ => true
73
+ 2.0.0-p247 :002 > Lisp::Initializer.register_builtins
74
+ => ...
75
+ 2.0.0-p247 :003 > Lisp::Parser.new.parse_and_eval('(+ 1 2)').to_s
76
+ => "3"
77
+ 2.0.0-p247 :004 >
78
+
79
+ But lets say you are using rubylisp to do something more embedded such as provide an extension language to your application. It's easy to provide the Lisp runtime with functions written in Ruby that can be called from Lisp code.
80
+
81
+ to be continued...
82
+
data/bin/rubylisp CHANGED
@@ -4,15 +4,21 @@ require 'rubylisp'
4
4
  require 'readline'
5
5
 
6
6
  Lisp::Initializer.register_builtins
7
+ Lisp::Debug.interactive = true
7
8
 
8
9
  puts 'RubyLisp REPL'
10
+ puts 'Copyright 2014 David R. Astels'
11
+ puts
9
12
  parser = Lisp::Parser.new
10
13
 
11
14
  while line = Readline.readline('> ', true)
12
15
  begin
13
16
  puts parser.parse_and_eval(line)
14
17
  rescue Exception => ex
18
+ exit if ex.to_s == "exit"
15
19
  puts "ERROR: #{ex}"
20
+ puts ex.backtrace
16
21
  end
17
22
  end
18
23
 
24
+
@@ -14,12 +14,12 @@ module Lisp
14
14
  end
15
15
 
16
16
  def self.acons_impl(args, env)
17
- raise "acons require at least 2 or 3 arguments" unless args.length == 2 || args.length == 3
17
+ return Lisp::Debug.process_error("acons require at least 2 or 3 arguments", env) unless args.length == 2 || args.length == 3
18
18
  key = args.car.evaluate(env)
19
19
  value = args.cadr.evaluate(env)
20
20
  alist = args.length == 2 ? nil : args.caddr.evaluate(env)
21
21
  alist = Lisp::AList.from_list(alist) if !alist.nil? && alist.list? && !alist.alist?
22
- raise "the last argument to acons has to be an association list" unless alist.nil? || 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
23
 
24
24
  if alist.nil?
25
25
  Lisp::AList.new({key => value})
@@ -29,44 +29,44 @@ module Lisp
29
29
  end
30
30
 
31
31
  def self.assoc_impl(args, env)
32
- raise "assoc require 2 arguments" unless args.length == 2
32
+ return Lisp::Debug.process_error("assoc require 2 arguments", env) unless args.length == 2
33
33
  key = args.car.evaluate(env)
34
34
  alist = args.cadr.evaluate(env)
35
35
  alist = Lisp::AList.from_list(alist) if alist.list? && !alist.alist?
36
- raise "the last argument to assoc has to be an association list" unless alist.alist?
36
+ return Lisp::Debug.process_error("the last argument to assoc has to be an association list", env) unless alist.alist?
37
37
  alist.assoc(key)
38
38
  end
39
39
 
40
40
  def self.rassoc_impl(args, env)
41
- raise "assoc require 2 arguments" unless args.length == 2
41
+ return Lisp::Debug.process_error("assoc require 2 arguments", env) unless args.length == 2
42
42
  value = args.car.evaluate(env)
43
43
  alist = args.cadr.evaluate(env)
44
44
  alist = Lisp::AList.from_list(alist) if alist.list? && !alist.alist?
45
- raise "the last argument to rassoc has to be an association list" unless alist.alist?
45
+ return Lisp::Debug.process_error("the last argument to rassoc has to be an association list", env) unless alist.alist?
46
46
  alist.rassoc(value)
47
47
  end
48
48
 
49
49
  def self.dissoc_impl(args, env)
50
- raise "assoc require 2 arguments" unless args.length == 2
50
+ return Lisp::Debug.process_error("assoc require 2 arguments", env) unless args.length == 2
51
51
  key = args.car.evaluate(env)
52
52
  alist = args.cadr.evaluate(env)
53
53
  alist = Lisp::AList.from_list(alist) if alist.list? && !alist.alist?
54
- raise "the last argument to dissoc has to be an association list" unless alist.alist?
54
+ return Lisp::Debug.process_error("the last argument to dissoc has to be an association list", env) unless alist.alist?
55
55
  alist.dissoc(key)
56
56
  end
57
57
 
58
58
  def self.zip_impl(args, env)
59
- raise "assoc require 2 or 3arguments" unless args.length == 2 || args.length == 3
59
+ return Lisp::Debug.process_error("assoc require 2 or 3arguments", env) unless args.length == 2 || args.length == 3
60
60
  key_list = args.car.evaluate(env)
61
- raise "the keys supplied to zip has to be a list" unless key_list.list?
61
+ return Lisp::Debug.process_error("the keys supplied to zip has to be a list", env) unless key_list.list?
62
62
  value_list = args.cadr.evaluate(env)
63
- raise "the values supplied to zip has to be a list" unless value_list.list?
64
- raise "zip requires the same number of keys and values" unless key_list.length == value_list.length
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
65
 
66
66
  if args.length == 3
67
67
  alist = args.caddr.evaluate(env)
68
68
  alist = Lisp::AList.from_list(alist) if alist.list? && !alist.alist?
69
- raise "the third argument to zip has to be an association list" unless alist.alist?
69
+ return Lisp::Debug.process_error("the third argument to zip has to be an association list", env) unless alist.alist?
70
70
  alist.zip(key_list.to_a, value_list.to_a)
71
71
  else
72
72
  Lisp::AList.zip(key_list.to_a, value_list.to_a)
@@ -74,18 +74,18 @@ module Lisp
74
74
  end
75
75
 
76
76
  def self.alist_to_list_impl(args, env)
77
- raise "alist-to-list requires 1 arguments" unless args.length == 1
77
+ return Lisp::Debug.process_error("alist-to-list requires 1 arguments", env) unless args.length == 1
78
78
  alist = args.car.evaluate(env)
79
79
  return alist if alist.list? && !alist.alist?
80
- raise "the argument to alist-to-list has to be an association list" unless 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
81
 
82
82
  alist.to_list
83
83
  end
84
84
 
85
85
  def self.list_to_alist_impl(args, env)
86
- raise "list-to-alist requires 1 arguments" unless args.length == 1
86
+ return Lisp::Debug.process_error("list-to-alist requires 1 arguments", env) unless args.length == 1
87
87
  list = args.car.evaluate(env)
88
- raise "the argument to list-to-alist has to be a list" unless list.list?
88
+ return Lisp::Debug.process_error("the argument to list-to-alist has to be a list", env) unless list.list?
89
89
 
90
90
  Lisp::AList.from_list(list)
91
91
  end
@@ -25,7 +25,7 @@ The `new-value` sexpr is evaluated to arrive at the new value to be bound to. Us
25
25
 
26
26
  def self.setbang_impl(args, env)
27
27
  sym = args.car
28
- raise "set! requires a raw (unevaluated) symbol as it's first argument." unless sym.symbol?
28
+ return Lisp::Debug.process_error("set! requires a raw (unevaluated) symbol as it's first argument.", env) unless sym.symbol?
29
29
  value = args.cadr.evaluate(env)
30
30
  env.set(sym, value)
31
31
  end
@@ -33,7 +33,7 @@ The `new-value` sexpr is evaluated to arrive at the new value to be bound to. Us
33
33
 
34
34
  def self.setcarbang_impl(args, env)
35
35
  pair = args.car.evaluate(env)
36
- raise "set-car! requires a pair as it's first argument." unless pair.pair?
36
+ return Lisp::Debug.process_error("set-car! requires a pair as it's first argument.", env) unless pair.pair?
37
37
  value = args.cadr.evaluate(env)
38
38
  pair.set_car!(value)
39
39
  end
@@ -41,20 +41,20 @@ The `new-value` sexpr is evaluated to arrive at the new value to be bound to. Us
41
41
 
42
42
  def self.setcdrbang_impl(args, env)
43
43
  pair = args.car.evaluate(env)
44
- raise "set-cdr! requires a pair as it's first argument." unless pair.pair?
44
+ return Lisp::Debug.process_error("set-cdr! requires a pair as it's first argument.", env) unless pair.pair?
45
45
  value = args.cadr.evaluate(env)
46
46
  pair.set_cdr!(value)
47
47
  end
48
48
 
49
49
 
50
50
  def self.setnthbang_impl(args, env)
51
- raise "set-nth! requires 3 arguments." unless args.length == 3
51
+ return Lisp::Debug.process_error("set-nth! requires 3 arguments.", env) unless args.length == 3
52
52
  n = args.car.evaluate(env)
53
- raise "The first argument of set-nth! has to be an number." unless n.number?
54
- raise "The first argument of set-nth! has to be positive." unless n.value > 0
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
55
 
56
56
  l = args.cadr.evaluate(env)
57
- raise "set-nth! requires a list or vector as it's first argument." unless l.list? || l.vector?
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
58
  value = args.caddr.evaluate(env)
59
59
  l.set_nth!(n.value, value)
60
60
  l
@@ -25,6 +25,7 @@ module Lisp
25
25
  Lisp::ClassObject.register
26
26
  Lisp::System.register
27
27
  Lisp::Vector.register
28
+ Lisp::Debug.register
28
29
  end
29
30
  end
30
31
 
@@ -99,39 +99,40 @@ module Lisp
99
99
 
100
100
 
101
101
  def self.char_name_impl(args, env)
102
- raise "char->name requires a single argument, found #{args.length}" unless args.length == 1
102
+ return Lisp::Debug.process_error("char->name requires a single argument, found #{args.length}", env) unless args.length == 1
103
103
  char = args.car.evaluate(env)
104
- raise "char->name requires a character argument" unless char.character?
104
+ return Lisp::Debug.process_error("char->name requires a character argument", env) unless char.character?
105
105
  kv = @@character_constants.rassoc(char)
106
106
  return Lisp::String.with_value(kv[0]) unless kv.nil?
107
- raise "char->name was passed an invalid character"
107
+ return Lisp::Debug.process_error("char->name was passed an invalid character", env)
108
108
  end
109
109
 
110
110
 
111
111
  def self.name_char_impl(args, env)
112
- raise "name->char requires a single argument, found #{args.length}" unless args.length == 1
112
+ return Lisp::Debug.process_error("name->char requires a single argument, found #{args.length}", env) unless args.length == 1
113
113
  name = args.car.evaluate(env)
114
- raise "name->char requires a string argument" unless name.string?
114
+ return Lisp::Debug.process_error("name->char requires a string argument", env) unless name.string?
115
115
  ch = find_character_for_name(name.value)
116
116
  return ch unless ch.nil?
117
- raise "There is no character with the name #{name}"
117
+ return Lisp::Debug.process_error("There is no character with the name #{name}", env)
118
118
  end
119
119
 
120
120
 
121
121
  def self.get_one_character_arg(func, args, env)
122
- raise "#{func} requires a character argument, found no args" unless args.length >= 1
122
+ return Lisp::Debug.process_error("#{func} requires a character argument, found no args", env) unless args.length >= 1
123
123
  char1 = args.car.evaluate(env)
124
- raise "#{func} requires a character argument, found #{char1}" unless char1.character?
124
+ return Lisp::Debug.process_error("#{func} requires a character argument, found #{char1}", env) unless char1.character?
125
125
  return char1
126
126
  end
127
127
 
128
128
 
129
129
  def self.get_two_character_args(func, args, env)
130
- raise "#{func} requires two arguments, found #{args.length}" unless args.length == 2
130
+ return Lisp::Debug.process_error("#{func} requires two arguments, found
131
+ ##{args.length}", env) unless args.length == 2
131
132
  char1 = args.car.evaluate(env)
132
- raise "#{func} requires character arguments, found #{char1}" unless char1.character?
133
+ return Lisp::Debug.process_error("#{func} requires character arguments, found #{char1}", env) unless char1.character?
133
134
  char2 = args.cadr.evaluate(env)
134
- raise "#{func} requires character arguments, found #{char2}" unless char2.character?
135
+ return Lisp::Debug.process_error("#{func} requires character arguments, found #{char2}", env) unless char2.character?
135
136
  return [char1, char2]
136
137
  end
137
138
 
@@ -197,7 +198,7 @@ module Lisp
197
198
 
198
199
 
199
200
  def self.charp_impl(args, env)
200
- raise "char->name requires a single argument, found #{args.length}" unless args.length == 1
201
+ return Lisp::Debug.process_error("char->name requires a single argument, found #{args.length}", env) unless args.length == 1
201
202
  char = args.car.evaluate(env)
202
203
  Lisp::Boolean.with_value(char.character?)
203
204
  end
@@ -221,8 +222,8 @@ module Lisp
221
222
  10
222
223
  else
223
224
  b = args.cadr.evaluate(env)
224
- raise "Base for char->digit has to be an integer" unless b.integer?
225
- raise "Base for char->digit has to be between 2 and 36" unless b.value >=2 && b.value <= 36
225
+ return Lisp::Debug.process_error("Base for char->digit has to be an integer", env) unless b.integer?
226
+ return Lisp::Debug.process_error("Base for char->digit has to be between 2 and 36", env) unless b.value >=2 && b.value <= 36
226
227
  b.value
227
228
  end
228
229
  ch = char.value.upcase
@@ -246,13 +247,13 @@ module Lisp
246
247
 
247
248
  def self.digit_char_impl(args, env)
248
249
  d = args.car.evaluate(env)
249
- raise "Digit value for digit->char has to be an integer" unless d.integer?
250
+ return Lisp::Debug.process_error("Digit value for digit->char has to be an integer", env) unless d.integer?
250
251
  base = if args.length == 1
251
252
  10
252
253
  else
253
254
  b = args.cadr.evaluate(env)
254
- raise "Base for char->digit has to be an integer" unless b.integer?
255
- raise "Base for char->digit has to be between 2 and 36" unless b.value >=2 && b.value <= 36
255
+ return Lisp::Debug.process_error("Base for char->digit has to be an integer", env) unless b.integer?
256
+ return Lisp::Debug.process_error("Base for char->digit has to be between 2 and 36", env) unless b.value >=2 && b.value <= 36
256
257
  b.value
257
258
  end
258
259
  val = d.value
@@ -269,7 +270,7 @@ module Lisp
269
270
 
270
271
  def self.int_char_impl(args, env)
271
272
  i = args.car.evaluate(env)
272
- raise "Integer value for int->char has to be an integer" unless i.integer?
273
+ return Lisp::Debug.process_error("Integer value for int->char has to be an integer", env) unless i.integer?
273
274
  find_character_for_chr(i.value.chr)
274
275
  end
275
276
 
@@ -374,10 +375,10 @@ module Lisp
374
375
  elsif n[0..1] == "U+"
375
376
  find_character_for_chr(n[2..-1].to_i(16).chr)
376
377
  else
377
- raise "Invalid character name: #{n}"
378
+ return Lisp::Debug.process_error("Invalid character name: #{n}", env)
378
379
  end
379
380
  end
380
381
 
381
- end
382
+ end
382
383
 
383
384
  end
@@ -123,6 +123,8 @@ module Lisp
123
123
  def to_s
124
124
  return "()" if self.empty?
125
125
  return "'#{@cdr.car.to_s}" if @car.symbol? && @car.name == "quote"
126
+ return "{#{@cdr.to_s_helper}}" if @car.symbol? && @car.name == "make-frame"
127
+ return "[#{@cdr.to_s_helper}]" if @car.symbol? && @car.name == "make-vector"
126
128
  return "(#{@car.to_s} . #{@cdr.to_s})" if !@cdr.nil? && !@cdr.pair?
127
129
  return "(#{self.to_s_helper})"
128
130
  end
@@ -134,6 +136,8 @@ module Lisp
134
136
  def print_string
135
137
  return "()" if self.empty?
136
138
  return "'#{@cdr.car.print_string}" if @car.symbol? && @car.name == "quote"
139
+ return "{#{@cdr.print_string_helper}}" if @car.symbol? && @car.name == "make-frame"
140
+ return "[#{@cdr.print_string_helper}]" if @car.symbol? && @car.name == "make-vector"
137
141
  return "(#{@car.print_string} . #{@cdr.print_string})" if !@cdr.nil? && !@cdr.pair?
138
142
  return "(#{self.print_string_helper})"
139
143
  end
@@ -203,13 +207,52 @@ module Lisp
203
207
  return nil unless obj.object?
204
208
  return obj.value
205
209
  end
210
+
211
+
212
+
213
+ def inner_eval(env)
214
+ func = @car.evaluate(env)
215
+ return Lisp::Debug.process_error("There is no function or macro named #{@car}", env) if func.nil?
216
+ env.current_code.unshift(self.print_string()) if !Lisp::Debug.eval_in_debug_repl && Lisp::Debug.interactive
217
+
218
+ Lisp::Debug.log_eval(self, env)
206
219
 
220
+ unless Lisp::Debug.eval_in_debug_repl
221
+ if !Lisp::Debug.target_env.nil? && env == Lisp::Debug.target_env.previous
222
+ Lisp::Debug.target_env = nil
223
+ Lisp::Debug.debug_repl(env)
224
+ elsif Lisp::Debug.single_step || (func.function? && Lisp::Debug.on_entry.include?(func.name))
225
+ Lisp::Debug.debug_repl(env)
226
+ end
227
+ end
228
+ result = func.apply_to(@cdr, env)
229
+ env.current_code.shift() if !Lisp::Debug.eval_in_debug_repl && Lisp::Debug.interactive
230
+ Lisp::Debug.log_result(result, env)
231
+ result
232
+ end
233
+
207
234
 
208
235
  def evaluate(env)
209
236
  return self if empty?
210
- func = @car.evaluate(env)
211
- raise "There is no function or macro named #{@car}" if func.nil?
212
- func.apply_to(@cdr, env)
237
+ sexpr = if @car.symbol?
238
+ key = @car
239
+ frame = nth(2)
240
+ value = nth(3)
241
+
242
+ s = key.name
243
+ if s.end_with?(":")
244
+ ConsCell.array_to_list([Symbol.named("get-slot"), frame, key])
245
+ elsif s.end_with?(":!")
246
+ ConsCell.array_to_list([Symbol.named("set-slot!"), frame, Symbol.named(s[0..-2]), value])
247
+ elsif s.end_with?(":?")
248
+ ConsCell.array_to_list([Symbol.named("has-slot?"), frame, Symbol.named(s[0..-2])])
249
+ else
250
+ self
251
+ end
252
+ else
253
+ self
254
+ end
255
+ sexpr.inner_eval(env)
213
256
  end
214
257
 
215
258
  def evaluate_each(env)