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 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 // 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 struct { type Machine [WORD Word] struct {
// Program is not modified by the machine, and can be freely set before // Program is not modified by the machine, and can be freely set before
// the machine is started. // the machine is started.
Program []int Program []WORD
stack []int stack []WORD
block []int block []WORD
counter int counter WORD
pointer int pointer WORD
functions map[int]MachineFunction functions map[WORD]MachineFunction[WORD]
} }
// 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 func(machine *Machine) (stop bool) type MachineFunction [WORD Word] func(machine *Machine[WORD]) (stop bool)
// All supported opcodes // All supported opcodes
const ( const (
@ -68,7 +75,7 @@ func (err Error) Error() (description string) {
// Reset resets the stack of the machine. This should be called before Execute, // Reset resets the stack of the machine. This should be called before Execute,
// since Execute does not reset the stack. // since Execute does not reset the stack.
func (machine *Machine) Reset() { func (machine *Machine[WORD]) Reset() {
machine.stack = nil machine.stack = nil
machine.pointer = 0 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 // 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 // reached. If an unknown instruction is encountered, the machine halts and
// returns an error. // returns an error.
func (machine *Machine) Execute(offset int) (err error) { func (machine *Machine[WORD]) Execute(offset WORD) (err error) {
machine.counter = offset machine.counter = offset
for 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 // 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() { if machine.Pop() == machine.Pop() {
equal = 1 equal = 1
} }
machine.Push(equal) machine.Push(WORD(equal))
case GT: case GT:
// checks if the last word on the stack is greater than // 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() { if machine.Pop() > machine.Pop() {
greater = 1 greater = 1
} }
machine.Push(greater) machine.Push(WORD(greater))
case LT: case LT:
// checks if the last word on the stack is less than the // 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() { if machine.Pop() < machine.Pop() {
less = 1 less = 1
} }
machine.Push(less) machine.Push(WORD(less))
case NEQ: case NEQ:
// checks if the last two words on the stack are not // 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() { if machine.Pop() != machine.Pop() {
notEqual = 1 notEqual = 1
} }
machine.Push(notEqual) machine.Push(WORD(notEqual))
case MOD: case MOD:
// performs a modulo operation of the second to last // 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. // 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] instruction = machine.Program[machine.counter]
return return
} }
@ -212,17 +219,17 @@ func (machine *Machine) instruction() (instruction int) {
// reallocateStack changes the size of the stack to something reasonable. This // reallocateStack changes the size of the stack to something reasonable. This
// should be called then the stack pointer is really small compared to the // 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. // actual stack size, or the stack pointer is bigger than the stack.
func (machine *Machine) reallocateStack() { func (machine *Machine[WORD]) reallocateStack() {
reallocatedStack := make([]int, machine.pointer*2) reallocatedStack := make([]WORD, machine.pointer*2)
copy(reallocatedStack, machine.stack) copy(reallocatedStack, machine.stack)
machine.stack = reallocatedStack machine.stack = reallocatedStack
} }
// Push pushes a word onto the stack, increasing the stack pointer and // Push pushes a word onto the stack, increasing the stack pointer and
// reallocating the stack if necessary. // reallocating the stack if necessary.
func (machine *Machine) Push(word int) { func (machine *Machine[WORD]) Push(word WORD) {
machine.pointer++ machine.pointer++
if len(machine.stack) <= machine.pointer { if len(machine.stack) <= int(machine.pointer) {
machine.reallocateStack() machine.reallocateStack()
} }
machine.stack[machine.pointer] = word 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 // Pop pops the last word off of the stack, and returns it, decreasing the stack
// pointer and reallocating the stack if necessary. // pointer and reallocating the stack if necessary.
func (machine *Machine) Pop() (word int) { func (machine *Machine[WORD]) Pop() (word WORD) {
if machine.pointer <= 0 || 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--
if machine.pointer < len(machine.stack)/3 { if int(machine.pointer) < len(machine.stack)/3 {
machine.reallocateStack() machine.reallocateStack()
} }
@ -246,17 +253,17 @@ func (machine *Machine) Pop() (word int) {
} }
// Peek returns the word at address in the block. // Peek returns the word at address in the block.
func (machine *Machine) Peek(address int) (word int) { func (machine *Machine[WORD]) Peek(address WORD) (word WORD) {
if address < len(machine.block) { if int(address) < len(machine.block) {
word = machine.block[address] word = machine.block[address]
} }
return return
} }
// Poke sets the value at address in the block to word. // Poke sets the value at address in the block to word.
func (machine *Machine) Poke(address int, word int) { func (machine *Machine[WORD]) Poke(address WORD, word WORD) {
if address >= len(machine.block) { if int(address) >= len(machine.block) {
reallocatedBlock := make([]int, address*3/2) reallocatedBlock := make([]WORD, address*3/2)
copy(reallocatedBlock, machine.block) copy(reallocatedBlock, machine.block)
machine.block = reallocatedBlock 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 // 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) Register(id int, function MachineFunction) (err error) { func (machine *Machine[WORD]) Register (
id WORD,
function MachineFunction[WORD],
) (
err error,
) {
if machine.functions == nil { if machine.functions == nil {
machine.functions = make(map[int]MachineFunction) machine.functions = make(map[WORD]MachineFunction[WORD])
} }
_, exists := machine.functions[id] _, 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. // 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 { if machine.functions == nil {
return return
} }
@ -290,10 +302,10 @@ func (machine *Machine) Unregister(id int) {
} }
// 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) LoadMemory(block []int) { func (machine *Machine[WORD]) LoadMemory(block []WORD) {
machine.block = make([]int, len(block)) machine.block = make([]WORD, len(block))
copy(machine.block, block) copy(machine.block, block)
} }
// Milk milks the stack machine. // Milk milks the stack machine.
func (machine *Machine) Milk() {} func (machine *Machine[WORD]) Milk() {}

View File

@ -7,9 +7,9 @@ func runMachineTest (
memory []int, memory []int,
test *testing.T, test *testing.T,
) ( ) (
machine *Machine, machine *Machine[int],
) { ) {
machine = &Machine { Program: program } machine = &Machine[int] { Program: program }
if memory != nil { if memory != nil {
machine.LoadMemory(memory) machine.LoadMemory(memory)
} }
@ -230,7 +230,7 @@ func TestJump(test *testing.T) {
func TestRegister(test *testing.T) { func TestRegister(test *testing.T) {
output := "" output := ""
machine := &Machine { Program: []int { machine := &Machine[int] { Program: []int {
PUSH, int('h'), PUSH, int('h'),
PUSH, 4, PUSH, 4,
CAL, CAL,
@ -259,7 +259,7 @@ func TestRegister(test *testing.T) {
PUSH, 4, PUSH, 4,
CAL, CAL,
}} }}
machine.Register(4, func(machine *Machine) (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

@ -9,7 +9,7 @@ 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 { Program: []int { machine := cre.Machine[int] { Program: []int {
cre.PUSH, 0, cre.PUSH, 0,
cre.CAL, cre.CAL,
@ -27,14 +27,14 @@ func main () {
if err != nil { panic(err.Error()) } if err != nil { panic(err.Error()) }
} }
func read (machine *cre.Machine) (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) (stop bool) { func write (machine *cre.Machine[int]) (stop bool) {
print(string(rune(machine.Pop()))) print(string(rune(machine.Pop())))
return return
} }

View File

@ -20,7 +20,7 @@ func main () {
responseStart := 71 responseStart := 71
responseLoopStart := 50 responseLoopStart := 50
machine := cre.Machine { Program: []int { machine := cre.Machine[int] { Program: []int {
// reset x // reset x
cre.PUSH, introStart, cre.PUSH, introStart,
cre.PUSH, x, cre.PUSH, x,
@ -129,14 +129,14 @@ func main () {
if err != nil { panic(err.Error()) } if err != nil { panic(err.Error()) }
} }
func read (machine *cre.Machine) (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) (stop bool) { func write (machine *cre.Machine[int]) (stop bool) {
print(string(rune(machine.Pop()))) print(string(rune(machine.Pop())))
return return
} }