package main import ( "unicode" "bytes" "bufio" "strings" "strconv" "os" "fmt" "errors" . "e1lama/elm" ) func Any[T any](ts []T, pred func(T) bool) bool { for _, t := range ts { if pred(t) { return true } } return false } func parseLabelledOperand(line string, preprocessed *PreprocessorResult) (int, error) { op := 0; dest := strings.Fields(line)[1] containsLetter := Any([]rune(dest), unicode.IsLetter) if (containsLetter) { op = preprocessed.LabelMap[dest] } else { op, err := strconv.Atoi(dest) if err != nil { return op, err } } return op, nil } var InstructionTable map[string]Operation = map[string]Operation{ "HALT": 0, "PUSH": 1, "POP" : 2, "DUP" : 3, "ADD" : 4, "SUB" : 5, "MUL" : 6, "DIV" : 7, "EQ" : 8, "JMP" : 9, "JMPIF" : 10, "CALL" : 11, "RET" : 12, "STORE" : 13, "GET" : 14, } type PreprocessorResult struct { LabelMap map[string]int Listing *bytes.Buffer } func PreprocessAssembly(f *os.File) (*PreprocessorResult, error) { labelMap := make(map[string]int) lineCount := 0 buf := bytes.Buffer{} scanner := bufio.NewScanner(f) for scanner.Scan() { line := scanner.Text() if (strings.HasSuffix(line, ":")) { labelMap[strings.Split(line, ":")[0]] = lineCount } else { _, ok := InstructionTable[strings.Fields(line)[0]] if (ok) { lineCount++ fmt.Fprintln(&buf, line) } else { return nil, errors.New("Unknown instruction: " + line) } } } return &PreprocessorResult{ LabelMap: labelMap, Listing: &buf }, nil } func parseProgramFromFile(m* Machine, f *os.File) error { preprocessed, err := PreprocessAssembly(f) if err != nil { return err } scanner := bufio.NewScanner(preprocessed.Listing) for scanner.Scan() { line := scanner.Text() if (strings.HasPrefix(line, "//")) { continue; } else if (strings.HasPrefix(line, "HALT")){ m.Push(Inst{Operation: HALT}) } else if (strings.HasPrefix(line, "PUSH")) { op, err := strconv.Atoi(strings.Fields(line)[1]) if err != nil { return err } m.Push(Inst{Operation: PUSH, Operand: int64(op)}) } else if (strings.HasPrefix(line, "POP")) { m.Push(Inst{Operation: POP}) } else if (strings.HasPrefix(line, "DUP")) { op, err := strconv.Atoi(strings.Fields(line)[1]) if err != nil { return err } m.Push(Inst{Operation: DUP, Operand: int64(op)}) } else if (strings.HasPrefix(line, "ADD")) { m.Push(Inst{Operation: ADD}) } else if (strings.HasPrefix(line, "SUB")) { m.Push(Inst{Operation: SUB}) } else if (strings.HasPrefix(line, "DIV")) { m.Push(Inst{Operation: DIV}) } else if (strings.HasPrefix(line, "MUL")) { m.Push(Inst{Operation: MUL}) } else if (strings.HasPrefix(line, "JMPIF")) { op, err := parseLabelledOperand(line, preprocessed) if err != nil { return err } m.Push(Inst{Operation: JMPIF, Operand: int64(op)}) } else if (strings.HasPrefix(line, "JMP")) { op, err := parseLabelledOperand(line, preprocessed) if err != nil { return err } m.Push(Inst{Operation: JMP, Operand: int64(op)}) } else if (strings.HasPrefix(line, "EQ")) { m.Push(Inst{Operation: EQ}) } else if (strings.HasPrefix(line, "CALL")) { op, err := parseLabelledOperand(line, preprocessed) if err != nil { return err } m.Push(Inst{Operation: CALL, Operand: int64(op)}) } else if (strings.HasPrefix(line, "RET")) { m.Push(Inst{Operation: RET}) } else if (strings.HasPrefix(line, "STORE")) { op, err := strconv.Atoi(strings.Fields(line)[1]) if err != nil { return err } m.Push(Inst{Operation: STORE, Operand: int64(op)}) } else if (strings.HasPrefix(line, "GET")) { op, err := strconv.Atoi(strings.Fields(line)[1]) if err != nil { return err } m.Push(Inst{Operation: GET, Operand: int64(op)}) } else { return errors.New("Unknown instruction: " + line) } } if err := scanner.Err(); err != nil { return err } return nil } func main() { f, err := os.Open("program.lil") if err != nil { fmt.Fprintf(os.Stderr, "[ERR] Couldn't open file: %s\n", err); os.Exit(1) } defer f.Close() m := Constructor() err = parseProgramFromFile(m, f) if err != nil { fmt.Fprintf(os.Stderr, "[ERR] %s\n", err); os.Exit(1) } m.Run() }