From 67ad47b7f86d97f75808e1a9336d72d96d18dd2c Mon Sep 17 00:00:00 2001 From: Mark Wooding Date: Mon, 5 Jul 2010 22:56:11 +0100 Subject: [PATCH] forth: New language. Very unpleasant coroutine hacking. I had a neat word for parsing the basename out of argv[0], but Gforth doesn't actually let you get at the image name, so it just said `gforth' which isn't very helpful, really. --- Makefile | 9 ++ forth-fringe.fth | 251 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 260 insertions(+) create mode 100644 forth-fringe.fth diff --git a/Makefile b/Makefile index e83ae5e..4f41be2 100644 --- a/Makefile +++ b/Makefile @@ -179,6 +179,15 @@ smalltalk-fringe: $(V_HIDE)chmod +x $@.new && mv $@.new $@ ###-------------------------------------------------------------------------- +### Forth. + +LANGS += forth +TARGETS += forth-fringe +SOURCES += forth-fringe.fth +forth-fringe: forth-fringe.fth + $(call v_echo,FORTHI)gforthmi $@ $< + +###-------------------------------------------------------------------------- ### Erlang. ERLC = erlc diff --git a/forth-fringe.fth b/forth-fringe.fth new file mode 100644 index 0000000..bab2eda --- /dev/null +++ b/forth-fringe.fth @@ -0,0 +1,251 @@ +\ -*-forth-*- +\ +\ Same-fringe solver in Forth. + +\ --------------------------------------------------------------------------- +\ Utilities. Most of these are GForth-specific in some way. + +\ String representation conversions. + +: string>bounds ( c-addr u -- c-addr-limit c-addr ) + \ Convert a string in the usual base/length form to a limit/base form + \ which is better suited to iteration. The base is left on the top + \ because it's likely to change more frequently. + chars over + swap ; + +: bounds>string ( c-addr-limit c-addr -- c-addr u ) + \ Convert a string in limit/base form back to base/length form. + tuck - [ 1 chars ] literal / ; + +\ Program name. Want the portion after the rightmost `/'. +\ +\ Bodge: gforth doesn't want to hand over the image filename so we'll have to +\ hardwire. + +: quis s" forth-fringe" ; + +\ Error reporting. + +: ouch ( a-addr u -- program exits ) + \ Report an error message on stderr and exit with a nonzero status. + quis stderr write-file drop + s" : " stderr write-file drop + 2dup stderr write-line drop + 1 (bye) \ Gforth specific +; + +\ --------------------------------------------------------------------------- +\ Coroutines. Largely very scary. + +\ A coroutine descriptor consists of a single cell containing the coroutine's +\ return-stack pointer. This cell is only valid when the coroutine is +\ inactive. +\ +\ Coroutines have distinct return stacks, but share the main value stack and +\ floating-point stack, which they can use for communication with other +\ coroutines. A coroutine will therefore typically stash state on the return +\ stack. +\ +\ There's no current provision for Gforth's separate locals stack. + +\ The amount of return-stack storage we allocate to a coroutine. +256 cells constant cr-space + +\ The current coroutine. This initially points to an uninitialized +\ descriptor which we'll fill in during the first coroutine switch. +variable current-cr +here current-cr ! cell allot + +\ The coroutine which invoked this one. This is used by `yield'. +variable caller-cr + +: switch-cr ( cr -- ) + \ Make `cr' the current coroutine, and tell it that it was called by this + \ one. + rp@ current-cr @ ! + current-cr @ caller-cr ! + dup current-cr ! + @ rp! +; + +: yield ( -- ) + \ Make the calling coroutine current again. + caller-cr @ switch-cr +; + +: start-cr ( cr xt -- ) + \ Switch to the new coroutine `cr', and have it execute the token `xt'. + swap + rp@ current-cr @ ! + current-cr @ caller-cr ! + dup current-cr ! + @ rp! + execute +; + +: init-cr ( a-addr -- cr ) + \ Initialize a chunk of memory at `a-addr' and turn it into a pointer to + \ a coroutine descriptor `cr' ready for use by `start-cr'. + [ cr-space cell - ] literal + + dup dup ! +; + +: [alloc-cr] ( -- cr ; R: -- cr-sys ) + \ Compile-time word: adjust the return stack pointer, returning a + \ coroutine descriptor `cr'. The space can be recovered using + \ `[drop-cr]'. This must be done at compile time, because returning is + \ hard after you've messed with the return stack pointer. + postpone rp@ postpone cr-space postpone - postpone dup + postpone rp! postpone init-cr +; immediate + +: [drop-cr] ( cr -- ; R: cr-sys -- ) + \ Compile-time word: adjust the return-stack pointer to reclaim the space + \ used for the coroutine `cr' and all those above it on the return stack. + postpone cell postpone + postpone rp! +; immediate + +\ --------------------------------------------------------------------------- +\ Iterator protocol. +\ +\ An iterator is a coroutine which yields a word and a flag. While there are +\ items available, it yields items paired with `true' flags; when all items +\ are exhausted, it yields a word and a `false' flag. After that, invoking +\ the coroutine again is invalid. + +: print-iterator ( cr -- ) + \ Print the characters returned by the iterator coroutine `cr'. + begin dup switch-cr while emit repeat + drop +; + +: same-iterators-p ( cr0 cr1 -- f ) + \ Report true if the iterator coroutines `cr0' and `cr1' return the same + \ items in the same order, as determined by `='. + begin + over switch-cr ( cr0 cr1 x0 f0 ) + 2 pick switch-cr ( cr0 cr1 x0 f0 x1 f1 ) + rot ( cr0 cr1 x0 x1 f1 f0 ) + over <> if 2drop 2drop drop false exit then + 0= if 2drop 2drop true exit then + <> if 2drop false exit then + again +; + +\ --------------------------------------------------------------------------- +\ Binary trees. + +: make-tree ( a-addr-left w-datum a-addr-right -- a-addr-tree ) + \ Construct a binary tree from components on the stack, returning the + \ address of the tree node. + here >r \ stash pointer + swap rot , , , \ reorder and store + r> \ recover pointer +; + +\ A leaf is an empty tree. The address of this variable is important; its +\ contents are not. +variable leaf + +\ Binary tree structure. +: tree-left ( a-addr -- a-addr' ) ; +: tree-datum ( a-addr -- a-addr' ) cell+ ; +: tree-right ( a-addr -- a-addr' ) [ 2 cells ] literal + ; +3 constant tree-ncells + +: parse-subtree ( c-addr-limit c-addr -- c-addr-limit c-addr' tree ) + \ Parse a subtree from the string on the stack (in limit/base form). + \ Update the string to reflect how much we consumed, and leave the tree + \ address for the caller. See `parse-tree' for the syntax. + 2dup > if dup c@ [char] ( <> else true then if + leaf + else + char+ + leaf 0 leaf make-tree >r + recurse r@ tree-left ! + 2dup <= if s" no data" ouch then + dup c@ r@ tree-datum ! char+ + recurse r@ tree-right ! + 2dup <= if true else dup c@ [char] ) <> then if + s" missing )" ouch + then + char+ + r> + then +; + +: parse-tree ( c-addr u -- tree ) + \ Parse a tree from the string on the stack. + \ + \ The syntax is simple: + \ + \ tree :: empty | `(' tree char tree `)' + \ + \ The ambiguity is resolved by always treating `(' as a tree when a tree + \ is expected. + string>bounds + parse-subtree >r + <> if s" trailing junk" ouch then + r> +; + +: do-tree-fringe ( tree -- yields: x f ) + \ Helper word for `tree-fringe' below. Recursively yields up the items + \ of the subtree rooted at `tree'. + dup leaf = if + drop + else + >r + r@ tree-left @ recurse + r@ tree-datum @ true yield + r> tree-right @ recurse + then +; + +: tree-fringe ( tree -- yields: x f ) + \ Yield up the items of `tree' in order, according to the iteration + \ protocol. + >r yield + r> do-tree-fringe + 0 false yield +; + +\ --------------------------------------------------------------------------- +\ Main program. + +: main + \ Main program: parse arguments and do what's asked for. + argc @ case + + 2 of + \ One proper argument: parse a tree and print its fringe. + [alloc-cr] + 1 arg parse-tree over ['] tree-fringe start-cr + dup print-iterator cr + [drop-cr] + endof + + 3 of + \ Two arguments: parse two trees and compare them. + [alloc-cr] 1 arg parse-tree over ['] tree-fringe start-cr + dup + [alloc-cr] 2 arg parse-tree over ['] tree-fringe start-cr + same-iterators-p + swap [drop-cr] + if ." match" else ." no match" then cr + endof + + \ Default. + s" bad args" ouch + + endcase +; + +\ Gforth image magic. +:noname + defers 'cold + main + bye +; is 'cold + +\ --------------------------------------------------------------------------- -- 2.11.0