diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..d1a98fa --- /dev/null +++ b/.gitignore @@ -0,0 +1,31 @@ +# Binaries +*.exe +*.exe~ +*.dll +*.so +*.dylib +*.test + +# Build output +bin/ +build/ +dist/ + +# Go workspace +*.out + +# Dependency directories (if any) +vendor/ + +# Go coverage files +coverage.out + +# IDE: GoLand / IntelliJ IDEA +.idea/ +*.iml +out/ + +# Logs and temp files +*.log +*.tmp +.DS_Store diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..eee1c81 --- /dev/null +++ b/go.mod @@ -0,0 +1,5 @@ +module c65gm + +go 1.25.1 + +require github.com/armon/go-radix v1.0.0 // indirect diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..09f0f80 --- /dev/null +++ b/go.sum @@ -0,0 +1,2 @@ +github.com/armon/go-radix v1.0.0 h1:F4z6KzEeeQIMeLFa97iZU6vupzoecKdU5TX24SNppXI= +github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= diff --git a/internal/preproc/definelist.go b/internal/preproc/definelist.go new file mode 100644 index 0000000..d374cf4 --- /dev/null +++ b/internal/preproc/definelist.go @@ -0,0 +1,57 @@ +package preproc + +import ( + "strings" + "unicode/utf8" + + "github.com/armon/go-radix" +) + +type DefineList struct { + tree *radix.Tree +} + +func NewDefineList() *DefineList { return &DefineList{tree: radix.New()} } + +// Add inserts or updates a define->value mapping. +func (d *DefineList) Add(define, value string) { d.tree.Insert(define, value) } + +// Delete removes a define. Returns true if it existed. +func (d *DefineList) Delete(define string) bool { + _, ok := d.tree.Delete(define) + return ok +} + +// Defined reports whether a define exists. +func (d *DefineList) Defined(s string) bool { + _, ok := d.tree.Get(s) + return ok +} + +// typed wrapper to avoid type assertions sprinkled in hot code +func (d *DefineList) longestPrefixString(s string) (key string, val string, ok bool) { + k, v, ok := d.tree.LongestPrefix(s) + if !ok { + return "", "", false + } + return k, v.(string), true +} + +// ReplaceDefines performs a single-pass, longest-prefix replacement. +// Case-sensitive. Matches anywhere. No recursive re-expansion. +func (d *DefineList) ReplaceDefines(s string) string { + var b strings.Builder + b.Grow(len(s)) // baseline; grows if expansions occur + + for i := 0; i < len(s); { + if key, val, ok := d.longestPrefixString(s[i:]); ok && key != "" { + b.WriteString(val) + i += len(key) // advance by matched bytes (UTF-8-safe) + continue + } + _, size := utf8.DecodeRuneInString(s[i:]) + b.WriteString(s[i : i+size]) + i += size + } + return b.String() +} diff --git a/main.go b/main.go new file mode 100644 index 0000000..7ac9c86 --- /dev/null +++ b/main.go @@ -0,0 +1,31 @@ +package main + +import ( + "c65gm/internal/preproc" + "fmt" +) + +// put the DefineList type in another file (same package) or import it; +// here I assume definelist.go is in its own package "definelist". +// If it's in the same package main, just call New() directly. + +func main() { + defs := preproc.NewDefineList() // <-- important + + defs.Add("ARRAY_MIN", "0") + defs.Add("ARRAY_MAX", "100") + defs.Add("ARRAY_MAX_PAX", "1000") + defs.Add("VERSION", "1.0") + defs.Add("π", "3.14159") + + src := `for i := ARRAY_MIN to ARRAY_MAX_PAX do + writeln(VERSION); + writeln(ARRAY_MAX); + const PI = π;` + + fmt.Println("Original:\n" + src) + fmt.Println("\nReplaced:\n" + defs.ReplaceDefines(src)) + defs.Delete("ARRAY_MAX_PAX") + fmt.Println("\nReplaced (after .Delete):\n" + defs.ReplaceDefines(src)) + +}