First commit refactoring and centralizing constant handling all over

This commit is contained in:
Mattias Hansson 2026-04-13 12:21:11 +02:00
parent 4f4df41c18
commit b25c11ae64
33 changed files with 248 additions and 196 deletions

5
.gitignore vendored
View file

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

167
AGENTS.md Normal file
View file

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

View file

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

View file

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

View file

@ -1,13 +1,15 @@
services: services:
claude-c65gm: opencode-deepseek-c65gm:
build: . #image: ghcr.io/anomalyco/opencode:0.0.0-beta-202604081541
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:
- ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY} - DEEPSEEK_API_KEY=${DEEPSEEK_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,13 +68,7 @@ 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 := func(name string) (int64, bool) { constLookup := ctx.SymbolTable.ConstantLookupFunc(scope)
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

@ -68,13 +68,7 @@ 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 := func(name string) (int64, bool) { constLookup := ctx.SymbolTable.ConstantLookupFunc(scope)
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

@ -55,13 +55,7 @@ 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 := func(name string) (int64, bool) { constLookup := ctx.SymbolTable.ConstantLookupFunc(ctx.CurrentScope())
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:

View file

@ -39,13 +39,7 @@ func (c *CaseCommand) Interpret(line preproc.Line, ctx *compiler.CompilerContext
} }
scope := ctx.CurrentScope() scope := ctx.CurrentScope()
constLookup := func(name string) (int64, bool) { constLookup := ctx.SymbolTable.ConstantLookupFunc(scope)
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,13 +97,7 @@ func (c *DecrCommand) Interpret(line preproc.Line, ctx *compiler.CompilerContext
} }
// Old syntax allows absolute addresses // Old syntax allows absolute addresses
constLookup := func(name string) (int64, bool) { constLookup := ctx.SymbolTable.ConstantLookupFunc(scope)
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,13 +48,7 @@ func (c *ForCommand) Interpret(line preproc.Line, ctx *compiler.CompilerContext)
} }
scope := ctx.CurrentScope() scope := ctx.CurrentScope()
constLookup := func(name string) (int64, bool) { constLookup := ctx.SymbolTable.ConstantLookupFunc(scope)
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

@ -70,13 +70,7 @@ func (c *GosubCommand) Interpret(line preproc.Line, ctx *compiler.CompilerContex
scope := ctx.CurrentScope() scope := ctx.CurrentScope()
constLookup := func(name string) (int64, bool) { constLookup := ctx.SymbolTable.ConstantLookupFunc(scope)
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,13 +52,7 @@ func (c *GotoCommand) Interpret(line preproc.Line, ctx *compiler.CompilerContext
targetParam := params[1] targetParam := params[1]
scope := ctx.CurrentScope() scope := ctx.CurrentScope()
constLookup := func(name string) (int64, bool) { constLookup := ctx.SymbolTable.ConstantLookupFunc(scope)
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,13 +40,7 @@ func (c *IfCommand) Interpret(line preproc.Line, ctx *compiler.CompilerContext)
} }
scope := ctx.CurrentScope() scope := ctx.CurrentScope()
constLookup := func(name string) (int64, bool) { constLookup := ctx.SymbolTable.ConstantLookupFunc(scope)
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

@ -97,13 +97,7 @@ func (c *IncrCommand) Interpret(line preproc.Line, ctx *compiler.CompilerContext
} }
// Old syntax allows absolute addresses // Old syntax allows absolute addresses
constLookup := func(name string) (int64, bool) { constLookup := ctx.SymbolTable.ConstantLookupFunc(scope)
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

@ -62,13 +62,7 @@ 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 := func(name string) (int64, bool) { constLookup := ctx.SymbolTable.ConstantLookupFunc(scope)
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

@ -68,13 +68,7 @@ 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 := func(name string) (int64, bool) { constLookup := ctx.SymbolTable.ConstantLookupFunc(scope)
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

@ -36,13 +36,7 @@ func (c *OriginCommand) Interpret(line preproc.Line, ctx *compiler.CompilerConte
} }
scope := ctx.CurrentScope() scope := ctx.CurrentScope()
constLookup := func(name string) (int64, bool) { constLookup := ctx.SymbolTable.ConstantLookupFunc(scope)
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,13 +115,7 @@ 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 := func(name string) (int64, bool) { constLookup := ctx.SymbolTable.ConstantLookupFunc(scope)
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

@ -116,13 +116,7 @@ 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 := func(name string) (int64, bool) { constLookup := ctx.SymbolTable.ConstantLookupFunc(scope)
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,13 +74,7 @@ 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 := func(name string) (int64, bool) { constLookup := ctx.SymbolTable.ConstantLookupFunc(scope)
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,13 +83,7 @@ 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 := func(name string) (int64, bool) { constLookup := ctx.SymbolTable.ConstantLookupFunc(scope)
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,13 +82,7 @@ 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 := func(name string) (int64, bool) { constLookup := ctx.SymbolTable.ConstantLookupFunc(scope)
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,13 +71,7 @@ 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 := func(name string) (int64, bool) { constLookup := ctx.SymbolTable.ConstantLookupFunc(scope)
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

@ -33,13 +33,7 @@ func (c *SwitchCommand) Interpret(line preproc.Line, ctx *compiler.CompilerConte
} }
scope := ctx.CurrentScope() scope := ctx.CurrentScope()
constLookup := func(name string) (int64, bool) { constLookup := ctx.SymbolTable.ConstantLookupFunc(scope)
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

@ -41,13 +41,7 @@ func (c *WhileCommand) Interpret(line preproc.Line, ctx *compiler.CompilerContex
} }
scope := ctx.CurrentScope() scope := ctx.CurrentScope()
constLookup := func(name string) (int64, bool) { constLookup := ctx.SymbolTable.ConstantLookupFunc(scope)
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

@ -57,13 +57,7 @@ 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 := func(name string) (int64, bool) { constLookup := ctx.SymbolTable.ConstantLookupFunc(ctx.CurrentScope())
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:

View file

@ -68,13 +68,7 @@ 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 := func(name string) (int64, bool) { constLookup := ctx.SymbolTable.ConstantLookupFunc(scope)
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

@ -343,12 +343,7 @@ func (fh *FunctionHandler) HandleFuncCall(line preproc.Line) ([]string, error) {
} }
// Use ParseOperandParam for variables, constants, and expressions // Use ParseOperandParam for variables, constants, and expressions
constLookup := func(name string) (int64, bool) { constLookup := fh.symTable.ConstantLookupFunc(fh.currentFuncs)
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)
@ -632,13 +627,7 @@ func (fh *FunctionHandler) parseImplicitDecl(decl string, funcName string) error
} }
// Create constant lookup function for address evaluation // Create constant lookup function for address evaluation
constLookup := func(name string) (int64, bool) { constLookup := fh.symTable.ConstantLookupFunc([]string{funcName})
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)

View file

@ -177,13 +177,7 @@ 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 := func(name string) (int64, bool) { lookup := ctx.SymbolTable.ConstantLookupFunc(nil)
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

@ -253,6 +253,24 @@ func (st *SymbolTable) ExpandName(name string, currentScopes []string) string {
return name return name
} }
// 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.Lookup(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)
}
}
// String representation for debugging // String representation for debugging
func (s *Symbol) String() string { func (s *Symbol) String() string {
var parts []string var parts []string

View file

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

12
opencode-docker.sh Normal file
View file

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