package optimizer import ( "strconv" "strings" ) // parseHexOrDec parses an ACME-style number: $ff or 255. // Returns -1 if s is not a parseable simple number (e.g. label references, label). func parseHexOrDec(s string) int { if strings.HasPrefix(s, "$") { v, err := strconv.ParseUint(s[1:], 16, 16) if err != nil { return -1 } return int(v) } // Strip trailing characters after the number (e.g., ",y" in "5,y") if idx := strings.IndexAny(s, ",)"); idx >= 0 { s = s[:idx] } v, err := strconv.ParseUint(s, 10, 16) if err != nil { return -1 } return int(v) } // isSimpleNumber returns true if s is a plain hex/dec number (no label refs, <, >, etc.) func isSimpleNumber(s string) bool { return parseHexOrDec(s) >= 0 } type asmLine struct { text string isLabel bool isCode bool isComment bool optMarker bool opcode string operand string } // Origin identifies the source of an output line. The optimizer must know // whether a line was produced by the compiler itself (and is therefore safe // to optimize) or emitted verbatim from an ASM block or SCRIPT output. type Origin int const ( OriginGenerated Origin = iota // compiler-generated code (optimizable) OriginAsm // handwritten ASM block content (verbatim) OriginScript // SCRIPT print() output (verbatim) ) // SourceLine is a single output line together with its provenance. type SourceLine struct { Text string Origin Origin } // SourceLineTexts extracts just the text from a slice of source lines. func SourceLineTexts(lines []SourceLine) []string { out := make([]string, len(lines)) for i, l := range lines { out[i] = l.Text } return out } func parseLines(lines []SourceLine) []asmLine { var result []asmLine for _, sl := range lines { l := sl.Text al := asmLine{text: l} if l == "" { result = append(result, al) continue } if strings.HasPrefix(l, "; @@OPT:") { al.optMarker = true al.isComment = true result = append(result, al) continue } if strings.HasPrefix(l, ";") { al.isComment = true result = append(result, al) continue } // Only compiler-generated, indented lines are treated as optimizable // instructions. ACME requires labels at column 0, so any leading // whitespace means "not a label" — the exact character (tab or space) // is irrelevant. Generated labels and any verbatim ASM/SCRIPT line // become barriers. if sl.Origin == OriginGenerated && isIndented(l) { parts := strings.Fields(l) if len(parts) > 0 { al.isCode = true al.opcode = strings.ToLower(parts[0]) if len(parts) > 1 { al.operand = parts[1] } } else { al.isLabel = true } } else { al.isLabel = true } result = append(result, al) } return result } // isIndented reports whether a line has leading whitespace (an instruction), // as opposed to a label which starts at column 0. func isIndented(l string) bool { return len(l) > 0 && (l[0] == ' ' || l[0] == '\t') } // stripOptMarkers removes @@OPT comment lines from the output func stripOptMarkers(lines []asmLine) []asmLine { var result []asmLine for _, l := range lines { if l.optMarker { continue } result = append(result, l) } return result } func linesToString(lines []asmLine) []string { var result []string for _, l := range lines { result = append(result, l.text) } return result } // skipJmpMarker returns true if the line is an @@OPT comment (to be skipped in pass logic) func skipJmpMarker(line asmLine) bool { return line.optMarker }