Fixed disabling some pragmas and test to go with them
This commit is contained in:
parent
2d2d665ebd
commit
51b9476a85
9 changed files with 127 additions and 7 deletions
|
|
@ -173,7 +173,7 @@ func (c *GosubCommand) Generate(ctx *compiler.CompilerContext) ([]string, error)
|
||||||
|
|
||||||
// Check for immutable code pragma when using variable target
|
// Check for immutable code pragma when using variable target
|
||||||
if c.isVar {
|
if c.isVar {
|
||||||
if c.pragmaSet.GetPragma("_P_USE_IMMUTABLE_CODE") != "" {
|
if c.pragmaSet.GetPragma("_P_USE_IMMUTABLE_CODE") != "" && c.pragmaSet.GetPragma("_P_USE_IMMUTABLE_CODE") != "0" {
|
||||||
return nil, fmt.Errorf("GOSUB: USE_IMMUTABLE_CODE pragma set but variable target requires self-modifying code")
|
return nil, fmt.Errorf("GOSUB: USE_IMMUTABLE_CODE pragma set but variable target requires self-modifying code")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -196,7 +196,7 @@ func (c *PeekCommand) Generate(ctx *compiler.CompilerContext) ([]string, error)
|
||||||
|
|
||||||
// Check for immutable code pragma when using self-modifying code
|
// Check for immutable code pragma when using self-modifying code
|
||||||
if c.isAddrVar && !c.isZPPointer {
|
if c.isAddrVar && !c.isZPPointer {
|
||||||
if c.pragmaSet.GetPragma("_P_USE_IMMUTABLE_CODE") != "" {
|
if c.pragmaSet.GetPragma("_P_USE_IMMUTABLE_CODE") != "" && c.pragmaSet.GetPragma("_P_USE_IMMUTABLE_CODE") != "0" {
|
||||||
return nil, fmt.Errorf("PEEK: USE_IMMUTABLE_CODE pragma set but construct requires self-modifying code (consider using zero page word for address)")
|
return nil, fmt.Errorf("PEEK: USE_IMMUTABLE_CODE pragma set but construct requires self-modifying code (consider using zero page word for address)")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -414,3 +414,97 @@ func TestPeekWErrors(t *testing.T) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestPeekImmutableCodePragma(t *testing.T) {
|
||||||
|
// Test that _P_USE_IMMUTABLE_CODE pragma with value "1" causes error for variable address
|
||||||
|
t.Run("Pragma enabled with 1", func(t *testing.T) {
|
||||||
|
pragma := preproc.NewPragma()
|
||||||
|
pragma.AddPragma("_P_USE_IMMUTABLE_CODE", "1")
|
||||||
|
ctx := compiler.NewCompilerContext(pragma)
|
||||||
|
|
||||||
|
// Add a non-ZP word variable for address
|
||||||
|
ctx.SymbolTable.AddVar("addr", "", compiler.KindWord, 0, preproc.Line{Filename: "test.c65", LineNo: 1})
|
||||||
|
ctx.SymbolTable.AddVar("result", "", compiler.KindByte, 0, preproc.Line{Filename: "test.c65", LineNo: 1})
|
||||||
|
|
||||||
|
cmd := &PeekCommand{}
|
||||||
|
line := preproc.Line{
|
||||||
|
Text: "PEEK addr GIVING result",
|
||||||
|
Kind: preproc.Source,
|
||||||
|
PragmaSetIndex: pragma.GetCurrentPragmaSetIndex(),
|
||||||
|
}
|
||||||
|
|
||||||
|
// First interpret the command
|
||||||
|
err := cmd.Interpret(line, ctx)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Interpret() error = %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Then generate code - this is where the pragma check happens
|
||||||
|
_, err = cmd.Generate(ctx)
|
||||||
|
if err == nil {
|
||||||
|
t.Error("Expected error when _P_USE_IMMUTABLE_CODE=1 and using variable address, got nil")
|
||||||
|
} else if !strings.Contains(err.Error(), "USE_IMMUTABLE_CODE pragma set") {
|
||||||
|
t.Errorf("Expected error about USE_IMMUTABLE_CODE pragma, got: %v", err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// Test that _P_USE_IMMUTABLE_CODE pragma with value "0" does NOT cause error
|
||||||
|
t.Run("Pragma disabled with 0", func(t *testing.T) {
|
||||||
|
pragma := preproc.NewPragma()
|
||||||
|
pragma.AddPragma("_P_USE_IMMUTABLE_CODE", "0")
|
||||||
|
ctx := compiler.NewCompilerContext(pragma)
|
||||||
|
|
||||||
|
// Add a non-ZP word variable for address
|
||||||
|
ctx.SymbolTable.AddVar("addr", "", compiler.KindWord, 0, preproc.Line{Filename: "test.c65", LineNo: 1})
|
||||||
|
ctx.SymbolTable.AddVar("result", "", compiler.KindByte, 0, preproc.Line{Filename: "test.c65", LineNo: 1})
|
||||||
|
|
||||||
|
cmd := &PeekCommand{}
|
||||||
|
line := preproc.Line{
|
||||||
|
Text: "PEEK addr GIVING result",
|
||||||
|
Kind: preproc.Source,
|
||||||
|
PragmaSetIndex: pragma.GetCurrentPragmaSetIndex(),
|
||||||
|
}
|
||||||
|
|
||||||
|
// First interpret the command
|
||||||
|
err := cmd.Interpret(line, ctx)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Interpret() error = %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Then generate code - this is where the pragma check happens
|
||||||
|
_, err = cmd.Generate(ctx)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Expected no error when _P_USE_IMMUTABLE_CODE=0, got: %v", err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// Test that _P_USE_IMMUTABLE_CODE pragma not set does NOT cause error
|
||||||
|
t.Run("Pragma not set", func(t *testing.T) {
|
||||||
|
pragma := preproc.NewPragma()
|
||||||
|
// Don't add the pragma at all
|
||||||
|
ctx := compiler.NewCompilerContext(pragma)
|
||||||
|
|
||||||
|
// Add a non-ZP word variable for address
|
||||||
|
ctx.SymbolTable.AddVar("addr", "", compiler.KindWord, 0, preproc.Line{Filename: "test.c65", LineNo: 1})
|
||||||
|
ctx.SymbolTable.AddVar("result", "", compiler.KindByte, 0, preproc.Line{Filename: "test.c65", LineNo: 1})
|
||||||
|
|
||||||
|
cmd := &PeekCommand{}
|
||||||
|
line := preproc.Line{
|
||||||
|
Text: "PEEK addr GIVING result",
|
||||||
|
Kind: preproc.Source,
|
||||||
|
PragmaSetIndex: pragma.GetCurrentPragmaSetIndex(),
|
||||||
|
}
|
||||||
|
|
||||||
|
// First interpret the command
|
||||||
|
err := cmd.Interpret(line, ctx)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Interpret() error = %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Then generate code - this is where the pragma check happens
|
||||||
|
_, err = cmd.Generate(ctx)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Expected no error when _P_USE_IMMUTABLE_CODE not set, got: %v", err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -240,7 +240,7 @@ func (c *PeekWCommand) Generate(ctx *compiler.CompilerContext) ([]string, error)
|
||||||
// Case 3: Word variable (self-modifying code)
|
// Case 3: Word variable (self-modifying code)
|
||||||
if c.isAddrVar && c.addrVarKind == compiler.KindWord {
|
if c.isAddrVar && c.addrVarKind == compiler.KindWord {
|
||||||
// Check for immutable code pragma
|
// Check for immutable code pragma
|
||||||
if c.pragmaSet.GetPragma("_P_USE_IMMUTABLE_CODE") != "" {
|
if c.pragmaSet.GetPragma("_P_USE_IMMUTABLE_CODE") != "" && c.pragmaSet.GetPragma("_P_USE_IMMUTABLE_CODE") != "0" {
|
||||||
return nil, fmt.Errorf("PEEKW: USE_IMMUTABLE_CODE pragma set but construct requires self-modifying code (consider using zero page word for address)")
|
return nil, fmt.Errorf("PEEKW: USE_IMMUTABLE_CODE pragma set but construct requires self-modifying code (consider using zero page word for address)")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -178,7 +178,7 @@ func (c *PokeCommand) Generate(ctx *compiler.CompilerContext) ([]string, error)
|
||||||
|
|
||||||
// Check for immutable code pragma when using self-modifying code
|
// Check for immutable code pragma when using self-modifying code
|
||||||
if c.isAddrVar && !c.isZPPointer {
|
if c.isAddrVar && !c.isZPPointer {
|
||||||
if c.pragmaSet.GetPragma("_P_USE_IMMUTABLE_CODE") != "" {
|
if c.pragmaSet.GetPragma("_P_USE_IMMUTABLE_CODE") != "" && c.pragmaSet.GetPragma("_P_USE_IMMUTABLE_CODE") != "0" {
|
||||||
return nil, fmt.Errorf("POKE: USE_IMMUTABLE_CODE pragma set but construct requires self-modifying code (consider using zero page word for address)")
|
return nil, fmt.Errorf("POKE: USE_IMMUTABLE_CODE pragma set but construct requires self-modifying code (consider using zero page word for address)")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -180,7 +180,7 @@ func (c *PokeWCommand) Generate(ctx *compiler.CompilerContext) ([]string, error)
|
||||||
|
|
||||||
// Check for immutable code pragma when using self-modifying code
|
// Check for immutable code pragma when using self-modifying code
|
||||||
if c.isAddrVar && c.addrVarKind == compiler.KindWord && !c.isZPPointer {
|
if c.isAddrVar && c.addrVarKind == compiler.KindWord && !c.isZPPointer {
|
||||||
if c.pragmaSet.GetPragma("_P_USE_IMMUTABLE_CODE") != "" {
|
if c.pragmaSet.GetPragma("_P_USE_IMMUTABLE_CODE") != "" && c.pragmaSet.GetPragma("_P_USE_IMMUTABLE_CODE") != "0" {
|
||||||
return nil, fmt.Errorf("POKEW: USE_IMMUTABLE_CODE pragma set but construct requires self-modifying code (consider using zero page word for address)")
|
return nil, fmt.Errorf("POKEW: USE_IMMUTABLE_CODE pragma set but construct requires self-modifying code (consider using zero page word for address)")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,7 @@ func NewConstantStringHandler() *ConstantStringHandler {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *ConstantStringHandler) AddConstStr(label, value string, compress bool, ps preproc.PragmaSet) string {
|
func (h *ConstantStringHandler) AddConstStr(label, value string, compress bool, ps preproc.PragmaSet) string {
|
||||||
useCBM := ps.GetPragma("_P_USE_CBM_STRINGS") != ""
|
useCBM := ps.GetPragma("_P_USE_CBM_STRINGS") != "" && ps.GetPragma("_P_USE_CBM_STRINGS") != "0"
|
||||||
|
|
||||||
if compress {
|
if compress {
|
||||||
if existingLabel, exists := h.compressMap[value]; exists {
|
if existingLabel, exists := h.compressMap[value]; exists {
|
||||||
|
|
|
||||||
|
|
@ -109,6 +109,31 @@ func TestGenerateConstStrDecls_CBM(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestGenerateConstStrDecls_CBM_DisabledWithZero(t *testing.T) {
|
||||||
|
h := NewConstantStringHandler()
|
||||||
|
ps := mockPragmaSet(map[string]string{"_P_USE_CBM_STRINGS": "0"})
|
||||||
|
|
||||||
|
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_MixedPragmas(t *testing.T) {
|
func TestGenerateConstStrDecls_MixedPragmas(t *testing.T) {
|
||||||
h := NewConstantStringHandler()
|
h := NewConstantStringHandler()
|
||||||
psNormal := preproc.PragmaSet{}
|
psNormal := preproc.PragmaSet{}
|
||||||
|
|
|
||||||
|
|
@ -241,11 +241,12 @@ Sets compiler pragmas (options).
|
||||||
- Prevents self-modifying code generation
|
- Prevents self-modifying code generation
|
||||||
- Required for ROM-based code
|
- Required for ROM-based code
|
||||||
- Errors on PEEK/POKE/GOSUB with variable addresses
|
- Errors on PEEK/POKE/GOSUB with variable addresses
|
||||||
|
- Value: any non-zero value enables
|
||||||
|
|
||||||
**_P_USE_CBM_STRINGS**
|
**_P_USE_CBM_STRINGS**
|
||||||
- Encodes strings in CBM PETSCII format
|
- Encodes strings in CBM PETSCII format
|
||||||
- Default: ASCII encoding
|
- Default: ASCII encoding
|
||||||
- Value: any non-empty value enables
|
- Value: any non-zero value enables
|
||||||
|
|
||||||
**_P_IGNORE_UNUSED**
|
**_P_IGNORE_UNUSED**
|
||||||
- Suppresses "variable declared but never used" warnings
|
- Suppresses "variable declared but never used" warnings
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue