@@ 13,35 13,79 @@ section .text
%define STDIN 0
%define STDOUT 1
-
-%define putchar(x) mov
-
_start:
-repl:
- mov rax, SYS_WRITE ; write prompt
+rep_loop:
+ ;; write prompt
+ mov rax, SYS_WRITE
mov rdi, STDOUT
mov rsi, prompt
mov rdx, prompt_len
syscall
- mov rax, SYS_READ ; read
+ ;; read input string
+ mov rax, SYS_READ
mov rdi, STDIN
- mov rsi, buf_in
+ mov rsi, in_buf
mov rdx, buf_len
syscall
- mov rdx, rax ; length in rax
- cmp rdx, 0
+ mov rdx, rax ; length in rax
+
+ cmp rdx, 0 ; exit if empty (user sent Ctrl-D)
jz exit
- ;; write
- mov rax, SYS_WRITE ; echo
+ ;; get the first word in the string
+ ;; while calculating hash of string
+
+ ; update hash (sdbm hash):
+ ; for c in str:
+ ; new_hash = c + (hash << 6) + (hash << 16) - hash
+ ; hash = new_hash
+
+ mov rbx, word_buf ; word_ptr: rbx = (char *) word_buf
+ mov rcx, 0 ; hash: rcx = 0
+ ; in_buf_ptr: rsi (set above)
+
+read_next_word_loop:
+ mov rax, 0 ; clear rax
+ mov al, [rsi] ; c = *in_buf_ptr
+ cmp al, byte ' ' ; check for whitespace
+ je .end
+ cmp al, byte `\n`
+ je .end
+ cmp al, byte `\t`
+ je .end
+ mov [rbx], al ; *word_buf = c
+
+ ; rdx = new_hash (copy of hash for iteration)
+ ; rax = current character (because of al)
+
+ ; calculate hash
+ mov rdx, rcx ; rdx = hash. copied so we can shift it around
+ sal rdx, 6 ; hash << 6
+ add rax, rdx
+ sal rdx, 10 ; hash << 16
+ add rax, rdx
+ sub rax, rcx ; - hash
+ mov rcx, rax ; hash = new_hash
+
+ add rsi, 1 ; in_buf_ptr++
+ add rbx, 1 ; word_ptr++
+ jmp read_next_word_loop
+.end:
+ ; calculate len, store it in rdx
+ mov rdx, rbx
+ sub rdx, word_buf
+
+ ;; echo word
+ mov rax, SYS_WRITE
mov rdi, STDOUT
- mov rsi, buf_in
+ mov rsi, word_buf
+ ; rdx set above
syscall
- jmp repl ; loop
+ jmp rep_loop
exit:
mov rax, SYS_EXIT
@@ 49,8 93,10 @@ exit:
syscall
section .bss
- buf_in: resb 128
- buf_len: equ $ - buf_in
+ in_buf: resb 128
+ buf_len: equ $ - in_buf
+
+ word_buf: resb 32
section .rodata
prompt: db "> "
@@ 16,41 16,13 @@ end
def create_ops
ops = {}
- ops['T'] = -> (cs, ds) {ds.unshift(true)}
- ops['F'] = -> (cs, ds) {ds.unshift(false)}
- ops['spaces'] = -> (cs, ds) {
- n = ds.shift
- print ' ' * n
- }
- ops['emit'] = -> (cs, ds) {
- ascii_code = ds.shift
- print ascii_code.chr
- }
- ops['dup'] = -> (cs, ds) {
- x = ds.shift
- ds.unshift(x)
- ds.unshift(x)
- }
- ops['drop'] = -> (cs, ds) {
- ds.shift
- }
- ops['over'] = -> (cs, ds) {
- n2, n1 = ds.shift, ds.shift
- ds.unshift(n1)
- ds.unshift(n2)
- ds.unshift(n1)
- }
- ops['swap'] = -> (cs, ds) {
- b, a = ds.shift, ds.shift
- ds.unshift(b)
- ds.unshift(a)
- }
- ops['rot'] = -> (cs, ds) {
- a, b, c = ds.shift, ds.shift, ds.shift
- ds.unshift(a)
- ds.unshift(c)
- ds.unshift(b)
- }
+ ops['drop'] = -> (cs, ds) { ds.shift }
+ ops['dup'] = -> (cs, ds) { x = ds.shift; ds.unshift(x); ds.unshift(x) }
+ ops['swap'] = -> (cs, ds) { b, a = ds.shift, ds.shift; ds.unshift(b); ds.unshift(a) }
+ ops['over'] = -> (cs, ds) { n2, n1 = ds.shift, ds.shift; ds.unshift(n1); ds.unshift(n2); ds.unshift(n1); }
+ ops['rot'] = -> (cs, ds) { a, b, c = ds.shift, ds.shift, ds.shift; ds.unshift(a); ds.unshift(c); ds.unshift(b) }
+ ops['-rot'] = -> (cs, ds) { a, b, c = ds.shift, ds.shift, ds.shift; ds.unshift(b); ds.unshift(a); ds.unshift(c) }
+ ops['?dup'] = -> (cs, ds) { x = ds.shift; if x then ds.unshift(x); ds.unshift(x) end}
ops['and'] = -> (cs, ds) {
b, a = ds.shift, ds.shift
ds.unshift(b && a)
@@ 63,6 35,10 @@ def create_ops
b, a = ds.shift, ds.shift
ds.unshift(b ^ a)
}
+
+ ops['T'] = -> (cs, ds) {ds.unshift(true)}
+ ops['F'] = -> (cs, ds) {ds.unshift(false)}
+ ops['emit'] = -> (cs, ds) { ascii_code = ds.shift; print ascii_code.chr }
ops['invert'] = -> (cs, ds) {
ds.unshift(!ds.shift)
}
@@ 78,7 54,7 @@ def create_ops
b, a = ds.shift, ds.shift
ds.unshift(a * b)
}
- ops['$words'] = -> (cs, ds) {
+ ops['_words'] = -> (cs, ds) {
puts ops.keys.join ', '
}
ops[':'] = -> (cs, ds) {
@@ 88,8 64,9 @@ def create_ops
while (t = cs.shift) != ';'
insts.push(t)
end
- ops[word] = -> (cs, ds) {
- feval(insts, ds)
+ ops[word] = -> (_cs, _ds) {
+ # dup so feval doesn't delete the original definition when popping
+ feval(insts.dup, ops, ds)
}
}
ops['/'] = -> (cs, ds) {
@@ 112,17 89,21 @@ def create_ops
b, a = ds.shift, ds.shift
ds.unshift(a % b)
}
+ ops['('] = -> (cs, ds) {
+ while cs.shift != ')'
+ end
+ }
ops['2*'] = -> (cs, ds) {ds.unshift(ds.shift * 2)}
ops['2/'] = -> (cs, ds) {ds.unshift(ds.shift / 2)}
ops['0='] = -> (cs, ds) {ds.unshift(ds.shift == 0)}
- ops['print'] = -> (cs, ds) {print ds.shift}
+ ops['.'] = -> (cs, ds) {print ds.shift}
ops
end
-# input string, data stack -> data_stack
-def feval(cstack, dstack = [], ops = create_ops)
+# control stack, ops list, data stack -> data_stack
+def feval(cstack, ops = create_ops, dstack = [])
while (word = cstack.shift)
- if word.is_a? Integer # we get a bit of type safety in ruby
+ if word.is_a? Integer # we get a bit of extra type safety in ruby
dstack.unshift word
else
raise "no word '#{word}'" if not ops.has_key? word
@@ 143,11 124,11 @@ def repl
input.chomp!
cstack = tokenize(input)
begin
- feval(cstack, dstack, ops)
+ feval(cstack, ops, dstack)
rescue
- puts $!
+ $stderr.puts $!
else
- puts " ok"
+ $stderr.puts " ok"
end
end
end
@@ 155,87 136,79 @@ end
require "test/unit"
class TestEval < Test::Unit::TestCase
- def test_plus
- dstack = feval(tokenize('1 5 +'))
- assert_equal(6, dstack[0])
+ def assert_eval(final_dstack, prog)
+ dstack = feval(tokenize(prog))
+ assert_equal(final_dstack, dstack)
end
- def test_plus_2
- dstack = feval(tokenize('3 1 5 + +'))
- assert_equal(9, dstack[0])
+ def assert_eval_stdout(stdout, prog, ops=create_ops)
+ $stdout = StringIO.new
+ feval(tokenize(prog), ops=ops)
+ assert_equal(stdout, $stdout.string)
end
- def test_print
- $stdout = StringIO.new
- feval(tokenize('3 1 5 + + print'))
- assert_equal($stdout.string, '9')
+ def test_plus
+ assert_eval([6], '1 5 +')
end
- def test_div
- dstack = feval(tokenize('6 2 /'))
- assert_equal(3, dstack[0])
+ def test_plus_2
+ assert_eval([9], '3 1 5 + +')
end
- def test_mod
- dstack = feval(tokenize('6 2 mod'))
- assert_equal(0, dstack[0])
+ def test_print
+ assert_eval_stdout('9', '3 1 5 + + .')
end
- def test_2times
- dstack = feval(tokenize('6 2*'))
- assert_equal(12, dstack[0])
+ def test_math
+ assert_eval([3], '6 2 /')
+ assert_eval([0], '6 2 mod')
+ assert_eval([12], '6 2*')
end
def test_cmp
- dstack = feval(tokenize('1 0='))
- assert_equal(false, dstack[0])
- dstack = feval(tokenize('0 0='))
- assert_equal(true, dstack[0])
- dstack = feval(tokenize('2 1 >'))
- assert_equal(true, dstack[0])
- dstack = feval(tokenize('1 2 <'))
- assert_equal(true, dstack[0])
- dstack = feval(tokenize('1 2 >'))
- assert_equal(false, dstack[0])
- dstack = feval(tokenize('2 1 <'))
- assert_equal(false, dstack[0])
- dstack = feval(tokenize('2 1 ='))
- assert_equal(false, dstack[0])
- dstack = feval(tokenize('1 1 ='))
- assert_equal(true, dstack[0])
- dstack = feval(tokenize('T T and'))
- assert_equal(true, dstack[0])
- dstack = feval(tokenize('T F and'))
- assert_equal(false, dstack[0])
- dstack = feval(tokenize('T F or'))
- assert_equal(true, dstack[0])
- dstack = feval(tokenize('F F or'))
- assert_equal(false, dstack[0])
- dstack = feval(tokenize('T F xor'))
- assert_equal(true, dstack[0])
- dstack = feval(tokenize('T T xor'))
- assert_equal(false, dstack[0])
- dstack = feval(tokenize('T invert T and'))
- assert_equal(false, dstack[0])
+ assert_eval([false], '6 0=')
+ assert_eval([true], '0 0=')
+ assert_eval([true], '2 1 >')
+ assert_eval([false], '2 1 <')
+ assert_eval([true], '2 2 =')
+ assert_eval([true], 'T T and')
+ assert_eval([true], 'T F or')
+ assert_eval([false], 'F F xor')
+ assert_eval([false], 'F F xor')
+ assert_eval([false], 'T invert T and')
end
def test_juggling # top of dstack is to the right,
- dstack = feval(tokenize('1 2 dup'))
- assert_equal([2, 2, 1], dstack)
- dstack = feval(tokenize('1 2 swap'))
- assert_equal([1, 2], dstack)
- dstack = feval(tokenize('2 6 swap /'))
- assert_equal([3], dstack)
- dstack = feval(tokenize('1 2 over'))
- assert_equal([1, 2, 1], dstack)
- dstack = feval(tokenize('3 2 1 rot'))
- assert_equal([2, 3, 1], dstack)
+ assert_eval([2, 2, 1], '1 2 dup')
+ assert_eval([1, 2], '1 2 swap')
+ assert_eval([3], '2 6 swap /')
+ assert_eval([1, 2, 1], '1 2 over')
+ assert_eval([2, 3, 1], '3 2 1 rot')
end
def test_compile
- dstack = feval(tokenize(': add1 1 + ; 5 add1'))
- assert_equal([6], dstack)
+ assert_eval([7, 6], ': add1 1 + ; 5 add1 6 add1')
+ end
+
+ def test_comment
+ assert_eval([], '( plz do nothing )')
+ assert_eval([], '( + 1 2 )')
+ assert_eval([2], '1 ( + 1 2 ) 1 +')
+ end
+
+=begin
+ def test_starting_forth_1
+ ops = create_ops
+ feval(tokenize(': star 42 emit ;'), ops=ops)
+ assert_true(ops.has_key? 'star')
+ assert_eval_stdout("\n*\n*\n*", 'cr star cr star cr star', ops=ops)
+ feval(tokenize(': margin cr 30 spaces ;'), ops=ops)
+ assert_eval_stdout("\n#{' ' * 30}*"*3, 'margin star margin star margin star', ops=ops)
+ feval(tokenize(': blip margin star ;'), ops=ops)
+ feval(tokenize(': stars 0 do star loop ;'), ops=ops)
+ assert_eval_stdout("*"*5, '5 stars', ops=ops)
end
+=end
end
repl