theta 0.1.3 → 0.1.4

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,6 +1,6 @@
1
1
  = Theta
2
2
 
3
- Theta is a Lisp interpreter for Ruby. It was heavily influenced by {lis.py}[http://norvig.com/lispy.html] and {flea}[https://github.com/aarongough/flea]. It was primarily made as a learning exercise.
3
+ Theta is a Lisp interpreter for Ruby (though it's really just a small subset of Lisp it interprets). It was heavily influenced by {lis.py}[http://norvig.com/lispy.html] and {flea}[https://github.com/aarongough/flea]. It was primarily made as a learning exercise.
4
4
 
5
5
  == How to use
6
6
 
@@ -12,7 +12,6 @@ After installation, simply type theta to get to the interactive interpreter prom
12
12
 
13
13
  % theta
14
14
  theta> (define a 2)
15
- 2
16
15
  theta> (+ a 3)
17
16
  5
18
17
  theta> (define square (lambda (x) (* x x)))
@@ -37,3 +36,77 @@ And finally, you can use Theta inside your own Ruby programs, if that sounds lik
37
36
  t = Theta::Interpreter.new
38
37
  t.run "(define n 15)"
39
38
  puts t.run "(+ 10 n)"
39
+
40
+ == Syntax
41
+
42
+ Theta uses a very simplified Lisp-like syntax.
43
+
44
+ === Math
45
+
46
+ Theta has your basic mathematical functions.
47
+
48
+ theta> (+ 2 2)
49
+ 4
50
+ theta> (- 3 2)
51
+ 1
52
+ theta> (* 4 2)
53
+ 8
54
+ theta> (/ 4 2)
55
+ 2
56
+
57
+ === Defining Functions and Variables
58
+
59
+ You also have the basic variable definition stuff.
60
+
61
+ theta> (define a 2)
62
+ theta> (+ a 3)
63
+ 5
64
+
65
+ And functions.
66
+
67
+ theta> (define square (lambda (x) (* x x)))
68
+ theta> (square 3)
69
+ 9
70
+
71
+ === Conditionals and Truthiness
72
+
73
+ We have your basic if statement that will evaluate the first piece of code
74
+ if true, otherwise the second (if you give it one).
75
+
76
+ theta> (if (= 2 2) (display "true") (display "not true"))
77
+ true
78
+ theta> (if (= 1 2) (display "true") (display "not true"))
79
+ not true
80
+
81
+ And some basic Boolean statements.
82
+
83
+ theta> (= 2 2)
84
+ true
85
+ theta> (> 1 2)
86
+ false
87
+ theta> (< 2 3)
88
+ true
89
+ theta> (>= 3 3 2)
90
+ true
91
+ theta> (<= 4 5 2)
92
+ false
93
+
94
+ === Input and Output
95
+
96
+ Theta also has basic input and output.
97
+
98
+ theta> (display "poop")
99
+ poop
100
+ theta> (define a (input))
101
+ hi <this is user input>
102
+ theta> (display a)
103
+ hi
104
+
105
+ And a function to turn user input into an integer.
106
+
107
+ theta> (define a (string-to-num (input)))
108
+ 123 <this is user input>
109
+ theta>(+ a 3)
110
+ 126
111
+
112
+
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.1.3
1
+ 0.1.4
@@ -16,7 +16,10 @@ module Theta
16
16
 
17
17
  # run given code
18
18
  def run(program)
19
- puts @interpreter.run(program)
19
+ output = @interpreter.run(program)
20
+ if not output.nil? and not output.empty?
21
+ output.each { |value| puts @interpreter.make_readable(value) }
22
+ end
20
23
  end
21
24
 
22
25
  # start an interactive interpreter
@@ -36,11 +39,11 @@ module Theta
36
39
  repl_help
37
40
  else
38
41
  begin
39
- value = @interpreter.run(input)
42
+ output = @interpreter.run(input)
40
43
  rescue SyntaxError
41
44
  end
42
- unless value.nil?
43
- puts @interpreter.make_readable(value)
45
+ if not output.nil? and not output.empty?
46
+ output.each { |value| puts @interpreter.make_readable(value) }
44
47
  end
45
48
  end
46
49
  end
@@ -9,8 +9,8 @@ module Theta
9
9
  @parent = parent
10
10
  @table = {}
11
11
  if @parent.nil?
12
- define(":#t", true)
13
- define(":#f", false)
12
+ define("#t".to_sym, true)
13
+ define("#f".to_sym, false)
14
14
  end
15
15
  end
16
16
 
@@ -28,7 +28,9 @@ module Theta
28
28
  # run some code
29
29
  def run(program)
30
30
  expression = parse(program)
31
- return evaluate(expression)
31
+ output = []
32
+ expression.each { |e| output << evaluate(e) }
33
+ return output.delete_if { |x| x.nil? }
32
34
  end
33
35
 
34
36
  # call the parser to make a string interpretable
@@ -49,17 +51,9 @@ module Theta
49
51
  return expression
50
52
  end
51
53
 
52
- if expression.count == 1
53
- if expression.is_a? Symbol
54
- return @current_environment.find(expression[0])
55
- else
56
- return expression[0]
57
- end
58
- end
59
-
60
54
  case expression[0]
61
55
  when :define
62
- return @current_environment.define(expression [1], evaluate(expression[2]))
56
+ @current_environment.define(expression [1], evaluate(expression[2]))
63
57
  when :ruby_func
64
58
  return eval expression[1]
65
59
  else
@@ -71,6 +65,7 @@ module Theta
71
65
  raise RuntimeError, "#{expression[0]} is not a function"
72
66
  end
73
67
  end
68
+ return nil
74
69
  end
75
70
  end
76
71
  end
@@ -0,0 +1,8 @@
1
+ (define display
2
+ (ruby_func "
3
+ lambda { |arguments, interpreter|
4
+ output = interpreter.evaluate(arguments[0])
5
+ puts interpreter.make_readable(output)
6
+ }
7
+ ")
8
+ )
@@ -6,6 +6,7 @@
6
6
  return false
7
7
  end
8
8
  end
9
+ return true
9
10
  }
10
11
  ")
11
12
  )
@@ -0,0 +1,7 @@
1
+ (define input
2
+ (ruby_func "
3
+ lambda { |arguments, interpreter|
4
+ gets
5
+ }
6
+ ")
7
+ )
@@ -0,0 +1,7 @@
1
+ (define string-to-num
2
+ (ruby_func "
3
+ lambda { |arguments, interpreter|
4
+ interpreter.evaluate(arguments[0]).to_i
5
+ }
6
+ ")
7
+ )
@@ -73,26 +73,49 @@ module Theta
73
73
  raise SyntaxError, "unexpected EOF while reading"
74
74
  end
75
75
  tokens.delete_if { |item| item.strip == "" || item == []}
76
- token = tokens.shift
77
- if "(" == token
78
- l = []
79
- until tokens[0] == ")"
80
- l << read_from(tokens)
76
+ tmp, expression = restructure tokens
77
+ return expression
78
+ end
79
+
80
+ def restructure(token_array, offset = 0)
81
+ statement = []
82
+ while offset < token_array.length
83
+ if token_array[offset] == "("
84
+ offset, tmp_array = restructure(token_array, offset + 1)
85
+ statement << tmp_array
86
+ elsif token_array[offset] == ")"
87
+ break
88
+ else
89
+ statement << atom(token_array[offset])
81
90
  end
82
- tokens.shift
83
- return l
84
- elsif ")" == token
85
- raise SyntaxError, "unexpected )"
86
- elsif token.start_with?("\"")
87
- return token.gsub("\"", "")
88
- else
89
- return atom(token)
91
+ offset += 1
90
92
  end
93
+ return offset, statement
91
94
  end
95
+
96
+ # token = tokens.shift
97
+ # if "(" == token
98
+ # l = []
99
+ # until tokens[0] == ")"
100
+ # l << read_from(tokens)
101
+ # end
102
+ # tokens.shift
103
+ # return l
104
+ # elsif ")" == token
105
+ # raise SyntaxError, "unexpected )"
106
+ # elsif token.start_with?("\"")
107
+ # return token.gsub("\"", "")
108
+ # else
109
+ # return atom(token)
110
+ # end
111
+ # end
92
112
 
93
113
  # returns appropriate numeric object if a number,
94
114
  # otherwise returns a symbol
95
115
  def atom(token)
116
+ if token.start_with?("\"")
117
+ return token.gsub("\"", "")
118
+ end
96
119
  token.gsub!(/\n\t/, "")
97
120
  begin
98
121
  return Integer(token)
@@ -108,7 +131,8 @@ module Theta
108
131
  # convert an expression back to a readable string
109
132
  def to_string(expression)
110
133
  if expression.is_a? Array
111
- return "(" + " ".join(expression.map { |exp| to_string(exp) }) + ")"
134
+ expression.map { |exp| to_string(exp) }
135
+ return "(" + expression.join(" ") + ")"
112
136
  else
113
137
  return expression.to_s
114
138
  end
@@ -0,0 +1,27 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), "..", "helper.rb"))
2
+
3
+ module Theta
4
+ class TestAdd < Test::Unit::TestCase
5
+ def setup
6
+ @interpreter = Interpreter.new
7
+ end
8
+
9
+ should "add numbers correctly" do
10
+ result = @interpreter.run("(+ 2 2)")
11
+ assert_equal(4, result[0])
12
+ end
13
+
14
+ should "add a series of numbers" do
15
+ result = @interpreter.run("(+ 5 4 3 2 1)")
16
+ assert_equal(15, result[0])
17
+ end
18
+
19
+ should "add defined variables" do
20
+ result = @interpreter.run(
21
+ "(define a 1)
22
+ (define b 2)
23
+ (+ a b)")
24
+ assert_equal(3, result[0])
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,37 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), "..", "helper.rb"))
2
+
3
+ module Theta
4
+ class TestDivide < Test::Unit::TestCase
5
+ def setup
6
+ @interpreter = Interpreter.new
7
+ end
8
+
9
+ should "divide numbers properly" do
10
+ result = @interpreter.run("(/ 10 2)")
11
+ assert_equal(5, result[0])
12
+ end
13
+
14
+ should "divide multiple numbers" do
15
+ result = @interpreter.run("(/ 4 2 2)")
16
+ assert_equal(1, result[0])
17
+ end
18
+
19
+ should "divide defined variables" do
20
+ result = @interpreter.run(
21
+ "(define a 4)
22
+ (define b 2)
23
+ (/ a b)")
24
+ assert_equal(2, result[0])
25
+ end
26
+
27
+ should "properly round for integer division" do
28
+ result = @interpreter.run("(/ 5 2)")
29
+ assert_equal(2, result[0])
30
+ end
31
+
32
+ should "not round for float division" do
33
+ result = @interpreter.run("(/ 5.0 2)")
34
+ assert_equal(2.5, result[0])
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,41 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), "..", "helper.rb"))
2
+
3
+ module Theta
4
+ class TestEquality < Test::Unit::TestCase
5
+ def setup
6
+ @interpreter = Interpreter.new
7
+ end
8
+
9
+ should "return true for correct expressions" do
10
+ result = @interpreter.run("(= 2 2)")
11
+ assert_equal(true, result[0])
12
+ end
13
+
14
+ should "return false for incorrect expressions" do
15
+ result = @interpreter.run("(= 2 3)")
16
+ assert_equal(false, result[0])
17
+ end
18
+
19
+ should "work with multiple arguments" do
20
+ result = @interpreter.run("
21
+ (= 2 2 2)
22
+ (= 2 2 3 2)
23
+ ")
24
+ assert_equal(true, result[0])
25
+ assert_equal(false, result[1])
26
+ end
27
+
28
+ should "work with variables" do
29
+ @interpreter.run("
30
+ (define a 1)
31
+ (define b 2)
32
+ ")
33
+ result = @interpreter.run("
34
+ (= a a)
35
+ (= a b)
36
+ ")
37
+ assert_equal(true, result[0])
38
+ assert_equal(false, result[1])
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,33 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), "..", "helper.rb"))
2
+
3
+ module Theta
4
+ class TestGreaterThan < Test::Unit::TestCase
5
+ def setup
6
+ @interpreter = Interpreter.new
7
+ end
8
+
9
+ should "return true if first arg is largest" do
10
+ result = @interpreter.run("(> 5 1 2 3)")
11
+ assert_equal(true, result[0])
12
+ end
13
+
14
+ should "return false if first arg isn't largest" do
15
+ result = @interpreter.run("(> 5 1 2 10)")
16
+ assert_equal(false, result[0])
17
+ end
18
+
19
+ should "work with variables" do
20
+ @interpreter.run("
21
+ (define a 1)
22
+ (define b 2)
23
+ (define c 3)
24
+ ")
25
+ result = @interpreter.run("
26
+ (> c b a)
27
+ (> b a c)
28
+ ")
29
+ assert_equal(true, result[0])
30
+ assert_equal(false, result[1])
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,40 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), "..", "helper.rb"))
2
+
3
+ module Theta
4
+ class TestGreaterThanOrEqual < Test::Unit::TestCase
5
+ def setup
6
+ @interpreter = Interpreter.new
7
+ end
8
+
9
+ should "return true if first arg is largest" do
10
+ result = @interpreter.run("(>= 4 3 2)")
11
+ assert_equal(true, result[0])
12
+ end
13
+
14
+ should "return true if first arg is equal to the largest" do
15
+ result = @interpreter.run("(>= 4 3 2 4)")
16
+ assert_equal(true, result[0])
17
+ end
18
+
19
+ should "return false if first arg is not greater than or equal to the largest" do
20
+ result = @interpreter.run("(>= 4 3 2 10)")
21
+ assert_equal(false, result[0])
22
+ end
23
+
24
+ should "work with variables" do
25
+ @interpreter.run("
26
+ (define a 1)
27
+ (define b 2)
28
+ (define c 3)
29
+ ")
30
+ result = @interpreter.run("
31
+ (>= c b a)
32
+ (>= c b a c)
33
+ (>= b c a)
34
+ ")
35
+ assert_equal(true, result[0])
36
+ assert_equal(true, result[1])
37
+ assert_equal(false, result[2])
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,49 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), "..", "helper.rb"))
2
+
3
+ module Theta
4
+ class TestIf < Test::Unit::TestCase
5
+ def setup
6
+ @interpreter = Interpreter.new
7
+ end
8
+
9
+ context "without else" do
10
+ should "execute statement if true" do
11
+ @interpreter.run("
12
+ (if #t
13
+ (define a 1))
14
+ ")
15
+ assert_equal(1, @interpreter.current_environment.find(:a))
16
+ end
17
+
18
+ should "not execute statement if false" do
19
+ @interpreter.run("
20
+ (if #f
21
+ (define a 1))
22
+ ")
23
+ assert_equal(nil, @interpreter.current_environment.find(:a))
24
+ end
25
+ end
26
+
27
+ context "with else" do
28
+ should "execute else statement if false" do
29
+ @interpreter.run("
30
+ (if #f
31
+ (define a 1)
32
+ (define b 1))
33
+ ")
34
+ assert_equal(nil, @interpreter.current_environment.find(:a))
35
+ assert_equal(1, @interpreter.current_environment.find(:b))
36
+ end
37
+
38
+ should "not execute else statement if true" do
39
+ @interpreter.run("
40
+ (if #t
41
+ (define a 1)
42
+ (define b 1))
43
+ ")
44
+ assert_equal(1, @interpreter.current_environment.find(:a))
45
+ assert_equal(nil, @interpreter.current_environment.find(:b))
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,40 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), "..", "helper.rb"))
2
+
3
+ module Theta
4
+ class TestLambda < Test::Unit::TestCase
5
+ def setup
6
+ @interpreter = Interpreter.new
7
+ end
8
+
9
+ should "create a function" do
10
+ result = @interpreter.run("
11
+ (lambda () ())
12
+ ")
13
+ assert_kind_of(Proc, result[0])
14
+ end
15
+
16
+ should "accept an argument" do
17
+ result = @interpreter.run("
18
+ (define test (lambda (a) (+ a a)))
19
+ (test 1)
20
+ ")
21
+ assert_equal(2, result[0])
22
+ end
23
+
24
+ should "accept multiple arguments" do
25
+ result = @interpreter.run("
26
+ (define test (lambda (a b c) (+ a b c)))
27
+ (test 1 2 3)
28
+ ")
29
+ assert_equal(6, result[0])
30
+ end
31
+
32
+ should "raise an error if a param is used more than once" do
33
+ assert_raise(RuntimeError) {
34
+ @interpreter.run("
35
+ (lambda (a a) (+ a 2))
36
+ ")
37
+ }
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,33 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), "..", "helper.rb"))
2
+
3
+ module Theta
4
+ class TestLessThan < Test::Unit::TestCase
5
+ def setup
6
+ @interpreter = Interpreter.new
7
+ end
8
+
9
+ should "return true if first arg is smallest" do
10
+ result = @interpreter.run("(< 1 2 3)")
11
+ assert_equal(true, result[0])
12
+ end
13
+
14
+ should "return false if first arg isn't smallest" do
15
+ result = @interpreter.run("(< 5 2 10)")
16
+ assert_equal(false, result[0])
17
+ end
18
+
19
+ should "work with variables" do
20
+ @interpreter.run("
21
+ (define a 1)
22
+ (define b 2)
23
+ (define c 3)
24
+ ")
25
+ result = @interpreter.run("
26
+ (< a b c)
27
+ (< b a c)
28
+ ")
29
+ assert_equal(true, result[0])
30
+ assert_equal(false, result[1])
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,40 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), "..", "helper.rb"))
2
+
3
+ module Theta
4
+ class TestGreaterThanOrEqual < Test::Unit::TestCase
5
+ def setup
6
+ @interpreter = Interpreter.new
7
+ end
8
+
9
+ should "return true if first arg is smallest" do
10
+ result = @interpreter.run("(<= 1 3 2)")
11
+ assert_equal(true, result[0])
12
+ end
13
+
14
+ should "return true if first arg is equal to the smallest" do
15
+ result = @interpreter.run("(<= 2 3 2 4)")
16
+ assert_equal(true, result[0])
17
+ end
18
+
19
+ should "return false if first arg is not less than or equal to the smallest" do
20
+ result = @interpreter.run("(<= 4 3 2 10)")
21
+ assert_equal(false, result[0])
22
+ end
23
+
24
+ should "work with variables" do
25
+ @interpreter.run("
26
+ (define a 1)
27
+ (define b 2)
28
+ (define c 3)
29
+ ")
30
+ result = @interpreter.run("
31
+ (<= a b c)
32
+ (<= a b a c)
33
+ (<= b c a)
34
+ ")
35
+ assert_equal(true, result[0])
36
+ assert_equal(true, result[1])
37
+ assert_equal(false, result[2])
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,27 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), "..", "helper.rb"))
2
+
3
+ module Theta
4
+ class TestMultiply < Test::Unit::TestCase
5
+ def setup
6
+ @interpreter = Interpreter.new
7
+ end
8
+
9
+ should "multiply numbers properly" do
10
+ result = @interpreter.run("(* 2 2)")
11
+ assert_equal(4, result[0])
12
+ end
13
+
14
+ should "multiply multiple numbers" do
15
+ result = @interpreter.run("(* 2 3 4)")
16
+ assert_equal(24, result[0])
17
+ end
18
+
19
+ should "multiply defined variables" do
20
+ result = @interpreter.run(
21
+ "(define a 4)
22
+ (define b 2)
23
+ (* a b)")
24
+ assert_equal(8, result[0])
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,21 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), "..", "helper.rb"))
2
+
3
+ module Theta
4
+ class TestSet < Test::Unit::TestCase
5
+ def setup
6
+ @interpreter = Interpreter.new
7
+ end
8
+
9
+ should "set value of existing variable" do
10
+ @interpreter.run("
11
+ (define a 1)
12
+ (set! a 2)
13
+ ")
14
+ assert_equal(2, @interpreter.current_environment.find(:a))
15
+ end
16
+
17
+ should "raise an error if variable not defined" do
18
+ assert_raise(TypeError) { @interpreter.run("(set! a 2)") }
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,27 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), "..", "helper.rb"))
2
+
3
+ module Theta
4
+ class TestSubtract < Test::Unit::TestCase
5
+ def setup
6
+ @interpreter = Interpreter.new
7
+ end
8
+
9
+ should "subtract numbers properly" do
10
+ result = @interpreter.run("(- 10 5)")
11
+ assert_equal(5, result[0])
12
+ end
13
+
14
+ should "subtract multiple numbers" do
15
+ result = @interpreter.run("(- 20 1 2 3 4 5)")
16
+ assert_equal(5, result[0])
17
+ end
18
+
19
+ should "subtract defined variables" do
20
+ result = @interpreter.run(
21
+ "(define a 4)
22
+ (define b 2)
23
+ (- a b)")
24
+ assert_equal(2, result[0])
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,49 @@
1
+ require 'helper'
2
+
3
+ module Theta
4
+ class TestEnvironment < Test::Unit::TestCase
5
+ def setup
6
+ @environment = Environment.new
7
+ end
8
+
9
+ context "new" do
10
+ should "add definition for true" do
11
+ result = @environment.find("#t".to_sym)
12
+ assert_equal(true, result)
13
+ end
14
+
15
+ should "add definition for false" do
16
+ result = @environment.find("#f".to_sym)
17
+ assert_equal(false, result)
18
+ end
19
+ end
20
+
21
+ context "find" do
22
+ should "return nil for undefined item" do
23
+ result = @environment.find(:a)
24
+ assert_equal(nil, result)
25
+ end
26
+
27
+ should "return defined item" do
28
+ @environment.define(:a, 1)
29
+ result = @environment.find(:a)
30
+ assert_equal(1, result)
31
+ end
32
+
33
+ should "return item defined in parent" do
34
+ @environment.define(:a, 1)
35
+ child = Environment.new(@environment)
36
+ result = child.find(:a)
37
+ assert_equal(1, result)
38
+ end
39
+
40
+ should "override a variable in parent if defined in current" do
41
+ @environment.define(:a, 1)
42
+ child = Environment.new(@environment)
43
+ child.define(:a, 3)
44
+ result = child.find(:a)
45
+ assert_equal(3, result)
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,29 @@
1
+ require 'helper'
2
+
3
+ module Theta
4
+ class TestInterpreter < Test::Unit::TestCase
5
+ def setup
6
+ @interpreter = Interpreter.new
7
+ end
8
+
9
+ context "run" do
10
+ should "run code and return a result" do
11
+ result = @interpreter.run("(+ 0 1)")
12
+ assert_equal(1, result[0])
13
+ end
14
+ end
15
+
16
+ context "evaluate" do
17
+ should "allow definition of items" do
18
+ @interpreter.evaluate([:define, :a, 1])
19
+ assert_equal(1, @interpreter.current_environment.find(:a))
20
+ end
21
+
22
+ should "allow ruby functions" do
23
+ result = @interpreter.evaluate([:ruby_func,
24
+ "lambda { 1 }"])
25
+ assert_equal(1, result.call)
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,59 @@
1
+ require 'helper'
2
+
3
+ module Theta
4
+ class TestParser < Test::Unit::TestCase
5
+ def setup
6
+ @parser = Parser.new
7
+ end
8
+
9
+ context "atom" do
10
+ should "convert string to symbol" do
11
+ result = @parser.atom("test")
12
+ assert_equal(:test, result)
13
+ end
14
+
15
+ should "leave numbers alone" do
16
+ result = @parser.atom("1")
17
+ assert_equal(1, result)
18
+ end
19
+ end
20
+
21
+ context "tokenize" do
22
+ should "split a statement apart into its component pieces" do
23
+ result = @parser.tokenize("(define a 1)")
24
+ assert_equal(["(", "define", "a", "1", ")"], result)
25
+ end
26
+
27
+ should "be able to handle multiple statements at a time" do
28
+ result = @parser.tokenize("(define a 1) (define b 2)")
29
+ assert_equal(["(", "define", "a", "1", ")","(", "define", "b", "2", ")"], result)
30
+ end
31
+ end
32
+
33
+ context "read_from" do
34
+ should "turn a tokenized statement into an interpreter understandable array" do
35
+ result = @parser.read_from(["(", "define", "a", "1", ")"])
36
+ assert_equal([[:define, :a, 1]], result)
37
+ end
38
+
39
+ should "be able to handle multiple statements at a time" do
40
+ tokens = @parser.tokenize("(define a 1) (define b 2)")
41
+ result = @parser.read_from(tokens)
42
+ assert_equal([[:define, :a, 1], [:define, :b, 2]], result)
43
+ end
44
+
45
+ should "be able to handle nested expressions" do
46
+ tokens = @parser.tokenize("(+ 2 (+ 3 5))")
47
+ result = @parser.read_from(tokens)
48
+ assert_equal([[:+, 2, [:+, 3, 5]]], result)
49
+ end
50
+ end
51
+
52
+ context "to_string" do
53
+ should "turn a parsed statement back into a human readable format" do
54
+ result = @parser.to_string([:define, :a, 1])
55
+ assert_equal("(define a 1)", result)
56
+ end
57
+ end
58
+ end
59
+ end
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{theta}
8
- s.version = "0.1.3"
8
+ s.version = "0.1.4"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Chris O'Neal"]
12
- s.date = %q{2011-06-10}
12
+ s.date = %q{2011-06-20}
13
13
  s.description = %q{Theta was created as a learning project based off of lis.py and flea}
14
14
  s.email = %q{ctoneal@gmail.com}
15
15
  s.executables = ["theta", "theta"]
@@ -30,19 +30,37 @@ Gem::Specification.new do |s|
30
30
  "lib/theta/environment.rb",
31
31
  "lib/theta/interpreter.rb",
32
32
  "lib/theta/library/add.scm",
33
+ "lib/theta/library/display.scm",
33
34
  "lib/theta/library/divide.scm",
34
35
  "lib/theta/library/equality.scm",
35
36
  "lib/theta/library/greater_than.scm",
36
37
  "lib/theta/library/greater_than_or_equal.scm",
37
38
  "lib/theta/library/if.scm",
39
+ "lib/theta/library/input.scm",
38
40
  "lib/theta/library/lambda.scm",
39
41
  "lib/theta/library/less_than.scm",
40
42
  "lib/theta/library/less_than_or_equal.scm",
41
43
  "lib/theta/library/multiply.scm",
42
44
  "lib/theta/library/set.scm",
45
+ "lib/theta/library/string-to-num.scm",
43
46
  "lib/theta/library/subtract.scm",
44
47
  "lib/theta/parser.rb",
45
48
  "test/helper.rb",
49
+ "test/library/test_add.rb",
50
+ "test/library/test_divide.rb",
51
+ "test/library/test_equality.rb",
52
+ "test/library/test_greater_than.rb",
53
+ "test/library/test_greater_than_or_equal.rb",
54
+ "test/library/test_if.rb",
55
+ "test/library/test_lambda.rb",
56
+ "test/library/test_less_than.rb",
57
+ "test/library/test_less_than_or_equal.rb",
58
+ "test/library/test_multiply.rb",
59
+ "test/library/test_set.rb",
60
+ "test/library/test_subtract.rb",
61
+ "test/test_environment.rb",
62
+ "test/test_interpreter.rb",
63
+ "test/test_parser.rb",
46
64
  "theta.gemspec"
47
65
  ]
48
66
  s.homepage = %q{http://github.com/ctoneal/theta}
@@ -51,7 +69,22 @@ Gem::Specification.new do |s|
51
69
  s.rubygems_version = %q{1.6.2}
52
70
  s.summary = %q{Theta is a Lisp interpreter in Ruby}
53
71
  s.test_files = [
54
- "test/helper.rb"
72
+ "test/helper.rb",
73
+ "test/library/test_add.rb",
74
+ "test/library/test_divide.rb",
75
+ "test/library/test_equality.rb",
76
+ "test/library/test_greater_than.rb",
77
+ "test/library/test_greater_than_or_equal.rb",
78
+ "test/library/test_if.rb",
79
+ "test/library/test_lambda.rb",
80
+ "test/library/test_less_than.rb",
81
+ "test/library/test_less_than_or_equal.rb",
82
+ "test/library/test_multiply.rb",
83
+ "test/library/test_set.rb",
84
+ "test/library/test_subtract.rb",
85
+ "test/test_environment.rb",
86
+ "test/test_interpreter.rb",
87
+ "test/test_parser.rb"
55
88
  ]
56
89
 
57
90
  if s.respond_to? :specification_version then
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: theta
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.3
4
+ version: 0.1.4
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,12 +9,12 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2011-06-10 00:00:00.000000000 -05:00
12
+ date: 2011-06-20 00:00:00.000000000 -05:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: shoulda
17
- requirement: &7339260 !ruby/object:Gem::Requirement
17
+ requirement: &2053176 !ruby/object:Gem::Requirement
18
18
  none: false
19
19
  requirements:
20
20
  - - ! '>='
@@ -22,10 +22,10 @@ dependencies:
22
22
  version: '0'
23
23
  type: :development
24
24
  prerelease: false
25
- version_requirements: *7339260
25
+ version_requirements: *2053176
26
26
  - !ruby/object:Gem::Dependency
27
27
  name: bundler
28
- requirement: &7338048 !ruby/object:Gem::Requirement
28
+ requirement: &2051736 !ruby/object:Gem::Requirement
29
29
  none: false
30
30
  requirements:
31
31
  - - ~>
@@ -33,10 +33,10 @@ dependencies:
33
33
  version: 1.0.0
34
34
  type: :development
35
35
  prerelease: false
36
- version_requirements: *7338048
36
+ version_requirements: *2051736
37
37
  - !ruby/object:Gem::Dependency
38
38
  name: jeweler
39
- requirement: &7336620 !ruby/object:Gem::Requirement
39
+ requirement: &2050476 !ruby/object:Gem::Requirement
40
40
  none: false
41
41
  requirements:
42
42
  - - ~>
@@ -44,10 +44,10 @@ dependencies:
44
44
  version: 1.5.2
45
45
  type: :development
46
46
  prerelease: false
47
- version_requirements: *7336620
47
+ version_requirements: *2050476
48
48
  - !ruby/object:Gem::Dependency
49
49
  name: rcov
50
- requirement: &7334976 !ruby/object:Gem::Requirement
50
+ requirement: &2048832 !ruby/object:Gem::Requirement
51
51
  none: false
52
52
  requirements:
53
53
  - - ! '>='
@@ -55,7 +55,7 @@ dependencies:
55
55
  version: '0'
56
56
  type: :development
57
57
  prerelease: false
58
- version_requirements: *7334976
58
+ version_requirements: *2048832
59
59
  description: Theta was created as a learning project based off of lis.py and flea
60
60
  email: ctoneal@gmail.com
61
61
  executables:
@@ -78,19 +78,37 @@ files:
78
78
  - lib/theta/environment.rb
79
79
  - lib/theta/interpreter.rb
80
80
  - lib/theta/library/add.scm
81
+ - lib/theta/library/display.scm
81
82
  - lib/theta/library/divide.scm
82
83
  - lib/theta/library/equality.scm
83
84
  - lib/theta/library/greater_than.scm
84
85
  - lib/theta/library/greater_than_or_equal.scm
85
86
  - lib/theta/library/if.scm
87
+ - lib/theta/library/input.scm
86
88
  - lib/theta/library/lambda.scm
87
89
  - lib/theta/library/less_than.scm
88
90
  - lib/theta/library/less_than_or_equal.scm
89
91
  - lib/theta/library/multiply.scm
90
92
  - lib/theta/library/set.scm
93
+ - lib/theta/library/string-to-num.scm
91
94
  - lib/theta/library/subtract.scm
92
95
  - lib/theta/parser.rb
93
96
  - test/helper.rb
97
+ - test/library/test_add.rb
98
+ - test/library/test_divide.rb
99
+ - test/library/test_equality.rb
100
+ - test/library/test_greater_than.rb
101
+ - test/library/test_greater_than_or_equal.rb
102
+ - test/library/test_if.rb
103
+ - test/library/test_lambda.rb
104
+ - test/library/test_less_than.rb
105
+ - test/library/test_less_than_or_equal.rb
106
+ - test/library/test_multiply.rb
107
+ - test/library/test_set.rb
108
+ - test/library/test_subtract.rb
109
+ - test/test_environment.rb
110
+ - test/test_interpreter.rb
111
+ - test/test_parser.rb
94
112
  - theta.gemspec
95
113
  has_rdoc: true
96
114
  homepage: http://github.com/ctoneal/theta
@@ -108,7 +126,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
108
126
  version: '0'
109
127
  segments:
110
128
  - 0
111
- hash: 260486451
129
+ hash: -440596785
112
130
  required_rubygems_version: !ruby/object:Gem::Requirement
113
131
  none: false
114
132
  requirements:
@@ -123,3 +141,18 @@ specification_version: 3
123
141
  summary: Theta is a Lisp interpreter in Ruby
124
142
  test_files:
125
143
  - test/helper.rb
144
+ - test/library/test_add.rb
145
+ - test/library/test_divide.rb
146
+ - test/library/test_equality.rb
147
+ - test/library/test_greater_than.rb
148
+ - test/library/test_greater_than_or_equal.rb
149
+ - test/library/test_if.rb
150
+ - test/library/test_lambda.rb
151
+ - test/library/test_less_than.rb
152
+ - test/library/test_less_than_or_equal.rb
153
+ - test/library/test_multiply.rb
154
+ - test/library/test_set.rb
155
+ - test/library/test_subtract.rb
156
+ - test/test_environment.rb
157
+ - test/test_interpreter.rb
158
+ - test/test_parser.rb