diff --git a/.gitignore b/.gitignore index cb9ad25..900c38e 100644 --- a/.gitignore +++ b/.gitignore @@ -33,3 +33,8 @@ c65gm *.sym *.prg *.s + +.cache/ +.env +.local/ +opencode-config/package-lock.json diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..35a5aa2 --- /dev/null +++ b/AGENTS.md @@ -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 +#INCLUDE + +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 ` + +### 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 \ No newline at end of file diff --git a/Dockerfile b/Dockerfile deleted file mode 100644 index 653f077..0000000 --- a/Dockerfile +++ /dev/null @@ -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"] diff --git a/claude_code_docker.sh b/claude_code_docker.sh deleted file mode 100644 index 96459ba..0000000 --- a/claude_code_docker.sh +++ /dev/null @@ -1,4 +0,0 @@ -#!/bin/bash -export DOCKER_UID=$(id -u) -export DOCKER_GID=$(id -g) -docker compose run --rm claude-c65gm diff --git a/docker-compose.yml b/docker-compose.yml index 8c143f2..957c753 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,13 +1,15 @@ services: - claude-c65gm: - build: . + opencode-deepseek-c65gm: + #image: ghcr.io/anomalyco/opencode:0.0.0-beta-202604081541 + image: ghcr.io/anomalyco/opencode:latest user: "${DOCKER_UID}:${DOCKER_GID}" + working_dir: /app volumes: - .:/app + - ./opencode-config:/app/.config/opencode + - ./opencode-data:/home/opencode/.local/share/opencode environment: - - ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY} - - CLAUDE_CONFIG_DIR=/app/.claude + - DEEPSEEK_API_KEY=${DEEPSEEK_API_KEY} - HOME=/app - - DISABLE_AUTOUPDATER=1 stdin_open: true tty: true diff --git a/internal/commands/add.go b/internal/commands/add.go index b3654a5..e824435 100644 --- a/internal/commands/add.go +++ b/internal/commands/add.go @@ -68,13 +68,7 @@ func (c *AddCommand) Interpret(line preproc.Line, ctx *compiler.CompilerContext) scope := ctx.CurrentScope() // Create constant lookup function - constLookup := func(name string) (int64, bool) { - sym := ctx.SymbolTable.Lookup(name, scope) - if sym != nil && sym.IsConst() { - return int64(sym.Value), true - } - return 0, false - } + constLookup := ctx.SymbolTable.ConstantLookupFunc(scope) // Determine syntax and parse accordingly if strings.ToUpper(params[0]) == "ADD" { diff --git a/internal/commands/and.go b/internal/commands/and.go index b338013..6c63eab 100644 --- a/internal/commands/and.go +++ b/internal/commands/and.go @@ -68,13 +68,7 @@ func (c *AndCommand) Interpret(line preproc.Line, ctx *compiler.CompilerContext) scope := ctx.CurrentScope() // Create constant lookup function - constLookup := func(name string) (int64, bool) { - sym := ctx.SymbolTable.Lookup(name, scope) - if sym != nil && sym.IsConst() { - return int64(sym.Value), true - } - return 0, false - } + constLookup := ctx.SymbolTable.ConstantLookupFunc(scope) // Determine syntax and parse accordingly if strings.ToUpper(params[0]) == "AND" { diff --git a/internal/commands/byte.go b/internal/commands/byte.go index 74b8030..50b5c7a 100644 --- a/internal/commands/byte.go +++ b/internal/commands/byte.go @@ -55,13 +55,7 @@ func (c *ByteCommand) Interpret(line preproc.Line, ctx *compiler.CompilerContext scope := ctx.FunctionHandler.CurrentFunction() // Create constant lookup function - constLookup := func(name string) (int64, bool) { - sym := ctx.SymbolTable.Lookup(name, ctx.CurrentScope()) - if sym != nil && sym.IsConst() { - return int64(sym.Value), true - } - return 0, false - } + constLookup := ctx.SymbolTable.ConstantLookupFunc(ctx.CurrentScope()) switch paramCount { case 2: diff --git a/internal/commands/case.go b/internal/commands/case.go index 6a6762d..febb002 100644 --- a/internal/commands/case.go +++ b/internal/commands/case.go @@ -39,13 +39,7 @@ func (c *CaseCommand) Interpret(line preproc.Line, ctx *compiler.CompilerContext } scope := ctx.CurrentScope() - constLookup := func(name string) (int64, bool) { - sym := ctx.SymbolTable.Lookup(name, scope) - if sym != nil && sym.IsConst() { - return int64(sym.Value), true - } - return 0, false - } + constLookup := ctx.SymbolTable.ConstantLookupFunc(scope) // Parse the case value varName, varKind, value, isVar, err := compiler.ParseOperandParam( diff --git a/internal/commands/decr.go b/internal/commands/decr.go index e43537a..6c393f9 100644 --- a/internal/commands/decr.go +++ b/internal/commands/decr.go @@ -97,13 +97,7 @@ func (c *DecrCommand) Interpret(line preproc.Line, ctx *compiler.CompilerContext } // Old syntax allows absolute addresses - constLookup := func(name string) (int64, bool) { - s := ctx.SymbolTable.Lookup(name, scope) - if s != nil && s.IsConst() { - return int64(s.Value), true - } - return 0, false - } + constLookup := ctx.SymbolTable.ConstantLookupFunc(scope) val, evalErr := utils.EvaluateExpression(targetParam, constLookup) if evalErr != nil { diff --git a/internal/commands/for.go b/internal/commands/for.go index 9a40dcc..6955872 100644 --- a/internal/commands/for.go +++ b/internal/commands/for.go @@ -48,13 +48,7 @@ func (c *ForCommand) Interpret(line preproc.Line, ctx *compiler.CompilerContext) } scope := ctx.CurrentScope() - constLookup := func(name string) (int64, bool) { - sym := ctx.SymbolTable.Lookup(name, scope) - if sym != nil && sym.IsConst() { - return int64(sym.Value), true - } - return 0, false - } + constLookup := ctx.SymbolTable.ConstantLookupFunc(scope) // Parse variable varName := params[1] diff --git a/internal/commands/gosub.go b/internal/commands/gosub.go index 97109ad..d1252e9 100644 --- a/internal/commands/gosub.go +++ b/internal/commands/gosub.go @@ -70,13 +70,7 @@ func (c *GosubCommand) Interpret(line preproc.Line, ctx *compiler.CompilerContex scope := ctx.CurrentScope() - constLookup := func(name string) (int64, bool) { - sym := ctx.SymbolTable.Lookup(name, scope) - if sym != nil && sym.IsConst() { - return int64(sym.Value), true - } - return 0, false - } + constLookup := ctx.SymbolTable.ConstantLookupFunc(scope) // Parse target (param 2) targetParam := params[1] diff --git a/internal/commands/goto.go b/internal/commands/goto.go index 78d277f..dccc286 100644 --- a/internal/commands/goto.go +++ b/internal/commands/goto.go @@ -52,13 +52,7 @@ func (c *GotoCommand) Interpret(line preproc.Line, ctx *compiler.CompilerContext targetParam := params[1] scope := ctx.CurrentScope() - constLookup := func(name string) (int64, bool) { - sym := ctx.SymbolTable.Lookup(name, scope) - if sym != nil && sym.IsConst() { - return int64(sym.Value), true - } - return 0, false - } + constLookup := ctx.SymbolTable.ConstantLookupFunc(scope) // Try ParseOperandParam - handles variables, constants, expressions varName, varKind, value, isVar, err := compiler.ParseOperandParam( diff --git a/internal/commands/if.go b/internal/commands/if.go index 496fa79..cfcb810 100644 --- a/internal/commands/if.go +++ b/internal/commands/if.go @@ -40,13 +40,7 @@ func (c *IfCommand) Interpret(line preproc.Line, ctx *compiler.CompilerContext) } scope := ctx.CurrentScope() - constLookup := func(name string) (int64, bool) { - sym := ctx.SymbolTable.Lookup(name, scope) - if sym != nil && sym.IsConst() { - return int64(sym.Value), true - } - return 0, false - } + constLookup := ctx.SymbolTable.ConstantLookupFunc(scope) // Parse param1 varName, varKind, value, isVar, err := compiler.ParseOperandParam( diff --git a/internal/commands/incr.go b/internal/commands/incr.go index 60573f2..0752811 100644 --- a/internal/commands/incr.go +++ b/internal/commands/incr.go @@ -97,13 +97,7 @@ func (c *IncrCommand) Interpret(line preproc.Line, ctx *compiler.CompilerContext } // Old syntax allows absolute addresses - constLookup := func(name string) (int64, bool) { - s := ctx.SymbolTable.Lookup(name, scope) - if s != nil && s.IsConst() { - return int64(s.Value), true - } - return 0, false - } + constLookup := ctx.SymbolTable.ConstantLookupFunc(scope) val, evalErr := utils.EvaluateExpression(targetParam, constLookup) if evalErr != nil { diff --git a/internal/commands/let.go b/internal/commands/let.go index 78bff6c..eaee451 100644 --- a/internal/commands/let.go +++ b/internal/commands/let.go @@ -62,13 +62,7 @@ func (c *LetCommand) Interpret(line preproc.Line, ctx *compiler.CompilerContext) scope := ctx.CurrentScope() // Create constant lookup function - constLookup := func(name string) (int64, bool) { - sym := ctx.SymbolTable.Lookup(name, scope) - if sym != nil && sym.IsConst() { - return int64(sym.Value), true - } - return 0, false - } + constLookup := ctx.SymbolTable.ConstantLookupFunc(scope) // Determine syntax and parse accordingly if strings.ToUpper(params[0]) == "LET" { diff --git a/internal/commands/or.go b/internal/commands/or.go index 540fa1d..c94869b 100644 --- a/internal/commands/or.go +++ b/internal/commands/or.go @@ -68,13 +68,7 @@ func (c *OrCommand) Interpret(line preproc.Line, ctx *compiler.CompilerContext) scope := ctx.CurrentScope() // Create constant lookup function - constLookup := func(name string) (int64, bool) { - sym := ctx.SymbolTable.Lookup(name, scope) - if sym != nil && sym.IsConst() { - return int64(sym.Value), true - } - return 0, false - } + constLookup := ctx.SymbolTable.ConstantLookupFunc(scope) // Determine syntax and parse accordingly if strings.ToUpper(params[0]) == "OR" { diff --git a/internal/commands/origin.go b/internal/commands/origin.go index eccb604..1a069c9 100644 --- a/internal/commands/origin.go +++ b/internal/commands/origin.go @@ -36,13 +36,7 @@ func (c *OriginCommand) Interpret(line preproc.Line, ctx *compiler.CompilerConte } scope := ctx.CurrentScope() - constLookup := func(name string) (int64, bool) { - sym := ctx.SymbolTable.Lookup(name, scope) - if sym != nil && sym.IsConst() { - return int64(sym.Value), true - } - return 0, false - } + constLookup := ctx.SymbolTable.ConstantLookupFunc(scope) val, err := utils.EvaluateExpression(params[1], constLookup) if err != nil { diff --git a/internal/commands/peek.go b/internal/commands/peek.go index dbf91ac..f3fe7eb 100644 --- a/internal/commands/peek.go +++ b/internal/commands/peek.go @@ -115,13 +115,7 @@ func (c *PeekCommand) Interpret(line preproc.Line, ctx *compiler.CompilerContext // Parse address - may have [offset] baseAddr, offsetParam := parseOffset(addrParam) - constLookup := func(name string) (int64, bool) { - sym := ctx.SymbolTable.Lookup(name, scope) - if sym != nil && sym.IsConst() { - return int64(sym.Value), true - } - return 0, false - } + constLookup := ctx.SymbolTable.ConstantLookupFunc(scope) // Try to parse base address varName, varKind, value, isVar, err := compiler.ParseOperandParam( diff --git a/internal/commands/peekw.go b/internal/commands/peekw.go index 5cd48c1..f9f1ff3 100644 --- a/internal/commands/peekw.go +++ b/internal/commands/peekw.go @@ -116,13 +116,7 @@ func (c *PeekWCommand) Interpret(line preproc.Line, ctx *compiler.CompilerContex // Parse address - may have [offset] baseAddr, offsetParam := parseOffsetW(addrParam) - constLookup := func(name string) (int64, bool) { - sym := ctx.SymbolTable.Lookup(name, scope) - if sym != nil && sym.IsConst() { - return int64(sym.Value), true - } - return 0, false - } + constLookup := ctx.SymbolTable.ConstantLookupFunc(scope) // Try to parse base address varName, varKind, value, isVar, err := compiler.ParseOperandParam( diff --git a/internal/commands/point.go b/internal/commands/point.go index a7f6f7c..3118ab9 100644 --- a/internal/commands/point.go +++ b/internal/commands/point.go @@ -74,13 +74,7 @@ func (c *PointerCommand) Interpret(line preproc.Line, ctx *compiler.CompilerCont // Parse target (param 4) targetParam := params[3] - constLookup := func(name string) (int64, bool) { - sym := ctx.SymbolTable.Lookup(name, scope) - if sym != nil && sym.IsConst() { - return int64(sym.Value), true - } - return 0, false - } + constLookup := ctx.SymbolTable.ConstantLookupFunc(scope) // Try ParseOperandParam - handles variables, constants, expressions varName, _, value, isVar, err := compiler.ParseOperandParam( diff --git a/internal/commands/poke.go b/internal/commands/poke.go index 431ec41..bef4a59 100644 --- a/internal/commands/poke.go +++ b/internal/commands/poke.go @@ -83,13 +83,7 @@ func (c *PokeCommand) Interpret(line preproc.Line, ctx *compiler.CompilerContext addrParam := params[1] baseAddr, offsetParam := parsePOKEOffset(addrParam) - constLookup := func(name string) (int64, bool) { - sym := ctx.SymbolTable.Lookup(name, scope) - if sym != nil && sym.IsConst() { - return int64(sym.Value), true - } - return 0, false - } + constLookup := ctx.SymbolTable.ConstantLookupFunc(scope) // Try to parse base address varName, varKind, value, isVar, err := compiler.ParseOperandParam( diff --git a/internal/commands/pokew.go b/internal/commands/pokew.go index 04d9fc5..ea24bce 100644 --- a/internal/commands/pokew.go +++ b/internal/commands/pokew.go @@ -82,13 +82,7 @@ func (c *PokeWCommand) Interpret(line preproc.Line, ctx *compiler.CompilerContex addrParam := params[1] baseAddr, offsetParam := parsePOKEWOffset(addrParam) - constLookup := func(name string) (int64, bool) { - sym := ctx.SymbolTable.Lookup(name, scope) - if sym != nil && sym.IsConst() { - return int64(sym.Value), true - } - return 0, false - } + constLookup := ctx.SymbolTable.ConstantLookupFunc(scope) // Try to parse base address varName, varKind, value, isVar, err := compiler.ParseOperandParam( diff --git a/internal/commands/subtr.go b/internal/commands/subtr.go index d2aef93..4ed8914 100644 --- a/internal/commands/subtr.go +++ b/internal/commands/subtr.go @@ -71,13 +71,7 @@ func (c *SubtractCommand) Interpret(line preproc.Line, ctx *compiler.CompilerCon scope := ctx.CurrentScope() // Create constant lookup function - constLookup := func(name string) (int64, bool) { - sym := ctx.SymbolTable.Lookup(name, scope) - if sym != nil && sym.IsConst() { - return int64(sym.Value), true - } - return 0, false - } + constLookup := ctx.SymbolTable.ConstantLookupFunc(scope) // Determine syntax and parse accordingly kw := strings.ToUpper(params[0]) diff --git a/internal/commands/switch.go b/internal/commands/switch.go index f822e68..56abba4 100644 --- a/internal/commands/switch.go +++ b/internal/commands/switch.go @@ -33,13 +33,7 @@ func (c *SwitchCommand) Interpret(line preproc.Line, ctx *compiler.CompilerConte } scope := ctx.CurrentScope() - constLookup := func(name string) (int64, bool) { - sym := ctx.SymbolTable.Lookup(name, scope) - if sym != nil && sym.IsConst() { - return int64(sym.Value), true - } - return 0, false - } + constLookup := ctx.SymbolTable.ConstantLookupFunc(scope) // Parse the switch variable/expression varName, varKind, value, isVar, err := compiler.ParseOperandParam( diff --git a/internal/commands/while.go b/internal/commands/while.go index 64e14cf..5278867 100644 --- a/internal/commands/while.go +++ b/internal/commands/while.go @@ -41,13 +41,7 @@ func (c *WhileCommand) Interpret(line preproc.Line, ctx *compiler.CompilerContex } scope := ctx.CurrentScope() - constLookup := func(name string) (int64, bool) { - sym := ctx.SymbolTable.Lookup(name, scope) - if sym != nil && sym.IsConst() { - return int64(sym.Value), true - } - return 0, false - } + constLookup := ctx.SymbolTable.ConstantLookupFunc(scope) // Parse param1 varName, varKind, value, isVar, err := compiler.ParseOperandParam( diff --git a/internal/commands/word.go b/internal/commands/word.go index c2d9cba..ed78af5 100644 --- a/internal/commands/word.go +++ b/internal/commands/word.go @@ -57,13 +57,7 @@ func (c *WordCommand) Interpret(line preproc.Line, ctx *compiler.CompilerContext scope := ctx.FunctionHandler.CurrentFunction() // Create constant lookup function - constLookup := func(name string) (int64, bool) { - sym := ctx.SymbolTable.Lookup(name, ctx.CurrentScope()) - if sym != nil && sym.IsConst() { - return int64(sym.Value), true - } - return 0, false - } + constLookup := ctx.SymbolTable.ConstantLookupFunc(ctx.CurrentScope()) switch paramCount { case 2: diff --git a/internal/commands/xor.go b/internal/commands/xor.go index cf3b422..0431c33 100644 --- a/internal/commands/xor.go +++ b/internal/commands/xor.go @@ -68,13 +68,7 @@ func (c *XorCommand) Interpret(line preproc.Line, ctx *compiler.CompilerContext) scope := ctx.CurrentScope() // Create constant lookup function - constLookup := func(name string) (int64, bool) { - sym := ctx.SymbolTable.Lookup(name, scope) - if sym != nil && sym.IsConst() { - return int64(sym.Value), true - } - return 0, false - } + constLookup := ctx.SymbolTable.ConstantLookupFunc(scope) // Determine syntax and parse accordingly if strings.ToUpper(params[0]) == "XOR" { diff --git a/internal/compiler/funchandler.go b/internal/compiler/funchandler.go index 8910098..0bfb449 100644 --- a/internal/compiler/funchandler.go +++ b/internal/compiler/funchandler.go @@ -343,12 +343,7 @@ func (fh *FunctionHandler) HandleFuncCall(line preproc.Line) ([]string, error) { } // Use ParseOperandParam for variables, constants, and expressions - constLookup := func(name string) (int64, bool) { - if sym := fh.symTable.Lookup(name, fh.currentFuncs); sym != nil && sym.IsConst() { - return int64(sym.Value), true - } - return 0, false - } + constLookup := fh.symTable.ConstantLookupFunc(fh.currentFuncs) _, _, value, isVar, err := ParseOperandParam( arg, fh.symTable, fh.currentFuncs, constLookup) @@ -632,13 +627,7 @@ func (fh *FunctionHandler) parseImplicitDecl(decl string, funcName string) error } // Create constant lookup function for address evaluation - constLookup := func(name string) (int64, bool) { - sym := fh.symTable.Lookup(name, []string{funcName}) - if sym != nil && sym.IsConst() { - return int64(sym.Value), true - } - return 0, false - } + constLookup := fh.symTable.ConstantLookupFunc([]string{funcName}) // Parse address (supports $hex and decimal) using EvaluateExpression addr, err := utils.EvaluateExpression(addrStr, constLookup) diff --git a/internal/compiler/scriptexec.go b/internal/compiler/scriptexec.go index ef52758..d0d848c 100644 --- a/internal/compiler/scriptexec.go +++ b/internal/compiler/scriptexec.go @@ -177,13 +177,7 @@ func evaluateMacroArg(arg string, ctx *CompilerContext) (starlark.Value, error) arg = strings.TrimSpace(arg) // Create lookup function for constants - lookup := func(name string) (int64, bool) { - sym := ctx.SymbolTable.Lookup(name, nil) - if sym != nil && sym.IsConst() { - return int64(sym.Value), true - } - return 0, false - } + lookup := ctx.SymbolTable.ConstantLookupFunc(nil) // Try to evaluate as expression (number, constant, arithmetic) val, err := utils.EvaluateExpression(arg, lookup) diff --git a/internal/compiler/symboltable.go b/internal/compiler/symboltable.go index f1bc7d1..05f1850 100644 --- a/internal/compiler/symboltable.go +++ b/internal/compiler/symboltable.go @@ -253,6 +253,24 @@ func (st *SymbolTable) ExpandName(name string, currentScopes []string) string { 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 func (s *Symbol) String() string { var parts []string diff --git a/opencode-config/opencode.json b/opencode-config/opencode.json new file mode 100644 index 0000000..86da758 --- /dev/null +++ b/opencode-config/opencode.json @@ -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" + } + } +} \ No newline at end of file diff --git a/opencode-docker.sh b/opencode-docker.sh new file mode 100644 index 0000000..986e629 --- /dev/null +++ b/opencode-docker.sh @@ -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