Compare commits

...

7 Commits
v1.0.1 ... main

Author SHA1 Message Date
Sasha Koshka c7d948cdcd Added some helpful comments to the creature command 2022-09-08 18:46:59 -04:00
Sasha Koshka dcd8c56c0d Added basic implementation that executes text files 2022-09-08 16:02:36 -04:00
Sasha Koshka a52e5d3426 Ran gofmt 2022-09-08 14:43:19 -04:00
Sasha Koshka b0068336cd Programs are now loaded with a function and not a member var 2022-09-08 14:41:50 -04:00
Sasha Koshka 3be08736b4 Downgraded to go 1.18 so it works on my laptop 2022-09-08 14:36:28 -04:00
Sasha Koshka cfae69c0c2 Updated readme accordingly 2022-08-29 20:20:46 -04:00
Sasha Koshka d7d5be6949 Reversed order of comparison and arithmetic operations 2022-08-29 20:15:46 -04:00
10 changed files with 264 additions and 134 deletions

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
/cmd/creature/creature

View File

@ -16,6 +16,11 @@ Each documented instruction has a list of what is pushed, and what is popped.
the items are listed in chronological order, from first pushed/popped to last the items are listed in chronological order, from first pushed/popped to last
pushed/popped. pushed/popped.
You'll notice that words specifying an address are always at the top of the
stack. This helps increse security and reduce the likelihood of bugs. When
writing functions to extend creature, it is highly advised that you follow this
principle when deciding on an order to pop arguments off of the stack in.
### 0x0 PUSH ### 0x0 PUSH
Pushes the next word in program memory onto the stack. Pushes the next word in program memory onto the stack.
@ -53,8 +58,8 @@ Pops:
Adds two words. Tip: this can be used as a logical OR. Adds two words. Tip: this can be used as a logical OR.
Pops: Pops:
1. left operand 1. right operand
2. right operand 2. left operand
Pushes: Pushes:
1. left operand + right operand 1. left operand + right operand
@ -64,8 +69,8 @@ Pushes:
Subtracts two words. Subtracts two words.
Pops: Pops:
1. left operand 1. right operand
2. right operand 2. left operand
Pushes: Pushes:
1. left operand - right operand 1. left operand - right operand
@ -75,8 +80,8 @@ Pushes:
Multiplies two words. Multiplies two words.
Pops: Pops:
1. left operand 1. right operand
2. right operand 2. left operand
Pushes: Pushes:
1. left operand * right operand 1. left operand * right operand
@ -86,8 +91,8 @@ Pushes:
Divides two words. Divides two words.
Pops: Pops:
1. left operand 1. right operand
2. right operand 2. left operand
Pushes: Pushes:
1. left operand / right operand 1. left operand / right operand
@ -97,8 +102,8 @@ Pushes:
Checks if two words are equal. Pushes 1 if true, zero if false. Checks if two words are equal. Pushes 1 if true, zero if false.
Pops: Pops:
1. left operand 1. right operand
2. right operand 2. left operand
Pushes: Pushes:
1. left operand == right operand 1. left operand == right operand
@ -108,8 +113,8 @@ Pushes:
Checks if a word is greater than another word. Pushes 1 if true, zero if false. Checks if a word is greater than another word. Pushes 1 if true, zero if false.
Pops: Pops:
1. left operand 1. right operand
2. right operand 2. left operand
Pushes: Pushes:
1. left operand > right operand 1. left operand > right operand
@ -119,8 +124,8 @@ Pushes:
Checks if a word is less than another word. Pushes 1 if true, zero if false. Checks if a word is less than another word. Pushes 1 if true, zero if false.
Pops: Pops:
1. left operand 1. right operand
2. right operand 2. left operand
Pushes: Pushes:
1. left operand < right operand 1. left operand < right operand
@ -130,8 +135,8 @@ Pushes:
Checks if two words are *not* equal. Pushes 1 if true, zero if false. Checks if two words are *not* equal. Pushes 1 if true, zero if false.
Pops: Pops:
1. left operand 1. right operand
2. right operand 2. left operand
Pushes: Pushes:
1. left operand != right operand 1. left operand != right operand
@ -142,8 +147,8 @@ Performs a modulo operation on two words. Tip: this can be used as a logical
AND. AND.
Pops: Pops:
1. left operand 1. right operand
2. right operand 2. left operand
Pushes: Pushes:
1. left operand % right operand 1. left operand % right operand

