package main import ( "fmt" "strconv" "strings" ) // Context is a set of Dictionary + Stack representing a runtime environment type Context struct { Dictionary Dictionary Stack *Stack } // 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 immediate := true 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 ':', ';': // COMPILE/IMMEDIATE mode swapping if len(word) == 0 { if line[i] == ':' { immediate = false } } else { if line[i-1] == ' ' && line[i] == ';' { def := strings.SplitN(strings.TrimSpace(string(word)), " ", 2) c.Dictionary.AddWord(def[0], nil, def[1]+" ") word = []byte{} immediate = true } else { word = append(word, line[i]) } } case ' ': if !immediate { // continue building our subroutine if we're not in immediate mode... word = append(word, line[i]) 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 w.Source != "" { // user-defined word; let's descend... err := c.Eval(w.Source) if err != nil { return err } word = []byte{} } default: if !comment { word = append(word, line[i]) } } } return nil }