diff --git a/builtins.go b/builtins.go index 055ab49..1b685b2 100644 --- a/builtins.go +++ b/builtins.go @@ -286,7 +286,7 @@ func (b *Builtins) Rot(s *Stack) func() error { func (b *Builtins) Words(d Dictionary) func() error { return func() error { for n := range d { - fmt.Printf("%s ", n) + fmt.Printf("%s %s\n", n, d[n].Source) } return nil } @@ -341,6 +341,41 @@ func (b *Builtins) RFetch(s *Stack, r *Stack) func() error { } } +// If checks if NOS != 0 and conditionally executes any following statements if so +func (b *Builtins) If(s *Stack, r *Stack) func() error { + return func() error { + return nil + } +} + +// Else executes a separate piece of code if NOS == 0 after an if check +func (b *Builtins) Else(s *Stack, r *Stack) func() error { + return func() error { + return nil + } +} + +// Then ends an If/Else block +func (b *Builtins) Then(s *Stack, r *Stack) func() error { + return func() error { + return nil + } +} + +// Do sets up a loop +func (b *Builtins) Do(s *Stack, r *Stack) func() error { + return func() error { + return nil + } +} + +// Loop closes a loop +func (b *Builtins) Loop(s *Stack, r *Stack) func() error { + return func() error { + return nil + } +} + // CR prints a newline to standard out func (b *Builtins) CR() func() error { return func() error { diff --git a/eval.go b/eval.go index 915a9f9..4dc5f75 100644 --- a/eval.go +++ b/eval.go @@ -6,57 +6,60 @@ import ( "strings" ) -// Context is a set of Dictionary + Stack representing a runtime environment +// 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 i := 0; i < len(line); i = i + 1 { - switch line[i] { + 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[i] == '(' { + if line[c.I] == '(' { comment = true continue } comment = false continue } else { - word = append(word, line[i]) + word = append(word, line[c.I]) } case ':', ';': // COMPILE/IMMEDIATE mode swapping if len(word) == 0 { - if line[i] == ':' { + if line[c.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]+" ") + } 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[i]) } + } else { + word = append(word, line[c.I]) } case ' ': if !immediate { // continue building our subroutine if we're not in immediate mode... - word = append(word, line[i]) + 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. @@ -64,6 +67,7 @@ func (c *Context) Eval(line string) error { word = []byte{} continue } + // it wasn't a number. Is it a word we know? w, err := c.Dictionary.GetWord(string(word)) if err != nil { @@ -76,17 +80,22 @@ func (c *Context) Eval(line string) error { return err } word = []byte{} - } else if w.Source != "" { + } else if len(w.Source) != 0 { // user-defined word; let's descend... - err := c.Eval(w.Source) + 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[i]) + word = append(word, line[c.I]) } } } diff --git a/main.go b/main.go index ba5df59..9ac3d19 100644 --- a/main.go +++ b/main.go @@ -14,31 +14,33 @@ func main() { dict := Dictionary{} b := &Builtins{} - dict.AddWord("+", b.Add(&stack), "") - dict.AddWord("-", b.Sub(&stack), "") - dict.AddWord("*", b.Mul(&stack), "") - dict.AddWord("/", b.Div(&stack), "") - dict.AddWord(".", b.Print(&stack), "") - dict.AddWord("=", b.Eq(&stack), "") - dict.AddWord("0=", nil, "0 = ") - dict.AddWord("<>", b.NEq(&stack), "") - dict.AddWord(">", b.Gt(&stack), "") - dict.AddWord("<", b.Lt(&stack), "") - dict.AddWord(">=", b.GtEq(&stack), "") - dict.AddWord("<=", b.LtEq(&stack), "") - dict.AddWord("DUP", b.Dup(&stack), "") - dict.AddWord("SWAP", b.Swap(&stack), "") - dict.AddWord("OVER", b.Over(&stack), "") - dict.AddWord("DROP", b.Drop(&stack), "") - dict.AddWord("ROT", b.Rot(&stack), "") - dict.AddWord("WORDS", b.Words(dict), "") - dict.AddWord(".S", b.Debug(&stack), "") - dict.AddWord("EMIT", b.Emit(&stack), "") - dict.AddWord("R>", b.RFrom(&stack, &rstack), "") - dict.AddWord(">R", b.ToR(&stack, &rstack), "") - dict.AddWord("R@", b.RFetch(&stack, &rstack), "") - dict.AddWord("CR", b.CR(), "") - dict.AddWord("QUIT", b.Quit(), "") + dict.AddWord("+", b.Add(&stack), nil) + dict.AddWord("-", b.Sub(&stack), nil) + dict.AddWord("*", b.Mul(&stack), nil) + dict.AddWord("/", b.Div(&stack), nil) + dict.AddWord(".", b.Print(&stack), nil) + dict.AddWord("=", b.Eq(&stack), nil) + dict.AddWord("0=", nil, []string{"0", "="}) + dict.AddWord("<>", b.NEq(&stack), nil) + dict.AddWord(">", b.Gt(&stack), nil) + dict.AddWord("<", b.Lt(&stack), nil) + dict.AddWord(">=", b.GtEq(&stack), nil) + dict.AddWord("<=", b.LtEq(&stack), nil) + dict.AddWord("0<", nil, []string{"0", "<"}) + dict.AddWord("0>", nil, []string{"0", ">"}) + dict.AddWord("DUP", b.Dup(&stack), nil) + dict.AddWord("SWAP", b.Swap(&stack), nil) + dict.AddWord("OVER", b.Over(&stack), nil) + dict.AddWord("DROP", b.Drop(&stack), nil) + dict.AddWord("ROT", b.Rot(&stack), nil) + dict.AddWord("WORDS", b.Words(dict), nil) + dict.AddWord(".S", b.Debug(&stack), nil) + dict.AddWord("EMIT", b.Emit(&stack), nil) + dict.AddWord("R>", b.RFrom(&stack, &rstack), nil) + dict.AddWord(">R", b.ToR(&stack, &rstack), nil) + dict.AddWord("R@", b.RFetch(&stack, &rstack), nil) + dict.AddWord("CR", b.CR(), nil) + dict.AddWord("QUIT", b.Quit(), nil) c := Context{ Dictionary: dict, @@ -64,8 +66,9 @@ func main() { fmt.Printf("bye\n") os.Exit(0) } else if err != nil { - fmt.Printf("error in evaluation: %v\n", err) + fmt.Printf("error in evaluation: %v", err) + } else { + fmt.Print("ok") } - fmt.Print("ok") } } diff --git a/words.go b/words.go index d5fc43c..c42f8ea 100644 --- a/words.go +++ b/words.go @@ -9,11 +9,11 @@ type Dictionary map[string]Word type Word struct { Name string Impl func() error - Source string + Source []string } // AddWord inserts a new word into the dictonary, optionally overwriting the existing word -func (d Dictionary) AddWord(name string, impl func() error, source string) { +func (d Dictionary) AddWord(name string, impl func() error, source []string) { d[name] = Word{ Name: name, Impl: impl, diff --git a/words_test.go b/words_test.go index 0ac07a0..a9e468c 100644 --- a/words_test.go +++ b/words_test.go @@ -5,7 +5,7 @@ import "testing" func TestDictionary(t *testing.T) { d := Dictionary{} - d.AddWord("INC", nil, "1 + ") + d.AddWord("INC", nil, []string{"1", "+"}) w, err := d.GetWord("INC") if err != nil { t.Fail() @@ -16,7 +16,7 @@ func TestDictionary(t *testing.T) { if w.Impl != nil { t.Fail() } - if w.Source != "1 +" { + if len(w.Source) != 2 || w.Source[0] != "1" || w.Source[1] != "+" { t.Fail() } }