Compare commits

..

No commits in common. "90caee543966c64f0b399856b392e1f0adbe7db2" and "4f4df41c189ef37570c9378b38708b611ba52cc9" have entirely different histories.

51 changed files with 770 additions and 1304 deletions

5
.gitignore vendored
View file

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

167
AGENTS.md
View file

@ -1,167 +0,0 @@
# 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

6
Dockerfile Normal file
View file

@ -0,0 +1,6 @@
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"]

4
claude_code_docker.sh Normal file
View file

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

View file

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

View file

@ -68,7 +68,13 @@ func (c *AddCommand) Interpret(line preproc.Line, ctx *compiler.CompilerContext)
scope := ctx.CurrentScope() scope := ctx.CurrentScope()
// Create constant lookup function // Create constant lookup function
constLookup := ctx.SymbolTable.ConstantLookupFunc(scope) 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
}
// Determine syntax and parse accordingly // Determine syntax and parse accordingly
if strings.ToUpper(params[0]) == "ADD" { if strings.ToUpper(params[0]) == "ADD" {

View file

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

View file

@ -68,7 +68,13 @@ func (c *AndCommand) Interpret(line preproc.Line, ctx *compiler.CompilerContext)
scope := ctx.CurrentScope() scope := ctx.CurrentScope()
// Create constant lookup function // Create constant lookup function
constLookup := ctx.SymbolTable.ConstantLookupFunc(scope) 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
}
// Determine syntax and parse accordingly // Determine syntax and parse accordingly
if strings.ToUpper(params[0]) == "AND" { if strings.ToUpper(params[0]) == "AND" {

View file

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

View file

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

View file

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

View file

@ -39,7 +39,13 @@ func (c *CaseCommand) Interpret(line preproc.Line, ctx *compiler.CompilerContext
} }
scope := ctx.CurrentScope() scope := ctx.CurrentScope()
constLookup := ctx.SymbolTable.ConstantLookupFunc(scope) 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
}
// Parse the case value // Parse the case value
varName, varKind, value, isVar, err := compiler.ParseOperandParam( varName, varKind, value, isVar, err := compiler.ParseOperandParam(

View file

@ -97,7 +97,13 @@ func (c *DecrCommand) Interpret(line preproc.Line, ctx *compiler.CompilerContext
} }
// Old syntax allows absolute addresses // Old syntax allows absolute addresses
constLookup := ctx.SymbolTable.ConstantLookupFunc(scope) 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
}
val, evalErr := utils.EvaluateExpression(targetParam, constLookup) val, evalErr := utils.EvaluateExpression(targetParam, constLookup)
if evalErr != nil { if evalErr != nil {

View file

@ -48,7 +48,13 @@ func (c *ForCommand) Interpret(line preproc.Line, ctx *compiler.CompilerContext)
} }
scope := ctx.CurrentScope() scope := ctx.CurrentScope()
constLookup := ctx.SymbolTable.ConstantLookupFunc(scope) 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
}
// Parse variable // Parse variable
varName := params[1] varName := params[1]

View file

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

View file

@ -70,7 +70,13 @@ func (c *GosubCommand) Interpret(line preproc.Line, ctx *compiler.CompilerContex
scope := ctx.CurrentScope() scope := ctx.CurrentScope()
constLookup := ctx.SymbolTable.ConstantLookupFunc(scope) 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
}
// Parse target (param 2) // Parse target (param 2)
targetParam := params[1] targetParam := params[1]

View file

