tests, abstracting output for PRINT and EMIT
This commit is contained in:
parent
1a229d5ddb
commit
2a1a6fc0d2
22
builtins.go
22
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
|
||||
}
|
||||
}
|
||||
|
331
builtins_test.go
Normal file
331
builtins_test.go
Normal 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
11
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.
|
||||
|
2
flags.go
2
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
|
||||
}
|
||||
|
4
main.go
4
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)
|
||||
|
Loading…
Reference in New Issue
Block a user