1
0
mirror of https://github.com/zellyn/go6502.git synced 2025-02-07 02:30:38 +00:00

Implemented macro-local labels for Merlin

This commit is contained in:
Zellyn Hunter 2014-07-23 08:13:53 -07:00
parent 6a51d73b66
commit 8f04a118ab
13 changed files with 100 additions and 69 deletions

View File

@ -154,6 +154,9 @@ func (a *Assembler) readMacro(in inst.I, ls lines.LineSource) error {
Name: in.TextArg,
Args: in.MacroArgs,
}
if a.Flavor.LocalMacroLabels() {
m.Locals = make(map[string]bool)
}
for {
line, done, err := ls.Next()
if err != nil {
@ -163,6 +166,9 @@ func (a *Assembler) readMacro(in inst.I, ls lines.LineSource) error {
return in.Errorf("end of file while reading macro %s", m.Name)
}
in2, err := a.Flavor.ParseInstr(line)
if a.Flavor.LocalMacroLabels() && in2.Label != "" {
m.Locals[in2.Label] = true
}
if err == nil && in2.Type == inst.TypeMacroEnd {
a.Macros[m.Name] = m
a.Flavor.AddMacroName(m.Name)

View File

@ -1,9 +1,10 @@
package context
/* Labeler is an interface that the flavors implement to handle label correction. */
type Labeler interface {
LastLabel() string
SetLastLabel(label string)
FixLabel(label string, macroCall int) (string, error)
FixLabel(label string, macroCall int, locals map[string]bool) (string, error)
IsNewParentLabel(label string) bool
}

View File

@ -207,20 +207,20 @@ func (e *E) CheckedEval(ctx context.Context, ln *lines.Line) (val uint16, labelM
}
// FixLabels attempts to turn .1 into LAST_LABEL.1, etc.
func (e *E) FixLabels(labeler context.Labeler, macroCall int, ln *lines.Line) error {
newL, err := labeler.FixLabel(e.Text, macroCall)
func (e *E) FixLabels(labeler context.Labeler, macroCall int, locals map[string]bool, ln *lines.Line) error {
newL, err := labeler.FixLabel(e.Text, macroCall, locals)
if err != nil {
return ln.Errorf("%v", err)
}
e.Text = newL
if e.Left != nil {
if err := e.Left.FixLabels(labeler, macroCall, ln); err != nil {
if err := e.Left.FixLabels(labeler, macroCall, locals, ln); err != nil {
return err
}
}
if e.Right != nil {
return e.Right.FixLabels(labeler, macroCall, ln)
return e.Right.FixLabels(labeler, macroCall, locals, ln)
}
return nil

View File

@ -45,6 +45,10 @@ func (a *AS65) IsNewParentLabel(label string) bool {
return label != "" && label[0] != '.'
}
func (a *AS65) FixLabel(label string, macroCall int) (string, error) {
func (a *AS65) FixLabel(label string, macroCall int, locals map[string]bool) (string, error) {
panic("AS65.FixLabel not implemented yet.")
}
func (a *AS65) LocalMacroLabels() bool {
return false
}

View File

@ -11,7 +11,6 @@ import (
// DecodeOp contains the common code that decodes an Opcode, once we
// have fully parsed it.
func DecodeOp(c context.Context, in inst.I, summary opcodes.OpSummary, indirect bool, xy rune, forceWide bool) (inst.I, error) {
i := inst.I{}
ex := in.Exprs[0]
// At this point we've parsed the Opcode. Let's see if it makes sense.
@ -20,10 +19,10 @@ func DecodeOp(c context.Context, in inst.I, summary opcodes.OpSummary, indirect
case 'x':
op, ok := summary.OpForMode(opcodes.MODE_INDIRECT_X)
if !ok {
return i, in.Errorf("%s doesn't support indexed indirect (addr,X) mode", in.Command)
return in, in.Errorf("%s doesn't support indexed indirect (addr,X) mode", in.Command)
}
if forceWide {
return i, in.Errorf("%s (addr,X) doesn't have a wide variant", in.Command)
return in, in.Errorf("%s (addr,X) doesn't have a wide variant", in.Command)
}
in.Op = op.Byte
in.WidthKnown = true
@ -34,10 +33,10 @@ func DecodeOp(c context.Context, in inst.I, summary opcodes.OpSummary, indirect
case 'y':
op, ok := summary.OpForMode(opcodes.MODE_INDIRECT_Y)
if !ok {
return i, in.Errorf("%s doesn't support indirect indexed (addr),Y mode", in.Command)
return in, in.Errorf("%s doesn't support indirect indexed (addr),Y mode", in.Command)
}
if forceWide {
return i, fmt.Errorf("%s (addr),Y doesn't have a wide variant", in.Command)
return in, fmt.Errorf("%s (addr),Y doesn't have a wide variant", in.Command)
}
in.WidthKnown = true
in.MinWidth = 2
@ -48,7 +47,7 @@ func DecodeOp(c context.Context, in inst.I, summary opcodes.OpSummary, indirect
default:
op, ok := summary.OpForMode(opcodes.MODE_INDIRECT)
if !ok {
return i, in.Errorf("%s doesn't support indirect (addr) mode", in.Command)
return in, in.Errorf("%s doesn't support indirect (addr) mode", in.Command)
}
in.Op = op.Byte
in.WidthKnown = true
@ -66,7 +65,7 @@ func DecodeOp(c context.Context, in inst.I, summary opcodes.OpSummary, indirect
panic(fmt.Sprintf("opcode error: %s has no MODE_RELATIVE opcode", in.Command))
}
if forceWide {
return i, fmt.Errorf("%s doesn't have a wide variant", in.Command)
return in, fmt.Errorf("%s doesn't have a wide variant", in.Command)
}
in.Op = op.Byte
@ -109,7 +108,7 @@ func DecodeOp(c context.Context, in inst.I, summary opcodes.OpSummary, indirect
opZp, zpOk := summary.OpForMode(zp)
if !summary.AnyModes(zp | wide) {
return i, in.Errorf("%s opcode doesn't support %s or %s modes.", zpS, wideS)
return in, in.Errorf("%s opcode doesn't support %s or %s modes.", zpS, wideS)
}
if !summary.AnyModes(zp) {
@ -128,7 +127,7 @@ func DecodeOp(c context.Context, in inst.I, summary opcodes.OpSummary, indirect
panic(fmt.Sprintf("opcode error: %s has no %s opcode", in.Command, zpS))
}
if forceWide {
return i, fmt.Errorf("%s doesn't have a wide variant", in.Command)
return in, fmt.Errorf("%s doesn't have a wide variant", in.Command)
}
in.Op = opZp.Byte
in.WidthKnown = true

View File

@ -11,6 +11,7 @@ type F interface {
DefaultOrigin() (uint16, error)
SetWidthsOnFirstPass() bool
ReplaceMacroArgs(line string, args []string, kwargs map[string]string) (string, error)
LocalMacroLabels() bool
context.Context
context.Labeler
}

View File

@ -140,7 +140,7 @@ func New() *Merlin {
for {
s, err := m.ParseMacroArg(in, lp)
if err != nil {
return inst.I{}, true, err
return in, true, err
}
in.MacroArgs = append(in.MacroArgs, s)
if !lp.Consume(";") {
@ -177,7 +177,7 @@ func (m *Merlin) ParseInclude(in inst.I, lp *lines.Parse) (inst.I, error) {
filename = strings.TrimSpace(filename[1:])
}
if filename == "" {
return inst.I{}, in.Errorf("%s expects filename", in.Command)
return in, in.Errorf("%s expects filename", in.Command)
}
in.TextArg = prefix + filename
in.WidthKnown = true
@ -187,15 +187,11 @@ func (m *Merlin) ParseInclude(in inst.I, lp *lines.Parse) (inst.I, error) {
return in, nil
}
func (m *Merlin) ReplaceMacroArgs(line string, args []string, kwargs map[string]string) (string, error) {
panic("Merlin.ReplaceMacroArgs not implemented yet.")
}
func (m *Merlin) IsNewParentLabel(label string) bool {
return label != "" && label[0] != ':'
}
func (m *Merlin) FixLabel(label string, macroCount int) (string, error) {
func (m *Merlin) FixLabel(label string, macroCount int, locals map[string]bool) (string, error) {
switch {
case label == "":
return label, nil
@ -205,6 +201,13 @@ func (m *Merlin) FixLabel(label string, macroCount int) (string, error) {
} else {
return fmt.Sprintf("%s/%s", last, label), nil
}
case locals[label]:
return fmt.Sprintf("%s{%d}", label, macroCount), nil
}
return label, nil
}
func (m *Merlin) LocalMacroLabels() bool {
return true
}

View File

@ -74,14 +74,14 @@ func (a *Base) ParseInstr(line lines.Line) (inst.I, error) {
if lp.AcceptRun(Digits) {
s := lp.Emit()
if len(s) != 4 {
return inst.I{}, line.Errorf("line number must be exactly 4 digits: %s", s)
return in, line.Errorf("line number must be exactly 4 digits: %s", s)
}
if !lp.Consume(" ") && lp.Peek() != lines.Eol {
return inst.I{}, line.Errorf("line number (%s) followed by non-space", s)
return in, line.Errorf("line number (%s) followed by non-space", s)
}
i, err := strconv.ParseUint(s, 10, 16)
if err != nil {
return inst.I{}, line.Errorf("invalid line number: %s: %s", s, err)
return in, line.Errorf("invalid line number: %s: %s", s, err)
}
in.DeclaredLine = uint16(i)
}
@ -107,7 +107,7 @@ func (a *Base) ParseInstr(line lines.Line) (inst.I, error) {
switch a.LabelColons {
case ReqRequired:
if !lp.Consume(":") {
return inst.I{}, line.Errorf("label '%s' must end in colon", in.Label)
return in, line.Errorf("label '%s' must end in colon", in.Label)
}
case ReqOptional:
lp.Consume(":")
@ -137,7 +137,7 @@ func (a *Base) SetWidthsOnFirstPass() bool {
func (a *Base) ParseCmd(in inst.I, lp *lines.Parse) (inst.I, error) {
if !lp.AcceptRun(cmdChars) && !(a.Directives["="].Func != nil && lp.Accept("=")) {
c := lp.Next()
return inst.I{}, in.Errorf("expecting instruction, found '%c' (%d)", c, c)
return in, in.Errorf("expecting instruction, found '%c' (%d)", c, c)
}
in.Command = lp.Emit()
@ -145,7 +145,7 @@ func (a *Base) ParseCmd(in inst.I, lp *lines.Parse) (inst.I, error) {
if a.ParseMacroCall != nil {
i, isMacro, err := a.ParseMacroCall(in, lp)
if err != nil {
return inst.I{}, err
return in, err
}
if isMacro {
return i, nil
@ -180,7 +180,7 @@ func (a *Base) ParseCmd(in inst.I, lp *lines.Parse) (inst.I, error) {
}
}
return inst.I{}, 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) {
@ -188,7 +188,7 @@ func (a *Base) ParseSetting(in inst.I, lp *lines.Parse) (inst.I, error) {
lp.IgnoreRun(Whitespace)
if !lp.AcceptRun(Letters) {
c := lp.Next()
return inst.I{}, in.Errorf("expecting ON/OFF, found '%s'", c)
return in, in.Errorf("expecting ON/OFF, found '%s'", c)
}
in.TextArg = lp.Emit()
switch in.TextArg {
@ -197,7 +197,7 @@ func (a *Base) ParseSetting(in inst.I, lp *lines.Parse) (inst.I, error) {
case "OFF":
a.SettingOff(in.Command)
default:
return inst.I{}, in.Errorf("expecting ON/OFF, found '%s'", in.TextArg)
return in, in.Errorf("expecting ON/OFF, found '%s'", in.TextArg)
}
return in, nil
@ -243,8 +243,6 @@ func (a *Base) ParseQuoted(in inst.I, lp *lines.Parse) (string, error) {
// ParseOpArgs parses the arguments to an assembly op. We expect to be
// 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) {
i := inst.I{}
// MODE_IMPLIED: we don't really care what comes next: it's a comment.
if summary.Modes == opcodes.MODE_IMPLIED {
op := summary.Ops[0]
@ -271,7 +269,7 @@ func (a *Base) ParseOpArgs(in inst.I, lp *lines.Parse, summary opcodes.OpSummary
if atEnd {
// Nothing left on line except comments.
if !summary.AnyModes(opcodes.MODE_A) {
return i, in.Errorf("%s with no arguments", in.Command)
return in, in.Errorf("%s with no arguments", in.Command)
}
op, ok := summary.OpForMode(opcodes.MODE_A)
if !ok {
@ -288,20 +286,20 @@ func (a *Base) ParseOpArgs(in inst.I, lp *lines.Parse, summary opcodes.OpSummary
indirect := lp.Consume("(")
if indirect && !summary.AnyModes(opcodes.MODE_INDIRECT_ANY) {
return i, 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 := '-'
expr, err := a.ParseExpression(in, lp)
if err != nil {
return i, err
return in, err
}
if !indirect && (expr.Text == "a" || expr.Text == "A") {
if !summary.AnyModes(opcodes.MODE_A) {
return i, in.Errorf("%s doesn't support A mode", in.Command)
return in, in.Errorf("%s doesn't support A mode", in.Command)
}
switch a.ExplicitARegister {
case ReqDisallowed:
return i, in.Errorf("Assembler flavor doesn't support A mode", in.Command)
return in, in.Errorf("Assembler flavor doesn't support A mode", in.Command)
case ReqOptional, ReqRequired:
op, ok := summary.OpForMode(opcodes.MODE_A)
if !ok {
@ -325,25 +323,25 @@ func (a *Base) ParseOpArgs(in inst.I, lp *lines.Parse, summary opcodes.OpSummary
xy = 'x'
} else if lp.Consume("yY") {
if indirect {
return i, in.Errorf(",Y unexpected inside parens")
return in, in.Errorf(",Y unexpected inside parens")
}
xy = 'y'
} else {
return i, in.Errorf("X or Y expected after comma")
return in, in.Errorf("X or Y expected after comma")
}
}
comma2 := false
if indirect {
if !lp.Consume(")") {
return i, in.Errorf("Expected closing paren")
return in, in.Errorf("Expected closing paren")
}
comma2 = lp.Consume(",")
if comma2 {
if comma {
return i, in.Errorf("Cannot have ,X or ,Y twice.")
return in, in.Errorf("Cannot have ,X or ,Y twice.")
}
if !lp.Consume("yY") {
return i, in.Errorf("Only ,Y can follow parens.")
return in, in.Errorf("Only ,Y can follow parens.")
}
xy = 'y'
}
@ -356,7 +354,7 @@ func (a *Base) ParseAddress(in inst.I, lp *lines.Parse) (inst.I, error) {
lp.IgnoreRun(Whitespace)
expr, err := a.ParseExpression(in, lp)
if err != nil {
return inst.I{}, err
return in, err
}
in.Exprs = append(in.Exprs, expr)
in.WidthKnown = true
@ -384,13 +382,13 @@ func (a *Base) ParseAscii(in inst.I, lp *lines.Parse) (inst.I, error) {
}
delim := lp.Next()
if delim == lines.Eol || strings.IndexRune(Whitespace, delim) >= 0 {
return inst.I{}, in.Errorf("%s expects delimeter, found '%s'", in.Command, delim)
return in, in.Errorf("%s expects delimeter, found '%s'", in.Command, delim)
}
lp.Ignore()
lp.AcceptUntil(string(delim))
delim2 := lp.Next()
if delim != delim2 && !(delim2 == lines.Eol && a.StringEndOptional) {
return inst.I{}, in.Errorf("%s: expected closing delimeter '%s'; got '%s'", in.Command, delim, delim2)
return in, in.Errorf("%s: expected closing delimeter '%s'; got '%s'", in.Command, delim, delim2)
}
lp.Backup()
in.Data = []byte(lp.Emit())
@ -407,7 +405,7 @@ func (a *Base) ParseBlockStorage(in inst.I, lp *lines.Parse) (inst.I, error) {
lp.IgnoreRun(Whitespace)
ex, err := a.ParseExpression(in, lp)
if err != nil {
return inst.I{}, err
return in, err
}
in.Exprs = append(in.Exprs, ex)
return in, nil
@ -418,7 +416,7 @@ func (a *Base) ParseData(in inst.I, lp *lines.Parse) (inst.I, error) {
for {
ex, err := a.ParseExpression(in, lp)
if err != nil {
return inst.I{}, err
return in, err
}
in.Exprs = append(in.Exprs, ex)
if !lp.Consume(",") {
@ -432,7 +430,7 @@ func (a *Base) ParseDo(in inst.I, lp *lines.Parse) (inst.I, error) {
lp.IgnoreRun(Whitespace)
expr, err := a.ParseExpression(in, lp)
if err != nil {
return inst.I{}, err
return in, err
}
in.Exprs = append(in.Exprs, expr)
in.WidthKnown = true
@ -446,7 +444,7 @@ func (a *Base) ParseEquate(in inst.I, lp *lines.Parse) (inst.I, error) {
lp.IgnoreRun(Whitespace)
expr, err := a.ParseExpression(in, lp)
if err != nil {
return inst.I{}, err
return in, err
}
in.Exprs = append(in.Exprs, expr)
in.WidthKnown = true
@ -461,15 +459,15 @@ func (a *Base) ParseHexString(in inst.I, lp *lines.Parse) (inst.I, error) {
for {
lp.Ignore()
if !lp.AcceptRun(hexdigits) {
return inst.I{}, in.Errorf("%s expects hex digits; got '%s'", in.Command, lp.Next())
return in, in.Errorf("%s expects hex digits; got '%s'", in.Command, lp.Next())
}
hs := lp.Emit()
if len(hs)%2 != 0 {
return inst.I{}, in.Errorf("%s expects pairs of hex digits; got %d", in.Command, len(hs))
return in, in.Errorf("%s expects pairs of hex digits; got %d", in.Command, len(hs))
}
data, err := hex.DecodeString(hs)
if err != nil {
return inst.I{}, in.Errorf("%s: error decoding hex string: %s", in.Command, err)
return in, in.Errorf("%s: error decoding hex string: %s", in.Command, err)
}
in.Data = append(in.Data, data...)
@ -484,7 +482,7 @@ func (a *Base) ParseHexString(in inst.I, lp *lines.Parse) (inst.I, error) {
func (a *Base) ParseInclude(in inst.I, lp *lines.Parse) (inst.I, error) {
lp.IgnoreRun(Whitespace)
if !lp.AcceptRun(fileChars) {
return inst.I{}, in.Errorf("Expecting filename, found '%c'", lp.Next())
return in, in.Errorf("Expecting filename, found '%c'", lp.Next())
}
in.TextArg = lp.Emit()
in.WidthKnown = true
@ -498,7 +496,7 @@ func (a *Base) ParseInclude(in inst.I, lp *lines.Parse) (inst.I, error) {
func (a *Base) ParseMacroStart(in inst.I, lp *lines.Parse) (inst.I, error) {
lp.IgnoreRun(Whitespace)
if !lp.AcceptRun(cmdChars) {
return inst.I{}, in.Errorf("Expecting valid macro name, found '%c'", lp.Next())
return in, in.Errorf("Expecting valid macro name, found '%c'", lp.Next())
}
in.TextArg = lp.Emit()
in.WidthKnown = true
@ -527,7 +525,7 @@ func (a *Base) ParseNoArgDir(in inst.I, lp *lines.Parse) (inst.I, error) {
}
func (a *Base) ParseNotImplemented(in inst.I, lp *lines.Parse) (inst.I, error) {
return inst.I{}, 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) {
@ -679,22 +677,23 @@ func (a *Base) ParseTerm(in inst.I, lp *lines.Parse) (*expr.E, error) {
var macroArgRe = regexp.MustCompile("][0-9]+")
func (a *Base) ReplaceMacroArgs(line string, args []string, kwargs map[string]string) (string, error) {
var err error
line = strings.Replace(line, "]#", strconv.Itoa(len(args)), -1)
line = string(macroArgRe.ReplaceAllFunc([]byte(line), func(in []byte) []byte {
n, _ := strconv.Atoi(string(in[1:]))
if n > 0 && n <= len(args) {
return []byte(args[n-1])
}
return []byte{}
return in
}))
return line, nil
return line, err
}
func (a *Base) IsNewParentLabel(label string) bool {
return label != "" && label[0] != '.'
}
func (a *Base) FixLabel(label string, macroCall int) (string, error) {
func (a *Base) FixLabel(label string, macroCall int, locals map[string]bool) (string, error) {
switch {
case label == "":
return label, nil

View File

@ -94,3 +94,7 @@ func newRedbook() *RedBook {
return r
}
func (r *RedBook) LocalMacroLabels() bool {
return false
}

View File

@ -100,7 +100,7 @@ func New() *SCMA {
for {
s, err := a.ParseMacroArg(in, lp)
if err != nil {
return inst.I{}, true, err
return in, true, err
}
in.MacroArgs = append(in.MacroArgs, s)
if !lp.Consume(",") {
@ -117,3 +117,7 @@ func New() *SCMA {
func (a *SCMA) Zero() (uint16, error) {
return uint16(0xffff), nil
}
func (a *SCMA) LocalMacroLabels() bool {
return false
}

View File

@ -223,8 +223,9 @@ func (i *I) Compute(c context.Context, setWidth bool, final bool) (bool, error)
// FixLabels attempts to turn .1 into LAST_LABEL.1, etc.
func (i *I) FixLabels(labeler context.Labeler) error {
macroCall := i.Line.GetMacroCall()
macroLocals := i.Line.GetMacroLocals()
parent := labeler.IsNewParentLabel(i.Label)
newL, err := labeler.FixLabel(i.Label, macroCall)
newL, err := labeler.FixLabel(i.Label, macroCall, macroLocals)
if err != nil {
return i.Errorf("%v", err)
}
@ -234,7 +235,7 @@ func (i *I) FixLabels(labeler context.Labeler) error {
}
for _, e := range i.Exprs {
if err := e.FixLabels(labeler, macroCall, i.Line); err != nil {
if err := e.FixLabels(labeler, macroCall, macroLocals, i.Line); err != nil {
return err
}
}

View File

@ -3,9 +3,10 @@ package lines
import "fmt"
type Context struct {
Filename string // Pointer to the filename
Parent *Line // Pointer to parent line (eg. include, macro)
MacroCall int
Filename string // Pointer to the filename
Parent *Line // Pointer to parent line (eg. include, macro)
MacroCall int
MacroLocals map[string]bool
}
type Line struct {
@ -45,6 +46,13 @@ func (c *Context) GetMacroCall() int {
return c.Parent.GetMacroCall()
}
func (l *Line) GetMacroLocals() map[string]bool {
if l == nil || l.Context == nil {
return nil
}
return l.Context.MacroLocals
}
func (l *Line) GetMacroCall() int {
if l == nil {
return 0

View File

@ -7,14 +7,15 @@ import (
)
type M struct {
Name string
Args []string
Lines []string
Name string
Args []string
Lines []string
Locals map[string]bool // labels that should be scoped to macro invocation
}
func (m M) LineSource(flavor flavors.F, in inst.I, macroCall int) (lines.LineSource, error) {
var ls []string
context := lines.Context{Filename: "macro:" + m.Name, Parent: in.Line, MacroCall: macroCall}
context := lines.Context{Filename: "macro:" + m.Name, Parent: in.Line, MacroCall: macroCall, MacroLocals: m.Locals}
for _, line := range m.Lines {
// TODO(zellyn): implement named macro args
subbed, err := flavor.ReplaceMacroArgs(line, in.MacroArgs, nil)