From 2a1a6fc0d237f468929905840192eb5e905d9d10 Mon Sep 17 00:00:00 2001 From: David Ashby Date: Sat, 20 Feb 2021 12:09:04 -0500 Subject: [PATCH] tests, abstracting output for PRINT and EMIT --- builtins.go | 22 +++- builtins_test.go | 331 +++++++++++++++++++++++++++++++++++++++++++++++ eval.go | 11 +- flags.go | 2 + main.go | 4 +- 5 files changed, 356 insertions(+), 14 deletions(-) create mode 100644 builtins_test.go diff --git a/builtins.go b/builtins.go index e45e52c..911ad24 100644 --- a/builtins.go +++ b/builtins.go @@ -1,6 +1,10 @@ package main -import "fmt" +import ( + "fmt" + "io" + "os" +) // Builtins is a handy holder for our various default words type Builtins struct{} @@ -228,14 +232,17 @@ func (b *Builtins) Div(s *Stack) func() error { } } -// Print pops the stack and outputs it to stdout -func (b *Builtins) Print(s *Stack) func() error { +// Print pops the stack and outputs it to the writer with a trailing space (defaults to stdout) +func (b *Builtins) Print(out io.Writer, s *Stack) func() error { + if out == nil { + out = os.Stdout + } return func() error { tos, err := s.Pop() if err != nil { return err } - fmt.Print(tos, " ") + fmt.Fprint(out, tos, " ") return nil } } @@ -333,13 +340,16 @@ func (b *Builtins) Flags(c Context) func() error { } // Emit outputs the UTF-8 rune for the int on the top of the stack -func (b *Builtins) Emit(s *Stack) func() error { +func (b *Builtins) Emit(out io.Writer, s *Stack) func() error { + if out == nil { + out = os.Stdout + } return func() error { tos, err := s.Pop() if err != nil { return err } - fmt.Print(string(rune(tos)) + " ") + fmt.Fprint(out, string(rune(tos))+" ") return nil } } diff --git a/builtins_test.go b/builtins_test.go new file mode 100644 index 0000000..c91b565 --- /dev/null +++ b/builtins_test.go @@ -0,0 +1,331 @@ +package main + +import ( + "bytes" + "testing" +) + +func TestAdd(t *testing.T) { + s := Stack{ + values: []int{1, 2}, + } + b := Builtins{} + err := b.Add(&s)() + if err != nil { + t.Fail() + } + if len(s.values) != 1 { + t.Fail() + } + if s.values[0] != 3 { + t.Fail() + } +} + +func TestSub(t *testing.T) { + s := Stack{ + values: []int{1, 2}, + } + b := Builtins{} + err := b.Sub(&s)() + if err != nil { + t.Fail() + } + if len(s.values) != 1 { + t.Fail() + } + if s.values[0] != 1 { + t.Fail() + } +} + +func TestMul(t *testing.T) { + b := Builtins{} + s := Stack{ + values: []int{4, 2}, + } + err := b.Mul(&s)() + if err != nil { + t.Fail() + } + if len(s.values) != 1 { + t.Fail() + } + if s.values[0] != 8 { + t.Fail() + } + + s = Stack{ + values: []int{-4, 2}, + } + err = b.Mul(&s)() + if err != nil { + t.Fail() + } + if len(s.values) != 1 { + t.Fail() + } + if s.values[0] != -8 { + t.Fail() + } +} + +func TestDiv(t *testing.T) { + b := Builtins{} + s := Stack{ + values: []int{2, 4}, + } + err := b.Div(&s)() + if err != nil { + t.Log(err) + t.Fail() + } + if len(s.values) != 1 { + t.Log(s.values) + t.Fail() + } + if s.values[0] != 2 { + t.Log(s.values[0]) + t.Fail() + } + + s = Stack{ + values: []int{2, -4}, + } + err = b.Div(&s)() + if err != nil { + t.Log(err) + t.Fail() + } + if len(s.values) != 1 { + t.Log(s.values) + t.Fail() + } + if s.values[0] != -2 { + t.Log(s.values[0]) + t.Fail() + } +} + +func TestPrint(t *testing.T) { + b := Builtins{} + s := Stack{ + values: []int{200}, + } + out := new(bytes.Buffer) + err := b.Print(out, &s)() + if err != nil { + t.Log(err) + t.Fail() + } + if len(s.values) != 0 { + t.Log(s.values) + t.Fail() + } + if string(out.Bytes()) != "200 " { + t.Log(string(out.Bytes()), out.Bytes()) + t.Fail() + } +} + +func TestDup(t *testing.T) { + b := Builtins{} + s := Stack{ + values: []int{200}, + } + err := b.Dup(&s)() + if err != nil { + t.Log(err) + t.Fail() + } + if len(s.values) != 2 { + t.Log(s.values) + t.Fail() + } + if s.values[0] != s.values[1] { + t.Log(s.values) + t.Fail() + } +} + +func TestSwap(t *testing.T) { + b := Builtins{} + s := Stack{ + values: []int{200, 100}, + } + err := b.Swap(&s)() + if err != nil { + t.Log(err) + t.Fail() + } + if len(s.values) != 2 { + t.Log(s.values) + t.Fail() + } + if s.values[0] != 100 || s.values[1] != 200 { + t.Log(s.values) + t.Fail() + } +} + +func TestOver(t *testing.T) { + b := Builtins{} + s := Stack{ + values: []int{200, 100}, + } + err := b.Over(&s)() + if err != nil { + t.Log(err) + t.Fail() + } + if len(s.values) != 3 { + t.Log(s.values) + t.Fail() + } + if s.values[0] != 100 || s.values[1] != 200 || s.values[2] != 100 { + t.Log(s.values) + t.Fail() + } +} + +func TestDrop(t *testing.T) { + b := Builtins{} + s := Stack{ + values: []int{200, 100}, + } + err := b.Drop(&s)() + if err != nil { + t.Log(err) + t.Fail() + } + if len(s.values) != 1 { + t.Log(s.values) + t.Fail() + } + if s.values[0] != 100 { + t.Log(s.values) + t.Fail() + } +} + +func TestRot(t *testing.T) { + b := Builtins{} + s := Stack{ + values: []int{100, 200, 300}, + } + err := b.Rot(&s)() + if err != nil { + t.Log(err) + t.Fail() + } + if len(s.values) != 3 { + t.Log(s.values) + t.Fail() + } + if s.values[0] != 300 || s.values[1] != 100 || s.values[2] != 200 { + t.Log(s.values) + t.Fail() + } +} + +func TestEmit(t *testing.T) { + b := Builtins{} + s := Stack{ + values: []int{42}, + } + out := new(bytes.Buffer) + err := b.Emit(out, &s)() + if err != nil { + t.Log(err) + t.Fail() + } + if len(s.values) != 0 { + t.Log(s.values) + t.Fail() + } + if string(out.Bytes()) != "* " { + t.Log(string(out.Bytes()), out.Bytes()) + t.Fail() + } +} + +func TestToR(t *testing.T) { + b := Builtins{} + s := Stack{ + values: []int{200, 100}, + } + r := Stack{ + values: []int{}, + } + err := b.ToR(&s, &r)() + if err != nil { + t.Log(err) + t.Fail() + } + if len(s.values) != 1 { + t.Log(s.values) + t.Fail() + } + if len(r.values) != 1 { + t.Log(r.values) + t.Fail() + } + if s.values[0] != 100 || r.values[0] != 200 { + t.Log(s.values, r.values) + t.Fail() + } +} + +func TestRFrom(t *testing.T) { + b := Builtins{} + s := Stack{ + values: []int{}, + } + r := Stack{ + values: []int{200, 100}, + } + err := b.RFrom(&s, &r)() + if err != nil { + t.Log(err) + t.Fail() + } + if len(s.values) != 1 { + t.Log(s.values) + t.Fail() + } + if len(r.values) != 1 { + t.Log(r.values) + t.Fail() + } + if s.values[0] != 200 || r.values[0] != 100 { + t.Log(s.values, r.values) + t.Fail() + } +} + +func TestRFetch(t *testing.T) { + b := Builtins{} + s := Stack{ + values: []int{}, + } + r := Stack{ + values: []int{200, 100}, + } + err := b.RFetch(&s, &r)() + if err != nil { + t.Log(err) + t.Fail() + } + if len(s.values) != 1 { + t.Log(s.values) + t.Fail() + } + if len(r.values) != 2 { + t.Log(r.values) + t.Fail() + } + if s.values[0] != 200 || r.values[0] != 200 || r.values[1] != 100 { + t.Log(s.values, r.values) + t.Fail() + } +} diff --git a/eval.go b/eval.go index bdc4437..05ff865 100644 --- a/eval.go +++ b/eval.go @@ -35,12 +35,10 @@ func (c *Context) Eval(line string) error { c.Flags.SetFlag("Immediate", w.Immediate) } - if !c.Flags.GetFlag("Immediate") && !c.Flags.GetFlag("Comment") { - c.Words = append(c.Words, sword) - word = []byte{} - continue - } - if !c.Flags.GetFlag("Immediate") && c.Flags.GetFlag("Comment") { + if !c.Flags.GetFlag("Immediate") { + if !c.Flags.GetFlag("Comment") { + c.Words = append(c.Words, sword) + } word = []byte{} continue } @@ -76,6 +74,7 @@ func (c *Context) Eval(line string) error { return nil } +// Exec wraps the branched execution of words (either built-in or user-defined) func (c *Context) Exec(w Word) error { if w.Impl != nil { // we have an implementation for that word. Run it. diff --git a/flags.go b/flags.go index 44501d5..b4f88d1 100644 --- a/flags.go +++ b/flags.go @@ -3,10 +3,12 @@ package main // Flags is a simple map of strings to boolean flag fields type Flags map[string]bool +// GetFlag returns a boolean for the given flag func (f Flags) GetFlag(s string) bool { return f[s] } +// SetFlag overwrites the existing boolean for the flag func (f Flags) SetFlag(s string, b bool) { f[s] = b } diff --git a/main.go b/main.go index 779aae5..339d35b 100644 --- a/main.go +++ b/main.go @@ -38,8 +38,8 @@ func main() { dict.AddWord("*", b.Mul(&stack), nil, false) dict.AddWord("/", b.Div(&stack), nil, false) // output - dict.AddWord(".", b.Print(&stack), nil, false) - dict.AddWord("EMIT", b.Emit(&stack), nil, false) + dict.AddWord(".", b.Print(os.Stdout, &stack), nil, false) + dict.AddWord("EMIT", b.Emit(os.Stdout, &stack), nil, false) dict.AddWord("CR", nil, []string{"10", "EMIT"}, false) // emit a newline // logic dict.AddWord("=", b.Eq(&stack), nil, false)