tests, abstracting output for PRINT and EMIT

This commit is contained in:
David 2021-02-20 12:09:04 -05:00
parent 1a229d5ddb
commit 2a1a6fc0d2
5 changed files with 356 additions and 14 deletions

View File

@ -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
}
}

331
builtins_test.go Normal file
View File

@ -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()
}
}

11
eval.go
View File

@ -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.

View File

@ -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
}

View File

@ -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)