Implemented commands until helloworld works \o/

This commit is contained in:
Julien Dessaux 2021-09-23 16:14:37 +02:00
parent f86b5724e5
commit 198efceb1f
10 changed files with 244 additions and 45 deletions

2
go.mod
View file

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

4
go.sum
View file

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

View file

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

View file

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

View file

@ -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 c == ';' {
jumpingMode = false
if p.stringMode {
if p.lastCharWasSpace {
for c == ' ' {
c = p.StepAndGet(*f)
}
continue
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 = !jumpingMode
}
}
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
}

View file

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

View file

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

View file

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

View file

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

View file

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