@ -52,7 +52,13 @@ func (c *GotoCommand) Interpret(line preproc.Line, ctx *compiler.CompilerContext
targetParam := params[1] targetParam := params[1]
scope := ctx.CurrentScope() scope := ctx.CurrentScope()
constLookup := ctx.SymbolTable.ConstantLookupFunc(scope) 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
}
// Try ParseOperandParam - handles variables, constants, expressions // Try ParseOperandParam - handles variables, constants, expressions
varName, varKind, value, isVar, err := compiler.ParseOperandParam( varName, varKind, value, isVar, err := compiler.ParseOperandParam(

View file

@ -40,7 +40,13 @@ func (c *IfCommand) Interpret(line preproc.Line, ctx *compiler.CompilerContext)
} }
scope := ctx.CurrentScope() scope := ctx.CurrentScope()
constLookup := ctx.SymbolTable.ConstantLookupFunc(scope) 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
}
// Parse param1 // Parse param1
varName, varKind, value, isVar, err := compiler.ParseOperandParam( varName, varKind, value, isVar, err := compiler.ParseOperandParam(

View file

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

View file

@ -97,7 +97,13 @@ func (c *IncrCommand) Interpret(line preproc.Line, ctx *compiler.CompilerContext
} }
// Old syntax allows absolute addresses // Old syntax allows absolute addresses
constLookup := ctx.SymbolTable.ConstantLookupFunc(scope) 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
}
val, evalErr := utils.EvaluateExpression(targetParam, constLookup) val, evalErr := utils.EvaluateExpression(targetParam, constLookup)
if evalErr != nil { if evalErr != nil {

View file

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

View file

@ -62,7 +62,13 @@ func (c *LetCommand) Interpret(line preproc.Line, ctx *compiler.CompilerContext)
scope := ctx.CurrentScope() scope := ctx.CurrentScope()
// Create constant lookup function // Create constant lookup function
constLookup := ctx.SymbolTable.ConstantLookupFunc(scope) 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
}
// Determine syntax and parse accordingly // Determine syntax and parse accordingly
if strings.ToUpper(params[0]) == "LET" { if strings.ToUpper(params[0]) == "LET" {

View file

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

View file

@ -68,7 +68,13 @@ func (c *OrCommand) Interpret(line preproc.Line, ctx *compiler.CompilerContext)
scope := ctx.CurrentScope() scope := ctx.CurrentScope()
// Create constant lookup function // Create constant lookup function
constLookup := ctx.SymbolTable.ConstantLookupFunc(scope) 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
}
// Determine syntax and parse accordingly // Determine syntax and parse accordingly
if strings.ToUpper(params[0]) == "OR" { if strings.ToUpper(params[0]) == "OR" {

View file

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

View file

@ -36,7 +36,13 @@ func (c *OriginCommand) Interpret(line preproc.Line, ctx *compiler.CompilerConte
} }
scope := ctx.CurrentScope() scope := ctx.CurrentScope()
constLookup := ctx.SymbolTable.ConstantLookupFunc(scope) 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
}
val, err := utils.EvaluateExpression(params[1], constLookup) val, err := utils.EvaluateExpression(params[1], constLookup)
if err != nil { if err != nil {

View file

@ -115,7 +115,13 @@ func (c *PeekCommand) Interpret(line preproc.Line, ctx *compiler.CompilerContext
// Parse address - may have [offset] // Parse address - may have [offset]
baseAddr, offsetParam := parseOffset(addrParam) baseAddr, offsetParam := parseOffset(addrParam)
constLookup := ctx.SymbolTable.ConstantLookupFunc(scope) 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
}
// Try to parse base address // Try to parse base address
varName, varKind, value, isVar, err := compiler.ParseOperandParam( varName, varKind, value, isVar, err := compiler.ParseOperandParam(

View file

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

View file

@ -116,7 +116,13 @@ func (c *PeekWCommand) Interpret(line preproc.Line, ctx *compiler.CompilerContex
// Parse address - may have [offset] // Parse address - may have [offset]
baseAddr, offsetParam := parseOffsetW(addrParam) baseAddr, offsetParam := parseOffsetW(addrParam)
constLookup := ctx.SymbolTable.ConstantLookupFunc(scope) 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
}
// Try to parse base address // Try to parse base address
varName, varKind, value, isVar, err := compiler.ParseOperandParam( varName, varKind, value, isVar, err := compiler.ParseOperandParam(

View file

@ -74,7 +74,13 @@ func (c *PointerCommand) Interpret(line preproc.Line, ctx *compiler.CompilerCont
// Parse target (param 4) // Parse target (param 4)
targetParam := params[3] targetParam := params[3]
constLookup := ctx.SymbolTable.ConstantLookupFunc(scope) 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
}
// Try ParseOperandParam - handles variables, constants, expressions // Try ParseOperandParam - handles variables, constants, expressions
varName, _, value, isVar, err := compiler.ParseOperandParam( varName, _, value, isVar, err := compiler.ParseOperandParam(

View file

@ -83,7 +83,13 @@ func (c *PokeCommand) Interpret(line preproc.Line, ctx *compiler.CompilerContext
addrParam := params[1] addrParam := params[1]
baseAddr, offsetParam := parsePOKEOffset(addrParam) baseAddr, offsetParam := parsePOKEOffset(addrParam)
constLookup := ctx.SymbolTable.ConstantLookupFunc(scope) 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
}
// Try to parse base address // Try to parse base address
varName, varKind, value, isVar, err := compiler.ParseOperandParam( varName, varKind, value, isVar, err := compiler.ParseOperandParam(

View file

@ -82,7 +82,13 @@ func (c *PokeWCommand) Interpret(line preproc.Line, ctx *compiler.CompilerContex
addrParam := params[1] addrParam := params[1]
baseAddr, offsetParam := parsePOKEWOffset(addrParam) baseAddr, offsetParam := parsePOKEWOffset(addrParam)
constLookup := ctx.SymbolTable.ConstantLookupFunc(scope) 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
}
// Try to parse base address // Try to parse base address
varName, varKind, value, isVar, err := compiler.ParseOperandParam( varName, varKind, value, isVar, err := compiler.ParseOperandParam(

View file

@ -71,7 +71,13 @@ func (c *SubtractCommand) Interpret(line preproc.Line, ctx *compiler.CompilerCon
scope := ctx.CurrentScope() scope := ctx.CurrentScope()
// Create constant lookup function // Create constant lookup function
constLookup := ctx.SymbolTable.ConstantLookupFunc(scope) 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
}
// Determine syntax and parse accordingly // Determine syntax and parse accordingly
kw := strings.ToUpper(params[0]) 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)", name: "SUBTRACT a FROM b (means b-a)",
line: "SUBTRACT 5 FROM 10 GIVING result", line: "SUBTRACT 5 FROM 10 GIVING result",
setupVars: func(st *compiler.SymbolTable) { setupVars: func(st *compiler.SymbolTable) {
st.AddVar("result", "", compiler.KindByte, 0, "test.c65", 1) st.AddVar("result", "", compiler.KindByte, 0)
}, },
wantAsm: []string{ wantAsm: []string{
"\tlda #$05", "\tlda #$05",
@ -61,9 +61,9 @@ func TestSubtractCommand_OldSyntax_FROM(t *testing.T) {
name: "SUBTRACT byte FROM byte -> byte (variables)", name: "SUBTRACT byte FROM byte -> byte (variables)",
line: "SUBTRACT a FROM b GIVING result", line: "SUBTRACT a FROM b GIVING result",
setupVars: func(st *compiler.SymbolTable) { setupVars: func(st *compiler.SymbolTable) {
st.AddVar("a", "", compiler.KindByte, 10, "test.c65", 1) st.AddVar("a", "", compiler.KindByte, 10)
st.AddVar("b", "", compiler.KindByte, 20, "test.c65", 1) st.AddVar("b", "", compiler.KindByte, 20)
st.AddVar("result", "", compiler.KindByte, 0, "test.c65", 1) st.AddVar("result", "", compiler.KindByte, 0)
}, },
wantAsm: []string{ wantAsm: []string{
"\tsec", "\tsec",
@ -76,9 +76,9 @@ func TestSubtractCommand_OldSyntax_FROM(t *testing.T) {
name: "SUBT a FROM b -> c with arrow", name: "SUBT a FROM b -> c with arrow",
line: "SUBT a FROM b -> result", line: "SUBT a FROM b -> result",
setupVars: func(st *compiler.SymbolTable) { setupVars: func(st *compiler.SymbolTable) {
st.AddVar("a", "", compiler.KindByte, 10, "test.c65", 1) st.AddVar("a", "", compiler.KindByte, 10)
st.AddVar("b", "", compiler.KindByte, 20, "test.c65", 1) st.AddVar("b", "", compiler.KindByte, 20)
st.AddVar("result", "", compiler.KindByte, 0, "test.c65", 1) st.AddVar("result", "", compiler.KindByte, 0)
}, },
wantAsm: []string{ wantAsm: []string{
"\tsec", "\tsec",
@ -91,8 +91,8 @@ func TestSubtractCommand_OldSyntax_FROM(t *testing.T) {
name: "SUBTRACT literal FROM variable", name: "SUBTRACT literal FROM variable",
line: "SUBTRACT 10 FROM a GIVING result", line: "SUBTRACT 10 FROM a GIVING result",
setupVars: func(st *compiler.SymbolTable) { setupVars: func(st *compiler.SymbolTable) {
st.AddVar("a", "", compiler.KindByte, 20, "test.c65", 1) st.AddVar("a", "", compiler.KindByte, 20)
st.AddVar("result", "", compiler.KindByte, 0, "test.c65", 1) st.AddVar("result", "", compiler.KindByte, 0)
}, },
wantAsm: []string{ wantAsm: []string{
"\tsec", "\tsec",
@ -105,9 +105,9 @@ func TestSubtractCommand_OldSyntax_FROM(t *testing.T) {
name: "word FROM word -> word", name: "word FROM word -> word",
line: "SUBTRACT x FROM y GIVING result", line: "SUBTRACT x FROM y GIVING result",
setupVars: func(st *compiler.SymbolTable) { setupVars: func(st *compiler.SymbolTable) {
st.AddVar("x", "", compiler.KindWord, 0x1000, "test.c65", 1) st.AddVar("x", "", compiler.KindWord, 0x1000)
st.AddVar("y", "", compiler.KindWord, 0x2000, "test.c65", 1) st.AddVar("y", "", compiler.KindWord, 0x2000)
st.AddVar("result", "", compiler.KindWord, 0, "test.c65", 1) st.AddVar("result", "", compiler.KindWord, 0)
}, },
wantAsm: []string{ wantAsm: []string{
"\tsec", "\tsec",
@ -166,7 +166,7 @@ func TestSubtractCommand_OldSyntax_Minus(t *testing.T) {
name: "SUBT a - b (means a-b, no swap)", name: "SUBT a - b (means a-b, no swap)",
line: "SUBT 10 - 5 GIVING result", line: "SUBT 10 - 5 GIVING result",
setupVars: func(st *compiler.SymbolTable) { setupVars: func(st *compiler.SymbolTable) {
st.AddVar("result", "", compiler.KindByte, 0, "test.c65", 1) st.AddVar("result", "", compiler.KindByte, 0)
}, },
wantAsm: []string{ wantAsm: []string{
"\tlda #$05", "\tlda #$05",
@ -177,9 +177,9 @@ func TestSubtractCommand_OldSyntax_Minus(t *testing.T) {
name: "SUBT byte - byte -> byte (variables)", name: "SUBT byte - byte -> byte (variables)",
line: "SUBT a - b -> result", line: "SUBT a - b -> result",
setupVars: func(st *compiler.SymbolTable) { setupVars: func(st *compiler.SymbolTable) {
st.AddVar("a", "", compiler.KindByte, 20, "test.c65", 1) st.AddVar("a", "", compiler.KindByte, 20)
st.AddVar("b", "", compiler.KindByte, 10, "test.c65", 1) st.AddVar("b", "", compiler.KindByte, 10)
st.AddVar("result", "", compiler.KindByte, 0, "test.c65", 1) st.AddVar("result", "", compiler.KindByte, 0)
}, },
wantAsm: []string{ wantAsm: []string{
"\tsec", "\tsec",
@ -192,9 +192,9 @@ func TestSubtractCommand_OldSyntax_Minus(t *testing.T) {
name: "SUBTRACT a - b GIVING c", name: "SUBTRACT a - b GIVING c",
line: "SUBTRACT a - b GIVING result", line: "SUBTRACT a - b GIVING result",
setupVars: func(st *compiler.SymbolTable) { setupVars: func(st *compiler.SymbolTable) {
st.AddVar("a", "", compiler.KindByte, 20, "test.c65", 1) st.AddVar("a", "", compiler.KindByte, 20)
st.AddVar("b", "", compiler.KindByte, 10, "test.c65", 1) st.AddVar("b", "", compiler.KindByte, 10)
st.AddVar("result", "", compiler.KindByte, 0, "test.c65", 1) st.AddVar("result", "", compiler.KindByte, 0)
}, },
wantAsm: []string{ wantAsm: []string{
"\tsec", "\tsec",
@ -207,8 +207,8 @@ func TestSubtractCommand_OldSyntax_Minus(t *testing.T) {
name: "SUBT variable - literal", name: "SUBT variable - literal",
line: "SUBT a - 10 -> result", line: "SUBT a - 10 -> result",
setupVars: func(st *compiler.SymbolTable) { setupVars: func(st *compiler.SymbolTable) {
st.AddVar("a", "", compiler.KindByte, 20, "test.c65", 1) st.AddVar("a", "", compiler.KindByte, 20)
st.AddVar("result", "", compiler.KindByte, 0, "test.c65", 1) st.AddVar("result", "", compiler.KindByte, 0)
}, },
wantAsm: []string{ wantAsm: []string{
"\tsec", "\tsec",
@ -221,9 +221,9 @@ func TestSubtractCommand_OldSyntax_Minus(t *testing.T) {
name: "word - word -> word", name: "word - word -> word",
line: "SUBT x - y -> result", line: "SUBT x - y -> result",
setupVars: func(st *compiler.SymbolTable) { setupVars: func(st *compiler.SymbolTable) {
st.AddVar("x", "", compiler.KindWord, 0x2000, "test.c65", 1) st.AddVar("x", "", compiler.KindWord, 0x2000)
st.AddVar("y", "", compiler.KindWord, 0x1000, "test.c65", 1) st.AddVar("y", "", compiler.KindWord, 0x1000)
st.AddVar("result", "", compiler.KindWord, 0, "test.c65", 1) st.AddVar("result", "", compiler.KindWord, 0)
}, },
wantAsm: []string{ wantAsm: []string{
"\tsec", "\tsec",
@ -239,9 +239,9 @@ func TestSubtractCommand_OldSyntax_Minus(t *testing.T) {
name: "byte - byte -> word", name: "byte - byte -> word",
line: "SUBT a - b GIVING result", line: "SUBT a - b GIVING result",
setupVars: func(st *compiler.SymbolTable) { setupVars: func(st *compiler.SymbolTable) {
st.AddVar("a", "", compiler.KindByte, 20, "test.c65", 1) st.AddVar("a", "", compiler.KindByte, 20)
st.AddVar("b", "", compiler.KindByte, 10, "test.c65", 1) st.AddVar("b", "", compiler.KindByte, 10)
st.AddVar("result", "", compiler.KindWord, 0, "test.c65", 1) st.AddVar("result", "", compiler.KindWord, 0)
}, },
wantAsm: []string{ wantAsm: []string{
"\tsec", "\tsec",
@ -300,9 +300,9 @@ func TestSubtractCommand_NewSyntax(t *testing.T) {
name: "byte - byte -> byte", name: "byte - byte -> byte",
line: "result = a - b", line: "result = a - b",
setupVars: func(st *compiler.SymbolTable) { setupVars: func(st *compiler.SymbolTable) {
st.AddVar("a", "", compiler.KindByte, 20, "test.c65", 1) st.AddVar("a", "", compiler.KindByte, 20)
st.AddVar("b", "", compiler.KindByte, 10, "test.c65", 1) st.AddVar("b", "", compiler.KindByte, 10)
st.AddVar("result", "", compiler.KindByte, 0, "test.c65", 1) st.AddVar("result", "", compiler.KindByte, 0)
}, },
wantAsm: []string{ wantAsm: []string{
"\tsec", "\tsec",
@ -315,9 +315,9 @@ func TestSubtractCommand_NewSyntax(t *testing.T) {
name: "byte - byte -> word", name: "byte - byte -> word",
line: "result = a - b", line: "result = a - b",
setupVars: func(st *compiler.SymbolTable) { setupVars: func(st *compiler.SymbolTable) {
st.AddVar("a", "", compiler.KindByte, 20, "test.c65", 1) st.AddVar("a", "", compiler.KindByte, 20)
st.AddVar("b", "", compiler.KindByte, 10, "test.c65", 1) st.AddVar("b", "", compiler.KindByte, 10)
st.AddVar("result", "", compiler.KindWord, 0, "test.c65", 1) st.AddVar("result", "", compiler.KindWord, 0)
}, },
wantAsm: []string{ wantAsm: []string{
"\tsec", "\tsec",
@ -333,9 +333,9 @@ func TestSubtractCommand_NewSyntax(t *testing.T) {
name: "word - word -> word", name: "word - word -> word",
line: "result = x - y", line: "result = x - y",
setupVars: func(st *compiler.SymbolTable) { setupVars: func(st *compiler.SymbolTable) {
st.AddVar("x", "", compiler.KindWord, 0x2000, "test.c65", 1) st.AddVar("x", "", compiler.KindWord, 0x2000)
st.AddVar("y", "", compiler.KindWord, 0x1000, "test.c65", 1) st.AddVar("y", "", compiler.KindWord, 0x1000)
st.AddVar("result", "", compiler.KindWord, 0, "test.c65", 1) st.AddVar("result", "", compiler.KindWord, 0)
}, },
wantAsm: []string{ wantAsm: []string{
"\tsec", "\tsec",
@ -351,8 +351,8 @@ func TestSubtractCommand_NewSyntax(t *testing.T) {
name: "variable - literal", name: "variable - literal",
line: "result = a - 10", line: "result = a - 10",
setupVars: func(st *compiler.SymbolTable) { setupVars: func(st *compiler.SymbolTable) {
st.AddVar("a", "", compiler.KindByte, 20, "test.c65", 1) st.AddVar("a", "", compiler.KindByte, 20)
st.AddVar("result", "", compiler.KindByte, 0, "test.c65", 1) st.AddVar("result", "", compiler.KindByte, 0)
}, },
wantAsm: []string{ wantAsm: []string{
"\tsec", "\tsec",
@ -365,8 +365,8 @@ func TestSubtractCommand_NewSyntax(t *testing.T) {
name: "literal - variable", name: "literal - variable",
line: "result = 20 - b", line: "result = 20 - b",
setupVars: func(st *compiler.SymbolTable) { setupVars: func(st *compiler.SymbolTable) {
st.AddVar("b", "", compiler.KindByte, 10, "test.c65", 1) st.AddVar("b", "", compiler.KindByte, 10)
st.AddVar("result", "", compiler.KindByte, 0, "test.c65", 1) st.AddVar("result", "", compiler.KindByte, 0)
}, },
wantAsm: []string{ wantAsm: []string{
"\tsec", "\tsec",
@ -379,7 +379,7 @@ func TestSubtractCommand_NewSyntax(t *testing.T) {
name: "constant folding", name: "constant folding",
line: "result = 100 - 25", line: "result = 100 - 25",
setupVars: func(st *compiler.SymbolTable) { setupVars: func(st *compiler.SymbolTable) {
st.AddVar("result", "", compiler.KindByte, 0, "test.c65", 1) st.AddVar("result", "", compiler.KindByte, 0)
}, },
wantAsm: []string{ wantAsm: []string{
"\tlda #$4b", "\tlda #$4b",
@ -390,7 +390,7 @@ func TestSubtractCommand_NewSyntax(t *testing.T) {
name: "constant folding word", name: "constant folding word",
line: "result = $2000 - $1000", line: "result = $2000 - $1000",
setupVars: func(st *compiler.SymbolTable) { setupVars: func(st *compiler.SymbolTable) {
st.AddVar("result", "", compiler.KindWord, 0, "test.c65", 1) st.AddVar("result", "", compiler.KindWord, 0)
}, },
wantAsm: []string{ wantAsm: []string{
"\tlda #$00", "\tlda #$00",
@ -403,9 +403,9 @@ func TestSubtractCommand_NewSyntax(t *testing.T) {
name: "using constant in expression", name: "using constant in expression",
line: "result = a - OFFSET", line: "result = a - OFFSET",
setupVars: func(st *compiler.SymbolTable) { setupVars: func(st *compiler.SymbolTable) {
st.AddVar("a", "", compiler.KindByte, 100, "test.c65", 1) st.AddVar("a", "", compiler.KindByte, 100)
st.AddConst("OFFSET", "", compiler.KindByte, 10, "test.c65", 1) st.AddConst("OFFSET", "", compiler.KindByte, 10)
st.AddVar("result", "", compiler.KindByte, 0, "test.c65", 1) st.AddVar("result", "", compiler.KindByte, 0)
}, },
wantAsm: []string{ wantAsm: []string{
"\tsec", "\tsec",
@ -418,9 +418,9 @@ func TestSubtractCommand_NewSyntax(t *testing.T) {
name: "word - byte -> word", name: "word - byte -> word",
line: "result = wval - bval", line: "result = wval - bval",
setupVars: func(st *compiler.SymbolTable) { setupVars: func(st *compiler.SymbolTable) {
st.AddVar("wval", "", compiler.KindWord, 0x1234, "test.c65", 1) st.AddVar("wval", "", compiler.KindWord, 0x1234)
st.AddVar("bval", "", compiler.KindByte, 0x10, "test.c65", 1) st.AddVar("bval", "", compiler.KindByte, 0x10)
st.AddVar("result", "", compiler.KindWord, 0, "test.c65", 1) st.AddVar("result", "", compiler.KindWord, 0)
}, },
wantAsm: []string{ wantAsm: []string{
"\tsec", "\tsec",
@ -436,8 +436,8 @@ func TestSubtractCommand_NewSyntax(t *testing.T) {
name: "error: unknown destination", name: "error: unknown destination",
line: "unknown = a - b", line: "unknown = a - b",
setupVars: func(st *compiler.SymbolTable) { setupVars: func(st *compiler.SymbolTable) {
st.AddVar("a", "", compiler.KindByte, 0, "test.c65", 1) st.AddVar("a", "", compiler.KindByte, 0)
st.AddVar("b", "", compiler.KindByte, 0, "test.c65", 1) st.AddVar("b", "", compiler.KindByte, 0)
}, },
wantErr: true, wantErr: true,
}, },
@ -445,9 +445,9 @@ func TestSubtractCommand_NewSyntax(t *testing.T) {
name: "error: wrong operator", name: "error: wrong operator",
line: "result = a + b", line: "result = a + b",
setupVars: func(st *compiler.SymbolTable) { setupVars: func(st *compiler.SymbolTable) {
st.AddVar("a", "", compiler.KindByte, 0, "test.c65", 1) st.AddVar("a", "", compiler.KindByte, 0)
st.AddVar("b", "", compiler.KindByte, 0, "test.c65", 1) st.AddVar("b", "", compiler.KindByte, 0)
st.AddVar("result", "", compiler.KindByte, 0, "test.c65", 1) st.AddVar("result", "", compiler.KindByte, 0)
}, },
wantErr: true, wantErr: true,
}, },
@ -455,9 +455,9 @@ func TestSubtractCommand_NewSyntax(t *testing.T) {
name: "error: cannot assign to constant", name: "error: cannot assign to constant",
line: "MAXVAL = a - b", line: "MAXVAL = a - b",
setupVars: func(st *compiler.SymbolTable) { setupVars: func(st *compiler.SymbolTable) {
st.AddVar("a", "", compiler.KindByte, 0, "test.c65", 1) st.AddVar("a", "", compiler.KindByte, 0)
st.AddVar("b", "", compiler.KindByte, 0, "test.c65", 1) st.AddVar("b", "", compiler.KindByte, 0)
st.AddConst("MAXVAL", "", compiler.KindByte, 255, "test.c65", 1) st.AddConst("MAXVAL", "", compiler.KindByte, 255)
}, },
wantErr: true, wantErr: true,
}, },

View file

@ -33,7 +33,13 @@ func (c *SwitchCommand) Interpret(line preproc.Line, ctx *compiler.CompilerConte
} }
scope := ctx.CurrentScope() scope := ctx.CurrentScope()
constLookup := ctx.SymbolTable.ConstantLookupFunc(scope) 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
}
// Parse the switch variable/expression // Parse the switch variable/expression
varName, varKind, value, isVar, err := compiler.ParseOperandParam( 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", name: "byte var with byte literal case",
setupVars: func(st *compiler.SymbolTable) { setupVars: func(st *compiler.SymbolTable) {
st.AddVar("x", "", compiler.KindByte, 0, "test.c65", 1) st.AddVar("x", "", compiler.KindByte, 0)
}, },
caseValue: "10", caseValue: "10",
wantSwitch: []string{}, wantSwitch: []string{},
@ -96,7 +96,7 @@ func TestSwitchBasicByte(t *testing.T) {
func TestSwitchMultipleCases(t *testing.T) { func TestSwitchMultipleCases(t *testing.T) {
pragma := preproc.NewPragma() pragma := preproc.NewPragma()
ctx := compiler.NewCompilerContext(pragma) ctx := compiler.NewCompilerContext(pragma)
ctx.SymbolTable.AddVar("x", "", compiler.KindByte, 0, "test.c65", 1) ctx.SymbolTable.AddVar("x", "", compiler.KindByte, 0)
pragmaIdx := pragma.GetCurrentPragmaSetIndex() pragmaIdx := pragma.GetCurrentPragmaSetIndex()
@ -157,7 +157,7 @@ func TestSwitchMultipleCases(t *testing.T) {
func TestSwitchWithDefault(t *testing.T) { func TestSwitchWithDefault(t *testing.T) {
pragma := preproc.NewPragma() pragma := preproc.NewPragma()
ctx := compiler.NewCompilerContext(pragma) ctx := compiler.NewCompilerContext(pragma)
ctx.SymbolTable.AddVar("x", "", compiler.KindByte, 0, "test.c65", 1) ctx.SymbolTable.AddVar("x", "", compiler.KindByte, 0)
pragmaIdx := pragma.GetCurrentPragmaSetIndex() pragmaIdx := pragma.GetCurrentPragmaSetIndex()
@ -198,7 +198,7 @@ func TestSwitchWithDefault(t *testing.T) {
func TestSwitchCaseAfterDefault(t *testing.T) { func TestSwitchCaseAfterDefault(t *testing.T) {
pragma := preproc.NewPragma() pragma := preproc.NewPragma()
ctx := compiler.NewCompilerContext(pragma) ctx := compiler.NewCompilerContext(pragma)
ctx.SymbolTable.AddVar("x", "", compiler.KindByte, 0, "test.c65", 1) ctx.SymbolTable.AddVar("x", "", compiler.KindByte, 0)
pragmaIdx := pragma.GetCurrentPragmaSetIndex() pragmaIdx := pragma.GetCurrentPragmaSetIndex()
@ -234,7 +234,7 @@ func TestSwitchCaseAfterDefault(t *testing.T) {
func TestSwitchMultipleDefaults(t *testing.T) { func TestSwitchMultipleDefaults(t *testing.T) {
pragma := preproc.NewPragma() pragma := preproc.NewPragma()
ctx := compiler.NewCompilerContext(pragma) ctx := compiler.NewCompilerContext(pragma)
ctx.SymbolTable.AddVar("x", "", compiler.KindByte, 0, "test.c65", 1) ctx.SymbolTable.AddVar("x", "", compiler.KindByte, 0)
pragmaIdx := pragma.GetCurrentPragmaSetIndex() pragmaIdx := pragma.GetCurrentPragmaSetIndex()
@ -333,7 +333,7 @@ func TestSwitchWrongParamCount(t *testing.T) {
func TestCaseWrongParamCount(t *testing.T) { func TestCaseWrongParamCount(t *testing.T) {
pragma := preproc.NewPragma() pragma := preproc.NewPragma()
ctx := compiler.NewCompilerContext(pragma) ctx := compiler.NewCompilerContext(pragma)
ctx.SymbolTable.AddVar("x", "", compiler.KindByte, 0, "test.c65", 1) ctx.SymbolTable.AddVar("x", "", compiler.KindByte, 0)
pragmaIdx := pragma.GetCurrentPragmaSetIndex() pragmaIdx := pragma.GetCurrentPragmaSetIndex()
@ -358,7 +358,7 @@ func TestCaseWrongParamCount(t *testing.T) {
func TestDefaultWrongParamCount(t *testing.T) { func TestDefaultWrongParamCount(t *testing.T) {
pragma := preproc.NewPragma() pragma := preproc.NewPragma()
ctx := compiler.NewCompilerContext(pragma) ctx := compiler.NewCompilerContext(pragma)
ctx.SymbolTable.AddVar("x", "", compiler.KindByte, 0, "test.c65", 1) ctx.SymbolTable.AddVar("x", "", compiler.KindByte, 0)
pragmaIdx := pragma.GetCurrentPragmaSetIndex() pragmaIdx := pragma.GetCurrentPragmaSetIndex()
@ -376,7 +376,7 @@ func TestDefaultWrongParamCount(t *testing.T) {
func TestEndswitchWrongParamCount(t *testing.T) { func TestEndswitchWrongParamCount(t *testing.T) {
pragma := preproc.NewPragma() pragma := preproc.NewPragma()
ctx := compiler.NewCompilerContext(pragma) ctx := compiler.NewCompilerContext(pragma)
ctx.SymbolTable.AddVar("x", "", compiler.KindByte, 0, "test.c65", 1) ctx.SymbolTable.AddVar("x", "", compiler.KindByte, 0)
pragmaIdx := pragma.GetCurrentPragmaSetIndex() pragmaIdx := pragma.GetCurrentPragmaSetIndex()
@ -394,7 +394,7 @@ func TestEndswitchWrongParamCount(t *testing.T) {
func TestSwitchWordType(t *testing.T) { func TestSwitchWordType(t *testing.T) {
pragma := preproc.NewPragma() pragma := preproc.NewPragma()
ctx := compiler.NewCompilerContext(pragma) ctx := compiler.NewCompilerContext(pragma)
ctx.SymbolTable.AddVar("big_val", "", compiler.KindWord, 0, "test.c65", 1) ctx.SymbolTable.AddVar("big_val", "", compiler.KindWord, 0)
pragmaIdx := pragma.GetCurrentPragmaSetIndex() pragmaIdx := pragma.GetCurrentPragmaSetIndex()
@ -434,8 +434,8 @@ func TestSwitchWordType(t *testing.T) {
func TestSwitchWithConstant(t *testing.T) { func TestSwitchWithConstant(t *testing.T) {
pragma := preproc.NewPragma() pragma := preproc.NewPragma()
ctx := compiler.NewCompilerContext(pragma) ctx := compiler.NewCompilerContext(pragma)
ctx.SymbolTable.AddConst("MAX_VAL", "", compiler.KindByte, 100, "test.c65", 1) ctx.SymbolTable.AddConst("MAX_VAL", "", compiler.KindByte, 100)
ctx.SymbolTable.AddVar("x", "", compiler.KindByte, 0, "test.c65", 1) ctx.SymbolTable.AddVar("x", "", compiler.KindByte, 0)
pragmaIdx := pragma.GetCurrentPragmaSetIndex() pragmaIdx := pragma.GetCurrentPragmaSetIndex()
@ -475,8 +475,8 @@ func TestSwitchWithConstant(t *testing.T) {
func TestSwitchNested(t *testing.T) { func TestSwitchNested(t *testing.T) {
pragma := preproc.NewPragma() pragma := preproc.NewPragma()
ctx := compiler.NewCompilerContext(pragma) ctx := compiler.NewCompilerContext(pragma)
ctx.SymbolTable.AddVar("outer", "", compiler.KindByte, 0, "test.c65", 1) ctx.SymbolTable.AddVar("outer", "", compiler.KindByte, 0)
ctx.SymbolTable.AddVar("inner", "", compiler.KindByte, 0, "test.c65", 1) ctx.SymbolTable.AddVar("inner", "", compiler.KindByte, 0)
pragmaIdx := pragma.GetCurrentPragmaSetIndex() pragmaIdx := pragma.GetCurrentPragmaSetIndex()
@ -546,7 +546,7 @@ func TestSwitchLongJump(t *testing.T) {
pragma := preproc.NewPragma() pragma := preproc.NewPragma()
pragma.AddPragma("_P_USE_LONG_JUMP", "1") pragma.AddPragma("_P_USE_LONG_JUMP", "1")
ctx := compiler.NewCompilerContext(pragma) ctx := compiler.NewCompilerContext(pragma)
ctx.SymbolTable.AddVar("x", "", compiler.KindByte, 0, "test.c65", 1) ctx.SymbolTable.AddVar("x", "", compiler.KindByte, 0)
pragmaIdx := pragma.GetCurrentPragmaSetIndex() pragmaIdx := pragma.GetCurrentPragmaSetIndex()
@ -600,7 +600,7 @@ func TestSwitchLongJump(t *testing.T) {
// Verify the pattern is different from normal mode // Verify the pattern is different from normal mode
pragmaNormal := preproc.NewPragma() pragmaNormal := preproc.NewPragma()
ctxNormal := compiler.NewCompilerContext(pragmaNormal) ctxNormal := compiler.NewCompilerContext(pragmaNormal)
ctxNormal.SymbolTable.AddVar("x", "", compiler.KindByte, 0, "test.c65", 1) ctxNormal.SymbolTable.AddVar("x", "", compiler.KindByte, 0)
pragmaIdxNormal := pragmaNormal.GetCurrentPragmaSetIndex() pragmaIdxNormal := pragmaNormal.GetCurrentPragmaSetIndex()
@ -629,7 +629,7 @@ func TestSwitchLongJump(t *testing.T) {
func TestSwitchOnConstant(t *testing.T) { func TestSwitchOnConstant(t *testing.T) {
pragma := preproc.NewPragma() pragma := preproc.NewPragma()
ctx := compiler.NewCompilerContext(pragma) ctx := compiler.NewCompilerContext(pragma)
ctx.SymbolTable.AddConst("VALUE", "", compiler.KindByte, 5, "test.c65", 1) ctx.SymbolTable.AddConst("VALUE", "", compiler.KindByte, 5)
pragmaIdx := pragma.GetCurrentPragmaSetIndex() pragmaIdx := pragma.GetCurrentPragmaSetIndex()
@ -688,7 +688,7 @@ func TestSwitchOnConstant(t *testing.T) {
func TestSwitchEmptyWithOnlyDefault(t *testing.T) { func TestSwitchEmptyWithOnlyDefault(t *testing.T) {
pragma := preproc.NewPragma() pragma := preproc.NewPragma()
ctx := compiler.NewCompilerContext(pragma) ctx := compiler.NewCompilerContext(pragma)
ctx.SymbolTable.AddVar("x", "", compiler.KindByte, 0, "test.c65", 1) ctx.SymbolTable.AddVar("x", "", compiler.KindByte, 0)
pragmaIdx := pragma.GetCurrentPragmaSetIndex() pragmaIdx := pragma.GetCurrentPragmaSetIndex()
@ -752,7 +752,7 @@ func TestSwitchComparisonTypes(t *testing.T) {
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
pragma := preproc.NewPragma() pragma := preproc.NewPragma()
ctx := compiler.NewCompilerContext(pragma) ctx := compiler.NewCompilerContext(pragma)
ctx.SymbolTable.AddVar("x", "", tt.varType, 0, "test.c65", 1) ctx.SymbolTable.AddVar("x", "", tt.varType, 0)
pragmaIdx := pragma.GetCurrentPragmaSetIndex() pragmaIdx := pragma.GetCurrentPragmaSetIndex()
@ -801,8 +801,8 @@ func TestSwitchWithVariableCase(t *testing.T) {
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
pragma := preproc.NewPragma() pragma := preproc.NewPragma()
ctx := compiler.NewCompilerContext(pragma) ctx := compiler.NewCompilerContext(pragma)
ctx.SymbolTable.AddVar("switch_var", "", tt.switchType, 0, "test.c65", 1) ctx.SymbolTable.AddVar("switch_var", "", tt.switchType, 0)
ctx.SymbolTable.AddVar("case_var", "", tt.caseType, 0, "test.c65", 1) ctx.SymbolTable.AddVar("case_var", "", tt.caseType, 0)
pragmaIdx := pragma.GetCurrentPragmaSetIndex() pragmaIdx := pragma.GetCurrentPragmaSetIndex()
@ -859,7 +859,7 @@ func TestSwitchByteRangeValidation(t *testing.T) {
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
pragma := preproc.NewPragma() pragma := preproc.NewPragma()
ctx := compiler.NewCompilerContext(pragma) ctx := compiler.NewCompilerContext(pragma)
ctx.SymbolTable.AddVar("x", "", compiler.KindByte, 0, "test.c65", 1) ctx.SymbolTable.AddVar("x", "", compiler.KindByte, 0)
pragmaIdx := pragma.GetCurrentPragmaSetIndex() pragmaIdx := pragma.GetCurrentPragmaSetIndex()

View file

@ -41,7 +41,13 @@ func (c *WhileCommand) Interpret(line preproc.Line, ctx *compiler.CompilerContex
} }
scope := ctx.CurrentScope() scope := ctx.CurrentScope()
constLookup := ctx.SymbolTable.ConstantLookupFunc(scope) 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
}
// Parse param1 // Parse param1
varName, varKind, value, isVar, err := compiler.ParseOperandParam( varName, varKind, value, isVar, err := compiler.ParseOperandParam(

View file

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

View file

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

View file

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

View file

@ -68,7 +68,13 @@ func (c *XorCommand) Interpret(line preproc.Line, ctx *compiler.CompilerContext)
scope := ctx.CurrentScope() scope := ctx.CurrentScope()
// Create constant lookup function // Create constant lookup function
constLookup := ctx.SymbolTable.ConstantLookupFunc(scope) 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
}
// Determine syntax and parse accordingly // Determine syntax and parse accordingly
if strings.ToUpper(params[0]) == "XOR" { if strings.ToUpper(params[0]) == "XOR" {

View file

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

View file

@ -222,12 +222,6 @@ func (c *Compiler) Compile(lines []preproc.Line) ([]string, error) {
// Analyze for overlapping absolute addresses in function call chains // Analyze for overlapping absolute addresses in function call chains
c.checkAbsoluteOverlaps() 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 // Assemble final output with headers and footers
return c.assembleOutput(codeOutput), nil return c.assembleOutput(codeOutput), nil
} }

View file

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

View file

@ -124,14 +124,14 @@ func (fh *FunctionHandler) HandleFuncDecl(line preproc.Line) ([]string, error) {
if isImplicit { if isImplicit {
// Parse and add implicit variable declaration // Parse and add implicit variable declaration
// Format: {BYTE varname} or {WORD varname} // Format: {BYTE varname} or {WORD varname}
if err := fh.parseImplicitDecl(implicitDecl, funcName, line.Filename, line.LineNo); err != nil { if err := fh.parseImplicitDecl(implicitDecl, funcName); err != nil {
fh.currentFuncs = fh.currentFuncs[:len(fh.currentFuncs)-1] 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) return nil, fmt.Errorf("%s:%d: FUNC %s: implicit declaration: %w", line.Filename, line.LineNo, funcName, err)
} }
} }
// Look up variable in symbol table (validation only, not usage) // Look up variable in symbol table
sym := fh.symTable.LookupWithoutUsage(varName, []string{funcName}) sym := fh.symTable.Lookup(varName, []string{funcName})
if sym == nil { if sym == nil {
fh.currentFuncs = fh.currentFuncs[:len(fh.currentFuncs)-1] 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) return nil, fmt.Errorf("%s:%d: FUNC %s: parameter %q not declared", line.Filename, line.LineNo, funcName, varName)
@ -343,7 +343,12 @@ func (fh *FunctionHandler) HandleFuncCall(line preproc.Line) ([]string, error) {
} }
// Use ParseOperandParam for variables, constants, and expressions // Use ParseOperandParam for variables, constants, and expressions
constLookup := fh.symTable.ConstantLookupFunc(fh.currentFuncs) 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
}
_, _, value, isVar, err := ParseOperandParam( _, _, value, isVar, err := ParseOperandParam(
arg, fh.symTable, fh.currentFuncs, constLookup) arg, fh.symTable, fh.currentFuncs, constLookup)
@ -594,7 +599,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 // parseImplicitDecl parses {BYTE varname} or {WORD varname} or {BYTE varname @ address} and adds to symbol table
func (fh *FunctionHandler) parseImplicitDecl(decl string, funcName string, filename string, lineNo int) error { func (fh *FunctionHandler) parseImplicitDecl(decl string, funcName string) error {
parts := strings.Fields(decl) parts := strings.Fields(decl)
if len(parts) != 2 && len(parts) != 4 { if len(parts) != 2 && len(parts) != 4 {
return fmt.Errorf("implicit declaration must be 'TYPE name' or 'TYPE name @ addr', got: %q", decl) return fmt.Errorf("implicit declaration must be 'TYPE name' or 'TYPE name @ addr', got: %q", decl)
@ -615,7 +620,7 @@ func (fh *FunctionHandler) parseImplicitDecl(decl string, funcName string, filen
if len(parts) == 2 { if len(parts) == 2 {
// Simple: BYTE name or WORD name // Simple: BYTE name or WORD name
return fh.symTable.AddVar(varName, funcName, kind, 0, filename, lineNo) return fh.symTable.AddVar(varName, funcName, kind, 0)
} }
// Extended: BYTE name @ address or WORD name @ address // Extended: BYTE name @ address or WORD name @ address
@ -627,7 +632,13 @@ func (fh *FunctionHandler) parseImplicitDecl(decl string, funcName string, filen
} }
// Create constant lookup function for address evaluation // Create constant lookup function for address evaluation
constLookup := fh.symTable.ConstantLookupFunc([]string{funcName}) 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
}
// Parse address (supports $hex and decimal) using EvaluateExpression // Parse address (supports $hex and decimal) using EvaluateExpression
addr, err := utils.EvaluateExpression(addrStr, constLookup) addr, err := utils.EvaluateExpression(addrStr, constLookup)
@ -639,7 +650,7 @@ func (fh *FunctionHandler) parseImplicitDecl(decl string, funcName string, filen
return fmt.Errorf("absolute address $%X out of range", addr) return fmt.Errorf("absolute address $%X out of range", addr)
} }
return fh.symTable.AddAbsolute(varName, funcName, kind, uint16(addr), filename, lineNo) return fh.symTable.AddAbsolute(varName, funcName, kind, uint16(addr))
} }
// EndFunction pops all functions from the stack (called by FEND) // 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) fh := NewFunctionHandler(st, ls, csh, pragma)
// Pre-declare parameters // Pre-declare parameters
st.AddVar("x", "test_func", KindByte, 0, "test.c65", 1) st.AddVar("x", "test_func", KindByte, 0)
st.AddVar("y", "test_func", KindWord, 0, "test.c65", 1) st.AddVar("y", "test_func", KindWord, 0)
asm, err := fh.HandleFuncDecl(makeLine("FUNC test_func ( x y )")) asm, err := fh.HandleFuncDecl(makeLine("FUNC test_func ( x y )"))
if err != nil { if err != nil {
@ -340,7 +340,7 @@ func TestHandleFuncDecl_Errors(t *testing.T) {
name: "const param", name: "const param",
line: "FUNC test ( constval )", line: "FUNC test ( constval )",
preDecl: func(st *SymbolTable) { preDecl: func(st *SymbolTable) {
st.AddConst("constval", "test", KindByte, 42, "test.c65", 1) st.AddConst("constval", "test", KindByte, 42)
}, },
wantErr: "cannot be a constant", wantErr: "cannot be a constant",
}, },
@ -387,13 +387,13 @@ func TestHandleFuncCall_VarArgs(t *testing.T) {
fh := NewFunctionHandler(st, ls, csh, pragma) fh := NewFunctionHandler(st, ls, csh, pragma)
// Declare function with params // Declare function with params
st.AddVar("param_a", "test_func", KindByte, 0, "test.c65", 1) st.AddVar("param_a", "test_func", KindByte, 0)
st.AddVar("param_b", "test_func", KindWord, 0, "test.c65", 1) st.AddVar("param_b", "test_func", KindWord, 0)
fh.HandleFuncDecl(makeLine("FUNC test_func ( param_a param_b )")) fh.HandleFuncDecl(makeLine("FUNC test_func ( param_a param_b )"))
// Declare caller variables // Declare caller variables
st.AddVar("var_a", "", KindByte, 0, "test.c65", 1) st.AddVar("var_a", "", KindByte, 0)
st.AddVar("var_b", "", KindWord, 0, "test.c65", 1) st.AddVar("var_b", "", KindWord, 0)
asm, err := fh.HandleFuncCall(makeLine("CALL test_func ( var_a var_b )")) asm, err := fh.HandleFuncCall(makeLine("CALL test_func ( var_a var_b )"))
if err != nil { if err != nil {
@ -430,11 +430,11 @@ func TestHandleFuncCall_OutParams(t *testing.T) {
fh := NewFunctionHandler(st, ls, csh, pragma) fh := NewFunctionHandler(st, ls, csh, pragma)
// Declare function with out param // Declare function with out param
st.AddVar("result", "get_result", KindByte, 0, "test.c65", 1) st.AddVar("result", "get_result", KindByte, 0)
fh.HandleFuncDecl(makeLine("FUNC get_result ( out:result )")) fh.HandleFuncDecl(makeLine("FUNC get_result ( out:result )"))
// Declare caller variable // Declare caller variable
st.AddVar("output", "", KindByte, 0, "test.c65", 1) st.AddVar("output", "", KindByte, 0)
asm, err := fh.HandleFuncCall(makeLine("CALL get_result ( output )")) asm, err := fh.HandleFuncCall(makeLine("CALL get_result ( output )"))
if err != nil { if err != nil {
@ -469,8 +469,8 @@ func TestHandleFuncCall_ConstArgs(t *testing.T) {
fh := NewFunctionHandler(st, ls, csh, pragma) fh := NewFunctionHandler(st, ls, csh, pragma)
// Declare function // Declare function
st.AddVar("x", "test_const", KindByte, 0, "test.c65", 1) st.AddVar("x", "test_const", KindByte, 0)
st.AddVar("y", "test_const", KindWord, 0, "test.c65", 1) st.AddVar("y", "test_const", KindWord, 0)
fh.HandleFuncDecl(makeLine("FUNC test_const ( x y )")) fh.HandleFuncDecl(makeLine("FUNC test_const ( x y )"))
asm, err := fh.HandleFuncCall(makeLine("CALL test_const ( 42 $1234 )")) 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) fh := NewFunctionHandler(st, ls, csh, pragma)
// Declare function // Declare function
st.AddVar("ptr", "test_label", KindWord, 0, "test.c65", 1) st.AddVar("ptr", "test_label", KindWord, 0)
fh.HandleFuncDecl(makeLine("FUNC test_label ( ptr )")) fh.HandleFuncDecl(makeLine("FUNC test_label ( ptr )"))
asm, err := fh.HandleFuncCall(makeLine("CALL test_label ( @my_label )")) 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) fh := NewFunctionHandler(st, ls, csh, pragma)
// Declare function // Declare function
st.AddVar("str_ptr", "print", KindWord, 0, "test.c65", 1) st.AddVar("str_ptr", "print", KindWord, 0)
fh.HandleFuncDecl(makeLine("FUNC print ( str_ptr )")) fh.HandleFuncDecl(makeLine("FUNC print ( str_ptr )"))
asm, err := fh.HandleFuncCall(makeLine(`CALL print ( "hello" )`)) asm, err := fh.HandleFuncCall(makeLine(`CALL print ( "hello" )`))
@ -582,7 +582,7 @@ func TestHandleFuncCall_Errors(t *testing.T) {
{ {
name: "wrong arg count", name: "wrong arg count",
setup: func(fh *FunctionHandler, st *SymbolTable) { setup: func(fh *FunctionHandler, st *SymbolTable) {
st.AddVar("x", "test", KindByte, 0, "test.c65", 1) st.AddVar("x", "test", KindByte, 0)
fh.HandleFuncDecl(makeLine("FUNC test ( x )")) fh.HandleFuncDecl(makeLine("FUNC test ( x )"))
}, },
line: "CALL test ( 1 2 )", line: "CALL test ( 1 2 )",
@ -593,9 +593,9 @@ func TestHandleFuncCall_Errors(t *testing.T) {
{ {
name: "type mismatch", name: "type mismatch",
setup: func(fh *FunctionHandler, st *SymbolTable) { setup: func(fh *FunctionHandler, st *SymbolTable) {
st.AddVar("param", "test", KindByte, 0, "test.c65", 1) st.AddVar("param", "test", KindByte, 0)
fh.HandleFuncDecl(makeLine("FUNC test ( param )")) fh.HandleFuncDecl(makeLine("FUNC test ( param )"))
st.AddVar("wvar", "", KindWord, 0, "test.c65", 1) st.AddVar("wvar", "", KindWord, 0)
}, },
line: "CALL test ( wvar )", line: "CALL test ( wvar )",
wantErr: "type mismatch", wantErr: "type mismatch",
@ -603,7 +603,7 @@ func TestHandleFuncCall_Errors(t *testing.T) {
{ {
name: "const to out param", name: "const to out param",
setup: func(fh *FunctionHandler, st *SymbolTable) { setup: func(fh *FunctionHandler, st *SymbolTable) {
st.AddVar("result", "test", KindByte, 0, "test.c65", 1) st.AddVar("result", "test", KindByte, 0)
fh.HandleFuncDecl(makeLine("FUNC test ( out:result )")) fh.HandleFuncDecl(makeLine("FUNC test ( out:result )"))
}, },
line: "CALL test ( 42 )", line: "CALL test ( 42 )",
@ -612,7 +612,7 @@ func TestHandleFuncCall_Errors(t *testing.T) {
{ {
name: "label to byte param", name: "label to byte param",
setup: func(fh *FunctionHandler, st *SymbolTable) { setup: func(fh *FunctionHandler, st *SymbolTable) {
st.AddVar("x", "test", KindByte, 0, "test.c65", 1) st.AddVar("x", "test", KindByte, 0)
fh.HandleFuncDecl(makeLine("FUNC test ( x )")) fh.HandleFuncDecl(makeLine("FUNC test ( x )"))
}, },
line: "CALL test ( @label )", 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} )")) fh.HandleFuncDecl(makeLine("FUNC test_abs ( {BYTE param_a @ $fa} {WORD param_b @ $fb} )"))
// Declare caller variables // Declare caller variables
st.AddVar("var_a", "", KindByte, 0, "test.c65", 1) st.AddVar("var_a", "", KindByte, 0)
st.AddVar("var_b", "", KindWord, 0, "test.c65", 1) st.AddVar("var_b", "", KindWord, 0)
asm, err := fh.HandleFuncCall(makeLine("CALL test_abs ( var_a var_b )")) asm, err := fh.HandleFuncCall(makeLine("CALL test_abs ( var_a var_b )"))
if err != nil { if err != nil {
@ -962,7 +962,7 @@ func TestHandleFuncCall_AbsoluteOutParams(t *testing.T) {
fh.HandleFuncDecl(makeLine("FUNC get_result ( out:{BYTE result @ $fa} )")) fh.HandleFuncDecl(makeLine("FUNC get_result ( out:{BYTE result @ $fa} )"))
// Declare caller variable // Declare caller variable
st.AddVar("output", "", KindByte, 0, "test.c65", 1) st.AddVar("output", "", KindByte, 0)
asm, err := fh.HandleFuncCall(makeLine("CALL get_result ( output )")) asm, err := fh.HandleFuncCall(makeLine("CALL get_result ( output )"))
if err != nil { if err != nil {
@ -1059,7 +1059,7 @@ func TestParseImplicitDecl_Absolute(t *testing.T) {
st = NewSymbolTable() st = NewSymbolTable()
fh.symTable = st fh.symTable = st
err := fh.parseImplicitDecl(tt.decl, tt.funcName, "test.c65", 1) err := fh.parseImplicitDecl(tt.decl, tt.funcName)
if tt.wantErr { if tt.wantErr {
if err == nil { if err == nil {
t.Fatal("expected error, got nil") t.Fatal("expected error, got nil")
@ -1167,7 +1167,7 @@ FEND
if strings.ToUpper(parts[0]) == "WORD" { if strings.ToUpper(parts[0]) == "WORD" {
varKind = KindWord varKind = KindWord
} }
st.AddVar(parts[1], "", varKind, 0, "test.c65", 1) st.AddVar(parts[1], "", varKind, 0)
} }
} else if strings.HasPrefix(lineText, "FUNC ") { } else if strings.HasPrefix(lineText, "FUNC ") {
_, err := fh.HandleFuncDecl(pline) _, err := fh.HandleFuncDecl(pline)
@ -1276,7 +1276,7 @@ FEND
addrStr := strings.TrimPrefix(parts[3], "$") addrStr := strings.TrimPrefix(parts[3], "$")
var addr uint16 var addr uint16
fmt.Sscanf(addrStr, "%x", &addr) fmt.Sscanf(addrStr, "%x", &addr)
st.AddAbsolute(parts[1], currentFunc, varKind, addr, "test.c65", 1) st.AddAbsolute(parts[1], currentFunc, varKind, addr)
} }
} else if strings.HasPrefix(lineText, "FUNC ") { } else if strings.HasPrefix(lineText, "FUNC ") {
_, err := fh.HandleFuncDecl(pline) _, err := fh.HandleFuncDecl(pline)
@ -1386,7 +1386,7 @@ FEND
if strings.ToUpper(parts[0]) == "WORD" { if strings.ToUpper(parts[0]) == "WORD" {
varKind = KindWord varKind = KindWord
} }
st.AddVar(parts[1], "", varKind, 0, "test.c65", 1) st.AddVar(parts[1], "", varKind, 0)
} }
} else if strings.HasPrefix(lineText, "FUNC ") { } else if strings.HasPrefix(lineText, "FUNC ") {
_, err := fh.HandleFuncDecl(pline) _, err := fh.HandleFuncDecl(pline)

View file

@ -177,7 +177,13 @@ func evaluateMacroArg(arg string, ctx *CompilerContext) (starlark.Value, error)
arg = strings.TrimSpace(arg) arg = strings.TrimSpace(arg)
// Create lookup function for constants // Create lookup function for constants
lookup := ctx.SymbolTable.ConstantLookupFunc(nil) 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
}
// Try to evaluate as expression (number, constant, arithmetic) // Try to evaluate as expression (number, constant, arithmetic)
val, err := utils.EvaluateExpression(arg, lookup) val, err := utils.EvaluateExpression(arg, lookup)

View file

@ -26,37 +26,14 @@ const (
FlagLabelRef FlagLabelRef
) )
// Symbol represents a variable, constant, or label reference // Symbol represents a variable or constant declaration
type Symbol struct { type Symbol struct {
Name string // Variable name Name string
Scope string // Function scope (empty for global) Scope string // empty string = global, otherwise function name
Flags SymbolFlags // Type and properties Flags SymbolFlags
Value uint16 // Initial value for variables, constant value for constants Value uint16 // init value or const value
AbsAddr uint16 // Absolute address (for @ variables) AbsAddr uint16 // if FlagAbsolute set
LabelRef string // Label reference (for label variables) LabelRef string // if FlagLabelRef set
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 // Helper methods for Symbol
@ -112,7 +89,7 @@ func (st *SymbolTable) SetFunctionHandler(fh FunctionHandlerInterface) {
} }
// AddVar adds a regular variable (byte or word) // AddVar adds a regular variable (byte or word)
func (st *SymbolTable) AddVar(name, scope string, kind VarKind, initValue uint16, filename string, lineNo int) error { func (st *SymbolTable) AddVar(name, scope string, kind VarKind, initValue uint16) error {
var flags SymbolFlags var flags SymbolFlags
switch kind { switch kind {
@ -132,13 +109,11 @@ func (st *SymbolTable) AddVar(name, scope string, kind VarKind, initValue uint16
Scope: scope, Scope: scope,
Flags: flags, Flags: flags,
Value: initValue, Value: initValue,
Filename: filename,
LineNo: lineNo,
}) })
} }
// AddConst adds a constant (byte or word) // AddConst adds a constant (byte or word)
func (st *SymbolTable) AddConst(name, scope string, kind VarKind, value uint16, filename string, lineNo int) error { func (st *SymbolTable) AddConst(name, scope string, kind VarKind, value uint16) error {
var flags SymbolFlags var flags SymbolFlags
switch kind { switch kind {
@ -158,13 +133,11 @@ func (st *SymbolTable) AddConst(name, scope string, kind VarKind, value uint16,
Scope: scope, Scope: scope,
Flags: flags, Flags: flags,
Value: value, Value: value,
Filename: filename,
LineNo: lineNo,
}) })
} }
// AddAbsolute adds a variable at a fixed memory address // AddAbsolute adds a variable at a fixed memory address
func (st *SymbolTable) AddAbsolute(name, scope string, kind VarKind, addr uint16, filename string, lineNo int) error { func (st *SymbolTable) AddAbsolute(name, scope string, kind VarKind, addr uint16) error {
if addr > 0xFFFF { if addr > 0xFFFF {
return fmt.Errorf("absolute address %d exceeds 16-bit range", addr) return fmt.Errorf("absolute address %d exceeds 16-bit range", addr)
} }
@ -198,20 +171,16 @@ func (st *SymbolTable) AddAbsolute(name, scope string, kind VarKind, addr uint16
Scope: scope, Scope: scope,
Flags: flags, Flags: flags,
AbsAddr: addr, AbsAddr: addr,
Filename: filename,
LineNo: lineNo,
}) })
} }
// AddLabel adds a word variable that references a label // AddLabel adds a word variable that references a label
func (st *SymbolTable) AddLabel(name, scope string, labelRef string, filename string, lineNo int) error { func (st *SymbolTable) AddLabel(name, scope string, labelRef string) error {
return st.add(&Symbol{ return st.add(&Symbol{
Name: name, Name: name,
Scope: scope, Scope: scope,
Flags: FlagWord | FlagLabelRef, Flags: FlagWord | FlagLabelRef,
LabelRef: labelRef, LabelRef: labelRef,
Filename: filename,
LineNo: lineNo,
}) })
} }
@ -239,17 +208,12 @@ func (st *SymbolTable) add(sym *Symbol) error {
// Lookup finds a symbol by name, resolving scope // Lookup finds a symbol by name, resolving scope
// Searches local scope first (if currentScopes provided), then global // 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 { func (st *SymbolTable) Lookup(name string, currentScopes []string) *Symbol {
// Try local scopes first (innermost to outermost) // Try local scopes first (innermost to outermost)
for i := len(currentScopes) - 1; i >= 0; i-- { for i := len(currentScopes) - 1; i >= 0; i-- {
scope := currentScopes[i] scope := currentScopes[i]
if scopeMap, ok := st.byScope[scope]; ok { if scopeMap, ok := st.byScope[scope]; ok {
if sym, ok := scopeMap[name]; 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 return sym
} }
} }
@ -258,10 +222,6 @@ func (st *SymbolTable) Lookup(name string, currentScopes []string) *Symbol {
// Try global scope // Try global scope
if scopeMap, ok := st.byScope[""]; ok { if scopeMap, ok := st.byScope[""]; ok {
if sym, ok := scopeMap[name]; 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 return sym
} }
} }
@ -293,77 +253,6 @@ func (st *SymbolTable) ExpandName(name string, currentScopes []string) string {
return name 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 // String representation for debugging
func (s *Symbol) String() string { func (s *Symbol) String() string {
var parts []string var parts []string
@ -388,11 +277,6 @@ func (s *Symbol) String() string {
parts = append(parts, fmt.Sprintf("=%d", s.Value)) 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, " ") return strings.Join(parts, " ")
} }

View file

@ -106,7 +106,7 @@ func TestAddVar(t *testing.T) {
st := NewSymbolTable() st := NewSymbolTable()
// Add byte var // Add byte var
err := st.AddVar("counter", "", KindByte, 0, "test.c65", 1) err := st.AddVar("counter", "", KindByte, 0)
if err != nil { if err != nil {
t.Fatalf("AddVar() error = %v", err) t.Fatalf("AddVar() error = %v", err)
} }
@ -123,7 +123,7 @@ func TestAddVar(t *testing.T) {
} }
// Add word var // Add word var
err = st.AddVar("ptr", "", KindWord, 0x1234, "test.c65", 1) err = st.AddVar("ptr", "", KindWord, 0x1234)
if err != nil { if err != nil {
t.Fatalf("AddVar() error = %v", err) t.Fatalf("AddVar() error = %v", err)
} }
@ -137,7 +137,7 @@ func TestAddVar(t *testing.T) {
} }
// Test byte value range check // Test byte value range check
err = st.AddVar("bad", "", KindByte, 256, "test.c65", 1) err = st.AddVar("bad", "", KindByte, 256)
if err == nil { if err == nil {
t.Error("expected error for byte value > 255") t.Error("expected error for byte value > 255")
} }
@ -146,7 +146,7 @@ func TestAddVar(t *testing.T) {
func TestAddConst(t *testing.T) { func TestAddConst(t *testing.T) {
st := NewSymbolTable() st := NewSymbolTable()
err := st.AddConst("MAX", "", KindByte, 255, "test.c65", 1) err := st.AddConst("MAX", "", KindByte, 255)
if err != nil { if err != nil {
t.Fatalf("AddConst() error = %v", err) t.Fatalf("AddConst() error = %v", err)
} }
@ -166,7 +166,7 @@ func TestAddConst(t *testing.T) {
} }
// Test byte range check // Test byte range check
err = st.AddConst("BAD", "", KindByte, 300, "test.c65", 1) err = st.AddConst("BAD", "", KindByte, 300)
if err == nil { if err == nil {
t.Error("expected error for byte const > 255") t.Error("expected error for byte const > 255")
} }
@ -176,7 +176,7 @@ func TestAddAbsolute(t *testing.T) {
st := NewSymbolTable() st := NewSymbolTable()
// Zero-page byte // Zero-page byte
err := st.AddAbsolute("ZP_VAR", "", KindByte, 0x80, "test.c65", 1) err := st.AddAbsolute("ZP_VAR", "", KindByte, 0x80)
if err != nil { if err != nil {
t.Fatalf("AddAbsolute() error = %v", err) t.Fatalf("AddAbsolute() error = %v", err)
} }
@ -193,7 +193,7 @@ func TestAddAbsolute(t *testing.T) {
} }
// Zero-page word pointer // Zero-page word pointer
err = st.AddAbsolute("ZP_PTR", "", KindWord, 0xFE, "test.c65", 1) err = st.AddAbsolute("ZP_PTR", "", KindWord, 0xFE)
if err != nil { if err != nil {
t.Fatalf("AddAbsolute() error = %v", err) t.Fatalf("AddAbsolute() error = %v", err)
} }
@ -204,7 +204,7 @@ func TestAddAbsolute(t *testing.T) {
} }
// Non-zero-page // Non-zero-page
err = st.AddAbsolute("VIC", "", KindWord, 0xD000, "test.c65", 1) err = st.AddAbsolute("VIC", "", KindWord, 0xD000)
if err != nil { if err != nil {
t.Fatalf("AddAbsolute() error = %v", err) t.Fatalf("AddAbsolute() error = %v", err)
} }
@ -224,7 +224,7 @@ func TestAddAbsolute(t *testing.T) {
func TestAddLabel(t *testing.T) { func TestAddLabel(t *testing.T) {
st := NewSymbolTable() st := NewSymbolTable()
err := st.AddLabel("handler", "", "irq_vector", "test.c65", 1) err := st.AddLabel("handler", "", "irq_vector")
if err != nil { if err != nil {
t.Fatalf("AddLabel() error = %v", err) t.Fatalf("AddLabel() error = %v", err)
} }
@ -247,19 +247,19 @@ func TestAddLabel(t *testing.T) {
func TestRedeclaration(t *testing.T) { func TestRedeclaration(t *testing.T) {
st := NewSymbolTable() st := NewSymbolTable()
err := st.AddVar("test", "", KindByte, 0, "test.c65", 1) err := st.AddVar("test", "", KindByte, 0)
if err != nil { if err != nil {
t.Fatalf("first AddVar() error = %v", err) t.Fatalf("first AddVar() error = %v", err)
} }
// Attempt redeclaration // Attempt redeclaration
err = st.AddVar("test", "", KindByte, 0, "test.c65", 1) err = st.AddVar("test", "", KindByte, 0)
if err == nil { if err == nil {
t.Error("expected error on redeclaration") t.Error("expected error on redeclaration")
} }
// Different scope should be OK // Different scope should be OK
err = st.AddVar("test", "main", KindByte, 0, "test.c65", 1) err = st.AddVar("test", "main", KindByte, 0)
if err != nil { if err != nil {
t.Errorf("AddVar() with different scope error = %v", err) t.Errorf("AddVar() with different scope error = %v", err)
} }
@ -269,13 +269,13 @@ func TestLookup(t *testing.T) {
st := NewSymbolTable() st := NewSymbolTable()
// Global variable // Global variable
st.AddVar("global", "", KindByte, 0, "test.c65", 1) st.AddVar("global", "", KindByte, 0)
// Local in main // Local in main
st.AddVar("local", "main", KindByte, 0, "test.c65", 1) st.AddVar("local", "main", KindByte, 0)
// Local in nested function // Local in nested function
st.AddVar("inner", "main_helper", KindByte, 0, "test.c65", 1) st.AddVar("inner", "main_helper", KindByte, 0)
tests := []struct { tests := []struct {
name string name string
@ -350,8 +350,8 @@ func TestLookup(t *testing.T) {
func TestExpandName(t *testing.T) { func TestExpandName(t *testing.T) {
st := NewSymbolTable() st := NewSymbolTable()
st.AddVar("global", "", KindByte, 0, "test.c65", 1) st.AddVar("global", "", KindByte, 0)
st.AddVar("local", "main", KindByte, 0, "test.c65", 1) st.AddVar("local", "main", KindByte, 0)
tests := []struct { tests := []struct {
name string name string
@ -379,7 +379,7 @@ func TestInsertionOrder(t *testing.T) {
names := []string{"first", "second", "third"} names := []string{"first", "second", "third"}
for _, name := range names { for _, name := range names {
st.AddVar(name, "", KindByte, 0, "test.c65", 1) st.AddVar(name, "", KindByte, 0)
} }
symbols := st.Symbols() symbols := st.Symbols()
@ -401,8 +401,8 @@ func TestCount(t *testing.T) {
t.Errorf("initial Count() = %d, want 0", st.Count()) t.Errorf("initial Count() = %d, want 0", st.Count())
} }
st.AddVar("a", "", KindByte, 0, "test.c65", 1) st.AddVar("a", "", KindByte, 0)
st.AddVar("b", "", KindByte, 0, "test.c65", 1) st.AddVar("b", "", KindByte, 0)
if st.Count() != 2 { if st.Count() != 2 {
t.Errorf("Count() = %d, want 2", st.Count()) t.Errorf("Count() = %d, want 2", st.Count())
@ -418,7 +418,7 @@ func TestSymbolString(t *testing.T) {
{ {
name: "byte variable", name: "byte variable",
setup: func(st *SymbolTable) *Symbol { setup: func(st *SymbolTable) *Symbol {
st.AddVar("test", "", KindByte, 0, "test.c65", 1) st.AddVar("test", "", KindByte, 0)
return st.Get("test") return st.Get("test")
}, },
contains: []string{"Name=test", "BYTE"}, contains: []string{"Name=test", "BYTE"},
@ -426,7 +426,7 @@ func TestSymbolString(t *testing.T) {
{ {
name: "word constant", name: "word constant",
setup: func(st *SymbolTable) *Symbol { setup: func(st *SymbolTable) *Symbol {
st.AddConst("MAX", "", KindWord, 65535, "test.c65", 1) st.AddConst("MAX", "", KindWord, 65535)
return st.Get("MAX") return st.Get("MAX")
}, },
contains: []string{"Name=MAX", "WORD", "CONST=65535"}, contains: []string{"Name=MAX", "WORD", "CONST=65535"},
@ -434,7 +434,7 @@ func TestSymbolString(t *testing.T) {
{ {
name: "zero-page pointer", name: "zero-page pointer",
setup: func(st *SymbolTable) *Symbol { setup: func(st *SymbolTable) *Symbol {
st.AddAbsolute("ptr", "", KindWord, 0x80, "test.c65", 1) st.AddAbsolute("ptr", "", KindWord, 0x80)
return st.Get("ptr") return st.Get("ptr")
}, },
contains: []string{"Name=ptr", "WORD", "@$0080", "ZP"}, contains: []string{"Name=ptr", "WORD", "@$0080", "ZP"},
@ -442,7 +442,7 @@ func TestSymbolString(t *testing.T) {
{ {
name: "label reference", name: "label reference",
setup: func(st *SymbolTable) *Symbol { setup: func(st *SymbolTable) *Symbol {
st.AddLabel("handler", "", "irq", "test.c65", 1) st.AddLabel("handler", "", "irq")
return st.Get("handler") return st.Get("handler")
}, },
contains: []string{"Name=handler", "->irq"}, contains: []string{"Name=handler", "->irq"},
@ -484,9 +484,9 @@ func TestGenerateConstants(t *testing.T) {
st := NewSymbolTable() st := NewSymbolTable()
// Add some constants // Add some constants
st.AddConst("MAX", "", KindByte, 255, "test.c65", 1) st.AddConst("MAX", "", KindByte, 255)
st.AddConst("SIZE", "", KindWord, 0x1234, "test.c65", 1) st.AddConst("SIZE", "", KindWord, 0x1234)
st.AddVar("notconst", "", KindByte, 0, "test.c65", 1) // should be skipped st.AddVar("notconst", "", KindByte, 0) // should be skipped
lines := GenerateConstants(st) lines := GenerateConstants(st)
@ -520,14 +520,14 @@ func TestGenerateAbsolutes(t *testing.T) {
st := NewSymbolTable() st := NewSymbolTable()
// Zero-page // Zero-page
st.AddAbsolute("ZP_VAR", "", KindByte, 0x80, "test.c65", 1) st.AddAbsolute("ZP_VAR", "", KindByte, 0x80)
st.AddAbsolute("ZP_PTR", "", KindWord, 0xFE, "test.c65", 1) st.AddAbsolute("ZP_PTR", "", KindWord, 0xFE)
// Non-zero-page // Non-zero-page
st.AddAbsolute("VIC", "", KindWord, 0xD000, "test.c65", 1) st.AddAbsolute("VIC", "", KindWord, 0xD000)
// Regular var (should be skipped) // Regular var (should be skipped)
st.AddVar("regular", "", KindByte, 0, "test.c65", 1) st.AddVar("regular", "", KindByte, 0)
lines := GenerateAbsolutes(st) lines := GenerateAbsolutes(st)
@ -564,19 +564,19 @@ func TestGenerateVariables(t *testing.T) {
st := NewSymbolTable() st := NewSymbolTable()
// Byte variable // Byte variable
st.AddVar("counter", "", KindByte, 42, "test.c65", 1) st.AddVar("counter", "", KindByte, 42)
// Word variable // Word variable
st.AddVar("ptr", "", KindWord, 0x1234, "test.c65", 1) st.AddVar("ptr", "", KindWord, 0x1234)
// Label reference // Label reference
st.AddLabel("handler", "", "irq_routine", "test.c65", 1) st.AddLabel("handler", "", "irq_routine")
// Const (should be skipped) // Const (should be skipped)
st.AddConst("SKIP", "", KindByte, 99, "test.c65", 1) st.AddConst("SKIP", "", KindByte, 99)
// Absolute (should be skipped) // Absolute (should be skipped)
st.AddAbsolute("SKIP2", "", KindByte, 0x80, "test.c65", 1) st.AddAbsolute("SKIP2", "", KindByte, 0x80)
lines := GenerateVariables(st) lines := GenerateVariables(st)
@ -633,7 +633,7 @@ func TestGenerateEmpty(t *testing.T) {
} }
// Only variables (no constants/absolutes) // Only variables (no constants/absolutes)
st.AddVar("test", "", KindByte, 0, "test.c65", 1) st.AddVar("test", "", KindByte, 0)
if lines := GenerateConstants(st); lines != nil { if lines := GenerateConstants(st); lines != nil {
t.Error("expected nil when no constants exist") t.Error("expected nil when no constants exist")
@ -646,9 +646,9 @@ func TestGenerateEmpty(t *testing.T) {
func TestGenerateScopedVariables(t *testing.T) { func TestGenerateScopedVariables(t *testing.T) {
st := NewSymbolTable() st := NewSymbolTable()
st.AddVar("global", "", KindByte, 0, "test.c65", 1) st.AddVar("global", "", KindByte, 0)
st.AddVar("local", "main", KindByte, 0, "test.c65", 1) st.AddVar("local", "main", KindByte, 0)
st.AddVar("nested", "main_helper", KindByte, 0, "test.c65", 1) st.AddVar("nested", "main_helper", KindByte, 0)
lines := GenerateVariables(st) lines := GenerateVariables(st)
output := strings.Join(lines, "\n") output := strings.Join(lines, "\n")
@ -668,9 +668,9 @@ func TestGenerateScopedVariables(t *testing.T) {
func TestGenerateHexLowercase(t *testing.T) { func TestGenerateHexLowercase(t *testing.T) {
st := NewSymbolTable() st := NewSymbolTable()
st.AddConst("TEST", "", KindByte, 0xAB, "test.c65", 1) st.AddConst("TEST", "", KindByte, 0xAB)
st.AddAbsolute("ADDR", "", KindWord, 0xDEAD, "test.c65", 1) st.AddAbsolute("ADDR", "", KindWord, 0xDEAD)
st.AddVar("VAR", "", KindWord, 0xBEEF, "test.c65", 1) st.AddVar("VAR", "", KindWord, 0xBEEF)
constLines := GenerateConstants(st) constLines := GenerateConstants(st)
absLines := GenerateAbsolutes(st) absLines := GenerateAbsolutes(st)
@ -693,381 +693,3 @@ func TestGenerateHexLowercase(t *testing.T) {
t.Error("expected lowercase hex in variable") 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

@ -1,13 +0,0 @@
{
"$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"
}
}
}

View file

@ -1,12 +0,0 @@
#!/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