package main import ( "fmt" "strconv" "strings" ) // Context is a set of Dictionary + Stacks representing a runtime environment type Context struct { Dictionary Dictionary Stack *Stack RStack *Stack Immediate bool Words []string } // Eval evaulates a given line, recursively descending into given words as needed func (c *Context) Eval(line string) error { // state var word []byte var comment bool for i := 0; i < len(line); i = i + 1 { switch line[i] { case '(', ')': // comments if len(word) == 0 { if line[i] == '(' { comment = true continue } comment = false continue } else { word = append(word, line[i]) } case ' ': sword := strings.TrimSpace(string(word)) if !c.Immediate { // continue building our subroutine if we're not in immediate mode... if len(word) != 0 { // don't add empty words to our list // Was that a word we know? w, _ := c.Dictionary.GetWord(sword) // check if it's an IMMEDIATE mode toggle word c.Immediate = w.Immediate if !c.Immediate { c.Words = append(c.Words, sword) word = []byte{} continue } } } if len(word) == 0 || comment { // empty space, just continue... continue } int, err := strconv.Atoi(sword) if err == nil { // it was a number! put it on the stack. c.Stack.Push(int) word = []byte{} continue } // it wasn't a number. Is it a word we know? w, err := c.Dictionary.GetWord(sword) if err != nil { return fmt.Errorf("could not parse %s; %v", w.Name, err) } // run word c.RStack.Push(i) if err = c.Exec(w); err != nil { return err } i, err = c.RStack.Pop() if err != nil { return fmt.Errorf("error while popping from return stack: %v", err) } word = []byte{} default: if !comment { word = append(word, line[i]) } } } return nil } func (c *Context) Exec(w Word) error { if w.Impl != nil { // we have an implementation for that word. Run it. err := w.Impl() if err != nil { return err } } else if len(w.Source) != 0 { // user-defined word; let's descend... err := c.Eval(strings.Join(w.Source, " ") + " ") if err != nil { return err } } return nil }