First main.go driver with only the BYTE command finished.
This commit is contained in:
parent
89d8493192
commit
ff64cc6b93
2 changed files with 46 additions and 207 deletions
|
|
@ -31,7 +31,7 @@ func (HaltError) Error() string { return "preprocessor HALT" }
|
||||||
|
|
||||||
// PreProcess processes the given root source file and returns the flattened, define-expanded
|
// PreProcess processes the given root source file and returns the flattened, define-expanded
|
||||||
// lines. Directive lines are not emitted.
|
// 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
|
var r FileReader
|
||||||
if len(reader) > 0 && reader[0] != nil {
|
if len(reader) > 0 && reader[0] != nil {
|
||||||
r = reader[0]
|
r = reader[0]
|
||||||
|
|
@ -39,7 +39,8 @@ func PreProcess(rootFilename string, reader ...FileReader) ([]Line, error) {
|
||||||
r = NewDiskFileReader()
|
r = NewDiskFileReader()
|
||||||
}
|
}
|
||||||
pp := newPreproc(r)
|
pp := newPreproc(r)
|
||||||
return pp.run(rootFilename)
|
lines, err := pp.run(rootFilename)
|
||||||
|
return lines, pp.pragma, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// -------------------- internal --------------------
|
// -------------------- internal --------------------
|
||||||
|
|
|
||||||
248
main.go
248
main.go
|
|
@ -1,253 +1,91 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"c65gm/internal/preproc"
|
|
||||||
"c65gm/internal/utils"
|
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"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
|
// Copyright (C) 1999, 2025 Mattias Hansson
|
||||||
// Distributed under GPL. External library github.com/armon/go-radix under MIT.
|
// Distributed under GPL.
|
||||||
|
|
||||||
// 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}]
|
|
||||||
}
|
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
|
||||||
testMultiIndex()
|
|
||||||
|
|
||||||
os.Exit(1)
|
|
||||||
fmt.Println("c65gm - A 6502 Cross-Compiler for the ACME Cross-Assembler.")
|
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("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()
|
fmt.Println()
|
||||||
|
|
||||||
inFile := flag.String("in", "", "input source file (required)")
|
inFile := flag.String("in", "", "input source file (required)")
|
||||||
outFile := flag.String("out", "", "output assembly file (required)")
|
outFile := flag.String("out", "", "output assembly file (required)")
|
||||||
view := flag.String("view", "lines", "output mode: none, count, lines")
|
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
|
|
||||||
if *inFile == "" || *outFile == "" {
|
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()
|
flag.Usage()
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
var showMode ShowMode
|
if err := run(*inFile, *outFile); err != nil {
|
||||||
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 _, ok := err.(preproc.HaltError); ok {
|
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)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fmt.Println("Compilation successful.")
|
||||||
}
|
}
|
||||||
|
|
||||||
func run(inFile, outFile string, showMode ShowMode) error {
|
func run(inFile, outFile string) error {
|
||||||
initSharedObjects()
|
// Preprocess
|
||||||
return compile(inFile, outFile, showMode)
|
lines, pragma, err := preproc.PreProcess(inFile)
|
||||||
}
|
|
||||||
|
|
||||||
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)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("preprocessor failed: %w", err)
|
return fmt.Errorf("preprocessing failed: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
asmSource := make([]string, 0, len(lines)*2)
|
fmt.Printf("Preprocessed %d lines\n", len(lines))
|
||||||
lineCount := 0
|
|
||||||
|
|
||||||
for i := 0; i < len(lines); {
|
// Create compiler and register commands
|
||||||
line := lines[i]
|
comp := compiler.NewCompiler(pragma)
|
||||||
lineCount++
|
registerCommands(comp)
|
||||||
|
|
||||||
displayProgress(showMode, lineCount, line.Text)
|
// Compile
|
||||||
|
asmLines, err := comp.Compile(lines)
|
||||||
// Handle inline ASM blocks
|
if err != nil {
|
||||||
if isAsmStart(line) {
|
return fmt.Errorf("compilation failed: %w", err)
|
||||||
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++
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
fmt.Printf("Generated %d lines of assembly\n", len(asmLines))
|
||||||
VarList.WriteDownTypesToAsm(&asmSource)
|
|
||||||
ConstStrHandler.GenerateConstStrDecls(&asmSource)
|
|
||||||
VarList.WriteAbsVars(&asmSource)
|
|
||||||
VarList.WriteConstants(&asmSource)
|
|
||||||
*/
|
|
||||||
|
|
||||||
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)
|
return fmt.Errorf("failed to write output: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if showMode != ShowNone {
|
|
||||||
fmt.Println("\ndone.")
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func displayProgress(mode ShowMode, lineCount int, text string) {
|
func registerCommands(comp *compiler.Compiler) {
|
||||||
switch mode {
|
// Register all command handlers here
|
||||||
case ShowLineCount:
|
// This is the single place where all commands are wired up
|
||||||
fmt.Printf("\rCompiling line: %d", lineCount)
|
|
||||||
case ShowLineView:
|
comp.Registry().Register(&commands.ByteCommand{})
|
||||||
fmt.Println(text)
|
|
||||||
}
|
// 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 {
|
func writeOutput(filename string, lines []string) error {
|
||||||
//return len(line.Tokens) == 1 && strings.ToUpper(line.Tokens[0]) == "ASM"
|
return os.WriteFile(filename, []byte(strings.Join(lines, "\n")+"\n"), 0644)
|
||||||
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
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue