theta 0.1.3 → 0.1.4

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.
@@ -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