~nch/onward

01efad4c8dfce8a7bf95aa49e3667fbf257337b4 — nc 4 years ago e9fad83
started working on prototype impl
1 files changed, 174 insertions(+), 0 deletions(-)

A prototype.rb
A prototype.rb => prototype.rb +174 -0
@@ 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