From 1757aacde64adb5598a2b456035a1726269dbda5 Mon Sep 17 00:00:00 2001 From: Scott Steadman Date: Fri, 15 Aug 2014 11:10:01 -0700 Subject: [PATCH 1/4] Implemented quote. My knowledge of scheme is primitive. So I only created a simple test. --- README.md | 2 +- lib/lisp.rb | 3 +++ test/test_lisp.rb | 5 +++++ 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index d234942..c034411 100644 --- a/README.md +++ b/README.md @@ -45,7 +45,7 @@ Features - [x] __conditional__ - (if test conseq alt) Evaluate test; if true, evaluate and return conseq; otherwise evaluate and return alt. _Example: (if (< 10 20) (+ 1 1) (+ 3 3)) ⇒ 2_ -- [ ] __quotation__ - (quote exp) Return the exp literally; do not evaluate it. _Example: (quote (a b c)) ⇒ (a b c)_ +- [x] __quotation__ - (quote exp) Return the exp literally; do not evaluate it. _Example: (quote (a b c)) ⇒ (a b c)_ - [ ] __assignment__ - (set! var exp) Evaluate exp and assign that value to var, which must have been previously defined (with a define or as a parameter to an enclosing procedure). _Example: (set! x2 (* x x))_ diff --git a/lib/lisp.rb b/lib/lisp.rb index b943d9e..fbc2cdf 100644 --- a/lib/lisp.rb +++ b/lib/lisp.rb @@ -52,6 +52,9 @@ def self.execute(exp, scope = global) _, test, conseq, alt = exp exp = execute(test, scope) ? conseq : alt execute(exp, scope) + when :quote + _, exp = exp + exp else func, *args = exp.map { |exp| execute(exp, scope) } func.call(*args) diff --git a/test/test_lisp.rb b/test/test_lisp.rb index c78141d..1f2bd45 100755 --- a/test/test_lisp.rb +++ b/test/test_lisp.rb @@ -54,4 +54,9 @@ def test_lambda Lisp.eval("(define fact (lambda (n) (if (<= n 1) 1 (* n (fact (- n 1))))))") assert_equal 3628800, Lisp.eval("(fact 10)") end + + def test_quote + assert_equal [:a, :b, :c], Lisp.eval('(quote (a b c))') + end + end From 2447a4e95f3193ebd3810abb80611ece0948379c Mon Sep 17 00:00:00 2001 From: Scott Steadman Date: Fri, 15 Aug 2014 11:26:44 -0700 Subject: [PATCH 2/4] Implemented assignment. --- README.md | 2 +- lib/lisp.rb | 6 ++++++ test/test_lisp.rb | 11 +++++++++++ 3 files changed, 18 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index c034411..4f00e6e 100644 --- a/README.md +++ b/README.md @@ -47,6 +47,6 @@ Features - [x] __quotation__ - (quote exp) Return the exp literally; do not evaluate it. _Example: (quote (a b c)) ⇒ (a b c)_ -- [ ] __assignment__ - (set! var exp) Evaluate exp and assign that value to var, which must have been previously defined (with a define or as a parameter to an enclosing procedure). _Example: (set! x2 (* x x))_ +- [x] __assignment__ - (set! var exp) Evaluate exp and assign that value to var, which must have been previously defined (with a define or as a parameter to an enclosing procedure). _Example: (set! x2 (* x x))_ - [ ] __sequencing__ - (begin exp...) Evaluate each of the expressions in left-to-right order, and return the final value. _Example: (begin (set! x 1) (set! x (+ x 1)) (* x 2)) ⇒ 4_ diff --git a/lib/lisp.rb b/lib/lisp.rb index fbc2cdf..0e8ea7f 100644 --- a/lib/lisp.rb +++ b/lib/lisp.rb @@ -55,6 +55,12 @@ def self.execute(exp, scope = global) when :quote _, exp = exp exp + when :set! + _, var, exp = exp + unless scope.has_key?(var) + raise "#{var} must be defined before you can set! it" + end + scope[var] = execute(exp, scope) else func, *args = exp.map { |exp| execute(exp, scope) } func.call(*args) diff --git a/test/test_lisp.rb b/test/test_lisp.rb index 1f2bd45..63a81e8 100755 --- a/test/test_lisp.rb +++ b/test/test_lisp.rb @@ -59,4 +59,15 @@ def test_quote assert_equal [:a, :b, :c], Lisp.eval('(quote (a b c))') end + def test_assignment + ex = assert_raises(RuntimeError) { Lisp.eval('(set! foo 42)') } + assert_equal 'foo must be defined before you can set! it', ex.message + + Lisp.eval('(define foo 3.14)') + assert_equal 42, Lisp.eval('(set! foo 42)') + assert_equal 42, Lisp.eval('(* 1 foo)') + + assert_equal -42, Lisp.eval('(set! foo (* -1 foo))') + end + end From d65de5e79412e26bac82d70797ca20ff1a3a2750 Mon Sep 17 00:00:00 2001 From: Scott Steadman Date: Fri, 15 Aug 2014 11:40:04 -0700 Subject: [PATCH 3/4] Implemented sequencing. --- README.md | 2 +- lib/lisp.rb | 3 +++ test/test_lisp.rb | 4 ++++ 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 4f00e6e..421b5f6 100644 --- a/README.md +++ b/README.md @@ -49,4 +49,4 @@ Features - [x] __assignment__ - (set! var exp) Evaluate exp and assign that value to var, which must have been previously defined (with a define or as a parameter to an enclosing procedure). _Example: (set! x2 (* x x))_ -- [ ] __sequencing__ - (begin exp...) Evaluate each of the expressions in left-to-right order, and return the final value. _Example: (begin (set! x 1) (set! x (+ x 1)) (* x 2)) ⇒ 4_ +- [x] __sequencing__ - (begin exp...) Evaluate each of the expressions in left-to-right order, and return the final value. _Example: (begin (define x 1) (set! x (+ x 1)) (* x 2)) ⇒ 4_ diff --git a/lib/lisp.rb b/lib/lisp.rb index 0e8ea7f..f0a9321 100644 --- a/lib/lisp.rb +++ b/lib/lisp.rb @@ -61,6 +61,9 @@ def self.execute(exp, scope = global) raise "#{var} must be defined before you can set! it" end scope[var] = execute(exp, scope) + when :begin + _ = exp.shift + exp.map { |exp| execute(exp) }.last else func, *args = exp.map { |exp| execute(exp, scope) } func.call(*args) diff --git a/test/test_lisp.rb b/test/test_lisp.rb index 63a81e8..4142070 100755 --- a/test/test_lisp.rb +++ b/test/test_lisp.rb @@ -70,4 +70,8 @@ def test_assignment assert_equal -42, Lisp.eval('(set! foo (* -1 foo))') end + def test_sequencing + assert_equal 4, Lisp.eval('(begin (define x 1) (set! x (+ x 1)) (* x 2))') + end + end From 0852dc9e2f8288571b8b57f9e532523c829b0cb1 Mon Sep 17 00:00:00 2001 From: Scott Steadman Date: Wed, 20 Aug 2014 14:56:16 -0700 Subject: [PATCH 4/4] Added display keyword. --- README.md | 2 ++ lib/lisp.rb | 7 ++++++- test/test_lisp.rb | 5 +++++ 3 files changed, 13 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 421b5f6..f88a839 100644 --- a/README.md +++ b/README.md @@ -50,3 +50,5 @@ Features - [x] __assignment__ - (set! var exp) Evaluate exp and assign that value to var, which must have been previously defined (with a define or as a parameter to an enclosing procedure). _Example: (set! x2 (* x x))_ - [x] __sequencing__ - (begin exp...) Evaluate each of the expressions in left-to-right order, and return the final value. _Example: (begin (define x 1) (set! x (+ x 1)) (* x 2)) ⇒ 4_ + +- [x] __display__ - (display exp...) Evaluate each of the expressions in left-to-right order, and write each result to STDOUT. _Example: (display Hello World! 42) ⇒ Hello World! 42_ diff --git a/lib/lisp.rb b/lib/lisp.rb index f0a9321..fb61fed 100644 --- a/lib/lisp.rb +++ b/lib/lisp.rb @@ -30,8 +30,10 @@ def self.parse(tokens, tree = []) def self.evaluator(token) case token - when /\d/ + when /\d*\.\d*/ token.to_f + when /\d/ + token.to_i else token.to_sym end @@ -64,6 +66,9 @@ def self.execute(exp, scope = global) when :begin _ = exp.shift exp.map { |exp| execute(exp) }.last + when :display + _ = exp.shift + exp.map { |exp| execute(exp) || exp }.join(' ').tap { |str| puts str } else func, *args = exp.map { |exp| execute(exp, scope) } func.call(*args) diff --git a/test/test_lisp.rb b/test/test_lisp.rb index 4142070..ff82add 100755 --- a/test/test_lisp.rb +++ b/test/test_lisp.rb @@ -74,4 +74,9 @@ def test_sequencing assert_equal 4, Lisp.eval('(begin (define x 1) (set! x (+ x 1)) (* x 2))') end + def test_display + assert_output("Hello World! 42\n") { Lisp.eval('(display Hello World! 42)') } + assert_output("Evaluated: 3.14\n") { Lisp.eval('(display Evaluated: (* 1 3.14))') } + end + end