Machine is now a generic type

This commit is contained in:
Sasha Koshka 2022-08-29 10:48:45 -04:00
parent 810ecdb3ac
commit 635aced0d4
4 changed files with 56 additions and 44 deletions

View File

@ -1,24 +1,31 @@
package creature
// Word is a type constraint defining possible word types for the Machine.
type Word interface {
int8 | int16 | int32 | int64 |
uint8 | uint16 | uint32 | uint64 |
int | uint
}
// 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
// it.
type Machine struct {
type Machine [WORD Word] struct {
// Program is not modified by the machine, and can be freely set before
// the machine is started.
Program []int
Program []WORD
stack []int
block []int
counter int
pointer int
functions map[int]MachineFunction
stack []WORD
block []WORD
counter WORD
pointer WORD
functions map[WORD]MachineFunction[WORD]
}
// 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 will halt execution if true is returned.
type MachineFunction func(machine *Machine) (stop bool)
type MachineFunction [WORD Word] func(machine *Machine[WORD]) (stop bool)
// All supported opcodes
const (
@ -68,7 +75,7 @@ func (err Error) Error() (description string) {
// Reset resets the stack of the machine. This should be called before Execute,
// since Execute does not reset the stack.
func (machine *Machine) Reset() {
func (machine *Machine[WORD]) Reset() {
machine.stack = nil
machine.pointer = 0
}
@ -77,10 +84,10 @@ func (machine *Machine) Reset() {
// run until a HALT instruction is encountered, or the end of program memory is
// reached. If an unknown instruction is encountered, the machine halts and
// returns an error.
func (machine *Machine) Execute(offset int) (err error) {
func (machine *Machine[WORD]) Execute(offset WORD) (err error) {
machine.counter = offset
for machine.counter < len(machine.Program) {
for int(machine.counter) < len(machine.Program) {
switch machine.instruction() {
case PUSH:
// push the next word in program memory onto the stack
@ -131,7 +138,7 @@ func (machine *Machine) Execute(offset int) (err error) {
if machine.Pop() == machine.Pop() {
equal = 1
}
machine.Push(equal)
machine.Push(WORD(equal))
case GT:
// checks if the last word on the stack is greater than
@ -140,7 +147,7 @@ func (machine *Machine) Execute(offset int) (err error) {
if machine.Pop() > machine.Pop() {
greater = 1
}
machine.Push(greater)
machine.Push(WORD(greater))
case LT:
// checks if the last word on the stack is less than the
@ -149,7 +156,7 @@ func (machine *Machine) Execute(offset int) (err error) {
if machine.Pop() < machine.Pop() {
less = 1
}
machine.Push(less)
machine.Push(WORD(less))
case NEQ:
// checks if the last two words on the stack are not
@ -158,7 +165,7 @@ func (machine *Machine) Execute(offset int) (err error) {
if machine.Pop() != machine.Pop() {
notEqual = 1
}
machine.Push(notEqual)
machine.Push(WORD(notEqual))
case MOD:
// performs a modulo operation of the second to last
@ -204,7 +211,7 @@ func (machine *Machine) Execute(offset int) (err error) {
}
// Instruction returns the current instruction in program memory.
func (machine *Machine) instruction() (instruction int) {
func (machine *Machine[WORD]) instruction() (instruction WORD) {
instruction = machine.Program[machine.counter]
return
}
@ -212,17 +219,17 @@ func (machine *Machine) instruction() (instruction int) {
// reallocateStack changes the size of the stack to something reasonable. This
// should be called then the stack pointer is really small compared to the
// actual stack size, or the stack pointer is bigger than the stack.
func (machine *Machine) reallocateStack() {
reallocatedStack := make([]int, machine.pointer*2)
func (machine *Machine[WORD]) reallocateStack() {
reallocatedStack := make([]WORD, machine.pointer*2)
copy(reallocatedStack, machine.stack)
machine.stack = reallocatedStack
}
// Push pushes a word onto the stack, increasing the stack pointer and
// reallocating the stack if necessary.
func (machine *Machine) Push(word int) {
func (machine *Machine[WORD]) Push(word WORD) {
machine.pointer++
if len(machine.stack) <= machine.pointer {
if len(machine.stack) <= int(machine.pointer) {
machine.reallocateStack()
}
machine.stack[machine.pointer] = word
@ -230,15 +237,15 @@ func (machine *Machine) Push(word int) {
// Pop pops the last word off of the stack, and returns it, decreasing the stack
// pointer and reallocating the stack if necessary.
func (machine *Machine) Pop() (word int) {
if machine.pointer <= 0 || machine.pointer >= len(machine.stack) {
func (machine *Machine[WORD]) Pop() (word WORD) {
if int(machine.pointer) <= 0 || int(machine.pointer) >= len(machine.stack) {
return
}
word = machine.stack[machine.pointer]
machine.pointer--
if machine.pointer < len(machine.stack)/3 {
if int(machine.pointer) < len(machine.stack)/3 {
machine.reallocateStack()
}
@ -246,17 +253,17 @@ func (machine *Machine) Pop() (word int) {
}
// Peek returns the word at address in the block.
func (machine *Machine) Peek(address int) (word int) {
if address < len(machine.block) {
func (machine *Machine[WORD]) Peek(address WORD) (word WORD) {
if int(address) < len(machine.block) {
word = machine.block[address]
}
return
}
// Poke sets the value at address in the block to word.
func (machine *Machine) Poke(address int, word int) {
if address >= len(machine.block) {
reallocatedBlock := make([]int, address*3/2)
func (machine *Machine[WORD]) Poke(address WORD, word WORD) {
if int(address) >= len(machine.block) {
reallocatedBlock := make([]WORD, address*3/2)
copy(reallocatedBlock, machine.block)
machine.block = reallocatedBlock
}
@ -265,9 +272,14 @@ func (machine *Machine) Poke(address int, word int) {
// Register registers a function at the specified ID. If there is already a
// function registered at that ID, this method will return an error.
func (machine *Machine) Register(id int, function MachineFunction) (err error) {
func (machine *Machine[WORD]) Register (
id WORD,
function MachineFunction[WORD],
) (
err error,
) {
if machine.functions == nil {
machine.functions = make(map[int]MachineFunction)
machine.functions = make(map[WORD]MachineFunction[WORD])
}
_, exists := machine.functions[id]
@ -282,7 +294,7 @@ func (machine *Machine) Register(id int, function MachineFunction) (err error) {
}
// Unregister removes a function that is registered at the specified ID.
func (machine *Machine) Unregister(id int) {
func (machine *Machine[WORD]) Unregister(id WORD) {
if machine.functions == nil {
return
}
@ -290,10 +302,10 @@ func (machine *Machine) Unregister(id int) {
}
// LoadMemory loads the contents of block into the machine's memory.
func (machine *Machine) LoadMemory(block []int) {
machine.block = make([]int, len(block))
func (machine *Machine[WORD]) LoadMemory(block []WORD) {
machine.block = make([]WORD, len(block))
copy(machine.block, block)
}
// Milk milks the stack machine.
func (machine *Machine) Milk() {}
func (machine *Machine[WORD]) Milk() {}

View File

@ -7,9 +7,9 @@ func runMachineTest (
memory []int,
test *testing.T,
) (
machine *Machine,
machine *Machine[int],
) {
machine = &Machine { Program: program }
machine = &Machine[int] { Program: program }
if memory != nil {
machine.LoadMemory(memory)
}
@ -230,7 +230,7 @@ func TestJump(test *testing.T) {
func TestRegister(test *testing.T) {
output := ""
machine := &Machine { Program: []int {
machine := &Machine[int] { Program: []int {
PUSH, int('h'),
PUSH, 4,
CAL,
@ -259,7 +259,7 @@ func TestRegister(test *testing.T) {
PUSH, 4,
CAL,
}}
machine.Register(4, func(machine *Machine) (stop bool) {
machine.Register(4, func(machine *Machine[int]) (stop bool) {
output += string(rune(machine.Pop()))
return
})

View File

@ -9,7 +9,7 @@ func main () {
// 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
// only print output once you have pressed enter.
machine := cre.Machine { Program: []int {
machine := cre.Machine[int] { Program: []int {
cre.PUSH, 0,
cre.CAL,
@ -27,14 +27,14 @@ func main () {
if err != nil { panic(err.Error()) }
}
func read (machine *cre.Machine) (stop bool) {
func read (machine *cre.Machine[int]) (stop bool) {
ch := []byte { 0 }
os.Stdin.Read(ch)
machine.Push(int(ch[0]))
return
}
func write (machine *cre.Machine) (stop bool) {
func write (machine *cre.Machine[int]) (stop bool) {
print(string(rune(machine.Pop())))
return
}

View File

@ -20,7 +20,7 @@ func main () {
responseStart := 71
responseLoopStart := 50
machine := cre.Machine { Program: []int {
machine := cre.Machine[int] { Program: []int {
// reset x
cre.PUSH, introStart,
cre.PUSH, x,
@ -129,14 +129,14 @@ func main () {
if err != nil { panic(err.Error()) }
}
func read (machine *cre.Machine) (stop bool) {
func read (machine *cre.Machine[int]) (stop bool) {
ch := []byte { 0 }
os.Stdin.Read(ch)
machine.Push(int(ch[0]))
return
}
func write (machine *cre.Machine) (stop bool) {
func write (machine *cre.Machine[int]) (stop bool) {
print(string(rune(machine.Pop())))
return
}