5666 - start of sugar for structured control flow
This commit is contained in:
parent
368212b1c3
commit
6ecddbaa92
|
@ -33,13 +33,12 @@ clear-stream: # f : (address stream) -> <void>
|
|||
c7 0/subop/copy 1/mod/*+disp8 0/rm32/eax . . . . 4/disp8 0/imm32 # copy to *(eax+4)
|
||||
# eax = f->data
|
||||
81 0/subop/add 3/mod/direct 0/rm32/eax . . . . . 0xc/imm32 # add to eax
|
||||
# while (true)
|
||||
$clear-stream:loop:
|
||||
# if (eax >= ecx) break
|
||||
39/compare 3/mod/direct 0/rm32/eax . . . 1/r32/ecx . . # compare eax with ecx
|
||||
73/jump-if-greater-or-equal-unsigned $clear-stream:end/disp8
|
||||
# *eax = 0
|
||||
c6 0/subop/copy 0/mod/direct 0/rm32/eax . . . . . 0/imm8 # copy byte to *eax
|
||||
c6 0/subop/copy-byte 0/mod/direct 0/rm32/eax . . . . . 0/imm8 # copy byte to *eax
|
||||
# ++eax
|
||||
40/increment-eax
|
||||
eb/jump $clear-stream:loop/disp8
|
||||
|
|
Binary file not shown.
|
@ -0,0 +1,337 @@
|
|||
# Structured control flow using break/loop rather than jump.
|
||||
#
|
||||
# To run (on Linux):
|
||||
# $ ./ntranslate init.linux 0*.subx apps/subx-common.subx apps/calls.subx
|
||||
# $ mv a.elf apps/calls
|
||||
#
|
||||
# Example 1:
|
||||
# $ cat x.subx
|
||||
# {
|
||||
# 7c/if-lesser break/disp8
|
||||
# 74/if-equal loop/disp8
|
||||
# }
|
||||
# $ cat x.subx |apps/calls
|
||||
# _loop1:
|
||||
# 7c/if-lesser _break1/disp8
|
||||
# 74/if-equal _loop1/disp8
|
||||
# _break1:
|
||||
#
|
||||
# Example 2:
|
||||
# $ cat x.subx
|
||||
# {
|
||||
# 7c/if-lesser break/disp8
|
||||
# }
|
||||
# {
|
||||
# 74/if-equal loop/disp8
|
||||
# }
|
||||
# $ cat x.subx |apps/calls
|
||||
# _loop1:
|
||||
# 7c/if-lesser _break1/disp8
|
||||
# _break1:
|
||||
# _loop2:
|
||||
# 74/if-equal _loop2/disp8
|
||||
# _break2:
|
||||
#
|
||||
# Example 3:
|
||||
# $ cat x.subx
|
||||
# {
|
||||
# {
|
||||
# 74/if-equal loop/disp8
|
||||
# }
|
||||
# 7c/if-lesser loop/disp8
|
||||
# }
|
||||
# $ cat x.subx |apps/calls
|
||||
# _loop1:
|
||||
# _loop2:
|
||||
# 74/if-equal _loop2/disp8
|
||||
# _break2:
|
||||
# 7c/if-lesser _loop1/disp8
|
||||
# _break1:
|
||||
|
||||
== code
|
||||
|
||||
Entry: # run tests if necessary, a REPL if not
|
||||
# . prolog
|
||||
89/<- %ebp 4/r32/esp
|
||||
# initialize heap
|
||||
(new-segment Heap-size Heap)
|
||||
# if (argc <= 1) goto run-main
|
||||
81 7/subop/compare *ebp 1/imm32
|
||||
7e/jump-if-lesser-or-equal $run-main/disp8
|
||||
# if (argv[1] != "test")) goto run-main
|
||||
(kernel-string-equal? *(ebp+8) "test") # => eax
|
||||
3d/compare-eax-and 0/imm32
|
||||
74/jump-if-equal $run-main/disp8
|
||||
#
|
||||
(run-tests)
|
||||
# syscall(exit, *Num-test-failures)
|
||||
8b/-> *Num-test-failures 3/r32/ebx
|
||||
eb/jump $main:end/disp8
|
||||
$run-main:
|
||||
(convert Stdin Stdout)
|
||||
# syscall(exit, 0)
|
||||
bb/copy-to-ebx 0/imm32
|
||||
$main:end:
|
||||
b8/copy-to-eax 1/imm32/exit
|
||||
cd/syscall 0x80/imm8
|
||||
|
||||
convert: # in : (address buffered-file), out : (address buffered-file) -> <void>
|
||||
# pseudocode:
|
||||
# var line = new-stream(512, 1)
|
||||
# var label-stack : (address stack) = new-stack(32*4) # at most 32 levels of nesting
|
||||
# var next-label-id : int = 1
|
||||
# while true
|
||||
# clear-stream(line)
|
||||
# read-line-buffered(in, line)
|
||||
# if (line->write == 0) break # end of file
|
||||
# skip-chars-matching-whitespace(line)
|
||||
# if line->data[line->read] == '{'
|
||||
# print(out, "_loop" next-label-id ":\n")
|
||||
# push(label-stack, next-label-id)
|
||||
# ++next-label-id
|
||||
# if line->data[line->read] == '}'
|
||||
# var top = pop(label-stack)
|
||||
# print(out, "_break" top ":\n")
|
||||
# while true
|
||||
# var word-slice : (address slice) = next-word-or-expression(line)
|
||||
# if slice-empty?(word-slice) # end of line
|
||||
# break
|
||||
# if slice-starts-with?(word-slice, "#") # comment
|
||||
# continue
|
||||
# if slice-starts-with?(word-slice, "break/")
|
||||
# var top = top(label-stack)
|
||||
# write(out, "_break" top)
|
||||
# word-slice->start += len("break")
|
||||
# else if slice-starts-with?(word-slice, "loop/")
|
||||
# var top = top(label-stack)
|
||||
# write(out, "_loop" top)
|
||||
# word-slice->start += len("loop")
|
||||
# write(out, word-slice " ")
|
||||
# write(out, "\n")
|
||||
# flush(out)
|
||||
# . prolog
|
||||
55/push-ebp
|
||||
89/<- %ebp 4/r32/esp
|
||||
# . save registers
|
||||
$convert:loop:
|
||||
eb/jump $convert:loop/disp8
|
||||
$convert:end:
|
||||
# . restore registers
|
||||
# . epilog
|
||||
89/<- %esp 5/r32/ebp
|
||||
5d/pop-to-ebp
|
||||
c3/return
|
||||
|
||||
# let's just put stack primitives here for now
|
||||
# we need to think about how to maintain layers of the library at different levels of syntax sugar
|
||||
|
||||
# A stack looks like this:
|
||||
# top: int
|
||||
# data: (array byte) # prefixed by length as usual
|
||||
|
||||
clear-stack: # s : (address stack)
|
||||
# . prolog
|
||||
55/push-ebp
|
||||
89/<- %ebp 4/r32/esp
|
||||
# . save registers
|
||||
50/push-eax
|
||||
51/push-ecx
|
||||
# eax = s
|
||||
8b/-> *(ebp+8) 0/r32/eax
|
||||
# ecx = s->length
|
||||
8b/-> *(eax+4) 1/r32/ecx
|
||||
# ecx = &s->data[s->length]
|
||||
8d/copy-address *(eax+ecx+8) 1/r32/ecx
|
||||
# s->top = 0
|
||||
c7/copy 0/subop/copy *eax 0/imm32
|
||||
# eax = s->data
|
||||
81 0/subop/add %eax 8/imm32
|
||||
$clear-stack:loop:
|
||||
# if (eax >= ecx) break
|
||||
39/compare %eax 1/r32/ecx
|
||||
73/jump-if-greater-or-equal-unsigned $clear-stack:end/disp8
|
||||
# *eax = 0
|
||||
c6 0/subop/copy-byte *eax 0/imm8
|
||||
# ++eax
|
||||
40/increment-eax
|
||||
eb/jump $clear-stack:loop/disp8
|
||||
$clear-stack:end:
|
||||
# . restore registers
|
||||
59/pop-to-ecx
|
||||
58/pop-to-eax
|
||||
# . epilog
|
||||
89/<- %esp 5/r32/ebp
|
||||
5d/pop-to-ebp
|
||||
c3/return
|
||||
|
||||
test-clear-stack:
|
||||
# var ecx : (address stack) = stack of size 8 with random data in it
|
||||
68/push 34/imm32
|
||||
68/push 35/imm32
|
||||
68/push 8/imm32/length
|
||||
68/push 14/imm32/top
|
||||
89/<- %ecx 4/r32/esp
|
||||
# clear
|
||||
(clear-stack %ecx)
|
||||
# top should be 0
|
||||
58/pop-to-eax
|
||||
(check-ints-equal %eax 0 "F - test-clear-stack: top")
|
||||
# length should remain 8
|
||||
58/pop-to-eax
|
||||
(check-ints-equal %eax 8 "F - test-clear-stack: length")
|
||||
# first word is 0
|
||||
58/pop-to-eax
|
||||
(check-ints-equal %eax 0 "F - test-clear-stack: data[0..3]")
|
||||
# second word is 0
|
||||
58/pop-to-eax
|
||||
(check-ints-equal %eax 0 "F - test-clear-stack: data[4..7]")
|
||||
c3/return
|
||||
|
||||
push: # s : (address stack), n : int
|
||||
# . prolog
|
||||
55/push-ebp
|
||||
89/<- %ebp 4/r32/esp
|
||||
# . save registers
|
||||
50/push-eax
|
||||
51/push-ecx
|
||||
56/push-esi
|
||||
# esi = s
|
||||
8b/-> *(ebp+8) 6/r32/esi
|
||||
# ecx = s->top
|
||||
8b/-> *esi 1/r32/ecx
|
||||
# if (s->top >= s->length) abort
|
||||
39/compare *(esi+4) 1/r32/ecx
|
||||
7e/jump-if-lesser-or-equal $push:abort/disp8
|
||||
# s->data[s->top] = n
|
||||
8b/-> *(ebp+0xc) 0/r32/eax
|
||||
89/<- *(esi+ecx+8) 0/r32/eax
|
||||
# s->top += 4
|
||||
81 0/subop/add *esi 4/imm32
|
||||
$push:end:
|
||||
# . restore registers
|
||||
5e/pop-to-esi
|
||||
59/pop-to-ecx
|
||||
58/pop-to-eax
|
||||
# . epilog
|
||||
89/<- %esp 5/r32/ebp
|
||||
5d/pop-to-ebp
|
||||
c3/return
|
||||
|
||||
$push:abort:
|
||||
# print(stderr, "error: push: no space left")
|
||||
# . write-buffered(Stderr, "error: push: no space left")
|
||||
# . . push args
|
||||
68/push "error: push: no space left"/imm32
|
||||
68/push Stderr/imm32
|
||||
# . . call
|
||||
e8/call write-buffered/disp32
|
||||
# . . discard args
|
||||
81 0/subop/add %esp 8/imm32
|
||||
# . flush(Stderr)
|
||||
# . . push args
|
||||
68/push Stderr/imm32
|
||||
# . . call
|
||||
e8/call flush/disp32
|
||||
# . . discard args
|
||||
81 0/subop/add %esp 4/imm32
|
||||
# . syscall(exit, 1)
|
||||
bb/copy-to-ebx 1/imm32
|
||||
b8/copy-to-eax 1/imm32/exit
|
||||
cd/syscall 0x80/imm8
|
||||
# never gets here
|
||||
|
||||
test-push:
|
||||
# var ecx : (address stack) = empty stack of size 8
|
||||
68/push 0/imm32
|
||||
68/push 0/imm32
|
||||
68/push 8/imm32/length
|
||||
68/push 0/imm32/top
|
||||
89/<- %ecx 4/r32/esp
|
||||
#
|
||||
(push %ecx 42)
|
||||
# top
|
||||
58/pop-to-eax
|
||||
(check-ints-equal %eax 4 "F - test-push: top")
|
||||
# length
|
||||
58/pop-to-eax
|
||||
(check-ints-equal %eax 8 "F - test-push: length")
|
||||
# first word is 42
|
||||
58/pop-to-eax
|
||||
(check-ints-equal %eax 42 "F - test-push: data[0..3]")
|
||||
# second word is 0
|
||||
58/pop-to-eax
|
||||
(check-ints-equal %eax 0 "F - test-push: data[4..7]")
|
||||
c3/return
|
||||
|
||||
pop: # s : (address stack) -> n/eax : int
|
||||
# . prolog
|
||||
55/push-ebp
|
||||
89/<- %ebp 4/r32/esp
|
||||
# . save registers
|
||||
51/push-ecx
|
||||
56/push-esi
|
||||
# esi = s
|
||||
8b/-> *(ebp+8) 6/r32/esi
|
||||
# if (s->top <= 0) abort
|
||||
81 7/subop/compare *esi 0/imm32
|
||||
7e/jump-if-lesser-or-equal $pop:abort/disp8
|
||||
# s->top -= 4
|
||||
81 5/subop/subtract *esi 4/imm32
|
||||
# eax = s->data[s->top]
|
||||
8b/-> *esi 1/r32/ecx/top
|
||||
8b/-> *(esi+ecx+8) 0/r32/eax
|
||||
$pop:end:
|
||||
# . restore registers
|
||||
5e/pop-to-esi
|
||||
59/pop-to-ecx
|
||||
# . epilog
|
||||
89/<- %esp 5/r32/ebp
|
||||
5d/pop-to-ebp
|
||||
c3/return
|
||||
|
||||
$pop:abort:
|
||||
# print(stderr, "error: pop: nothing left in stack")
|
||||
# . write-buffered(Stderr, "error: pop: nothing left in stack")
|
||||
# . . push args
|
||||
68/push "error: pop: nothing left in stack"/imm32
|
||||
68/push Stderr/imm32
|
||||
# . . call
|
||||
e8/call write-buffered/disp32
|
||||
# . . discard args
|
||||
81 0/subop/add %esp 8/imm32
|
||||
# . flush(Stderr)
|
||||
# . . push args
|
||||
68/push Stderr/imm32
|
||||
# . . call
|
||||
e8/call flush/disp32
|
||||
# . . discard args
|
||||
81 0/subop/add %esp 4/imm32
|
||||
# . syscall(exit, 1)
|
||||
bb/copy-to-ebx 1/imm32
|
||||
b8/copy-to-eax 1/imm32/exit
|
||||
cd/syscall 0x80/imm8
|
||||
# never gets here
|
||||
|
||||
test-pop:
|
||||
# var ecx : (address stack) = stack of size 8 containing just 42
|
||||
68/push 0/imm32
|
||||
68/push 42/imm32
|
||||
68/push 8/imm32/length
|
||||
68/push 4/imm32/top
|
||||
89/<- %ecx 4/r32/esp
|
||||
#
|
||||
(pop %ecx) # => eax
|
||||
# result
|
||||
(check-ints-equal %eax 42 "F - test-pop: result")
|
||||
# top
|
||||
58/pop-to-eax
|
||||
(check-ints-equal %eax 0 "F - test-pop: top")
|
||||
# length
|
||||
58/pop-to-eax
|
||||
(check-ints-equal %eax 8 "F - test-pop: length")
|
||||
# clean up
|
||||
58/pop-to-eax
|
||||
58/pop-to-eax
|
||||
c3/return
|
||||
|
||||
== data
|
27
test_apps
27
test_apps
|
@ -309,14 +309,9 @@ test `uname` = 'Linux' && {
|
|||
echo
|
||||
}
|
||||
|
||||
# Only native runs beyond this point. We start using syntax that the emulator
|
||||
# doesn't support.
|
||||
test $EMULATED && echo "skipping remaining runs in emulated mode"
|
||||
test $NATIVE || exit 0
|
||||
|
||||
echo calls
|
||||
cat 0*.subx apps/subx-common.subx apps/calls.subx | apps/sigils > a.sigils
|
||||
./subx translate init.$OS a.sigils -o apps/calls
|
||||
cat init.$OS 0*.subx apps/subx-common.subx apps/calls.subx | apps/sigils > a.sigils
|
||||
./subx translate a.sigils -o apps/calls
|
||||
[ "$1" != record ] && git diff --exit-code apps/calls
|
||||
./subx run apps/calls test
|
||||
echo
|
||||
|
@ -325,6 +320,22 @@ test `uname` = 'Linux' && {
|
|||
echo
|
||||
}
|
||||
|
||||
echo braces
|
||||
cat init.$OS 0*.subx apps/subx-common.subx apps/braces.subx | apps/calls | apps/sigils > a.sigils
|
||||
./subx translate a.sigils -o apps/braces
|
||||
[ "$1" != record ] && git diff --exit-code apps/braces
|
||||
./subx run apps/braces test
|
||||
echo
|
||||
test `uname` = 'Linux' && {
|
||||
apps/braces test
|
||||
echo
|
||||
}
|
||||
|
||||
# Only native runs beyond this point. We start using syntax that the emulator
|
||||
# doesn't support.
|
||||
test $EMULATED && echo "skipping remaining runs in emulated mode"
|
||||
test $NATIVE || exit 0
|
||||
|
||||
echo "== translating using SubX"
|
||||
|
||||
# example programs
|
||||
|
@ -347,7 +358,7 @@ done
|
|||
|
||||
# Phases of the self-hosted SubX translator.
|
||||
|
||||
for app in hex survey pack assort dquotes tests sigils calls
|
||||
for app in hex survey pack assort dquotes tests sigils calls braces
|
||||
do
|
||||
echo $app
|
||||
./ntranslate init.$OS 0*.subx apps/subx-common.subx apps/$app.subx
|
||||
|
|
Loading…
Reference in New Issue