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 }