Added conststr + tests for handling constant strings.
This commit is contained in:
parent
134dbd1d3c
commit
8b4c7dc0d4
3 changed files with 267 additions and 0 deletions
74
internal/compiler/conststr.go
Normal file
74
internal/compiler/conststr.go
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
package compiler
|
||||
|
||||
import "c65gm/internal/preproc"
|
||||
|
||||
const (
|
||||
asmStringDeclStart = " !raw "
|
||||
asmStringDeclEnd = " !8 0"
|
||||
asmCBMStringDeclStart = " !pet "
|
||||
asmCBMStringDeclEnd = " !8 0"
|
||||
)
|
||||
|
||||
type ConstantString struct {
|
||||
label string
|
||||
value string
|
||||
compressable bool
|
||||
useCBMStrings bool
|
||||
}
|
||||
|
||||
type ConstantStringHandler struct {
|
||||
strings []*ConstantString
|
||||
compressMap map[string]string
|
||||
}
|
||||
|
||||
func NewConstantStringHandler() *ConstantStringHandler {
|
||||
return &ConstantStringHandler{
|
||||
strings: make([]*ConstantString, 0),
|
||||
compressMap: make(map[string]string),
|
||||
}
|
||||
}
|
||||
|
||||
func (h *ConstantStringHandler) AddConstStr(label, value string, compress bool, ps preproc.PragmaSet) string {
|
||||
useCBM := ps.GetPragma("_P_USE_CBM_STRINGS") != ""
|
||||
|
||||
if compress {
|
||||
if existingLabel, exists := h.compressMap[value]; exists {
|
||||
return existingLabel
|
||||
}
|
||||
}
|
||||
|
||||
cs := &ConstantString{
|
||||
label: label,
|
||||
value: value,
|
||||
compressable: compress,
|
||||
useCBMStrings: useCBM,
|
||||
}
|
||||
|
||||
h.strings = append(h.strings, cs)
|
||||
|
||||
if compress {
|
||||
h.compressMap[value] = label
|
||||
}
|
||||
|
||||
return label
|
||||
}
|
||||
|
||||
func (h *ConstantStringHandler) GenerateConstStrDecls() []string {
|
||||
result := make([]string, 0, len(h.strings)*3)
|
||||
|
||||
for _, cs := range h.strings {
|
||||
startStr := asmStringDeclStart
|
||||
endStr := asmStringDeclEnd
|
||||
|
||||
if cs.useCBMStrings {
|
||||
startStr = asmCBMStringDeclStart
|
||||
endStr = asmCBMStringDeclEnd
|
||||
}
|
||||
|
||||
result = append(result, cs.label)
|
||||
result = append(result, startStr+cs.value)
|
||||
result = append(result, endStr)
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
188
internal/compiler/conststr_test.go
Normal file
188
internal/compiler/conststr_test.go
Normal file
|
|
@ -0,0 +1,188 @@
|
|||
package compiler
|
||||
|
||||
import (
|
||||
"c65gm/internal/preproc"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestAddConstStr_NonCompressable(t *testing.T) {
|
||||
h := NewConstantStringHandler()
|
||||
ps := preproc.PragmaSet{}
|
||||
|
||||
label1 := h.AddConstStr("lbl1", "hello", false, ps)
|
||||
label2 := h.AddConstStr("lbl2", "hello", false, ps)
|
||||
|
||||
if label1 != "lbl1" {
|
||||
t.Errorf("Expected label1 = lbl1, got %s", label1)
|
||||
}
|
||||
if label2 != "lbl2" {
|
||||
t.Errorf("Expected label2 = lbl2, got %s", label2)
|
||||
}
|
||||
if len(h.strings) != 2 {
|
||||
t.Errorf("Expected 2 strings, got %d", len(h.strings))
|
||||
}
|
||||
}
|
||||
|
||||
func TestAddConstStr_CompressableDedupe(t *testing.T) {
|
||||
h := NewConstantStringHandler()
|
||||
ps := preproc.PragmaSet{}
|
||||
|
||||
label1 := h.AddConstStr("lbl1", "hello", true, ps)
|
||||
label2 := h.AddConstStr("lbl2", "hello", true, ps)
|
||||
|
||||
if label1 != "lbl1" {
|
||||
t.Errorf("Expected label1 = lbl1, got %s", label1)
|
||||
}
|
||||
if label2 != "lbl1" {
|
||||
t.Errorf("Expected label2 = lbl1 (deduped), got %s", label2)
|
||||
}
|
||||
if len(h.strings) != 1 {
|
||||
t.Errorf("Expected 1 string after dedup, got %d", len(h.strings))
|
||||
}
|
||||
}
|
||||
|
||||
func TestAddConstStr_CompressableNoDedupe(t *testing.T) {
|
||||
h := NewConstantStringHandler()
|
||||
ps := preproc.PragmaSet{}
|
||||
|
||||
label1 := h.AddConstStr("lbl1", "hello", true, ps)
|
||||
label2 := h.AddConstStr("lbl2", "world", true, ps)
|
||||
|
||||
if label1 != "lbl1" {
|
||||
t.Errorf("Expected label1 = lbl1, got %s", label1)
|
||||
}
|
||||
if label2 != "lbl2" {
|
||||
t.Errorf("Expected label2 = lbl2, got %s", label2)
|
||||
}
|
||||
if len(h.strings) != 2 {
|
||||
t.Errorf("Expected 2 strings (different values), got %d", len(h.strings))
|
||||
}
|
||||
}
|
||||
|
||||
func TestGenerateConstStrDecls_Normal(t *testing.T) {
|
||||
h := NewConstantStringHandler()
|
||||
ps := preproc.PragmaSet{}
|
||||
|
||||
h.AddConstStr("str1", "\"hello\"", false, ps)
|
||||
|
||||
result := h.GenerateConstStrDecls()
|
||||
|
||||
expected := []string{
|
||||
"str1",
|
||||
" !raw \"hello\"",
|
||||
" !8 0",
|
||||
}
|
||||
|
||||
if len(result) != len(expected) {
|
||||
t.Fatalf("Expected %d lines, got %d", len(expected), len(result))
|
||||
}
|
||||
|
||||
for i, exp := range expected {
|
||||
if result[i] != exp {
|
||||
t.Errorf("Line %d: expected %q, got %q", i, exp, result[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestGenerateConstStrDecls_CBM(t *testing.T) {
|
||||
h := NewConstantStringHandler()
|
||||
ps := mockPragmaSet(map[string]string{"_P_USE_CBM_STRINGS": "1"})
|
||||
|
||||
h.AddConstStr("str1", "\"hello\"", false, ps)
|
||||
|
||||
result := h.GenerateConstStrDecls()
|
||||
|
||||
expected := []string{
|
||||
"str1",
|
||||
" !pet \"hello\"",
|
||||
" !8 0",
|
||||
}
|
||||
|
||||
if len(result) != len(expected) {
|
||||
t.Fatalf("Expected %d lines, got %d", len(expected), len(result))
|
||||
}
|
||||
|
||||
for i, exp := range expected {
|
||||
if result[i] != exp {
|
||||
t.Errorf("Line %d: expected %q, got %q", i, exp, result[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestGenerateConstStrDecls_MixedPragmas(t *testing.T) {
|
||||
h := NewConstantStringHandler()
|
||||
psNormal := preproc.PragmaSet{}
|
||||
psCBM := mockPragmaSet(map[string]string{"_P_USE_CBM_STRINGS": "1"})
|
||||
|
||||
h.AddConstStr("str1", "\"hello\"", false, psNormal)
|
||||
h.AddConstStr("str2", "\"world\"", false, psCBM)
|
||||
|
||||
result := h.GenerateConstStrDecls()
|
||||
|
||||
expected := []string{
|
||||
"str1",
|
||||
" !raw \"hello\"",
|
||||
" !8 0",
|
||||
"str2",
|
||||
" !pet \"world\"",
|
||||
" !8 0",
|
||||
}
|
||||
|
||||
if len(result) != len(expected) {
|
||||
t.Fatalf("Expected %d lines, got %d", len(expected), len(result))
|
||||
}
|
||||
|
||||
for i, exp := range expected {
|
||||
if result[i] != exp {
|
||||
t.Errorf("Line %d: expected %q, got %q", i, exp, result[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestGenerateConstStrDecls_Order(t *testing.T) {
|
||||
h := NewConstantStringHandler()
|
||||
ps := preproc.PragmaSet{}
|
||||
|
||||
h.AddConstStr("str1", "\"first\"", false, ps)
|
||||
h.AddConstStr("str2", "\"second\"", false, ps)
|
||||
h.AddConstStr("str3", "\"third\"", false, ps)
|
||||
|
||||
result := h.GenerateConstStrDecls()
|
||||
|
||||
if len(result) != 9 {
|
||||
t.Fatalf("Expected 9 lines, got %d", len(result))
|
||||
}
|
||||
|
||||
if result[0] != "str1" || result[3] != "str2" || result[6] != "str3" {
|
||||
t.Errorf("Labels not in correct order")
|
||||
}
|
||||
}
|
||||
|
||||
func TestAddConstStr_CompressOnlyDedupesSameValue(t *testing.T) {
|
||||
h := NewConstantStringHandler()
|
||||
ps := preproc.PragmaSet{}
|
||||
|
||||
h.AddConstStr("lbl1", "hello", true, ps)
|
||||
h.AddConstStr("lbl2", "hello", true, ps)
|
||||
h.AddConstStr("lbl3", "world", true, ps)
|
||||
h.AddConstStr("lbl4", "hello", true, ps)
|
||||
|
||||
if len(h.strings) != 2 {
|
||||
t.Errorf("Expected 2 unique strings, got %d", len(h.strings))
|
||||
}
|
||||
|
||||
if len(h.compressMap) != 2 {
|
||||
t.Errorf("Expected 2 entries in compress map, got %d", len(h.compressMap))
|
||||
}
|
||||
|
||||
if h.compressMap["hello"] != "lbl1" {
|
||||
t.Errorf("Expected hello -> lbl1, got %s", h.compressMap["hello"])
|
||||
}
|
||||
if h.compressMap["world"] != "lbl3" {
|
||||
t.Errorf("Expected world -> lbl3, got %s", h.compressMap["world"])
|
||||
}
|
||||
}
|
||||
|
||||
func mockPragmaSet(m map[string]string) preproc.PragmaSet {
|
||||
return preproc.NewPragmaSet(m)
|
||||
}
|
||||
|
|
@ -13,6 +13,11 @@ func (ps PragmaSet) GetPragma(name string) string {
|
|||
return ""
|
||||
}
|
||||
|
||||
// For mocking
|
||||
func NewPragmaSet(m map[string]string) PragmaSet {
|
||||
return PragmaSet{m: m}
|
||||
}
|
||||
|
||||
// Pragma manages an immutable stack of PragmaSet snapshots.
|
||||
type Pragma struct {
|
||||
pragmaSetStack []PragmaSet
|
||||
|
|
|
|||
Loading…
Reference in a new issue