online compiler and debugger for c/c++

code. compile. run. debug. share.
Source Code    Language
#!/bin/bash set -u # set -x # required bash 2.04 or more recent, but probably depends on bash 3.x now, since v0.54 version="0.63a" # bashforth - forth interpreter in bash # v0.03 20030219 ls added bool, logical, constants, fixed nip and other # v0.04 20030219 ls added ?dup, fixed 0branch # v0.05 20030220 ls reviewed auto-inc/dec addressing modi, fixed 0branch again # v0.06 20030220 ls constants redone # v0.07 20030220 ls added lshift rshift # v0.08 20030220 ls emit outputs correctly decimal numbers on stack. thanks dufflebunk # v0.09 20030220 ls simplified asc table building. # v0.10 20030220 ls accept works. uses external command cut right now. # v0.11 20030220 ls added pad c@ @ c! ! count # v0.12 20030221 ls key and accept return asciis, rather than chars. # emit, type, find work on asciis # v0.13 20030221 ls word, input stream parser, query, interpret, quit added # this enables multiple words on input line # v0.14 20030221 ls ?number added, extended interpreter. numbers work, but # only decimal # v0.15 20030221 ls added deferred words, improved error handler. first # defining words. creation of variables works. # v0.16 20030221 ls immediate, colon definitions work # v0.17 20030222 ls improved prompt, added ' and ['], compiles numbers # find returns the word#, can get to xt, name and header flags. # added 2*, 2/, negate, begin..again begin..until # v0.18 20030222 ls if..then, if..else..then begin..while..repeat work. structure is tested # v0.19 20030222 ls do..loop, i, j, negative numbers input, commented out debug output # from virtual machine for 50% speed improvement # v0.20 20030223 ls added does> 2+ # v0.21 20030223 ls hide, reveal, constant. started redoing error handler. loops broken # v0.22 20030223 ls loops fixed. ?comp # v0.23 20030223 ls added catch throw ?exec . fixed key (space). ctrl chars return asc of space too. # v0.24 20030224 ls added ." , s" , $, .( fixed bug in word . tests stack underflow # v0.26 20030225 ls added s( \ ( # v0.27 20030225 ls errorhandler through throw. top level error handler catches gracefully # v0.28 20030225 ls speed increase of about 50 % # v0.29 20030225 ls exit, outputs asciis 0...31, speeded up compares, improved move # v0.30 20030225 ls .. outputs decimal (quick), . respects base (slower), number input respects base # added hex, decimal, binary # v0.31 20030226 ls pictured number output added ( <# # #s #> #>type sign ) # v0.31a20030226 ls hold (forgotten, pictured number output), rot, -rot # v0.32 20030226 ls system (shells to command), pack ( a n -- x ) packs string to string on tos, # unpack (explodes tos string to memory), cleaned up messy accept and name # v0.33 20030226 ls added bash, fixed does>, started include. sent out for does> fix # v0.34 20030226 ls first rough version of include works. no nesting yet. thanks deltab for getting the source into vars # v0.35 20030226 ls fixed backslash bug in include. # this is for the time being the last version of bashforth. i'm now busy working on a target translator which allows to generate source # for several languages, including bash # v0.36 20030305 ls added pick, found a way to split input stream into chars w/o requiring external cut, as a result # including source files works much quicker. bashforth is "pure" now. # v0.37 20030309 ls number output with . doesn't complain about zero-string stack elements. # stack order reversed. added */ */mod ?do leave . speeded up type # v0.37a20030310 ls fixed include, broken in 0.37 because of changed do # v0.37b20030310 ls fixed include again. * in source was expanded to file list # v0.37c20030310 ls fixed ." which had cr appended # v0.38 20030310 ls added skip, scan, tuck, compare # v0.39 20030310 ls added min max abs fill doc, abort throws, removed ?exec # v0.40 20030311 ls bugfix for 2.05a, hopefully for 2.04 too. incompatible with 2.03 # v0.41 20030311 ls redone doc. this implementation writes line number to word body. added rnd +! cell cells chars # v0.42 20030311 ls more consistent use of addressing modes, added # date&time.fixed negative number big introduced with .40 # v0.42a20030313 ls changed email address. verified function on bash 2.04. thanks, stepan # v0.42b20030315 ls fixed sign bug, result of v0.40, added >name # v0.43 20030316 ls added .name, roll, improved locate and >name, last points now to cfa of last word # v0.44 20030316 ls added cell+ char +loop ?leave ** # v0.45 20030316 ls added 2>r 2r>, cleaned up code, speeded up some words (type, #, words) # v0.46 20030316 ls added literal, compiling, addressing modes optimizations # v0.46a20030316 ls bugfix addressing modes v0.46. untested with bash 2.04 # v0.47 20030319 ls added black yellow green red blue magenta cyan white fg bg colors # v0.47a20030320 ls added normal bold underscore reverse attr@ attr! # v0.47b20030320 ls added at home # v0.47c20030325 ls added ?at (doesn't work yet) number /string right$ left$ # v0.48 20030325 ls added system2 2swap dup$ drop$ depth$ 2dup$ swap$ over$ nip$ rot$ push$ pop$ append$ # modified left$ right$, these work on stop string stack element now # modified doc to show word description, besides stack effect. optimized does> # v0.48a20030325 ls added/modified descriptions # v0.48b20030526 ls replaced hide/reveal against versions by h-peter recktenwald. these ones seem # to be less sensitive for the used version of bash # v0.48c20030527 ls bug fix "hold", bug discovered by h-peter recktenwald # v0.48d20030530 ls merged with h-peter recktenwald's patches: info, hold, immediate # hi-level . is about 50 % slower than former primitive version # (output 1000 number 7.5 rather than 4.7 seconds now) # v0.48e20030808 ls attempted fix of ?number, number and * for bash v2.04 on BEOS # v0.49 20030809 ls fixed time&date, broken after 2.04 fix in 0.48e # v0.49a20030809 ls fixed loop +loop for 2.04 # v0.49b20030818 ls found a better fix for time&date # v0.49c20031019 ls fixed : foo ." *" ; bug which displayed current directory # v0.49d20031019 ls added for .. next, compatible with i j , added spaces. # made count tolerant for non-initialized memory locations # v0.49e20031019 ls attempt to include nonexisting file throws -38 # 0.50 20031028 ls added see (does not decompile, shows script source instead) # 0.50a 20040101 ls fixed : $structured, not structured in until # 0.50b 20040928 ls optional doc <word> uses sed rather than tail - recently tail args were changed. # 0.51 20041004 ls added 2@ and 2!, suggested by Antonio Maschio # 0.52 20041116 ls slow (1sec) version of key?, added secs and epoche # 0.52a 20041123 ls can emit ascii <32 correctly # 0.53 20041217 ls ***STACK EFFECT OF 'WORD' HAS CHANGED*** previously ( c -- a n ), it is now ( c -- cstring ), with string at HERE # previous a was pointing into input stream. STREAM was added, providing function of former WORD. new WORD uses STREAM. # added :noname . bugfix compare . # 0.53a 20041220 ls trapped Ctrl-C: warm start # 0.53b 20041220 ls added >body body> # 0.53c 20041222 ls include appends .bashforth extension and retries if file not found # 0.54 20050119 ls fixed bug in move # 0.54a 20050222 ls added ? # 0.54b 20050331 ls div/0 exception # 0.55 20060314 ls unhandled exceptions quit, not warmstart, leaving radix untouched # 0.55a 20061003 lsls removed unnecessary cat in see # 0.55b 20071220 ls reversed logic in key? # changed comparison against empty string to -z test in exception and 2 other # speeded up by using [[ or (( instead of [ # simplified logic here and there # 0.55c20071223 ls exception accepts literal # 0.56 20071229 ls line numbers (for doc and see) dont't require info #LINENO per word anymore # changed all function foo { } to foo() { } # passed command line is executed # string stack underflow detected # string stack emptied on warm and cold # fixed bug in include # string stack operators testing for underflow # first mac debian package # 0.56a 20071231 ls fix in key (returns ascii for space now) # added nanoseconds, time (measures execution time) # made distance between HERE and PAD a config variable: PADAWAY # tib size configurable too # simplified some logic # changed find to resemble a bit more the standard # using new find in interpreter loop # using printf instead of echo # misc small speedups (or rather, removed a few slowdowns) # 0.56b intermediate testing speed improvements # 0.56c 20080114 ls added control characters in output ascii table # using (( cond )) && action where appropriate # changing spacing to accommodate fte syntax highlighting better # some more arithmetic optimisations # 0.57 20091005 ls key?, needs bash 4, waits 1ms. single char buffer, # read by key?, used by key and accept. # 0.57a 20101022 ls fixed bug in (s") which must have slipped into with # a recent version # slight optimisation of abs # 0.57b 20101101 ls added env, removed "upload" handling, which went into a source file by the same name. # renamed "timestamp" to "epoche" # renamed "merge$" to "append$" # attempts to source ~/.bashforthrc, use to set variables: # sources=/path/to/sourcefiles # "include" reads source files from that dir, # # and defaults to current directory if unset. # added "type$" # 0.57c 20101112 ls simplified exception, and some style improvements sprinkled all over the code # user interrupt (ctrl-c) improved # 0.57d 20101127 ls removed load and loadfrom. reversed logic on -z string tests. # removed -n from string tests. # 0.58 20101220 ls replaced right$. simpler, shorter, faster # changed result generation of key? # bug fix number - may have another, dropping sign with hex -ff # 0.58a 20110819 ls fixed bug with multiple consecutive revealheader # 0.58b 20120312 ls multi line compound arithmetic expressions problem with bash 4.2-1 at hash # 0.58c 20170609 ls A syntax error affecting bash v4.4 was fixed. # ASCII to char translation array initialised with char(1) now. # 0.59 20190806 ls uses $EPOCHSECONDS instead of $(date +%s) for epoche when running under bash 5+ # 0.59a 20190821 ls some more quoting, removed saving IFS contents in key and key? # changed !(( to ! (( to pacify shellcheck. # 0.60 20190830 ls added restore, restore-from, save-system, saveas, contributed by quaraman-me # type$ didn't drop top string stack element. Fixed # changed output of .s$ to vertical. top of string stack is uppermost output line. # Fixed error in type when outputting % char. # 0.60a 20190830 ls .s$ autodecrements # 0.60b 20190830 ls added nlimit, producing highest signed number. # fixed rshift: making it logical right shift while bash does arithmetic right shift. # partially (attempted to fix) sign problem in # # 0.60c 20190830 ls see prevented from mangling output lines. # 0.60d 20190830 ls fixed expanding * in restore. # 0.60e 20190830 ls fixed: number input accepting some non numeric chars. A side effect is that digits > 10 are now case insensitive. # added: 2swap d= sub$ # changed: left$ and right$ call sub$, ?number uses (fixed) number # 0.60f 20190831 ls fixed: wrong number output when outputting a number with only msb set (nlimit+1) # changed (already in a previous version): executing save-system and restore without file name write to/read from $sources directory # 0.61 20190831 ls functionally reverted to 0.60f, undoing changes to floored modulo and division, causing more damage than benefits # 0.61a 20190831 ls fixed: (s") bug from 0.57a again, seems to have reinstroduced when reverting. # 0.62 20190909 ls added: !sourcepath complements sourcepath # changed: set working variables in compare to local # 0.63 20190909 ls changed: words attempts to break lines # 0.63a 20201121 ls fixed: exposed one superfluous "epoche" header # # known bugs: # catch: doesn't return the thrown value correctly sometimes # include: max line length in source files isn't checked against TIBSIZE # env: without name abort with "invalid variable name" # see: doesn't look into included source files # /: while modulo and divison of /mod and */mod are floored, / isn't. # global variables: # ip virtual machine instruction pointer # w virtual machine word pointer. # sp data stack pointer # rp return stack pointer # wc word count, number of headers. used as name field address # temp scratch. never used to carry data across words/functions # tos top of stack, stack cache # dp dictionary pointer, "here". new words are added at this address # state compile/interpret switch # catchframe pointer to latest frame # ssp string stack pointer # global variable arrays: # m memory # s data stack # r return stack # h headers (word names) # hf header flags (precedence bit, smudge bit) # x execution tokens # asc characters array, indexed by decimal ascii # ss string stack ################################# example primitive ##################################### # # ( -- ) description # stack diagram, description # revealheader "foo" # name in forth vocabulary # code foo foo # name in bash, call of executable # --------- executable may follow, but may also be seperated ---------- # foo() { # executable implementated as function # s[++sp]=$tos # stack push # tos=${s[sp--]} # stack pop # } # empty lines follows # ######################################################################################### ################################# example hi-level word ##################################### # # ( -- ) description # stack diagram, description # revealheader "foo" # name in forth vocabulary # colon foo \ # name in bash. line continuation # $word $word $word \ # compiled words, line continuation # $word $word # last line does not need continuation, empty line follows # ######################################################################################### # # # # ------------------------------------------------------------------------- # --- configuration --- # ------------------------------------------------------------------------- PADAWAY=256 # distance between HERE and PAD TIBSIZE=256 PROMPT="ok" LOADING="" EDITOR=sensible-editor # ------------------------------------------------------------------------- # --- allocate memory / initialize vars --- # ------------------------------------------------------------------------- m=() # memory s=() # data stack r=() # return stack h=() # headers, wordcount hf=() # header flags, corresponding to headers x=() # execution tokens, corresponding to headers ss=() # string stack declare -i ip w # instruction and word pointer of virtual machine declare -i s0=0 sp # data stack origin and pointer declare -i r0=0 rp # return stack origin and pointer declare -i ss0=0 ssp # string stack origin and pointer declare -i dp=0 # dictionary pointer declare -i wc=0 # word count declare -i state=0 # compiler/interpreter switch declare -i catchframe=0 # pointer to latest catch frame, or 0 sources="." # unless overwritten from .bashforthrc or !sourcepath # ---- bitmasks ------------------------------------------------------------ # declared as read-only, integer declare -ri precedencebit=1 # immediate words declare -ri smudgebit=2 # hide/reveal headers # --------------- build decimal>ascii lookup table for emit ---------------- asc=() for i in {1..255}; do asc[i]=$(echo -en "\\x$(printf '%x' $i)") # ascii 0-255 done # ------------------------------- "macros" --------------------------------- # --- array of variables and functions which will be removed after the script has been loaded --- # --- only to use with words which help building bashforth, but aren't required at runtime --- remove=() transient() { remove[${#remove[@]}]=$1 } transient remove # remove must either be non-transient, or the first transient. transient transient transient compile compile() { for nextword in $* do m[dp++]="${nextword}" done } transient code code() { (( $1 = dp )) shift m[dp++]="$*" } dovar() { s[++sp]="$tos" tos="$w" } transient var var() { (( $1 = dp )) compile dovar 0 } var lastxt header() { m[lastxt+1]=$dp x[wc]=$dp hf[wc]=0 h[wc++]="$1" # word name } reveal() { ((hf[wc-1] |= smudgebit)) } hide() { ((hf[wc-1] &= ~smudgebit)) } transient revealheader revealheader() { ((m[dp++]=BASH_LINENO[0]-1)) # source line number - consider to put file/line into an array with source locations header "$1" reveal } transient semicolon semicolon() { compile "$unnest" reveal } transient colon colon() { (( $1 = dp )) shift 1 compile nest compile "$*" semicolon } doconst() { s[++sp]=$tos tos=${m[w]} } transient constant constant() { (( $1 = dp )) shift compile doconst "$1" } dodefer() { ip=$w; } transient defer defer() { (( $1 = dp )) compile dodefer 0 } # ----------------------------------------------------------------------------- # -------------------------------- system start ------------------------------- # ----------------------------------------------------------------------------- revealheader "" # warm start vector # ( ??? -- ) init stacks and vars, restart interpreter revealheader "warm" defer warm # ------------------------------------------------------------------------- # --- ctrl-c: user interrupt --- # ------------------------------------------------------------------------- #trap "echo bashforth finished" EXIT #trap "echo err" ERR #trap "echo return" RETURN ctrl-c() { tos=-28 ip=$warm printf '%s\n' " ${throw[-tos]}" return 0 } trap ctrl-c 2 # ----------------------------------------------------------------------------- # ------------------------------ virtual machine ------------------------------ # ----------------------------------------------------------------------------- nest() { r[++rp]=$ip ip=$w } # ( -- ) exits the current definition. compiled by ; revealheader "exit" code unnest unnest unnest() { ip=${r[rp--]} } # ---------------------------------------------------------------------------- # --------------------------- constants, variables --------------------------- # ---------------------------------------------------------------------------- msb=1; until ((msb<0)); do ((msb<<=1)); done # ( -- -1 ) revealheader "true" constant minone -1 # ( -- -1 ) revealheader "-1" constant minone -1 # ( -- 0 ) revealheader "false" constant zero 0 # ( -- 0 ) revealheader "0" constant zero 0 # ( -- 1 ) revealheader "cell" constant one 1 # ( -- 1 ) revealheader "1" constant one 1 # ( -- 2 ) revealheader "2" constant two 2 # ( -- 3 ) revealheader "3" constant three 3 # ( -- 4 ) revealheader "4" constant four 4 # ( -- 5 ) revealheader "5" constant five 5 # ( -- 6 ) revealheader "6" constant six 6 # ( -- 27 ) ASCII of Escape char revealheader "esc" constant esc 27 # ( -- 32 ) ASCII of space char revealheader "bl" constant bl 32 # ( -- x ) highest signed number revealheader "nlimit" constant nlimit $((msb-1)) # ( -- a ) revealheader ">in" var in # ( -- a ) flags/switches interpret/compile mode revealheader "state" var state # ( -- a ) variable, pointing to cfa of last word revealheader "last" constant last $((lastxt+1)) # ( -- a ) a memory area, relative to here, for user purposes revealheader "tib" var tib ((dp+=TIBSIZE)) # ( -- a ) variable containing the input and output radix revealheader "base" var base # ---------------------------------------------------------------------------- # ------------------------------- run time ----------------------------------- # ---------------------------------------------------------------------------- # ( -- ) run time word - to be compiled by another word revealheader "branch" code branch branch branch() { ((ip+=m[ip])); } # ( f -- ) run time word - to be compiled by another word revealheader "0branch" code branch0 branch0 branch0() { if ((tos)); then ((ip++)) else ((ip+=m[ip])) fi tos=${s[sp--]} } # ( f -- ) run time word - compiled internally instead of 0= branch0 code branchx branchx branchx() { if ((tos)); then ((ip+=m[ip])) else ((ip++)) fi tos=${s[sp--]} } # ( -- x ) when compiled into a word, the contents of the cell under $ip are pushed to stack and skipped from execution revealheader "lit" code lit lit lit() { s[++sp]=$tos tos=${m[ip++]} } # ( a n -- x ) assembles asciis at m[a] to string in tos revealheader "pack" code pack pack pack() { i=$tos temp=${s[sp--]} unset tos while ((i--)); do tos+="${asc[m[temp++]]}" done } #pack() { # temp="${s[sp--]}" # temp2=$tos # tos="$(printf '\x0' $(printf '%x' "${m[@]:temp:temp2}"))" # echo ">>> $tos <<<" # printf '\x0%x ' "${m[@]:temp:temp2}" #} # ( x a -- n ) unpacks string x to ascii ordinals at a revealheader "unpack" code unpack unpack unpack() { local string=${s[sp--]} len=${#string} ((dest=tos+len)) tos=$len for ((; len; len-- )); do m[--dest]=$(printf '%d' "'${string:len-1:1}") done } # ( -- a c ) run time word - to be compiled by s" revealheader '(s")' code dosquote dosquote dosquote() { s[++sp]=$tos tos=${m[ip++]} s[++sp]=$ip ((ip+=tos)) } # ( -- ) run time word - to be compiled by ." revealheader '(.")' code dodotquote dodotquote dodotquote() { dosquote pack printf '%s' "$tos" tos=${s[sp--]} } # ( limit start -- ) run time word - to be compiled by for revealheader "(for)" code dofor dofor dofor() { r[++rp]=$tos r[++rp]=$tos tos=${s[sp--]} ((ip++)) } # ( -- ) run time word - to be compiled by next revealheader "(next)" code donext donext donext() { ((r[rp]--)) if ((r[rp])); then ((ip+=m[ip])) else ((ip++, rp-=2)) fi } # ( limit start -- ) run time word - to be compiled by do revealheader "(do)" code dodo dodo dodo() { r[++rp]=${s[sp--]} r[++rp]=$tos ((ip++)) tos=${s[sp--]} } # ( limit start -- ) run time word - to be compiled by ?do revealheader "(?do)" code doqdo doqdo doqdo() { if (( tos == s[sp] )); then ((sp--)) ((ip+=m[ip])) else r[++rp]=${s[sp--]} r[++rp]=$tos ((ip++)) fi tos=${s[sp--]} } # ( -- ) run time word - to be compiled by leave revealheader "(leave)" code doleave doleave doleave() { ((rp-=2)) ip=${m[ip]} ((ip+=m[ip])) } # ( -- ) run time word - to be compiled by ?leave revealheader "(?leave)" code doqleave doqleave doqleave() { if ((tos)); then doleave else ((ip++)) fi tos=${s[sp--]} } # ( -- ) run time word - to be compiled by loop revealheader "(loop)" code doloop doloop doloop() { ((r[rp]++)) if ((r[rp] - r[rp-1])); then ((ip += m[ip])) else ((ip++, rp -= 2)) fi } # ( -- ) run time word - to be compiled by +loop revealheader "(+loop)" code doplusloop doplusloop doplusloop() { ((temp = r[rp] - r[rp-1], r[rp] += tos, tos = s[sp--])) if (( (temp ^ (r[rp] - r[rp-1])) > 0 )); then ((ip += m[ip])) else (( ip++, rp -= 2 )) fi } # ( ? xt -- ? ) revealheader "execute" code execute execute execute() { w=$tos tos=${s[sp--]} ${m[w++]} } # ----------------------------------------------------------------------------- # ------------------------------ stack operators ------------------------------ # ----------------------------------------------------------------------------- # ( -- n ) returns number stack elements on data stack revealheader "depth" code depth depth depth() { s[++sp]=$tos ((tos=sp-s0-1)) } # ( x -- x x ) duplicate top stack element revealheader "dup" code dup dup dup() { s[++sp]=$tos; } # ( x1 x2 -- x1 x2 x1 x2 ) duplicate top two stck elements revealheader "2dup" code dup2 dup2 dup2() { s[++sp]=$tos s[++sp]=${s[sp-1]} } # ( 0 -- 0 ) ( x -- x x ) duplicate top stack element only if it is not zero revealheader "?dup" code qdup qdup qdup() { ((tos)) && s[++sp]=$tos } # ( x -- ) discard top stack element revealheader "drop" code drop drop drop() { tos=${s[sp--]} } # ( x1 x2 -- ) discard top two stack elements revealheader "2drop" code drop2 drop2 drop2() { ((sp--)) tos=${s[sp--]} } # ( x1 x2 -- x2 x1 ) swap the top two stack elements with each other revealheader "swap" code swap swap swap() { temp=$tos tos=${s[sp]} s[sp]=$temp } # ( x1 x2 x3 x4 -- x3 x4 x1 x2 ) swap top 2 stack items against 3rd and 4th of stack revealheader "2swap" code swap2 swap2 swap2() { temp=${s[sp-1]} s[sp-1]=$tos tos=$temp temp=${s[sp-2]} s[sp-2]=${s[sp]} s[sp]=$temp } # ( x1 x2 -- x1 x2 x1 ) push copy of second stack element to top revealheader "over" code over over over() { s[++sp]=$tos tos=${s[sp-1]} } # ( x1 x2 x3 x4 -- x1 x2 x3 x4 x1 x2 ) copy 3rd and 4th stack item to stack top revealheader "2over" code over2 over2 over2() { s[++sp]=$tos tos=${s[sp-3]} s[++sp]=$tos tos=${s[sp-3]} } # ( x1 x2 -- x2 ) discard second stack element revealheader "nip" code nip nip nip() { ((sp--)) } # ( x1 x2 -- x2 x1 x2 ) insert a copy of top of stack under second stack element revealheader "tuck" code tuck tuck tuck() { temp=${s[sp]} s[sp]=$tos s[++sp]=$temp } # ( x1 x2 x3 -- x2 x3 x1 ) rotate third stack element to top revealheader "rot" code rot rot rot() { temp=${s[sp]} s[sp]=$tos tos=${s[sp-1]} s[sp-1]=$temp } # ( x1 x2 x3 -- x3 x1 x2 ) rotate top stack element under second stack element revealheader "-rot" code minrot minrot minrot() { temp=${s[sp-1]} s[sp-1]=$tos tos=${s[sp]} s[sp]=$temp } # ( ... x2 x1 x0 n -- xn ) place a copy of stack element n on top of stack revealheader "pick" code pick pick pick() { tos=${s[sp-tos]}; } # ( ... x2 x1 x0 n -- ... x2 x1 x0 xn ) rotate stack element n to top of stack revealheader "roll" code roll roll roll() { temp=${s[sp-tos]} for ((; tos; --tos)); do s[sp-tos]=${s[sp-tos+1]} done ((sp--)) tos=$temp } # ( x -- ) moves top of data stack to return stack revealheader ">r" code to_r to_r to_r() { r[++rp]=$tos tos=${s[sp--]} } # ( -- x ) moves top of return stack to data stack revealheader "r>" code r_from r_from r_from() { s[++sp]=$tos tos=${r[rp--]} } # ( -- x ) copies top of return stack to data stack revealheader "r@" code r_fetch r_fetch r_fetch() { s[++sp]=$tos tos=${r[rp]} } # ( -- ) drops top of return stack revealheader "rdrop" code rdrop rdrop rdrop() { ((rp--)) } # ( x1 x2 -- ) moves top two data stack elements to return stack revealheader "2>r" code twoto_r twoto_r twoto_r() { r[++rp]=$tos r[++rp]=${s[sp--]} tos=${s[sp--]} } # ( -- x1 x2 ) moves top two return stack elements to data stack revealheader "2r>" code twor_from twor_from twor_from() { s[++sp]=$tos s[++sp]=${r[rp--]} tos=${r[rp--]} } # ( -- x ) returns index of innermost loop revealheader "i" code i r_fetch # ( -- x ) returns index of innermost loop revealheader "j" code j j j() { s[++sp]=$tos tos=${r[rp-2]} } # ----------------------------------------------------------------------------- # ------------------------------- catch / throw ------------------------------- # ----------------------------------------------------------------------------- throw[1]="terminated" throw[2]="aborted" throw[3]="stack overflow" throw[4]="stack underflow" throw[5]="return stack overflow" throw[6]="return stack underflow" throw[7]="do loops nested too deeply" throw[8]="dictionary overflow" throw[9]="invalid memory address" throw[10]="division by zero" throw[11]="result out of range" throw[12]="argument type mismatch" throw[13]=" not found" throw[14]="use only during compilation" throw[15]="invalid forget" throw[16]="attempt to use zero-length string as name" throw[17]="pictured numeric ouput string overflow" throw[18]="pictured numeric ouput string overflow" throw[19]="word name too long" throw[20]="write to a read-only location" throw[21]="unsupported operation" throw[22]="unstructured" throw[23]="address alignment exception" throw[24]="invalid numeric argument" throw[25]="return stack imbalance" throw[26]="loop parameters unavailable" throw[27]="invalid recursion" throw[28]="user interrupt" throw[29]="compiler nesting" throw[30]="obsolescent feature" throw[31]=">BODY used on non-CREATEd definition" throw[32]="invalid name argument" throw[38]="file not found" throw[65]="string stack underflow" # throw without catch frame - top level error handler code exception exception exception() { (( $tos == -1 )) && exit # exception one terminates interpreter. other exceptions are dealt with within local message="caught $tos" ((tos<0)) && message="${throw[-tos]:-$message}" printf '%s\n' "$message" if ((proceed)); then ip=$proceed start fi } code throw0 throw0 throw0() { catchframe=${r[rp]} sp=${r[--rp]} ip=${r[--rp]} tos=0 (( rp-- )) } brthrow0=$throw0 # ( a -- n ) part of catch / throw exception handling mechanism revealheader "catch" code catch catch catch() { r[++rp]=$ip r[++rp]=$sp r[++rp]=$catchframe catchframe=$rp r[++rp]=$brthrow0 execute } # ( n -- ) part of catch / throw exception handling mechanism revealheader "throw" defer throw code realthrow realthrow realthrow() { if ((tos)); then if ((catchframe)); then rp=$catchframe catchframe=${r[rp--]} sp=${r[rp--]} ip=${r[rp--]} else proceed=$warm exception echo continue fi else tos=${s[sp--]} fi echo "--- realthrow [$tos] ---" (( tos )) && { echo "something fishy with 0 throw" proceed=$warm exception } } # when primitives throw exceptions, they can't easily execute # a deferred word. Current workaround is to let the code entry # into throwing check whether it is the deferred handler, and # flowcontrols accordingly. # Reason for two different handlings: when interpreting command line, # errors need to terminate rather than warmstart the interpreter. code codethrow codethrow codethrow() { : "${tos:=0}" if [[ ${m[throw+1]} == $codethrow ]]; then printf '\n' proceed=0 (( tos )) && { exception } exit "${tos#-}" fi realthrow } m[throw+1]="$codethrow" # ( -- ) throw exception -2 revealheader "abort" colon abort $lit -2 $throw colon stackunderflow $lit -4 $throw colon invalidaddr $lit -9 $throw colon notfound $lit -13 $throw colon compileonly $lit -14 $throw colon unsupported $lit -21 $throw colon unstruc $lit -22 $throw colon invalidarg $lit -24 $throw colon nolooppars $lit -26 $throw colon filenotfound $lit -38 $throw # ----------------------------------------------------------------------------- # -------------------------------- arithmetic --------------------------------- # ----------------------------------------------------------------------------- # ( n1 -- n2 ) increment top of stack by one revealheader "1+" code oneplus oneplus oneplus() { ((tos++)) } # ( n1 -- n2 ) increment top of stack by cell revealheader "cell+" code cellplus oneplus # ( n1 -- n2 ) increment top of stack by two revealheader "2+" code twoplus twoplus twoplus() { ((tos+=2)) } # ( n1 -- n2 ) decrement top of stack by one revealheader "1-" code oneminus oneminus oneminus() { ((tos--)) } # ( n1 n2 -- n3 ) add top two stack elements together, leave result revealheader "+" code plus plus plus() { ((tos+=s[sp--])) } # ( n1 n2 -- n3 ) subtract tos from nos, leave result revealheader "-" code minus minus minus() { ((tos =s[sp--]-tos)) } # ( n -- u ) remove sign revealheader "abs" code abs abs abs() { (( tos < 0 )) && (( tos *= -1 )) # tos=${tos#-} } # ( n1 n2 -- n3 ) multiply top two numbers, leave result revealheader "*" code mul mul mul() { ((tos*=s[sp--])) } # ( n1 u -- n2 ) calculate power of n1 ** u, leave result revealheader "**" code power power power() { ((tos=s[sp--]**tos)) } divzero() { tos=-10 codethrow } # ( n1 n2 n3 -- n4 n5 ) multiply n1 with n2, divide by n3, returning remainder n4 and quotient n5 revealheader "*/mod" code starslashmod starslashmod starslashmod() { ((tos)) || divzero ((temp=s[sp--]*s[sp], s[sp]=temp%tos, tos=temp/tos)) } # ((tos)) || divzero # (( temp = s[sp--] * s[sp] )) # (( temp2 = tos )) # (( s[sp] = temp % tos )) # (( tos = temp / tos )) # if (( tos < 0 )); then # (( tos-- )) # (( s[sp] += temp2 )) # fi #} # ( n1 n2 -- n3 ) return remainder of n1/n2 revealheader "mod" code mod mod mod() { ((tos)) || divzero ((tos=s[sp--]%tos)) } # ((tos=(s[sp--]%tos+tos)%tos)) # ( n1 n2 -- n3 n4 ) return remainder n3 and quotient n4 of n1/n2 revealheader "/mod" code slashmod slashmod slashmod() { ((tos)) || divzero ((temp=s[sp], s[sp]=temp%tos, tos=temp/tos)) } # ((tos)) || divzero # (( temp = s[sp] )) # (( temp2 = tos )) # (( s[sp] = temp % tos )) # (( tos = temp / tos )) # if (( tos < 0 )); then # (( tos-- )) # (( s[sp] += temp2 )) # fi # ( n1 n2 -- n3 ) divide n1 by n2, return result revealheader "/" code div div div() { ((tos)) || divzero ((tos=s[sp--]/tos)) } # ( n1 n2 n3 -- n4 ) multiply n1 with n2, divide by n3 revealheader "*/" code starslash starslash starslashmod() { ((tos)) || divzero ((tos=s[sp--]*s[sp--]/tos)) } # ((tos)) || divzero # (( temp = s[sp--] * s[sp] )) # (( temp2 = tos )) # (( tos = temp / tos )) # if (( tos < 0 )); then # (( tos-- )) # fi # ( u1 n -- u2 ) shift right u1 by n revealheader "rshift" code rshift rshift rshift() { ((tos=(s[sp--]>>tos) & ~msb)) } # ( u1 n -- u2 ) shift left u1 by n revealheader "lshift" code lshift lshift lshift() { ((tos="s[sp--]<<tos")) # quotes defeat faulty syntax highlighting } # ( n1 -- n2 ) multiply n1 by 2, implemented as (quicker) shift left by 1 revealheader "2*" code mul2 mul2 mul2() { (("tos<<=1")); } # quotes help syntax hilighting of editor joe from getting confused # ( n1 -- n2 ) divide n1 by 2, imeplemented as (quicker) shift right by 1 revealheader "2/" code div2 div2 div2() { ((tos>>=1)); } # ( n1 -- n2 ) reverse sign of n1 revealheader "negate" code negate negate negate() { ((tos=-tos)); } # ( n1 n2 -- n1|n2 ) return the smaller one of two numbers revealheader "min" code min min min() { temp=${s[sp--]} ((tos>temp)) && tos=$temp } # ( n1 n2 -- n1|n2 ) return the greater one of two numbers revealheader "max" code max max max() { ((temp=s[sp--])) ((tos<temp)) && ((tos=temp)) } # ----------------------------------------------------------------------------- # ---------------------------- arithmetic compare ---------------------------- # ----------------------------------------------------------------------------- # ( x1 x2 -- flag ) compare top two stack elements, return true if equal, false otherwise revealheader "=" code equ equ equ() { tos=$((-(tos==s[sp--]))); } # ( x1 x2 -- flag ) compare top two stack elements, return true if unequal, false otherwise revealheader "<>" code nequ nequ nequ() { tos=$((-(tos!=s[sp--]))); } # ( x -- flag ) compare top stack element with zero, return true if equal, false otherwise revealheader "0=" code equ0 equ0 equ0() { tos=$((-(tos==0))); } # ( x -- flag ) return true if top element is less than 0, false otherwise revealheader "0<" code less0 less0 less0() { tos=$((-(tos<0))); } # ( n1 n2 -- flag ) return true if second stack element is smaller than top element, false otherwise revealheader "<" code less less less() { tos=$((-(s[sp--]<tos))); } # ( n1 n2 -- flag ) return true if second stack element is greater than top element, false otherwise revealheader ">" code greater greater greater() { tos=$((-(s[sp--]>tos))); } # ( x1 x2 x3 x4 -- flag ) compare x1,x2 with x3,x4, return true if equal, false otherwise revealheader "d=" code dequ dequ dequ() { tos=$((-(tos==s[sp-1] & s[sp]==s[sp-2]))) ((sp-=3)) } # ----------------------------------------------------------------------------- # ----------------------------------- bool ------------------------------------ # ----------------------------------------------------------------------------- # ( x1 x2 -- x3 ) bitwise and of top two stack elements revealheader "and" code and and and() { ((tos&=s[sp--])); } # ( x1 x2 -- x3 ) bitwise or of top two stack elements revealheader "or" code or or or() { ((tos|=s[sp--])); } # ( x1 x2 -- x3 ) bitwise xor of top two stack elements revealheader "xor" code xor xor xor() { ((tos^=s[sp--])); } # ( x1 -- x2 ) invert all bits of top stack elements revealheader "invert" code invert invert invert() { ((tos=~tos)); } # ----------------------------------------------------------------------------- # ------------------------ number conversion and i/o -------------------------- # ----------------------------------------------------------------------------- # alternative implementation. different stack effect. if conversion fails, n # is the number of character not converted. x is the accumulated values of all # legal digits up to the offending one # ( a n -- x 0 | x n ) try to convert n chars at a to number, respecting base revealheader "number" code number number number() { local digit sign=0 radix=${m[base+1]} (( src = s[sp] )) # addr of next digit (( s[sp] = 0 )) # accumulator (( m[src] == 45 )) && (( sign = -1 , src++ , tos-- )) # strip leading - for ((; tos; tos-- )); do # for all digits (( digit=m[src++]-48 )) # read ascii of digit, convert to numeric (( digit < 0 )) && break # flag chars below 0 as invalid numbers (( digit > 9 )) && { # chars above 9 need more attention (( digit -= 7 )) # convert A... to numeric (( digit < 10 )) && break # flag :...@ as invalid numbers (( digit >= 36 )) && { # chars above Z need more attention (( digit -= 32 )) # convert a... to numeric (( digit < 10 )) && break # flag [...' as invalid numbers } } (( digit >= radix )) && break # flag chars as invalid number" (( s[sp]=s[sp]*radix+digit )) done (( sign )) && (( s[sp] = -s[sp] )) } # conversion with standard stack effect. Uses alternative implementation now. # ( a n -- x -1 | 0 ) try to convert n chars at a to number, respecting base revealheader "?number" colon qnumber $number $equ0 $qdup $drop # ( n -- 0 n f ) start pictured number conversion revealheader "<#" code lesshash lesshash lesshash() { ((s[++sp]=0)) if ((tos<0)); then ((s[++sp]=-tos, tos=-1)) else ((s[++sp]=tos, tos=0)) fi } # problem with bash 4.2-1: comma delimited compound arithmetic expressions would segfault # ( n1 n2 f -- c n3 n4 f ) pictured number conversion: convert a single digit revealheader "#" code hash hash hash() { local radix=${m[base+1]} r[++rp]=$tos ((r[++rp]=s[sp]/radix)) ((tos=s[sp--]%radix)) ((tos<0)) && ((tos*=-1)) ((tos+=48)) ((tos>57)) && ((tos+=39)) s[sp+1]=$((s[sp]+1)) s[sp++]=$tos s[++sp]=${r[rp--]} tos=${r[rp--]} } # ( n1 n2 f -- ??? n3 n4 f ) pictured number conversion: convert remaining digits revealheader "#s" code hashs hashs hashs() { hash while ((s[sp])); do hash done } # ( n1 n2 f c -- c n3 n4 f ) pictured number conversion: insert a specified character revealheader "hold" code hold hold hold() { temp=${tos} tos=${s[sp]} s[sp]=${s[sp-1]} s[sp-1]=$((s[sp-2]+1)) s[sp-2]=${temp} } # ( n1 n2 f -- c n3 n4 f ) pictured number conversion: insert minus sign if converted number is negative revealheader "sign" code sign sign sign() { ((tos)) || return twoto_r ((tos++)) s[++sp]=45 twor_from } # ( ??? n1 n2 f -- a n3 ) pictured number conversion: end conversion, leaving number, converted to string revealheader "#>" code hashgreater hashgreater hashgreater() { ((sp--)) tos=${s[sp--]} i=$tos dest=$((dp+PADAWAY-tos)) temp=$dest while ((i--)); do m[dest++]=${s[sp--]} done s[++sp]=$temp } # ( n1 -- ) pictured number conversion: output the string to which number has been converted revealheader "#>type" code hashgreatertype hashgreatertype hashgreatertype() { ((sp--)) for ((i=s[sp--]; i; --i)); do printf '%s' "${asc[${s[sp--]}]}" done tos=${s[sp--]} } # ----------------------------------------------------------------------------- # ------------------------------------ i/o ------------------------------------ # ----------------------------------------------------------------------------- # ( c -- ) output the character which ascii is on top of stack revealheader "emit" code emit emit emit() { printf '%s' "${asc[tos]}" tos="${s[sp--]}" } # ( -- ) output a space character revealheader "space" code space space space() { printf '%1s' " " } # ( n -- ) output spaces revealheader "spaces" code spaces spaces spaces() { printf "%${tos}s" tos="${s[sp--]}" } # ( -- ) clear screen revealheader "page" code page clear # ( -- ) clear screen revealheader "cls" code cls clear # ( a n -- ) output the string, which address and len are given on stack revealheader "type" code type type type() { pack printf '%s' "$tos" tos="${s[sp--]}" } # ( -- ) output a line feed revealheader "cr" code cr printf '\n' # ( n -- ) raw output of tos. does not respect base, but can output string in tos. revealheader ".." code dotdot dotdot dotdot() { printf '%s ' "$tos" tos="${s[sp--]}" } # ( n -- ) output the signed number in tos, respecting base revealheader "." colon dot $lesshash $bl $hold $hashs $sign $hashgreatertype keybuf="" # ( -- c ) 0 or (immediately) ascii of keystroke # would need to stuff ascii into a key buffer, read by key revealheader "key?" code keyq keyq keyq() { [[ -z $keybuf ]] && IFS="" read -rsn1 -t0.001 keybuf s[++sp]="$tos" tos=$(((${#keybuf}==0)-1)) } # key: ( -- c ) read one char from input, return ascii revealheader "key" code key key key() { s[++sp]="$tos" [[ -z $keybuf ]] && IFS="" read -rsn1 keybuf tos=$(printf '%d' "'$keybuf") keybuf="" } # ( a n1 -- n2 ) read n1 chars from input, store at a. number of actually entered chars returned as n2 revealheader "accept" code accept accept accept() { printf '%s' "$keybuf" read -ersn "$tos" buffer tos="${keybuf}${buffer}" keybuf="" swap unpack } # ( c -- a n ) read word, delimited by c, from input stream. return address and len revealheader "stream" code stream stream stream() { local delimiter=$tos temp=${m[in+1]} char=${m[temp]} if ((delimiter==32)); then char=${m[temp]} while ((char!=255)); do ((char!=delimiter)) && break ((temp++)) char=${m[temp]} done fi s[++sp]=$temp tos=-$temp while ((char!=255)); do ((char==delimiter)) && break ((temp++)) char=${m[temp]} done ((tos+=temp)) ((char!=255)) && ((temp++)) m[in+1]=$temp } # ( -- ) output the prompt revealheader "prompt" code prompt prompt prompt() { if ((!m[state+1])); then printf '%s' " $PROMPT" for ((i=sp-s0; i; i--)); do printf '%s' "." done printf '%b' "\\n" fi } # ( -- ) show numbers on stack revealheader ".s" code dot_s dot_s dot_s() { if ((sp)); then temp=$s0 while ((sp>++temp)); do printf '%s' "${s[temp+1]} " done printf '%s' "$tos " fi } # ( -- ) exit bashforth, return to calling program of command line revealheader "bye" code bye exit # ----------------------------------------------------------------------------- # ------------------------------- dictionary --------------------------------- # ----------------------------------------------------------------------------- # ( -- ) modify header of most recently defined word to keep it from being found revealheader "hide" code hide hide # ( -- ) set most recent word "findable" revealheader "reveal" code reveal reveal # ( a n -- ) create a new header with name identical to string passed on stack revealheader "newheader" code newheader newheader newheader() { pack header $tos tos=${s[sp--]} } # ( xt -- a ) given xt, return word body address revealheader ">body" code tobody oneplus # ( a -- xt ) given word body address, return xt revealheader "body>" code bodyfrom oneminus # ( xt -- wordnum ) returns word number or 0, opposite of name>. revealheader ">name" code toname toname toname() { temp=$wc while ((temp)); do ((tos==x[--temp])) && break done tos=$temp } # ( wordnum -- xt ) calculate code field address from word number revealheader "name>" code name_from name_from name_from() { tos=${x[tos]}; } # ( wordnum -- a n ) return string with name of word, given word number revealheader "name" code name name name() { s[++sp]=$dp s[++sp]=${h[tos]} tos=$dp unpack } # ( wordnum -- ) output word name, given word number ("nfa") revealheader ".name" code dotname dotname dotname() { printf '%s' "${h[tos]}" tos=${s[sp--]} } # ( word# -- flag ) return true flag if word, specified by word number ("nfa"), is an immediate word revealheader "?immediate" code qimm qimm qimm() { ((tos=hf[tos]&precedencebit)) } # ( -- ) make most recently defined word an immediate word revealheader "immediate" code immediate immediate immediate() { ((hf[wc-1]|=precedencebit)) } # ( a n -- namefield | 0 ) returns 0 or word number of word which name is given as string on stack revealheader "locate" code locate locate locate() { pack temp=$wc while ((temp)); do if ((hf[--temp] & smudgebit)); then [[ "$tos" == "${h[temp]}" ]] && break fi done tos=$temp } # ( -- ) show list of words in vocabulary revealheader "words" code words words words() { (( COLUMNS )) || clear # initialize COLUMNS is necessary local out=0 local len for ((i=wc; i--;)); do len=$(( ${#h[i]}+2 )) (( out += len )) if (( out >= COLUMNS )); then printf '\n%s' "${h[i]} " out=$len else printf '%s' "${h[i]} " fi done } # ----------------------------------------------------------------------------- # ------------------------------ compilation ---------------------------------- # ----------------------------------------------------------------------------- # ( x -- ) revealheader "," code comma comma comma() { m[dp++]="$tos" tos="${s[sp--]}" } # ( c -- ) compile an 8-bit number to memory at "here" revealheader "c," code ccomma ccomma ccomma() { ((m[dp++]=tos&255)) tos="${s[sp--]}" } # ( -- ) turns compilation off revealheader "[" code leftbracket leftbracket leftbracket() { m[state+1]=0 } immediate # ( -- ) turns compilation on revealheader "]" code rightbracket rightbracket rightbracket() { m[state+1]=-1; } # ( n -- ) statically reserve n memory locations revealheader "allot" code allot allot allot() { ((dp+=tos)) tos=${s[sp--]} } # ( -- a ) returns end-of-code address revealheader "here" code here here here() { s[++sp]=$tos tos=$dp } # ----------------------------------------------------------------------------- # ----------------------------------- mem ------------------------------------- # ----------------------------------------------------------------------------- # ( a -- x ) read and return contents of address revealheader "@" code fetch fetch fetch() { tos="${m[tos]}"; } # ( a -- ) output the contents of address a as signed number. revealheader "?" colon dot $fetch $dot # ( x a -- ) store x into memory address a revealheader "!" code store store store() { m[tos]=${s[sp--]} tos=${s[sp--]} } # ( a -- c ) read and return 8 bits from memory address a revealheader "c@" code cfetch cfetch cfetch() { ((tos=m[tos]&255)) } # ( c a -- ) write 8 bits to memory at address a revealheader "c!" code cstore cstore cstore() { ((m[tos]=s[sp--]&255)) tos=${s[sp--]} } # ( a1 -- a2 c ) a1+1 -> a2, [a1] -> c revealheader "count" code count count count() { ((s[++sp]=tos+1, tos=m[tos]&255)) } # ( a1 -- a2 x ) a1+cell -> a2, [a1] -> x revealheader "skim" code skim skim skim() { ((s[++sp]=tos+1, tos=m[tos])) } # ( a -- x1 x2 ) fetch two cells from a revealheader "2@" colon twofetch $skim $swap $fetch # ( x1 x2 a -- ) store cells at a revealheader "2!" colon twostore $tuck $cellplus $store $store # ( n a -- ) add n to contents of memory att a revealheader "+!" code plusstore plusstore plusstore() { ((m[tos]+=s[sp--])) tos=${s[sp--]} } # ( x1 a -- x2 ) read x2 from a, then store x1 in a revealheader "exchange" code exchange exchange exchange() { temp=${m[tos]} m[tos]=${s[sp--]} tos=$temp } # ( a n1 c -- n2 ) search for c in string a n1. n2 is len of remainder, including first c revealheader "scan" code scan scan scan() { temp=$tos tos=${s[sp--]} dest=${s[sp--]} while ((tos)); do [[ "${m[dest++]}" == "$temp" ]] && break ((tos--)) done } # ( a n1 c -- n2 ) skip all leading c in atring a n1. n2 is len of remainder revealheader "skip" code skip skip skip() { temp=$tos tos=${s[sp--]} dest=${s[sp--]} while ((tos)); do [[ "${m[dest++]}" == "$temp" ]] || break ((tos--)) done } # ---------- compare is a bit dirty, because of quick fix ------------ # compare $tos bytes at $source and $dest # result of comparison (-1/0/1) in $tos compare1() { while ((tos)); do ((temp=m[source++]-m[dest++])) if ((temp)); then tos="$(( ((temp > 0) << 1) - 1))" break fi ((tos--)) done } # ( a1 n1 a2 n2 -- -1 | 0 | 1 ) compare two strings at a1 and a2. revealheader "compare" code compare compare compare() { # n2 in tos local dest=${s[sp--]} local temp=${s[sp--]} local source=${s[sp--]} if [[ "$temp" = "$tos" ]]; then compare1 else temp2=1 if [[ $temp < $tos ]]; then tos=$temp temp2=-1 fi compare1 if ! ((tos)); then tos=$temp2 fi fi } # ( a1 n c -- ) fill n memory locations at a1 with c revealheader "fill" code fill fill fill() { i=${s[sp--]} dest=${s[sp--]} for ((; i; i--)); do m[dest++]=$tos done tos=${s[sp--]} } # ( a1 a2 n -- ) move contents of n memory locations at a1 to a2 revealheader "move" code move move move() { if [[ ${s[sp]} > ${s[sp+1]} ]]; then ((dest=s[sp--]+tos, src=s[sp--]+tos)) while ((tos--)); do m[--dest]=${m[--src]} done else local dest=${s[sp--]} src=${s[sp--]} while ((tos--)); do m[dest++]=${m[src++]} done fi tos=${s[sp--]} } # ( a1 n a2 -- ) store string a1 n at a2, with leading count byte revealheader "move$" code movestr movestr movestr() { temp=${s[sp]} m[tos++]=$temp s[sp]=$tos tos=$temp move } # ( a1 n1 n -- a2 n2 ) clip first n chars off string at a1 revealheader "/string" code slashstring slashstring slashstring() { temp=$tos tos=${s[sp--]} if ((tos<temp)); then temp=$tos fi ((s[sp]+=temp, tos-=temp)) } # ( c -- a n ) read word, delimited by c, from input stream. return address and len revealheader "word" colon word $stream $here $movestr $here # ----------------------------------------------------------------------------- # ------------------------------ string stack --------------------------------- # ----------------------------------------------------------------------------- # ( a n -- ) push string at a to string stack revealheader "push$" code pushstr pushstr pushstr() { pack ss[++ssp]="$stos" stos="$tos" tos="${s[sp--]}"; } # ( -- a n ) pop string from string stack to here revealheader "pop$" code popstr popstr popstr() { if ((!ssp)); then tos=-65; codethrow fi s[++sp]="$tos" tos=$dp s[++sp]=$tos s[++sp]="$stos" stos="${ss[ssp]}" ss[ssp--]="" unpack } # ( stringstack: string -- ) pop and output string from string stack revealheader "type$" code typestr typestr typestr() { if ((!ssp)); then tos=-65; codethrow fi printf '%s' "$stos" stos="${ss[ssp]}" ((ssp--)) } # ( -- n ) returns number stack elements on string stack revealheader "depth$" code depthstr depthstr depthstr() { s[++sp]=$tos tos=$((ssp-ss0)) } # ( -- ) show strings on string stack revealheader ".s$" code dot_sstr dot_sstr dot_sstr() { if ((ssp)); then printf '%s\n' "$stos " temp=$(( ssp )) while ((temp > ss0+1)); do printf '%s\n' "${ss[temp--]}" done fi } # ( stringstack: str -- str str ) duplicate top string stack element revealheader "dup$" code dupstr dupstr dupstr() { ss[++ssp]="$stos"; } # ( stringstack: x1 x2 -- x1 x2 x1 x2 ) duplicate top two elements of stack element revealheader "2dup$" code dup2str dup2str dup2str() { ss[++ssp]="$stos" ss[++ssp]="${ss[ssp-1]}"; } # ( stringstack: x -- ) drop top stringstack element revealheader "drop$" code dropstr dropstr dropstr() { if ((!ssp)); then tos=-65; codethrow fi stos="${ss[ssp]}" ss[ssp--]="" } # ( stringstack: x1 x2 -- x2 x1 ) swap top two string stack elements revealheader "swap$" code swapstr swapstr swapstr() { if ((ssp<2)); then tos=-65; codethrow fi temp="$stos" stos="${ss[ssp]}" ss[ssp]="$temp" } # ( stringstack: x1 x2 -- x1 x2 x1 ) copies next-of-stack of string stack to top revealheader "over$" code overstr overstr overstr() { if ((ssp<2)); then tos=-65; codethrow fi ss[++ssp]="$stos" stos="${ss[ssp-1]}" } # ( stringstack: x1 x2 -- x2 ) discards next-of-stack string stack element revealheader "nip$" code nipstr nipstr nipstr() { if ((ssp<2)); then tos=-65; codethrow fi ((ssp--)) } # ( stringstack: x1 x2 x3 -- x2 x3 x1 ) rotate 3rd string stack element to top revealheader "rot$" code rotstr rotstr rotstr() { if ((ssp<3)); then tos=-65; codethrow fi temp="${ss[ssp]}" ss[ssp]="$stos" stos="${ss[ssp-1]}" ss[ssp-1]="$temp" } # ( stringstack: "string1" "string2" -- "string1string2" ) joins top two strings revealheader "append$" code appendstr appendstr appendstr() { if ((ssp<2)); then tos=-65; codethrow fi stos="${ss[ssp--]}$stos" } # ( u1 u2 -- ) ( ss: $1 -- $2 ) cut and return string starting at index u1 (zero based) with max length of u2 chars. negative index counts from end of string revealheader "sub$" code substr substr substr() { if ((!ssp)); then tos=-65; codethrow fi temp=${s[sp--]} stos="${stos:$temp:$tos}" tos=${s[sp--]} } # ( u -- ) ( ss: $1 -- $2 ) modifies string so that leading u chars of string remain revealheader "left$" colon leftstr $zero $swap $substr # ( u -- ) ( ss: $1 -- $2 ) modifies string so that trailing u chars of string remain revealheader "right$" colon rightstr $dup $negate $swap $substr # ( a n -- ) creates header. expects ascii array type string revealheader "create$" code createstr createstr createstr() { newheader m[dp++]=dovar reveal } # ----------------------------------------------------------------------------- # --------------------------------- does> ------------------------------------- # ----------------------------------------------------------------------------- # executed upon execution of word defined by defining word: # puts body address of defined word on stack, nests into does> action # ( -- a ) code dodoes dodoes dodoes() { s[++sp]=$tos tos=$w r[++rp]=$ip ip=$1 } code setdoes setdoes setdoes() { m[m[lastxt+1]]="dodoes $((ip+1))"; } # ( -- ) define run time action of a compiling word revealheader "does>" colon does \ $lit $setdoes $comma \ $lit $unnest $comma immediate start() { while w=${m[ip++]}; do ${m[w++]} w=${m[ip++]}; ${m[w++]} w=${m[ip++]}; ${m[w++]} w=${m[ip++]}; ${m[w++]} w=${m[ip++]}; ${m[w++]} w=${m[ip++]}; ${m[w++]} w=${m[ip++]}; ${m[w++]} w=${m[ip++]}; ${m[w++]} w=${m[ip++]}; ${m[w++]} w=${m[ip++]}; ${m[w++]} w=${m[ip++]}; ${m[w++]} w=${m[ip++]}; ${m[w++]} w=${m[ip++]}; ${m[w++]} w=${m[ip++]}; ${m[w++]} w=${m[ip++]}; ${m[w++]} w=${m[ip++]}; ${m[w++]} done } # ----------------------------------------------------------------------------- # ---------------------------- hi-level words --------------------------------- # ----------------------------------------------------------------------------- # ( ??? -- ) initialize stacks, return to forth command line interpreter revealheader "quit" defer quit # ( a -- ) set cfa of last word to a revealheader "use" colon use $last $fetch $store # ( -- f ) returns flag, indicating whether bashforth is compiling (-1) or interpreting (0) revealheader "compiling" colon compiling $state $fetch # ( -- ) throw exception if in intepreting state revealheader "?comp" colon qcomp $compiling $branchx 2 $compileonly # ( a n -- a n 0 | xt 1 | xt -1 ) search dictionary, returns name and 0 if not found, xt and precedence (1=imm) if found revealheader "find" colon findx \ $dup2 $locate \ $dup $branch0 10 \ $nip $nip \ $dup $name_from \ $swap $qimm \ $equ0 $one $or \ # ( x -- ) immediate word which compile top of stack as number into word revealheader 'literal' colon literal $lit $lit $comma $comma immediate # ( <stream> -- a ) return execution token of word which name is read from input stream revealheader "'" colon tick \ $bl $stream $findx \ $branchx 3 \ $type $notfound # ( <stream> -- ) compile execution token of next word revealheader "[']" colon brackettick $qcomp $tick $literal immediate revealheader "postpone" colon postpone $tick $comma immediate # ( -- ) do nothing revealheader "nop" ; code nop : ; immediate # ( n1 -- n2 ) convert cells to number of memory locations revealheader "cells" ; code cells : ; immediate # ( n1 -- n2 ) convert chars to number of memory locations revealheader "chars" ; code chars : ; immediate # ( -- ) set number base to 16 revealheader "hex" colon hex $lit 16 $base $store # ( -- ) set number base to 10 revealheader "decimal" colon decimal $lit 10 $base $store # ( -- ) set number base to 2 revealheader "binary" colon binary $two $base $store # ( -- a ) return address of a scratch string space revealheader "pad" colon pad $here $lit $PADAWAY $plus # ( <stream> -- ) create a new header, name read from input stream revealheader "create" colon create $bl $stream $createstr # ( <stream> -- ) create a variable revealheader "variable" colon variable $create $zero $comma # ( <stream> x -- ) create a constant revealheader "constant" colon constant $create $comma $lit doconst $use # ( <stream> -- ) create new high-level word revealheader ":" colon hllcolon $bl $stream $newheader $lit nest $comma $rightbracket revealheader ":noname" colon colnoname $here $lit nest $comma $rightbracket # ( -- ) finish compilation of a high-level word revealheader ";" colon hllsemicolon $lit $unnest $comma $leftbracket $reveal immediate # ( a n -- ) compile the string, whose address and len is passed on stack revealheader ',$' colon commastr $here $over $oneplus $allot $movestr # ( <stream> -- ) compile a string from input stream revealheader ',"' colon commaquote $lit 34 $stream $commastr # ( <stream> -- ) put address and len of string, delimited by ), interactively on stack revealheader 's(' colon sbracket $lit 41 $stream $here $movestr $here $count immediate # ( <stream> -- ) compile string from input stream into word, return address and len during run time revealheader 's"' colon squote $qcomp $lit $dosquote $comma $commaquote immediate # ( <stream> -- ) output string from input stream, in interpreting mode revealheader '.(' colon dotbracket $lit 41 $stream $type immediate # ( <stream> -- ) compile string to high-level word, output string at run time revealheader '."' colon dotquote $qcomp $lit $dodotquote $comma $commaquote immediate # ( <stream> -- ) ignore text until ) as comment revealheader '(' colon bracket $lit 41 $stream $drop2 immediate # ( <stream> -- ) ignore rest of line as comment revealheader '\' colon backslash $zero $stream $drop2 immediate # ( <stream> -- ) ignore rest of line as comment revealheader '#!' colon shebang $zero $stream $drop2 immediate # ( <stream> -- c ) return ascii of next char on stack revealheader 'char' colon brchar $bl $stream $drop $cfetch immediate # ( <stream> -- c ) return ascii of next char on stack, or compile as literal revealheader '[char]' colon brchar $brchar $compiling $branch0 2 $literal immediate # ----------------------------------------------------------------------------- # -------------------------------- flow control ------------------------------- # ----------------------------------------------------------------------------- colon structured $nequ $branch0 2 $unstruc colon qclause $lit $branch0 $comma colon clause $lit $branch $comma colon resolve $here $minus $comma colon mark $here $zero $comma colon resolveback $here $over $minus $swap $store # ( f -- ) flow control: true/false if ... else ... then . else part is optional revealheader "if" colon fif $qcomp $qclause $mark $one immediate # ( -- ) flow control: true/false if ... else ... then revealheader "else" colon felse $qcomp $one $structured $clause $mark $swap $resolveback $two immediate # ( -- ) flow control: true/false if ... else ... then . else part is optional revealheader "then" colon fthen $qcomp $dup $two $equ $plus $one $structured $resolveback immediate # ( -- ) flow control: begin ... true/false until or begin ... true/false while ... repeat revealheader "begin" colon fbegin $qcomp $here $three immediate # ( f -- ) flow control: begin ... true/false while ... repeat revealheader "while" colon fwhile $qcomp $three $structured $qclause $mark $four immediate # ( -- ) flow control: begin ... true/false while ... repeat revealheader "repeat" colon frepeat $qcomp $four $structured $swap $clause $resolve $resolveback immediate # ( -- ) flow control: begin ... again revealheader "again" colon fagain $qcomp $three $structured $clause $resolve immediate # ( f -- ) flow control: begin ... true/false until revealheader "until" colon funtil $qcomp $three $structured $qclause $resolve immediate var innerloop # ( start -- ) flow control: (limit) for ... next , counts down revealheader "for" colon ffor $qcomp $lit $dofor $comma \ $here $innerloop $exchange \ $here $zero $comma \ $six immediate # ( -- ) flow control: (limit) for ... next , counts down revealheader "next" colon floop $qcomp $six $structured \ $lit $donext $comma \ $dup $oneplus $resolve \ $resolveback \ $innerloop $store immediate # ( limit start -- ) flow control: (limit) (start) do ... loop revealheader "do" colon fdo $qcomp $lit $dodo $comma \ $here $innerloop $exchange \ $here $zero $comma \ $five immediate # ( limit start -- ) flow control: (limit) (start) ?do ... loop revealheader "?do" colon fqdo $qcomp $lit $doqdo $comma \ $here $innerloop $exchange \ $here $zero $comma \ $five immediate # ( -- ) flow control: (limit) (start) do ... loop revealheader "loop" colon floop $qcomp $five $structured \ $lit $doloop $comma \ $dup $oneplus $resolve \ $resolveback \ $innerloop $store immediate # ( n -- ) flow control: (limit) (start) do ... (increment) +loop revealheader "+loop" colon fplusloop $qcomp $five $structured \ $lit $doplusloop $comma \ $dup $oneplus $resolve \ $resolveback \ $innerloop $store immediate # ( a -- ) colon putleave $qcomp $comma $innerloop $fetch $qdup $branch0 3 $comma $unnest $nolooppars # ( -- ) flow control: (limit) (start) do ... if ... leave then ... loop revealheader "leave" colon leave $lit $doleave $putleave immediate # ( f -- ) flow control: (limit) (start) do ... (flag) ?leave ... loop revealheader "?leave" colon qleave $lit $doqleave $putleave immediate # ----------------------------------------------------------------------------- # -------------------------------- interpreter -------------------------------- # ----------------------------------------------------------------------------- # ( -- ) fill input buffer from standard input revealheader "query" colon query \ $lit 255 \ $tib $dup $in $store \ $dup \ $lit $((TIBSIZE-1)) \ $accept \ $plus \ $cstore # ( a n -- ) interpreter for a single word revealheader "interpret1" colon interpret1 \ $findx \ $qdup $branch0 17 \ $oneminus $branch0 7 \ $compiling $branch0 4 \ $comma \ $branch 7 \ $execute \ $depth $less0 $branch0 \ 2 $stackunderflow $unnest \ $dup2 $qnumber \ $branch0 8 \ $nip $nip \ $compiling \ $branch0 2 \ $literal \ $unnest \ $type $notfound # ( -- ) interpret one line of forth source revealheader "interpret" colon interpret \ $lit 32 $stream \ $qdup \ $branch0 4 \ $interpret1 \ $branch -8 \ $drop # ( a n -- ) interpret the string passed on stack #revealheader "evaluate" #colon evaluate \ # string to tib $interpret # ----------------------------------------------------------------------------- # ---------------------------------- include ---------------------------------- # ----------------------------------------------------------------------------- # ( a n1 -- n2 ) code from from from() { local i pack f=(); i=0 if [[ ! -f "$tos" ]]; then tos="${tos}.bashforth" fi if [[ -f "$tos" ]]; then while read -r f[i] do (( i++ )) done < $tos tos=$i else tos=-38; codethrow fi } # ( a n1 -- n2 ) code endfrom endfrom endfrom() { unset f } # ( n -- ) code line line line() { [[ $LOADING ]] && printf '%s' "$LOADING" s[++sp]=${f[tos]} tos=${m[tib+1]} m[in+1]=$tos unpack m[tos+${m[tib+1]}]=255 tos=${s[sp--]} } revealheader "sourcepath" code sourcepath sourcepath sourcepath() { ss[++ssp]="$stos" stos="$sources/" } revealheader "!sourcepath" code storesourcepath storesourcepath storesourcepath() { if ((!ssp)); then tos=-65; codethrow fi sources="$stos" stos="${ss[ssp]}" ((ssp--)) } # ( <stream> -- ) read forth source from file revealheader "include" colon include \ $sourcepath \ $bl $stream $pushstr $appendstr \ $popstr $from \ $zero $dodo 6 \ $i $line \ $interpret \ $doloop -4 \ $endfrom # ----- file interface ----- # ( -- x ) a constant for file access method r/o #revealheader "r/o" #constant famreadwrite 0 # ( -- x ) a constant for file access method r/w #revealheader "r/w" #constant famreadwrite 1 # ( a n fam -- fileid ior ) #revealheader "create-file" #code create-file create-file #create-file() { # r[++rp]=$tos # tos=${s[sp]} # pack # (echo -n > $tos) 2> /dev/null # s[sp]="12345678" # can only use one handle as far # tos=$? # (( rp-- )) # ior is not used now # if fam=0 then chmod -r filename #} # open-file # read-file # write-file # close-file # file-size # file-position # ----------------------------------------------------------------------------- save-system and restore were # ------------------------------- save-system --------------------------------- contributed by quaraman-me # ----------------------------------------------------------------------------- save_array_print() { local i local inext local -n a=$1 inext=0 printf 'a0\n' for i in "${!a[@]}"; do [[ "$inext" != "$i" ]] && printf 'ao %s\n' "$i" printf 'a %s\n' "${a[i]}" (( inext++ )) done } # ( a c -- ) writes image of system to file, file name passed as address, count on stack code saveas saveas saveas() { local i local inext local len pack [[ "$tos" ]] || tos="$sources/saved_system" { printf "version %s\n" "$version" printf "wc %s\n" "$wc" printf "dp %s\n" "$dp" save_array_print m save_array_print h save_array_print hf save_array_print x } > "$tos" tos=${s[sp--]} } # ( <stream> -- ) writes image of system to file, file name taken from input stream. If no name given, "save_system" will be the name. revealheader "save-system" colon savesystem $bl $stream $saveas # ----------------------------------------------------------------------------- # -------------------------- restore saved system ----------------------------- # ----------------------------------------------------------------------------- # ( a c -- ) code restorefrom restorefrom restorefrom() { local load_version local cmd local prg local a=0 local ai=0 local linenr=0 pack m=() h=() hf=() x=() fname="${tos:-$sources/saved_system}" tos=${s[sp--]} while read -r line; do read -a prg <<< "$line" cmd="${prg[0]}" p1="${prg[1]}" p2="${prg[2]}" case "$cmd" in version) load_version="$p1" [[ "$load_version" != "$version" ]] && echo "Not same Version : $load_version" ;; wc) wc="$p1" ;; dp) dp="$p1" ;; a0) ai=0 (( a++ )) ;; ao) ai="$p1" ;; a) case $a in 1) m[ai++]="$p1" ;; 2) h[ai++]="$p1" ;; 3) hf[ai++]="$p1" ;; 4) x[ai++]="$p1" ;; esac ;; hlt) ai=0 break; ;; esac (( linenr++ )) done < "$fname" } # ( <stream> -- ) write image of system to file, file name taken from input stream. If no name given, saved_system will be the name. revealheader "restore" colon restore $bl $stream $restorefrom # ----------------------------------------------------------------------------- # ------------------------------ init / startup ------------------------------- # ----------------------------------------------------------------------------- code init_stacks init_stacks init_stacks() { sp=$s0 temp=${r[rp]} rp=$r0 r[rp]=$temp } # executed by cold and warm code init_other init_other init_other() { tos=0 ssp=$ss0 ss[ssp]="" m[base+1]=10 m[innerloop+1]=0 m[state+1]=0 } # ( ??? -- ) revealheader "(quit)" colon bracketquit \ $init_stacks \ $zero $innerloop $store \ $leftbracket \ $query \ $interpret \ $prompt \ $branch -4 m[quit+1]=$bracketquit # set deferred quit # ( ??? -- ) revealheader "(warm)" colon warmhandler \ $init_stacks \ $init_other \ $decimal \ $prompt \ $quit m[warm+1]=$warmhandler # set deferred warm # ( -- ) prints GPL notice revealheader "license" code license license license() { echo " This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA " } # ( -- ) prints the opening screen revealheader "hello" code hello hello hello() { echo -e "\nBashForth v$version $(license) www: https://github.com/Bushmills/Bashforth words <enter> shows a list of available words doc word <enter> gives description of word " } # ----------------------------------------------------------------------------- # ------------------------------ misc optionals ------------------------------- # ----------------------------------------------------------------------------- # these may shell to other programs. in fact, several of the following words do # ----------------------------------- doc ------------------------------------- # ( xt -- x ) code sourceline sourceline sourceline() { tos=${m[tos-1]}; } # calls cat, sed, cut # ( -- ) code printdoc printdoc printdoc() { temp=$(sed -n $(( tos+1 ))p $0 | cut -f 2 -d " ") NAME=${temp:1:${#temp}-2} temp=$(sed -n ${tos}p $0 | sed s/"# "//) STACKEFFECT=${temp%%)*} DESCRIPTION=${temp#*)} echo "$NAME $STACKEFFECT)" temp="sorry, this word hasn't been documented yet" echo "${DESCRIPTION:-$temp}" tos=${s[sp--]} } # ( <stream> -- ) print stack effect and description of word, name taken from input stream revealheader "doc" colon doc $tick $sourceline $printdoc # ----------------- see ------------------ # calls sed # ( -- ) code printsource printsource printsource() { echo "in file $0:" sed -n "1,${tos}d;p;/^ *$/q" "$0" tos=${s[sp--]} } # ( <stream> -- ) print source of a word (read from the executed bashforth script file) revealheader "see" colon see $tick $sourceline $printsource # ---------------------- terminal control --------------------- # ( -- 0 ) returns color code for color black revealheader "black" constant black 0 # ( -- 1 ) returns color code for color red revealheader "red" constant red 1 # ( -- 2 ) returns color code for color green revealheader "green" constant green 2 # ( -- 3 ) returns color code for color yellow revealheader "yellow" constant yellow 3 # ( -- 4 ) returns color code for color blue revealheader "blue" constant blue 4 # ( -- 5 ) returns color code for color magenta revealheader "magenta" constant magenta 5 # ( -- 6 ) returns color code for color cyan revealheader "cyan" constant cyan 6 # ( -- 7 ) returns color code for color white revealheader "white" constant white 7 # fg: 0:3 bg: 4:7 bold: 8 underscore: 9 (( attributes = 112 )) # ( color -- ) set foreground color revealheader "fg" code fg fg fg() { ((tos&=7, attributes&=-16, attributes|=tos )) printf '%b' "\e[3${tos}m" tos=${s[sp--]} } # ( color -- ) set background color revealheader "bg" code bg bg bg() { ((tos &= 7, attributes&=-241, attributes|=(tos << 4))) printf '%b' "\e[4${tos}m" tos=${s[sp--]} } # ( -- ) reset colors and attributes to normal revealheader "normal" code normal normal normal() { attributes=112 printf '%b' "\e[0m" } # ( -- ) set bold attribute revealheader "bold" code bold bold bold() { ((attributes&=-257, attributes|=256)) printf '%b' "\e[1m" } # ( -- ) set underscore attribute revealheader "underscore" code underscore underscore underscore() { ((attributes&=-513, attributes|=512)) printf '%b' "\e[4m" } # ( -- ) reverse screen colors revealheader "reverse" code reverse reverse reverse() { colors; fg; bg; } # ( -- u ) read all screen attributes, incl color revealheader "attr@" code attrfetch attrfetch attrfetch() { s[++sp]=$tos tos=$attributes } # ( u -- ) set all screen attributes, incl color, as read with attr@ revealheader "attr!" code attrstore attrstore attrstore() { attributes=$tos printf '%b' "\e[3$((tos&7));4$(((tos>>4)&7))" ((temp=(tos>>8)&1)) ((temp)) && printf '%b' ";$temp" ((temp=(tos>>7)&4)) ((temp)) && printf '%b' ";$temp" echo -n "m" tos=${s[sp--]} } # ( -- fg bg ) return current colors revealheader "colors" code colors colors colors() { ((s[++sp]=tos, s[++sp]=attributes&7, tos=(attributes>>4)&7)) } # ( x y -- ) position cursor at x,y revealheader "at" code atxy atxy atxy() { printf '%b' "\e[$((tos+1));$((s[sp--]+1))H" tos=${s[sp--]} } # ( -- ) position cursor at upper left revealheader "home" code home home home() { printf '%b' "\e[H"; } # --------------------------------------------------------------------- # ( n1 -- n2 ) returns random number between 0 and n1-1 (max 2^30-1 = 1073741823) revealheader "rnd" code rnd rnd rnd() { ((tos="((RANDOM<<15)|RANDOM)%tos")); } # ( -- s m h d m y ) returns system time: seconds minutes hours day month year revealheader "time&date" code timeanddate timeanddate timeanddate() { s[++sp]=$tos temp=( $( date "+%S %M %H %d %m %Y" ) ) for i in {0..4}; do s[++sp]=$( printf '%g' "${temp[i]}" ) done tos=${temp[5]} } # ----------------------------------------------------------------------------- # ---------------------------------- shell ------------------------------------ # ----------------------------------------------------------------------------- # ( -- ) shows environment variables revealheader "set" code shellset set # ( -- ) ( string: name -- contents ) replaces name of an environment variable against contents revealheader "env" code environment environment environment() { stos="${!stos}" } # ( -- ) shells to bash revealheader "bash" code shellbash bash # ( a n1 -- n2 ) shell, string is command + arguments. returns exit code revealheader "system" code system system system() { pack $tos tos=$? } # ( a1 n1 a2 n2 -- n3 ) shell, append a2 n2 as arguments to command a1 n1, returns exit code revealheader "system2" code system2 system2 system2() { pack cmdline=$tos tos=${s[sp--]} pack $tos $cmdline tos=$? } # ( a n -- ) takes file name from stack and edits file, using external editor revealheader "(edit)" code brtextedit brtextedit brtextedit() { pack $EDITOR $tos tos=${s[sp--]} } # ( <stream> -- ) edit the file with name taken from stream revealheader "edit" colon textedit $zero $stream $brtextedit # ( n -- ) sleeps for n seconds revealheader "secs" code secs secs secs() { sleep $tos tos=${s[sp--]} } # ( n -- ) sleeps for n milliseconds revealheader "ms" code ms ms ms() { sleep $((tos/1000)).$((tos%1000)) tos=${s[sp--]} } revealheader "epoche" code epoche epoche if (( ${BASH_VERSION%%.*} < 5 )); then # ( -- n ) returns seconds since 1/1/1970 epoche() { s[++sp]=$tos tos=$(date +%s) } else # ( -- n ) returns seconds since 1/1/1970 code epoche epoche epoche() { s[++sp]=$tos tos=$EPOCHSECONDS } fi # ( -- n ) returns nanoseconds since 1/1/1970 revealheader "nanoseconds" code nanoseconds nanoseconds nanoseconds() { s[++sp]=$tos tos=$(date +%s%N); } # ( xt -- n ) measures the time in nanoseconds to execute xt, returned as n revealheader "time" colon measuretime \ $nanoseconds $to_r $execute $nanoseconds $r_from $minus # ----------------------------------------------------------------------------- # ------------------------- interpreter entry point -------------------------- # ----------------------------------------------------------------------------- code commandline commandline commandline() { s[++sp]=$tos tos=0 # assume no command line if [[ $COMMANDLINE ]]; then s[++sp]="nop $COMMANDLINE" # unless one received ((m[in+1]=tos=tib+1)) # destination, dest becomes input buffer unpack # convert string to chars m[tos+tib+1]=255 # end of line delimiter at end of buffer works unset COMMANDLINE # execute only once tos=-1 # indicate "commandline found" fi } code setrealthrow setrealthrow setrealthrow() { m[throw+1]="$realthrow" } revealheader "cold" boot=$dp; colon cold \ $init_stacks \ $init_other \ $decimal \ $commandline \ $branch0 8 \ $tick $interpret $catch $throw $bye \ $branch 2 \ $hello \ $setrealthrow \ $prompt \ $query \ $interpret \ $prompt \ $branch -4 # duplicating part of the outer interpreter loop here is done # to allow command line actions to carry over stack into the # interactive interpreter - the "quit" outer interpreter # initializes the stacks # ----------------------------------------------------------------------------- # ---------------------------- remove transients ------------------------------ # ----------------------------------------------------------------------------- i=${#remove[*]} while ((i)); do unset "${remove[--i]}" done # ----------------------------------------------------------------------------- # ----------------------------- start interpreter ----------------------------- # ----------------------------------------------------------------------------- set +u [[ -f ~/.bashforthrc ]] && source ~/.bashforthrc if [[ -f "$sources/$1" ]]; then COMMANDLINE="include $*" else COMMANDLINE="$*" fi ip=$boot start # ----------------------------------------------------------------------------- # end of shell script # -----------------------------------------------------------------------------

Compiling Program...

Command line arguments:
Standard Input: Interactive Console Text
×

                

                

Program is not being debugged. Click "Debug" button to start program in debug mode.

#FunctionFile:Line
VariableValue
RegisterValue
ExpressionValue