From 635aced0d4af5abcf2e5b5cc85e22b76c31d6ba9 Mon Sep 17 00:00:00 2001 From: Sasha Koshka Date: Mon, 29 Aug 2022 10:48:45 -0400 Subject: [PATCH] Machine is now a generic type --- creature.go | 80 +++++++++++++++++++++++----------------- creature_test.go | 8 ++-- examples/echo.go | 6 +-- examples/psychiatrist.go | 6 +-- 4 files changed, 56 insertions(+), 44 deletions(-) diff --git a/creature.go b/creature.go index 18bd92e..85cc0f6 100644 --- a/creature.go +++ b/creature.go @@ -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() {} diff --git a/creature_test.go b/creature_test.go index 7ba7243..abe27ae 100644 --- a/creature_test.go +++ b/creature_test.go @@ -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 }) diff --git a/examples/echo.go b/examples/echo.go index 105ca58..8a3d8ba 100644 --- a/examples/echo.go +++ b/examples/echo.go @@ -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 } diff --git a/examples/psychiatrist.go b/examples/psychiatrist.go index 26e7316..72338b5 100644 --- a/examples/psychiatrist.go +++ b/examples/psychiatrist.go @@ -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 }