View File

@ -0,0 +1,10 @@
---
0 0
F
0 1
F
0 1
0 0
E

Binary file not shown.

112
cmd/creature/main.go Normal file
View File

@ -0,0 +1,112 @@
package main
import "io"
import "os"
import "fmt"
import "bufio"
import "strconv"
import cre "git.tebibyte.media/sashakoshka/creature"
func main() {
if len(os.Args) != 2 {
logLine("ERR file unspecified")
os.Exit(1)
}
file, err := os.Open(os.Args[1])
if err != nil {
logLine("ERR could not open file: " + err.Error())
os.Exit(1)
}
program, block, err := readFile(file)
if err != nil {
logLine("ERR could not read file: " + err.Error())
os.Exit(1)
}
machine := cre.Machine[int]{}
machine.LoadProgram(program)
machine.LoadMemory(block)
machine.Register(0, read)
machine.Register(1, write)
err = machine.Execute(0)
if err != nil {
logLine("XXX machine failed: " + err.Error())
os.Exit(1)
}
}
// read implements a basic input function for the creature. It waits until it
// has recieved one byte of user input, and then pushes that byte onto the
// stack.
func read(machine *cre.Machine[int]) (stop bool) {
ch := []byte{0}
os.Stdin.Read(ch)
machine.Push(int(ch[0]))
return
}
// write implements a basic output function for the creature. It pops one byte
// off of the stack, and writes it to stdout.
func write(machine *cre.Machine[int]) (stop bool) {
print(string(rune(machine.Pop())))
return
}
// logLine prints a message to stderr.
func logLine(message ...any) {
fmt.Fprintln(os.Stderr, message...)
}
// readFile reads data from an io.Reader into a program slice and a block slice.
// Data in the file is represented by signed hexidecimal numbers, separated by
// whitespace. Three dashes (---) divide the block data from the program data.
// See examples/echo.
func readFile(reader io.Reader) (program []int, block []int, err error) {
scanner := bufio.NewScanner(reader)
scanner.Split(bufio.ScanWords)
blockLen := 0
block = make([]int, 8)
for scanner.Scan() {
if scanner.Text() == "---" {
break
}
if blockLen >= len(block) {
newSlice := make([]int, len(block)*2)
copy(newSlice, block)
block = newSlice
}
var number int64
number, err = strconv.ParseInt(scanner.Text(), 16, 64)
if err != nil {
return
}
block[blockLen] = int(number)
blockLen++
}
block = block[:blockLen]
programLen := 0
program = make([]int, 8)
for scanner.Scan() {
if programLen >= len(program) {
newSlice := make([]int, len(program)*2)
copy(newSlice, program)
program = newSlice
}
var number int64
number, err = strconv.ParseInt(scanner.Text(), 16, 64)
if err != nil {
return
}
program[programLen] = int(number)
programLen++
}
program = program[:programLen]
return
}

View File

