265 lines · 5.3 KB · Go
RawDownload
// the forth package is designed for use by programs // needing to evaluate command-line arguments or simple // expressions to set program variables. It is designed // to map host names to numbers. We use it to // easily convert host names and IP addresses into // parameters. // The language// is a Forth-like postfix notation. Elements are // either commands or strings. Strings are // immediately pushed. Commands consume stack variables// and produce new ones. // Simple examples: // push hostname, strip alpha characters to produce a number. If your// hostname is sb47, top of stack will be left with 47. // hostname  hostbase // Get the hostbase, if it is 0 mod 20, return the hostbase / 20, // else return hostbase mod 20// hostname hostbase dup 20 / swap 20 % dup ifelse// At the end of the evaluation the stack should have one element // left; that element is popped and returned. It is an error (currently)// to return with a non-empty stack. package forth import (	"errors"	"os"	"runtime"	"strconv"	"strings") type forthop func(f Forth) type forthstack struct {	stack []string} var opmap = map[string]forthop{	"+":        plus,	"-":        sub,	"*":        times,	"/":        div,	"%":        mod,	"swap":     swap,	"ifelse":   ifelse,	"hostname": hostname,	"hostbase": hostbase,	"strcat":   strcat,	"roundup":  roundup,	"dup":      dup,} // Forth is an interface used by the package. The interface// requires definition of Push, Pop, Length, Empty (convenience function// meaning Length is 0), Newop (insert a new or replacement operator), // and Reset (clear the stack, mainly diagnostic)type Forth interface {	Push(string)	Pop() string	Length() int	Empty() bool	Newop(string, forthop)	Reset()	Stack()[]string} // New creates a new stackfunc New() Forth {	f := new(forthstack)	return f} // Newop creates a new operation. We considered having// an opmap per stack but don't feel the package requires itfunc (f *forthstack) Newop(n string, op forthop) {	opmap[n] = op} func  Ops() (map[string] forthop){	return opmap}// Reset resets the stack to emptyfunc (f *forthstack) Reset() {	f.stack = f.stack[0:0]}// Return the stack as a []stringfunc (f *forthstack) Stack() []string{	return f.stack} // Push pushes the string on the stack. func (f *forthstack) Push(s string) {	f.stack = append(f.stack, s)	//fmt.Printf("push: %v: stack: %v\n", s, f.stack)} // Pop pops the stack. If the stack is Empty Pop will panic. // Eval recovers() the panic. func (f *forthstack) Pop() (ret string) { 	if len(f.stack) < 1 {		panic(errors.New("Empty stack"))	}	ret = f.stack[len(f.stack)-1]	f.stack = f.stack[0 : len(f.stack)-1]	//fmt.Printf("Pop: %v stack %v\n", ret, f.stack)	return ret} // Length returns the stack length. func (f *forthstack) Length() int {	return len(f.stack)} // Empty is a convenience function synonymous with Length == 0func (f *forthstack) Empty() bool {	return len(f.stack) == 0} // errRecover converts panics to errstr iff it is an os.Error, panics// otherwise. func errRecover(errp *error) {	e := recover()	if e != nil {		if _, ok := e.(runtime.Error); ok {			panic(e)		}		*errp = e.(error)	}} /* iEval takes a Forth and strings and splits the string on space * characters, pushing each element on the stack or invoking the  * operator if it is found in the opmap.  */func iEval(f Forth, s string) {	for _, val := range strings.Split(s, " ") {		/* two or more spaces can have odd results */		if len(val) == 0 || val == " " {			continue		}		fun := opmap[val]		if fun != nil {			fun(f)		} else {			f.Push(val)		}	}	return}/* Eval takes a Forth and strings and splits the string on space * characters, pushing each element on the stack or invoking the  * operator if it is found in the opmap. It returns TOS when it is done.  * it is an error to leave the stack non-Empty.  */func Eval(f Forth, s string) (ret string, err error) {	defer errRecover(&err)	iEval(f, s)	ret = f.Pop()	return } func toInt(f Forth) int {	i, err := strconv.Atoi(f.Pop())	if err != nil {		panic(err)	}	return i} func plus(f Forth) {	x := toInt(f)	y := toInt(f)	z := x + y	f.Push(strconv.Itoa(z))} func times(f Forth) {	x := toInt(f)	y := toInt(f)	z := x * y	f.Push(strconv.Itoa(z))} func sub(f Forth) {	x := toInt(f)	y := toInt(f)	z := y - x	f.Push(strconv.Itoa(z))} func div(f Forth) {	x := toInt(f)	y := toInt(f)	z := y / x	f.Push(strconv.Itoa(z))} func mod(f Forth) {	x := toInt(f)	y := toInt(f)	z := y % x	f.Push(strconv.Itoa(z))} func roundup(f Forth) {	rnd := toInt(f)	v := toInt(f)	v = ((v + rnd - 1) / rnd) * rnd	f.Push(strconv.Itoa(v))} func swap(f Forth) {	x := f.Pop()	y := f.Pop()	f.Push(x)	f.Push(y)} func strcat(f Forth) {	x := f.Pop()	y := f.Pop()	f.Push(y + x)} func dup(f Forth) {	x := f.Pop()	f.Push(x)	f.Push(x)} func ifelse(f Forth) {	x := toInt(f)	y := f.Pop()	z := f.Pop()	if x != 0 {		f.Push(y)	} else {		f.Push(z)	}} func hostname(f Forth) {	h, err := os.Hostname()	if err != nil {		panic("No hostname")	}	f.Push(h)} func hostbase(f Forth) {	host := f.Pop()	f.Push(strings.TrimLeft(host, "abcdefghijklmnopqrstuvwxyz -"))} func NewWord(f Forth, name, command string) {	newword := func(f Forth) {		iEval(f, command)		return	}	opmap[name] = newword	return}