package commands import ( "strings" "testing" "c65gm/internal/compiler" "c65gm/internal/preproc" ) func TestPeekOldSyntax(t *testing.T) { tests := []struct { name string line string setupVars func(*compiler.SymbolTable) wantAsm []string }{ { name: "peek constant address to byte", line: "PEEK 1024 GIVING result", setupVars: func(st *compiler.SymbolTable) { st.AddVar("result", "", compiler.KindByte, 0) }, wantAsm: []string{ "\tlda 1024", "\tsta result", }, }, { name: "peek ZP pointer to byte", line: "PEEK ptr GIVING result", setupVars: func(st *compiler.SymbolTable) { st.AddAbsolute("ptr", "", compiler.KindWord, 0x80) st.AddVar("result", "", compiler.KindByte, 0) }, wantAsm: []string{ "\tldy #0", "\tlda (ptr),y", "\tsta result", }, }, { name: "peek ZP pointer with offset to byte", line: "PEEK ptr[5] GIVING result", setupVars: func(st *compiler.SymbolTable) { st.AddAbsolute("ptr", "", compiler.KindWord, 0x80) st.AddVar("result", "", compiler.KindByte, 0) }, wantAsm: []string{ "\tldy #5", "\tlda (ptr),y", "\tsta result", }, }, { name: "peek with -> separator", line: "PEEK 2048 -> result", setupVars: func(st *compiler.SymbolTable) { st.AddVar("result", "", compiler.KindByte, 0) }, wantAsm: []string{ "\tlda 2048", "\tsta result", }, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { pragma := preproc.NewPragma() ctx := compiler.NewCompilerContext(pragma) tt.setupVars(ctx.SymbolTable) cmd := &PeekCommand{} line := preproc.Line{ Text: tt.line, Kind: preproc.Source, PragmaSetIndex: pragma.GetCurrentPragmaSetIndex(), } if err := cmd.Interpret(line, ctx); err != nil { t.Fatalf("Interpret() error = %v", err) } asm, err := cmd.Generate(ctx) if err != nil { t.Fatalf("Generate() error = %v", err) } if !equalAsm(asm, tt.wantAsm) { t.Errorf("Generate() mismatch\ngot:\n%s\nwant:\n%s", strings.Join(asm, "\n"), strings.Join(tt.wantAsm, "\n")) } }) } } func TestPeekNewSyntax(t *testing.T) { tests := []struct { name string line string setupVars func(*compiler.SymbolTable) wantAsm []string }{ { name: "new syntax constant address to byte", line: "result = PEEK 1024", setupVars: func(st *compiler.SymbolTable) { st.AddVar("result", "", compiler.KindByte, 0) }, wantAsm: []string{ "\tlda 1024", "\tsta result", }, }, { name: "new syntax ZP pointer to byte", line: "result = PEEK ptr", setupVars: func(st *compiler.SymbolTable) { st.AddAbsolute("ptr", "", compiler.KindWord, 0x80) st.AddVar("result", "", compiler.KindByte, 0) }, wantAsm: []string{ "\tldy #0", "\tlda (ptr),y", "\tsta result", }, }, { name: "new syntax ZP pointer with offset", line: "result = PEEK ptr[10]", setupVars: func(st *compiler.SymbolTable) { st.AddAbsolute("ptr", "", compiler.KindWord, 0x80) st.AddVar("result", "", compiler.KindByte, 0) }, wantAsm: []string{ "\tldy #10", "\tlda (ptr),y", "\tsta result", }, }, { name: "new syntax ZP pointer with variable offset", line: "result = PEEK ptr[idx]", setupVars: func(st *compiler.SymbolTable) { st.AddAbsolute("ptr", "", compiler.KindWord, 0x80) st.AddVar("idx", "", compiler.KindByte, 0) st.AddVar("result", "", compiler.KindByte, 0) }, wantAsm: []string{ "\tldy idx", "\tlda (ptr),y", "\tsta result", }, }, { name: "new syntax to word destination", line: "result = PEEK 2048", setupVars: func(st *compiler.SymbolTable) { st.AddVar("result", "", compiler.KindWord, 0) }, wantAsm: []string{ "\tlda 2048", "\tsta result", "\tlda #0", "\tsta result+1", }, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { pragma := preproc.NewPragma() ctx := compiler.NewCompilerContext(pragma) tt.setupVars(ctx.SymbolTable) cmd := &PeekCommand{} line := preproc.Line{ Text: tt.line, Kind: preproc.Source, PragmaSetIndex: pragma.GetCurrentPragmaSetIndex(), } if err := cmd.Interpret(line, ctx); err != nil { t.Fatalf("Interpret() error = %v", err) } asm, err := cmd.Generate(ctx) if err != nil { t.Fatalf("Generate() error = %v", err) } if !equalAsm(asm, tt.wantAsm) { t.Errorf("Generate() mismatch\ngot:\n%s\nwant:\n%s", strings.Join(asm, "\n"), strings.Join(tt.wantAsm, "\n")) } }) } } func TestPeekWNewSyntax(t *testing.T) { tests := []struct { name string line string setupVars func(*compiler.SymbolTable) wantAsm []string }{ { name: "new syntax constant address", line: "result = PEEKW 1024", setupVars: func(st *compiler.SymbolTable) { st.AddVar("result", "", compiler.KindWord, 0) }, wantAsm: []string{ "\tlda 1024", "\tsta result", "\tlda 1025", "\tsta result+1", }, }, { name: "new syntax ZP pointer", line: "result = PEEKW ptr", setupVars: func(st *compiler.SymbolTable) { st.AddAbsolute("ptr", "", compiler.KindWord, 0x80) st.AddVar("result", "", compiler.KindWord, 0) }, wantAsm: []string{ "\tldy #0", "\tlda (ptr),y", "\tsta result", "\tiny", "\tlda (ptr),y", "\tsta result+1", }, }, { name: "new syntax ZP pointer with offset", line: "result = PEEKW ptr[8]", setupVars: func(st *compiler.SymbolTable) { st.AddAbsolute("ptr", "", compiler.KindWord, 0x80) st.AddVar("result", "", compiler.KindWord, 0) }, wantAsm: []string{ "\tldy #8", "\tlda (ptr),y", "\tsta result", "\tiny", "\tlda (ptr),y", "\tsta result+1", }, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { pragma := preproc.NewPragma() ctx := compiler.NewCompilerContext(pragma) tt.setupVars(ctx.SymbolTable) cmd := &PeekWCommand{} line := preproc.Line{ Text: tt.line, Kind: preproc.Source, PragmaSetIndex: pragma.GetCurrentPragmaSetIndex(), } if err := cmd.Interpret(line, ctx); err != nil { t.Fatalf("Interpret() error = %v", err) } asm, err := cmd.Generate(ctx) if err != nil { t.Fatalf("Generate() error = %v", err) } if !equalAsm(asm, tt.wantAsm) { t.Errorf("Generate() mismatch\ngot:\n%s\nwant:\n%s", strings.Join(asm, "\n"), strings.Join(tt.wantAsm, "\n")) } }) } } func TestPeekErrors(t *testing.T) { tests := []struct { name string line string setupVars func(*compiler.SymbolTable) wantError string }{ { name: "unknown destination", line: "PEEK 1024 GIVING unknown", setupVars: func(st *compiler.SymbolTable) { }, wantError: "unknown destination variable", }, { name: "constant destination", line: "PEEK 1024 GIVING CONST", setupVars: func(st *compiler.SymbolTable) { st.AddConst("CONST", "", compiler.KindByte, 100) }, wantError: "cannot PEEK into constant", }, { name: "new syntax unknown destination", line: "unknown = PEEK 1024", setupVars: func(st *compiler.SymbolTable) { }, wantError: "unknown destination variable", }, { name: "new syntax constant destination", line: "CONST = PEEK 1024", setupVars: func(st *compiler.SymbolTable) { st.AddConst("CONST", "", compiler.KindByte, 100) }, wantError: "cannot PEEK into constant", }, { name: "offset without ZP pointer old syntax", line: "PEEK addr[5] GIVING result", setupVars: func(st *compiler.SymbolTable) { st.AddVar("addr", "", compiler.KindWord, 0) st.AddVar("result", "", compiler.KindByte, 0) }, wantError: "offset", }, { name: "offset without ZP pointer new syntax", line: "result = PEEK addr[5]", setupVars: func(st *compiler.SymbolTable) { st.AddVar("addr", "", compiler.KindWord, 0) st.AddVar("result", "", compiler.KindByte, 0) }, wantError: "offset", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { pragma := preproc.NewPragma() ctx := compiler.NewCompilerContext(pragma) tt.setupVars(ctx.SymbolTable) cmd := &PeekCommand{} line := preproc.Line{ Text: tt.line, Kind: preproc.Source, PragmaSetIndex: pragma.GetCurrentPragmaSetIndex(), } err := cmd.Interpret(line, ctx) if err == nil { t.Fatal("Expected error but got none") } if !strings.Contains(err.Error(), tt.wantError) { t.Errorf("Error message mismatch\ngot: %v\nwant substring: %s", err, tt.wantError) } }) } } func TestPeekWErrors(t *testing.T) { tests := []struct { name string line string setupVars func(*compiler.SymbolTable) wantError string }{ { name: "byte destination", line: "PEEKW 1024 GIVING result", setupVars: func(st *compiler.SymbolTable) { st.AddVar("result", "", compiler.KindByte, 0) }, wantError: "must be word type", }, { name: "new syntax byte destination", line: "result = PEEKW 1024", setupVars: func(st *compiler.SymbolTable) { st.AddVar("result", "", compiler.KindByte, 0) }, wantError: "must be word type", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { pragma := preproc.NewPragma() ctx := compiler.NewCompilerContext(pragma) tt.setupVars(ctx.SymbolTable) cmd := &PeekWCommand{} line := preproc.Line{ Text: tt.line, Kind: preproc.Source, PragmaSetIndex: pragma.GetCurrentPragmaSetIndex(), } err := cmd.Interpret(line, ctx) if err == nil { t.Fatal("Expected error but got none") } if !strings.Contains(err.Error(), tt.wantError) { t.Errorf("Error message mismatch\ngot: %v\nwant substring: %s", err, tt.wantError) } }) } }