diff --git a/internal/preproc/preproc.go b/internal/preproc/preproc.go index 025fb7e..e96fbc7 100644 --- a/internal/preproc/preproc.go +++ b/internal/preproc/preproc.go @@ -31,7 +31,7 @@ func (HaltError) Error() string { return "preprocessor HALT" } // PreProcess processes the given root source file and returns the flattened, define-expanded // lines. Directive lines are not emitted. -func PreProcess(rootFilename string, reader ...FileReader) ([]Line, error) { +func PreProcess(rootFilename string, reader ...FileReader) ([]Line, *Pragma, error) { var r FileReader if len(reader) > 0 && reader[0] != nil { r = reader[0] @@ -39,7 +39,8 @@ func PreProcess(rootFilename string, reader ...FileReader) ([]Line, error) { r = NewDiskFileReader() } pp := newPreproc(r) - return pp.run(rootFilename) + lines, err := pp.run(rootFilename) + return lines, pp.pragma, err } // -------------------- internal -------------------- diff --git a/main.go b/main.go index 0af2cd4..533147a 100644 --- a/main.go +++ b/main.go @@ -1,253 +1,91 @@ package main import ( - "c65gm/internal/preproc" - "c65gm/internal/utils" "flag" "fmt" "os" "strings" + + "c65gm/internal/commands" + "c65gm/internal/compiler" + "c65gm/internal/preproc" ) -// c65cm - A 6502 Cross-Compiler for the ACME Cross-Assembler +// c65gm - A 6502 Cross-Compiler for the ACME Cross-Assembler // Copyright (C) 1999, 2025 Mattias Hansson -// Distributed under GPL. External library github.com/armon/go-radix under MIT. - -// Package-level shared state accessible by other compilation units -var ( -/* - VarList *VarList - WhileStack *LabelStack - WendStack *LabelStack - IfStack *LabelStack - GeneralStack *LabelStack - FuncList *FunctionHandler - ConstStrHandler *ConstantStringHandler - Pragmas []string -*/ - -) - -type ShowMode int - -const ( - ShowNone ShowMode = iota - ShowLineCount - ShowLineView -) - -func testMultiIndex() { - type User struct { - ID string - FirstName string - LastName string - Email string - } - - // 2 indexes: [0]=email only, [1]=firstname+lastname - idx := utils.NewMultiIndex[User](2) - - idx.AddItem(User{"1", "John", "Doe", "john@example.com"}, [][]string{ - {"john@example.com"}, - {"john", "doe"}, - }) - - idx.AddItem(User{"2", "Jane", "Smith", "jane@example.com"}, [][]string{ - {"jane@example.com"}, - {"jane", "smith"}, - }) - - // Search by email (index 0, single string) - user, found := idx.FindItem(0, []string{"john@example.com"}) - fmt.Println(user, found) // {1 John Doe john@example.com} true - - // Search by name (index 1, two strings) - user, found = idx.FindItem(1, []string{"jane", "smith"}) - fmt.Println(user, found) // {2 Jane Smith jane@example.com} true - - // Wrong composite key fails - user, found = idx.FindItem(1, []string{"jane", "doe"}) - fmt.Println(found) // false - - // All items - fmt.Println(idx.Items()) // [{1 John Doe john@example.com} {2 Jane Smith jane@example.com}] -} +// Distributed under GPL. func main() { - - testMultiIndex() - - os.Exit(1) fmt.Println("c65gm - A 6502 Cross-Compiler for the ACME Cross-Assembler.") fmt.Println("Copyright (C) 1999, 2025 Mattias Hansson. v1.0.0") - fmt.Println("Distributed under GPL. External library github.com/armon/go-radix under MIT.") + fmt.Println("Distributed under GPL.") fmt.Println() inFile := flag.String("in", "", "input source file (required)") outFile := flag.String("out", "", "output assembly file (required)") - view := flag.String("view", "lines", "output mode: none, count, lines") flag.Parse() if *inFile == "" || *outFile == "" { - fmt.Fprintln(os.Stderr, "Error: -in and -out are required") + _, _ = fmt.Fprintln(os.Stderr, "Error: -in and -out are required") flag.Usage() os.Exit(1) } - var showMode ShowMode - switch strings.ToLower(*view) { - case "none": - showMode = ShowNone - case "count": - showMode = ShowLineCount - case "lines": - showMode = ShowLineView - default: - fmt.Fprintf(os.Stderr, "Error: invalid view mode: %s\n", *view) - os.Exit(1) - } - - if err := run(*inFile, *outFile, showMode); err != nil { + if err := run(*inFile, *outFile); err != nil { if _, ok := err.(preproc.HaltError); ok { - os.Exit(0) + fmt.Println("Halted by #HALT directive") + os.Exit(2) } - fmt.Fprintf(os.Stderr, "Error: %v\n", err) + _, _ = fmt.Fprintf(os.Stderr, "Error: %v\n", err) os.Exit(1) } + + fmt.Println("Compilation successful.") } -func run(inFile, outFile string, showMode ShowMode) error { - initSharedObjects() - return compile(inFile, outFile, showMode) -} - -func initSharedObjects() { - /* - VarList = NewVarList() - WhileStack = NewLabelStack("whilelbl") - WendStack = NewLabelStack("wendlbl") - IfStack = NewLabelStack("iflbl") - GeneralStack = NewLabelStack("General") - FuncList = NewFunctionHandler() - ConstStrHandler = NewConstantStringHandler() - Pragmas = make([]string, 0) - */ -} - -func compile(inFile, outFile string, showMode ShowMode) error { - lines, err := preproc.PreProcess(inFile) +func run(inFile, outFile string) error { + // Preprocess + lines, pragma, err := preproc.PreProcess(inFile) if err != nil { - return fmt.Errorf("preprocessor failed: %w", err) + return fmt.Errorf("preprocessing failed: %w", err) } - asmSource := make([]string, 0, len(lines)*2) - lineCount := 0 + fmt.Printf("Preprocessed %d lines\n", len(lines)) - for i := 0; i < len(lines); { - line := lines[i] - lineCount++ + // Create compiler and register commands + comp := compiler.NewCompiler(pragma) + registerCommands(comp) - displayProgress(showMode, lineCount, line.Text) - - // Handle inline ASM blocks - if isAsmStart(line) { - asmSource = append(asmSource, ";"+line.Text) - i++ - - for i < len(lines) && !isAsmEnd(lines[i]) { - lineCount++ - displayProgress(showMode, lineCount, lines[i].Text) - - if lines[i].Text != "" { - //processed := LocalVarsInAsmLine(lines[i].Text) - processed := lines[i].Text - asmSource = append(asmSource, processed) - } - i++ - } - - i++ // Skip ENDASM line - continue - } - - if isAsmEnd(line) { - i++ - continue - } - - text := stripComments(line.Text) - if text == "" { - i++ - continue - } - - /* - if !InterpretLine(text, &asmSource) { - return fmt.Errorf("line not recognized: %s", text) - } - */ - - i++ + // Compile + asmLines, err := comp.Compile(lines) + if err != nil { + return fmt.Errorf("compilation failed: %w", err) } - /* - VarList.WriteDownTypesToAsm(&asmSource) - ConstStrHandler.GenerateConstStrDecls(&asmSource) - VarList.WriteAbsVars(&asmSource) - VarList.WriteConstants(&asmSource) - */ + fmt.Printf("Generated %d lines of assembly\n", len(asmLines)) - if err := writeLines(outFile, asmSource); err != nil { + // Write output + if err := writeOutput(outFile, asmLines); err != nil { return fmt.Errorf("failed to write output: %w", err) } - if showMode != ShowNone { - fmt.Println("\ndone.") - } - return nil } -func displayProgress(mode ShowMode, lineCount int, text string) { - switch mode { - case ShowLineCount: - fmt.Printf("\rCompiling line: %d", lineCount) - case ShowLineView: - fmt.Println(text) - } +func registerCommands(comp *compiler.Compiler) { + // Register all command handlers here + // This is the single place where all commands are wired up + + comp.Registry().Register(&commands.ByteCommand{}) + + // TODO: Add more commands as they're implemented: + // comp.Registry().Register(&commands.WordCommand{}) + // comp.Registry().Register(&commands.LetCommand{}) + // comp.Registry().Register(&commands.IfCommand{}) + // comp.Registry().Register(&commands.WhileCommand{}) + // etc. } -func isAsmStart(line preproc.Line) bool { - //return len(line.Tokens) == 1 && strings.ToUpper(line.Tokens[0]) == "ASM" - return false -} - -func isAsmEnd(line preproc.Line) bool { - //return len(line.Tokens) == 1 && strings.ToUpper(line.Tokens[0]) == "ENDASM" - return false -} - -func stripComments(s string) string { - if strings.HasPrefix(s, "//") { - return "" - } - if idx := strings.Index(s, "//"); idx >= 0 { - return strings.TrimSpace(s[:idx]) - } - return s -} - -func writeLines(filename string, lines []string) error { - f, err := os.Create(filename) - if err != nil { - return err - } - defer f.Close() - - for _, line := range lines { - if _, err := fmt.Fprintln(f, line); err != nil { - return err - } - } - return nil +func writeOutput(filename string, lines []string) error { + return os.WriteFile(filename, []byte(strings.Join(lines, "\n")+"\n"), 0644) }