1
0
mirror of https://github.com/zellyn/go6502.git synced 2024-06-24 15:29:40 +00:00

Separated context out of Flavor

This commit is contained in:
Zellyn Hunter 2014-08-22 08:24:30 -07:00
parent 457446772a
commit c7308bf05c
11 changed files with 226 additions and 187 deletions

View File

@ -5,6 +5,7 @@ import (
"io" "io"
"path/filepath" "path/filepath"
"github.com/zellyn/go6502/asm/context"
"github.com/zellyn/go6502/asm/flavors" "github.com/zellyn/go6502/asm/flavors"
"github.com/zellyn/go6502/asm/inst" "github.com/zellyn/go6502/asm/inst"
"github.com/zellyn/go6502/asm/lines" "github.com/zellyn/go6502/asm/lines"
@ -17,13 +18,17 @@ type Assembler struct {
Opener lines.Opener Opener lines.Opener
Insts []*inst.I Insts []*inst.I
Macros map[string]macros.M Macros map[string]macros.M
Ctx *context.SimpleContext
} }
func NewAssembler(flavor flavors.F, opener lines.Opener) *Assembler { func NewAssembler(flavor flavors.F, opener lines.Opener) *Assembler {
ctx := &context.SimpleContext{}
flavor.InitContext(ctx)
return &Assembler{ return &Assembler{
Flavor: flavor, Flavor: flavor,
Opener: opener, Opener: opener,
Macros: make(map[string]macros.M), Macros: make(map[string]macros.M),
Ctx: ctx,
} }
} }
@ -47,7 +52,7 @@ func (a *Assembler) Load(filename string, prefix int) error {
lineSources = lineSources[1:] lineSources = lineSources[1:]
continue continue
} }
in, parseErr := a.Flavor.ParseInstr(line, false) in, parseErr := a.Flavor.ParseInstr(a.Ctx, line, false)
if len(ifdefs) > 0 && !ifdefs[0] && in.Type != inst.TypeIfdefElse && in.Type != inst.TypeIfdefEnd { if len(ifdefs) > 0 && !ifdefs[0] && in.Type != inst.TypeIfdefElse && in.Type != inst.TypeIfdefEnd {
// we're in an inactive ifdef branch // we're in an inactive ifdef branch
continue continue
@ -83,17 +88,17 @@ func (a *Assembler) Load(filename string, prefix int) error {
return in.Errorf(`error calling macro "%s": %v`, m.Name, err) return in.Errorf(`error calling macro "%s": %v`, m.Name, err)
} }
lineSources = append([]lines.LineSource{subLs}, lineSources...) lineSources = append([]lines.LineSource{subLs}, lineSources...)
a.Flavor.PushMacroCall(m.Name, macroCall, m.Locals) a.Ctx.PushMacroCall(m.Name, macroCall, m.Locals)
case inst.TypeMacroEnd: case inst.TypeMacroEnd:
// If we reached here, it's in a macro call, not a definition. // If we reached here, it's in a macro call, not a definition.
if !a.Flavor.PopMacroCall() { if !a.Ctx.PopMacroCall() {
return in.Errorf("unexpected end of macro") return in.Errorf("unexpected end of macro")
} }
case inst.TypeIfdef: case inst.TypeIfdef:
if len(in.Exprs) == 0 { if len(in.Exprs) == 0 {
panic(fmt.Sprintf("Ifdef got parsed with no expression: %s", line)) panic(fmt.Sprintf("Ifdef got parsed with no expression: %s", line))
} }
val, err := in.Exprs[0].Eval(a.Flavor, in.Line) val, err := in.Exprs[0].Eval(a.Ctx, in.Line)
if err != nil { if err != nil {
return in.Errorf("cannot eval ifdef condition: %v", err) return in.Errorf("cannot eval ifdef condition: %v", err)
} }
@ -163,14 +168,14 @@ func (a *Assembler) readMacro(in inst.I, ls lines.LineSource) error {
if done { if done {
return in.Errorf("end of file while reading macro %s", m.Name) return in.Errorf("end of file while reading macro %s", m.Name)
} }
in2, err := a.Flavor.ParseInstr(line, true) in2, err := a.Flavor.ParseInstr(a.Ctx, line, true)
if a.Flavor.LocalMacroLabels() && in2.Label != "" { if a.Flavor.LocalMacroLabels() && in2.Label != "" {
m.Locals[in2.Label] = true m.Locals[in2.Label] = true
} }
m.Lines = append(m.Lines, line.Parse.Text()) m.Lines = append(m.Lines, line.Parse.Text())
if err == nil && in2.Type == inst.TypeMacroEnd { if err == nil && in2.Type == inst.TypeMacroEnd {
a.Macros[m.Name] = m a.Macros[m.Name] = m
a.Flavor.AddMacroName(m.Name) a.Ctx.AddMacroName(m.Name)
return nil return nil
} }
} }
@ -178,9 +183,9 @@ func (a *Assembler) readMacro(in inst.I, ls lines.LineSource) error {
// Clear out stuff that may be hanging around from the previous pass, set origin to default, etc. // Clear out stuff that may be hanging around from the previous pass, set origin to default, etc.
func (a *Assembler) initPass() { func (a *Assembler) initPass() {
a.Flavor.SetLastLabel("") // No last label (yet) a.Ctx.SetLastLabel("") // No last label (yet)
a.Flavor.RemoveChanged() // Remove any variables whose value ever changed. a.Ctx.RemoveChanged() // Remove any variables whose value ever changed.
a.Flavor.SetAddr(a.Flavor.DefaultOrigin()) a.Ctx.SetAddr(a.Flavor.DefaultOrigin())
} }
// passInst performs a pass on a single instruction. It forces the // passInst performs a pass on a single instruction. It forces the
@ -188,16 +193,15 @@ func (a *Assembler) initPass() {
// arguments. If final is true, and the instruction cannot be // arguments. If final is true, and the instruction cannot be
// finalized, it returns an error. // finalized, it returns an error.
func (a *Assembler) passInst(in *inst.I, final bool) (isFinal bool, err error) { func (a *Assembler) passInst(in *inst.I, final bool) (isFinal bool, err error) {
// fmt.Printf("PLUGH: in.Compute(a.Flavor, true, true) on %s\n", in) isFinal, err = in.Compute(a.Ctx, final)
isFinal, err = in.Compute(a.Flavor, final)
if err != nil { if err != nil {
return false, err return false, err
} }
// Update address // Update address
addr, _ := a.Flavor.GetAddr() addr, _ := a.Ctx.GetAddr()
in.Addr = addr in.Addr = addr
a.Flavor.SetAddr(addr + in.Width) a.Ctx.SetAddr(addr + in.Width)
return isFinal, nil return isFinal, nil
} }

View File

@ -7,7 +7,8 @@ type Context interface {
Get(name string) (uint16, bool) Get(name string) (uint16, bool)
SetAddr(uint16) SetAddr(uint16)
GetAddr() (uint16, bool) GetAddr() (uint16, bool)
DivZero() (uint16, error) DivZero() *uint16
SetDivZero(uint16)
RemoveChanged() RemoveChanged()
Clear() Clear()
SettingOn(name string) error SettingOn(name string) error
@ -19,7 +20,7 @@ type Context interface {
PushMacroCall(name string, number int, locals map[string]bool) PushMacroCall(name string, number int, locals map[string]bool)
PopMacroCall() bool PopMacroCall() bool
GetMacroCall() (string, int, map[string]bool) GetMacroCall() (string, int, map[string]bool)
SetOnOffDefaults(map[string]bool)
LastLabel() string LastLabel() string
SetLastLabel(label string) SetLastLabel(label string)
} }
@ -39,6 +40,7 @@ type SimpleContext struct {
onOffDefaults map[string]bool onOffDefaults map[string]bool
macroNames map[string]bool macroNames map[string]bool
macroCalls []macroCall macroCalls []macroCall
divZeroVal *uint16
} }
type symbolValue struct { type symbolValue struct {
@ -52,10 +54,6 @@ func (sc *SimpleContext) fix() {
} }
} }
func (sc *SimpleContext) DivZero() (uint16, error) {
return 0, fmt.Errorf("Not implemented: context.SimpleContext.DivZero()")
}
func (sc *SimpleContext) Get(name string) (uint16, bool) { func (sc *SimpleContext) Get(name string) (uint16, bool) {
if name == "*" { if name == "*" {
return sc.GetAddr() return sc.GetAddr()
@ -185,3 +183,11 @@ func (sc *SimpleContext) LastLabel() string {
func (sc *SimpleContext) SetLastLabel(l string) { func (sc *SimpleContext) SetLastLabel(l string) {
sc.lastLabel = l sc.lastLabel = l
} }
func (sc *SimpleContext) DivZero() *uint16 {
return sc.divZeroVal
}
func (sc *SimpleContext) SetDivZero(val uint16) {
sc.divZeroVal = &val
}

View File

@ -174,7 +174,11 @@ func (e *E) Eval(ctx context.Context, ln *lines.Line) (uint16, error) {
return 0, nil return 0, nil
case OpDiv: case OpDiv:
if r == 0 { if r == 0 {
return ctx.DivZero() z := ctx.DivZero()
if z == nil {
return 0, ln.Errorf("divizion by zero")
}
return *z, nil
} }
return l / r, nil return l / r, nil
case OpAnd: case OpAnd:

View File

@ -11,16 +11,14 @@ import (
// AS65 implements the AS65-compatible assembler flavor. // AS65 implements the AS65-compatible assembler flavor.
// See http://www.kingswood-consulting.co.uk/assemblers/ // See http://www.kingswood-consulting.co.uk/assemblers/
type AS65 struct { type AS65 struct{}
context.SimpleContext
}
func New() *AS65 { func New() *AS65 {
return &AS65{} return &AS65{}
} }
// Parse an entire instruction, or return an appropriate error. // Parse an entire instruction, or return an appropriate error.
func (a *AS65) ParseInstr(line lines.Line, quick bool) (inst.I, error) { func (a *AS65) ParseInstr(ctx context.Context, line lines.Line, quick bool) (inst.I, error) {
return inst.I{}, nil return inst.I{}, nil
} }
@ -47,3 +45,6 @@ func (a *AS65) LocalMacroLabels() bool {
func (a *AS65) String() string { func (a *AS65) String() string {
return "as65" return "as65"
} }
func (a *AS65) InitContext(ctx context.Context) {
}

View File

@ -7,10 +7,10 @@ import (
) )
type F interface { type F interface {
ParseInstr(Line lines.Line, quick bool) (inst.I, error) ParseInstr(ctx context.Context, Line lines.Line, quick bool) (inst.I, error)
DefaultOrigin() uint16 DefaultOrigin() uint16
ReplaceMacroArgs(line string, args []string, kwargs map[string]string) (string, error) ReplaceMacroArgs(line string, args []string, kwargs map[string]string) (string, error)
LocalMacroLabels() bool LocalMacroLabels() bool
String() string String() string
context.Context InitContext(context.Context)
} }

View File

@ -1,10 +1,10 @@
package merlin package merlin
import ( import (
"errors"
"fmt" "fmt"
"strings" "strings"
"github.com/zellyn/go6502/asm/context"
"github.com/zellyn/go6502/asm/expr" "github.com/zellyn/go6502/asm/expr"
"github.com/zellyn/go6502/asm/flavors/oldschool" "github.com/zellyn/go6502/asm/flavors/oldschool"
"github.com/zellyn/go6502/asm/inst" "github.com/zellyn/go6502/asm/inst"
@ -38,6 +38,8 @@ func New() *Merlin {
m.InvCharChars = `"` m.InvCharChars = `"`
m.MacroArgSep = ";" m.MacroArgSep = ";"
m.SuffixForWide = true m.SuffixForWide = true
m.LocalMacroLabelsVal = true
m.DefaultOriginVal = 0x8000
m.Directives = map[string]oldschool.DirectiveInfo{ m.Directives = map[string]oldschool.DirectiveInfo{
"ORG": {inst.TypeOrg, m.ParseAddress, 0}, "ORG": {inst.TypeOrg, m.ParseAddress, 0},
@ -77,7 +79,8 @@ func New() *Merlin {
"!": expr.OpXor, "!": expr.OpXor,
} }
m.SetOnOffDefaults(map[string]bool{ m.InitContextFunc = func(ctx context.Context) {
ctx.SetOnOffDefaults(map[string]bool{
"LST": true, // Display listing: not used "LST": true, // Display listing: not used
"XC": false, // Extended commands: not implemented yet "XC": false, // Extended commands: not implemented yet
"EXP": false, // How to print macro calls "EXP": false, // How to print macro calls
@ -85,8 +88,9 @@ func New() *Merlin {
"TR": false, // truncate listing to 3 bytes? "TR": false, // truncate listing to 3 bytes?
"CYC": false, // print cycle times? "CYC": false, // print cycle times?
}) })
}
m.SetAsciiVariation = func(in *inst.I, lp *lines.Parse) { m.SetAsciiVariation = func(ctx context.Context, in *inst.I, lp *lines.Parse) {
if in.Command != "ASC" && in.Command != "DCI" { if in.Command != "ASC" && in.Command != "DCI" {
panic(fmt.Sprintf("Unimplemented/unknown ascii directive: '%s'", in.Command)) panic(fmt.Sprintf("Unimplemented/unknown ascii directive: '%s'", in.Command))
} }
@ -108,11 +112,11 @@ func New() *Merlin {
// the "command column" value, which caused isMacroCall to return // the "command column" value, which caused isMacroCall to return
// true, and the lp to be pointing to the following character // true, and the lp to be pointing to the following character
// (probably whitespace). // (probably whitespace).
m.ParseMacroCall = func(in inst.I, lp *lines.Parse) (inst.I, bool, error) { m.ParseMacroCall = func(ctx context.Context, in inst.I, lp *lines.Parse) (inst.I, bool, error) {
if in.Command == "" { if in.Command == "" {
return in, false, nil return in, false, nil
} }
byName := m.HasMacroName(in.Command) byName := ctx.HasMacroName(in.Command)
// It's not a macro call. // It's not a macro call.
if in.Command != ">>>" && in.Command != "PMC" && !byName { if in.Command != ">>>" && in.Command != "PMC" && !byName {
@ -127,7 +131,7 @@ func New() *Merlin {
return in, true, in.Errorf("Expected macro name, got char '%c'", c) return in, true, in.Errorf("Expected macro name, got char '%c'", c)
} }
in.Command = lp.Emit() in.Command = lp.Emit()
if !m.HasMacroName(in.Command) { if !ctx.HasMacroName(in.Command) {
return in, true, in.Errorf("Unknown macro: '%s'", in.Command) return in, true, in.Errorf("Unknown macro: '%s'", in.Command)
} }
if !lp.Consume(" ./,-(") { if !lp.Consume(" ./,-(") {
@ -153,13 +157,13 @@ func New() *Merlin {
return in, true, nil return in, true, nil
} }
m.FixLabel = func(label string) (string, error) { m.FixLabel = func(ctx context.Context, label string) (string, error) {
_, macroCount, locals := m.GetMacroCall() _, macroCount, locals := ctx.GetMacroCall()
switch { switch {
case label == "": case label == "":
return label, nil return label, nil
case label[0] == ':': case label[0] == ':':
if last := m.LastLabel(); last == "" { if last := ctx.LastLabel(); last == "" {
return "", fmt.Errorf("sublabel '%s' without previous label", label) return "", fmt.Errorf("sublabel '%s' without previous label", label)
} else { } else {
return fmt.Sprintf("%s/%s", last, label), nil return fmt.Sprintf("%s/%s", last, label), nil
@ -178,15 +182,7 @@ func New() *Merlin {
return m return m
} }
func (m *Merlin) Zero() (uint16, error) { func (m *Merlin) ParseInclude(ctx context.Context, in inst.I, lp *lines.Parse) (inst.I, error) {
return 0, errors.New("Division by zero.")
}
func (m *Merlin) DefaultOrigin() uint16 {
return 0x8000
}
func (m *Merlin) ParseInclude(in inst.I, lp *lines.Parse) (inst.I, error) {
lp.IgnoreRun(whitespace) lp.IgnoreRun(whitespace)
lp.AcceptUntil(";") lp.AcceptUntil(";")
filename := strings.TrimSpace(lp.Emit()) filename := strings.TrimSpace(lp.Emit())
@ -204,7 +200,3 @@ func (m *Merlin) ParseInclude(in inst.I, lp *lines.Parse) (inst.I, error) {
in.Final = true in.Final = true
return in, nil return in, nil
} }
func (m *Merlin) LocalMacroLabels() bool {
return true
}

View File

@ -2,6 +2,7 @@ package oldschool
import ( import (
"encoding/hex" "encoding/hex"
"errors"
"fmt" "fmt"
"regexp" "regexp"
"strconv" "strconv"
@ -25,7 +26,7 @@ const fileChars = Letters + Digits + "."
type DirectiveInfo struct { type DirectiveInfo struct {
Type inst.Type Type inst.Type
Func func(inst.I, *lines.Parse) (inst.I, error) Func func(context.Context, inst.I, *lines.Parse) (inst.I, error)
Var inst.Variant Var inst.Variant
} }
@ -43,7 +44,6 @@ type Base struct {
Name string Name string
Directives map[string]DirectiveInfo Directives map[string]DirectiveInfo
Operators map[string]expr.Operator Operators map[string]expr.Operator
context.SimpleContext
LabelChars string LabelChars string
LabelColons Requiredness LabelColons Requiredness
ExplicitARegister Requiredness ExplicitARegister Requiredness
@ -60,12 +60,15 @@ type Base struct {
CharChars string CharChars string
InvCharChars string InvCharChars string
MacroArgSep string MacroArgSep string
LocalMacroLabels bool
ExtraCommenty func(string) bool ExtraCommenty func(string) bool
SetAsciiVariation func(*inst.I, *lines.Parse) SetAsciiVariation func(context.Context, *inst.I, *lines.Parse)
ParseMacroCall func(inst.I, *lines.Parse) (inst.I, bool, error) ParseMacroCall func(context.Context, inst.I, *lines.Parse) (inst.I, bool, error)
FixLabel func(label string) (string, error)
IsNewParentLabel func(label string) bool IsNewParentLabel func(label string) bool
InitContextFunc func(context.Context)
FixLabel func(context.Context, string) (string, error)
LocalMacroLabelsVal bool
DivZeroVal *uint16
DefaultOriginVal uint16
} }
func (a *Base) String() string { func (a *Base) String() string {
@ -73,7 +76,7 @@ func (a *Base) String() string {
} }
// Parse an entire instruction, or return an appropriate error. // Parse an entire instruction, or return an appropriate error.
func (a *Base) ParseInstr(line lines.Line, quick bool) (inst.I, error) { func (a *Base) ParseInstr(ctx context.Context, line lines.Line, quick bool) (inst.I, error) {
lp := line.Parse lp := line.Parse
in := inst.I{Line: &line} in := inst.I{Line: &line}
@ -125,13 +128,13 @@ func (a *Base) ParseInstr(line lines.Line, quick bool) (inst.I, error) {
// If appropriate, set the last parent label. // If appropriate, set the last parent label.
if !quick { if !quick {
parent := a.IsNewParentLabel(in.Label) parent := a.IsNewParentLabel(in.Label)
newL, err := a.FixLabel(in.Label) newL, err := a.FixLabel(ctx, in.Label)
if err != nil { if err != nil {
return in, in.Errorf("%v", err) return in, in.Errorf("%v", err)
} }
in.Label = newL in.Label = newL
if parent { if parent {
a.SetLastLabel(in.Label) ctx.SetLastLabel(in.Label)
} }
} }
@ -142,16 +145,12 @@ func (a *Base) ParseInstr(line lines.Line, quick bool) (inst.I, error) {
in.Type = inst.TypeNone in.Type = inst.TypeNone
return in, nil return in, nil
} }
return a.parseCmd(in, lp, quick) return a.parseCmd(ctx, in, lp, quick)
}
func (a *Base) DefaultOrigin() uint16 {
return 0x0800
} }
// parseCmd parses the "command" part of an instruction: we expect to be // parseCmd parses the "command" part of an instruction: we expect to be
// looking at a non-whitespace character. // looking at a non-whitespace character.
func (a *Base) parseCmd(in inst.I, lp *lines.Parse, quick bool) (inst.I, error) { func (a *Base) parseCmd(ctx context.Context, in inst.I, lp *lines.Parse, quick bool) (inst.I, error) {
if !lp.AcceptRun(cmdChars) && !(a.Directives["="].Func != nil && lp.Accept("=")) { if !lp.AcceptRun(cmdChars) && !(a.Directives["="].Func != nil && lp.Accept("=")) {
c := lp.Next() c := lp.Next()
return in, in.Errorf("expecting instruction, found '%c' (%d)", c, c) return in, in.Errorf("expecting instruction, found '%c' (%d)", c, c)
@ -170,7 +169,7 @@ func (a *Base) parseCmd(in inst.I, lp *lines.Parse, quick bool) (inst.I, error)
} }
// Give ParseMacroCall a chance // Give ParseMacroCall a chance
if a.ParseMacroCall != nil { if a.ParseMacroCall != nil {
i, isMacro, err := a.ParseMacroCall(in, lp) i, isMacro, err := a.ParseMacroCall(ctx, in, lp)
if err != nil { if err != nil {
return in, err return in, err
} }
@ -185,16 +184,16 @@ func (a *Base) parseCmd(in inst.I, lp *lines.Parse, quick bool) (inst.I, error)
if dir.Func == nil { if dir.Func == nil {
return in, nil return in, nil
} }
return dir.Func(in, lp) return dir.Func(ctx, in, lp)
} }
if a.HasSetting(in.Command) { if ctx.HasSetting(in.Command) {
return a.parseSetting(in, lp) return a.parseSetting(ctx, in, lp)
} }
if summary, ok := opcodes.ByName[in.Command]; ok { if summary, ok := opcodes.ByName[in.Command]; ok {
in.Type = inst.TypeOp in.Type = inst.TypeOp
return a.parseOpArgs(in, lp, summary, false) return a.parseOpArgs(ctx, in, lp, summary, false)
} }
// Merlin lets you say "LDA:" or "LDA@" or "LDAZ" to force non-zero-page. // Merlin lets you say "LDA:" or "LDA@" or "LDAZ" to force non-zero-page.
@ -203,14 +202,14 @@ func (a *Base) parseCmd(in inst.I, lp *lines.Parse, quick bool) (inst.I, error)
if summary, ok := opcodes.ByName[prefix]; ok { if summary, ok := opcodes.ByName[prefix]; ok {
in.Command = prefix in.Command = prefix
in.Type = inst.TypeOp in.Type = inst.TypeOp
return a.parseOpArgs(in, lp, summary, true) return a.parseOpArgs(ctx, in, lp, summary, true)
} }
} }
return in, in.Errorf(`unknown command/instruction: "%s"`, in.Command) return in, in.Errorf(`unknown command/instruction: "%s"`, in.Command)
} }
func (a *Base) parseSetting(in inst.I, lp *lines.Parse) (inst.I, error) { func (a *Base) parseSetting(ctx context.Context, in inst.I, lp *lines.Parse) (inst.I, error) {
in.Type = inst.TypeSetting in.Type = inst.TypeSetting
lp.IgnoreRun(Whitespace) lp.IgnoreRun(Whitespace)
if !lp.AcceptRun(Letters) { if !lp.AcceptRun(Letters) {
@ -220,9 +219,9 @@ func (a *Base) parseSetting(in inst.I, lp *lines.Parse) (inst.I, error) {
in.TextArg = lp.Emit() in.TextArg = lp.Emit()
switch in.TextArg { switch in.TextArg {
case "ON": case "ON":
a.SettingOn(in.Command) ctx.SettingOn(in.Command)
case "OFF": case "OFF":
a.SettingOff(in.Command) ctx.SettingOff(in.Command)
default: default:
return in, in.Errorf("expecting ON/OFF, found '%s'", in.TextArg) return in, in.Errorf("expecting ON/OFF, found '%s'", in.TextArg)
} }
@ -269,7 +268,7 @@ func (a *Base) parseQuoted(in inst.I, lp *lines.Parse) (string, error) {
// parseOpArgs parses the arguments to an assembly op. We expect to be // parseOpArgs parses the arguments to an assembly op. We expect to be
// looking at the first non-op character (probably whitespace) // looking at the first non-op character (probably whitespace)
func (a *Base) parseOpArgs(in inst.I, lp *lines.Parse, summary opcodes.OpSummary, forceWide bool) (inst.I, error) { func (a *Base) parseOpArgs(ctx context.Context, in inst.I, lp *lines.Parse, summary opcodes.OpSummary, forceWide bool) (inst.I, error) {
// MODE_IMPLIED: we don't really care what comes next: it's a comment. // MODE_IMPLIED: we don't really care what comes next: it's a comment.
if summary.Modes == opcodes.MODE_IMPLIED { if summary.Modes == opcodes.MODE_IMPLIED {
op := summary.Ops[0] op := summary.Ops[0]
@ -314,7 +313,7 @@ func (a *Base) parseOpArgs(in inst.I, lp *lines.Parse, summary opcodes.OpSummary
return in, in.Errorf("%s doesn't support any indirect modes", in.Command) return in, in.Errorf("%s doesn't support any indirect modes", in.Command)
} }
xy := '-' xy := '-'
expr, err := a.ParseExpression(in, lp) expr, err := a.parseExpression(ctx, in, lp)
if err != nil { if err != nil {
return in, err return in, err
} }
@ -371,12 +370,12 @@ func (a *Base) parseOpArgs(in inst.I, lp *lines.Parse, summary opcodes.OpSummary
} }
} }
return common.DecodeOp(a, in, summary, indirect, xy, forceWide) return common.DecodeOp(ctx, in, summary, indirect, xy, forceWide)
} }
func (a *Base) ParseAddress(in inst.I, lp *lines.Parse) (inst.I, error) { func (a *Base) ParseAddress(ctx context.Context, in inst.I, lp *lines.Parse) (inst.I, error) {
lp.IgnoreRun(Whitespace) lp.IgnoreRun(Whitespace)
expr, err := a.ParseExpression(in, lp) expr, err := a.parseExpression(ctx, in, lp)
if err != nil { if err != nil {
return in, err return in, err
} }
@ -387,9 +386,9 @@ func (a *Base) ParseAddress(in inst.I, lp *lines.Parse) (inst.I, error) {
return in, nil return in, nil
} }
func (a *Base) ParseAscii(in inst.I, lp *lines.Parse) (inst.I, error) { func (a *Base) ParseAscii(ctx context.Context, in inst.I, lp *lines.Parse) (inst.I, error) {
lp.IgnoreRun(Whitespace) lp.IgnoreRun(Whitespace)
a.SetAsciiVariation(&in, lp) a.SetAsciiVariation(ctx, &in, lp)
var invert, invertLast byte var invert, invertLast byte
switch in.Var { switch in.Var {
case inst.VarAscii: case inst.VarAscii:
@ -424,9 +423,9 @@ func (a *Base) ParseAscii(in inst.I, lp *lines.Parse) (inst.I, error) {
return in, nil return in, nil
} }
func (a *Base) ParseBlockStorage(in inst.I, lp *lines.Parse) (inst.I, error) { func (a *Base) ParseBlockStorage(ctx context.Context, in inst.I, lp *lines.Parse) (inst.I, error) {
lp.IgnoreRun(Whitespace) lp.IgnoreRun(Whitespace)
ex, err := a.ParseExpression(in, lp) ex, err := a.parseExpression(ctx, in, lp)
if err != nil { if err != nil {
return in, err return in, err
} }
@ -434,10 +433,10 @@ func (a *Base) ParseBlockStorage(in inst.I, lp *lines.Parse) (inst.I, error) {
return in, nil return in, nil
} }
func (a *Base) ParseData(in inst.I, lp *lines.Parse) (inst.I, error) { func (a *Base) ParseData(ctx context.Context, in inst.I, lp *lines.Parse) (inst.I, error) {
lp.IgnoreRun(Whitespace) lp.IgnoreRun(Whitespace)
for { for {
ex, err := a.ParseExpression(in, lp) ex, err := a.parseExpression(ctx, in, lp)
if err != nil { if err != nil {
return in, err return in, err
} }
@ -449,9 +448,9 @@ func (a *Base) ParseData(in inst.I, lp *lines.Parse) (inst.I, error) {
return in, nil return in, nil
} }
func (a *Base) ParseDo(in inst.I, lp *lines.Parse) (inst.I, error) { func (a *Base) ParseDo(ctx context.Context, in inst.I, lp *lines.Parse) (inst.I, error) {
lp.IgnoreRun(Whitespace) lp.IgnoreRun(Whitespace)
expr, err := a.ParseExpression(in, lp) expr, err := a.parseExpression(ctx, in, lp)
if err != nil { if err != nil {
return in, err return in, err
} }
@ -462,12 +461,18 @@ func (a *Base) ParseDo(in inst.I, lp *lines.Parse) (inst.I, error) {
return in, nil return in, nil
} }
func (a *Base) ParseEquate(in inst.I, lp *lines.Parse) (inst.I, error) { func (a *Base) ParseEquate(ctx context.Context, in inst.I, lp *lines.Parse) (inst.I, error) {
lp.IgnoreRun(Whitespace) lp.IgnoreRun(Whitespace)
expr, err := a.ParseExpression(in, lp) expr, err := a.parseExpression(ctx, in, lp)
if err != nil { if err != nil {
return in, err return in, err
} }
xyzzy, err := expr.Eval(ctx, in.Line)
if err != nil {
return in, err
}
_ = xyzzy
in.Exprs = append(in.Exprs, expr) in.Exprs = append(in.Exprs, expr)
in.WidthKnown = true in.WidthKnown = true
in.Width = 0 in.Width = 0
@ -475,7 +480,7 @@ func (a *Base) ParseEquate(in inst.I, lp *lines.Parse) (inst.I, error) {
return in, nil return in, nil
} }
func (a *Base) ParseHexString(in inst.I, lp *lines.Parse) (inst.I, error) { func (a *Base) ParseHexString(ctx context.Context, in inst.I, lp *lines.Parse) (inst.I, error) {
lp.AcceptRun(Whitespace) lp.AcceptRun(Whitespace)
for { for {
lp.Ignore() lp.Ignore()
@ -500,7 +505,7 @@ func (a *Base) ParseHexString(in inst.I, lp *lines.Parse) (inst.I, error) {
return in, nil return in, nil
} }
func (a *Base) ParseInclude(in inst.I, lp *lines.Parse) (inst.I, error) { func (a *Base) ParseInclude(ctx context.Context, in inst.I, lp *lines.Parse) (inst.I, error) {
lp.IgnoreRun(Whitespace) lp.IgnoreRun(Whitespace)
if !lp.AcceptRun(fileChars) { if !lp.AcceptRun(fileChars) {
return in, in.Errorf("Expecting filename, found '%c'", lp.Next()) return in, in.Errorf("Expecting filename, found '%c'", lp.Next())
@ -513,7 +518,7 @@ func (a *Base) ParseInclude(in inst.I, lp *lines.Parse) (inst.I, error) {
} }
// For assemblers where the macro name follows the macro directive. // For assemblers where the macro name follows the macro directive.
func (a *Base) ParseMacroStart(in inst.I, lp *lines.Parse) (inst.I, error) { func (a *Base) ParseMacroStart(ctx context.Context, in inst.I, lp *lines.Parse) (inst.I, error) {
lp.IgnoreRun(Whitespace) lp.IgnoreRun(Whitespace)
if !lp.AcceptRun(cmdChars) { if !lp.AcceptRun(cmdChars) {
return in, in.Errorf("Expecting valid macro name, found '%c'", lp.Next()) return in, in.Errorf("Expecting valid macro name, found '%c'", lp.Next())
@ -526,7 +531,7 @@ func (a *Base) ParseMacroStart(in inst.I, lp *lines.Parse) (inst.I, error) {
} }
// For assemblers where the macro name is the label, followed by the directive. // For assemblers where the macro name is the label, followed by the directive.
func (a *Base) MarkMacroStart(in inst.I, lp *lines.Parse) (inst.I, error) { func (a *Base) MarkMacroStart(ctx context.Context, in inst.I, lp *lines.Parse) (inst.I, error) {
in.TextArg, in.Label = in.Label, "" in.TextArg, in.Label = in.Label, ""
in.WidthKnown = true in.WidthKnown = true
in.Width = 0 in.Width = 0
@ -534,18 +539,18 @@ func (a *Base) MarkMacroStart(in inst.I, lp *lines.Parse) (inst.I, error) {
return in, nil return in, nil
} }
func (a *Base) ParseNoArgDir(in inst.I, lp *lines.Parse) (inst.I, error) { func (a *Base) ParseNoArgDir(ctx context.Context, in inst.I, lp *lines.Parse) (inst.I, error) {
in.WidthKnown = true in.WidthKnown = true
in.Width = 0 in.Width = 0
in.Final = true in.Final = true
return in, nil return in, nil
} }
func (a *Base) ParseNotImplemented(in inst.I, lp *lines.Parse) (inst.I, error) { func (a *Base) ParseNotImplemented(ctx context.Context, in inst.I, lp *lines.Parse) (inst.I, error) {
return in, in.Errorf("not implemented (yet?): %s", in.Command) return in, in.Errorf("not implemented (yet?): %s", in.Command)
} }
func (a *Base) ParseExpression(in inst.I, lp *lines.Parse) (*expr.E, error) { func (a *Base) parseExpression(ctx context.Context, in inst.I, lp *lines.Parse) (*expr.E, error) {
if a.operatorChars == "" { if a.operatorChars == "" {
for k, _ := range a.Operators { for k, _ := range a.Operators {
@ -581,14 +586,14 @@ func (a *Base) ParseExpression(in inst.I, lp *lines.Parse) (*expr.E, error) {
} }
} }
tree, err := a.ParseTerm(in, lp) tree, err := a.ParseTerm(ctx, in, lp)
if err != nil { if err != nil {
return &expr.E{}, err return &expr.E{}, err
} }
for lp.Accept(a.operatorChars) { for lp.Accept(a.operatorChars) {
c := lp.Emit() c := lp.Emit()
right, err := a.ParseTerm(in, lp) right, err := a.ParseTerm(ctx, in, lp)
if err != nil { if err != nil {
return &expr.E{}, err return &expr.E{}, err
} }
@ -602,7 +607,7 @@ func (a *Base) ParseExpression(in inst.I, lp *lines.Parse) (*expr.E, error) {
return tree, nil return tree, nil
} }
func (a *Base) ParseTerm(in inst.I, lp *lines.Parse) (*expr.E, error) { func (a *Base) ParseTerm(ctx context.Context, in inst.I, lp *lines.Parse) (*expr.E, error) {
ex := &expr.E{} ex := &expr.E{}
top := ex top := ex
@ -688,7 +693,7 @@ func (a *Base) ParseTerm(in inst.I, lp *lines.Parse) (*expr.E, error) {
ex.Op = expr.OpLeaf ex.Op = expr.OpLeaf
ex.Text = lp.Emit() ex.Text = lp.Emit()
newL, err := a.FixLabel(ex.Text) newL, err := a.FixLabel(ctx, ex.Text)
if err != nil { if err != nil {
return &expr.E{}, in.Errorf("%v", err) return &expr.E{}, in.Errorf("%v", err)
} }
@ -715,18 +720,18 @@ func (a *Base) DefaultIsNewParentLabel(label string) bool {
return label != "" && label[0] != '.' return label != "" && label[0] != '.'
} }
func (a *Base) DefaultFixLabel(label string) (string, error) { func (a *Base) DefaultFixLabel(ctx context.Context, label string) (string, error) {
switch { switch {
case label == "": case label == "":
return label, nil return label, nil
case label[0] == '.': case label[0] == '.':
if last := a.LastLabel(); last == "" { if last := ctx.LastLabel(); last == "" {
return "", fmt.Errorf("sublabel '%s' without previous label", label) return "", fmt.Errorf("sublabel '%s' without previous label", label)
} else { } else {
return fmt.Sprintf("%s/%s", last, label), nil return fmt.Sprintf("%s/%s", last, label), nil
} }
case label[0] == ':': case label[0] == ':':
_, macroCall, _ := a.GetMacroCall() _, macroCall, _ := ctx.GetMacroCall()
if macroCall == 0 { if macroCall == 0 {
return "", fmt.Errorf("macro-local label '%s' seen outside macro", label) return "", fmt.Errorf("macro-local label '%s' seen outside macro", label)
} else { } else {
@ -735,3 +740,24 @@ func (a *Base) DefaultFixLabel(label string) (string, error) {
} }
return label, nil return label, nil
} }
func (a *Base) LocalMacroLabels() bool {
return a.LocalMacroLabelsVal
}
func (a *Base) Zero() (uint16, error) {
if a.DivZeroVal == nil {
return 0, errors.New("Division by zero.")
}
return *a.DivZeroVal, nil
}
func (a *Base) DefaultOrigin() uint16 {
return a.DefaultOriginVal
}
func (a *Base) InitContext(ctx context.Context) {
if a.InitContextFunc != nil {
a.InitContextFunc(ctx)
}
}

View File

@ -3,6 +3,7 @@ package redbook
import ( import (
"fmt" "fmt"
"github.com/zellyn/go6502/asm/context"
"github.com/zellyn/go6502/asm/expr" "github.com/zellyn/go6502/asm/expr"
"github.com/zellyn/go6502/asm/flavors/oldschool" "github.com/zellyn/go6502/asm/flavors/oldschool"
"github.com/zellyn/go6502/asm/inst" "github.com/zellyn/go6502/asm/inst"
@ -37,6 +38,7 @@ func newRedbook(name string) *RedBook {
r.MsbChars = "/" r.MsbChars = "/"
r.ImmediateChars = "#" r.ImmediateChars = "#"
r.HexCommas = oldschool.ReqOptional r.HexCommas = oldschool.ReqOptional
r.DefaultOriginVal = 0x0800
r.Directives = map[string]oldschool.DirectiveInfo{ r.Directives = map[string]oldschool.DirectiveInfo{
"ORG": {inst.TypeOrg, r.ParseAddress, 0}, "ORG": {inst.TypeOrg, r.ParseAddress, 0},
@ -67,14 +69,16 @@ func newRedbook(name string) *RedBook {
"=": expr.OpEq, "=": expr.OpEq,
} }
r.SetOnOffDefaults(map[string]bool{ r.InitContextFunc = func(ctx context.Context) {
ctx.SetOnOffDefaults(map[string]bool{
"MSB": true, // MSB defaults to true, as per manual "MSB": true, // MSB defaults to true, as per manual
"LST": true, // Display listing: not used "LST": true, // Display listing: not used
}) })
}
r.SetAsciiVariation = func(in *inst.I, lp *lines.Parse) { r.SetAsciiVariation = func(ctx context.Context, in *inst.I, lp *lines.Parse) {
if in.Command == "ASC" { if in.Command == "ASC" {
if r.Setting("MSB") { if ctx.Setting("MSB") {
in.Var = inst.VarAsciiHi in.Var = inst.VarAsciiHi
} else { } else {
in.Var = inst.VarAscii in.Var = inst.VarAscii
@ -93,7 +97,3 @@ func newRedbook(name string) *RedBook {
return r return r
} }
func (r *RedBook) LocalMacroLabels() bool {
return false
}

View File

@ -3,6 +3,7 @@ package scma
import ( import (
"strings" "strings"
"github.com/zellyn/go6502/asm/context"
"github.com/zellyn/go6502/asm/expr" "github.com/zellyn/go6502/asm/expr"
"github.com/zellyn/go6502/asm/flavors/oldschool" "github.com/zellyn/go6502/asm/flavors/oldschool"
"github.com/zellyn/go6502/asm/inst" "github.com/zellyn/go6502/asm/inst"
@ -29,6 +30,9 @@ func New() *SCMA {
a.ImmediateChars = "#" a.ImmediateChars = "#"
a.CharChars = "'" a.CharChars = "'"
a.MacroArgSep = "," a.MacroArgSep = ","
a.DefaultOriginVal = 0x0800
divZeroVal := uint16(0xffff)
a.DivZeroVal = &divZeroVal
a.Directives = map[string]oldschool.DirectiveInfo{ a.Directives = map[string]oldschool.DirectiveInfo{
".IN": {inst.TypeInclude, a.ParseInclude, 0}, ".IN": {inst.TypeInclude, a.ParseInclude, 0},
@ -67,7 +71,7 @@ func New() *SCMA {
return strings.HasPrefix(s, commentWhitespacePrefix) return strings.HasPrefix(s, commentWhitespacePrefix)
} }
a.SetAsciiVariation = func(in *inst.I, lp *lines.Parse) { a.SetAsciiVariation = func(ctx context.Context, in *inst.I, lp *lines.Parse) {
// For S-C Assembler, leading "-" flips high bit // For S-C Assembler, leading "-" flips high bit
invert := lp.Consume("-") invert := lp.Consume("-")
invertLast := in.Command == ".AT" invertLast := in.Command == ".AT"
@ -87,7 +91,7 @@ func New() *SCMA {
// the "command column" value, which caused isMacroCall to return // the "command column" value, which caused isMacroCall to return
// true, and the lp to be pointing to the following character // true, and the lp to be pointing to the following character
// (probably whitespace). // (probably whitespace).
a.ParseMacroCall = func(in inst.I, lp *lines.Parse) (inst.I, bool, error) { a.ParseMacroCall = func(ctx context.Context, in inst.I, lp *lines.Parse) (inst.I, bool, error) {
if in.Command == "" || in.Command[0] != '>' { if in.Command == "" || in.Command[0] != '>' {
// not a macro call // not a macro call
return in, false, nil return in, false, nil
@ -117,11 +121,3 @@ func New() *SCMA {
return a return a
} }
func (a *SCMA) Zero() (uint16, error) {
return uint16(0xffff), nil
}
func (a *SCMA) LocalMacroLabels() bool {
return false
}

View File

@ -2,7 +2,6 @@ package flavors
import ( import (
"encoding/hex" "encoding/hex"
"fmt"
"reflect" "reflect"
"strings" "strings"
"testing" "testing"
@ -24,15 +23,23 @@ func h(s string) []byte {
return b return b
} }
type asmFactory func() *asm.Assembler
func TestMultiline(t *testing.T) { func TestMultiline(t *testing.T) {
o := lines.NewTestOpener() o := lines.NewTestOpener()
ss := asm.NewAssembler(scma.New(), o) ss := asmFactory(func() *asm.Assembler {
ra := asm.NewAssembler(redbook.NewRedbookA(), o) return asm.NewAssembler(scma.New(), o)
mm := asm.NewAssembler(merlin.New(), o) })
ra := asmFactory(func() *asm.Assembler {
return asm.NewAssembler(redbook.NewRedbookA(), o)
})
mm := asmFactory(func() *asm.Assembler {
return asm.NewAssembler(merlin.New(), o)
})
tests := []struct { tests := []struct {
a *asm.Assembler // assembler af asmFactory // assembler factory
name string // name name string // name
i []string // main file: lines i []string // main file: lines
ii map[string][]string // other files: lines ii map[string][]string // other files: lines
@ -293,42 +300,41 @@ func TestMultiline(t *testing.T) {
if !tt.active { if !tt.active {
continue continue
} }
fmt.Println(tt.name) a := tt.af()
if tt.b == "" && len(tt.ps) == 0 { if tt.b == "" && len(tt.ps) == 0 {
t.Fatalf(`%d("%s" - %s): test case must specify bytes or pieces`, i, tt.name, tt.a.Flavor) t.Fatalf(`%d("%s" - %s): test case must specify bytes or pieces`, i, tt.name, a.Flavor)
} }
tt.a.Reset()
o.Clear() o.Clear()
o["TESTFILE"] = strings.Join(tt.i, "\n") o["TESTFILE"] = strings.Join(tt.i, "\n")
for k, v := range tt.ii { for k, v := range tt.ii {
o[k] = strings.Join(v, "\n") o[k] = strings.Join(v, "\n")
} }
if err := tt.a.Load("TESTFILE", 0); err != nil { if err := a.Load("TESTFILE", 0); err != nil {
t.Fatalf(`%d("%s" - %s): tt.a.Load("TESTFILE") failed: %s`, i, tt.name, tt.a.Flavor, err) t.Fatalf(`%d("%s" - %s): a.Load("TESTFILE") failed: %s`, i, tt.name, a.Flavor, err)
} }
err := tt.a.Pass2() err := a.Pass2()
if err != nil { if err != nil {
t.Fatalf(`%d("%s" - %s): tt.a.Pass(true) failed: %s`, i, tt.name, tt.a.Flavor, err) t.Fatalf(`%d("%s" - %s): a.Pass(true) failed: %s`, i, tt.name, a.Flavor, err)
} }
if tt.b != "" { if tt.b != "" {
bb, err := tt.a.RawBytes() bb, err := a.RawBytes()
if err != nil { if err != nil {
t.Fatalf(`%d("%s" - %s): tt.a.RawBytes() failed: %s`, i, tt.name, tt.a.Flavor, err) t.Fatalf(`%d("%s" - %s): a.RawBytes() failed: %s`, i, tt.name, a.Flavor, err)
} }
hx := hex.EncodeToString(bb) hx := hex.EncodeToString(bb)
if hx != tt.b { if hx != tt.b {
t.Fatalf(`%d("%s" - %s): tt.a.RawBytes()=[%s]; want [%s]`, i, tt.name, tt.a.Flavor, hx, tt.b) t.Fatalf(`%d("%s" - %s): a.RawBytes()=[%s]; want [%s]`, i, tt.name, a.Flavor, hx, tt.b)
} }
} }
if len(tt.ps) != 0 { if len(tt.ps) != 0 {
m, err := tt.a.Membuf() m, err := a.Membuf()
if err != nil { if err != nil {
t.Fatalf(`%d("%s" - %s): tt.a.Membuf() failed: %s`, i, tt.name, tt.a.Flavor, err) t.Fatalf(`%d("%s" - %s): a.Membuf() failed: %s`, i, tt.name, a.Flavor, err)
} }
ps := m.Pieces() ps := m.Pieces()
if !reflect.DeepEqual(ps, tt.ps) { if !reflect.DeepEqual(ps, tt.ps) {
t.Fatalf(`%d("%s" - %s): tt.Membuf().Pieces() = %v; want %v`, i, tt.name, tt.a.Flavor, ps, tt.ps) t.Fatalf(`%d("%s" - %s): tt.Membuf().Pieces() = %v; want %v`, i, tt.name, a.Flavor, ps, tt.ps)
} }
} }
} }

View File

@ -4,6 +4,7 @@ import (
"encoding/hex" "encoding/hex"
"testing" "testing"
"github.com/zellyn/go6502/asm/context"
"github.com/zellyn/go6502/asm/flavors" "github.com/zellyn/go6502/asm/flavors"
"github.com/zellyn/go6502/asm/flavors/as65" "github.com/zellyn/go6502/asm/flavors/as65"
"github.com/zellyn/go6502/asm/flavors/merlin" "github.com/zellyn/go6502/asm/flavors/merlin"
@ -20,7 +21,7 @@ func TestSimpleCommonFunctions(t *testing.T) {
mm := merlin.New() mm := merlin.New()
tests := []struct { tests := []struct {
a flavors.F // assembler flavor f flavors.F // assembler flavor
i string // input string i string // input string
p string // printed instruction, expected p string // printed instruction, expected
b string // bytes, expected b string // bytes, expected
@ -209,6 +210,7 @@ func TestSimpleCommonFunctions(t *testing.T) {
{ss, ` .AT -DABCD`, "{data/b}", "c1c243"}, {ss, ` .AT -DABCD`, "{data/b}", "c1c243"},
{ss, ` .AT /ABC/`, "{data/b}", "4142c3"}, {ss, ` .AT /ABC/`, "{data/b}", "4142c3"},
{ss, `>SAM AB,$12,"A B","A, B, "" C"`, `{call SAM {"AB", "$12", "A B", "A, B, \" C"}}`, ""}, {ss, `>SAM AB,$12,"A B","A, B, "" C"`, `{call SAM {"AB", "$12", "A B", "A, B, \" C"}}`, ""},
// {ss, " LDA #3/0", "{LDA/imm (lsb (/ $0003 $0000))}", "a9ff"},
} }
// TODO(zellyn): Add tests for finalization of four SCMA directives: // TODO(zellyn): Add tests for finalization of four SCMA directives:
@ -220,30 +222,32 @@ func TestSimpleCommonFunctions(t *testing.T) {
// TODO(zellyn): Test AS65 and Merlin too. // TODO(zellyn): Test AS65 and Merlin too.
// Initialize to a known state for testing. // Initialize to a known state for testing.
tt.a.Clear() ctx := &context.SimpleContext{}
tt.a.SetAddr(0x2345) ctx.Clear()
tt.a.Set("A.B", 0x6789) ctx.SetAddr(0x2345)
tt.a.Set("C.D", 0x789a) ctx.Set("A.B", 0x6789)
tt.a.Set("L2", 0x6789) ctx.Set("C.D", 0x789a)
tt.a.Set("L3", 0x789a) ctx.Set("L2", 0x6789)
tt.a.AddMacroName("INCW") ctx.Set("L3", 0x789a)
tt.a.AddMacroName("M1") ctx.AddMacroName("INCW")
ctx.AddMacroName("M1")
tt.f.InitContext(ctx)
inst, err := tt.a.ParseInstr(lines.NewSimple(tt.i), false) inst, err := tt.f.ParseInstr(ctx, lines.NewSimple(tt.i), false)
if err != nil { if err != nil {
t.Errorf(`%d. %s.ParseInstr("%s") => error: %s`, i, tt.a, tt.i, err) t.Errorf(`%d. %s.ParseInstr("%s") => error: %s`, i, tt.f, tt.i, err)
continue continue
} }
if inst.Line.Parse == nil { if inst.Line.Parse == nil {
t.Errorf("Got empty inst.Line.Parse on input '%s'", tt.i) t.Errorf("Got empty inst.Line.Parse on input '%s'", tt.i)
} }
_, err = inst.Compute(tt.a, true) _, err = inst.Compute(ctx, true)
if err != nil { if err != nil {
t.Errorf(`%d. %s.ParseInstr("%s"): %s.Compute(tt.a, true) => error: %s`, i, tt.a, tt.i, inst, err) t.Errorf(`%d. %s.ParseInstr("%s"): %s.Compute(tt.f, true) => error: %s`, i, tt.f, tt.i, inst, err)
continue continue
} }
if inst.String() != tt.p { if inst.String() != tt.p {
t.Errorf(`%d. %s.ParseInstr("%s") = %s; want %s`, i, tt.a, tt.i, inst.String(), tt.p) t.Errorf(`%d. %s.ParseInstr("%s") = %s; want %s`, i, tt.f, tt.i, inst.String(), tt.p)
continue continue
} }
@ -251,7 +255,7 @@ func TestSimpleCommonFunctions(t *testing.T) {
hx := hex.EncodeToString(inst.Data) hx := hex.EncodeToString(inst.Data)
// xxxxxx sets the width, but doesn't expect actual data // xxxxxx sets the width, but doesn't expect actual data
if hx != tt.b && (len(tt.b) == 0 || tt.b[0] != 'x') { if hx != tt.b && (len(tt.b) == 0 || tt.b[0] != 'x') {
t.Errorf(`%d. %s.ParseInstr("%s").Data = [%s]; want [%s]`, i, tt.a, tt.i, hx, tt.b) t.Errorf(`%d. %s.ParseInstr("%s").Data = [%s]; want [%s]`, i, tt.f, tt.i, hx, tt.b)
continue continue
} }
@ -275,7 +279,7 @@ func TestSimpleErrors(t *testing.T) {
mm := merlin.New() mm := merlin.New()
tests := []struct { tests := []struct {
a flavors.F // assembler flavor f flavors.F // assembler flavor
i string // input string i string // input string
}{ }{
@ -289,13 +293,13 @@ func TestSimpleErrors(t *testing.T) {
} }
for i, tt := range tests { for i, tt := range tests {
// TODO(zellyn): Test AS65 and Merlin too. if tt.f != ss {
if tt.a != ss {
continue continue
} }
inst, err := tt.a.ParseInstr(lines.NewSimple(tt.i), false) ctx := &context.SimpleContext{}
inst, err := tt.f.ParseInstr(ctx, lines.NewSimple(tt.i), false)
if err == nil { if err == nil {
t.Errorf(`%d. %s.ParseInstr("%s") want err; got %s`, i, tt.a, tt.i, inst) t.Errorf(`%d. %s.ParseInstr("%s") want err; got %s`, i, tt.f, tt.i, inst)
continue continue
} }
} }