@@ 0,0 1,174 @@
+# prototype implementation of Forth
+
+def is_num?(x)
+ x !~ /\D/
+end
+
+def feval(str)
+ stack = []
+ ops = {
+ 'T' => -> {stack.push(true)},
+ 'F' => -> {stack.push(false)},
+ 'dup' => -> {
+ x = stack.pop
+ stack.push(x)
+ stack.push(x)
+ },
+ 'drop' => -> {
+ stack.pop
+ },
+ 'over' => -> {
+ n2, n1 = stack.pop, stack.pop
+ stack.push(n1)
+ stack.push(n2)
+ stack.push(n1)
+ },
+ 'swap' => -> {
+ b, a = stack.pop, stack.pop
+ stack.push(b)
+ stack.push(a)
+ },
+ 'rot' => -> {
+ c, b, a = stack.pop, stack.pop, stack.pop
+ stack.push(b)
+ stack.push(c)
+ stack.push(a)
+ },
+ 'and' => -> {
+ b, a = stack.pop, stack.pop
+ stack.push(b && a)
+ },
+ 'or' => -> {
+ b, a = stack.pop, stack.pop
+ stack.push(b || a)
+ },
+ 'xor' => -> {
+ b, a = stack.pop, stack.pop
+ stack.push(b ^ a)
+ },
+ 'invert' => -> {
+ stack.push(!stack.pop)
+ },
+ '+' => -> {
+ b, a = stack.pop, stack.pop
+ stack.push(a + b)
+ },
+ '*' => -> {
+ b, a = stack.pop, stack.pop
+ stack.push(a * b)
+ },
+ '/' => -> {
+ b, a = stack.pop, stack.pop
+ stack.push(a / b)
+ },
+ '<' => -> {
+ b, a = stack.pop, stack.pop
+ stack.push(a < b)
+ },
+ '>' => -> {
+ b, a = stack.pop, stack.pop
+ stack.push(a > b)
+ },
+ '=' => -> {
+ b, a = stack.pop, stack.pop
+ stack.push(a == b)
+ },
+ 'mod' => -> {
+ b, a = stack.pop, stack.pop
+ stack.push(a % b)
+ },
+ '2*' => -> {stack.push(stack.pop * 2)},
+ '2/' => -> {stack.push(stack.pop / 2)},
+ '0=' => -> {stack.push(stack.pop == 0)},
+ 'print' => -> {print stack.pop}
+ }
+ str.split(' ').each do |x|
+ if is_num? x
+ stack.push x.to_i
+ else
+ ops[x].call
+ end
+ end
+ stack
+end
+
+require "test/unit"
+
+class TestEval < Test::Unit::TestCase
+ def test_plus
+ stack = feval('1 5 +')
+ assert_equal(6, stack[0])
+ end
+
+ def test_plus_2
+ stack = feval('3 1 5 + +')
+ assert_equal(9, stack[0])
+ end
+
+ def test_print
+ $stdout = StringIO.new
+ feval('3 1 5 + + print')
+ assert_equal($stdout.string, '9')
+ end
+
+ def test_div
+ stack = feval('6 2 /')
+ assert_equal(3, stack[0])
+ end
+
+ def test_mod
+ stack = feval('6 2 mod')
+ assert_equal(0, stack[0])
+ end
+
+ def test_2times
+ stack = feval('6 2*')
+ assert_equal(12, stack[0])
+ end
+
+ def test_cmp
+ stack = feval('1 0=')
+ assert_equal(false, stack[0])
+ stack = feval('0 0=')
+ assert_equal(true, stack[0])
+ stack = feval('2 1 >')
+ assert_equal(true, stack[0])
+ stack = feval('1 2 <')
+ assert_equal(true, stack[0])
+ stack = feval('1 2 >')
+ assert_equal(false, stack[0])
+ stack = feval('2 1 <')
+ assert_equal(false, stack[0])
+ stack = feval('2 1 =')
+ assert_equal(false, stack[0])
+ stack = feval('1 1 =')
+ assert_equal(true, stack[0])
+ stack = feval('T T and')
+ assert_equal(true, stack[0])
+ stack = feval('T F and')
+ assert_equal(false, stack[0])
+ stack = feval('T F or')
+ assert_equal(true, stack[0])
+ stack = feval('F F or')
+ assert_equal(false, stack[0])
+ stack = feval('T F xor')
+ assert_equal(true, stack[0])
+ stack = feval('T T xor')
+ assert_equal(false, stack[0])
+ stack = feval('T invert T and')
+ assert_equal(false, stack[0])
+ end
+
+ def test_juggling # order is right to left
+ stack = feval('1 2 dup')
+ assert_equal([1, 2, 2], stack)
+ stack = feval('1 2 swap')
+ assert_equal([2, 1], stack)
+ stack = feval('1 2 over')
+ assert_equal([1, 2, 1], stack)
+ stack = feval('1 2 3 rot')
+ assert_equal([2, 3, 1], stack)
+ end
+end
+
+