@ -2,19 +2,16 @@ package creature
// Word is a type constraint defining possible word types for the Machine. // Word is a type constraint defining possible word types for the Machine.
type Word interface { type Word interface {
int8 | int16 | int32 | int64 | int8 | int16 | int32 | int64 |
uint8 | uint16 | uint32 | uint64 | uint8 | uint16 | uint32 | uint64 |
int | uint int | uint
} }
// Machine is a stack machine. It contains an array of integers as its program // Machine is a stack machine. It contains an array of integers as its program
// data, and provides methods to run this program data, as well as interact with // data, and provides methods to run this program data, as well as interact with
// it. // it.
type Machine [WORD Word] struct { type Machine[WORD Word] struct {
// Program is not modified by the machine, and can be freely set before program []WORD
// the machine is started.
Program []WORD
stack []WORD stack []WORD
block []WORD block []WORD
counter WORD counter WORD
@ -25,7 +22,7 @@ type Machine [WORD Word] struct {
// MachineFunction is a function that can extend the functionality of the stack // MachineFunction is a function that can extend the functionality of the stack
// machine. It is passed a pointer to the machine that is calling it, and the // machine. It is passed a pointer to the machine that is calling it, and the
// machine will halt execution if true is returned. // machine will halt execution if true is returned.
type MachineFunction [WORD Word] func(machine *Machine[WORD]) (stop bool) type MachineFunction[WORD Word] func(machine *Machine[WORD]) (stop bool)
// All supported opcodes // All supported opcodes
const ( const (
@ -87,45 +84,41 @@ func (machine *Machine[WORD]) Reset() {
func (machine *Machine[WORD]) Execute(offset WORD) (err error) { func (machine *Machine[WORD]) Execute(offset WORD) (err error) {
machine.counter = offset machine.counter = offset
for int(machine.counter) < len(machine.Program) { for int(machine.counter) < len(machine.program) {
switch machine.instruction() { switch machine.instruction() {
case PUSH: case PUSH:
// push the next word in program memory onto the stack
machine.counter++ machine.counter++
machine.Push(machine.instruction()) machine.Push(machine.instruction())
case POP: case POP:
// pop the top word off of the stack, and discard it
machine.Pop() machine.Pop()
case PEEK: case PEEK:
// push the word at an address onto the stack
machine.Push(machine.Peek(machine.Pop())) machine.Push(machine.Peek(machine.Pop()))
case POKE: case POKE:
// store a word at an address
address := machine.Pop() address := machine.Pop()
word := machine.Pop() word := machine.Pop()
machine.Poke(address, word) machine.Poke(address, word)
case ADD: case ADD:
// adds the last two words on the stack right := machine.Pop()
machine.Push(machine.Pop() + machine.Pop()) left := machine.Pop()
machine.Push(left + right)
case SUB: case SUB:
// subtracts the second to last word on the stack from right := machine.Pop()
// the last word on the stack left := machine.Pop()
machine.Push(machine.Pop() - machine.Pop()) machine.Push(left - right)
case MUL: case MUL:
// multiplies the last two words on the stack right := machine.Pop()
machine.Push(machine.Pop() * machine.Pop()) left := machine.Pop()
machine.Push(left * right)
case DIV: case DIV:
// divides the last word on the stack by the second to
// last word on the stack
left := machine.Pop()
right := machine.Pop() right := machine.Pop()
left := machine.Pop()
if right == 0 { if right == 0 {
err = ErrorDivideByZero err = ErrorDivideByZero
return return
@ -133,62 +126,56 @@ func (machine *Machine[WORD]) Execute(offset WORD) (err error) {
machine.Push(left / right) machine.Push(left / right)
case EQ: case EQ:
// checks if the last two words on the stack are equal right := machine.Pop()
left := machine.Pop()
equal := 0 equal := 0
if machine.Pop() == machine.Pop() { if left == right {
equal = 1 equal = 1
} }
machine.Push(WORD(equal)) machine.Push(WORD(equal))
case GT: case GT:
// checks if the last word on the stack is greater than right := machine.Pop()
// the second to last word on the stack left := machine.Pop()
greater := 0 greater := 0
if machine.Pop() > machine.Pop() { if left > right {
greater = 1 greater = 1
} }
machine.Push(WORD(greater)) machine.Push(WORD(greater))
case LT: case LT:
// checks if the last word on the stack is less than the right := machine.Pop()
// second to last word on the stack left := machine.Pop()
less := 0 less := 0
if machine.Pop() < machine.Pop() { if left < right {
less = 1 less = 1
} }
machine.Push(WORD(less)) machine.Push(WORD(less))
case NEQ: case NEQ:
// checks if the last two words on the stack are not right := machine.Pop()
// equal left := machine.Pop()
notEqual := 0 notEqual := 0
if machine.Pop() != machine.Pop() { if left != right {
notEqual = 1 notEqual = 1
} }
machine.Push(WORD(notEqual)) machine.Push(WORD(notEqual))
case MOD: case MOD:
// performs a modulo operation of the second to last right := machine.Pop()
// word on the stack to the last word on the stack left := machine.Pop()
machine.Push(machine.Pop() % machine.Pop()) machine.Push(left % right)
case HALT: case HALT:
// stops execution
return return
case JMP: case JMP:
// jump to the address specified by the last word on the
// stack if the second to last word on the stack is
// nonzero
jumpTo := machine.Pop() jumpTo := machine.Pop()
if machine.Pop() != 0 { if machine.Pop() != 0 {
machine.counter = jumpTo - 1 machine.counter = jumpTo - 1
} }
case CAL: case CAL:
// call an implementation-defined subroutine, with the
// id specified by the last word on the stack. this may
// push and pop various things from the stack
id := machine.Pop() id := machine.Pop()
if machine.functions == nil { if machine.functions == nil {
break break
@ -212,7 +199,7 @@ func (machine *Machine[WORD]) Execute(offset WORD) (err error) {
// Instruction returns the current instruction in program memory. // Instruction returns the current instruction in program memory.
func (machine *Machine[WORD]) instruction() (instruction WORD) { func (machine *Machine[WORD]) instruction() (instruction WORD) {
instruction = machine.Program[machine.counter] instruction = machine.program[machine.counter]
return return
} }
@ -241,7 +228,7 @@ func (machine *Machine[WORD]) Pop() (word WORD) {
if int(machine.pointer) <= 0 || int(machine.pointer) >= len(machine.stack) { if int(machine.pointer) <= 0 || int(machine.pointer) >= len(machine.stack) {
return return
} }
word = machine.stack[machine.pointer] word = machine.stack[machine.pointer]
machine.pointer-- machine.pointer--
@ -272,7 +259,7 @@ func (machine *Machine[WORD]) Poke(address WORD, word WORD) {
// Register registers a function at the specified ID. If there is already a // Register registers a function at the specified ID. If there is already a
// function registered at that ID, this method will return an error. // function registered at that ID, this method will return an error.
func (machine *Machine[WORD]) Register ( func (machine *Machine[WORD]) Register(
id WORD, id WORD,
function MachineFunction[WORD], function MachineFunction[WORD],
) ( ) (
@ -301,6 +288,12 @@ func (machine *Machine[WORD]) Unregister(id WORD) {
delete(machine.functions, id) delete(machine.functions, id)
} }
// LoadProgram loads the contents of program into the machine's program memory.
func (machine *Machine[WORD]) LoadProgram(program []WORD) {
machine.program = make([]WORD, len(program))
copy(machine.program, program)
}
// LoadMemory loads the contents of block into the machine's memory. // LoadMemory loads the contents of block into the machine's memory.
func (machine *Machine[WORD]) LoadMemory(block []WORD) { func (machine *Machine[WORD]) LoadMemory(block []WORD) {
machine.block = make([]WORD, len(block)) machine.block = make([]WORD, len(block))

View File

@ -2,14 +2,15 @@ package creature
import "testing" import "testing"
func runMachineTest ( func runMachineTest(
program []int, program []int,
memory []int, memory []int,
test *testing.T, test *testing.T,
) ( ) (
machine *Machine[int], machine *Machine[int],
) { ) {
machine = &Machine[int] { Program: program } machine = &Machine[int]{}
machine.LoadProgram(program)
if memory != nil { if memory != nil {
machine.LoadMemory(memory) machine.LoadMemory(memory)
} }
@ -24,12 +25,12 @@ func runMachineTest (
} }
func TestPush(test *testing.T) { func TestPush(test *testing.T) {
machine := runMachineTest ([]int { machine := runMachineTest([]int{
PUSH, 3, PUSH, 3,
POP, POP,
PUSH, 654, PUSH, 654,
}, nil, test) }, nil, test)
result := machine.Pop() result := machine.Pop()
test.Log("popped:", result) test.Log("popped:", result)
@ -40,25 +41,25 @@ func TestPush(test *testing.T) {
} }
func TestArithmetic(test *testing.T) { func TestArithmetic(test *testing.T) {
machine := runMachineTest([]int { machine := runMachineTest([]int{
PUSH, 2,
PUSH, 3, PUSH, 3,
PUSH, 2,
ADD, ADD,
PUSH, 4,
PUSH, 10, PUSH, 10,
PUSH, 4,
SUB, SUB,
PUSH, 7,
PUSH, 2, PUSH, 2,
PUSH, 7,
MUL, MUL,
PUSH, 3,
PUSH, 12, PUSH, 12,
PUSH, 3,
DIV, DIV,
PUSH, 6,
PUSH, 8, PUSH, 8,
PUSH, 6,
MOD, MOD,
}, nil, test) }, nil, test)
@ -101,23 +102,22 @@ func TestArithmetic(test *testing.T) {
} }
func TestComparison(test *testing.T) { func TestComparison(test *testing.T) {
machine := runMachineTest([]int { machine := runMachineTest([]int{
PUSH, 6, PUSH, 6,
PUSH, 6, PUSH, 6,
EQ, EQ,
PUSH, 4,
PUSH, 324, PUSH, 324,
PUSH, 4,
GT, GT,
PUSH, 324,
PUSH, 4, PUSH, 4,
PUSH, 324,
LT, LT,
PUSH, 6,
PUSH, 54, PUSH, 54,
PUSH, 6,
NEQ, NEQ,
}, nil, test) }, nil, test)
neqResult := machine.Pop() neqResult := machine.Pop()
@ -152,7 +152,7 @@ func TestComparison(test *testing.T) {
} }
func TestPeekPoke(test *testing.T) { func TestPeekPoke(test *testing.T) {
machine := runMachineTest ([]int { machine := runMachineTest([]int{
PUSH, 0, PUSH, 0,
PEEK, PEEK,
@ -162,14 +162,14 @@ func TestPeekPoke(test *testing.T) {
PUSH, 1, PUSH, 1,
PEEK, PEEK,
}, []int { }, []int{
632, 632,
13, 13,
}, test) }, test)
secondResult := machine.Pop() secondResult := machine.Pop()
firstResult := machine.Pop() firstResult := machine.Pop()
test.Log("first:", firstResult) test.Log("first:", firstResult)
test.Log("second:", secondResult) test.Log("second:", secondResult)
@ -185,12 +185,12 @@ func TestPeekPoke(test *testing.T) {
} }
func TestHalt(test *testing.T) { func TestHalt(test *testing.T) {
machine := runMachineTest ([]int { machine := runMachineTest([]int{
PUSH, 32, PUSH, 32,
HALT, HALT,
PUSH, 3, PUSH, 3,
}, nil, test) }, nil, test)
result := machine.Pop() result := machine.Pop()
test.Log("popped:", result) test.Log("popped:", result)
@ -201,11 +201,11 @@ func TestHalt(test *testing.T) {
} }
func TestJump(test *testing.T) { func TestJump(test *testing.T) {
machine := runMachineTest ([]int { machine := runMachineTest([]int{
PUSH, 1, PUSH, 1,
PUSH, 8, PUSH, 8,
JMP, JMP,
PUSH, 3, PUSH, 3,
HALT, HALT,
@ -218,7 +218,7 @@ func TestJump(test *testing.T) {
PUSH, 5, PUSH, 5,
}, nil, test) }, nil, test)
result := machine.Pop() result := machine.Pop()
test.Log("popped:", result) test.Log("popped:", result)
@ -230,7 +230,8 @@ func TestJump(test *testing.T) {
func TestRegister(test *testing.T) { func TestRegister(test *testing.T) {
output := "" output := ""
machine := &Machine[int] { Program: []int { machine := &Machine[int]{}
machine.LoadProgram([]int{
PUSH, int('h'), PUSH, int('h'),
PUSH, 4, PUSH, 4,
CAL, CAL,
@ -258,7 +259,7 @@ func TestRegister(test *testing.T) {
PUSH, int('!'), PUSH, int('!'),
PUSH, 4, PUSH, 4,
CAL, CAL,
}} })
machine.Register(4, func(machine *Machine[int]) (stop bool) { machine.Register(4, func(machine *Machine[int]) (stop bool) {
output += string(rune(machine.Pop())) output += string(rune(machine.Pop()))
return return

View File

@ -1,3 +1,4 @@
//go:build ignore
// +build ignore // +build ignore
package main package main
@ -5,11 +6,12 @@ package main
import "os" import "os"
import cre "git.tebibyte.media/sashakoshka/creature" import cre "git.tebibyte.media/sashakoshka/creature"
func main () { func main() {
// this is a simple echo program. it will take in input indefinetly and // this is a simple echo program. it will take in input indefinetly and
// repeat it. due to line buffering in the terminal however, it will // repeat it. due to line buffering in the terminal however, it will
// only print output once you have pressed enter. // only print output once you have pressed enter.
machine := cre.Machine[int] { Program: []int { machine := cre.Machine[int]{}
machine.LoadProgram([]int{
cre.PUSH, 0, cre.PUSH, 0,
cre.CAL, cre.CAL,
@ -19,22 +21,24 @@ func main () {
cre.PUSH, 1, cre.PUSH, 1,
cre.PUSH, 0, cre.PUSH, 0,
cre.JMP, cre.JMP,
}} })
machine.Register (0, read) machine.Register(0, read)
machine.Register (1, write) machine.Register(1, write)
err := machine.Execute(0) err := machine.Execute(0)
if err != nil { panic(err.Error()) } if err != nil {
panic(err.Error())
}
} }
func read (machine *cre.Machine[int]) (stop bool) { func read(machine *cre.Machine[int]) (stop bool) {
ch := []byte { 0 } ch := []byte{0}
os.Stdin.Read(ch) os.Stdin.Read(ch)
machine.Push(int(ch[0])) machine.Push(int(ch[0]))
return return
} }
func write (machine *cre.Machine[int]) (stop bool) { func write(machine *cre.Machine[int]) (stop bool) {
print(string(rune(machine.Pop()))) print(string(rune(machine.Pop())))
return return
} }

View File

@ -1,3 +1,4 @@
//go:build ignore
// +build ignore // +build ignore
package main package main
@ -5,7 +6,7 @@ package main
import "os" import "os"
import cre "git.tebibyte.media/sashakoshka/creature" import cre "git.tebibyte.media/sashakoshka/creature"
func main () { func main() {
// this is an "ai psychiatrist" program. when it starts, it asks the // this is an "ai psychiatrist" program. when it starts, it asks the
// user "What brings you in today?", and no matter what the user types, // user "What brings you in today?", and no matter what the user types,
// the program will respond with "And how does that make you feel?". // the program will respond with "And how does that make you feel?".
@ -13,14 +14,15 @@ func main () {
// point. // point.
// constant address values // constant address values
x := 0 x := 0
introStart := 1 introStart := 1
introLoopStart := 5 introLoopStart := 5
readLoopStart := 36 readLoopStart := 36
responseStart := 71 responseStart := 71
responseLoopStart := 50 responseLoopStart := 50
machine := cre.Machine[int] { Program: []int { machine := cre.Machine[int]{}
machine.LoadProgram([]int{
// reset x // reset x
cre.PUSH, introStart, cre.PUSH, introStart,
cre.PUSH, x, cre.PUSH, x,
@ -37,7 +39,7 @@ func main () {
cre.EQ, cre.EQ,
cre.PUSH, readLoopStart, cre.PUSH, readLoopStart,
cre.JMP, cre.JMP,
// get char // get char
cre.PUSH, x, cre.PUSH, x,
cre.PEEK, cre.PEEK,
@ -86,7 +88,7 @@ func main () {
cre.EQ, cre.EQ,
cre.PUSH, readLoopStart, cre.PUSH, readLoopStart,
cre.JMP, cre.JMP,
// get char // get char
cre.PUSH, x, cre.PUSH, x,
cre.PEEK, cre.PEEK,
@ -108,35 +110,37 @@ func main () {
cre.PUSH, 1, cre.PUSH, 1,
cre.PUSH, responseLoopStart, cre.PUSH, responseLoopStart,
cre.JMP, cre.JMP,
}} })
stringData := []byte ( stringData := []byte(
"\x00" + "\x00" +
"== Artificial Intelligence Psychiatrist ==\n" + "== Artificial Intelligence Psychiatrist ==\n" +
"What brings you in today?\n\x00" + "What brings you in today?\n\x00" +
"And how does that make you feel?\n\x00") "And how does that make you feel?\n\x00")
block := make([]int, len(stringData)) block := make([]int, len(stringData))
for index, char := range stringData { for index, char := range stringData {
block[index] = int(char) block[index] = int(char)
} }
machine.LoadMemory(block) machine.LoadMemory(block)
machine.Register (0, read) machine.Register(0, read)
machine.Register (1, write) machine.Register(1, write)
err := machine.Execute(0) err := machine.Execute(0)
if err != nil { panic(err.Error()) } if err != nil {
panic(err.Error())
}
} }
func read (machine *cre.Machine[int]) (stop bool) { func read(machine *cre.Machine[int]) (stop bool) {
ch := []byte { 0 } ch := []byte{0}
os.Stdin.Read(ch) os.Stdin.Read(ch)
machine.Push(int(ch[0])) machine.Push(int(ch[0]))
return return
} }
func write (machine *cre.Machine[int]) (stop bool) { func write(machine *cre.Machine[int]) (stop bool) {
print(string(rune(machine.Pop()))) print(string(rune(machine.Pop())))
return return
} }

2
go.mod
View File

@ -1,3 +1,3 @@
module git.tebibyte.media/sashakoshka/creature module git.tebibyte.media/sashakoshka/creature
go 1.19 go 1.18