Added ADD command
This commit is contained in:
parent
f529835ba8
commit
ac40f67ec0
4 changed files with 992 additions and 6 deletions
273
internal/commands/add.go
Normal file
273
internal/commands/add.go
Normal file
|
|
@ -0,0 +1,273 @@
|
||||||
|
package commands
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"c65gm/internal/compiler"
|
||||||
|
"c65gm/internal/preproc"
|
||||||
|
"c65gm/internal/utils"
|
||||||
|
)
|
||||||
|
|
||||||
|
// AddCommand handles ADD operations
|
||||||
|
// Syntax:
|
||||||
|
//
|
||||||
|
// ADD <param1> TO <param2> GIVING <dest> # old syntax with TO/GIVING
|
||||||
|
// ADD <param1> + <param2> -> <dest> # old syntax with +/->
|
||||||
|
// <dest> = <param1> + <param2> # new syntax
|
||||||
|
type AddCommand struct {
|
||||||
|
param1VarName string
|
||||||
|
param1VarKind compiler.VarKind
|
||||||
|
param1Value uint16
|
||||||
|
param1IsVar bool
|
||||||
|
|
||||||
|
param2VarName string
|
||||||
|
param2VarKind compiler.VarKind
|
||||||
|
param2Value uint16
|
||||||
|
param2IsVar bool
|
||||||
|
|
||||||
|
destVarName string
|
||||||
|
destVarKind compiler.VarKind
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *AddCommand) WillHandle(line preproc.Line) bool {
|
||||||
|
params, err := utils.ParseParams(line.Text)
|
||||||
|
if err != nil || len(params) == 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Old syntax: ADD ... (must have exactly 6 params)
|
||||||
|
if strings.ToUpper(params[0]) == "ADD" && len(params) == 6 {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// New syntax: <dest> = <param1> + <param2>
|
||||||
|
if len(params) == 5 && params[1] == "=" && params[3] == "+" {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *AddCommand) Interpret(line preproc.Line, ctx *compiler.CompilerContext) error {
|
||||||
|
// Clear state
|
||||||
|
c.param1VarName = ""
|
||||||
|
c.param1IsVar = false
|
||||||
|
c.param1Value = 0
|
||||||
|
c.param2VarName = ""
|
||||||
|
c.param2IsVar = false
|
||||||
|
c.param2Value = 0
|
||||||
|
c.destVarName = ""
|
||||||
|
|
||||||
|
params, err := utils.ParseParams(line.Text)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
paramCount := len(params)
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
// Determine syntax and parse accordingly
|
||||||
|
if strings.ToUpper(params[0]) == "ADD" {
|
||||||
|
// Old syntax: ADD <param1> TO/+ <param2> GIVING/-> <dest>
|
||||||
|
if paramCount != 6 {
|
||||||
|
return fmt.Errorf("ADD: wrong number of parameters (%d), expected 6", paramCount)
|
||||||
|
}
|
||||||
|
|
||||||
|
separator1 := strings.ToUpper(params[2])
|
||||||
|
if separator1 != "TO" && separator1 != "+" {
|
||||||
|
return fmt.Errorf("ADD: parameter #3 must be 'TO' or '+', got %q", params[2])
|
||||||
|
}
|
||||||
|
|
||||||
|
separator2 := strings.ToUpper(params[4])
|
||||||
|
if separator2 != "GIVING" && separator2 != "->" {
|
||||||
|
return fmt.Errorf("ADD: parameter #5 must be 'GIVING' or '->', got %q", params[4])
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse destination
|
||||||
|
destName := params[5]
|
||||||
|
destSym := ctx.SymbolTable.Lookup(destName, scope)
|
||||||
|
if destSym == nil {
|
||||||
|
return fmt.Errorf("ADD: unknown variable %q", destName)
|
||||||
|
}
|
||||||
|
if destSym.IsConst() {
|
||||||
|
return fmt.Errorf("ADD: cannot assign to constant %q", destName)
|
||||||
|
}
|
||||||
|
c.destVarName = destSym.FullName()
|
||||||
|
c.destVarKind = getVarKind(destSym)
|
||||||
|
|
||||||
|
// Parse param1
|
||||||
|
if err := c.parseParam(params[1], &c.param1VarName, &c.param1VarKind, &c.param1Value, &c.param1IsVar, ctx, scope, constLookup); err != nil {
|
||||||
|
return fmt.Errorf("ADD: param1: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse param2
|
||||||
|
if err := c.parseParam(params[3], &c.param2VarName, &c.param2VarKind, &c.param2Value, &c.param2IsVar, ctx, scope, constLookup); err != nil {
|
||||||
|
return fmt.Errorf("ADD: param2: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// New syntax: <dest> = <param1> + <param2>
|
||||||
|
if paramCount != 5 {
|
||||||
|
return fmt.Errorf("ADD: wrong number of parameters (%d), expected 5", paramCount)
|
||||||
|
}
|
||||||
|
|
||||||
|
if params[1] != "=" {
|
||||||
|
return fmt.Errorf("ADD: expected '=' at position 2, got %q", params[1])
|
||||||
|
}
|
||||||
|
|
||||||
|
if params[3] != "+" {
|
||||||
|
return fmt.Errorf("ADD: expected '+' at position 4, got %q", params[3])
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse destination
|
||||||
|
destName := params[0]
|
||||||
|
destSym := ctx.SymbolTable.Lookup(destName, scope)
|
||||||
|
if destSym == nil {
|
||||||
|
return fmt.Errorf("ADD: unknown variable %q", destName)
|
||||||
|
}
|
||||||
|
if destSym.IsConst() {
|
||||||
|
return fmt.Errorf("ADD: cannot assign to constant %q", destName)
|
||||||
|
}
|
||||||
|
c.destVarName = destSym.FullName()
|
||||||
|
c.destVarKind = getVarKind(destSym)
|
||||||
|
|
||||||
|
// Parse param1
|
||||||
|
if err := c.parseParam(params[2], &c.param1VarName, &c.param1VarKind, &c.param1Value, &c.param1IsVar, ctx, scope, constLookup); err != nil {
|
||||||
|
return fmt.Errorf("ADD: param1: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse param2
|
||||||
|
if err := c.parseParam(params[4], &c.param2VarName, &c.param2VarKind, &c.param2Value, &c.param2IsVar, ctx, scope, constLookup); err != nil {
|
||||||
|
return fmt.Errorf("ADD: param2: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *AddCommand) parseParam(
|
||||||
|
param string,
|
||||||
|
varName *string,
|
||||||
|
varKind *compiler.VarKind,
|
||||||
|
value *uint16,
|
||||||
|
isVar *bool,
|
||||||
|
ctx *compiler.CompilerContext,
|
||||||
|
scope []string,
|
||||||
|
constLookup utils.ConstantLookup,
|
||||||
|
) error {
|
||||||
|
// Try variable lookup first
|
||||||
|
sym := ctx.SymbolTable.Lookup(param, scope)
|
||||||
|
if sym != nil {
|
||||||
|
// It's a variable or constant
|
||||||
|
*varName = sym.FullName()
|
||||||
|
*varKind = getVarKind(sym)
|
||||||
|
*value = sym.Value
|
||||||
|
*isVar = true
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Not a variable, must be an expression
|
||||||
|
val, err := utils.EvaluateExpression(param, constLookup)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("not a valid variable or expression: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if val < 0 || val > 65535 {
|
||||||
|
return fmt.Errorf("value %d out of range (0-65535)", val)
|
||||||
|
}
|
||||||
|
|
||||||
|
*value = uint16(val)
|
||||||
|
*isVar = false
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *AddCommand) Generate(_ *compiler.CompilerContext) ([]string, error) {
|
||||||
|
var asm []string
|
||||||
|
|
||||||
|
// If both params are literals, fold at compile time
|
||||||
|
if !c.param1IsVar && !c.param2IsVar {
|
||||||
|
sum := uint32(c.param1Value) + uint32(c.param2Value)
|
||||||
|
lo := uint8(sum & 0xFF)
|
||||||
|
hi := uint8((sum >> 8) & 0xFF)
|
||||||
|
|
||||||
|
asm = append(asm, fmt.Sprintf("\tlda #$%02x", lo))
|
||||||
|
asm = append(asm, fmt.Sprintf("\tsta %s", c.destVarName))
|
||||||
|
|
||||||
|
if c.destVarKind == compiler.KindWord {
|
||||||
|
asm = append(asm, fmt.Sprintf("\tlda #$%02x", hi))
|
||||||
|
asm = append(asm, fmt.Sprintf("\tsta %s+1", c.destVarName))
|
||||||
|
}
|
||||||
|
|
||||||
|
return asm, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// At least one param is a variable - generate add code
|
||||||
|
asm = append(asm, "\tclc")
|
||||||
|
|
||||||
|
// Load param1
|
||||||
|
if c.param1IsVar {
|
||||||
|
asm = append(asm, fmt.Sprintf("\tlda %s", c.param1VarName))
|
||||||
|
} else {
|
||||||
|
asm = append(asm, fmt.Sprintf("\tlda #$%02x", uint8(c.param1Value&0xFF)))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add param2
|
||||||
|
if c.param2IsVar {
|
||||||
|
asm = append(asm, fmt.Sprintf("\tadc %s", c.param2VarName))
|
||||||
|
} else {
|
||||||
|
asm = append(asm, fmt.Sprintf("\tadc #$%02x", uint8(c.param2Value&0xFF)))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Store low byte
|
||||||
|
asm = append(asm, fmt.Sprintf("\tsta %s", c.destVarName))
|
||||||
|
|
||||||
|
// If destination is word, handle high byte
|
||||||
|
if c.destVarKind == compiler.KindWord {
|
||||||
|
// Load high byte of param1
|
||||||
|
if c.param1IsVar {
|
||||||
|
if c.param1VarKind == compiler.KindWord {
|
||||||
|
asm = append(asm, fmt.Sprintf("\tlda %s+1", c.param1VarName))
|
||||||
|
} else {
|
||||||
|
asm = append(asm, "\tlda #0")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
hi := uint8((c.param1Value >> 8) & 0xFF)
|
||||||
|
asm = append(asm, fmt.Sprintf("\tlda #$%02x", hi))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add high byte of param2
|
||||||
|
if c.param2IsVar {
|
||||||
|
if c.param2VarKind == compiler.KindWord {
|
||||||
|
asm = append(asm, fmt.Sprintf("\tadc %s+1", c.param2VarName))
|
||||||
|
} else {
|
||||||
|
asm = append(asm, "\tadc #0")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
hi := uint8((c.param2Value >> 8) & 0xFF)
|
||||||
|
asm = append(asm, fmt.Sprintf("\tadc #$%02x", hi))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Store high byte
|
||||||
|
asm = append(asm, fmt.Sprintf("\tsta %s+1", c.destVarName))
|
||||||
|
}
|
||||||
|
|
||||||
|
return asm, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// getVarKind extracts VarKind from Symbol
|
||||||
|
func getVarKind(sym *compiler.Symbol) compiler.VarKind {
|
||||||
|
if sym.IsByte() {
|
||||||
|
return compiler.KindByte
|
||||||
|
}
|
||||||
|
return compiler.KindWord
|
||||||
|
}
|
||||||
717
internal/commands/add_test.go
Normal file
717
internal/commands/add_test.go
Normal file
|
|
@ -0,0 +1,717 @@
|
||||||
|
package commands
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"c65gm/internal/compiler"
|
||||||
|
"c65gm/internal/preproc"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestAddCommand_WillHandle(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
text string
|
||||||
|
want bool
|
||||||
|
}{
|
||||||
|
// Old syntax
|
||||||
|
{"old syntax with TO/GIVING", "ADD a TO b GIVING c", true},
|
||||||
|
{"old syntax with +/->", "ADD a + b -> c", true},
|
||||||
|
{"old syntax mixed case", "add x to y giving z", true},
|
||||||
|
|
||||||
|
// New syntax
|
||||||
|
{"new syntax basic", "result = a + b", true},
|
||||||
|
{"new syntax with literals", "x = 10 + 20", true},
|
||||||
|
|
||||||
|
// Should not handle
|
||||||
|
{"not add - subtract", "result = a - b", false},
|
||||||
|
{"not add - multiply", "result = a * b", false},
|
||||||
|
{"not add - wrong params", "ADD a b c", false},
|
||||||
|
{"empty", "", false},
|
||||||
|
{"just ADD", "ADD", false},
|
||||||
|
{"assignment without add", "x = y", false},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
cmd := &AddCommand{}
|
||||||
|
line := preproc.Line{Text: tt.text}
|
||||||
|
if got := cmd.WillHandle(line); got != tt.want {
|
||||||
|
t.Errorf("WillHandle() = %v, want %v", got, tt.want)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAddCommand_Interpret_OldSyntax(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
setup func(*compiler.CompilerContext)
|
||||||
|
text string
|
||||||
|
wantErr bool
|
||||||
|
check func(*testing.T, *AddCommand)
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "byte + byte -> byte",
|
||||||
|
setup: func(ctx *compiler.CompilerContext) {
|
||||||
|
ctx.SymbolTable.AddVar("a", "", compiler.KindByte, 10)
|
||||||
|
ctx.SymbolTable.AddVar("b", "", compiler.KindByte, 20)
|
||||||
|
ctx.SymbolTable.AddVar("c", "", compiler.KindByte, 0)
|
||||||
|
},
|
||||||
|
text: "ADD a TO b GIVING c",
|
||||||
|
wantErr: false,
|
||||||
|
check: func(t *testing.T, cmd *AddCommand) {
|
||||||
|
if !cmd.param1IsVar || cmd.param1VarName != "a" {
|
||||||
|
t.Errorf("param1 should be var 'a'")
|
||||||
|
}
|
||||||
|
if !cmd.param2IsVar || cmd.param2VarName != "b" {
|
||||||
|
t.Errorf("param2 should be var 'b'")
|
||||||
|
}
|
||||||
|
if cmd.destVarName != "c" || cmd.destVarKind != compiler.KindByte {
|
||||||
|
t.Errorf("dest should be byte 'c'")
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "word + word -> word",
|
||||||
|
setup: func(ctx *compiler.CompilerContext) {
|
||||||
|
ctx.SymbolTable.AddVar("x", "", compiler.KindWord, 1000)
|
||||||
|
ctx.SymbolTable.AddVar("y", "", compiler.KindWord, 2000)
|
||||||
|
ctx.SymbolTable.AddVar("z", "", compiler.KindWord, 0)
|
||||||
|
},
|
||||||
|
text: "ADD x TO y GIVING z",
|
||||||
|
wantErr: false,
|
||||||
|
check: func(t *testing.T, cmd *AddCommand) {
|
||||||
|
if cmd.destVarKind != compiler.KindWord {
|
||||||
|
t.Errorf("dest should be word")
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "byte + word -> word",
|
||||||
|
setup: func(ctx *compiler.CompilerContext) {
|
||||||
|
ctx.SymbolTable.AddVar("a", "", compiler.KindByte, 10)
|
||||||
|
ctx.SymbolTable.AddVar("b", "", compiler.KindWord, 1000)
|
||||||
|
ctx.SymbolTable.AddVar("c", "", compiler.KindWord, 0)
|
||||||
|
},
|
||||||
|
text: "ADD a TO b GIVING c",
|
||||||
|
wantErr: false,
|
||||||
|
check: func(t *testing.T, cmd *AddCommand) {
|
||||||
|
if cmd.param1VarKind != compiler.KindByte {
|
||||||
|
t.Errorf("param1 should be byte")
|
||||||
|
}
|
||||||
|
if cmd.param2VarKind != compiler.KindWord {
|
||||||
|
t.Errorf("param2 should be word")
|
||||||
|
}
|
||||||
|
if cmd.destVarKind != compiler.KindWord {
|
||||||
|
t.Errorf("dest should be word")
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "literal + var -> var",
|
||||||
|
setup: func(ctx *compiler.CompilerContext) {
|
||||||
|
ctx.SymbolTable.AddVar("b", "", compiler.KindByte, 20)
|
||||||
|
ctx.SymbolTable.AddVar("c", "", compiler.KindByte, 0)
|
||||||
|
},
|
||||||
|
text: "ADD 10 TO b GIVING c",
|
||||||
|
wantErr: false,
|
||||||
|
check: func(t *testing.T, cmd *AddCommand) {
|
||||||
|
if cmd.param1IsVar {
|
||||||
|
t.Errorf("param1 should be literal")
|
||||||
|
}
|
||||||
|
if cmd.param1Value != 10 {
|
||||||
|
t.Errorf("param1 value = %d, want 10", cmd.param1Value)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "var + literal -> var",
|
||||||
|
setup: func(ctx *compiler.CompilerContext) {
|
||||||
|
ctx.SymbolTable.AddVar("a", "", compiler.KindByte, 10)
|
||||||
|
ctx.SymbolTable.AddVar("c", "", compiler.KindByte, 0)
|
||||||
|
},
|
||||||
|
text: "ADD a TO 20 GIVING c",
|
||||||
|
wantErr: false,
|
||||||
|
check: func(t *testing.T, cmd *AddCommand) {
|
||||||
|
if cmd.param2IsVar {
|
||||||
|
t.Errorf("param2 should be literal")
|
||||||
|
}
|
||||||
|
if cmd.param2Value != 20 {
|
||||||
|
t.Errorf("param2 value = %d, want 20", cmd.param2Value)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "literal + literal -> var",
|
||||||
|
setup: func(ctx *compiler.CompilerContext) {
|
||||||
|
ctx.SymbolTable.AddVar("c", "", compiler.KindByte, 0)
|
||||||
|
},
|
||||||
|
text: "ADD 15 TO 25 GIVING c",
|
||||||
|
wantErr: false,
|
||||||
|
check: func(t *testing.T, cmd *AddCommand) {
|
||||||
|
if cmd.param1IsVar || cmd.param2IsVar {
|
||||||
|
t.Errorf("both params should be literals")
|
||||||
|
}
|
||||||
|
if cmd.param1Value != 15 || cmd.param2Value != 25 {
|
||||||
|
t.Errorf("param values = %d, %d, want 15, 25", cmd.param1Value, cmd.param2Value)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "hex literal",
|
||||||
|
setup: func(ctx *compiler.CompilerContext) {
|
||||||
|
ctx.SymbolTable.AddVar("c", "", compiler.KindByte, 0)
|
||||||
|
},
|
||||||
|
text: "ADD $10 TO $20 GIVING c",
|
||||||
|
wantErr: false,
|
||||||
|
check: func(t *testing.T, cmd *AddCommand) {
|
||||||
|
if cmd.param1Value != 0x10 || cmd.param2Value != 0x20 {
|
||||||
|
t.Errorf("param values = %d, %d, want 16, 32", cmd.param1Value, cmd.param2Value)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "constant usage",
|
||||||
|
setup: func(ctx *compiler.CompilerContext) {
|
||||||
|
ctx.SymbolTable.AddConst("MAX", "", compiler.KindByte, 100)
|
||||||
|
ctx.SymbolTable.AddVar("a", "", compiler.KindByte, 10)
|
||||||
|
ctx.SymbolTable.AddVar("c", "", compiler.KindByte, 0)
|
||||||
|
},
|
||||||
|
text: "ADD MAX TO a GIVING c",
|
||||||
|
wantErr: false,
|
||||||
|
check: func(t *testing.T, cmd *AddCommand) {
|
||||||
|
if !cmd.param1IsVar || cmd.param1VarName != "MAX" {
|
||||||
|
t.Errorf("param1 should be constant MAX")
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "alternative syntax +/->",
|
||||||
|
setup: func(ctx *compiler.CompilerContext) {
|
||||||
|
ctx.SymbolTable.AddVar("a", "", compiler.KindByte, 10)
|
||||||
|
ctx.SymbolTable.AddVar("b", "", compiler.KindByte, 20)
|
||||||
|
ctx.SymbolTable.AddVar("c", "", compiler.KindByte, 0)
|
||||||
|
},
|
||||||
|
text: "ADD a + b -> c",
|
||||||
|
wantErr: false,
|
||||||
|
check: func(t *testing.T, cmd *AddCommand) {
|
||||||
|
if cmd.destVarName != "c" {
|
||||||
|
t.Errorf("dest should be c")
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "unknown variable",
|
||||||
|
setup: func(ctx *compiler.CompilerContext) {
|
||||||
|
ctx.SymbolTable.AddVar("a", "", compiler.KindByte, 10)
|
||||||
|
},
|
||||||
|
text: "ADD a TO b GIVING c",
|
||||||
|
wantErr: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "assign to constant",
|
||||||
|
setup: func(ctx *compiler.CompilerContext) {
|
||||||
|
ctx.SymbolTable.AddVar("a", "", compiler.KindByte, 10)
|
||||||
|
ctx.SymbolTable.AddVar("b", "", compiler.KindByte, 20)
|
||||||
|
ctx.SymbolTable.AddConst("MAX", "", compiler.KindByte, 100)
|
||||||
|
},
|
||||||
|
text: "ADD a TO b GIVING MAX",
|
||||||
|
wantErr: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "wrong parameter count",
|
||||||
|
setup: func(ctx *compiler.CompilerContext) {
|
||||||
|
ctx.SymbolTable.AddVar("a", "", compiler.KindByte, 10)
|
||||||
|
},
|
||||||
|
text: "ADD a TO b",
|
||||||
|
wantErr: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "wrong separator #3",
|
||||||
|
setup: func(ctx *compiler.CompilerContext) {
|
||||||
|
ctx.SymbolTable.AddVar("a", "", compiler.KindByte, 10)
|
||||||
|
ctx.SymbolTable.AddVar("b", "", compiler.KindByte, 20)
|
||||||
|
ctx.SymbolTable.AddVar("c", "", compiler.KindByte, 0)
|
||||||
|
},
|
||||||
|
text: "ADD a AND b GIVING c",
|
||||||
|
wantErr: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "wrong separator #5",
|
||||||
|
setup: func(ctx *compiler.CompilerContext) {
|
||||||
|
ctx.SymbolTable.AddVar("a", "", compiler.KindByte, 10)
|
||||||
|
ctx.SymbolTable.AddVar("b", "", compiler.KindByte, 20)
|
||||||
|
ctx.SymbolTable.AddVar("c", "", compiler.KindByte, 0)
|
||||||
|
},
|
||||||
|
text: "ADD a TO b INTO c",
|
||||||
|
wantErr: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "invalid expression",
|
||||||
|
setup: func(ctx *compiler.CompilerContext) {
|
||||||
|
ctx.SymbolTable.AddVar("c", "", compiler.KindByte, 0)
|
||||||
|
},
|
||||||
|
text: "ADD @#$% TO 10 GIVING c",
|
||||||
|
wantErr: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
ctx := compiler.NewCompilerContext(preproc.NewPragma())
|
||||||
|
if tt.setup != nil {
|
||||||
|
tt.setup(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd := &AddCommand{}
|
||||||
|
line := preproc.Line{Text: tt.text}
|
||||||
|
|
||||||
|
err := cmd.Interpret(line, ctx)
|
||||||
|
|
||||||
|
if (err != nil) != tt.wantErr {
|
||||||
|
t.Errorf("Interpret() error = %v, wantErr %v", err, tt.wantErr)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if !tt.wantErr && tt.check != nil {
|
||||||
|
tt.check(t, cmd)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAddCommand_Interpret_NewSyntax(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
setup func(*compiler.CompilerContext)
|
||||||
|
text string
|
||||||
|
wantErr bool
|
||||||
|
check func(*testing.T, *AddCommand)
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "dest = var1 + var2",
|
||||||
|
setup: func(ctx *compiler.CompilerContext) {
|
||||||
|
ctx.SymbolTable.AddVar("a", "", compiler.KindByte, 10)
|
||||||
|
ctx.SymbolTable.AddVar("b", "", compiler.KindByte, 20)
|
||||||
|
ctx.SymbolTable.AddVar("result", "", compiler.KindByte, 0)
|
||||||
|
},
|
||||||
|
text: "result = a + b",
|
||||||
|
wantErr: false,
|
||||||
|
check: func(t *testing.T, cmd *AddCommand) {
|
||||||
|
if cmd.destVarName != "result" {
|
||||||
|
t.Errorf("dest = %q, want 'result'", cmd.destVarName)
|
||||||
|
}
|
||||||
|
if !cmd.param1IsVar || cmd.param1VarName != "a" {
|
||||||
|
t.Errorf("param1 should be var 'a'")
|
||||||
|
}
|
||||||
|
if !cmd.param2IsVar || cmd.param2VarName != "b" {
|
||||||
|
t.Errorf("param2 should be var 'b'")
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "dest = literal + var",
|
||||||
|
setup: func(ctx *compiler.CompilerContext) {
|
||||||
|
ctx.SymbolTable.AddVar("x", "", compiler.KindByte, 5)
|
||||||
|
ctx.SymbolTable.AddVar("result", "", compiler.KindByte, 0)
|
||||||
|
},
|
||||||
|
text: "result = 100 + x",
|
||||||
|
wantErr: false,
|
||||||
|
check: func(t *testing.T, cmd *AddCommand) {
|
||||||
|
if cmd.param1IsVar {
|
||||||
|
t.Errorf("param1 should be literal")
|
||||||
|
}
|
||||||
|
if cmd.param1Value != 100 {
|
||||||
|
t.Errorf("param1 = %d, want 100", cmd.param1Value)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "dest = var + literal",
|
||||||
|
setup: func(ctx *compiler.CompilerContext) {
|
||||||
|
ctx.SymbolTable.AddVar("x", "", compiler.KindByte, 5)
|
||||||
|
ctx.SymbolTable.AddVar("result", "", compiler.KindByte, 0)
|
||||||
|
},
|
||||||
|
text: "result = x + 50",
|
||||||
|
wantErr: false,
|
||||||
|
check: func(t *testing.T, cmd *AddCommand) {
|
||||||
|
if cmd.param2IsVar {
|
||||||
|
t.Errorf("param2 should be literal")
|
||||||
|
}
|
||||||
|
if cmd.param2Value != 50 {
|
||||||
|
t.Errorf("param2 = %d, want 50", cmd.param2Value)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "dest = literal + literal",
|
||||||
|
setup: func(ctx *compiler.CompilerContext) {
|
||||||
|
ctx.SymbolTable.AddVar("result", "", compiler.KindByte, 0)
|
||||||
|
},
|
||||||
|
text: "result = 30 + 70",
|
||||||
|
wantErr: false,
|
||||||
|
check: func(t *testing.T, cmd *AddCommand) {
|
||||||
|
if cmd.param1IsVar || cmd.param2IsVar {
|
||||||
|
t.Errorf("both params should be literals")
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "word destination",
|
||||||
|
setup: func(ctx *compiler.CompilerContext) {
|
||||||
|
ctx.SymbolTable.AddVar("a", "", compiler.KindWord, 1000)
|
||||||
|
ctx.SymbolTable.AddVar("b", "", compiler.KindWord, 2000)
|
||||||
|
ctx.SymbolTable.AddVar("result", "", compiler.KindWord, 0)
|
||||||
|
},
|
||||||
|
text: "result = a + b",
|
||||||
|
wantErr: false,
|
||||||
|
check: func(t *testing.T, cmd *AddCommand) {
|
||||||
|
if cmd.destVarKind != compiler.KindWord {
|
||||||
|
t.Errorf("dest should be word")
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "unknown dest variable",
|
||||||
|
setup: func(ctx *compiler.CompilerContext) {
|
||||||
|
ctx.SymbolTable.AddVar("a", "", compiler.KindByte, 10)
|
||||||
|
ctx.SymbolTable.AddVar("b", "", compiler.KindByte, 20)
|
||||||
|
},
|
||||||
|
text: "result = a + b",
|
||||||
|
wantErr: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "assign to constant",
|
||||||
|
setup: func(ctx *compiler.CompilerContext) {
|
||||||
|
ctx.SymbolTable.AddVar("a", "", compiler.KindByte, 10)
|
||||||
|
ctx.SymbolTable.AddVar("b", "", compiler.KindByte, 20)
|
||||||
|
ctx.SymbolTable.AddConst("MAX", "", compiler.KindByte, 100)
|
||||||
|
},
|
||||||
|
text: "MAX = a + b",
|
||||||
|
wantErr: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "wrong operator (not +)",
|
||||||
|
setup: func(ctx *compiler.CompilerContext) {
|
||||||
|
ctx.SymbolTable.AddVar("a", "", compiler.KindByte, 10)
|
||||||
|
ctx.SymbolTable.AddVar("b", "", compiler.KindByte, 20)
|
||||||
|
ctx.SymbolTable.AddVar("result", "", compiler.KindByte, 0)
|
||||||
|
},
|
||||||
|
text: "result = a - b",
|
||||||
|
wantErr: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
ctx := compiler.NewCompilerContext(preproc.NewPragma())
|
||||||
|
if tt.setup != nil {
|
||||||
|
tt.setup(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd := &AddCommand{}
|
||||||
|
line := preproc.Line{Text: tt.text}
|
||||||
|
|
||||||
|
err := cmd.Interpret(line, ctx)
|
||||||
|
|
||||||
|
if (err != nil) != tt.wantErr {
|
||||||
|
t.Errorf("Interpret() error = %v, wantErr %v", err, tt.wantErr)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if !tt.wantErr && tt.check != nil {
|
||||||
|
tt.check(t, cmd)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAddCommand_Generate(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
setup func(*compiler.CompilerContext) *AddCommand
|
||||||
|
wantLines []string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "constant folding - both literals to byte",
|
||||||
|
setup: func(ctx *compiler.CompilerContext) *AddCommand {
|
||||||
|
ctx.SymbolTable.AddVar("result", "", compiler.KindByte, 0)
|
||||||
|
return &AddCommand{
|
||||||
|
param1IsVar: false,
|
||||||
|
param1Value: 10,
|
||||||
|
param2IsVar: false,
|
||||||
|
param2Value: 20,
|
||||||
|
destVarName: "result",
|
||||||
|
destVarKind: compiler.KindByte,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
wantLines: []string{
|
||||||
|
"\tlda #$1e",
|
||||||
|
"\tsta result",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "constant folding - both literals to word",
|
||||||
|
setup: func(ctx *compiler.CompilerContext) *AddCommand {
|
||||||
|
ctx.SymbolTable.AddVar("result", "", compiler.KindWord, 0)
|
||||||
|
return &AddCommand{
|
||||||
|
param1IsVar: false,
|
||||||
|
param1Value: 100,
|
||||||
|
param2IsVar: false,
|
||||||
|
param2Value: 200,
|
||||||
|
destVarName: "result",
|
||||||
|
destVarKind: compiler.KindWord,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
wantLines: []string{
|
||||||
|
"\tlda #$2c",
|
||||||
|
"\tsta result",
|
||||||
|
"\tlda #$01",
|
||||||
|
"\tsta result+1",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "constant folding with overflow",
|
||||||
|
setup: func(ctx *compiler.CompilerContext) *AddCommand {
|
||||||
|
ctx.SymbolTable.AddVar("result", "", compiler.KindWord, 0)
|
||||||
|
return &AddCommand{
|
||||||
|
param1IsVar: false,
|
||||||
|
param1Value: 200,
|
||||||
|
param2IsVar: false,
|
||||||
|
param2Value: 100,
|
||||||
|
destVarName: "result",
|
||||||
|
destVarKind: compiler.KindWord,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
wantLines: []string{
|
||||||
|
"\tlda #$2c",
|
||||||
|
"\tsta result",
|
||||||
|
"\tlda #$01",
|
||||||
|
"\tsta result+1",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "byte + byte -> byte",
|
||||||
|
setup: func(ctx *compiler.CompilerContext) *AddCommand {
|
||||||
|
ctx.SymbolTable.AddVar("a", "", compiler.KindByte, 10)
|
||||||
|
ctx.SymbolTable.AddVar("b", "", compiler.KindByte, 20)
|
||||||
|
ctx.SymbolTable.AddVar("result", "", compiler.KindByte, 0)
|
||||||
|
return &AddCommand{
|
||||||
|
param1IsVar: true,
|
||||||
|
param1VarName: "a",
|
||||||
|
param1VarKind: compiler.KindByte,
|
||||||
|
param2IsVar: true,
|
||||||
|
param2VarName: "b",
|
||||||
|
param2VarKind: compiler.KindByte,
|
||||||
|
destVarName: "result",
|
||||||
|
destVarKind: compiler.KindByte,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
wantLines: []string{
|
||||||
|
"\tclc",
|
||||||
|
"\tlda a",
|
||||||
|
"\tadc b",
|
||||||
|
"\tsta result",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "word + word -> word",
|
||||||
|
setup: func(ctx *compiler.CompilerContext) *AddCommand {
|
||||||
|
ctx.SymbolTable.AddVar("x", "", compiler.KindWord, 1000)
|
||||||
|
ctx.SymbolTable.AddVar("y", "", compiler.KindWord, 2000)
|
||||||
|
ctx.SymbolTable.AddVar("z", "", compiler.KindWord, 0)
|
||||||
|
return &AddCommand{
|
||||||
|
param1IsVar: true,
|
||||||
|
param1VarName: "x",
|
||||||
|
param1VarKind: compiler.KindWord,
|
||||||
|
param2IsVar: true,
|
||||||
|
param2VarName: "y",
|
||||||
|
param2VarKind: compiler.KindWord,
|
||||||
|
destVarName: "z",
|
||||||
|
destVarKind: compiler.KindWord,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
wantLines: []string{
|
||||||
|
"\tclc",
|
||||||
|
"\tlda x",
|
||||||
|
"\tadc y",
|
||||||
|
"\tsta z",
|
||||||
|
"\tlda x+1",
|
||||||
|
"\tadc y+1",
|
||||||
|
"\tsta z+1",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "byte + word -> word",
|
||||||
|
setup: func(ctx *compiler.CompilerContext) *AddCommand {
|
||||||
|
ctx.SymbolTable.AddVar("a", "", compiler.KindByte, 10)
|
||||||
|
ctx.SymbolTable.AddVar("w", "", compiler.KindWord, 1000)
|
||||||
|
ctx.SymbolTable.AddVar("result", "", compiler.KindWord, 0)
|
||||||
|
return &AddCommand{
|
||||||
|
param1IsVar: true,
|
||||||
|
param1VarName: "a",
|
||||||
|
param1VarKind: compiler.KindByte,
|
||||||
|
param2IsVar: true,
|
||||||
|
param2VarName: "w",
|
||||||
|
param2VarKind: compiler.KindWord,
|
||||||
|
destVarName: "result",
|
||||||
|
destVarKind: compiler.KindWord,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
wantLines: []string{
|
||||||
|
"\tclc",
|
||||||
|
"\tlda a",
|
||||||
|
"\tadc w",
|
||||||
|
"\tsta result",
|
||||||
|
"\tlda #0",
|
||||||
|
"\tadc w+1",
|
||||||
|
"\tsta result+1",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "literal + var -> byte",
|
||||||
|
setup: func(ctx *compiler.CompilerContext) *AddCommand {
|
||||||
|
ctx.SymbolTable.AddVar("a", "", compiler.KindByte, 10)
|
||||||
|
ctx.SymbolTable.AddVar("result", "", compiler.KindByte, 0)
|
||||||
|
return &AddCommand{
|
||||||
|
param1IsVar: false,
|
||||||
|
param1Value: 5,
|
||||||
|
param2IsVar: true,
|
||||||
|
param2VarName: "a",
|
||||||
|
param2VarKind: compiler.KindByte,
|
||||||
|
destVarName: "result",
|
||||||
|
destVarKind: compiler.KindByte,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
wantLines: []string{
|
||||||
|
"\tclc",
|
||||||
|
"\tlda #$05",
|
||||||
|
"\tadc a",
|
||||||
|
"\tsta result",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "var + literal -> word",
|
||||||
|
setup: func(ctx *compiler.CompilerContext) *AddCommand {
|
||||||
|
ctx.SymbolTable.AddVar("w", "", compiler.KindWord, 1000)
|
||||||
|
ctx.SymbolTable.AddVar("result", "", compiler.KindWord, 0)
|
||||||
|
return &AddCommand{
|
||||||
|
param1IsVar: true,
|
||||||
|
param1VarName: "w",
|
||||||
|
param1VarKind: compiler.KindWord,
|
||||||
|
param2IsVar: false,
|
||||||
|
param2Value: 300,
|
||||||
|
destVarName: "result",
|
||||||
|
destVarKind: compiler.KindWord,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
wantLines: []string{
|
||||||
|
"\tclc",
|
||||||
|
"\tlda w",
|
||||||
|
"\tadc #$2c",
|
||||||
|
"\tsta result",
|
||||||
|
"\tlda w+1",
|
||||||
|
"\tadc #$01",
|
||||||
|
"\tsta result+1",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "byte + byte -> word (promotion)",
|
||||||
|
setup: func(ctx *compiler.CompilerContext) *AddCommand {
|
||||||
|
ctx.SymbolTable.AddVar("a", "", compiler.KindByte, 10)
|
||||||
|
ctx.SymbolTable.AddVar("b", "", compiler.KindByte, 20)
|
||||||
|
ctx.SymbolTable.AddVar("result", "", compiler.KindWord, 0)
|
||||||
|
return &AddCommand{
|
||||||
|
param1IsVar: true,
|
||||||
|
param1VarName: "a",
|
||||||
|
param1VarKind: compiler.KindByte,
|
||||||
|
param2IsVar: true,
|
||||||
|
param2VarName: "b",
|
||||||
|
param2VarKind: compiler.KindByte,
|
||||||
|
destVarName: "result",
|
||||||
|
destVarKind: compiler.KindWord,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
wantLines: []string{
|
||||||
|
"\tclc",
|
||||||
|
"\tlda a",
|
||||||
|
"\tadc b",
|
||||||
|
"\tsta result",
|
||||||
|
"\tlda #0",
|
||||||
|
"\tadc #0",
|
||||||
|
"\tsta result+1",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
ctx := compiler.NewCompilerContext(preproc.NewPragma())
|
||||||
|
cmd := tt.setup(ctx)
|
||||||
|
|
||||||
|
got, err := cmd.Generate(ctx)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Generate() error = %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(got) != len(tt.wantLines) {
|
||||||
|
t.Errorf("Generate() got %d lines, want %d lines\nGot:\n%s\nWant:\n%s",
|
||||||
|
len(got), len(tt.wantLines),
|
||||||
|
strings.Join(got, "\n"), strings.Join(tt.wantLines, "\n"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := range got {
|
||||||
|
if got[i] != tt.wantLines[i] {
|
||||||
|
t.Errorf("Line %d:\ngot: %q\nwant: %q", i, got[i], tt.wantLines[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAddCommand_Scopes(t *testing.T) {
|
||||||
|
ctx := compiler.NewCompilerContext(preproc.NewPragma())
|
||||||
|
|
||||||
|
// Global variables
|
||||||
|
ctx.SymbolTable.AddVar("globalA", "", compiler.KindByte, 10)
|
||||||
|
ctx.SymbolTable.AddVar("globalB", "", compiler.KindByte, 20)
|
||||||
|
ctx.SymbolTable.AddVar("result", "", compiler.KindByte, 0)
|
||||||
|
|
||||||
|
// Simulate function declaration to enter scope
|
||||||
|
line := preproc.Line{Text: "FUNC myFunc"}
|
||||||
|
_, err := ctx.FunctionHandler.HandleFuncDecl(line)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("HandleFuncDecl() error = %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Function scope variables
|
||||||
|
ctx.SymbolTable.AddVar("localA", "myFunc", compiler.KindByte, 5)
|
||||||
|
ctx.SymbolTable.AddVar("localB", "myFunc", compiler.KindByte, 15)
|
||||||
|
|
||||||
|
// Test local variables
|
||||||
|
cmd := &AddCommand{}
|
||||||
|
cmdLine := preproc.Line{Text: "result = localA + localB"}
|
||||||
|
if err := cmd.Interpret(cmdLine, ctx); err != nil {
|
||||||
|
t.Fatalf("Interpret() error = %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if cmd.param1VarName != "myFunc_localA" {
|
||||||
|
t.Errorf("param1 = %q, want 'myFunc_localA'", cmd.param1VarName)
|
||||||
|
}
|
||||||
|
if cmd.param2VarName != "myFunc_localB" {
|
||||||
|
t.Errorf("param2 = %q, want 'myFunc_localB'", cmd.param2VarName)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test global variables while in function scope
|
||||||
|
cmd2 := &AddCommand{}
|
||||||
|
cmdLine2 := preproc.Line{Text: "result = globalA + globalB"}
|
||||||
|
if err := cmd2.Interpret(cmdLine2, ctx); err != nil {
|
||||||
|
t.Fatalf("Interpret() error = %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if cmd2.param1VarName != "globalA" {
|
||||||
|
t.Errorf("param1 = %q, want 'globalA'", cmd2.param1VarName)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -68,6 +68,7 @@ func (c *Compiler) Compile(lines []preproc.Line) ([]string, error) {
|
||||||
return nil, fmt.Errorf("%s:%d: %w", line.Filename, line.LineNo, err)
|
return nil, fmt.Errorf("%s:%d: %w", line.Filename, line.LineNo, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
codeOutput = append(codeOutput, fmt.Sprintf("; %s", line.Text))
|
||||||
codeOutput = append(codeOutput, asmLines...)
|
codeOutput = append(codeOutput, asmLines...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
7
main.go
7
main.go
|
|
@ -78,12 +78,7 @@ func registerCommands(comp *compiler.Compiler) {
|
||||||
|
|
||||||
comp.Registry().Register(&commands.ByteCommand{})
|
comp.Registry().Register(&commands.ByteCommand{})
|
||||||
comp.Registry().Register(&commands.WordCommand{})
|
comp.Registry().Register(&commands.WordCommand{})
|
||||||
|
comp.Registry().Register(&commands.AddCommand{})
|
||||||
// TODO: Add more commands as they're implemented:
|
|
||||||
// comp.Registry().Register(&commands.LetCommand{})
|
|
||||||
// comp.Registry().Register(&commands.IfCommand{})
|
|
||||||
// comp.Registry().Register(&commands.WhileCommand{})
|
|
||||||
// etc.
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func writeOutput(filename string, lines []string) error {
|
func writeOutput(filename string, lines []string) error {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue