#!/bin/sh # Weird shell stack based forth "lookalike" # recursion can be done by repeating the # word. Words cannot have multiple defs # in a dictionary. # todo: # implement conditionals in _execword :^) ignore() : alias :=_newword alias ']=_execbody' _stack="" # top is _builtin_dictionary="swap dup rot over drop add sub div mod mul put not or and xor eq lt gt ne" # + - / * . _newword() { name="$1" shift [ "$_debug" ] && { printf '_newword %s: %s\n' "$name" "$*" read } eval "${name}_definition=\"$*\"" } _execword() { [ "$_debug" ] && { printf '_execword %s\n' "$1" read } eval "def=\"\$$1_definition\"" [ "$def" ] || { printf '%s\n' "Error: no word $1" return } _execbody $def } _execbody() { while [ "$1" ]; do word="$1" [ "$_debug" ] && { printf 'word %s\nstack %s\n' "$word" "$_stack" read } case "$_builtin_dictionary" in *"$word"*) "_$word" $_stack ;; *) case "$word" in then) ignore ;; if) [ "${_stack%% *}" = 0 ] && { [ "$_debug" ] && { printf 'failed if, jumping ahead\n' read } _ifcnt=1 _thencnt=0 while [ "$_ifcnt" != "$_thencnt" ]; do [ "$_debug" ] && { printf '$1 %s\n' "$1" printf '$* %s\n' "$*" printf 'ifcnt: %s thencnt: %s\n' "$_ifcnt" "$_thencnt" read } shift case "$1" in if) ignore $(( _ifcnt += 1 )) ;; then) ignore $(( _thencnt += 1 )) ;; "") printf 'Unmatched if\n'; return ;; esac done } _drop $_stack shift continue ;; *[0-9]*) _stack="$word $_stack" ;; *) _execword "$word" ;; esac ;; esac shift done } _swap() { one="$1" two="$2" shift 2 _stack="$two $one $*" } _dup() { _stack="$1 $*" } _rot() { one="$1" two="$2" thr="$3" shift 3 _stack="$thr $one $two $*" } _over() { _stack="$2 $*" } _drop() { shift _stack="$*" } _add() { one="$1" two="$2" shift 2 _stack="$(( one + two )) $*" } _sub() { one="$1" two="$2" shift 2 _stack="$(( two - one )) $*" } _div() { one="$1" two="$2" shift 2 _stack="$(( two / one )) $*" } _mod() { one="$1" two="$2" shift 2 _stack="$(( two % one )) $*" } _mul() { one="$1" two="$2" shift 2 _stack="$(( one * two )) $*" } _put() { one="$1" shift printf '%s \n' "$one" _stack="$*" } _not() { one="$1" shift _stack="$(( ~ one )) $*" } _or() { one="$1" two="$2" shift 2 _stack="$(( one | two )) $*" } _and() { one="$1" two="$2" shift 2 _stack="$(( one & two )) $*" } _xor() { one="$1" two="$2" shift 2 _stack="$(( one ^ two )) $*" } _eq() { one="$1" two="$2" shift 2 if [ "$one" -eq "$two" ]; then _stack="$(( ~ 0 )) $*" else _stack="$(( 0 )) $*" fi } _lt() { one="$1" two="$2" shift 2 if [ "$two" -lt "$one" ]; then _stack="$(( ~ 0 )) $*" else _stack="$(( 0 )) $*" fi } _gt() { one="$1" two="$2" shift 2 if [ "$two" -gt "$one" ]; then _stack="$(( ~ 0 )) $*" else _stack="$(( 0 )) $*" fi } _ne() { one="$1" two="$2" shift 2 if [ "$two" -ne "$one" ]; then _stack="$(( ~ 0 )) $*" else _stack="$(( 0 )) $*" fi } # definitions in forsh : true not false ; : false 0 ;