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 I int // current parser location } // Eval evaulates a given line, recursively descending into given words as needed func (c *Context) Eval(line string) error { // state var word []byte var words []string var comment bool immediate := true for c.I = 0; c.I < len(line); c.I = c.I + 1 { switch line[c.I] { case '(', ')': // comments if len(word) == 0 { if line[c.I] == '(' { comment = true continue } comment = false continue } else { word = append(word, line[c.I]) } case ':', ';': // COMPILE/IMMEDIATE mode swapping if len(word) == 0 { if line[c.I] == ':' { immediate = false } else if line[c.I-1] == ' ' && line[c.I] == ';' { c.Dictionary.AddWord(words[0], nil, words[1:]) word = []byte{} immediate = true } } else { word = append(word, line[c.I]) } case ' ': if !immediate { // continue building our subroutine if we're not in immediate mode... if len(word) != 0 { // don't add empty words to our list words = append(words, strings.TrimSpace(string(word))) word = []byte{} } continue } if len(word) == 0 || comment { // empty space, just continue... continue } int, err := strconv.Atoi(string(word)) 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(string(word)) if err != nil { return fmt.Errorf("could not parse %s; %v", w.Name, err) } if w.Impl != nil { // we have an implementation for that word. Run it. err := w.Impl() if err != nil { return err } word = []byte{} } else if len(w.Source) != 0 { // user-defined word; let's descend... c.RStack.Push(c.I) err := c.Eval(strings.Join(w.Source, " ") + " ") if err != nil { return err } c.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[c.I]) } } } return nil }