Compare commits

...

5 commits

51 changed files with 1304 additions and 770 deletions

5
.gitignore vendored
View file

@ -33,3 +33,8 @@ c65gm
*.sym
*.prg
*.s
.cache/
.env
.local/
opencode-config/package-lock.json

167
AGENTS.md Normal file
View file

@ -0,0 +1,167 @@
# c65gm Agent Instructions
## Project Overview
c65gm is a high-level 6502 cross-compiler targeting the ACME Cross-Assembler for Commodore 64 and similar 6502-based platforms. It provides a more expressive language for writing 6502 assembly programs with modern programming constructs.
## Key Documentation Locations
### Primary Documentation
1. **README.md** - Project overview, building, usage, and requirements
2. **language.md** - Complete language reference with examples (1091 lines)
3. **syntax.md** - Syntax rules and preprocessor directives (646 lines)
4. **commands.md** - Complete command reference (776 lines)
### Code Structure
- **main.go** - Entry point and command-line interface
- **internal/compiler/** - Core compiler implementation
- **internal/preproc/** - Preprocessor implementation
- **internal/commands/** - Individual command implementations
- **internal/utils/** - Utility functions
- **lib/** - Standard library files for C64 development
- **examples/** - Example programs demonstrating language features
### Standard Library (lib/)
- **c64start.c65** - Required for C64 executables (BASIC loader setup)
- **c64defs.c65** - C64 hardware definitions (colors, addresses, etc.)
- **c64kernal.c65** - Kernal routine wrappers
- **c64scr.c65** - Screen manipulation functions
- **string.c65** - String handling functions
- **memlib.c65** - Memory management functions
- **fat16/** - FAT16 filesystem support
- **koalalib.c65** - Koala graphics support
## Language Features to Note
### Key Concepts
1. **Type system**: BYTE (8-bit) and WORD (16-bit) variables with scope resolution
2. **Functions**: Named functions with parameters and call graph analysis
3. **Control flow**: IF/ENDIF, WHILE/WEND, FOR loops, SWITCH/CASE
4. **Memory operations**: PEEK/POKE/PEEKW/POKEW with zero-page optimization
5. **Preprocessor**: File inclusion, macros, conditional compilation, Starlark scripting
6. **Optimizations**: Constant folding, self-assignment detection
7. **Safety features**: Compile-time detection of overlapping absolute addresses
### Important Syntax Rules
1. **No operator precedence**: Expressions evaluate strictly left-to-right
2. **Memory-mapped variables**: Use `@` to place variables at specific addresses
3. **Constants**: Prefer `BYTE CONST`/`WORD CONST` over `#DEFINE`
4. **Zero-page optimization**: Place frequently accessed pointers in zero page ($00-$FF)
5. **Include guards**: Always use `#IFNDEF` in library files
### Special Blocks
1. **ASM blocks**: Inline assembly code
2. **SCRIPT blocks**: Starlark code for compile-time code generation
3. **SCRIPT LIBRARY blocks**: Reusable Starlark functions
4. **SCRIPT MACRO blocks**: Parameterized macros for inline expansion
## Common Patterns
### Program Structure
```c65
#INCLUDE <c64start.c65>
#INCLUDE <c64defs.c65>
GOTO start // Jump over function definitions
// Variables and functions here
LABEL start // Program entry point
main()
```
### Library Structure
```c65
#IFNDEF __MY_LIBRARY
#DEFINE __MY_LIBRARY = 1
GOTO lib_mylib_skip
// Library code here
LABEL lib_mylib_skip
#IFEND
```
### Memory Operations
- Use `PEEK`/`POKE` for byte access
- Use `PEEKW`/`POKEW` for word (16-bit) access
- For indexed access, address must be a WORD variable in zero page
## Development Guidelines
### Environment Constraints
**IMPORTANT**: The agent runs in a Docker container without access to compilers, testing tools, or external build systems. All compilation and testing must be performed by the user. The agent can only:
1. Read and analyze source code
2. Make code changes
3. Provide instructions for the user to compile and test
### File Access Restrictions
**CRITICAL**: The agent must only access normal project files within the current working directory. The agent must NEVER:
1. Look at files outside the project directory (e.g., `/tmp/`, `/etc/`, `/home/`, etc.)
2. Read or attempt to access system files, environment files (`.env`), or configuration files outside the project
3. Search for or read files containing secrets, credentials, or sensitive information
4. Attempt to access files in parent directories or unrelated paths
All file operations must be restricted to the project's source code and documentation files only.
### When Modifying the Compiler
1. Check `internal/commands/` for command implementations
2. Check `internal/compiler/` for core compiler logic
3. Check `internal/preproc/` for preprocessor functionality
4. **DO NOT run tests** - provide instructions for the user to run tests
### When Adding New Features
1. Follow existing patterns in command implementations
2. Add comprehensive tests
3. Update documentation in appropriate .md files
4. Consider adding examples in `examples/` directory
### Testing
**IMPORTANT**: The agent cannot run tests. Provide these instructions to the user:
- Run all tests: `go test ./...`
- Run specific package tests: `go test ./internal/compiler`
- Test with verbose output: `go test -v ./...`
## Common Pitfalls
1. **Expression evaluation**: Remember no operator precedence
2. **Zero-page requirements**: Indexed memory operations require zero-page addresses
3. **Include guards**: Essential for library files to prevent multiple inclusion
4. **Program structure**: Always use `GOTO start` to jump over function definitions
5. **String handling**: Strings are passed as pointers to WORD parameters
## Quick Reference
### Compilation
**IMPORTANT**: The agent cannot compile code. Provide these instructions to the user:
```bash
./c65gm -in input.c65 -out output.asm
acme -f cbm -o output.prg output.asm
```
### Environment Variables
- `C65LIBPATH`: Library search path for `#INCLUDE <file>`
### Editor Support
- Kate syntax: `editor_syntaxes/kate/`
- Sublime syntax: `editor_syntaxes/sublime/`
## Where to Look for Specifics
### Language Syntax
- `language.md`: Sections 1-10 cover all language features
- `syntax.md`: Detailed syntax rules and preprocessor directives
- `commands.md`: Complete command reference with examples
### Implementation Details
- `internal/compiler/compiler.go`: Main compiler logic
- `internal/commands/*.go`: Individual command implementations
- `internal/preproc/preproc.go`: Preprocessor implementation
### Examples
- `examples/`: Working example programs
- `lib/`: Standard library usage examples
### Testing
- `*_test.go` files throughout the codebase
- `examples/`: Functional test programs

View file

@ -1,6 +0,0 @@
FROM node:18-alpine
WORKDIR /app
RUN apk add --no-cache bash
ENV SHELL=/bin/bash
RUN npm install -g @anthropic-ai/claude-code
CMD ["claude"]

View file

@ -1,4 +0,0 @@
#!/bin/bash
export DOCKER_UID=$(id -u)
export DOCKER_GID=$(id -g)
docker compose run --rm claude-c65gm

View file

@ -1,13 +1,15 @@
services:
claude-c65gm:
build: .
opencode-deepseek-c65gm:
#image: ghcr.io/anomalyco/opencode:0.0.0-beta-202604081541
image: ghcr.io/anomalyco/opencode:latest
user: "${DOCKER_UID}:${DOCKER_GID}"
working_dir: /app
volumes:
- .:/app
- ./opencode-config:/app/.config/opencode
- ./opencode-data:/home/opencode/.local/share/opencode
environment:
- ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY}
- CLAUDE_CONFIG_DIR=/app/.claude
- DEEPSEEK_API_KEY=${DEEPSEEK_API_KEY}
- HOME=/app
- DISABLE_AUTOUPDATER=1
stdin_open: true
tty: true

View file

@ -68,13 +68,7 @@ func (c *AddCommand) Interpret(line preproc.Line, ctx *compiler.CompilerContext)
scope := ctx.CurrentScope()
// Create constant lookup function
constLookup := func(name string) (int64, bool) {
sym := ctx.SymbolTable.Lookup(name, scope)
if sym != nil && sym.IsConst() {
return int64(sym.Value), true
}
return 0, false
}
constLookup := ctx.SymbolTable.ConstantLookupFunc(scope)
// Determine syntax and parse accordingly
if strings.ToUpper(params[0]) == "ADD" {

View file

@ -54,9 +54,9 @@ func TestAddCommand_Interpret_OldSyntax(t *testing.T) {
{
name: "byte + byte -> byte",
setup: func(ctx *compiler.CompilerContext) {
ctx.SymbolTable.AddVar("a", "", compiler.KindByte, 10)
ctx.SymbolTable.AddVar("b", "", compiler.KindByte, 20)
ctx.SymbolTable.AddVar("c", "", compiler.KindByte, 0)
ctx.SymbolTable.AddVar("a", "", compiler.KindByte, 10, "test.c65", 1)
ctx.SymbolTable.AddVar("b", "", compiler.KindByte, 20, "test.c65", 1)
ctx.SymbolTable.AddVar("c", "", compiler.KindByte, 0, "test.c65", 1)
},
text: "ADD a TO b GIVING c",
wantErr: false,
@ -75,9 +75,9 @@ func TestAddCommand_Interpret_OldSyntax(t *testing.T) {
{
name: "word + word -> word",
setup: func(ctx *compiler.CompilerContext) {
ctx.SymbolTable.AddVar("x", "", compiler.KindWord, 1000)
ctx.SymbolTable.AddVar("y", "", compiler.KindWord, 2000)
ctx.SymbolTable.AddVar("z", "", compiler.KindWord, 0)
ctx.SymbolTable.AddVar("x", "", compiler.KindWord, 1000, "test.c65", 1)
ctx.SymbolTable.AddVar("y", "", compiler.KindWord, 2000, "test.c65", 1)
ctx.SymbolTable.AddVar("z", "", compiler.KindWord, 0, "test.c65", 1)
},
text: "ADD x TO y GIVING z",
wantErr: false,
@ -90,9 +90,9 @@ func TestAddCommand_Interpret_OldSyntax(t *testing.T) {
{
name: "byte + word -> word",
setup: func(ctx *compiler.CompilerContext) {
ctx.SymbolTable.AddVar("a", "", compiler.KindByte, 10)
ctx.SymbolTable.AddVar("b", "", compiler.KindWord, 1000)
ctx.SymbolTable.AddVar("c", "", compiler.KindWord, 0)
ctx.SymbolTable.AddVar("a", "", compiler.KindByte, 10, "test.c65", 1)
ctx.SymbolTable.AddVar("b", "", compiler.KindWord, 1000, "test.c65", 1)
ctx.SymbolTable.AddVar("c", "", compiler.KindWord, 0, "test.c65", 1)
},
text: "ADD a TO b GIVING c",
wantErr: false,
@ -111,8 +111,8 @@ func TestAddCommand_Interpret_OldSyntax(t *testing.T) {
{
name: "literal + var -> var",
setup: func(ctx *compiler.CompilerContext) {
ctx.SymbolTable.AddVar("b", "", compiler.KindByte, 20)
ctx.SymbolTable.AddVar("c", "", compiler.KindByte, 0)
ctx.SymbolTable.AddVar("b", "", compiler.KindByte, 20, "test.c65", 1)
ctx.SymbolTable.AddVar("c", "", compiler.KindByte, 0, "test.c65", 1)
},
text: "ADD 10 TO b GIVING c",
wantErr: false,
@ -128,8 +128,8 @@ func TestAddCommand_Interpret_OldSyntax(t *testing.T) {
{
name: "var + literal -> var",
setup: func(ctx *compiler.CompilerContext) {
ctx.SymbolTable.AddVar("a", "", compiler.KindByte, 10)
ctx.SymbolTable.AddVar("c", "", compiler.KindByte, 0)
ctx.SymbolTable.AddVar("a", "", compiler.KindByte, 10, "test.c65", 1)
ctx.SymbolTable.AddVar("c", "", compiler.KindByte, 0, "test.c65", 1)
},
text: "ADD a TO 20 GIVING c",
wantErr: false,
@ -145,7 +145,7 @@ func TestAddCommand_Interpret_OldSyntax(t *testing.T) {
{
name: "literal + literal -> var",
setup: func(ctx *compiler.CompilerContext) {
ctx.SymbolTable.AddVar("c", "", compiler.KindByte, 0)
ctx.SymbolTable.AddVar("c", "", compiler.KindByte, 0, "test.c65", 1)
},
text: "ADD 15 TO 25 GIVING c",
wantErr: false,
@ -161,7 +161,7 @@ func TestAddCommand_Interpret_OldSyntax(t *testing.T) {
{
name: "hex literal",
setup: func(ctx *compiler.CompilerContext) {
ctx.SymbolTable.AddVar("c", "", compiler.KindByte, 0)
ctx.SymbolTable.AddVar("c", "", compiler.KindByte, 0, "test.c65", 1)
},
text: "ADD $10 TO $20 GIVING c",
wantErr: false,
@ -174,9 +174,9 @@ func TestAddCommand_Interpret_OldSyntax(t *testing.T) {
{
name: "constant usage",
setup: func(ctx *compiler.CompilerContext) {
ctx.SymbolTable.AddConst("MAX", "", compiler.KindByte, 100)
ctx.SymbolTable.AddVar("a", "", compiler.KindByte, 10)
ctx.SymbolTable.AddVar("c", "", compiler.KindByte, 0)
ctx.SymbolTable.AddConst("MAX", "", compiler.KindByte, 100, "test.c65", 1)
ctx.SymbolTable.AddVar("a", "", compiler.KindByte, 10, "test.c65", 1)
ctx.SymbolTable.AddVar("c", "", compiler.KindByte, 0, "test.c65", 1)
},
text: "ADD MAX TO a GIVING c",
wantErr: false,
@ -192,9 +192,9 @@ func TestAddCommand_Interpret_OldSyntax(t *testing.T) {
{
name: "alternative syntax +/->",
setup: func(ctx *compiler.CompilerContext) {
ctx.SymbolTable.AddVar("a", "", compiler.KindByte, 10)
ctx.SymbolTable.AddVar("b", "", compiler.KindByte, 20)
ctx.SymbolTable.AddVar("c", "", compiler.KindByte, 0)
ctx.SymbolTable.AddVar("a", "", compiler.KindByte, 10, "test.c65", 1)
ctx.SymbolTable.AddVar("b", "", compiler.KindByte, 20, "test.c65", 1)
ctx.SymbolTable.AddVar("c", "", compiler.KindByte, 0, "test.c65", 1)
},
text: "ADD a + b -> c",
wantErr: false,
@ -207,7 +207,7 @@ func TestAddCommand_Interpret_OldSyntax(t *testing.T) {
{
name: "unknown variable",
setup: func(ctx *compiler.CompilerContext) {
ctx.SymbolTable.AddVar("a", "", compiler.KindByte, 10)
ctx.SymbolTable.AddVar("a", "", compiler.KindByte, 10, "test.c65", 1)
},
text: "ADD a TO b GIVING c",
wantErr: true,
@ -215,9 +215,9 @@ func TestAddCommand_Interpret_OldSyntax(t *testing.T) {
{
name: "assign to constant",
setup: func(ctx *compiler.CompilerContext) {
ctx.SymbolTable.AddVar("a", "", compiler.KindByte, 10)
ctx.SymbolTable.AddVar("b", "", compiler.KindByte, 20)
ctx.SymbolTable.AddConst("MAX", "", compiler.KindByte, 100)
ctx.SymbolTable.AddVar("a", "", compiler.KindByte, 10, "test.c65", 1)
ctx.SymbolTable.AddVar("b", "", compiler.KindByte, 20, "test.c65", 1)
ctx.SymbolTable.AddConst("MAX", "", compiler.KindByte, 100, "test.c65", 1)
},
text: "ADD a TO b GIVING MAX",
wantErr: true,
@ -225,7 +225,7 @@ func TestAddCommand_Interpret_OldSyntax(t *testing.T) {
{
name: "wrong parameter count",
setup: func(ctx *compiler.CompilerContext) {
ctx.SymbolTable.AddVar("a", "", compiler.KindByte, 10)
ctx.SymbolTable.AddVar("a", "", compiler.KindByte, 10, "test.c65", 1)
},
text: "ADD a TO b",
wantErr: true,
@ -233,9 +233,9 @@ func TestAddCommand_Interpret_OldSyntax(t *testing.T) {
{
name: "wrong separator #3",
setup: func(ctx *compiler.CompilerContext) {
ctx.SymbolTable.AddVar("a", "", compiler.KindByte, 10)
ctx.SymbolTable.AddVar("b", "", compiler.KindByte, 20)
ctx.SymbolTable.AddVar("c", "", compiler.KindByte, 0)
ctx.SymbolTable.AddVar("a", "", compiler.KindByte, 10, "test.c65", 1)
ctx.SymbolTable.AddVar("b", "", compiler.KindByte, 20, "test.c65", 1)
ctx.SymbolTable.AddVar("c", "", compiler.KindByte, 0, "test.c65", 1)
},
text: "ADD a AND b GIVING c",
wantErr: true,
@ -243,9 +243,9 @@ func TestAddCommand_Interpret_OldSyntax(t *testing.T) {
{
name: "wrong separator #5",
setup: func(ctx *compiler.CompilerContext) {
ctx.SymbolTable.AddVar("a", "", compiler.KindByte, 10)
ctx.SymbolTable.AddVar("b", "", compiler.KindByte, 20)
ctx.SymbolTable.AddVar("c", "", compiler.KindByte, 0)
ctx.SymbolTable.AddVar("a", "", compiler.KindByte, 10, "test.c65", 1)
ctx.SymbolTable.AddVar("b", "", compiler.KindByte, 20, "test.c65", 1)
ctx.SymbolTable.AddVar("c", "", compiler.KindByte, 0, "test.c65", 1)
},
text: "ADD a TO b INTO c",
wantErr: true,
@ -253,7 +253,7 @@ func TestAddCommand_Interpret_OldSyntax(t *testing.T) {
{
name: "invalid expression",
setup: func(ctx *compiler.CompilerContext) {
ctx.SymbolTable.AddVar("c", "", compiler.KindByte, 0)
ctx.SymbolTable.AddVar("c", "", compiler.KindByte, 0, "test.c65", 1)
},
text: "ADD @#$% TO 10 GIVING c",
wantErr: true,
@ -295,9 +295,9 @@ func TestAddCommand_Interpret_NewSyntax(t *testing.T) {
{
name: "dest = var1 + var2",
setup: func(ctx *compiler.CompilerContext) {
ctx.SymbolTable.AddVar("a", "", compiler.KindByte, 10)
ctx.SymbolTable.AddVar("b", "", compiler.KindByte, 20)
ctx.SymbolTable.AddVar("result", "", compiler.KindByte, 0)
ctx.SymbolTable.AddVar("a", "", compiler.KindByte, 10, "test.c65", 1)
ctx.SymbolTable.AddVar("b", "", compiler.KindByte, 20, "test.c65", 1)
ctx.SymbolTable.AddVar("result", "", compiler.KindByte, 0, "test.c65", 1)
},
text: "result = a + b",
wantErr: false,
@ -316,8 +316,8 @@ func TestAddCommand_Interpret_NewSyntax(t *testing.T) {
{
name: "dest = literal + var",
setup: func(ctx *compiler.CompilerContext) {
ctx.SymbolTable.AddVar("x", "", compiler.KindByte, 5)
ctx.SymbolTable.AddVar("result", "", compiler.KindByte, 0)
ctx.SymbolTable.AddVar("x", "", compiler.KindByte, 5, "test.c65", 1)
ctx.SymbolTable.AddVar("result", "", compiler.KindByte, 0, "test.c65", 1)
},
text: "result = 100 + x",
wantErr: false,
@ -333,8 +333,8 @@ func TestAddCommand_Interpret_NewSyntax(t *testing.T) {
{
name: "dest = var + literal",
setup: func(ctx *compiler.CompilerContext) {
ctx.SymbolTable.AddVar("x", "", compiler.KindByte, 5)
ctx.SymbolTable.AddVar("result", "", compiler.KindByte, 0)
ctx.SymbolTable.AddVar("x", "", compiler.KindByte, 5, "test.c65", 1)
ctx.SymbolTable.AddVar("result", "", compiler.KindByte, 0, "test.c65", 1)
},
text: "result = x + 50",
wantErr: false,
@ -350,7 +350,7 @@ func TestAddCommand_Interpret_NewSyntax(t *testing.T) {
{
name: "dest = literal + literal",
setup: func(ctx *compiler.CompilerContext) {
ctx.SymbolTable.AddVar("result", "", compiler.KindByte, 0)
ctx.SymbolTable.AddVar("result", "", compiler.KindByte, 0, "test.c65", 1)
},
text: "result = 30 + 70",
wantErr: false,
@ -363,9 +363,9 @@ func TestAddCommand_Interpret_NewSyntax(t *testing.T) {
{
name: "word destination",
setup: func(ctx *compiler.CompilerContext) {
ctx.SymbolTable.AddVar("a", "", compiler.KindWord, 1000)
ctx.SymbolTable.AddVar("b", "", compiler.KindWord, 2000)
ctx.SymbolTable.AddVar("result", "", compiler.KindWord, 0)
ctx.SymbolTable.AddVar("a", "", compiler.KindWord, 1000, "test.c65", 1)
ctx.SymbolTable.AddVar("b", "", compiler.KindWord, 2000, "test.c65", 1)
ctx.SymbolTable.AddVar("result", "", compiler.KindWord, 0, "test.c65", 1)
},
text: "result = a + b",
wantErr: false,
@ -378,8 +378,8 @@ func TestAddCommand_Interpret_NewSyntax(t *testing.T) {
{
name: "unknown dest variable",
setup: func(ctx *compiler.CompilerContext) {
ctx.SymbolTable.AddVar("a", "", compiler.KindByte, 10)
ctx.SymbolTable.AddVar("b", "", compiler.KindByte, 20)
ctx.SymbolTable.AddVar("a", "", compiler.KindByte, 10, "test.c65", 1)
ctx.SymbolTable.AddVar("b", "", compiler.KindByte, 20, "test.c65", 1)
},
text: "result = a + b",
wantErr: true,
@ -387,9 +387,9 @@ func TestAddCommand_Interpret_NewSyntax(t *testing.T) {
{
name: "assign to constant",
setup: func(ctx *compiler.CompilerContext) {
ctx.SymbolTable.AddVar("a", "", compiler.KindByte, 10)
ctx.SymbolTable.AddVar("b", "", compiler.KindByte, 20)
ctx.SymbolTable.AddConst("MAX", "", compiler.KindByte, 100)
ctx.SymbolTable.AddVar("a", "", compiler.KindByte, 10, "test.c65", 1)
ctx.SymbolTable.AddVar("b", "", compiler.KindByte, 20, "test.c65", 1)
ctx.SymbolTable.AddConst("MAX", "", compiler.KindByte, 100, "test.c65", 1)
},
text: "MAX = a + b",
wantErr: true,
@ -397,9 +397,9 @@ func TestAddCommand_Interpret_NewSyntax(t *testing.T) {
{
name: "wrong operator (not +)",
setup: func(ctx *compiler.CompilerContext) {
ctx.SymbolTable.AddVar("a", "", compiler.KindByte, 10)
ctx.SymbolTable.AddVar("b", "", compiler.KindByte, 20)
ctx.SymbolTable.AddVar("result", "", compiler.KindByte, 0)
ctx.SymbolTable.AddVar("a", "", compiler.KindByte, 10, "test.c65", 1)
ctx.SymbolTable.AddVar("b", "", compiler.KindByte, 20, "test.c65", 1)
ctx.SymbolTable.AddVar("result", "", compiler.KindByte, 0, "test.c65", 1)
},
text: "result = a - b",
wantErr: true,
@ -439,7 +439,7 @@ func TestAddCommand_Generate(t *testing.T) {
{
name: "constant folding - both literals to byte",
setup: func(ctx *compiler.CompilerContext) *AddCommand {
ctx.SymbolTable.AddVar("result", "", compiler.KindByte, 0)
ctx.SymbolTable.AddVar("result", "", compiler.KindByte, 0, "test.c65", 1)
return &AddCommand{
param1IsVar: false,
param1Value: 10,
@ -457,7 +457,7 @@ func TestAddCommand_Generate(t *testing.T) {
{
name: "constant folding - both literals to word",
setup: func(ctx *compiler.CompilerContext) *AddCommand {
ctx.SymbolTable.AddVar("result", "", compiler.KindWord, 0)
ctx.SymbolTable.AddVar("result", "", compiler.KindWord, 0, "test.c65", 1)
return &AddCommand{
param1IsVar: false,
param1Value: 100,
@ -477,7 +477,7 @@ func TestAddCommand_Generate(t *testing.T) {
{
name: "constant folding with overflow",
setup: func(ctx *compiler.CompilerContext) *AddCommand {
ctx.SymbolTable.AddVar("result", "", compiler.KindWord, 0)
ctx.SymbolTable.AddVar("result", "", compiler.KindWord, 0, "test.c65", 1)
return &AddCommand{
param1IsVar: false,
param1Value: 200,
@ -497,9 +497,9 @@ func TestAddCommand_Generate(t *testing.T) {
{
name: "byte + byte -> byte",
setup: func(ctx *compiler.CompilerContext) *AddCommand {
ctx.SymbolTable.AddVar("a", "", compiler.KindByte, 10)
ctx.SymbolTable.AddVar("b", "", compiler.KindByte, 20)
ctx.SymbolTable.AddVar("result", "", compiler.KindByte, 0)
ctx.SymbolTable.AddVar("a", "", compiler.KindByte, 10, "test.c65", 1)
ctx.SymbolTable.AddVar("b", "", compiler.KindByte, 20, "test.c65", 1)
ctx.SymbolTable.AddVar("result", "", compiler.KindByte, 0, "test.c65", 1)
return &AddCommand{
param1IsVar: true,
param1VarName: "a",
@ -521,9 +521,9 @@ func TestAddCommand_Generate(t *testing.T) {
{
name: "word + word -> word",
setup: func(ctx *compiler.CompilerContext) *AddCommand {
ctx.SymbolTable.AddVar("x", "", compiler.KindWord, 1000)
ctx.SymbolTable.AddVar("y", "", compiler.KindWord, 2000)
ctx.SymbolTable.AddVar("z", "", compiler.KindWord, 0)
ctx.SymbolTable.AddVar("x", "", compiler.KindWord, 1000, "test.c65", 1)
ctx.SymbolTable.AddVar("y", "", compiler.KindWord, 2000, "test.c65", 1)
ctx.SymbolTable.AddVar("z", "", compiler.KindWord, 0, "test.c65", 1)
return &AddCommand{
param1IsVar: true,
param1VarName: "x",
@ -548,9 +548,9 @@ func TestAddCommand_Generate(t *testing.T) {
{
name: "byte + word -> word",
setup: func(ctx *compiler.CompilerContext) *AddCommand {
ctx.SymbolTable.AddVar("a", "", compiler.KindByte, 10)
ctx.SymbolTable.AddVar("w", "", compiler.KindWord, 1000)
ctx.SymbolTable.AddVar("result", "", compiler.KindWord, 0)
ctx.SymbolTable.AddVar("a", "", compiler.KindByte, 10, "test.c65", 1)
ctx.SymbolTable.AddVar("w", "", compiler.KindWord, 1000, "test.c65", 1)
ctx.SymbolTable.AddVar("result", "", compiler.KindWord, 0, "test.c65", 1)
return &AddCommand{
param1IsVar: true,
param1VarName: "a",
@ -575,8 +575,8 @@ func TestAddCommand_Generate(t *testing.T) {
{
name: "literal + var -> byte",
setup: func(ctx *compiler.CompilerContext) *AddCommand {
ctx.SymbolTable.AddVar("a", "", compiler.KindByte, 10)
ctx.SymbolTable.AddVar("result", "", compiler.KindByte, 0)
ctx.SymbolTable.AddVar("a", "", compiler.KindByte, 10, "test.c65", 1)
ctx.SymbolTable.AddVar("result", "", compiler.KindByte, 0, "test.c65", 1)
return &AddCommand{
param1IsVar: false,
param1Value: 5,
@ -597,8 +597,8 @@ func TestAddCommand_Generate(t *testing.T) {
{
name: "var + literal -> word",
setup: func(ctx *compiler.CompilerContext) *AddCommand {
ctx.SymbolTable.AddVar("w", "", compiler.KindWord, 1000)
ctx.SymbolTable.AddVar("result", "", compiler.KindWord, 0)
ctx.SymbolTable.AddVar("w", "", compiler.KindWord, 1000, "test.c65", 1)
ctx.SymbolTable.AddVar("result", "", compiler.KindWord, 0, "test.c65", 1)
return &AddCommand{
param1IsVar: true,
param1VarName: "w",
@ -622,9 +622,9 @@ func TestAddCommand_Generate(t *testing.T) {
{
name: "byte + byte -> word (promotion)",
setup: func(ctx *compiler.CompilerContext) *AddCommand {
ctx.SymbolTable.AddVar("a", "", compiler.KindByte, 10)
ctx.SymbolTable.AddVar("b", "", compiler.KindByte, 20)
ctx.SymbolTable.AddVar("result", "", compiler.KindWord, 0)
ctx.SymbolTable.AddVar("a", "", compiler.KindByte, 10, "test.c65", 1)
ctx.SymbolTable.AddVar("b", "", compiler.KindByte, 20, "test.c65", 1)
ctx.SymbolTable.AddVar("result", "", compiler.KindWord, 0, "test.c65", 1)
return &AddCommand{
param1IsVar: true,
param1VarName: "a",
@ -678,9 +678,9 @@ func TestAddCommand_Scopes(t *testing.T) {
ctx := compiler.NewCompilerContext(preproc.NewPragma())
// Global variables
ctx.SymbolTable.AddVar("globalA", "", compiler.KindByte, 10)
ctx.SymbolTable.AddVar("globalB", "", compiler.KindByte, 20)
ctx.SymbolTable.AddVar("result", "", compiler.KindByte, 0)
ctx.SymbolTable.AddVar("globalA", "", compiler.KindByte, 10, "test.c65", 1)
ctx.SymbolTable.AddVar("globalB", "", compiler.KindByte, 20, "test.c65", 1)
ctx.SymbolTable.AddVar("result", "", compiler.KindByte, 0, "test.c65", 1)
// Simulate function declaration to enter scope
line := preproc.Line{Text: "FUNC myFunc"}
@ -690,8 +690,8 @@ func TestAddCommand_Scopes(t *testing.T) {
}
// Function scope variables
ctx.SymbolTable.AddVar("localA", "myFunc", compiler.KindByte, 5)
ctx.SymbolTable.AddVar("localB", "myFunc", compiler.KindByte, 15)
ctx.SymbolTable.AddVar("localA", "myFunc", compiler.KindByte, 5, "test.c65", 1)
ctx.SymbolTable.AddVar("localB", "myFunc", compiler.KindByte, 15, "test.c65", 1)
// Test local variables
cmd := &AddCommand{}

View file

@ -68,13 +68,7 @@ func (c *AndCommand) Interpret(line preproc.Line, ctx *compiler.CompilerContext)
scope := ctx.CurrentScope()
// Create constant lookup function
constLookup := func(name string) (int64, bool) {
sym := ctx.SymbolTable.Lookup(name, scope)
if sym != nil && sym.IsConst() {
return int64(sym.Value), true
}
return 0, false
}
constLookup := ctx.SymbolTable.ConstantLookupFunc(scope)
// Determine syntax and parse accordingly
if strings.ToUpper(params[0]) == "AND" {

View file

@ -48,9 +48,9 @@ func TestAndCommand_OldSyntax(t *testing.T) {
name: "byte AND byte -> byte (variables)",
line: "AND a WITH b GIVING result",
setupVars: func(st *compiler.SymbolTable) {
st.AddVar("a", "", compiler.KindByte, 0xFF)
st.AddVar("b", "", compiler.KindByte, 0x0F)
st.AddVar("result", "", compiler.KindByte, 0)
st.AddVar("a", "", compiler.KindByte, 0xFF, "test.c65", 1)
st.AddVar("b", "", compiler.KindByte, 0x0F, "test.c65", 1)
st.AddVar("result", "", compiler.KindByte, 0, "test.c65", 1)
},
wantAsm: []string{
"\tlda a",
@ -62,9 +62,9 @@ func TestAndCommand_OldSyntax(t *testing.T) {
name: "byte AND byte -> word",
line: "AND a WITH b GIVING result",
setupVars: func(st *compiler.SymbolTable) {
st.AddVar("a", "", compiler.KindByte, 0xFF)
st.AddVar("b", "", compiler.KindByte, 0x0F)
st.AddVar("result", "", compiler.KindWord, 0)
st.AddVar("a", "", compiler.KindByte, 0xFF, "test.c65", 1)
st.AddVar("b", "", compiler.KindByte, 0x0F, "test.c65", 1)
st.AddVar("result", "", compiler.KindWord, 0, "test.c65", 1)
},
wantAsm: []string{
"\tlda a",
@ -79,9 +79,9 @@ func TestAndCommand_OldSyntax(t *testing.T) {
name: "word AND word -> word",
line: "AND x WITH y GIVING result",
setupVars: func(st *compiler.SymbolTable) {
st.AddVar("x", "", compiler.KindWord, 0x1234)
st.AddVar("y", "", compiler.KindWord, 0x0F0F)
st.AddVar("result", "", compiler.KindWord, 0)
st.AddVar("x", "", compiler.KindWord, 0x1234, "test.c65", 1)
st.AddVar("y", "", compiler.KindWord, 0x0F0F, "test.c65", 1)
st.AddVar("result", "", compiler.KindWord, 0, "test.c65", 1)
},
wantAsm: []string{
"\tlda x",
@ -96,8 +96,8 @@ func TestAndCommand_OldSyntax(t *testing.T) {
name: "byte AND literal -> byte",
line: "AND a WITH $F0 GIVING result",
setupVars: func(st *compiler.SymbolTable) {
st.AddVar("a", "", compiler.KindByte, 0xFF)
st.AddVar("result", "", compiler.KindByte, 0)
st.AddVar("a", "", compiler.KindByte, 0xFF, "test.c65", 1)
st.AddVar("result", "", compiler.KindByte, 0, "test.c65", 1)
},
wantAsm: []string{
"\tlda a",
@ -109,8 +109,8 @@ func TestAndCommand_OldSyntax(t *testing.T) {
name: "literal AND byte -> byte",
line: "AND 255 WITH b GIVING result",
setupVars: func(st *compiler.SymbolTable) {
st.AddVar("b", "", compiler.KindByte, 0x0F)
st.AddVar("result", "", compiler.KindByte, 0)
st.AddVar("b", "", compiler.KindByte, 0x0F, "test.c65", 1)
st.AddVar("result", "", compiler.KindByte, 0, "test.c65", 1)
},
wantAsm: []string{
"\tlda #$ff",
@ -122,7 +122,7 @@ func TestAndCommand_OldSyntax(t *testing.T) {
name: "constant folding: 255 AND 15 -> byte",
line: "AND 255 WITH 15 GIVING result",
setupVars: func(st *compiler.SymbolTable) {
st.AddVar("result", "", compiler.KindByte, 0)
st.AddVar("result", "", compiler.KindByte, 0, "test.c65", 1)
},
wantAsm: []string{
"\tlda #$0f",
@ -133,7 +133,7 @@ func TestAndCommand_OldSyntax(t *testing.T) {
name: "constant folding: $FFFF AND $0F0F -> word",
line: "AND $FFFF WITH $0F0F GIVING result",
setupVars: func(st *compiler.SymbolTable) {
st.AddVar("result", "", compiler.KindWord, 0)
st.AddVar("result", "", compiler.KindWord, 0, "test.c65", 1)
},
wantAsm: []string{
"\tlda #$0f",
@ -146,9 +146,9 @@ func TestAndCommand_OldSyntax(t *testing.T) {
name: "arrow syntax",
line: "AND a WITH b -> result",
setupVars: func(st *compiler.SymbolTable) {
st.AddVar("a", "", compiler.KindByte, 0)
st.AddVar("b", "", compiler.KindByte, 0)
st.AddVar("result", "", compiler.KindByte, 0)
st.AddVar("a", "", compiler.KindByte, 0, "test.c65", 1)
st.AddVar("b", "", compiler.KindByte, 0, "test.c65", 1)
st.AddVar("result", "", compiler.KindByte, 0, "test.c65", 1)
},
wantAsm: []string{
"\tlda a",
@ -160,9 +160,9 @@ func TestAndCommand_OldSyntax(t *testing.T) {
name: "word AND byte -> byte",
line: "AND wval WITH bval GIVING result",
setupVars: func(st *compiler.SymbolTable) {
st.AddVar("wval", "", compiler.KindWord, 0x1234)
st.AddVar("bval", "", compiler.KindByte, 0xFF)
st.AddVar("result", "", compiler.KindByte, 0)
st.AddVar("wval", "", compiler.KindWord, 0x1234, "test.c65", 1)
st.AddVar("bval", "", compiler.KindByte, 0xFF, "test.c65", 1)
st.AddVar("result", "", compiler.KindByte, 0, "test.c65", 1)
},
wantAsm: []string{
"\tlda wval",
@ -174,9 +174,9 @@ func TestAndCommand_OldSyntax(t *testing.T) {
name: "word AND byte -> word",
line: "AND wval WITH bval GIVING result",
setupVars: func(st *compiler.SymbolTable) {
st.AddVar("wval", "", compiler.KindWord, 0x1234)
st.AddVar("bval", "", compiler.KindByte, 0xFF)
st.AddVar("result", "", compiler.KindWord, 0)
st.AddVar("wval", "", compiler.KindWord, 0x1234, "test.c65", 1)
st.AddVar("bval", "", compiler.KindByte, 0xFF, "test.c65", 1)
st.AddVar("result", "", compiler.KindWord, 0, "test.c65", 1)
},
wantAsm: []string{
"\tlda wval",
@ -191,8 +191,8 @@ func TestAndCommand_OldSyntax(t *testing.T) {
name: "error: unknown destination variable",
line: "AND a WITH b GIVING unknown",
setupVars: func(st *compiler.SymbolTable) {
st.AddVar("a", "", compiler.KindByte, 0)
st.AddVar("b", "", compiler.KindByte, 0)
st.AddVar("a", "", compiler.KindByte, 0, "test.c65", 1)
st.AddVar("b", "", compiler.KindByte, 0, "test.c65", 1)
},
wantErr: true,
},
@ -200,9 +200,9 @@ func TestAndCommand_OldSyntax(t *testing.T) {
name: "error: wrong separator",
line: "AND a TO b GIVING result",
setupVars: func(st *compiler.SymbolTable) {
st.AddVar("a", "", compiler.KindByte, 0)
st.AddVar("b", "", compiler.KindByte, 0)
st.AddVar("result", "", compiler.KindByte, 0)
st.AddVar("a", "", compiler.KindByte, 0, "test.c65", 1)
st.AddVar("b", "", compiler.KindByte, 0, "test.c65", 1)
st.AddVar("result", "", compiler.KindByte, 0, "test.c65", 1)
},
wantErr: true,
},
@ -210,9 +210,9 @@ func TestAndCommand_OldSyntax(t *testing.T) {
name: "error: cannot assign to constant",
line: "AND a WITH b GIVING MAXVAL",
setupVars: func(st *compiler.SymbolTable) {
st.AddVar("a", "", compiler.KindByte, 0)
st.AddVar("b", "", compiler.KindByte, 0)
st.AddConst("MAXVAL", "", compiler.KindByte, 255)
st.AddVar("a", "", compiler.KindByte, 0, "test.c65", 1)
st.AddVar("b", "", compiler.KindByte, 0, "test.c65", 1)
st.AddConst("MAXVAL", "", compiler.KindByte, 255, "test.c65", 1)
},
wantErr: true,
},
@ -263,9 +263,9 @@ func TestAndCommand_NewSyntax(t *testing.T) {
name: "byte & byte -> byte",
line: "result = a & b",
setupVars: func(st *compiler.SymbolTable) {
st.AddVar("a", "", compiler.KindByte, 0xFF)
st.AddVar("b", "", compiler.KindByte, 0x0F)
st.AddVar("result", "", compiler.KindByte, 0)
st.AddVar("a", "", compiler.KindByte, 0xFF, "test.c65", 1)
st.AddVar("b", "", compiler.KindByte, 0x0F, "test.c65", 1)
st.AddVar("result", "", compiler.KindByte, 0, "test.c65", 1)
},
wantAsm: []string{
"\tlda a",
@ -277,9 +277,9 @@ func TestAndCommand_NewSyntax(t *testing.T) {
name: "byte & byte -> word",
line: "result = a & b",
setupVars: func(st *compiler.SymbolTable) {
st.AddVar("a", "", compiler.KindByte, 0xFF)
st.AddVar("b", "", compiler.KindByte, 0x0F)
st.AddVar("result", "", compiler.KindWord, 0)
st.AddVar("a", "", compiler.KindByte, 0xFF, "test.c65", 1)
st.AddVar("b", "", compiler.KindByte, 0x0F, "test.c65", 1)
st.AddVar("result", "", compiler.KindWord, 0, "test.c65", 1)
},
wantAsm: []string{
"\tlda a",
@ -294,9 +294,9 @@ func TestAndCommand_NewSyntax(t *testing.T) {
name: "word & word -> word",
line: "result = x & y",
setupVars: func(st *compiler.SymbolTable) {
st.AddVar("x", "", compiler.KindWord, 0x1234)
st.AddVar("y", "", compiler.KindWord, 0x0F0F)
st.AddVar("result", "", compiler.KindWord, 0)
st.AddVar("x", "", compiler.KindWord, 0x1234, "test.c65", 1)
st.AddVar("y", "", compiler.KindWord, 0x0F0F, "test.c65", 1)
st.AddVar("result", "", compiler.KindWord, 0, "test.c65", 1)
},
wantAsm: []string{
"\tlda x",
@ -311,8 +311,8 @@ func TestAndCommand_NewSyntax(t *testing.T) {
name: "variable & literal",
line: "result = a & $F0",
setupVars: func(st *compiler.SymbolTable) {
st.AddVar("a", "", compiler.KindByte, 0xFF)
st.AddVar("result", "", compiler.KindByte, 0)
st.AddVar("a", "", compiler.KindByte, 0xFF, "test.c65", 1)
st.AddVar("result", "", compiler.KindByte, 0, "test.c65", 1)
},
wantAsm: []string{
"\tlda a",
@ -324,7 +324,7 @@ func TestAndCommand_NewSyntax(t *testing.T) {
name: "constant folding",
line: "result = 255 & 15",
setupVars: func(st *compiler.SymbolTable) {
st.AddVar("result", "", compiler.KindByte, 0)
st.AddVar("result", "", compiler.KindByte, 0, "test.c65", 1)
},
wantAsm: []string{
"\tlda #$0f",
@ -335,7 +335,7 @@ func TestAndCommand_NewSyntax(t *testing.T) {
name: "constant folding word",
line: "result = $FFFF & $1234",
setupVars: func(st *compiler.SymbolTable) {
st.AddVar("result", "", compiler.KindWord, 0)
st.AddVar("result", "", compiler.KindWord, 0, "test.c65", 1)
},
wantAsm: []string{
"\tlda #$34",
@ -348,9 +348,9 @@ func TestAndCommand_NewSyntax(t *testing.T) {
name: "using constant in expression",
line: "result = a & MASK",
setupVars: func(st *compiler.SymbolTable) {
st.AddVar("a", "", compiler.KindByte, 0xFF)
st.AddConst("MASK", "", compiler.KindByte, 0xF0)
st.AddVar("result", "", compiler.KindByte, 0)
st.AddVar("a", "", compiler.KindByte, 0xFF, "test.c65", 1)
st.AddConst("MASK", "", compiler.KindByte, 0xF0, "test.c65", 1)
st.AddVar("result", "", compiler.KindByte, 0, "test.c65", 1)
},
wantAsm: []string{
"\tlda a",
@ -362,8 +362,8 @@ func TestAndCommand_NewSyntax(t *testing.T) {
name: "error: unknown destination",
line: "unknown = a & b",
setupVars: func(st *compiler.SymbolTable) {
st.AddVar("a", "", compiler.KindByte, 0)
st.AddVar("b", "", compiler.KindByte, 0)
st.AddVar("a", "", compiler.KindByte, 0, "test.c65", 1)
st.AddVar("b", "", compiler.KindByte, 0, "test.c65", 1)
},
wantErr: true,
},
@ -371,9 +371,9 @@ func TestAndCommand_NewSyntax(t *testing.T) {
name: "error: wrong operator",
line: "result = a + b",
setupVars: func(st *compiler.SymbolTable) {
st.AddVar("a", "", compiler.KindByte, 0)
st.AddVar("b", "", compiler.KindByte, 0)
st.AddVar("result", "", compiler.KindByte, 0)
st.AddVar("a", "", compiler.KindByte, 0, "test.c65", 1)
st.AddVar("b", "", compiler.KindByte, 0, "test.c65", 1)
st.AddVar("result", "", compiler.KindByte, 0, "test.c65", 1)
},
wantErr: true,
},

View file

@ -55,13 +55,7 @@ func (c *ByteCommand) Interpret(line preproc.Line, ctx *compiler.CompilerContext
scope := ctx.FunctionHandler.CurrentFunction()
// Create constant lookup function
constLookup := func(name string) (int64, bool) {
sym := ctx.SymbolTable.Lookup(name, ctx.CurrentScope())
if sym != nil && sym.IsConst() {
return int64(sym.Value), true
}
return 0, false
}
constLookup := ctx.SymbolTable.ConstantLookupFunc(ctx.CurrentScope())
switch paramCount {
case 2:
@ -73,7 +67,7 @@ func (c *ByteCommand) Interpret(line preproc.Line, ctx *compiler.CompilerContext
return fmt.Errorf("BYTE: invalid identifier %q", varName)
}
err = ctx.SymbolTable.AddVar(varName, scope, compiler.KindByte, uint16(value))
err = ctx.SymbolTable.AddVar(varName, scope, compiler.KindByte, uint16(value), line.Filename, line.LineNo)
case 4:
// BYTE varname = value OR BYTE varname @ address
@ -95,7 +89,7 @@ func (c *ByteCommand) Interpret(line preproc.Line, ctx *compiler.CompilerContext
if value < 0 || value > 255 {
return fmt.Errorf("BYTE: init value %d out of range (0-255)", value)
}
err = ctx.SymbolTable.AddVar(varName, scope, compiler.KindByte, uint16(value))
err = ctx.SymbolTable.AddVar(varName, scope, compiler.KindByte, uint16(value), line.Filename, line.LineNo)
} else if operator == "@" {
// BYTE varname @ address
@ -103,7 +97,7 @@ func (c *ByteCommand) Interpret(line preproc.Line, ctx *compiler.CompilerContext
return fmt.Errorf("BYTE: absolute address $%X out of range", value)
}
c.isAbs = true
err = ctx.SymbolTable.AddAbsolute(varName, scope, compiler.KindByte, uint16(value))
err = ctx.SymbolTable.AddAbsolute(varName, scope, compiler.KindByte, uint16(value), line.Filename, line.LineNo)
} else {
return fmt.Errorf("BYTE: expected '=' or '@', got %q", operator)
@ -138,7 +132,7 @@ func (c *ByteCommand) Interpret(line preproc.Line, ctx *compiler.CompilerContext
}
c.isConst = true
err = ctx.SymbolTable.AddConst(varName, scope, compiler.KindByte, uint16(value))
err = ctx.SymbolTable.AddConst(varName, scope, compiler.KindByte, uint16(value), line.Filename, line.LineNo)
}
if err != nil {

View file

@ -302,7 +302,7 @@ func TestByteCommand_Interpret(t *testing.T) {
// For duplicate test, pre-declare the variable
if tt.name == "duplicate declaration" {
ctx.SymbolTable.AddVar("x", "", compiler.KindByte, 0)
ctx.SymbolTable.AddVar("x", "", compiler.KindByte, 0, "test.c65", 1)
}
cmd := &ByteCommand{}
@ -370,7 +370,7 @@ func TestByteCommand_InFunctionScope(t *testing.T) {
// Simulate being inside a function
// We'd need to push function context, but for now we can test the scoping manually
scope := "myFunc"
ctx.SymbolTable.AddVar("localVar", scope, compiler.KindByte, 5)
ctx.SymbolTable.AddVar("localVar", scope, compiler.KindByte, 5, "test.c65", 1)
// Check it was added with correct scope
sym := ctx.SymbolTable.Lookup("localVar", []string{scope})
@ -390,8 +390,8 @@ func TestByteCommand_WithConstantExpression(t *testing.T) {
ctx := compiler.NewCompilerContext(pragma)
// First, declare a constant
ctx.SymbolTable.AddConst("MAXVAL", "", compiler.KindByte, 200)
ctx.SymbolTable.AddConst("OFFSET", "", compiler.KindByte, 10)
ctx.SymbolTable.AddConst("MAXVAL", "", compiler.KindByte, 200, "test.c65", 1)
ctx.SymbolTable.AddConst("OFFSET", "", compiler.KindByte, 10, "test.c65", 1)
// Now declare a byte using the constant in an expression
cmd := &ByteCommand{}

View file

@ -39,13 +39,7 @@ func (c *CaseCommand) Interpret(line preproc.Line, ctx *compiler.CompilerContext
}
scope := ctx.CurrentScope()
constLookup := func(name string) (int64, bool) {
sym := ctx.SymbolTable.Lookup(name, scope)
if sym != nil && sym.IsConst() {
return int64(sym.Value), true
}
return 0, false
}
constLookup := ctx.SymbolTable.ConstantLookupFunc(scope)
// Parse the case value
varName, varKind, value, isVar, err := compiler.ParseOperandParam(

View file

@ -97,13 +97,7 @@ func (c *DecrCommand) Interpret(line preproc.Line, ctx *compiler.CompilerContext
}
// Old syntax allows absolute addresses
constLookup := func(name string) (int64, bool) {
s := ctx.SymbolTable.Lookup(name, scope)
if s != nil && s.IsConst() {
return int64(s.Value), true
}
return 0, false
}
constLookup := ctx.SymbolTable.ConstantLookupFunc(scope)
val, evalErr := utils.EvaluateExpression(targetParam, constLookup)
if evalErr != nil {

View file

@ -48,13 +48,7 @@ func (c *ForCommand) Interpret(line preproc.Line, ctx *compiler.CompilerContext)
}
scope := ctx.CurrentScope()
constLookup := func(name string) (int64, bool) {
sym := ctx.SymbolTable.Lookup(name, scope)
if sym != nil && sym.IsConst() {
return int64(sym.Value), true
}
return 0, false
}
constLookup := ctx.SymbolTable.ConstantLookupFunc(scope)
// Parse variable
varName := params[1]

View file

@ -20,7 +20,7 @@ func TestForBasicTO(t *testing.T) {
name: "byte var TO byte literal",
forLine: "FOR i = 0 TO 10",
setupVars: func(st *compiler.SymbolTable) {
st.AddVar("i", "", compiler.KindByte, 0)
st.AddVar("i", "", compiler.KindByte, 0, "test.c65", 1)
},
// Do-while style: initial guard (constant folded - 0<=10 is true, no code)
// then loop label
@ -43,7 +43,7 @@ func TestForBasicTO(t *testing.T) {
name: "word var TO word literal",
forLine: "FOR counter = 0 TO 1000",
setupVars: func(st *compiler.SymbolTable) {
st.AddVar("counter", "", compiler.KindWord, 0)
st.AddVar("counter", "", compiler.KindWord, 0, "test.c65", 1)
},
// Do-while style: initial guard (constant folded - 0<=1000 is true, no code)
wantFor: []string{
@ -126,7 +126,7 @@ func TestForBasicTO(t *testing.T) {
func TestForBreak(t *testing.T) {
pragma := preproc.NewPragma()
ctx := compiler.NewCompilerContext(pragma)
ctx.SymbolTable.AddVar("i", "", compiler.KindByte, 0)
ctx.SymbolTable.AddVar("i", "", compiler.KindByte, 0, "test.c65", 1)
forCmd := &ForCommand{}
breakCmd := &BreakCommand{}
@ -166,8 +166,8 @@ func TestForBreak(t *testing.T) {
func TestForNested(t *testing.T) {
pragma := preproc.NewPragma()
ctx := compiler.NewCompilerContext(pragma)
ctx.SymbolTable.AddVar("i", "", compiler.KindByte, 0)
ctx.SymbolTable.AddVar("j", "", compiler.KindByte, 0)
ctx.SymbolTable.AddVar("i", "", compiler.KindByte, 0, "test.c65", 1)
ctx.SymbolTable.AddVar("j", "", compiler.KindByte, 0, "test.c65", 1)
pragmaIdx := pragma.GetCurrentPragmaSetIndex()
@ -226,8 +226,8 @@ func TestForNested(t *testing.T) {
func TestForMixedWithWhile(t *testing.T) {
pragma := preproc.NewPragma()
ctx := compiler.NewCompilerContext(pragma)
ctx.SymbolTable.AddVar("i", "", compiler.KindByte, 0)
ctx.SymbolTable.AddVar("x", "", compiler.KindByte, 0)
ctx.SymbolTable.AddVar("i", "", compiler.KindByte, 0, "test.c65", 1)
ctx.SymbolTable.AddVar("x", "", compiler.KindByte, 0, "test.c65", 1)
pragmaIdx := pragma.GetCurrentPragmaSetIndex()
@ -263,8 +263,8 @@ func TestForMixedWithWhile(t *testing.T) {
func TestForIllegalNesting(t *testing.T) {
pragma := preproc.NewPragma()
ctx := compiler.NewCompilerContext(pragma)
ctx.SymbolTable.AddVar("i", "", compiler.KindByte, 0)
ctx.SymbolTable.AddVar("x", "", compiler.KindByte, 0)
ctx.SymbolTable.AddVar("i", "", compiler.KindByte, 0, "test.c65", 1)
ctx.SymbolTable.AddVar("x", "", compiler.KindByte, 0, "test.c65", 1)
pragmaIdx := pragma.GetCurrentPragmaSetIndex()
@ -320,7 +320,7 @@ func TestNextWithoutFor(t *testing.T) {
func TestForWrongParamCount(t *testing.T) {
pragma := preproc.NewPragma()
ctx := compiler.NewCompilerContext(pragma)
ctx.SymbolTable.AddVar("i", "", compiler.KindByte, 0)
ctx.SymbolTable.AddVar("i", "", compiler.KindByte, 0, "test.c65", 1)
tests := []string{
"FOR i",
@ -348,7 +348,7 @@ func TestForWrongParamCount(t *testing.T) {
func TestForInvalidDirection(t *testing.T) {
pragma := preproc.NewPragma()
ctx := compiler.NewCompilerContext(pragma)
ctx.SymbolTable.AddVar("i", "", compiler.KindByte, 0)
ctx.SymbolTable.AddVar("i", "", compiler.KindByte, 0, "test.c65", 1)
cmd := &ForCommand{}
line := preproc.Line{
@ -369,7 +369,7 @@ func TestForInvalidDirection(t *testing.T) {
func TestForDOWNTORejected(t *testing.T) {
pragma := preproc.NewPragma()
ctx := compiler.NewCompilerContext(pragma)
ctx.SymbolTable.AddVar("i", "", compiler.KindByte, 0)
ctx.SymbolTable.AddVar("i", "", compiler.KindByte, 0, "test.c65", 1)
cmd := &ForCommand{}
line := preproc.Line{
@ -390,7 +390,7 @@ func TestForDOWNTORejected(t *testing.T) {
func TestForConstVariable(t *testing.T) {
pragma := preproc.NewPragma()
ctx := compiler.NewCompilerContext(pragma)
ctx.SymbolTable.AddConst("LIMIT", "", compiler.KindByte, 10)
ctx.SymbolTable.AddConst("LIMIT", "", compiler.KindByte, 10, "test.c65", 1)
cmd := &ForCommand{}
line := preproc.Line{
@ -431,8 +431,8 @@ func TestForUnknownVariable(t *testing.T) {
func TestForConstantEnd(t *testing.T) {
pragma := preproc.NewPragma()
ctx := compiler.NewCompilerContext(pragma)
ctx.SymbolTable.AddVar("i", "", compiler.KindByte, 0)
ctx.SymbolTable.AddConst("MAX", "", compiler.KindByte, 100)
ctx.SymbolTable.AddVar("i", "", compiler.KindByte, 0, "test.c65", 1)
ctx.SymbolTable.AddConst("MAX", "", compiler.KindByte, 100, "test.c65", 1)
forCmd := &ForCommand{}
nextCmd := &NextCommand{}
@ -484,7 +484,7 @@ func TestForByteMaxEndValue(t *testing.T) {
// This naturally handles the max value case (255) without overflow.
pragma := preproc.NewPragma()
ctx := compiler.NewCompilerContext(pragma)
ctx.SymbolTable.AddVar("b", "", compiler.KindByte, 0)
ctx.SymbolTable.AddVar("b", "", compiler.KindByte, 0, "test.c65", 1)
forCmd := &ForCommand{}
nextCmd := &NextCommand{}
@ -543,7 +543,7 @@ func TestForWordMaxEndValue(t *testing.T) {
// Naturally handles the max value case (65535) without overflow.
pragma := preproc.NewPragma()
ctx := compiler.NewCompilerContext(pragma)
ctx.SymbolTable.AddVar("w", "", compiler.KindWord, 0)
ctx.SymbolTable.AddVar("w", "", compiler.KindWord, 0, "test.c65", 1)
forCmd := &ForCommand{}
nextCmd := &NextCommand{}

View file

@ -70,13 +70,7 @@ func (c *GosubCommand) Interpret(line preproc.Line, ctx *compiler.CompilerContex
scope := ctx.CurrentScope()
constLookup := func(name string) (int64, bool) {
sym := ctx.SymbolTable.Lookup(name, scope)
if sym != nil && sym.IsConst() {
return int64(sym.Value), true
}
return 0, false
}
constLookup := ctx.SymbolTable.ConstantLookupFunc(scope)
// Parse target (param 2)
targetParam := params[1]

View file

@ -52,13 +52,7 @@ func (c *GotoCommand) Interpret(line preproc.Line, ctx *compiler.CompilerContext
targetParam := params[1]
scope := ctx.CurrentScope()
constLookup := func(name string) (int64, bool) {
sym := ctx.SymbolTable.Lookup(name, scope)
if sym != nil && sym.IsConst() {
return int64(sym.Value), true
}
return 0, false
}
constLookup := ctx.SymbolTable.ConstantLookupFunc(scope)
// Try ParseOperandParam - handles variables, constants, expressions
varName, varKind, value, isVar, err := compiler.ParseOperandParam(

View file

@ -40,13 +40,7 @@ func (c *IfCommand) Interpret(line preproc.Line, ctx *compiler.CompilerContext)
}
scope := ctx.CurrentScope()
constLookup := func(name string) (int64, bool) {
sym := ctx.SymbolTable.Lookup(name, scope)
if sym != nil && sym.IsConst() {
return int64(sym.Value), true
}
return 0, false
}
constLookup := ctx.SymbolTable.ConstantLookupFunc(scope)
// Parse param1
varName, varKind, value, isVar, err := compiler.ParseOperandParam(

View file

@ -20,7 +20,7 @@ func TestIfBasicEqual(t *testing.T) {
name: "byte var == byte literal",
ifLine: "IF x = 10",
setupVars: func(st *compiler.SymbolTable) {
st.AddVar("x", "", compiler.KindByte, 0)
st.AddVar("x", "", compiler.KindByte, 0, "test.c65", 1)
},
wantIf: []string{
"\tlda x",
@ -35,7 +35,7 @@ func TestIfBasicEqual(t *testing.T) {
name: "word var == word literal",
ifLine: "IF x = 1000",
setupVars: func(st *compiler.SymbolTable) {
st.AddVar("x", "", compiler.KindWord, 0)
st.AddVar("x", "", compiler.KindWord, 0, "test.c65", 1)
},
wantIf: []string{
"\tlda x",
@ -116,7 +116,7 @@ func TestIfElseEndif(t *testing.T) {
name: "byte var with else",
ifLine: "IF x = 10",
setupVars: func(st *compiler.SymbolTable) {
st.AddVar("x", "", compiler.KindByte, 0)
st.AddVar("x", "", compiler.KindByte, 0, "test.c65", 1)
},
wantIf: []string{
"\tlda x",
@ -209,7 +209,7 @@ func TestIfAllOperators(t *testing.T) {
t.Run(tt.name, func(t *testing.T) {
pragma := preproc.NewPragma()
ctx := compiler.NewCompilerContext(pragma)
ctx.SymbolTable.AddVar("x", "", compiler.KindByte, 0)
ctx.SymbolTable.AddVar("x", "", compiler.KindByte, 0, "test.c65", 1)
cmd := &IfCommand{}
line := preproc.Line{
@ -244,8 +244,8 @@ func TestIfAllOperators(t *testing.T) {
func TestIfMixedTypes(t *testing.T) {
pragma := preproc.NewPragma()
ctx := compiler.NewCompilerContext(pragma)
ctx.SymbolTable.AddVar("x", "", compiler.KindByte, 0)
ctx.SymbolTable.AddVar("y", "", compiler.KindWord, 0)
ctx.SymbolTable.AddVar("x", "", compiler.KindByte, 0, "test.c65", 1)
ctx.SymbolTable.AddVar("y", "", compiler.KindWord, 0, "test.c65", 1)
cmd := &IfCommand{}
line := preproc.Line{
@ -318,8 +318,8 @@ func TestElseWithoutIf(t *testing.T) {
func TestIfNested(t *testing.T) {
pragma := preproc.NewPragma()
ctx := compiler.NewCompilerContext(pragma)
ctx.SymbolTable.AddVar("x", "", compiler.KindByte, 0)
ctx.SymbolTable.AddVar("y", "", compiler.KindByte, 0)
ctx.SymbolTable.AddVar("x", "", compiler.KindByte, 0, "test.c65", 1)
ctx.SymbolTable.AddVar("y", "", compiler.KindByte, 0, "test.c65", 1)
pragmaIdx := pragma.GetCurrentPragmaSetIndex()
@ -366,8 +366,8 @@ func TestIfNested(t *testing.T) {
func TestIfNestedWithElse(t *testing.T) {
pragma := preproc.NewPragma()
ctx := compiler.NewCompilerContext(pragma)
ctx.SymbolTable.AddVar("x", "", compiler.KindByte, 0)
ctx.SymbolTable.AddVar("y", "", compiler.KindByte, 0)
ctx.SymbolTable.AddVar("x", "", compiler.KindByte, 0, "test.c65", 1)
ctx.SymbolTable.AddVar("y", "", compiler.KindByte, 0, "test.c65", 1)
pragmaIdx := pragma.GetCurrentPragmaSetIndex()
@ -407,7 +407,7 @@ func TestIfLongJump(t *testing.T) {
pragma := preproc.NewPragma()
pragma.AddPragma("_P_USE_LONG_JUMP", "1")
ctx := compiler.NewCompilerContext(pragma)
ctx.SymbolTable.AddVar("x", "", compiler.KindByte, 0)
ctx.SymbolTable.AddVar("x", "", compiler.KindByte, 0, "test.c65", 1)
cmd := &IfCommand{}
line := preproc.Line{
@ -440,8 +440,8 @@ func TestIfLongJump(t *testing.T) {
func TestIfConstant(t *testing.T) {
pragma := preproc.NewPragma()
ctx := compiler.NewCompilerContext(pragma)
ctx.SymbolTable.AddConst("MAX", "", compiler.KindByte, 100)
ctx.SymbolTable.AddVar("x", "", compiler.KindByte, 0)
ctx.SymbolTable.AddConst("MAX", "", compiler.KindByte, 100, "test.c65", 1)
ctx.SymbolTable.AddVar("x", "", compiler.KindByte, 0, "test.c65", 1)
cmd := &IfCommand{}
line := preproc.Line{
@ -499,7 +499,7 @@ func TestIfWrongParamCount(t *testing.T) {
func TestElseWrongParamCount(t *testing.T) {
pragma := preproc.NewPragma()
ctx := compiler.NewCompilerContext(pragma)
ctx.SymbolTable.AddVar("x", "", compiler.KindByte, 0)
ctx.SymbolTable.AddVar("x", "", compiler.KindByte, 0, "test.c65", 1)
// Setup IF first
ifCmd := &IfCommand{}
@ -525,7 +525,7 @@ func TestElseWrongParamCount(t *testing.T) {
func TestEndifWrongParamCount(t *testing.T) {
pragma := preproc.NewPragma()
ctx := compiler.NewCompilerContext(pragma)
ctx.SymbolTable.AddVar("x", "", compiler.KindByte, 0)
ctx.SymbolTable.AddVar("x", "", compiler.KindByte, 0, "test.c65", 1)
// Setup IF first
ifCmd := &IfCommand{}

View file

@ -97,13 +97,7 @@ func (c *IncrCommand) Interpret(line preproc.Line, ctx *compiler.CompilerContext
}
// Old syntax allows absolute addresses
constLookup := func(name string) (int64, bool) {
s := ctx.SymbolTable.Lookup(name, scope)
if s != nil && s.IsConst() {
return int64(s.Value), true
}
return 0, false
}
constLookup := ctx.SymbolTable.ConstantLookupFunc(scope)
val, evalErr := utils.EvaluateExpression(targetParam, constLookup)
if evalErr != nil {

View file

@ -77,7 +77,7 @@ func TestIncrCommand_InterpretAndGenerate(t *testing.T) {
{
name: "INC byte variable old syntax",
setup: func(ctx *compiler.CompilerContext) {
ctx.SymbolTable.AddVar("counter", "", compiler.KindByte, 0)
ctx.SymbolTable.AddVar("counter", "", compiler.KindByte, 0, "test.c65", 1)
},
line: "INC counter",
expectError: false,
@ -94,7 +94,7 @@ func TestIncrCommand_InterpretAndGenerate(t *testing.T) {
{
name: "INC byte variable new syntax",
setup: func(ctx *compiler.CompilerContext) {
ctx.SymbolTable.AddVar("counter", "", compiler.KindByte, 0)
ctx.SymbolTable.AddVar("counter", "", compiler.KindByte, 0, "test.c65", 1)
},
line: "counter++",
expectError: false,
@ -111,7 +111,7 @@ func TestIncrCommand_InterpretAndGenerate(t *testing.T) {
{
name: "INC word variable old syntax",
setup: func(ctx *compiler.CompilerContext) {
ctx.SymbolTable.AddVar("pointer", "", compiler.KindWord, 0)
ctx.SymbolTable.AddVar("pointer", "", compiler.KindWord, 0, "test.c65", 1)
},
line: "INCREMENT pointer",
expectError: false,
@ -135,7 +135,7 @@ func TestIncrCommand_InterpretAndGenerate(t *testing.T) {
{
name: "INC word variable new syntax",
setup: func(ctx *compiler.CompilerContext) {
ctx.SymbolTable.AddVar("pointer", "", compiler.KindWord, 0)
ctx.SymbolTable.AddVar("pointer", "", compiler.KindWord, 0, "test.c65", 1)
},
line: "pointer++",
expectError: false,
@ -174,7 +174,7 @@ func TestIncrCommand_InterpretAndGenerate(t *testing.T) {
{
name: "Error: INC constant variable",
setup: func(ctx *compiler.CompilerContext) {
ctx.SymbolTable.AddConst("MAX", "", compiler.KindByte, 100)
ctx.SymbolTable.AddConst("MAX", "", compiler.KindByte, 100, "test.c65", 1)
},
line: "INC MAX",
expectError: true,
@ -233,7 +233,7 @@ func TestDecrCommand_InterpretAndGenerate(t *testing.T) {
{
name: "DEC byte variable old syntax",
setup: func(ctx *compiler.CompilerContext) {
ctx.SymbolTable.AddVar("counter", "", compiler.KindByte, 0)
ctx.SymbolTable.AddVar("counter", "", compiler.KindByte, 0, "test.c65", 1)
},
line: "DEC counter",
expectError: false,
@ -250,7 +250,7 @@ func TestDecrCommand_InterpretAndGenerate(t *testing.T) {
{
name: "DEC byte variable new syntax",
setup: func(ctx *compiler.CompilerContext) {
ctx.SymbolTable.AddVar("counter", "", compiler.KindByte, 0)
ctx.SymbolTable.AddVar("counter", "", compiler.KindByte, 0, "test.c65", 1)
},
line: "counter--",
expectError: false,
@ -267,7 +267,7 @@ func TestDecrCommand_InterpretAndGenerate(t *testing.T) {
{
name: "DEC word variable old syntax",
setup: func(ctx *compiler.CompilerContext) {
ctx.SymbolTable.AddVar("pointer", "", compiler.KindWord, 0)
ctx.SymbolTable.AddVar("pointer", "", compiler.KindWord, 0, "test.c65", 1)
},
line: "DECREMENT pointer",
expectError: false,
@ -294,7 +294,7 @@ func TestDecrCommand_InterpretAndGenerate(t *testing.T) {
{
name: "DEC word variable new syntax",
setup: func(ctx *compiler.CompilerContext) {
ctx.SymbolTable.AddVar("pointer", "", compiler.KindWord, 0)
ctx.SymbolTable.AddVar("pointer", "", compiler.KindWord, 0, "test.c65", 1)
},
line: "pointer--",
expectError: false,
@ -332,7 +332,7 @@ func TestDecrCommand_InterpretAndGenerate(t *testing.T) {
{
name: "Error: DEC constant variable",
setup: func(ctx *compiler.CompilerContext) {
ctx.SymbolTable.AddConst("MAX", "", compiler.KindByte, 100)
ctx.SymbolTable.AddConst("MAX", "", compiler.KindByte, 100, "test.c65", 1)
},
line: "DEC MAX",
expectError: true,
@ -385,7 +385,7 @@ func TestIncrDecrCommand_FullNameResolution(t *testing.T) {
ctx := compiler.NewCompilerContext(preproc.NewPragma())
// Add a variable with scoped name directly
ctx.SymbolTable.AddVar("counter", "myfunc", compiler.KindWord, 0)
ctx.SymbolTable.AddVar("counter", "myfunc", compiler.KindWord, 0, "test.c65", 1)
// Note: CurrentScope will return nil (global) since we're not in a function context
// The symbol table lookup will try scoped search and fall back to global

View file

@ -62,13 +62,7 @@ func (c *LetCommand) Interpret(line preproc.Line, ctx *compiler.CompilerContext)
scope := ctx.CurrentScope()
// Create constant lookup function
constLookup := func(name string) (int64, bool) {
sym := ctx.SymbolTable.Lookup(name, scope)
if sym != nil && sym.IsConst() {
return int64(sym.Value), true
}
return 0, false
}
constLookup := ctx.SymbolTable.ConstantLookupFunc(scope)
// Determine syntax and parse accordingly
if strings.ToUpper(params[0]) == "LET" {

View file

@ -48,8 +48,8 @@ func TestLetCommand_OldSyntax(t *testing.T) {
name: "LET byte GET byte",
line: "LET a GET b",
setupVars: func(st *compiler.SymbolTable) {
st.AddVar("a", "", compiler.KindByte, 0)
st.AddVar("b", "", compiler.KindByte, 10)
st.AddVar("a", "", compiler.KindByte, 0, "test.c65", 1)
st.AddVar("b", "", compiler.KindByte, 10, "test.c65", 1)
},
wantAsm: []string{
"\tlda b",
@ -60,7 +60,7 @@ func TestLetCommand_OldSyntax(t *testing.T) {
name: "LET byte = literal",
line: "LET a = 100",
setupVars: func(st *compiler.SymbolTable) {
st.AddVar("a", "", compiler.KindByte, 0)
st.AddVar("a", "", compiler.KindByte, 0, "test.c65", 1)
},
wantAsm: []string{
"\tlda #$64",
@ -71,8 +71,8 @@ func TestLetCommand_OldSyntax(t *testing.T) {
name: "LET word GET word",
line: "LET x GET y",
setupVars: func(st *compiler.SymbolTable) {
st.AddVar("x", "", compiler.KindWord, 0)
st.AddVar("y", "", compiler.KindWord, 0x1234)
st.AddVar("x", "", compiler.KindWord, 0, "test.c65", 1)
st.AddVar("y", "", compiler.KindWord, 0x1234, "test.c65", 1)
},
wantAsm: []string{
"\tlda y",
@ -85,7 +85,7 @@ func TestLetCommand_OldSyntax(t *testing.T) {
name: "LET word = literal",
line: "LET x = $1234",
setupVars: func(st *compiler.SymbolTable) {
st.AddVar("x", "", compiler.KindWord, 0)
st.AddVar("x", "", compiler.KindWord, 0, "test.c65", 1)
},
wantAsm: []string{
"\tlda #$34",
@ -98,8 +98,8 @@ func TestLetCommand_OldSyntax(t *testing.T) {
name: "LET word GET byte (zero-extend)",
line: "LET x GET b",
setupVars: func(st *compiler.SymbolTable) {
st.AddVar("x", "", compiler.KindWord, 0)
st.AddVar("b", "", compiler.KindByte, 100)
st.AddVar("x", "", compiler.KindWord, 0, "test.c65", 1)
st.AddVar("b", "", compiler.KindByte, 100, "test.c65", 1)
},
wantAsm: []string{
"\tlda b",
@ -112,8 +112,8 @@ func TestLetCommand_OldSyntax(t *testing.T) {
name: "LET byte GET word (take low byte)",
line: "LET b GET x",
setupVars: func(st *compiler.SymbolTable) {
st.AddVar("b", "", compiler.KindByte, 0)
st.AddVar("x", "", compiler.KindWord, 0x1234)
st.AddVar("b", "", compiler.KindByte, 0, "test.c65", 1)
st.AddVar("x", "", compiler.KindWord, 0x1234, "test.c65", 1)
},
wantAsm: []string{
"\tlda x",
@ -124,7 +124,7 @@ func TestLetCommand_OldSyntax(t *testing.T) {
name: "LET word = $0000 (optimization)",
line: "LET x = 0",
setupVars: func(st *compiler.SymbolTable) {
st.AddVar("x", "", compiler.KindWord, 0)
st.AddVar("x", "", compiler.KindWord, 0, "test.c65", 1)
},
wantAsm: []string{
"\tlda #$00",
@ -136,7 +136,7 @@ func TestLetCommand_OldSyntax(t *testing.T) {
name: "LET word = $FFFF (optimization)",
line: "LET x = $FFFF",
setupVars: func(st *compiler.SymbolTable) {
st.AddVar("x", "", compiler.KindWord, 0)
st.AddVar("x", "", compiler.KindWord, 0, "test.c65", 1)
},
wantAsm: []string{
"\tlda #$ff",
@ -148,8 +148,8 @@ func TestLetCommand_OldSyntax(t *testing.T) {
name: "LET with constant",
line: "LET a = MAXVAL",
setupVars: func(st *compiler.SymbolTable) {
st.AddVar("a", "", compiler.KindByte, 0)
st.AddConst("MAXVAL", "", compiler.KindByte, 255)
st.AddVar("a", "", compiler.KindByte, 0, "test.c65", 1)
st.AddConst("MAXVAL", "", compiler.KindByte, 255, "test.c65", 1)
},
wantAsm: []string{
"\tlda #$ff",
@ -160,7 +160,7 @@ func TestLetCommand_OldSyntax(t *testing.T) {
name: "error: unknown destination",
line: "LET unknown GET a",
setupVars: func(st *compiler.SymbolTable) {
st.AddVar("a", "", compiler.KindByte, 0)
st.AddVar("a", "", compiler.KindByte, 0, "test.c65", 1)
},
wantErr: true,
},
@ -168,8 +168,8 @@ func TestLetCommand_OldSyntax(t *testing.T) {
name: "error: wrong separator",
line: "LET a TO b",
setupVars: func(st *compiler.SymbolTable) {
st.AddVar("a", "", compiler.KindByte, 0)
st.AddVar("b", "", compiler.KindByte, 0)
st.AddVar("a", "", compiler.KindByte, 0, "test.c65", 1)
st.AddVar("b", "", compiler.KindByte, 0, "test.c65", 1)
},
wantErr: true,
},
@ -177,7 +177,7 @@ func TestLetCommand_OldSyntax(t *testing.T) {
name: "error: cannot assign to constant",
line: "LET MAXVAL = 100",
setupVars: func(st *compiler.SymbolTable) {
st.AddConst("MAXVAL", "", compiler.KindByte, 255)
st.AddConst("MAXVAL", "", compiler.KindByte, 255, "test.c65", 1)
},
wantErr: true,
},
@ -228,8 +228,8 @@ func TestLetCommand_NewSyntax(t *testing.T) {
name: "byte = byte",
line: "a = b",
setupVars: func(st *compiler.SymbolTable) {
st.AddVar("a", "", compiler.KindByte, 0)
st.AddVar("b", "", compiler.KindByte, 10)
st.AddVar("a", "", compiler.KindByte, 0, "test.c65", 1)
st.AddVar("b", "", compiler.KindByte, 10, "test.c65", 1)
},
wantAsm: []string{
"\tlda b",
@ -240,7 +240,7 @@ func TestLetCommand_NewSyntax(t *testing.T) {
name: "byte = literal",
line: "a = 100",
setupVars: func(st *compiler.SymbolTable) {
st.AddVar("a", "", compiler.KindByte, 0)
st.AddVar("a", "", compiler.KindByte, 0, "test.c65", 1)
},
wantAsm: []string{
"\tlda #$64",
@ -251,8 +251,8 @@ func TestLetCommand_NewSyntax(t *testing.T) {
name: "word = word",
line: "x = y",
setupVars: func(st *compiler.SymbolTable) {
st.AddVar("x", "", compiler.KindWord, 0)
st.AddVar("y", "", compiler.KindWord, 0x1234)
st.AddVar("x", "", compiler.KindWord, 0, "test.c65", 1)
st.AddVar("y", "", compiler.KindWord, 0x1234, "test.c65", 1)
},
wantAsm: []string{
"\tlda y",
@ -265,7 +265,7 @@ func TestLetCommand_NewSyntax(t *testing.T) {
name: "word = literal",
line: "x = $1234",
setupVars: func(st *compiler.SymbolTable) {
st.AddVar("x", "", compiler.KindWord, 0)
st.AddVar("x", "", compiler.KindWord, 0, "test.c65", 1)
},
wantAsm: []string{
"\tlda #$34",
@ -278,8 +278,8 @@ func TestLetCommand_NewSyntax(t *testing.T) {
name: "word = byte (zero-extend)",
line: "x = b",
setupVars: func(st *compiler.SymbolTable) {
st.AddVar("x", "", compiler.KindWord, 0)
st.AddVar("b", "", compiler.KindByte, 100)
st.AddVar("x", "", compiler.KindWord, 0, "test.c65", 1)
st.AddVar("b", "", compiler.KindByte, 100, "test.c65", 1)
},
wantAsm: []string{
"\tlda b",
@ -292,8 +292,8 @@ func TestLetCommand_NewSyntax(t *testing.T) {
name: "byte = word (take low byte)",
line: "b = x",
setupVars: func(st *compiler.SymbolTable) {
st.AddVar("b", "", compiler.KindByte, 0)
st.AddVar("x", "", compiler.KindWord, 0x1234)
st.AddVar("b", "", compiler.KindByte, 0, "test.c65", 1)
st.AddVar("x", "", compiler.KindWord, 0x1234, "test.c65", 1)
},
wantAsm: []string{
"\tlda x",
@ -304,7 +304,7 @@ func TestLetCommand_NewSyntax(t *testing.T) {
name: "word = 0 (optimization)",
line: "x = 0",
setupVars: func(st *compiler.SymbolTable) {
st.AddVar("x", "", compiler.KindWord, 0)
st.AddVar("x", "", compiler.KindWord, 0, "test.c65", 1)
},
wantAsm: []string{
"\tlda #$00",
@ -316,7 +316,7 @@ func TestLetCommand_NewSyntax(t *testing.T) {
name: "word = $FFFF (optimization)",
line: "x = 65535",
setupVars: func(st *compiler.SymbolTable) {
st.AddVar("x", "", compiler.KindWord, 0)
st.AddVar("x", "", compiler.KindWord, 0, "test.c65", 1)
},
wantAsm: []string{
"\tlda #$ff",
@ -328,7 +328,7 @@ func TestLetCommand_NewSyntax(t *testing.T) {
name: "word = $0102 (different bytes, no optimization)",
line: "x = $0102",
setupVars: func(st *compiler.SymbolTable) {
st.AddVar("x", "", compiler.KindWord, 0)
st.AddVar("x", "", compiler.KindWord, 0, "test.c65", 1)
},
wantAsm: []string{
"\tlda #$02",
@ -341,8 +341,8 @@ func TestLetCommand_NewSyntax(t *testing.T) {
name: "using constant",
line: "a = MAXVAL",
setupVars: func(st *compiler.SymbolTable) {
st.AddVar("a", "", compiler.KindByte, 0)
st.AddConst("MAXVAL", "", compiler.KindByte, 255)
st.AddVar("a", "", compiler.KindByte, 0, "test.c65", 1)
st.AddConst("MAXVAL", "", compiler.KindByte, 255, "test.c65", 1)
},
wantAsm: []string{
"\tlda #$ff",
@ -353,7 +353,7 @@ func TestLetCommand_NewSyntax(t *testing.T) {
name: "expression with constant",
line: "a = 10+5",
setupVars: func(st *compiler.SymbolTable) {
st.AddVar("a", "", compiler.KindByte, 0)
st.AddVar("a", "", compiler.KindByte, 0, "test.c65", 1)
},
wantAsm: []string{
"\tlda #$0f",
@ -364,7 +364,7 @@ func TestLetCommand_NewSyntax(t *testing.T) {
name: "error: unknown destination",
line: "unknown = a",
setupVars: func(st *compiler.SymbolTable) {
st.AddVar("a", "", compiler.KindByte, 0)
st.AddVar("a", "", compiler.KindByte, 0, "test.c65", 1)
},
wantErr: true,
},
@ -372,7 +372,7 @@ func TestLetCommand_NewSyntax(t *testing.T) {
name: "error: cannot assign to constant",
line: "MAXVAL = 100",
setupVars: func(st *compiler.SymbolTable) {
st.AddConst("MAXVAL", "", compiler.KindByte, 255)
st.AddConst("MAXVAL", "", compiler.KindByte, 255, "test.c65", 1)
},
wantErr: true,
},

View file

@ -68,13 +68,7 @@ func (c *OrCommand) Interpret(line preproc.Line, ctx *compiler.CompilerContext)
scope := ctx.CurrentScope()
// Create constant lookup function
constLookup := func(name string) (int64, bool) {
sym := ctx.SymbolTable.Lookup(name, scope)
if sym != nil && sym.IsConst() {
return int64(sym.Value), true
}
return 0, false
}
constLookup := ctx.SymbolTable.ConstantLookupFunc(scope)
// Determine syntax and parse accordingly
if strings.ToUpper(params[0]) == "OR" {

View file

@ -48,9 +48,9 @@ func TestOrCommand_OldSyntax(t *testing.T) {
name: "byte OR byte -> byte (variables)",
line: "OR a WITH b GIVING result",
setupVars: func(st *compiler.SymbolTable) {
st.AddVar("a", "", compiler.KindByte, 0xF0)
st.AddVar("b", "", compiler.KindByte, 0x0F)
st.AddVar("result", "", compiler.KindByte, 0)
st.AddVar("a", "", compiler.KindByte, 0xF0, "test.c65", 1)
st.AddVar("b", "", compiler.KindByte, 0x0F, "test.c65", 1)
st.AddVar("result", "", compiler.KindByte, 0, "test.c65", 1)
},
wantAsm: []string{
"\tlda a",
@ -62,9 +62,9 @@ func TestOrCommand_OldSyntax(t *testing.T) {
name: "byte OR byte -> word",
line: "OR a WITH b GIVING result",
setupVars: func(st *compiler.SymbolTable) {
st.AddVar("a", "", compiler.KindByte, 0xF0)
st.AddVar("b", "", compiler.KindByte, 0x0F)
st.AddVar("result", "", compiler.KindWord, 0)
st.AddVar("a", "", compiler.KindByte, 0xF0, "test.c65", 1)
st.AddVar("b", "", compiler.KindByte, 0x0F, "test.c65", 1)
st.AddVar("result", "", compiler.KindWord, 0, "test.c65", 1)
},
wantAsm: []string{
"\tlda a",
@ -79,9 +79,9 @@ func TestOrCommand_OldSyntax(t *testing.T) {
name: "word OR word -> word",
line: "OR x WITH y GIVING result",
setupVars: func(st *compiler.SymbolTable) {
st.AddVar("x", "", compiler.KindWord, 0x1234)
st.AddVar("y", "", compiler.KindWord, 0x0F0F)
st.AddVar("result", "", compiler.KindWord, 0)
st.AddVar("x", "", compiler.KindWord, 0x1234, "test.c65", 1)
st.AddVar("y", "", compiler.KindWord, 0x0F0F, "test.c65", 1)
st.AddVar("result", "", compiler.KindWord, 0, "test.c65", 1)
},
wantAsm: []string{
"\tlda x",
@ -96,8 +96,8 @@ func TestOrCommand_OldSyntax(t *testing.T) {
name: "byte OR literal -> byte",
line: "OR a WITH $0F GIVING result",
setupVars: func(st *compiler.SymbolTable) {
st.AddVar("a", "", compiler.KindByte, 0xF0)
st.AddVar("result", "", compiler.KindByte, 0)
st.AddVar("a", "", compiler.KindByte, 0xF0, "test.c65", 1)
st.AddVar("result", "", compiler.KindByte, 0, "test.c65", 1)
},
wantAsm: []string{
"\tlda a",
@ -109,8 +109,8 @@ func TestOrCommand_OldSyntax(t *testing.T) {
name: "literal OR byte -> byte",
line: "OR 15 WITH b GIVING result",
setupVars: func(st *compiler.SymbolTable) {
st.AddVar("b", "", compiler.KindByte, 0xF0)
st.AddVar("result", "", compiler.KindByte, 0)
st.AddVar("b", "", compiler.KindByte, 0xF0, "test.c65", 1)
st.AddVar("result", "", compiler.KindByte, 0, "test.c65", 1)
},
wantAsm: []string{
"\tlda #$0f",
@ -122,7 +122,7 @@ func TestOrCommand_OldSyntax(t *testing.T) {
name: "constant folding: 15 OR 240 -> byte",
line: "OR 15 WITH 240 GIVING result",
setupVars: func(st *compiler.SymbolTable) {
st.AddVar("result", "", compiler.KindByte, 0)
st.AddVar("result", "", compiler.KindByte, 0, "test.c65", 1)
},
wantAsm: []string{
"\tlda #$ff",
@ -133,7 +133,7 @@ func TestOrCommand_OldSyntax(t *testing.T) {
name: "constant folding: $00F0 OR $0F00 -> word",
line: "OR $00F0 WITH $0F00 GIVING result",
setupVars: func(st *compiler.SymbolTable) {
st.AddVar("result", "", compiler.KindWord, 0)
st.AddVar("result", "", compiler.KindWord, 0, "test.c65", 1)
},
wantAsm: []string{
"\tlda #$f0",
@ -146,9 +146,9 @@ func TestOrCommand_OldSyntax(t *testing.T) {
name: "arrow syntax",
line: "OR a WITH b -> result",
setupVars: func(st *compiler.SymbolTable) {
st.AddVar("a", "", compiler.KindByte, 0)
st.AddVar("b", "", compiler.KindByte, 0)
st.AddVar("result", "", compiler.KindByte, 0)
st.AddVar("a", "", compiler.KindByte, 0, "test.c65", 1)
st.AddVar("b", "", compiler.KindByte, 0, "test.c65", 1)
st.AddVar("result", "", compiler.KindByte, 0, "test.c65", 1)
},
wantAsm: []string{
"\tlda a",
@ -160,9 +160,9 @@ func TestOrCommand_OldSyntax(t *testing.T) {
name: "word OR byte -> byte",
line: "OR wval WITH bval GIVING result",
setupVars: func(st *compiler.SymbolTable) {
st.AddVar("wval", "", compiler.KindWord, 0x1234)
st.AddVar("bval", "", compiler.KindByte, 0x0F)
st.AddVar("result", "", compiler.KindByte, 0)
st.AddVar("wval", "", compiler.KindWord, 0x1234, "test.c65", 1)
st.AddVar("bval", "", compiler.KindByte, 0x0F, "test.c65", 1)
st.AddVar("result", "", compiler.KindByte, 0, "test.c65", 1)
},
wantAsm: []string{
"\tlda wval",
@ -174,9 +174,9 @@ func TestOrCommand_OldSyntax(t *testing.T) {
name: "word OR byte -> word",
line: "OR wval WITH bval GIVING result",
setupVars: func(st *compiler.SymbolTable) {
st.AddVar("wval", "", compiler.KindWord, 0x1234)
st.AddVar("bval", "", compiler.KindByte, 0x0F)
st.AddVar("result", "", compiler.KindWord, 0)
st.AddVar("wval", "", compiler.KindWord, 0x1234, "test.c65", 1)
st.AddVar("bval", "", compiler.KindByte, 0x0F, "test.c65", 1)
st.AddVar("result", "", compiler.KindWord, 0, "test.c65", 1)
},
wantAsm: []string{
"\tlda wval",
@ -191,8 +191,8 @@ func TestOrCommand_OldSyntax(t *testing.T) {
name: "error: unknown destination variable",
line: "OR a WITH b GIVING unknown",
setupVars: func(st *compiler.SymbolTable) {
st.AddVar("a", "", compiler.KindByte, 0)
st.AddVar("b", "", compiler.KindByte, 0)
st.AddVar("a", "", compiler.KindByte, 0, "test.c65", 1)
st.AddVar("b", "", compiler.KindByte, 0, "test.c65", 1)
},
wantErr: true,
},
@ -200,9 +200,9 @@ func TestOrCommand_OldSyntax(t *testing.T) {
name: "error: wrong separator",
line: "OR a TO b GIVING result",
setupVars: func(st *compiler.SymbolTable) {
st.AddVar("a", "", compiler.KindByte, 0)
st.AddVar("b", "", compiler.KindByte, 0)
st.AddVar("result", "", compiler.KindByte, 0)
st.AddVar("a", "", compiler.KindByte, 0, "test.c65", 1)
st.AddVar("b", "", compiler.KindByte, 0, "test.c65", 1)
st.AddVar("result", "", compiler.KindByte, 0, "test.c65", 1)
},
wantErr: true,
},
@ -210,9 +210,9 @@ func TestOrCommand_OldSyntax(t *testing.T) {
name: "error: cannot assign to constant",
line: "OR a WITH b GIVING MAXVAL",
setupVars: func(st *compiler.SymbolTable) {
st.AddVar("a", "", compiler.KindByte, 0)
st.AddVar("b", "", compiler.KindByte, 0)
st.AddConst("MAXVAL", "", compiler.KindByte, 255)
st.AddVar("a", "", compiler.KindByte, 0, "test.c65", 1)
st.AddVar("b", "", compiler.KindByte, 0, "test.c65", 1)
st.AddConst("MAXVAL", "", compiler.KindByte, 255, "test.c65", 1)
},
wantErr: true,
},
@ -263,9 +263,9 @@ func TestOrCommand_NewSyntax(t *testing.T) {
name: "byte | byte -> byte",
line: "result = a | b",
setupVars: func(st *compiler.SymbolTable) {
st.AddVar("a", "", compiler.KindByte, 0xF0)
st.AddVar("b", "", compiler.KindByte, 0x0F)
st.AddVar("result", "", compiler.KindByte, 0)
st.AddVar("a", "", compiler.KindByte, 0xF0, "test.c65", 1)
st.AddVar("b", "", compiler.KindByte, 0x0F, "test.c65", 1)
st.AddVar("result", "", compiler.KindByte, 0, "test.c65", 1)
},
wantAsm: []string{
"\tlda a",
@ -277,9 +277,9 @@ func TestOrCommand_NewSyntax(t *testing.T) {
name: "byte | byte -> word",
line: "result = a | b",
setupVars: func(st *compiler.SymbolTable) {
st.AddVar("a", "", compiler.KindByte, 0xF0)
st.AddVar("b", "", compiler.KindByte, 0x0F)
st.AddVar("result", "", compiler.KindWord, 0)
st.AddVar("a", "", compiler.KindByte, 0xF0, "test.c65", 1)
st.AddVar("b", "", compiler.KindByte, 0x0F, "test.c65", 1)
st.AddVar("result", "", compiler.KindWord, 0, "test.c65", 1)
},
wantAsm: []string{
"\tlda a",
@ -294,9 +294,9 @@ func TestOrCommand_NewSyntax(t *testing.T) {
name: "word | word -> word",
line: "result = x | y",
setupVars: func(st *compiler.SymbolTable) {
st.AddVar("x", "", compiler.KindWord, 0x1234)
st.AddVar("y", "", compiler.KindWord, 0x0F0F)
st.AddVar("result", "", compiler.KindWord, 0)
st.AddVar("x", "", compiler.KindWord, 0x1234, "test.c65", 1)
st.AddVar("y", "", compiler.KindWord, 0x0F0F, "test.c65", 1)
st.AddVar("result", "", compiler.KindWord, 0, "test.c65", 1)
},
wantAsm: []string{
"\tlda x",
@ -311,8 +311,8 @@ func TestOrCommand_NewSyntax(t *testing.T) {
name: "variable | literal",
line: "result = a | $0F",
setupVars: func(st *compiler.SymbolTable) {
st.AddVar("a", "", compiler.KindByte, 0xF0)
st.AddVar("result", "", compiler.KindByte, 0)
st.AddVar("a", "", compiler.KindByte, 0xF0, "test.c65", 1)
st.AddVar("result", "", compiler.KindByte, 0, "test.c65", 1)
},
wantAsm: []string{
"\tlda a",
@ -324,7 +324,7 @@ func TestOrCommand_NewSyntax(t *testing.T) {
name: "constant folding",
line: "result = 15 | 240",
setupVars: func(st *compiler.SymbolTable) {
st.AddVar("result", "", compiler.KindByte, 0)
st.AddVar("result", "", compiler.KindByte, 0, "test.c65", 1)
},
wantAsm: []string{
"\tlda #$ff",
@ -335,7 +335,7 @@ func TestOrCommand_NewSyntax(t *testing.T) {
name: "constant folding word",
line: "result = $00F0 | $0F00",
setupVars: func(st *compiler.SymbolTable) {
st.AddVar("result", "", compiler.KindWord, 0)
st.AddVar("result", "", compiler.KindWord, 0, "test.c65", 1)
},
wantAsm: []string{
"\tlda #$f0",
@ -348,9 +348,9 @@ func TestOrCommand_NewSyntax(t *testing.T) {
name: "using constant in expression",
line: "result = a | BITS",
setupVars: func(st *compiler.SymbolTable) {
st.AddVar("a", "", compiler.KindByte, 0xF0)
st.AddConst("BITS", "", compiler.KindByte, 0x0F)
st.AddVar("result", "", compiler.KindByte, 0)
st.AddVar("a", "", compiler.KindByte, 0xF0, "test.c65", 1)
st.AddConst("BITS", "", compiler.KindByte, 0x0F, "test.c65", 1)
st.AddVar("result", "", compiler.KindByte, 0, "test.c65", 1)
},
wantAsm: []string{
"\tlda a",
@ -362,8 +362,8 @@ func TestOrCommand_NewSyntax(t *testing.T) {
name: "error: unknown destination",
line: "unknown = a | b",
setupVars: func(st *compiler.SymbolTable) {
st.AddVar("a", "", compiler.KindByte, 0)
st.AddVar("b", "", compiler.KindByte, 0)
st.AddVar("a", "", compiler.KindByte, 0, "test.c65", 1)
st.AddVar("b", "", compiler.KindByte, 0, "test.c65", 1)
},
wantErr: true,
},
@ -371,9 +371,9 @@ func TestOrCommand_NewSyntax(t *testing.T) {
name: "error: wrong operator",
line: "result = a + b",
setupVars: func(st *compiler.SymbolTable) {
st.AddVar("a", "", compiler.KindByte, 0)
st.AddVar("b", "", compiler.KindByte, 0)
st.AddVar("result", "", compiler.KindByte, 0)
st.AddVar("a", "", compiler.KindByte, 0, "test.c65", 1)
st.AddVar("b", "", compiler.KindByte, 0, "test.c65", 1)
st.AddVar("result", "", compiler.KindByte, 0, "test.c65", 1)
},
wantErr: true,
},

View file

@ -36,13 +36,7 @@ func (c *OriginCommand) Interpret(line preproc.Line, ctx *compiler.CompilerConte
}
scope := ctx.CurrentScope()
constLookup := func(name string) (int64, bool) {
sym := ctx.SymbolTable.Lookup(name, scope)
if sym != nil && sym.IsConst() {
return int64(sym.Value), true
}
return 0, false
}
constLookup := ctx.SymbolTable.ConstantLookupFunc(scope)
val, err := utils.EvaluateExpression(params[1], constLookup)
if err != nil {

View file

@ -115,13 +115,7 @@ func (c *PeekCommand) Interpret(line preproc.Line, ctx *compiler.CompilerContext
// Parse address - may have [offset]
baseAddr, offsetParam := parseOffset(addrParam)
constLookup := func(name string) (int64, bool) {
sym := ctx.SymbolTable.Lookup(name, scope)
if sym != nil && sym.IsConst() {
return int64(sym.Value), true
}
return 0, false
}
constLookup := ctx.SymbolTable.ConstantLookupFunc(scope)
// Try to parse base address
varName, varKind, value, isVar, err := compiler.ParseOperandParam(

View file

@ -19,7 +19,7 @@ func TestPeekOldSyntax(t *testing.T) {
name: "peek constant address to byte",
line: "PEEK 1024 GIVING result",
setupVars: func(st *compiler.SymbolTable) {
st.AddVar("result", "", compiler.KindByte, 0)
st.AddVar("result", "", compiler.KindByte, 0, "test.c65", 1)
},
wantAsm: []string{
"\tlda 1024",
@ -30,8 +30,8 @@ func TestPeekOldSyntax(t *testing.T) {
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)
st.AddAbsolute("ptr", "", compiler.KindWord, 0x80, "test.c65", 1)
st.AddVar("result", "", compiler.KindByte, 0, "test.c65", 1)
},
wantAsm: []string{
"\tldy #0",
@ -43,8 +43,8 @@ func TestPeekOldSyntax(t *testing.T) {
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)
st.AddAbsolute("ptr", "", compiler.KindWord, 0x80, "test.c65", 1)
st.AddVar("result", "", compiler.KindByte, 0, "test.c65", 1)
},
wantAsm: []string{
"\tldy #5",
@ -56,7 +56,7 @@ func TestPeekOldSyntax(t *testing.T) {
name: "peek with -> separator",
line: "PEEK 2048 -> result",
setupVars: func(st *compiler.SymbolTable) {
st.AddVar("result", "", compiler.KindByte, 0)
st.AddVar("result", "", compiler.KindByte, 0, "test.c65", 1)
},
wantAsm: []string{
"\tlda 2048",
@ -107,7 +107,7 @@ func TestPeekNewSyntax(t *testing.T) {
name: "new syntax constant address to byte",
line: "result = PEEK 1024",
setupVars: func(st *compiler.SymbolTable) {
st.AddVar("result", "", compiler.KindByte, 0)
st.AddVar("result", "", compiler.KindByte, 0, "test.c65", 1)
},
wantAsm: []string{
"\tlda 1024",
@ -118,8 +118,8 @@ func TestPeekNewSyntax(t *testing.T) {
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)
st.AddAbsolute("ptr", "", compiler.KindWord, 0x80, "test.c65", 1)
st.AddVar("result", "", compiler.KindByte, 0, "test.c65", 1)
},
wantAsm: []string{
"\tldy #0",
@ -131,8 +131,8 @@ func TestPeekNewSyntax(t *testing.T) {
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)
st.AddAbsolute("ptr", "", compiler.KindWord, 0x80, "test.c65", 1)
st.AddVar("result", "", compiler.KindByte, 0, "test.c65", 1)
},
wantAsm: []string{
"\tldy #10",
@ -144,9 +144,9 @@ func TestPeekNewSyntax(t *testing.T) {
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)
st.AddAbsolute("ptr", "", compiler.KindWord, 0x80, "test.c65", 1)
st.AddVar("idx", "", compiler.KindByte, 0, "test.c65", 1)
st.AddVar("result", "", compiler.KindByte, 0, "test.c65", 1)
},
wantAsm: []string{
"\tldy idx",
@ -158,7 +158,7 @@ func TestPeekNewSyntax(t *testing.T) {
name: "new syntax to word destination",
line: "result = PEEK 2048",
setupVars: func(st *compiler.SymbolTable) {
st.AddVar("result", "", compiler.KindWord, 0)
st.AddVar("result", "", compiler.KindWord, 0, "test.c65", 1)
},
wantAsm: []string{
"\tlda 2048",
@ -211,7 +211,7 @@ func TestPeekWNewSyntax(t *testing.T) {
name: "new syntax constant address",
line: "result = PEEKW 1024",
setupVars: func(st *compiler.SymbolTable) {
st.AddVar("result", "", compiler.KindWord, 0)
st.AddVar("result", "", compiler.KindWord, 0, "test.c65", 1)
},
wantAsm: []string{
"\tlda 1024",
@ -224,8 +224,8 @@ func TestPeekWNewSyntax(t *testing.T) {
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)
st.AddAbsolute("ptr", "", compiler.KindWord, 0x80, "test.c65", 1)
st.AddVar("result", "", compiler.KindWord, 0, "test.c65", 1)
},
wantAsm: []string{
"\tldy #0",
@ -240,8 +240,8 @@ func TestPeekWNewSyntax(t *testing.T) {
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)
st.AddAbsolute("ptr", "", compiler.KindWord, 0x80, "test.c65", 1)
st.AddVar("result", "", compiler.KindWord, 0, "test.c65", 1)
},
wantAsm: []string{
"\tldy #8",
@ -303,7 +303,7 @@ func TestPeekErrors(t *testing.T) {
name: "constant destination",
line: "PEEK 1024 GIVING CONST",
setupVars: func(st *compiler.SymbolTable) {
st.AddConst("CONST", "", compiler.KindByte, 100)
st.AddConst("CONST", "", compiler.KindByte, 100, "test.c65", 1)
},
wantError: "cannot PEEK into constant",
},
@ -318,7 +318,7 @@ func TestPeekErrors(t *testing.T) {
name: "new syntax constant destination",
line: "CONST = PEEK 1024",
setupVars: func(st *compiler.SymbolTable) {
st.AddConst("CONST", "", compiler.KindByte, 100)
st.AddConst("CONST", "", compiler.KindByte, 100, "test.c65", 1)
},
wantError: "cannot PEEK into constant",
},
@ -326,8 +326,8 @@ func TestPeekErrors(t *testing.T) {
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)
st.AddVar("addr", "", compiler.KindWord, 0, "test.c65", 1)
st.AddVar("result", "", compiler.KindByte, 0, "test.c65", 1)
},
wantError: "offset",
},
@ -335,8 +335,8 @@ func TestPeekErrors(t *testing.T) {
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)
st.AddVar("addr", "", compiler.KindWord, 0, "test.c65", 1)
st.AddVar("result", "", compiler.KindByte, 0, "test.c65", 1)
},
wantError: "offset",
},
@ -377,7 +377,7 @@ func TestPeekWErrors(t *testing.T) {
name: "byte destination",
line: "PEEKW 1024 GIVING result",
setupVars: func(st *compiler.SymbolTable) {
st.AddVar("result", "", compiler.KindByte, 0)
st.AddVar("result", "", compiler.KindByte, 0, "test.c65", 1)
},
wantError: "must be word type",
},
@ -385,7 +385,7 @@ func TestPeekWErrors(t *testing.T) {
name: "new syntax byte destination",
line: "result = PEEKW 1024",
setupVars: func(st *compiler.SymbolTable) {
st.AddVar("result", "", compiler.KindByte, 0)
st.AddVar("result", "", compiler.KindByte, 0, "test.c65", 1)
},
wantError: "must be word type",
},

View file

@ -116,13 +116,7 @@ func (c *PeekWCommand) Interpret(line preproc.Line, ctx *compiler.CompilerContex
// Parse address - may have [offset]
baseAddr, offsetParam := parseOffsetW(addrParam)
constLookup := func(name string) (int64, bool) {
sym := ctx.SymbolTable.Lookup(name, scope)
if sym != nil && sym.IsConst() {
return int64(sym.Value), true
}
return 0, false
}
constLookup := ctx.SymbolTable.ConstantLookupFunc(scope)
// Try to parse base address
varName, varKind, value, isVar, err := compiler.ParseOperandParam(

View file

@ -74,13 +74,7 @@ func (c *PointerCommand) Interpret(line preproc.Line, ctx *compiler.CompilerCont
// Parse target (param 4)
targetParam := params[3]
constLookup := func(name string) (int64, bool) {
sym := ctx.SymbolTable.Lookup(name, scope)
if sym != nil && sym.IsConst() {
return int64(sym.Value), true
}
return 0, false
}
constLookup := ctx.SymbolTable.ConstantLookupFunc(scope)
// Try ParseOperandParam - handles variables, constants, expressions
varName, _, value, isVar, err := compiler.ParseOperandParam(

View file

@ -83,13 +83,7 @@ func (c *PokeCommand) Interpret(line preproc.Line, ctx *compiler.CompilerContext
addrParam := params[1]
baseAddr, offsetParam := parsePOKEOffset(addrParam)
constLookup := func(name string) (int64, bool) {
sym := ctx.SymbolTable.Lookup(name, scope)
if sym != nil && sym.IsConst() {
return int64(sym.Value), true
}
return 0, false
}
constLookup := ctx.SymbolTable.ConstantLookupFunc(scope)
// Try to parse base address
varName, varKind, value, isVar, err := compiler.ParseOperandParam(

View file

@ -82,13 +82,7 @@ func (c *PokeWCommand) Interpret(line preproc.Line, ctx *compiler.CompilerContex
addrParam := params[1]
baseAddr, offsetParam := parsePOKEWOffset(addrParam)
constLookup := func(name string) (int64, bool) {
sym := ctx.SymbolTable.Lookup(name, scope)
if sym != nil && sym.IsConst() {
return int64(sym.Value), true
}
return 0, false
}
constLookup := ctx.SymbolTable.ConstantLookupFunc(scope)
// Try to parse base address
varName, varKind, value, isVar, err := compiler.ParseOperandParam(

View file

@ -71,13 +71,7 @@ func (c *SubtractCommand) Interpret(line preproc.Line, ctx *compiler.CompilerCon
scope := ctx.CurrentScope()
// Create constant lookup function
constLookup := func(name string) (int64, bool) {
sym := ctx.SymbolTable.Lookup(name, scope)
if sym != nil && sym.IsConst() {
return int64(sym.Value), true
}
return 0, false
}
constLookup := ctx.SymbolTable.ConstantLookupFunc(scope)
// Determine syntax and parse accordingly
kw := strings.ToUpper(params[0])

View file

@ -50,7 +50,7 @@ func TestSubtractCommand_OldSyntax_FROM(t *testing.T) {
name: "SUBTRACT a FROM b (means b-a)",
line: "SUBTRACT 5 FROM 10 GIVING result",
setupVars: func(st *compiler.SymbolTable) {
st.AddVar("result", "", compiler.KindByte, 0)
st.AddVar("result", "", compiler.KindByte, 0, "test.c65", 1)
},
wantAsm: []string{
"\tlda #$05",
@ -61,9 +61,9 @@ func TestSubtractCommand_OldSyntax_FROM(t *testing.T) {
name: "SUBTRACT byte FROM byte -> byte (variables)",
line: "SUBTRACT a FROM b GIVING result",
setupVars: func(st *compiler.SymbolTable) {
st.AddVar("a", "", compiler.KindByte, 10)
st.AddVar("b", "", compiler.KindByte, 20)
st.AddVar("result", "", compiler.KindByte, 0)
st.AddVar("a", "", compiler.KindByte, 10, "test.c65", 1)
st.AddVar("b", "", compiler.KindByte, 20, "test.c65", 1)
st.AddVar("result", "", compiler.KindByte, 0, "test.c65", 1)
},
wantAsm: []string{
"\tsec",
@ -76,9 +76,9 @@ func TestSubtractCommand_OldSyntax_FROM(t *testing.T) {
name: "SUBT a FROM b -> c with arrow",
line: "SUBT a FROM b -> result",
setupVars: func(st *compiler.SymbolTable) {
st.AddVar("a", "", compiler.KindByte, 10)
st.AddVar("b", "", compiler.KindByte, 20)
st.AddVar("result", "", compiler.KindByte, 0)
st.AddVar("a", "", compiler.KindByte, 10, "test.c65", 1)
st.AddVar("b", "", compiler.KindByte, 20, "test.c65", 1)
st.AddVar("result", "", compiler.KindByte, 0, "test.c65", 1)
},
wantAsm: []string{
"\tsec",
@ -91,8 +91,8 @@ func TestSubtractCommand_OldSyntax_FROM(t *testing.T) {
name: "SUBTRACT literal FROM variable",
line: "SUBTRACT 10 FROM a GIVING result",
setupVars: func(st *compiler.SymbolTable) {
st.AddVar("a", "", compiler.KindByte, 20)
st.AddVar("result", "", compiler.KindByte, 0)
st.AddVar("a", "", compiler.KindByte, 20, "test.c65", 1)
st.AddVar("result", "", compiler.KindByte, 0, "test.c65", 1)
},
wantAsm: []string{
"\tsec",
@ -105,9 +105,9 @@ func TestSubtractCommand_OldSyntax_FROM(t *testing.T) {
name: "word FROM word -> word",
line: "SUBTRACT x FROM y GIVING result",
setupVars: func(st *compiler.SymbolTable) {
st.AddVar("x", "", compiler.KindWord, 0x1000)
st.AddVar("y", "", compiler.KindWord, 0x2000)
st.AddVar("result", "", compiler.KindWord, 0)
st.AddVar("x", "", compiler.KindWord, 0x1000, "test.c65", 1)
st.AddVar("y", "", compiler.KindWord, 0x2000, "test.c65", 1)
st.AddVar("result", "", compiler.KindWord, 0, "test.c65", 1)
},
wantAsm: []string{
"\tsec",
@ -166,7 +166,7 @@ func TestSubtractCommand_OldSyntax_Minus(t *testing.T) {
name: "SUBT a - b (means a-b, no swap)",
line: "SUBT 10 - 5 GIVING result",
setupVars: func(st *compiler.SymbolTable) {
st.AddVar("result", "", compiler.KindByte, 0)
st.AddVar("result", "", compiler.KindByte, 0, "test.c65", 1)
},
wantAsm: []string{
"\tlda #$05",
@ -177,9 +177,9 @@ func TestSubtractCommand_OldSyntax_Minus(t *testing.T) {
name: "SUBT byte - byte -> byte (variables)",
line: "SUBT a - b -> result",
setupVars: func(st *compiler.SymbolTable) {
st.AddVar("a", "", compiler.KindByte, 20)
st.AddVar("b", "", compiler.KindByte, 10)
st.AddVar("result", "", compiler.KindByte, 0)
st.AddVar("a", "", compiler.KindByte, 20, "test.c65", 1)
st.AddVar("b", "", compiler.KindByte, 10, "test.c65", 1)
st.AddVar("result", "", compiler.KindByte, 0, "test.c65", 1)
},
wantAsm: []string{
"\tsec",
@ -192,9 +192,9 @@ func TestSubtractCommand_OldSyntax_Minus(t *testing.T) {
name: "SUBTRACT a - b GIVING c",
line: "SUBTRACT a - b GIVING result",
setupVars: func(st *compiler.SymbolTable) {
st.AddVar("a", "", compiler.KindByte, 20)
st.AddVar("b", "", compiler.KindByte, 10)
st.AddVar("result", "", compiler.KindByte, 0)
st.AddVar("a", "", compiler.KindByte, 20, "test.c65", 1)
st.AddVar("b", "", compiler.KindByte, 10, "test.c65", 1)
st.AddVar("result", "", compiler.KindByte, 0, "test.c65", 1)
},
wantAsm: []string{
"\tsec",
@ -207,8 +207,8 @@ func TestSubtractCommand_OldSyntax_Minus(t *testing.T) {
name: "SUBT variable - literal",
line: "SUBT a - 10 -> result",
setupVars: func(st *compiler.SymbolTable) {
st.AddVar("a", "", compiler.KindByte, 20)
st.AddVar("result", "", compiler.KindByte, 0)
st.AddVar("a", "", compiler.KindByte, 20, "test.c65", 1)
st.AddVar("result", "", compiler.KindByte, 0, "test.c65", 1)
},
wantAsm: []string{
"\tsec",
@ -221,9 +221,9 @@ func TestSubtractCommand_OldSyntax_Minus(t *testing.T) {
name: "word - word -> word",
line: "SUBT x - y -> result",
setupVars: func(st *compiler.SymbolTable) {
st.AddVar("x", "", compiler.KindWord, 0x2000)
st.AddVar("y", "", compiler.KindWord, 0x1000)
st.AddVar("result", "", compiler.KindWord, 0)
st.AddVar("x", "", compiler.KindWord, 0x2000, "test.c65", 1)
st.AddVar("y", "", compiler.KindWord, 0x1000, "test.c65", 1)
st.AddVar("result", "", compiler.KindWord, 0, "test.c65", 1)
},
wantAsm: []string{
"\tsec",
@ -239,9 +239,9 @@ func TestSubtractCommand_OldSyntax_Minus(t *testing.T) {
name: "byte - byte -> word",
line: "SUBT a - b GIVING result",
setupVars: func(st *compiler.SymbolTable) {
st.AddVar("a", "", compiler.KindByte, 20)
st.AddVar("b", "", compiler.KindByte, 10)
st.AddVar("result", "", compiler.KindWord, 0)
st.AddVar("a", "", compiler.KindByte, 20, "test.c65", 1)
st.AddVar("b", "", compiler.KindByte, 10, "test.c65", 1)
st.AddVar("result", "", compiler.KindWord, 0, "test.c65", 1)
},
wantAsm: []string{
"\tsec",
@ -300,9 +300,9 @@ func TestSubtractCommand_NewSyntax(t *testing.T) {
name: "byte - byte -> byte",
line: "result = a - b",
setupVars: func(st *compiler.SymbolTable) {
st.AddVar("a", "", compiler.KindByte, 20)
st.AddVar("b", "", compiler.KindByte, 10)
st.AddVar("result", "", compiler.KindByte, 0)
st.AddVar("a", "", compiler.KindByte, 20, "test.c65", 1)
st.AddVar("b", "", compiler.KindByte, 10, "test.c65", 1)
st.AddVar("result", "", compiler.KindByte, 0, "test.c65", 1)
},
wantAsm: []string{
"\tsec",
@ -315,9 +315,9 @@ func TestSubtractCommand_NewSyntax(t *testing.T) {
name: "byte - byte -> word",
line: "result = a - b",
setupVars: func(st *compiler.SymbolTable) {
st.AddVar("a", "", compiler.KindByte, 20)
st.AddVar("b", "", compiler.KindByte, 10)
st.AddVar("result", "", compiler.KindWord, 0)
st.AddVar("a", "", compiler.KindByte, 20, "test.c65", 1)
st.AddVar("b", "", compiler.KindByte, 10, "test.c65", 1)
st.AddVar("result", "", compiler.KindWord, 0, "test.c65", 1)
},
wantAsm: []string{
"\tsec",
@ -333,9 +333,9 @@ func TestSubtractCommand_NewSyntax(t *testing.T) {
name: "word - word -> word",
line: "result = x - y",
setupVars: func(st *compiler.SymbolTable) {
st.AddVar("x", "", compiler.KindWord, 0x2000)
st.AddVar("y", "", compiler.KindWord, 0x1000)
st.AddVar("result", "", compiler.KindWord, 0)
st.AddVar("x", "", compiler.KindWord, 0x2000, "test.c65", 1)
st.AddVar("y", "", compiler.KindWord, 0x1000, "test.c65", 1)
st.AddVar("result", "", compiler.KindWord, 0, "test.c65", 1)
},
wantAsm: []string{
"\tsec",
@ -351,8 +351,8 @@ func TestSubtractCommand_NewSyntax(t *testing.T) {
name: "variable - literal",
line: "result = a - 10",
setupVars: func(st *compiler.SymbolTable) {
st.AddVar("a", "", compiler.KindByte, 20)
st.AddVar("result", "", compiler.KindByte, 0)
st.AddVar("a", "", compiler.KindByte, 20, "test.c65", 1)
st.AddVar("result", "", compiler.KindByte, 0, "test.c65", 1)
},
wantAsm: []string{
"\tsec",
@ -365,8 +365,8 @@ func TestSubtractCommand_NewSyntax(t *testing.T) {
name: "literal - variable",
line: "result = 20 - b",
setupVars: func(st *compiler.SymbolTable) {
st.AddVar("b", "", compiler.KindByte, 10)
st.AddVar("result", "", compiler.KindByte, 0)
st.AddVar("b", "", compiler.KindByte, 10, "test.c65", 1)
st.AddVar("result", "", compiler.KindByte, 0, "test.c65", 1)
},
wantAsm: []string{
"\tsec",
@ -379,7 +379,7 @@ func TestSubtractCommand_NewSyntax(t *testing.T) {
name: "constant folding",
line: "result = 100 - 25",
setupVars: func(st *compiler.SymbolTable) {
st.AddVar("result", "", compiler.KindByte, 0)
st.AddVar("result", "", compiler.KindByte, 0, "test.c65", 1)
},
wantAsm: []string{
"\tlda #$4b",
@ -390,7 +390,7 @@ func TestSubtractCommand_NewSyntax(t *testing.T) {
name: "constant folding word",
line: "result = $2000 - $1000",
setupVars: func(st *compiler.SymbolTable) {
st.AddVar("result", "", compiler.KindWord, 0)
st.AddVar("result", "", compiler.KindWord, 0, "test.c65", 1)
},
wantAsm: []string{
"\tlda #$00",
@ -403,9 +403,9 @@ func TestSubtractCommand_NewSyntax(t *testing.T) {
name: "using constant in expression",
line: "result = a - OFFSET",
setupVars: func(st *compiler.SymbolTable) {
st.AddVar("a", "", compiler.KindByte, 100)
st.AddConst("OFFSET", "", compiler.KindByte, 10)
st.AddVar("result", "", compiler.KindByte, 0)
st.AddVar("a", "", compiler.KindByte, 100, "test.c65", 1)
st.AddConst("OFFSET", "", compiler.KindByte, 10, "test.c65", 1)
st.AddVar("result", "", compiler.KindByte, 0, "test.c65", 1)
},
wantAsm: []string{
"\tsec",
@ -418,9 +418,9 @@ func TestSubtractCommand_NewSyntax(t *testing.T) {
name: "word - byte -> word",
line: "result = wval - bval",
setupVars: func(st *compiler.SymbolTable) {
st.AddVar("wval", "", compiler.KindWord, 0x1234)
st.AddVar("bval", "", compiler.KindByte, 0x10)
st.AddVar("result", "", compiler.KindWord, 0)
st.AddVar("wval", "", compiler.KindWord, 0x1234, "test.c65", 1)
st.AddVar("bval", "", compiler.KindByte, 0x10, "test.c65", 1)
st.AddVar("result", "", compiler.KindWord, 0, "test.c65", 1)
},
wantAsm: []string{
"\tsec",
@ -436,8 +436,8 @@ func TestSubtractCommand_NewSyntax(t *testing.T) {
name: "error: unknown destination",
line: "unknown = a - b",
setupVars: func(st *compiler.SymbolTable) {
st.AddVar("a", "", compiler.KindByte, 0)
st.AddVar("b", "", compiler.KindByte, 0)
st.AddVar("a", "", compiler.KindByte, 0, "test.c65", 1)
st.AddVar("b", "", compiler.KindByte, 0, "test.c65", 1)
},
wantErr: true,
},
@ -445,9 +445,9 @@ func TestSubtractCommand_NewSyntax(t *testing.T) {
name: "error: wrong operator",
line: "result = a + b",
setupVars: func(st *compiler.SymbolTable) {
st.AddVar("a", "", compiler.KindByte, 0)
st.AddVar("b", "", compiler.KindByte, 0)
st.AddVar("result", "", compiler.KindByte, 0)
st.AddVar("a", "", compiler.KindByte, 0, "test.c65", 1)
st.AddVar("b", "", compiler.KindByte, 0, "test.c65", 1)
st.AddVar("result", "", compiler.KindByte, 0, "test.c65", 1)
},
wantErr: true,
},
@ -455,9 +455,9 @@ func TestSubtractCommand_NewSyntax(t *testing.T) {
name: "error: cannot assign to constant",
line: "MAXVAL = a - b",
setupVars: func(st *compiler.SymbolTable) {
st.AddVar("a", "", compiler.KindByte, 0)
st.AddVar("b", "", compiler.KindByte, 0)
st.AddConst("MAXVAL", "", compiler.KindByte, 255)
st.AddVar("a", "", compiler.KindByte, 0, "test.c65", 1)
st.AddVar("b", "", compiler.KindByte, 0, "test.c65", 1)
st.AddConst("MAXVAL", "", compiler.KindByte, 255, "test.c65", 1)
},
wantErr: true,
},

View file

@ -33,13 +33,7 @@ func (c *SwitchCommand) Interpret(line preproc.Line, ctx *compiler.CompilerConte
}
scope := ctx.CurrentScope()
constLookup := func(name string) (int64, bool) {
sym := ctx.SymbolTable.Lookup(name, scope)
if sym != nil && sym.IsConst() {
return int64(sym.Value), true
}
return 0, false
}
constLookup := ctx.SymbolTable.ConstantLookupFunc(scope)
// Parse the switch variable/expression
varName, varKind, value, isVar, err := compiler.ParseOperandParam(

View file

@ -20,7 +20,7 @@ func TestSwitchBasicByte(t *testing.T) {
{
name: "byte var with byte literal case",
setupVars: func(st *compiler.SymbolTable) {
st.AddVar("x", "", compiler.KindByte, 0)
st.AddVar("x", "", compiler.KindByte, 0, "test.c65", 1)
},
caseValue: "10",
wantSwitch: []string{},
@ -96,7 +96,7 @@ func TestSwitchBasicByte(t *testing.T) {
func TestSwitchMultipleCases(t *testing.T) {
pragma := preproc.NewPragma()
ctx := compiler.NewCompilerContext(pragma)
ctx.SymbolTable.AddVar("x", "", compiler.KindByte, 0)
ctx.SymbolTable.AddVar("x", "", compiler.KindByte, 0, "test.c65", 1)
pragmaIdx := pragma.GetCurrentPragmaSetIndex()
@ -157,7 +157,7 @@ func TestSwitchMultipleCases(t *testing.T) {
func TestSwitchWithDefault(t *testing.T) {
pragma := preproc.NewPragma()
ctx := compiler.NewCompilerContext(pragma)
ctx.SymbolTable.AddVar("x", "", compiler.KindByte, 0)
ctx.SymbolTable.AddVar("x", "", compiler.KindByte, 0, "test.c65", 1)
pragmaIdx := pragma.GetCurrentPragmaSetIndex()
@ -198,7 +198,7 @@ func TestSwitchWithDefault(t *testing.T) {
func TestSwitchCaseAfterDefault(t *testing.T) {
pragma := preproc.NewPragma()
ctx := compiler.NewCompilerContext(pragma)
ctx.SymbolTable.AddVar("x", "", compiler.KindByte, 0)
ctx.SymbolTable.AddVar("x", "", compiler.KindByte, 0, "test.c65", 1)
pragmaIdx := pragma.GetCurrentPragmaSetIndex()
@ -234,7 +234,7 @@ func TestSwitchCaseAfterDefault(t *testing.T) {
func TestSwitchMultipleDefaults(t *testing.T) {
pragma := preproc.NewPragma()
ctx := compiler.NewCompilerContext(pragma)
ctx.SymbolTable.AddVar("x", "", compiler.KindByte, 0)
ctx.SymbolTable.AddVar("x", "", compiler.KindByte, 0, "test.c65", 1)
pragmaIdx := pragma.GetCurrentPragmaSetIndex()
@ -333,7 +333,7 @@ func TestSwitchWrongParamCount(t *testing.T) {
func TestCaseWrongParamCount(t *testing.T) {
pragma := preproc.NewPragma()
ctx := compiler.NewCompilerContext(pragma)
ctx.SymbolTable.AddVar("x", "", compiler.KindByte, 0)
ctx.SymbolTable.AddVar("x", "", compiler.KindByte, 0, "test.c65", 1)
pragmaIdx := pragma.GetCurrentPragmaSetIndex()
@ -358,7 +358,7 @@ func TestCaseWrongParamCount(t *testing.T) {
func TestDefaultWrongParamCount(t *testing.T) {
pragma := preproc.NewPragma()
ctx := compiler.NewCompilerContext(pragma)
ctx.SymbolTable.AddVar("x", "", compiler.KindByte, 0)
ctx.SymbolTable.AddVar("x", "", compiler.KindByte, 0, "test.c65", 1)
pragmaIdx := pragma.GetCurrentPragmaSetIndex()
@ -376,7 +376,7 @@ func TestDefaultWrongParamCount(t *testing.T) {
func TestEndswitchWrongParamCount(t *testing.T) {
pragma := preproc.NewPragma()
ctx := compiler.NewCompilerContext(pragma)
ctx.SymbolTable.AddVar("x", "", compiler.KindByte, 0)
ctx.SymbolTable.AddVar("x", "", compiler.KindByte, 0, "test.c65", 1)
pragmaIdx := pragma.GetCurrentPragmaSetIndex()
@ -394,7 +394,7 @@ func TestEndswitchWrongParamCount(t *testing.T) {
func TestSwitchWordType(t *testing.T) {
pragma := preproc.NewPragma()
ctx := compiler.NewCompilerContext(pragma)
ctx.SymbolTable.AddVar("big_val", "", compiler.KindWord, 0)
ctx.SymbolTable.AddVar("big_val", "", compiler.KindWord, 0, "test.c65", 1)
pragmaIdx := pragma.GetCurrentPragmaSetIndex()
@ -434,8 +434,8 @@ func TestSwitchWordType(t *testing.T) {
func TestSwitchWithConstant(t *testing.T) {
pragma := preproc.NewPragma()
ctx := compiler.NewCompilerContext(pragma)
ctx.SymbolTable.AddConst("MAX_VAL", "", compiler.KindByte, 100)
ctx.SymbolTable.AddVar("x", "", compiler.KindByte, 0)
ctx.SymbolTable.AddConst("MAX_VAL", "", compiler.KindByte, 100, "test.c65", 1)
ctx.SymbolTable.AddVar("x", "", compiler.KindByte, 0, "test.c65", 1)
pragmaIdx := pragma.GetCurrentPragmaSetIndex()
@ -475,8 +475,8 @@ func TestSwitchWithConstant(t *testing.T) {
func TestSwitchNested(t *testing.T) {
pragma := preproc.NewPragma()
ctx := compiler.NewCompilerContext(pragma)
ctx.SymbolTable.AddVar("outer", "", compiler.KindByte, 0)
ctx.SymbolTable.AddVar("inner", "", compiler.KindByte, 0)
ctx.SymbolTable.AddVar("outer", "", compiler.KindByte, 0, "test.c65", 1)
ctx.SymbolTable.AddVar("inner", "", compiler.KindByte, 0, "test.c65", 1)
pragmaIdx := pragma.GetCurrentPragmaSetIndex()
@ -546,7 +546,7 @@ func TestSwitchLongJump(t *testing.T) {
pragma := preproc.NewPragma()
pragma.AddPragma("_P_USE_LONG_JUMP", "1")
ctx := compiler.NewCompilerContext(pragma)
ctx.SymbolTable.AddVar("x", "", compiler.KindByte, 0)
ctx.SymbolTable.AddVar("x", "", compiler.KindByte, 0, "test.c65", 1)
pragmaIdx := pragma.GetCurrentPragmaSetIndex()
@ -600,7 +600,7 @@ func TestSwitchLongJump(t *testing.T) {
// Verify the pattern is different from normal mode
pragmaNormal := preproc.NewPragma()
ctxNormal := compiler.NewCompilerContext(pragmaNormal)
ctxNormal.SymbolTable.AddVar("x", "", compiler.KindByte, 0)
ctxNormal.SymbolTable.AddVar("x", "", compiler.KindByte, 0, "test.c65", 1)
pragmaIdxNormal := pragmaNormal.GetCurrentPragmaSetIndex()
@ -629,7 +629,7 @@ func TestSwitchLongJump(t *testing.T) {
func TestSwitchOnConstant(t *testing.T) {
pragma := preproc.NewPragma()
ctx := compiler.NewCompilerContext(pragma)
ctx.SymbolTable.AddConst("VALUE", "", compiler.KindByte, 5)
ctx.SymbolTable.AddConst("VALUE", "", compiler.KindByte, 5, "test.c65", 1)
pragmaIdx := pragma.GetCurrentPragmaSetIndex()
@ -688,7 +688,7 @@ func TestSwitchOnConstant(t *testing.T) {
func TestSwitchEmptyWithOnlyDefault(t *testing.T) {
pragma := preproc.NewPragma()
ctx := compiler.NewCompilerContext(pragma)
ctx.SymbolTable.AddVar("x", "", compiler.KindByte, 0)
ctx.SymbolTable.AddVar("x", "", compiler.KindByte, 0, "test.c65", 1)
pragmaIdx := pragma.GetCurrentPragmaSetIndex()
@ -752,7 +752,7 @@ func TestSwitchComparisonTypes(t *testing.T) {
t.Run(tt.name, func(t *testing.T) {
pragma := preproc.NewPragma()
ctx := compiler.NewCompilerContext(pragma)
ctx.SymbolTable.AddVar("x", "", tt.varType, 0)
ctx.SymbolTable.AddVar("x", "", tt.varType, 0, "test.c65", 1)
pragmaIdx := pragma.GetCurrentPragmaSetIndex()
@ -801,8 +801,8 @@ func TestSwitchWithVariableCase(t *testing.T) {
t.Run(tt.name, func(t *testing.T) {
pragma := preproc.NewPragma()
ctx := compiler.NewCompilerContext(pragma)
ctx.SymbolTable.AddVar("switch_var", "", tt.switchType, 0)
ctx.SymbolTable.AddVar("case_var", "", tt.caseType, 0)
ctx.SymbolTable.AddVar("switch_var", "", tt.switchType, 0, "test.c65", 1)
ctx.SymbolTable.AddVar("case_var", "", tt.caseType, 0, "test.c65", 1)
pragmaIdx := pragma.GetCurrentPragmaSetIndex()
@ -859,7 +859,7 @@ func TestSwitchByteRangeValidation(t *testing.T) {
t.Run(tt.name, func(t *testing.T) {
pragma := preproc.NewPragma()
ctx := compiler.NewCompilerContext(pragma)
ctx.SymbolTable.AddVar("x", "", compiler.KindByte, 0)
ctx.SymbolTable.AddVar("x", "", compiler.KindByte, 0, "test.c65", 1)
pragmaIdx := pragma.GetCurrentPragmaSetIndex()

View file

@ -41,13 +41,7 @@ func (c *WhileCommand) Interpret(line preproc.Line, ctx *compiler.CompilerContex
}
scope := ctx.CurrentScope()
constLookup := func(name string) (int64, bool) {
sym := ctx.SymbolTable.Lookup(name, scope)
if sym != nil && sym.IsConst() {
return int64(sym.Value), true
}
return 0, false
}
constLookup := ctx.SymbolTable.ConstantLookupFunc(scope)
// Parse param1
varName, varKind, value, isVar, err := compiler.ParseOperandParam(

View file

@ -20,7 +20,7 @@ func TestWhileBasicEqual(t *testing.T) {
name: "byte var == byte literal",
whileLine: "WHILE x = 10",
setupVars: func(st *compiler.SymbolTable) {
st.AddVar("x", "", compiler.KindByte, 0)
st.AddVar("x", "", compiler.KindByte, 0, "test.c65", 1)
},
wantWhile: []string{
"_LOOPSTART1",
@ -37,7 +37,7 @@ func TestWhileBasicEqual(t *testing.T) {
name: "word var == word literal",
whileLine: "WHILE x = 1000",
setupVars: func(st *compiler.SymbolTable) {
st.AddVar("x", "", compiler.KindWord, 0)
st.AddVar("x", "", compiler.KindWord, 0, "test.c65", 1)
},
wantWhile: []string{
"_LOOPSTART1",
@ -125,7 +125,7 @@ func TestWhileAllOperators(t *testing.T) {
t.Run(tt.name, func(t *testing.T) {
pragma := preproc.NewPragma()
ctx := compiler.NewCompilerContext(pragma)
ctx.SymbolTable.AddVar("x", "", compiler.KindByte, 0)
ctx.SymbolTable.AddVar("x", "", compiler.KindByte, 0, "test.c65", 1)
cmd := &WhileCommand{}
line := preproc.Line{
@ -160,8 +160,8 @@ func TestWhileAllOperators(t *testing.T) {
func TestWhileMixedTypes(t *testing.T) {
pragma := preproc.NewPragma()
ctx := compiler.NewCompilerContext(pragma)
ctx.SymbolTable.AddVar("x", "", compiler.KindByte, 0)
ctx.SymbolTable.AddVar("y", "", compiler.KindWord, 0)
ctx.SymbolTable.AddVar("x", "", compiler.KindByte, 0, "test.c65", 1)
ctx.SymbolTable.AddVar("y", "", compiler.KindWord, 0, "test.c65", 1)
cmd := &WhileCommand{}
line := preproc.Line{
@ -194,7 +194,7 @@ func TestWhileMixedTypes(t *testing.T) {
func TestWhileBreak(t *testing.T) {
pragma := preproc.NewPragma()
ctx := compiler.NewCompilerContext(pragma)
ctx.SymbolTable.AddVar("x", "", compiler.KindByte, 0)
ctx.SymbolTable.AddVar("x", "", compiler.KindByte, 0, "test.c65", 1)
whileCmd := &WhileCommand{}
breakCmd := &BreakCommand{}
@ -274,8 +274,8 @@ func TestWendWithoutWhile(t *testing.T) {
func TestWhileNested(t *testing.T) {
pragma := preproc.NewPragma()
ctx := compiler.NewCompilerContext(pragma)
ctx.SymbolTable.AddVar("i", "", compiler.KindByte, 0)
ctx.SymbolTable.AddVar("j", "", compiler.KindByte, 0)
ctx.SymbolTable.AddVar("i", "", compiler.KindByte, 0, "test.c65", 1)
ctx.SymbolTable.AddVar("j", "", compiler.KindByte, 0, "test.c65", 1)
pragmaIdx := pragma.GetCurrentPragmaSetIndex()
@ -316,7 +316,7 @@ func TestWhileLongJump(t *testing.T) {
pragma := preproc.NewPragma()
pragma.AddPragma("_P_USE_LONG_JUMP", "1")
ctx := compiler.NewCompilerContext(pragma)
ctx.SymbolTable.AddVar("x", "", compiler.KindByte, 0)
ctx.SymbolTable.AddVar("x", "", compiler.KindByte, 0, "test.c65", 1)
cmd := &WhileCommand{}
line := preproc.Line{
@ -349,8 +349,8 @@ func TestWhileLongJump(t *testing.T) {
func TestWhileConstant(t *testing.T) {
pragma := preproc.NewPragma()
ctx := compiler.NewCompilerContext(pragma)
ctx.SymbolTable.AddConst("MAX", "", compiler.KindByte, 100)
ctx.SymbolTable.AddVar("x", "", compiler.KindByte, 0)
ctx.SymbolTable.AddConst("MAX", "", compiler.KindByte, 100, "test.c65", 1)
ctx.SymbolTable.AddVar("x", "", compiler.KindByte, 0, "test.c65", 1)
cmd := &WhileCommand{}
line := preproc.Line{

View file

@ -57,13 +57,7 @@ func (c *WordCommand) Interpret(line preproc.Line, ctx *compiler.CompilerContext
scope := ctx.FunctionHandler.CurrentFunction()
// Create constant lookup function
constLookup := func(name string) (int64, bool) {
sym := ctx.SymbolTable.Lookup(name, ctx.CurrentScope())
if sym != nil && sym.IsConst() {
return int64(sym.Value), true
}
return 0, false
}
constLookup := ctx.SymbolTable.ConstantLookupFunc(ctx.CurrentScope())
switch paramCount {
case 2:
@ -75,7 +69,7 @@ func (c *WordCommand) Interpret(line preproc.Line, ctx *compiler.CompilerContext
return fmt.Errorf("WORD: invalid identifier %q", varName)
}
err = ctx.SymbolTable.AddVar(varName, scope, compiler.KindWord, uint16(value))
err = ctx.SymbolTable.AddVar(varName, scope, compiler.KindWord, uint16(value), line.Filename, line.LineNo)
case 4:
// WORD varname = value OR WORD varname @ address OR WORD varname = "string"
@ -102,7 +96,7 @@ func (c *WordCommand) Interpret(line preproc.Line, ctx *compiler.CompilerContext
// Add string to constant string handler
ctx.ConstStrHandler.AddConstStr(label, valueStr, false, pragmaSet)
err = ctx.SymbolTable.AddLabel(varName, scope, label)
err = ctx.SymbolTable.AddLabel(varName, scope, label, line.Filename, line.LineNo)
if err != nil {
return fmt.Errorf("WORD: %w", err)
}
@ -129,7 +123,7 @@ func (c *WordCommand) Interpret(line preproc.Line, ctx *compiler.CompilerContext
labelRef = sym.FullName()
}
err = ctx.SymbolTable.AddLabel(varName, scope, labelRef)
err = ctx.SymbolTable.AddLabel(varName, scope, labelRef, line.Filename, line.LineNo)
if err != nil {
return fmt.Errorf("WORD: %w", err)
}
@ -150,7 +144,7 @@ func (c *WordCommand) Interpret(line preproc.Line, ctx *compiler.CompilerContext
if value < 0 || value > 65535 {
return fmt.Errorf("WORD: init value %d out of range (0-65535)", value)
}
err = ctx.SymbolTable.AddVar(varName, scope, compiler.KindWord, uint16(value))
err = ctx.SymbolTable.AddVar(varName, scope, compiler.KindWord, uint16(value), line.Filename, line.LineNo)
} else if operator == "@" {
// WORD varname @ address
@ -158,7 +152,7 @@ func (c *WordCommand) Interpret(line preproc.Line, ctx *compiler.CompilerContext
return fmt.Errorf("WORD: absolute address $%X out of range", value)
}
c.isAbs = true
err = ctx.SymbolTable.AddAbsolute(varName, scope, compiler.KindWord, uint16(value))
err = ctx.SymbolTable.AddAbsolute(varName, scope, compiler.KindWord, uint16(value), line.Filename, line.LineNo)
} else {
return fmt.Errorf("WORD: expected '=' or '@', got %q", operator)
@ -193,7 +187,7 @@ func (c *WordCommand) Interpret(line preproc.Line, ctx *compiler.CompilerContext
}
c.isConst = true
err = ctx.SymbolTable.AddConst(varName, scope, compiler.KindWord, uint16(value))
err = ctx.SymbolTable.AddConst(varName, scope, compiler.KindWord, uint16(value), line.Filename, line.LineNo)
}
if err != nil {

View file

@ -330,7 +330,7 @@ func TestWordCommand_Interpret(t *testing.T) {
// For duplicate test, pre-declare the variable
if tt.name == "duplicate declaration" {
ctx.SymbolTable.AddVar("x", "", compiler.KindWord, 0)
ctx.SymbolTable.AddVar("x", "", compiler.KindWord, 0, "test.c65", 1)
}
cmd := &WordCommand{}
@ -396,8 +396,8 @@ func TestWordCommand_WithConstantExpression(t *testing.T) {
ctx := compiler.NewCompilerContext(pragma)
// First, declare a constant
ctx.SymbolTable.AddConst("MAXVAL", "", compiler.KindWord, 1000)
ctx.SymbolTable.AddConst("OFFSET", "", compiler.KindWord, 500)
ctx.SymbolTable.AddConst("MAXVAL", "", compiler.KindWord, 1000, "test.c65", 1)
ctx.SymbolTable.AddConst("OFFSET", "", compiler.KindWord, 500, "test.c65", 1)
// Now declare a word using the constant in an expression
cmd := &WordCommand{}
@ -474,7 +474,7 @@ func TestWordCommand_LabelReference(t *testing.T) {
// Pre-declare existingVar for the reference test
if tt.name == "word referencing another variable" {
ctx.SymbolTable.AddVar("existingVar", "", compiler.KindWord, 0)
ctx.SymbolTable.AddVar("existingVar", "", compiler.KindWord, 0, "test.c65", 1)
}
cmd := &WordCommand{}
@ -525,7 +525,7 @@ func TestWordCommand_LabelReferenceLocalExpansion(t *testing.T) {
}
// Declare a local variable in function scope
ctx.SymbolTable.AddVar("localBuffer", "myFunc", compiler.KindWord, 0)
ctx.SymbolTable.AddVar("localBuffer", "myFunc", compiler.KindWord, 0, "test.c65", 1)
// Now declare another variable referencing the local
cmd := &WordCommand{}

View file

@ -68,13 +68,7 @@ func (c *XorCommand) Interpret(line preproc.Line, ctx *compiler.CompilerContext)
scope := ctx.CurrentScope()
// Create constant lookup function
constLookup := func(name string) (int64, bool) {
sym := ctx.SymbolTable.Lookup(name, scope)
if sym != nil && sym.IsConst() {
return int64(sym.Value), true
}
return 0, false
}
constLookup := ctx.SymbolTable.ConstantLookupFunc(scope)
// Determine syntax and parse accordingly
if strings.ToUpper(params[0]) == "XOR" {

View file

@ -48,9 +48,9 @@ func TestXorCommand_OldSyntax(t *testing.T) {
name: "byte XOR byte -> byte (variables)",
line: "XOR a WITH b GIVING result",
setupVars: func(st *compiler.SymbolTable) {
st.AddVar("a", "", compiler.KindByte, 0xFF)
st.AddVar("b", "", compiler.KindByte, 0xAA)
st.AddVar("result", "", compiler.KindByte, 0)
st.AddVar("a", "", compiler.KindByte, 0xFF, "test.c65", 1)
st.AddVar("b", "", compiler.KindByte, 0xAA, "test.c65", 1)
st.AddVar("result", "", compiler.KindByte, 0, "test.c65", 1)
},
wantAsm: []string{
"\tlda a",
@ -62,9 +62,9 @@ func TestXorCommand_OldSyntax(t *testing.T) {
name: "byte XOR byte -> word",
line: "XOR a WITH b GIVING result",
setupVars: func(st *compiler.SymbolTable) {
st.AddVar("a", "", compiler.KindByte, 0xFF)
st.AddVar("b", "", compiler.KindByte, 0xAA)
st.AddVar("result", "", compiler.KindWord, 0)
st.AddVar("a", "", compiler.KindByte, 0xFF, "test.c65", 1)
st.AddVar("b", "", compiler.KindByte, 0xAA, "test.c65", 1)
st.AddVar("result", "", compiler.KindWord, 0, "test.c65", 1)
},
wantAsm: []string{
"\tlda a",
@ -78,9 +78,9 @@ func TestXorCommand_OldSyntax(t *testing.T) {
name: "word XOR word -> word",
line: "XOR x WITH y GIVING result",
setupVars: func(st *compiler.SymbolTable) {
st.AddVar("x", "", compiler.KindWord, 0x1234)
st.AddVar("y", "", compiler.KindWord, 0x5678)
st.AddVar("result", "", compiler.KindWord, 0)
st.AddVar("x", "", compiler.KindWord, 0x1234, "test.c65", 1)
st.AddVar("y", "", compiler.KindWord, 0x5678, "test.c65", 1)
st.AddVar("result", "", compiler.KindWord, 0, "test.c65", 1)
},
wantAsm: []string{
"\tlda x",
@ -95,8 +95,8 @@ func TestXorCommand_OldSyntax(t *testing.T) {
name: "byte XOR literal -> byte",
line: "XOR a WITH $AA GIVING result",
setupVars: func(st *compiler.SymbolTable) {
st.AddVar("a", "", compiler.KindByte, 0xFF)
st.AddVar("result", "", compiler.KindByte, 0)
st.AddVar("a", "", compiler.KindByte, 0xFF, "test.c65", 1)
st.AddVar("result", "", compiler.KindByte, 0, "test.c65", 1)
},
wantAsm: []string{
"\tlda a",
@ -108,8 +108,8 @@ func TestXorCommand_OldSyntax(t *testing.T) {
name: "literal XOR byte -> byte",
line: "XOR 255 WITH b GIVING result",
setupVars: func(st *compiler.SymbolTable) {
st.AddVar("b", "", compiler.KindByte, 0xAA)
st.AddVar("result", "", compiler.KindByte, 0)
st.AddVar("b", "", compiler.KindByte, 0xAA, "test.c65", 1)
st.AddVar("result", "", compiler.KindByte, 0, "test.c65", 1)
},
wantAsm: []string{
"\tlda #$ff",
@ -121,7 +121,7 @@ func TestXorCommand_OldSyntax(t *testing.T) {
name: "constant folding: 255 XOR 170 -> byte",
line: "XOR 255 WITH 170 GIVING result",
setupVars: func(st *compiler.SymbolTable) {
st.AddVar("result", "", compiler.KindByte, 0)
st.AddVar("result", "", compiler.KindByte, 0, "test.c65", 1)
},
wantAsm: []string{
"\tlda #$55",
@ -132,7 +132,7 @@ func TestXorCommand_OldSyntax(t *testing.T) {
name: "constant folding: $FFFF XOR $AAAA -> word",
line: "XOR $FFFF WITH $AAAA GIVING result",
setupVars: func(st *compiler.SymbolTable) {
st.AddVar("result", "", compiler.KindWord, 0)
st.AddVar("result", "", compiler.KindWord, 0, "test.c65", 1)
},
wantAsm: []string{
"\tlda #$55",
@ -145,9 +145,9 @@ func TestXorCommand_OldSyntax(t *testing.T) {
name: "arrow syntax",
line: "XOR a WITH b -> result",
setupVars: func(st *compiler.SymbolTable) {
st.AddVar("a", "", compiler.KindByte, 0)
st.AddVar("b", "", compiler.KindByte, 0)
st.AddVar("result", "", compiler.KindByte, 0)
st.AddVar("a", "", compiler.KindByte, 0, "test.c65", 1)
st.AddVar("b", "", compiler.KindByte, 0, "test.c65", 1)
st.AddVar("result", "", compiler.KindByte, 0, "test.c65", 1)
},
wantAsm: []string{
"\tlda a",
@ -159,9 +159,9 @@ func TestXorCommand_OldSyntax(t *testing.T) {
name: "word XOR byte -> byte",
line: "XOR wval WITH bval GIVING result",
setupVars: func(st *compiler.SymbolTable) {
st.AddVar("wval", "", compiler.KindWord, 0x1234)
st.AddVar("bval", "", compiler.KindByte, 0xFF)
st.AddVar("result", "", compiler.KindByte, 0)
st.AddVar("wval", "", compiler.KindWord, 0x1234, "test.c65", 1)
st.AddVar("bval", "", compiler.KindByte, 0xFF, "test.c65", 1)
st.AddVar("result", "", compiler.KindByte, 0, "test.c65", 1)
},
wantAsm: []string{
"\tlda wval",
@ -173,9 +173,9 @@ func TestXorCommand_OldSyntax(t *testing.T) {
name: "word XOR byte -> word",
line: "XOR wval WITH bval GIVING result",
setupVars: func(st *compiler.SymbolTable) {
st.AddVar("wval", "", compiler.KindWord, 0x1234)
st.AddVar("bval", "", compiler.KindByte, 0xFF)
st.AddVar("result", "", compiler.KindWord, 0)
st.AddVar("wval", "", compiler.KindWord, 0x1234, "test.c65", 1)
st.AddVar("bval", "", compiler.KindByte, 0xFF, "test.c65", 1)
st.AddVar("result", "", compiler.KindWord, 0, "test.c65", 1)
},
wantAsm: []string{
"\tlda wval",
@ -189,8 +189,8 @@ func TestXorCommand_OldSyntax(t *testing.T) {
name: "error: unknown destination variable",
line: "XOR a WITH b GIVING unknown",
setupVars: func(st *compiler.SymbolTable) {
st.AddVar("a", "", compiler.KindByte, 0)
st.AddVar("b", "", compiler.KindByte, 0)
st.AddVar("a", "", compiler.KindByte, 0, "test.c65", 1)
st.AddVar("b", "", compiler.KindByte, 0, "test.c65", 1)
},
wantErr: true,
},
@ -198,9 +198,9 @@ func TestXorCommand_OldSyntax(t *testing.T) {
name: "error: wrong separator",
line: "XOR a TO b GIVING result",
setupVars: func(st *compiler.SymbolTable) {
st.AddVar("a", "", compiler.KindByte, 0)
st.AddVar("b", "", compiler.KindByte, 0)
st.AddVar("result", "", compiler.KindByte, 0)
st.AddVar("a", "", compiler.KindByte, 0, "test.c65", 1)
st.AddVar("b", "", compiler.KindByte, 0, "test.c65", 1)
st.AddVar("result", "", compiler.KindByte, 0, "test.c65", 1)
},
wantErr: true,
},
@ -208,9 +208,9 @@ func TestXorCommand_OldSyntax(t *testing.T) {
name: "error: cannot assign to constant",
line: "XOR a WITH b GIVING MAXVAL",
setupVars: func(st *compiler.SymbolTable) {
st.AddVar("a", "", compiler.KindByte, 0)
st.AddVar("b", "", compiler.KindByte, 0)
st.AddConst("MAXVAL", "", compiler.KindByte, 255)
st.AddVar("a", "", compiler.KindByte, 0, "test.c65", 1)
st.AddVar("b", "", compiler.KindByte, 0, "test.c65", 1)
st.AddConst("MAXVAL", "", compiler.KindByte, 255, "test.c65", 1)
},
wantErr: true,
},
@ -261,9 +261,9 @@ func TestXorCommand_NewSyntax(t *testing.T) {
name: "byte ^ byte -> byte",
line: "result = a ^ b",
setupVars: func(st *compiler.SymbolTable) {
st.AddVar("a", "", compiler.KindByte, 0xFF)
st.AddVar("b", "", compiler.KindByte, 0xAA)
st.AddVar("result", "", compiler.KindByte, 0)
st.AddVar("a", "", compiler.KindByte, 0xFF, "test.c65", 1)
st.AddVar("b", "", compiler.KindByte, 0xAA, "test.c65", 1)
st.AddVar("result", "", compiler.KindByte, 0, "test.c65", 1)
},
wantAsm: []string{
"\tlda a",
@ -275,9 +275,9 @@ func TestXorCommand_NewSyntax(t *testing.T) {
name: "byte ^ byte -> word",
line: "result = a ^ b",
setupVars: func(st *compiler.SymbolTable) {
st.AddVar("a", "", compiler.KindByte, 0xFF)
st.AddVar("b", "", compiler.KindByte, 0xAA)
st.AddVar("result", "", compiler.KindWord, 0)
st.AddVar("a", "", compiler.KindByte, 0xFF, "test.c65", 1)
st.AddVar("b", "", compiler.KindByte, 0xAA, "test.c65", 1)
st.AddVar("result", "", compiler.KindWord, 0, "test.c65", 1)
},
wantAsm: []string{
"\tlda a",
@ -291,9 +291,9 @@ func TestXorCommand_NewSyntax(t *testing.T) {
name: "word ^ word -> word",
line: "result = x ^ y",
setupVars: func(st *compiler.SymbolTable) {
st.AddVar("x", "", compiler.KindWord, 0x1234)
st.AddVar("y", "", compiler.KindWord, 0x5678)
st.AddVar("result", "", compiler.KindWord, 0)
st.AddVar("x", "", compiler.KindWord, 0x1234, "test.c65", 1)
st.AddVar("y", "", compiler.KindWord, 0x5678, "test.c65", 1)
st.AddVar("result", "", compiler.KindWord, 0, "test.c65", 1)
},
wantAsm: []string{
"\tlda x",
@ -308,8 +308,8 @@ func TestXorCommand_NewSyntax(t *testing.T) {
name: "byte ^ byte_const -> word (optimization: skip eor #0)",
line: "result = b ^ 5",
setupVars: func(st *compiler.SymbolTable) {
st.AddVar("b", "", compiler.KindByte, 0xFF)
st.AddVar("result", "", compiler.KindWord, 0)
st.AddVar("b", "", compiler.KindByte, 0xFF, "test.c65", 1)
st.AddVar("result", "", compiler.KindWord, 0, "test.c65", 1)
},
wantAsm: []string{
"\tlda b",
@ -323,9 +323,9 @@ func TestXorCommand_NewSyntax(t *testing.T) {
name: "byte ^ word -> word (swapped to word ^ byte)",
line: "result = bval ^ wval",
setupVars: func(st *compiler.SymbolTable) {
st.AddVar("bval", "", compiler.KindByte, 0xFF)
st.AddVar("wval", "", compiler.KindWord, 0x1234)
st.AddVar("result", "", compiler.KindWord, 0)
st.AddVar("bval", "", compiler.KindByte, 0xFF, "test.c65", 1)
st.AddVar("wval", "", compiler.KindWord, 0x1234, "test.c65", 1)
st.AddVar("result", "", compiler.KindWord, 0, "test.c65", 1)
},
wantAsm: []string{
"\tlda wval",
@ -339,8 +339,8 @@ func TestXorCommand_NewSyntax(t *testing.T) {
name: "byte_const ^ byte -> word (optimization: skip eor #0)",
line: "result = 5 ^ b",
setupVars: func(st *compiler.SymbolTable) {
st.AddVar("b", "", compiler.KindByte, 0xFF)
st.AddVar("result", "", compiler.KindWord, 0)
st.AddVar("b", "", compiler.KindByte, 0xFF, "test.c65", 1)
st.AddVar("result", "", compiler.KindWord, 0, "test.c65", 1)
},
wantAsm: []string{
"\tlda #$05",
@ -354,8 +354,8 @@ func TestXorCommand_NewSyntax(t *testing.T) {
name: "byte ^ word_const -> word (no optimization)",
line: "result = b ^ 300",
setupVars: func(st *compiler.SymbolTable) {
st.AddVar("b", "", compiler.KindByte, 0xFF)
st.AddVar("result", "", compiler.KindWord, 0)
st.AddVar("b", "", compiler.KindByte, 0xFF, "test.c65", 1)
st.AddVar("result", "", compiler.KindWord, 0, "test.c65", 1)
},
wantAsm: []string{
"\tlda b",
@ -370,8 +370,8 @@ func TestXorCommand_NewSyntax(t *testing.T) {
name: "word_const ^ byte -> word (optimization: skip eor #0)",
line: "result = 300 ^ b",
setupVars: func(st *compiler.SymbolTable) {
st.AddVar("b", "", compiler.KindByte, 0xFF)
st.AddVar("result", "", compiler.KindWord, 0)
st.AddVar("b", "", compiler.KindByte, 0xFF, "test.c65", 1)
st.AddVar("result", "", compiler.KindWord, 0, "test.c65", 1)
},
wantAsm: []string{
"\tlda #$2c",
@ -385,8 +385,8 @@ func TestXorCommand_NewSyntax(t *testing.T) {
name: "self-assignment: word ^= byte (optimization: skip high byte entirely)",
line: "wval = wval ^ bval",
setupVars: func(st *compiler.SymbolTable) {
st.AddVar("wval", "", compiler.KindWord, 0x1234)
st.AddVar("bval", "", compiler.KindByte, 0xFF)
st.AddVar("wval", "", compiler.KindWord, 0x1234, "test.c65", 1)
st.AddVar("bval", "", compiler.KindByte, 0xFF, "test.c65", 1)
},
wantAsm: []string{
"\tlda wval",
@ -398,8 +398,8 @@ func TestXorCommand_NewSyntax(t *testing.T) {
name: "self-assignment reversed: word ^= byte (optimization via swap)",
line: "wval = bval ^ wval",
setupVars: func(st *compiler.SymbolTable) {
st.AddVar("wval", "", compiler.KindWord, 0x1234)
st.AddVar("bval", "", compiler.KindByte, 0xFF)
st.AddVar("wval", "", compiler.KindWord, 0x1234, "test.c65", 1)
st.AddVar("bval", "", compiler.KindByte, 0xFF, "test.c65", 1)
},
wantAsm: []string{
"\tlda wval",
@ -411,7 +411,7 @@ func TestXorCommand_NewSyntax(t *testing.T) {
name: "self-assignment: word ^= byte_const (optimization: skip high byte entirely)",
line: "wval = wval ^ 42",
setupVars: func(st *compiler.SymbolTable) {
st.AddVar("wval", "", compiler.KindWord, 0x1234)
st.AddVar("wval", "", compiler.KindWord, 0x1234, "test.c65", 1)
},
wantAsm: []string{
"\tlda wval",
@ -423,7 +423,7 @@ func TestXorCommand_NewSyntax(t *testing.T) {
name: "self-assignment: word ^= word_const (no optimization: high byte needed)",
line: "wval = wval ^ 300",
setupVars: func(st *compiler.SymbolTable) {
st.AddVar("wval", "", compiler.KindWord, 0x1234)
st.AddVar("wval", "", compiler.KindWord, 0x1234, "test.c65", 1)
},
wantAsm: []string{
"\tlda wval",
@ -438,8 +438,8 @@ func TestXorCommand_NewSyntax(t *testing.T) {
name: "self-assignment: word ^= word (no optimization: both high bytes needed)",
line: "wval = wval ^ wval2",
setupVars: func(st *compiler.SymbolTable) {
st.AddVar("wval", "", compiler.KindWord, 0x1234)
st.AddVar("wval2", "", compiler.KindWord, 0x5678)
st.AddVar("wval", "", compiler.KindWord, 0x1234, "test.c65", 1)
st.AddVar("wval2", "", compiler.KindWord, 0x5678, "test.c65", 1)
},
wantAsm: []string{
"\tlda wval",
@ -454,8 +454,8 @@ func TestXorCommand_NewSyntax(t *testing.T) {
name: "variable ^ literal",
line: "result = a ^ $AA",
setupVars: func(st *compiler.SymbolTable) {
st.AddVar("a", "", compiler.KindByte, 0xFF)
st.AddVar("result", "", compiler.KindByte, 0)
st.AddVar("a", "", compiler.KindByte, 0xFF, "test.c65", 1)
st.AddVar("result", "", compiler.KindByte, 0, "test.c65", 1)
},
wantAsm: []string{
"\tlda a",
@ -467,7 +467,7 @@ func TestXorCommand_NewSyntax(t *testing.T) {
name: "constant folding",
line: "result = 255 ^ 170",
setupVars: func(st *compiler.SymbolTable) {
st.AddVar("result", "", compiler.KindByte, 0)
st.AddVar("result", "", compiler.KindByte, 0, "test.c65", 1)
},
wantAsm: []string{
"\tlda #$55",
@ -478,7 +478,7 @@ func TestXorCommand_NewSyntax(t *testing.T) {
name: "constant folding word",
line: "result = $FFFF ^ $AAAA",
setupVars: func(st *compiler.SymbolTable) {
st.AddVar("result", "", compiler.KindWord, 0)
st.AddVar("result", "", compiler.KindWord, 0, "test.c65", 1)
},
wantAsm: []string{
"\tlda #$55",
@ -491,9 +491,9 @@ func TestXorCommand_NewSyntax(t *testing.T) {
name: "using constant in expression",
line: "result = a ^ MASK",
setupVars: func(st *compiler.SymbolTable) {
st.AddVar("a", "", compiler.KindByte, 0xFF)
st.AddConst("MASK", "", compiler.KindByte, 0xAA)
st.AddVar("result", "", compiler.KindByte, 0)
st.AddVar("a", "", compiler.KindByte, 0xFF, "test.c65", 1)
st.AddConst("MASK", "", compiler.KindByte, 0xAA, "test.c65", 1)
st.AddVar("result", "", compiler.KindByte, 0, "test.c65", 1)
},
wantAsm: []string{
"\tlda a",
@ -505,8 +505,8 @@ func TestXorCommand_NewSyntax(t *testing.T) {
name: "error: unknown destination",
line: "unknown = a ^ b",
setupVars: func(st *compiler.SymbolTable) {
st.AddVar("a", "", compiler.KindByte, 0)
st.AddVar("b", "", compiler.KindByte, 0)
st.AddVar("a", "", compiler.KindByte, 0, "test.c65", 1)
st.AddVar("b", "", compiler.KindByte, 0, "test.c65", 1)
},
wantErr: true,
},
@ -514,9 +514,9 @@ func TestXorCommand_NewSyntax(t *testing.T) {
name: "error: wrong operator",
line: "result = a + b",
setupVars: func(st *compiler.SymbolTable) {
st.AddVar("a", "", compiler.KindByte, 0)
st.AddVar("b", "", compiler.KindByte, 0)
st.AddVar("result", "", compiler.KindByte, 0)
st.AddVar("a", "", compiler.KindByte, 0, "test.c65", 1)
st.AddVar("b", "", compiler.KindByte, 0, "test.c65", 1)
st.AddVar("result", "", compiler.KindByte, 0, "test.c65", 1)
},
wantErr: true,
},

View file

@ -222,6 +222,12 @@ func (c *Compiler) Compile(lines []preproc.Line) ([]string, error) {
// Analyze for overlapping absolute addresses in function call chains
c.checkAbsoluteOverlaps()
// Check for unused variables and print warnings
warnings := c.ctx.SymbolTable.CheckUnused()
for _, warning := range warnings {
_, _ = fmt.Fprintf(os.Stderr, "%s\n", warning)
}
// Assemble final output with headers and footers
return c.assembleOutput(codeOutput), nil
}

View file

@ -428,7 +428,7 @@ func TestExecuteMacro_LocalVariableExpansion(t *testing.T) {
ctx := NewCompilerContext(pragma)
// Add a local variable in function scope "testfunc"
ctx.SymbolTable.AddVar("myvar", "testfunc", KindByte, 0)
ctx.SymbolTable.AddVar("myvar", "testfunc", KindByte, 0, "test.c65", 1)
// Enter the function scope by declaring the function
_, err := ctx.FunctionHandler.HandleFuncDecl(makeLine("FUNC testfunc"))
@ -468,11 +468,11 @@ func TestExecuteMacro_LocalVariableExpansion_MultipleVars(t *testing.T) {
ctx := NewCompilerContext(pragma)
// Add local variables in function scope
ctx.SymbolTable.AddVar("color_index", "myfunc", KindByte, 0)
ctx.SymbolTable.AddVar("row_color", "myfunc", KindWord, 0)
ctx.SymbolTable.AddVar("color_index", "myfunc", KindByte, 0, "test.c65", 1)
ctx.SymbolTable.AddVar("row_color", "myfunc", KindWord, 0, "test.c65", 1)
// Add a global table (no function scope)
ctx.SymbolTable.AddVar("scroll_color_table", "", KindWord, 0)
ctx.SymbolTable.AddVar("scroll_color_table", "", KindWord, 0, "test.c65", 1)
// Enter the function scope
_, err := ctx.FunctionHandler.HandleFuncDecl(makeLine("FUNC myfunc"))
@ -524,7 +524,7 @@ func TestExecuteScript_LocalVariableExpansion(t *testing.T) {
ctx := NewCompilerContext(pragma)
// Add a local variable in function scope
ctx.SymbolTable.AddVar("counter", "loopfunc", KindByte, 0)
ctx.SymbolTable.AddVar("counter", "loopfunc", KindByte, 0, "test.c65", 1)
// Enter the function scope
_, err := ctx.FunctionHandler.HandleFuncDecl(makeLine("FUNC loopfunc"))
@ -557,7 +557,7 @@ func TestExecuteScript_Library_GlobalVariableExpansion(t *testing.T) {
ctx := NewCompilerContext(pragma)
// Add a global variable
ctx.SymbolTable.AddVar("global_counter", "", KindByte, 0)
ctx.SymbolTable.AddVar("global_counter", "", KindByte, 0, "test.c65", 1)
// Library script that uses |varname| syntax at global scope
// Should expand to the global name (unchanged since it's already global)
@ -597,7 +597,7 @@ func TestExecuteScript_Library_VariableExpansionAtDefinitionTime(t *testing.T) {
ctx := NewCompilerContext(pragma)
// Add a local variable in a function scope
ctx.SymbolTable.AddVar("local_var", "caller", KindByte, 0)
ctx.SymbolTable.AddVar("local_var", "caller", KindByte, 0, "test.c65", 1)
// Library is defined at GLOBAL scope (not inside any function)
// So |varname| expansion happens at global scope during library definition
@ -728,7 +728,7 @@ func TestAsmBlock_VariableExpansion_IgnoresComments(t *testing.T) {
comp := NewCompiler(pragma)
// Add a variable to the symbol table so expansion can work
comp.Context().SymbolTable.AddVar("myvar", "", KindByte, 0)
comp.Context().SymbolTable.AddVar("myvar", "", KindByte, 0, "test.c65", 1)
tests := []struct {
name string
@ -771,7 +771,7 @@ func TestAsmBlock_VariableExpansion_IgnoresComments(t *testing.T) {
t.Run(tt.name, func(t *testing.T) {
// Create fresh compiler for each test
comp := NewCompiler(pragma)
comp.Context().SymbolTable.AddVar("myvar", "", KindByte, 0)
comp.Context().SymbolTable.AddVar("myvar", "", KindByte, 0, "test.c65", 1)
lines := []preproc.Line{
{

View file

@ -124,14 +124,14 @@ func (fh *FunctionHandler) HandleFuncDecl(line preproc.Line) ([]string, error) {
if isImplicit {
// Parse and add implicit variable declaration
// Format: {BYTE varname} or {WORD varname}
if err := fh.parseImplicitDecl(implicitDecl, funcName); err != nil {
if err := fh.parseImplicitDecl(implicitDecl, funcName, line.Filename, line.LineNo); err != nil {
fh.currentFuncs = fh.currentFuncs[:len(fh.currentFuncs)-1]
return nil, fmt.Errorf("%s:%d: FUNC %s: implicit declaration: %w", line.Filename, line.LineNo, funcName, err)
}
}
// Look up variable in symbol table
sym := fh.symTable.Lookup(varName, []string{funcName})
// Look up variable in symbol table (validation only, not usage)
sym := fh.symTable.LookupWithoutUsage(varName, []string{funcName})
if sym == nil {
fh.currentFuncs = fh.currentFuncs[:len(fh.currentFuncs)-1]
return nil, fmt.Errorf("%s:%d: FUNC %s: parameter %q not declared", line.Filename, line.LineNo, funcName, varName)
@ -343,12 +343,7 @@ func (fh *FunctionHandler) HandleFuncCall(line preproc.Line) ([]string, error) {
}
// Use ParseOperandParam for variables, constants, and expressions
constLookup := func(name string) (int64, bool) {
if sym := fh.symTable.Lookup(name, fh.currentFuncs); sym != nil && sym.IsConst() {
return int64(sym.Value), true
}
return 0, false
}
constLookup := fh.symTable.ConstantLookupFunc(fh.currentFuncs)
_, _, value, isVar, err := ParseOperandParam(
arg, fh.symTable, fh.currentFuncs, constLookup)
@ -599,7 +594,7 @@ func (fh *FunctionHandler) processConstValue(value uint16, param *FuncParam, fun
}
// parseImplicitDecl parses {BYTE varname} or {WORD varname} or {BYTE varname @ address} and adds to symbol table
func (fh *FunctionHandler) parseImplicitDecl(decl string, funcName string) error {
func (fh *FunctionHandler) parseImplicitDecl(decl string, funcName string, filename string, lineNo int) error {
parts := strings.Fields(decl)
if len(parts) != 2 && len(parts) != 4 {
return fmt.Errorf("implicit declaration must be 'TYPE name' or 'TYPE name @ addr', got: %q", decl)
@ -620,7 +615,7 @@ func (fh *FunctionHandler) parseImplicitDecl(decl string, funcName string) error
if len(parts) == 2 {
// Simple: BYTE name or WORD name
return fh.symTable.AddVar(varName, funcName, kind, 0)
return fh.symTable.AddVar(varName, funcName, kind, 0, filename, lineNo)
}
// Extended: BYTE name @ address or WORD name @ address
@ -632,13 +627,7 @@ func (fh *FunctionHandler) parseImplicitDecl(decl string, funcName string) error
}
// Create constant lookup function for address evaluation
constLookup := func(name string) (int64, bool) {
sym := fh.symTable.Lookup(name, []string{funcName})
if sym != nil && sym.IsConst() {
return int64(sym.Value), true
}
return 0, false
}
constLookup := fh.symTable.ConstantLookupFunc([]string{funcName})
// Parse address (supports $hex and decimal) using EvaluateExpression
addr, err := utils.EvaluateExpression(addrStr, constLookup)
@ -650,7 +639,7 @@ func (fh *FunctionHandler) parseImplicitDecl(decl string, funcName string) error
return fmt.Errorf("absolute address $%X out of range", addr)
}
return fh.symTable.AddAbsolute(varName, funcName, kind, uint16(addr))
return fh.symTable.AddAbsolute(varName, funcName, kind, uint16(addr), filename, lineNo)
}
// EndFunction pops all functions from the stack (called by FEND)

View file

@ -231,8 +231,8 @@ func TestHandleFuncDecl_WithExistingParams(t *testing.T) {
fh := NewFunctionHandler(st, ls, csh, pragma)
// Pre-declare parameters
st.AddVar("x", "test_func", KindByte, 0)
st.AddVar("y", "test_func", KindWord, 0)
st.AddVar("x", "test_func", KindByte, 0, "test.c65", 1)
st.AddVar("y", "test_func", KindWord, 0, "test.c65", 1)
asm, err := fh.HandleFuncDecl(makeLine("FUNC test_func ( x y )"))
if err != nil {
@ -340,7 +340,7 @@ func TestHandleFuncDecl_Errors(t *testing.T) {
name: "const param",
line: "FUNC test ( constval )",
preDecl: func(st *SymbolTable) {
st.AddConst("constval", "test", KindByte, 42)
st.AddConst("constval", "test", KindByte, 42, "test.c65", 1)
},
wantErr: "cannot be a constant",
},
@ -387,13 +387,13 @@ func TestHandleFuncCall_VarArgs(t *testing.T) {
fh := NewFunctionHandler(st, ls, csh, pragma)
// Declare function with params
st.AddVar("param_a", "test_func", KindByte, 0)
st.AddVar("param_b", "test_func", KindWord, 0)
st.AddVar("param_a", "test_func", KindByte, 0, "test.c65", 1)
st.AddVar("param_b", "test_func", KindWord, 0, "test.c65", 1)
fh.HandleFuncDecl(makeLine("FUNC test_func ( param_a param_b )"))
// Declare caller variables
st.AddVar("var_a", "", KindByte, 0)
st.AddVar("var_b", "", KindWord, 0)
st.AddVar("var_a", "", KindByte, 0, "test.c65", 1)
st.AddVar("var_b", "", KindWord, 0, "test.c65", 1)
asm, err := fh.HandleFuncCall(makeLine("CALL test_func ( var_a var_b )"))
if err != nil {
@ -430,11 +430,11 @@ func TestHandleFuncCall_OutParams(t *testing.T) {
fh := NewFunctionHandler(st, ls, csh, pragma)
// Declare function with out param
st.AddVar("result", "get_result", KindByte, 0)
st.AddVar("result", "get_result", KindByte, 0, "test.c65", 1)
fh.HandleFuncDecl(makeLine("FUNC get_result ( out:result )"))
// Declare caller variable
st.AddVar("output", "", KindByte, 0)
st.AddVar("output", "", KindByte, 0, "test.c65", 1)
asm, err := fh.HandleFuncCall(makeLine("CALL get_result ( output )"))
if err != nil {
@ -469,8 +469,8 @@ func TestHandleFuncCall_ConstArgs(t *testing.T) {
fh := NewFunctionHandler(st, ls, csh, pragma)
// Declare function
st.AddVar("x", "test_const", KindByte, 0)
st.AddVar("y", "test_const", KindWord, 0)
st.AddVar("x", "test_const", KindByte, 0, "test.c65", 1)
st.AddVar("y", "test_const", KindWord, 0, "test.c65", 1)
fh.HandleFuncDecl(makeLine("FUNC test_const ( x y )"))
asm, err := fh.HandleFuncCall(makeLine("CALL test_const ( 42 $1234 )"))
@ -506,7 +506,7 @@ func TestHandleFuncCall_LabelArg(t *testing.T) {
fh := NewFunctionHandler(st, ls, csh, pragma)
// Declare function
st.AddVar("ptr", "test_label", KindWord, 0)
st.AddVar("ptr", "test_label", KindWord, 0, "test.c65", 1)
fh.HandleFuncDecl(makeLine("FUNC test_label ( ptr )"))
asm, err := fh.HandleFuncCall(makeLine("CALL test_label ( @my_label )"))
@ -539,7 +539,7 @@ func TestHandleFuncCall_StringArg(t *testing.T) {
fh := NewFunctionHandler(st, ls, csh, pragma)
// Declare function
st.AddVar("str_ptr", "print", KindWord, 0)
st.AddVar("str_ptr", "print", KindWord, 0, "test.c65", 1)
fh.HandleFuncDecl(makeLine("FUNC print ( str_ptr )"))
asm, err := fh.HandleFuncCall(makeLine(`CALL print ( "hello" )`))
@ -582,7 +582,7 @@ func TestHandleFuncCall_Errors(t *testing.T) {
{
name: "wrong arg count",
setup: func(fh *FunctionHandler, st *SymbolTable) {
st.AddVar("x", "test", KindByte, 0)
st.AddVar("x", "test", KindByte, 0, "test.c65", 1)
fh.HandleFuncDecl(makeLine("FUNC test ( x )"))
},
line: "CALL test ( 1 2 )",
@ -593,9 +593,9 @@ func TestHandleFuncCall_Errors(t *testing.T) {
{
name: "type mismatch",
setup: func(fh *FunctionHandler, st *SymbolTable) {
st.AddVar("param", "test", KindByte, 0)
st.AddVar("param", "test", KindByte, 0, "test.c65", 1)
fh.HandleFuncDecl(makeLine("FUNC test ( param )"))
st.AddVar("wvar", "", KindWord, 0)
st.AddVar("wvar", "", KindWord, 0, "test.c65", 1)
},
line: "CALL test ( wvar )",
wantErr: "type mismatch",
@ -603,7 +603,7 @@ func TestHandleFuncCall_Errors(t *testing.T) {
{
name: "const to out param",
setup: func(fh *FunctionHandler, st *SymbolTable) {
st.AddVar("result", "test", KindByte, 0)
st.AddVar("result", "test", KindByte, 0, "test.c65", 1)
fh.HandleFuncDecl(makeLine("FUNC test ( out:result )"))
},
line: "CALL test ( 42 )",
@ -612,7 +612,7 @@ func TestHandleFuncCall_Errors(t *testing.T) {
{
name: "label to byte param",
setup: func(fh *FunctionHandler, st *SymbolTable) {
st.AddVar("x", "test", KindByte, 0)
st.AddVar("x", "test", KindByte, 0, "test.c65", 1)
fh.HandleFuncDecl(makeLine("FUNC test ( x )"))
},
line: "CALL test ( @label )",
@ -921,8 +921,8 @@ func TestHandleFuncCall_AbsoluteParams(t *testing.T) {
fh.HandleFuncDecl(makeLine("FUNC test_abs ( {BYTE param_a @ $fa} {WORD param_b @ $fb} )"))
// Declare caller variables
st.AddVar("var_a", "", KindByte, 0)
st.AddVar("var_b", "", KindWord, 0)
st.AddVar("var_a", "", KindByte, 0, "test.c65", 1)
st.AddVar("var_b", "", KindWord, 0, "test.c65", 1)
asm, err := fh.HandleFuncCall(makeLine("CALL test_abs ( var_a var_b )"))
if err != nil {
@ -962,7 +962,7 @@ func TestHandleFuncCall_AbsoluteOutParams(t *testing.T) {
fh.HandleFuncDecl(makeLine("FUNC get_result ( out:{BYTE result @ $fa} )"))
// Declare caller variable
st.AddVar("output", "", KindByte, 0)
st.AddVar("output", "", KindByte, 0, "test.c65", 1)
asm, err := fh.HandleFuncCall(makeLine("CALL get_result ( output )"))
if err != nil {
@ -1059,7 +1059,7 @@ func TestParseImplicitDecl_Absolute(t *testing.T) {
st = NewSymbolTable()
fh.symTable = st
err := fh.parseImplicitDecl(tt.decl, tt.funcName)
err := fh.parseImplicitDecl(tt.decl, tt.funcName, "test.c65", 1)
if tt.wantErr {
if err == nil {
t.Fatal("expected error, got nil")
@ -1167,7 +1167,7 @@ FEND
if strings.ToUpper(parts[0]) == "WORD" {
varKind = KindWord
}
st.AddVar(parts[1], "", varKind, 0)
st.AddVar(parts[1], "", varKind, 0, "test.c65", 1)
}
} else if strings.HasPrefix(lineText, "FUNC ") {
_, err := fh.HandleFuncDecl(pline)
@ -1276,7 +1276,7 @@ FEND
addrStr := strings.TrimPrefix(parts[3], "$")
var addr uint16
fmt.Sscanf(addrStr, "%x", &addr)
st.AddAbsolute(parts[1], currentFunc, varKind, addr)
st.AddAbsolute(parts[1], currentFunc, varKind, addr, "test.c65", 1)
}
} else if strings.HasPrefix(lineText, "FUNC ") {
_, err := fh.HandleFuncDecl(pline)
@ -1386,7 +1386,7 @@ FEND
if strings.ToUpper(parts[0]) == "WORD" {
varKind = KindWord
}
st.AddVar(parts[1], "", varKind, 0)
st.AddVar(parts[1], "", varKind, 0, "test.c65", 1)
}
} else if strings.HasPrefix(lineText, "FUNC ") {
_, err := fh.HandleFuncDecl(pline)

View file

@ -177,13 +177,7 @@ func evaluateMacroArg(arg string, ctx *CompilerContext) (starlark.Value, error)
arg = strings.TrimSpace(arg)
// Create lookup function for constants
lookup := func(name string) (int64, bool) {
sym := ctx.SymbolTable.Lookup(name, nil)
if sym != nil && sym.IsConst() {
return int64(sym.Value), true
}
return 0, false
}
lookup := ctx.SymbolTable.ConstantLookupFunc(nil)
// Try to evaluate as expression (number, constant, arithmetic)
val, err := utils.EvaluateExpression(arg, lookup)

View file

@ -26,14 +26,37 @@ const (
FlagLabelRef
)
// Symbol represents a variable or constant declaration
// Symbol represents a variable, constant, or label reference
type Symbol struct {
Name string
Scope string // empty string = global, otherwise function name
Flags SymbolFlags
Value uint16 // init value or const value
AbsAddr uint16 // if FlagAbsolute set
LabelRef string // if FlagLabelRef set
Name string // Variable name
Scope string // Function scope (empty for global)
Flags SymbolFlags // Type and properties
Value uint16 // Initial value for variables, constant value for constants
AbsAddr uint16 // Absolute address (for @ variables)
LabelRef string // Label reference (for label variables)
Usage uint8 // Usage tracking
Filename string // Source file where declared
LineNo int // Line number in source file
}
// Usage tracking for variables
const (
UsageNone uint8 = 0
UsageUsed uint8 = 1 << 0
// Future flags can be added:
// UsageRead = 1 << 1
// UsageWritten = 1 << 2
// UsageAddress = 1 << 3
)
// MarkUsed marks the symbol as used (should not be called for constants or absolutes)
func (s *Symbol) MarkUsed() {
s.Usage |= UsageUsed
}
// IsUsed returns true if the symbol has been marked as used
func (s *Symbol) IsUsed() bool {
return s.Usage&UsageUsed != 0
}
// Helper methods for Symbol
@ -89,7 +112,7 @@ func (st *SymbolTable) SetFunctionHandler(fh FunctionHandlerInterface) {
}
// AddVar adds a regular variable (byte or word)
func (st *SymbolTable) AddVar(name, scope string, kind VarKind, initValue uint16) error {
func (st *SymbolTable) AddVar(name, scope string, kind VarKind, initValue uint16, filename string, lineNo int) error {
var flags SymbolFlags
switch kind {
@ -109,11 +132,13 @@ func (st *SymbolTable) AddVar(name, scope string, kind VarKind, initValue uint16
Scope: scope,
Flags: flags,
Value: initValue,
Filename: filename,
LineNo: lineNo,
})
}
// AddConst adds a constant (byte or word)
func (st *SymbolTable) AddConst(name, scope string, kind VarKind, value uint16) error {
func (st *SymbolTable) AddConst(name, scope string, kind VarKind, value uint16, filename string, lineNo int) error {
var flags SymbolFlags
switch kind {
@ -133,11 +158,13 @@ func (st *SymbolTable) AddConst(name, scope string, kind VarKind, value uint16)
Scope: scope,
Flags: flags,
Value: value,
Filename: filename,
LineNo: lineNo,
})
}
// AddAbsolute adds a variable at a fixed memory address
func (st *SymbolTable) AddAbsolute(name, scope string, kind VarKind, addr uint16) error {
func (st *SymbolTable) AddAbsolute(name, scope string, kind VarKind, addr uint16, filename string, lineNo int) error {
if addr > 0xFFFF {
return fmt.Errorf("absolute address %d exceeds 16-bit range", addr)
}
@ -171,16 +198,20 @@ func (st *SymbolTable) AddAbsolute(name, scope string, kind VarKind, addr uint16
Scope: scope,
Flags: flags,
AbsAddr: addr,
Filename: filename,
LineNo: lineNo,
})
}
// AddLabel adds a word variable that references a label
func (st *SymbolTable) AddLabel(name, scope string, labelRef string) error {
func (st *SymbolTable) AddLabel(name, scope string, labelRef string, filename string, lineNo int) error {
return st.add(&Symbol{
Name: name,
Scope: scope,
Flags: FlagWord | FlagLabelRef,
LabelRef: labelRef,
Filename: filename,
LineNo: lineNo,
})
}
@ -208,12 +239,17 @@ func (st *SymbolTable) add(sym *Symbol) error {
// Lookup finds a symbol by name, resolving scope
// Searches local scope first (if currentScopes provided), then global
// Marks non-constant, non-absolute variables as used
func (st *SymbolTable) Lookup(name string, currentScopes []string) *Symbol {
// Try local scopes first (innermost to outermost)
for i := len(currentScopes) - 1; i >= 0; i-- {
scope := currentScopes[i]
if scopeMap, ok := st.byScope[scope]; ok {
if sym, ok := scopeMap[name]; ok {
// Mark as used if it's a regular variable (not constant, not absolute)
if !sym.IsConst() && !sym.IsAbsolute() {
sym.MarkUsed()
}
return sym
}
}
@ -222,6 +258,10 @@ func (st *SymbolTable) Lookup(name string, currentScopes []string) *Symbol {
// Try global scope
if scopeMap, ok := st.byScope[""]; ok {
if sym, ok := scopeMap[name]; ok {
// Mark as used if it's a regular variable (not constant, not absolute)
if !sym.IsConst() && !sym.IsAbsolute() {
sym.MarkUsed()
}
return sym
}
}
@ -253,6 +293,77 @@ func (st *SymbolTable) ExpandName(name string, currentScopes []string) string {
return name
}
// LookupWithoutUsage finds a symbol by name without marking it as used
// Used for validation-only lookups (e.g., checking if variable exists)
func (st *SymbolTable) LookupWithoutUsage(name string, currentScopes []string) *Symbol {
// Try local scopes first (innermost to outermost)
for i := len(currentScopes) - 1; i >= 0; i-- {
scope := currentScopes[i]
if scopeMap, ok := st.byScope[scope]; ok {
if sym, ok := scopeMap[name]; ok {
return sym
}
}
}
// Try global scope
if scopeMap, ok := st.byScope[""]; ok {
if sym, ok := scopeMap[name]; ok {
return sym
}
}
return nil
}
// LookupConstant looks up a constant by name and returns its value if found
// Returns (value, true) if symbol exists and is a constant, (0, false) otherwise
func (st *SymbolTable) LookupConstant(name string, currentScopes []string) (int64, bool) {
sym := st.LookupWithoutUsage(name, currentScopes)
if sym != nil && sym.IsConst() {
return int64(sym.Value), true
}
return 0, false
}
// ConstantLookupFunc returns a ConstantLookup function that captures the current scopes
// This can be used directly with utils.EvaluateExpression
func (st *SymbolTable) ConstantLookupFunc(currentScopes []string) func(string) (int64, bool) {
return func(name string) (int64, bool) {
return st.LookupConstant(name, currentScopes)
}
}
// CheckUnused returns warnings for unused variables
// Returns slice of warning messages for regular variables (not constants, not absolutes) that were never used
func (st *SymbolTable) CheckUnused() []string {
var warnings []string
for _, sym := range st.symbols {
// Skip constants and absolute variables (they shouldn't track usage)
if sym.IsConst() || sym.IsAbsolute() {
// Sanity check: constants and absolutes should never be marked as used
// If they are, it's a bug in the compiler
if sym.IsUsed() {
// This would be an internal error, but we'll just skip it
continue
}
continue
}
// Check if variable was never used
if !sym.IsUsed() {
// Format warning message with file and line info
var scopeInfo string
if sym.Scope != "" {
scopeInfo = fmt.Sprintf(" in function '%s'", sym.Scope)
}
warning := fmt.Sprintf("%s:%d: warning: variable '%s'%s declared but never used",
sym.Filename, sym.LineNo, sym.Name, scopeInfo)
warnings = append(warnings, warning)
}
}
return warnings
}
// String representation for debugging
func (s *Symbol) String() string {
var parts []string
@ -277,6 +388,11 @@ func (s *Symbol) String() string {
parts = append(parts, fmt.Sprintf("=%d", s.Value))
}
// Add location info if available
if s.Filename != "" {
parts = append(parts, fmt.Sprintf("@%s:%d", s.Filename, s.LineNo))
}
return strings.Join(parts, " ")
}

View file

@ -106,7 +106,7 @@ func TestAddVar(t *testing.T) {
st := NewSymbolTable()
// Add byte var
err := st.AddVar("counter", "", KindByte, 0)
err := st.AddVar("counter", "", KindByte, 0, "test.c65", 1)
if err != nil {
t.Fatalf("AddVar() error = %v", err)
}
@ -123,7 +123,7 @@ func TestAddVar(t *testing.T) {
}
// Add word var
err = st.AddVar("ptr", "", KindWord, 0x1234)
err = st.AddVar("ptr", "", KindWord, 0x1234, "test.c65", 1)
if err != nil {
t.Fatalf("AddVar() error = %v", err)
}
@ -137,7 +137,7 @@ func TestAddVar(t *testing.T) {
}
// Test byte value range check
err = st.AddVar("bad", "", KindByte, 256)
err = st.AddVar("bad", "", KindByte, 256, "test.c65", 1)
if err == nil {
t.Error("expected error for byte value > 255")
}
@ -146,7 +146,7 @@ func TestAddVar(t *testing.T) {
func TestAddConst(t *testing.T) {
st := NewSymbolTable()
err := st.AddConst("MAX", "", KindByte, 255)
err := st.AddConst("MAX", "", KindByte, 255, "test.c65", 1)
if err != nil {
t.Fatalf("AddConst() error = %v", err)
}
@ -166,7 +166,7 @@ func TestAddConst(t *testing.T) {
}
// Test byte range check
err = st.AddConst("BAD", "", KindByte, 300)
err = st.AddConst("BAD", "", KindByte, 300, "test.c65", 1)
if err == nil {
t.Error("expected error for byte const > 255")
}
@ -176,7 +176,7 @@ func TestAddAbsolute(t *testing.T) {
st := NewSymbolTable()
// Zero-page byte
err := st.AddAbsolute("ZP_VAR", "", KindByte, 0x80)
err := st.AddAbsolute("ZP_VAR", "", KindByte, 0x80, "test.c65", 1)
if err != nil {
t.Fatalf("AddAbsolute() error = %v", err)
}
@ -193,7 +193,7 @@ func TestAddAbsolute(t *testing.T) {
}
// Zero-page word pointer
err = st.AddAbsolute("ZP_PTR", "", KindWord, 0xFE)
err = st.AddAbsolute("ZP_PTR", "", KindWord, 0xFE, "test.c65", 1)
if err != nil {
t.Fatalf("AddAbsolute() error = %v", err)
}
@ -204,7 +204,7 @@ func TestAddAbsolute(t *testing.T) {
}
// Non-zero-page
err = st.AddAbsolute("VIC", "", KindWord, 0xD000)
err = st.AddAbsolute("VIC", "", KindWord, 0xD000, "test.c65", 1)
if err != nil {
t.Fatalf("AddAbsolute() error = %v", err)
}
@ -224,7 +224,7 @@ func TestAddAbsolute(t *testing.T) {
func TestAddLabel(t *testing.T) {
st := NewSymbolTable()
err := st.AddLabel("handler", "", "irq_vector")
err := st.AddLabel("handler", "", "irq_vector", "test.c65", 1)
if err != nil {
t.Fatalf("AddLabel() error = %v", err)
}
@ -247,19 +247,19 @@ func TestAddLabel(t *testing.T) {
func TestRedeclaration(t *testing.T) {
st := NewSymbolTable()
err := st.AddVar("test", "", KindByte, 0)
err := st.AddVar("test", "", KindByte, 0, "test.c65", 1)
if err != nil {
t.Fatalf("first AddVar() error = %v", err)
}
// Attempt redeclaration
err = st.AddVar("test", "", KindByte, 0)
err = st.AddVar("test", "", KindByte, 0, "test.c65", 1)
if err == nil {
t.Error("expected error on redeclaration")
}
// Different scope should be OK
err = st.AddVar("test", "main", KindByte, 0)
err = st.AddVar("test", "main", KindByte, 0, "test.c65", 1)
if err != nil {
t.Errorf("AddVar() with different scope error = %v", err)
}
@ -269,13 +269,13 @@ func TestLookup(t *testing.T) {
st := NewSymbolTable()
// Global variable
st.AddVar("global", "", KindByte, 0)
st.AddVar("global", "", KindByte, 0, "test.c65", 1)
// Local in main
st.AddVar("local", "main", KindByte, 0)
st.AddVar("local", "main", KindByte, 0, "test.c65", 1)
// Local in nested function
st.AddVar("inner", "main_helper", KindByte, 0)
st.AddVar("inner", "main_helper", KindByte, 0, "test.c65", 1)
tests := []struct {
name string
@ -350,8 +350,8 @@ func TestLookup(t *testing.T) {
func TestExpandName(t *testing.T) {
st := NewSymbolTable()
st.AddVar("global", "", KindByte, 0)
st.AddVar("local", "main", KindByte, 0)
st.AddVar("global", "", KindByte, 0, "test.c65", 1)
st.AddVar("local", "main", KindByte, 0, "test.c65", 1)
tests := []struct {
name string
@ -379,7 +379,7 @@ func TestInsertionOrder(t *testing.T) {
names := []string{"first", "second", "third"}
for _, name := range names {
st.AddVar(name, "", KindByte, 0)
st.AddVar(name, "", KindByte, 0, "test.c65", 1)
}
symbols := st.Symbols()
@ -401,8 +401,8 @@ func TestCount(t *testing.T) {
t.Errorf("initial Count() = %d, want 0", st.Count())
}
st.AddVar("a", "", KindByte, 0)
st.AddVar("b", "", KindByte, 0)
st.AddVar("a", "", KindByte, 0, "test.c65", 1)
st.AddVar("b", "", KindByte, 0, "test.c65", 1)
if st.Count() != 2 {
t.Errorf("Count() = %d, want 2", st.Count())
@ -418,7 +418,7 @@ func TestSymbolString(t *testing.T) {
{
name: "byte variable",
setup: func(st *SymbolTable) *Symbol {
st.AddVar("test", "", KindByte, 0)
st.AddVar("test", "", KindByte, 0, "test.c65", 1)
return st.Get("test")
},
contains: []string{"Name=test", "BYTE"},
@ -426,7 +426,7 @@ func TestSymbolString(t *testing.T) {
{
name: "word constant",
setup: func(st *SymbolTable) *Symbol {
st.AddConst("MAX", "", KindWord, 65535)
st.AddConst("MAX", "", KindWord, 65535, "test.c65", 1)
return st.Get("MAX")
},
contains: []string{"Name=MAX", "WORD", "CONST=65535"},
@ -434,7 +434,7 @@ func TestSymbolString(t *testing.T) {
{
name: "zero-page pointer",
setup: func(st *SymbolTable) *Symbol {
st.AddAbsolute("ptr", "", KindWord, 0x80)
st.AddAbsolute("ptr", "", KindWord, 0x80, "test.c65", 1)
return st.Get("ptr")
},
contains: []string{"Name=ptr", "WORD", "@$0080", "ZP"},
@ -442,7 +442,7 @@ func TestSymbolString(t *testing.T) {
{
name: "label reference",
setup: func(st *SymbolTable) *Symbol {
st.AddLabel("handler", "", "irq")
st.AddLabel("handler", "", "irq", "test.c65", 1)
return st.Get("handler")
},
contains: []string{"Name=handler", "->irq"},
@ -484,9 +484,9 @@ func TestGenerateConstants(t *testing.T) {
st := NewSymbolTable()
// Add some constants
st.AddConst("MAX", "", KindByte, 255)
st.AddConst("SIZE", "", KindWord, 0x1234)
st.AddVar("notconst", "", KindByte, 0) // should be skipped
st.AddConst("MAX", "", KindByte, 255, "test.c65", 1)
st.AddConst("SIZE", "", KindWord, 0x1234, "test.c65", 1)
st.AddVar("notconst", "", KindByte, 0, "test.c65", 1) // should be skipped
lines := GenerateConstants(st)
@ -520,14 +520,14 @@ func TestGenerateAbsolutes(t *testing.T) {
st := NewSymbolTable()
// Zero-page
st.AddAbsolute("ZP_VAR", "", KindByte, 0x80)
st.AddAbsolute("ZP_PTR", "", KindWord, 0xFE)
st.AddAbsolute("ZP_VAR", "", KindByte, 0x80, "test.c65", 1)
st.AddAbsolute("ZP_PTR", "", KindWord, 0xFE, "test.c65", 1)
// Non-zero-page
st.AddAbsolute("VIC", "", KindWord, 0xD000)
st.AddAbsolute("VIC", "", KindWord, 0xD000, "test.c65", 1)
// Regular var (should be skipped)
st.AddVar("regular", "", KindByte, 0)
st.AddVar("regular", "", KindByte, 0, "test.c65", 1)
lines := GenerateAbsolutes(st)
@ -564,19 +564,19 @@ func TestGenerateVariables(t *testing.T) {
st := NewSymbolTable()
// Byte variable
st.AddVar("counter", "", KindByte, 42)
st.AddVar("counter", "", KindByte, 42, "test.c65", 1)
// Word variable
st.AddVar("ptr", "", KindWord, 0x1234)
st.AddVar("ptr", "", KindWord, 0x1234, "test.c65", 1)
// Label reference
st.AddLabel("handler", "", "irq_routine")
st.AddLabel("handler", "", "irq_routine", "test.c65", 1)
// Const (should be skipped)
st.AddConst("SKIP", "", KindByte, 99)
st.AddConst("SKIP", "", KindByte, 99, "test.c65", 1)
// Absolute (should be skipped)
st.AddAbsolute("SKIP2", "", KindByte, 0x80)
st.AddAbsolute("SKIP2", "", KindByte, 0x80, "test.c65", 1)
lines := GenerateVariables(st)
@ -633,7 +633,7 @@ func TestGenerateEmpty(t *testing.T) {
}
// Only variables (no constants/absolutes)
st.AddVar("test", "", KindByte, 0)
st.AddVar("test", "", KindByte, 0, "test.c65", 1)
if lines := GenerateConstants(st); lines != nil {
t.Error("expected nil when no constants exist")
@ -646,9 +646,9 @@ func TestGenerateEmpty(t *testing.T) {
func TestGenerateScopedVariables(t *testing.T) {
st := NewSymbolTable()
st.AddVar("global", "", KindByte, 0)
st.AddVar("local", "main", KindByte, 0)
st.AddVar("nested", "main_helper", KindByte, 0)
st.AddVar("global", "", KindByte, 0, "test.c65", 1)
st.AddVar("local", "main", KindByte, 0, "test.c65", 1)
st.AddVar("nested", "main_helper", KindByte, 0, "test.c65", 1)
lines := GenerateVariables(st)
output := strings.Join(lines, "\n")
@ -668,9 +668,9 @@ func TestGenerateScopedVariables(t *testing.T) {
func TestGenerateHexLowercase(t *testing.T) {
st := NewSymbolTable()
st.AddConst("TEST", "", KindByte, 0xAB)
st.AddAbsolute("ADDR", "", KindWord, 0xDEAD)
st.AddVar("VAR", "", KindWord, 0xBEEF)
st.AddConst("TEST", "", KindByte, 0xAB, "test.c65", 1)
st.AddAbsolute("ADDR", "", KindWord, 0xDEAD, "test.c65", 1)
st.AddVar("VAR", "", KindWord, 0xBEEF, "test.c65", 1)
constLines := GenerateConstants(st)
absLines := GenerateAbsolutes(st)
@ -693,3 +693,381 @@ func TestGenerateHexLowercase(t *testing.T) {
t.Error("expected lowercase hex in variable")
}
}
func TestUsageTracking(t *testing.T) {
t.Run("Unused variable generates warning", func(t *testing.T) {
st := NewSymbolTable()
// Add an unused variable
err := st.AddVar("unused", "", KindByte, 0, "test.c65", 10)
if err != nil {
t.Fatalf("AddVar() error = %v", err)
}
// Check that it's marked as unused
sym := st.Get("unused")
if sym == nil {
t.Fatal("symbol not found")
}
if sym.IsUsed() {
t.Errorf("IsUsed() = true, want false")
}
// Check that warning is generated
warnings := st.CheckUnused()
if len(warnings) != 1 {
t.Fatalf("CheckUnused() returned %d warnings, want 1", len(warnings))
}
expected := "test.c65:10: warning: variable 'unused' declared but never used"
if warnings[0] != expected {
t.Errorf("warning = %q, want %q", warnings[0], expected)
}
})
t.Run("Used variable doesn't generate warning", func(t *testing.T) {
st := NewSymbolTable()
// Add a variable
err := st.AddVar("used", "", KindByte, 0, "test.c65", 20)
if err != nil {
t.Fatalf("AddVar() error = %v", err)
}
// Mark it as used via Lookup (simulating actual usage)
sym := st.Lookup("used", []string{})
if sym == nil {
t.Fatal("symbol not found")
}
// Check that it's marked as used
if !sym.IsUsed() {
t.Errorf("IsUsed() = false, want true")
}
// Check that no warning is generated
warnings := st.CheckUnused()
if len(warnings) != 0 {
t.Errorf("CheckUnused() returned %d warnings, want 0: %v", len(warnings), warnings)
}
})
t.Run("Constants are ignored", func(t *testing.T) {
st := NewSymbolTable()
// Add a constant (should not generate warning)
err := st.AddConst("MAX", "", KindByte, 100, "test.c65", 30)
if err != nil {
t.Fatalf("AddConst() error = %v", err)
}
// Check that no warning is generated
warnings := st.CheckUnused()
if len(warnings) != 0 {
t.Errorf("CheckUnused() returned %d warnings for constant, want 0: %v", len(warnings), warnings)
}
// Verify constant is not marked as used (constants shouldn't track usage)
sym := st.Get("MAX")
if sym.IsUsed() {
t.Errorf("Constant IsUsed() = true, want false")
}
})
t.Run("Absolute variables are ignored", func(t *testing.T) {
st := NewSymbolTable()
// Add an absolute variable (should not generate warning)
err := st.AddAbsolute("SCREEN", "", KindWord, 0xD000, "test.c65", 40)
if err != nil {
t.Fatalf("AddAbsolute() error = %v", err)
}
// Check that no warning is generated
warnings := st.CheckUnused()
if len(warnings) != 0 {
t.Errorf("CheckUnused() returned %d warnings for absolute variable, want 0: %v", len(warnings), warnings)
}
// Verify absolute variable is not marked as used (absolutes shouldn't track usage)
sym := st.Get("SCREEN")
if sym.IsUsed() {
t.Errorf("Absolute variable IsUsed() = true, want false")
}
})
t.Run("Function-scoped variables", func(t *testing.T) {
st := NewSymbolTable()
// Add a global unused variable
err := st.AddVar("global_unused", "", KindByte, 0, "test.c65", 50)
if err != nil {
t.Fatalf("AddVar() error = %v", err)
}
// Add a function-scoped unused variable
err = st.AddVar("local_unused", "myFunc", KindByte, 0, "test.c65", 60)
if err != nil {
t.Fatalf("AddVar() error = %v", err)
}
// Add a function-scoped used variable
err = st.AddVar("local_used", "myFunc", KindByte, 0, "test.c65", 70)
if err != nil {
t.Fatalf("AddVar() error = %v", err)
}
st.Lookup("local_used", []string{"myFunc"})
// Check warnings
warnings := st.CheckUnused()
if len(warnings) != 2 {
t.Fatalf("CheckUnused() returned %d warnings, want 2: %v", len(warnings), warnings)
}
// Check warning formats
expectedGlobal := "test.c65:50: warning: variable 'global_unused' declared but never used"
expectedLocal := "test.c65:60: warning: variable 'local_unused' in function 'myFunc' declared but never used"
foundGlobal := false
foundLocal := false
for _, w := range warnings {
if w == expectedGlobal {
foundGlobal = true
}
if w == expectedLocal {
foundLocal = true
}
}
if !foundGlobal {
t.Errorf("Missing warning for global variable: %q", expectedGlobal)
}
if !foundLocal {
t.Errorf("Missing warning for local variable: %q", expectedLocal)
}
})
t.Run("Multiple lookups don't affect usage flag", func(t *testing.T) {
st := NewSymbolTable()
// Add a variable
err := st.AddVar("counter", "", KindByte, 0, "test.c65", 80)
if err != nil {
t.Fatalf("AddVar() error = %v", err)
}
// Lookup multiple times (should only mark as used once)
sym1 := st.Lookup("counter", []string{})
sym2 := st.Lookup("counter", []string{})
sym3 := st.Lookup("counter", []string{})
if sym1 != sym2 || sym2 != sym3 {
t.Error("Lookup should return same symbol instance")
}
// Should still be marked as used (idempotent)
if !sym1.IsUsed() {
t.Errorf("IsUsed() after multiple lookups = false, want true")
}
// No warnings should be generated
warnings := st.CheckUnused()
if len(warnings) != 0 {
t.Errorf("CheckUnused() returned %d warnings for used variable, want 0", len(warnings))
}
})
t.Run("LookupWithoutUsage doesn't mark as used", func(t *testing.T) {
st := NewSymbolTable()
// Add a variable
err := st.AddVar("temp", "", KindByte, 0, "test.c65", 90)
if err != nil {
t.Fatalf("AddVar() error = %v", err)
}
// Use LookupWithoutUsage (validation only, not actual usage)
sym := st.LookupWithoutUsage("temp", []string{})
if sym == nil {
t.Fatal("symbol not found")
}
// Should NOT be marked as used
if sym.IsUsed() {
t.Errorf("IsUsed() after LookupWithoutUsage = true, want false")
}
// Warning should be generated
warnings := st.CheckUnused()
if len(warnings) != 1 {
t.Errorf("CheckUnused() returned %d warnings, want 1", len(warnings))
}
})
t.Run("Mixed variables with usage", func(t *testing.T) {
st := NewSymbolTable()
// Add various types of variables
st.AddVar("unused1", "", KindByte, 0, "test.c65", 100)
st.AddVar("used1", "", KindByte, 0, "test.c65", 101)
st.AddConst("CONST1", "", KindByte, 255, "test.c65", 102)
st.AddAbsolute("ABS1", "", KindWord, 0xC000, "test.c65", 103)
st.AddVar("unused2", "func1", KindWord, 0, "test.c65", 104)
st.AddVar("used2", "func1", KindWord, 0, "test.c65", 105)
// Mark some as used
st.Lookup("used1", []string{})
st.Lookup("used2", []string{"func1"})
// Check warnings
warnings := st.CheckUnused()
if len(warnings) != 2 {
t.Fatalf("CheckUnused() returned %d warnings, want 2: %v", len(warnings), warnings)
}
// Verify correct warnings
expected1 := "test.c65:100: warning: variable 'unused1' declared but never used"
expected2 := "test.c65:104: warning: variable 'unused2' in function 'func1' declared but never used"
found1 := false
found2 := false
for _, w := range warnings {
if w == expected1 {
found1 = true
}
if w == expected2 {
found2 = true
}
}
if !found1 {
t.Errorf("Missing warning: %q", expected1)
}
if !found2 {
t.Errorf("Missing warning: %q", expected2)
}
})
t.Run("Label references", func(t *testing.T) {
st := NewSymbolTable()
// Add a label reference variable
err := st.AddLabel("handler", "", "irq_vector", "test.c65", 110)
if err != nil {
t.Fatalf("AddLabel() error = %v", err)
}
// Label references are word variables that reference labels
// They should be treated like regular variables for usage tracking
sym := st.Get("handler")
if !sym.Has(FlagLabelRef) {
t.Error("Expected FlagLabelRef")
}
if !sym.IsWord() {
t.Error("Label reference should be word")
}
// Since we haven't used it, it should generate a warning
warnings := st.CheckUnused()
if len(warnings) != 1 {
t.Errorf("CheckUnused() returned %d warnings for label reference, want 1", len(warnings))
}
// Mark it as used
st.Lookup("handler", []string{})
// Now no warning should be generated
warnings = st.CheckUnused()
if len(warnings) != 0 {
t.Errorf("CheckUnused() returned %d warnings for used label reference, want 0", len(warnings))
}
})
t.Run("LookupConstant doesn't mark as used", func(t *testing.T) {
st := NewSymbolTable()
// Add a regular variable
err := st.AddVar("var1", "", KindByte, 0, "test.c65", 120)
if err != nil {
t.Fatalf("AddVar() error = %v", err)
}
// Add a constant
err = st.AddConst("CONST1", "", KindByte, 100, "test.c65", 121)
if err != nil {
t.Fatalf("AddConst() error = %v", err)
}
// Lookup constant (should not mark variable as used)
val, found := st.LookupConstant("CONST1", []string{})
if !found {
t.Fatal("constant not found")
}
if val != 100 {
t.Errorf("constant value = %d, want 100", val)
}
// LookupConstant on non-constant variable (should not mark as used)
val, found = st.LookupConstant("var1", []string{})
if found {
t.Error("LookupConstant should return false for non-constant")
}
// Check that variable is still unused
sym := st.Get("var1")
if sym.IsUsed() {
t.Errorf("IsUsed() after LookupConstant = true, want false")
}
// Warning should be generated for the variable
warnings := st.CheckUnused()
if len(warnings) != 1 {
t.Errorf("CheckUnused() returned %d warnings, want 1", len(warnings))
}
expected := "test.c65:120: warning: variable 'var1' declared but never used"
if warnings[0] != expected {
t.Errorf("warning = %q, want %q", warnings[0], expected)
}
})
t.Run("Lookup doesn't mark constants or absolutes as used", func(t *testing.T) {
st := NewSymbolTable()
// Add a constant
err := st.AddConst("MAX", "", KindByte, 255, "test.c65", 130)
if err != nil {
t.Fatalf("AddConst() error = %v", err)
}
// Add an absolute variable
err = st.AddAbsolute("VIC", "", KindWord, 0xD000, "test.c65", 131)
if err != nil {
t.Fatalf("AddAbsolute() error = %v", err)
}
// Lookup constant (should not mark as used)
symConst := st.Lookup("MAX", []string{})
if symConst == nil {
t.Fatal("constant not found")
}
if symConst.IsUsed() {
t.Error("constant should not be marked as used after Lookup")
}
// Lookup absolute variable (should not mark as used)
symAbs := st.Lookup("VIC", []string{})
if symAbs == nil {
t.Fatal("absolute variable not found")
}
if symAbs.IsUsed() {
t.Error("absolute variable should not be marked as used after Lookup")
}
// No warnings should be generated
warnings := st.CheckUnused()
if len(warnings) != 0 {
t.Errorf("CheckUnused() returned %d warnings for constants/absolutes, want 0", len(warnings))
}
})
}

View file

@ -0,0 +1,13 @@
{
"$schema": "https://opencode.ai/config.json",
"agent": {
"plan": {
"model": "deepseek/deepseek-reasoner",
"description": "Planning and architecture analysis using DeepSeek Reasoner"
},
"build": {
"model": "deepseek/deepseek-chat",
"description": "Implementation and coding using DeepSeek Chat"
}
}
}

12
opencode-docker.sh Normal file
View file

@ -0,0 +1,12 @@
#!/bin/bash
set -euo pipefail
export DOCKER_UID=$(id -u)
export DOCKER_GID=$(id -g)
# Load from .env if it exists
if [ -f .env ]; then
export $(grep -v '^#' .env | xargs)
fi
docker compose run --rm opencode-deepseek-c65gm