aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--go.mod2
-rw-r--r--go.sum4
-rw-r--r--pkg/interpreter/interpreter.go4
-rw-r--r--pkg/interpreter/interpreter_test.go2
-rw-r--r--pkg/pointer/exec.go152
-rw-r--r--pkg/pointer/intput-output.go61
-rw-r--r--pkg/pointer/pointer.go28
-rw-r--r--pkg/pointer/pointer_test.go16
-rw-r--r--pkg/pointer/stack-stack.go8
-rw-r--r--pkg/pointer/stack-stack_test.go10
10 files changed, 243 insertions, 44 deletions
diff --git a/go.mod b/go.mod
index 718a815..4dea3d5 100644
--- a/go.mod
+++ b/go.mod
@@ -6,6 +6,8 @@ require github.com/stretchr/testify v1.7.0
require (
github.com/davecgh/go-spew v1.1.1 // indirect
+ github.com/pkg/term v1.1.0 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
+ golang.org/x/sys v0.0.0-20200909081042-eff7692f9009 // indirect
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect
)
diff --git a/go.sum b/go.sum
index c221f64..edc2e45 100644
--- a/go.sum
+++ b/go.sum
@@ -1,11 +1,15 @@
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/pkg/term v1.1.0 h1:xIAAdCMh3QIAy+5FrE8Ad8XoDhEU4ufwbaSozViP9kk=
+github.com/pkg/term v1.1.0/go.mod h1:E25nymQcrSllhX42Ok8MRm1+hyBdHY0dCeiKZ9jpNGw=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
+golang.org/x/sys v0.0.0-20200909081042-eff7692f9009 h1:W0lCpv29Hv0UaM1LXb9QlBHLNP8UFfcKjblhVCWftOM=
+golang.org/x/sys v0.0.0-20200909081042-eff7692f9009/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
diff --git a/pkg/interpreter/interpreter.go b/pkg/interpreter/interpreter.go
index 76917b7..433f264 100644
--- a/pkg/interpreter/interpreter.go
+++ b/pkg/interpreter/interpreter.go
@@ -16,14 +16,14 @@ func NewInterpreter(f *field.Field, p *pointer.Pointer) *Interpreter {
func (i *Interpreter) Run() int {
for i.p != nil {
- if v := i.Step(); v != nil {
+ if v := i.step(); v != nil {
return *v
}
}
return 0
}
-func (i *Interpreter) Step() *int {
+func (i *Interpreter) step() *int {
var prev *pointer.Pointer = nil
for p := i.p; p != nil; p = p.Next {
done, v := p.Exec(i.f)
diff --git a/pkg/interpreter/interpreter_test.go b/pkg/interpreter/interpreter_test.go
index ff633a2..73902b2 100644
--- a/pkg/interpreter/interpreter_test.go
+++ b/pkg/interpreter/interpreter_test.go
@@ -38,7 +38,7 @@ func TestStep(t *testing.T) {
if tc.pointer == nil {
tc.pointer = pointer.NewPointer()
}
- NewInterpreter(f, tc.pointer).Step()
+ NewInterpreter(f, tc.pointer).step()
if tc.expectedField != nil {
require.Equal(t, tc.expectedField, f)
}
diff --git a/pkg/pointer/exec.go b/pkg/pointer/exec.go
index 9d55136..f7fcf8f 100644
--- a/pkg/pointer/exec.go
+++ b/pkg/pointer/exec.go
@@ -8,32 +8,168 @@ import (
func (p *Pointer) Exec(f *field.Field) (done bool, returnValue *int) {
c := p.Get(*f)
- for jumpingMode := false; jumpingMode || c == ' ' || c == ';'; c = p.StepAndGet(*f) {
- if jumpingMode {
+ if p.stringMode {
+ if p.lastCharWasSpace {
+ for c == ' ' {
+ c = p.StepAndGet(*f)
+ }
+ p.lastCharWasSpace = false
+ }
+ if c == '"' {
+ p.stringMode = false
+ p.lastCharWasSpace = false
+ } else {
+ if c == ' ' {
+ p.lastCharWasSpace = true
+ }
+ p.ss.head.Push(c)
+ }
+ } else {
+ for jumpingMode := false; jumpingMode || c == ' ' || c == ';'; c = p.StepAndGet(*f) {
if c == ';' {
- jumpingMode = false
+ jumpingMode = !jumpingMode
}
- continue
}
+ done, returnValue = p.eval(c, f)
}
+ p.Step(*f)
+ return
+}
+
+func (p *Pointer) eval(c int, f *field.Field) (done bool, returnValue *int) {
+ handled := true
switch c {
case '@':
return true, nil
case '#':
p.Step(*f)
case 'j':
- n := p.Ss.Pop()
+ n := p.ss.head.Pop()
for j := 0; j < n; j++ {
p.Step(*f)
}
case 'q':
- v := p.Ss.Pop()
+ v := p.ss.head.Pop()
return true, &v
+ case 'k':
+ n := p.ss.head.Pop()
+ c = p.StepAndGet(*f)
+ for jumpingMode := false; jumpingMode || c == ' ' || c == ';'; c = p.StepAndGet(*f) {
+ if c == ';' {
+ jumpingMode = !jumpingMode
+ }
+ }
+ for j := 0; j < n; j++ {
+ p.eval(c, f)
+ }
+ case '!':
+ v := p.ss.head.Pop()
+ if v == 0 {
+ v = 1
+ } else {
+ v = 0
+ }
+ p.ss.head.Push(v)
+ case '`':
+ b, a := p.ss.head.Pop(), p.ss.head.Pop()
+ if a > b {
+ a = 1
+ } else {
+ a = 0
+ }
+ p.ss.head.Push(a)
+ case '_':
+ v := p.ss.head.Pop()
+ if v == 0 {
+ p.Redirect('>')
+ } else {
+ p.Redirect('<')
+ }
+ case '|':
+ v := p.ss.head.Pop()
+ if v == 0 {
+ p.Redirect('v')
+ } else {
+ p.Redirect('^')
+ }
+ case 'w':
+ b, a := p.ss.head.Pop(), p.ss.head.Pop()
+ if a < b {
+ p.Redirect('[')
+ } else if a > b {
+ p.Redirect(']')
+ }
+ case '+':
+ b, a := p.ss.head.Pop(), p.ss.head.Pop()
+ p.ss.head.Push(a + b)
+ case '*':
+ b, a := p.ss.head.Pop(), p.ss.head.Pop()
+ p.ss.head.Push(a * b)
+ case '-':
+ b, a := p.ss.head.Pop(), p.ss.head.Pop()
+ p.ss.head.Push(a - b)
+ case '/':
+ b, a := p.ss.head.Pop(), p.ss.head.Pop()
+ if b == 0 {
+ p.ss.head.Push(0)
+ return
+ }
+ p.ss.head.Push(a / b)
+ case '%':
+ b, a := p.ss.head.Pop(), p.ss.head.Pop()
+ if b == 0 {
+ p.ss.head.Push(0)
+ return
+ }
+ p.ss.head.Push(a % b)
+ case '"':
+ p.stringMode = true
+ case '\'':
+ p.ss.head.Push(p.StepAndGet(*f))
+ case 's':
+ p.Step(*f)
+ f.Set(p.x, p.y, p.ss.head.Pop())
+ case '$':
+ p.ss.head.Pop()
+ case ':':
+ p.ss.head.Duplicate()
+ case '\\':
+ p.ss.head.Swap()
+ case 'n':
+ p.ss.head.Clear()
+ case '{':
+ p.ss.Begin(p)
+ case '}':
+ p.ss.End(p)
+ case 'u':
+ p.ss.Under()
+ case 'g':
+ y, x := p.ss.head.Pop(), p.ss.head.Pop()
+ p.ss.head.Push(f.Get(x+p.sox, y+p.soy))
+ case 'p':
+ y, x, v := p.ss.head.Pop(), p.ss.head.Pop(), p.ss.head.Pop()
+ f.Set(x+p.sox, y+p.soy, v)
+ case '.':
+ p.DecimalOutput(p.ss.head.Pop())
+ case ',':
+ p.CharacterOutput(p.ss.head.Pop())
+ case '&':
+ p.ss.head.Push(p.DecimalInput())
+ case '~':
+ p.ss.head.Push(p.CharacterInput())
default:
- if !p.Redirect(c) {
+ handled = false
+ }
+ if !handled {
+ switch {
+ case p.Redirect(c):
+ case c >= '0' && c <= '9':
+ p.ss.head.Push(c - '0')
+ case c >= 'a' && c <= 'f':
+ p.ss.head.Push(c - 'a' + 10)
+ default:
log.Fatalf("Non implemented instruction code %d : %c", c, c)
}
}
- p.Step(*f)
return
}
diff --git a/pkg/pointer/intput-output.go b/pkg/pointer/intput-output.go
new file mode 100644
index 0000000..cc7533e
--- /dev/null
+++ b/pkg/pointer/intput-output.go
@@ -0,0 +1,61 @@
+package pointer
+
+import (
+ "fmt"
+ "log"
+ "os"
+
+ "github.com/pkg/term"
+)
+
+var defaultInputLastChar *int = nil
+
+func DefaultCharacterInput() int {
+ if defaultInputLastChar != nil {
+ c := *defaultInputLastChar
+ defaultInputLastChar = nil
+ return c
+ }
+ t, err := term.Open("/dev/stdin")
+ if err != nil {
+ log.Fatalf("Could not open stdin: %+v", err)
+ }
+ defer t.Close()
+ defer t.Restore()
+ term.RawMode(t)
+ b := make([]byte, 1)
+ i, err := os.Stdin.Read(b)
+ if err != nil {
+ log.Fatalf("Error in DefaultCharacterInput { b: %c, i: %d, err: %+v }", b[0], i, err)
+ }
+ return int(b[0])
+}
+
+func DefaultDecimalInput() int {
+ var v int
+ for {
+ c := DefaultCharacterInput()
+ if c >= '0' && c <= '9' {
+ v = c - '0'
+ break
+ }
+ }
+ for {
+ c := DefaultCharacterInput()
+ if c >= '0' && c <= '9' {
+ v = v*10 + c - '0'
+ } else {
+ defaultInputLastChar = &c
+ break
+ }
+ }
+ return v
+}
+
+func DefaultCharacterOutput(c int) {
+ fmt.Printf("%c", c)
+}
+
+func DefaultDecimalOutput(c int) {
+ fmt.Printf("%d ", c)
+}
diff --git a/pkg/pointer/pointer.go b/pkg/pointer/pointer.go
index 902fe69..2eea79d 100644
--- a/pkg/pointer/pointer.go
+++ b/pkg/pointer/pointer.go
@@ -6,6 +6,9 @@ import (
"git.adyxax.org/adyxax/gofunge/pkg/field"
)
+type InputFunction func() int
+type OutputFunction func(v int)
+
type Pointer struct {
// the position
x int
@@ -16,18 +19,33 @@ type Pointer struct {
// The Storage offset
sox int
soy int
+ // The stringmode flag
+ stringMode bool
+ lastCharWasSpace bool
// The stack
- Ss *StackStack
+ ss *StackStack
// The next element for the multi-"threaded" b98 interpreter
Next *Pointer
+ // The input/output functions
+ CharacterInput InputFunction
+ DecimalInput InputFunction
+ CharacterOutput OutputFunction
+ DecimalOutput OutputFunction
}
func NewPointer() *Pointer {
- return &Pointer{dx: 1, Ss: NewStackStack()}
+ return &Pointer{
+ dx: 1,
+ ss: NewStackStack(),
+ CharacterInput: DefaultCharacterInput,
+ DecimalInput: DefaultDecimalInput,
+ CharacterOutput: DefaultCharacterOutput,
+ DecimalOutput: DefaultDecimalOutput,
+ }
}
func (p Pointer) Split() *Pointer {
- return &p // p is already a copy
+ return &p // p is already a copy TODO we need to duplicate the stack and handle the Next
}
func (p *Pointer) Step(f field.Field) {
@@ -76,8 +94,8 @@ func (p *Pointer) Redirect(c int) bool {
case 'r':
p.Reverse()
case 'x':
- dy := p.Ss.Pop()
- dx := p.Ss.Pop()
+ dy := p.ss.head.Pop()
+ dx := p.ss.head.Pop()
p.RedirectTo(dx, dy)
default:
return false
diff --git a/pkg/pointer/pointer_test.go b/pkg/pointer/pointer_test.go
index aa007fe..59c09ee 100644
--- a/pkg/pointer/pointer_test.go
+++ b/pkg/pointer/pointer_test.go
@@ -8,10 +8,6 @@ import (
"github.com/stretchr/testify/require"
)
-func TestNewPointer(t *testing.T) {
- require.Equal(t, NewPointer(), &Pointer{dx: 1, Ss: NewStackStack()})
-}
-
func TestSplit(t *testing.T) {
file, err := os.Open("../field/test_data/hello.b98")
require.NoError(t, err, "Failed to open file")
@@ -23,12 +19,11 @@ func TestSplit(t *testing.T) {
// We check that p2 is a real copy
p.Step(*f)
p2.Step(*f)
- require.Equal(t, &Pointer{x: 1, y: 0, dx: 1, Ss: NewStackStack()}, p)
- require.Equal(t, &Pointer{x: 1, y: 0, dx: 1, Ss: NewStackStack()}, p2)
+ require.Equal(t, 1, p.x)
+ require.Equal(t, 0, p.y)
}
func TestStep(t *testing.T) { // Step is thoroughly tested in the field package
- defaultPointer := NewPointer()
// File of one char
file, err := os.Open("../field/test_data/minimal.b98")
require.NoError(t, err, "Failed to open file")
@@ -37,7 +32,8 @@ func TestStep(t *testing.T) { // Step is thoroughly tested in the field package
require.NoError(t, err)
p := NewPointer()
p.Step(*f)
- require.Equal(t, defaultPointer, p)
+ require.Equal(t, 0, p.x)
+ require.Equal(t, 0, p.y)
}
func TestGet(t *testing.T) {
@@ -104,8 +100,8 @@ func TestRedirect(t *testing.T) {
t.Run(tc.name, func(t *testing.T) {
p := NewPointer()
p.RedirectTo(3, 14)
- p.Ss.Push(2)
- p.Ss.Push(7)
+ p.ss.head.Push(2)
+ p.ss.head.Push(7)
v := p.Redirect(int(tc.input))
require.Equal(t, true, v)
require.Equal(t, tc.expectedDx, p.dx, "Invalid dx value")
diff --git a/pkg/pointer/stack-stack.go b/pkg/pointer/stack-stack.go
index 6d22f51..2f57b05 100644
--- a/pkg/pointer/stack-stack.go
+++ b/pkg/pointer/stack-stack.go
@@ -85,11 +85,3 @@ func (ss *StackStack) Under() (reflect bool) {
}
return false
}
-
-func (ss StackStack) Pop() int {
- return ss.head.Pop()
-}
-
-func (ss StackStack) Push(v int) {
- ss.head.Push(v)
-}
diff --git a/pkg/pointer/stack-stack_test.go b/pkg/pointer/stack-stack_test.go
index fd4c9ea..d5809fd 100644
--- a/pkg/pointer/stack-stack_test.go
+++ b/pkg/pointer/stack-stack_test.go
@@ -248,13 +248,3 @@ func TestUnder(t *testing.T) {
require.Equal(t, expected, ss)
})
}
-
-func TestPushPop(t *testing.T) {
- ss := NewStackStack()
- ss.Push(12)
- ss.Push(5)
- v := ss.Pop()
- require.Equal(t, 5, v)
- v = ss.Pop()
- require.Equal(t, 12, v)
-}