Machine is now a generic type
This commit is contained in:
parent
810ecdb3ac
commit
635aced0d4
80
creature.go
80
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() {}
|
||||
|
@ -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
|
||||
})
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user