mirror of
https://github.com/zellyn/go6502.git
synced 2025-04-09 00:36:59 +00:00
Pulling out parsing of macro calls.
This commit is contained in:
parent
1754dcf7a3
commit
006f18e51d
@ -68,11 +68,11 @@ func (a *Assembler) Load(filename string, prefix int) error {
|
||||
}
|
||||
return line.Errorf("unknown instruction: %s", line.Parse.Text())
|
||||
case inst.TypeMacroStart:
|
||||
|
||||
if err := a.readMacro(in, lineSources[0]); err != nil {
|
||||
return err
|
||||
}
|
||||
continue // no need to append
|
||||
return in.Errorf("macro start not (yet) implemented: %s", line)
|
||||
case inst.TypeMacroCall:
|
||||
macroCall++
|
||||
m, ok := a.Macros[in.Command]
|
||||
|
@ -17,15 +17,19 @@ type Context interface {
|
||||
SettingOff(name string) error
|
||||
Setting(name string) bool
|
||||
HasSetting(name string) bool
|
||||
AddMacroName(name string)
|
||||
HasMacroName(name string) bool
|
||||
}
|
||||
|
||||
type SimpleContext struct {
|
||||
symbols map[string]symbolValue
|
||||
addr int32
|
||||
lastLabel string
|
||||
clearMesg string // Saved message describing why Addr was cleared.
|
||||
highbit byte // OR-mask for ASCII high bit
|
||||
OnOff map[string]bool
|
||||
symbols map[string]symbolValue
|
||||
addr int32
|
||||
lastLabel string
|
||||
clearMesg string // Saved message describing why Addr was cleared.
|
||||
highbit byte // OR-mask for ASCII high bit
|
||||
onOff map[string]bool
|
||||
onOffDefaults map[string]bool
|
||||
macroNames map[string]bool
|
||||
}
|
||||
|
||||
type symbolValue struct {
|
||||
@ -98,16 +102,18 @@ func (sc *SimpleContext) RemoveChanged() {
|
||||
func (sc *SimpleContext) Clear() {
|
||||
sc.symbols = make(map[string]symbolValue)
|
||||
sc.highbit = 0x00
|
||||
sc.macroNames = nil
|
||||
sc.resetOnOff()
|
||||
}
|
||||
|
||||
func (sc *SimpleContext) SettingOn(name string) error {
|
||||
if !sc.HasSetting(name) {
|
||||
return fmt.Errorf("no settable variable named '%s'", name)
|
||||
}
|
||||
if sc.OnOff == nil {
|
||||
sc.OnOff = map[string]bool{name: true}
|
||||
if sc.onOff == nil {
|
||||
sc.onOff = map[string]bool{name: true}
|
||||
} else {
|
||||
sc.OnOff[name] = true
|
||||
sc.onOff[name] = true
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@ -116,19 +122,42 @@ func (sc *SimpleContext) SettingOff(name string) error {
|
||||
if !sc.HasSetting(name) {
|
||||
return fmt.Errorf("no settable variable named '%s'", name)
|
||||
}
|
||||
if sc.OnOff == nil {
|
||||
sc.OnOff = map[string]bool{name: false}
|
||||
if sc.onOff == nil {
|
||||
sc.onOff = map[string]bool{name: false}
|
||||
} else {
|
||||
sc.OnOff[name] = false
|
||||
sc.onOff[name] = false
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (sc *SimpleContext) Setting(name string) bool {
|
||||
return sc.OnOff[name]
|
||||
return sc.onOff[name]
|
||||
}
|
||||
|
||||
func (sc *SimpleContext) HasSetting(name string) bool {
|
||||
_, ok := sc.OnOff[name]
|
||||
_, ok := sc.onOff[name]
|
||||
return ok
|
||||
}
|
||||
|
||||
func (sc *SimpleContext) AddMacroName(name string) {
|
||||
if sc.macroNames == nil {
|
||||
sc.macroNames = make(map[string]bool)
|
||||
}
|
||||
sc.macroNames[name] = true
|
||||
}
|
||||
|
||||
func (sc *SimpleContext) HasMacroName(name string) bool {
|
||||
return sc.macroNames[name]
|
||||
}
|
||||
|
||||
func (sc *SimpleContext) resetOnOff() {
|
||||
sc.onOff = make(map[string]bool)
|
||||
for k, v := range sc.onOffDefaults {
|
||||
sc.onOff[k] = v
|
||||
}
|
||||
}
|
||||
|
||||
func (sc *SimpleContext) SetOnOffDefaults(defaults map[string]bool) {
|
||||
sc.onOffDefaults = defaults
|
||||
sc.resetOnOff()
|
||||
}
|
||||
|
@ -67,14 +67,14 @@ func New() *Merlin {
|
||||
"=": expr.OpEq,
|
||||
}
|
||||
|
||||
m.OnOff = map[string]bool{
|
||||
m.SetOnOffDefaults(map[string]bool{
|
||||
"LST": true, // Display listing: not used
|
||||
"XC": false, // Extended commands: not implemented yet
|
||||
"EXP": false, // How to print macro calls
|
||||
"LSTDO": false, // List conditional code?
|
||||
"TR": false, // truncate listing to 3 bytes?
|
||||
"CYC": false, // print cycle times?
|
||||
}
|
||||
})
|
||||
|
||||
m.SetAsciiVariation = func(in *inst.I, lp *lines.Parse) {
|
||||
if in.Command != "ASC" && in.Command != "DCI" {
|
||||
|
@ -19,9 +19,8 @@ const Letters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_"
|
||||
const Digits = "0123456789"
|
||||
const binarydigits = "01"
|
||||
const hexdigits = Digits + "abcdefABCDEF"
|
||||
const whitespace = " \t"
|
||||
const cmdChars = Letters + "."
|
||||
const macroNameChars = Letters + Digits + "._"
|
||||
const Whitespace = " \t"
|
||||
const cmdChars = Letters + Digits + ".>_"
|
||||
const fileChars = Letters + Digits + "."
|
||||
const operatorChars = "+-*/<>="
|
||||
|
||||
@ -50,15 +49,16 @@ type Base struct {
|
||||
LabelColons Requiredness
|
||||
ExplicitARegister Requiredness
|
||||
HexCommas Requiredness
|
||||
ExtraCommenty func(string) bool
|
||||
SpacesForComment int // this many spaces after command means it's the comment field
|
||||
StringEndOptional bool // can omit closing delimeter from string args?
|
||||
SetAsciiVariation func(*inst.I, *lines.Parse)
|
||||
CommentChar rune
|
||||
BinaryChar rune
|
||||
MsbChars string
|
||||
LsbChars string
|
||||
ImmediateChars string
|
||||
ExtraCommenty func(string) bool
|
||||
SetAsciiVariation func(*inst.I, *lines.Parse)
|
||||
ParseMacroCall func(inst.I, *lines.Parse) (inst.I, bool, error)
|
||||
}
|
||||
|
||||
// Parse an entire instruction, or return an appropriate error.
|
||||
@ -111,7 +111,7 @@ func (a *Base) ParseInstr(line lines.Line) (inst.I, error) {
|
||||
}
|
||||
|
||||
// Ignore whitespace at the start or after the label.
|
||||
lp.IgnoreRun(whitespace)
|
||||
lp.IgnoreRun(Whitespace)
|
||||
|
||||
if lp.Peek() == lines.Eol || lp.Peek() == a.CommentChar {
|
||||
in.Type = inst.TypeNone
|
||||
@ -131,14 +131,23 @@ func (a *Base) SetWidthsOnFirstPass() bool {
|
||||
// ParseCmd parses the "command" part of an instruction: we expect to be
|
||||
// looking at a non-whitespace character.
|
||||
func (a *Base) ParseCmd(in inst.I, lp *lines.Parse) (inst.I, error) {
|
||||
if lp.Consume(">") {
|
||||
return a.ParseMacroCall(in, lp)
|
||||
}
|
||||
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)
|
||||
}
|
||||
in.Command = lp.Emit()
|
||||
|
||||
// Give ParseMacroCall a chance
|
||||
if a.ParseMacroCall != nil {
|
||||
i, isMacro, err := a.ParseMacroCall(in, lp)
|
||||
if err != nil {
|
||||
return inst.I{}, err
|
||||
}
|
||||
if isMacro {
|
||||
return i, nil
|
||||
}
|
||||
}
|
||||
|
||||
if dir, ok := a.Directives[in.Command]; ok {
|
||||
in.Type = dir.Type
|
||||
in.Var = dir.Var
|
||||
@ -156,12 +165,13 @@ func (a *Base) ParseCmd(in inst.I, lp *lines.Parse) (inst.I, error) {
|
||||
in.Type = inst.TypeOp
|
||||
return a.ParseOpArgs(in, lp, summary)
|
||||
}
|
||||
|
||||
return inst.I{}, in.Errorf(`unknown command/instruction: "%s"`, in.Command)
|
||||
}
|
||||
|
||||
func (a *Base) ParseSetting(in inst.I, lp *lines.Parse) (inst.I, error) {
|
||||
in.Type = inst.TypeSetting
|
||||
lp.IgnoreRun(whitespace)
|
||||
lp.IgnoreRun(Whitespace)
|
||||
if !lp.AcceptRun(Letters) {
|
||||
c := lp.Next()
|
||||
return inst.I{}, in.Errorf("expecting ON/OFF, found '%s'", c)
|
||||
@ -179,39 +189,13 @@ func (a *Base) ParseSetting(in inst.I, lp *lines.Parse) (inst.I, error) {
|
||||
|
||||
}
|
||||
|
||||
// ParseMacroCall parses a macro call. We expect to be looking at a the
|
||||
// first character of the macro name.
|
||||
func (a *Base) ParseMacroCall(in inst.I, lp *lines.Parse) (inst.I, error) {
|
||||
in.Type = inst.TypeMacroCall
|
||||
if !lp.AcceptRun(macroNameChars) {
|
||||
c := lp.Next()
|
||||
return inst.I{}, in.Errorf("expecting macro name, found '%c' (%d)", c, c)
|
||||
}
|
||||
in.Command = lp.Emit()
|
||||
|
||||
lp.Consume(whitespace)
|
||||
|
||||
for {
|
||||
s, err := a.ParseMacroArg(in, lp)
|
||||
if err != nil {
|
||||
return inst.I{}, err
|
||||
}
|
||||
in.MacroArgs = append(in.MacroArgs, s)
|
||||
if !lp.Consume(",") {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return in, nil
|
||||
}
|
||||
|
||||
// ParseMacroArg parses a single macro argument. We expect to be looking at the first
|
||||
// character of a macro argument.
|
||||
func (a *Base) ParseMacroArg(in inst.I, lp *lines.Parse) (string, error) {
|
||||
if lp.Peek() == '"' {
|
||||
return a.ParseQuoted(in, lp)
|
||||
}
|
||||
lp.AcceptUntil(whitespace + ",")
|
||||
lp.AcceptUntil(Whitespace + ",")
|
||||
return lp.Emit(), nil
|
||||
}
|
||||
|
||||
@ -260,7 +244,7 @@ func (a *Base) ParseOpArgs(in inst.I, lp *lines.Parse, summary opcodes.OpSummary
|
||||
}
|
||||
|
||||
// Nothing else on the line? Must be MODE_A
|
||||
lp.AcceptRun(whitespace)
|
||||
lp.AcceptRun(Whitespace)
|
||||
ws := lp.Emit()
|
||||
atEnd := false
|
||||
if a.SpacesForComment != 0 && len(ws) >= a.SpacesForComment {
|
||||
@ -355,7 +339,7 @@ func (a *Base) ParseOpArgs(in inst.I, lp *lines.Parse, summary opcodes.OpSummary
|
||||
}
|
||||
|
||||
func (a *Base) ParseAddress(in inst.I, lp *lines.Parse) (inst.I, error) {
|
||||
lp.IgnoreRun(whitespace)
|
||||
lp.IgnoreRun(Whitespace)
|
||||
expr, err := a.ParseExpression(in, lp)
|
||||
if err != nil {
|
||||
return inst.I{}, err
|
||||
@ -369,7 +353,7 @@ func (a *Base) ParseAddress(in inst.I, lp *lines.Parse) (inst.I, error) {
|
||||
}
|
||||
|
||||
func (a *Base) ParseAscii(in inst.I, lp *lines.Parse) (inst.I, error) {
|
||||
lp.IgnoreRun(whitespace)
|
||||
lp.IgnoreRun(Whitespace)
|
||||
a.SetAsciiVariation(&in, lp)
|
||||
var invert, invertLast byte
|
||||
switch in.Var {
|
||||
@ -385,7 +369,7 @@ func (a *Base) ParseAscii(in inst.I, lp *lines.Parse) (inst.I, error) {
|
||||
panic(fmt.Sprintf("ParseAscii with weird Variation: %d", in.Var))
|
||||
}
|
||||
delim := lp.Next()
|
||||
if delim == lines.Eol || strings.IndexRune(whitespace, delim) >= 0 {
|
||||
if delim == lines.Eol || strings.IndexRune(Whitespace, delim) >= 0 {
|
||||
return inst.I{}, in.Errorf("%s expects delimeter, found '%s'", in.Command, delim)
|
||||
}
|
||||
lp.Ignore()
|
||||
@ -406,7 +390,7 @@ func (a *Base) ParseAscii(in inst.I, lp *lines.Parse) (inst.I, error) {
|
||||
}
|
||||
|
||||
func (a *Base) ParseBlockStorage(in inst.I, lp *lines.Parse) (inst.I, error) {
|
||||
lp.IgnoreRun(whitespace)
|
||||
lp.IgnoreRun(Whitespace)
|
||||
ex, err := a.ParseExpression(in, lp)
|
||||
if err != nil {
|
||||
return inst.I{}, err
|
||||
@ -416,7 +400,7 @@ func (a *Base) ParseBlockStorage(in inst.I, lp *lines.Parse) (inst.I, error) {
|
||||
}
|
||||
|
||||
func (a *Base) ParseData(in inst.I, lp *lines.Parse) (inst.I, error) {
|
||||
lp.IgnoreRun(whitespace)
|
||||
lp.IgnoreRun(Whitespace)
|
||||
for {
|
||||
ex, err := a.ParseExpression(in, lp)
|
||||
if err != nil {
|
||||
@ -431,7 +415,7 @@ func (a *Base) ParseData(in inst.I, lp *lines.Parse) (inst.I, error) {
|
||||
}
|
||||
|
||||
func (a *Base) ParseDo(in inst.I, lp *lines.Parse) (inst.I, error) {
|
||||
lp.IgnoreRun(whitespace)
|
||||
lp.IgnoreRun(Whitespace)
|
||||
expr, err := a.ParseExpression(in, lp)
|
||||
if err != nil {
|
||||
return inst.I{}, err
|
||||
@ -445,7 +429,7 @@ func (a *Base) ParseDo(in inst.I, lp *lines.Parse) (inst.I, error) {
|
||||
}
|
||||
|
||||
func (a *Base) ParseEquate(in inst.I, lp *lines.Parse) (inst.I, error) {
|
||||
lp.IgnoreRun(whitespace)
|
||||
lp.IgnoreRun(Whitespace)
|
||||
expr, err := a.ParseExpression(in, lp)
|
||||
if err != nil {
|
||||
return inst.I{}, err
|
||||
@ -459,7 +443,7 @@ func (a *Base) ParseEquate(in inst.I, lp *lines.Parse) (inst.I, error) {
|
||||
}
|
||||
|
||||
func (a *Base) ParseHexString(in inst.I, lp *lines.Parse) (inst.I, error) {
|
||||
lp.AcceptRun(whitespace)
|
||||
lp.AcceptRun(Whitespace)
|
||||
for {
|
||||
lp.Ignore()
|
||||
if !lp.AcceptRun(hexdigits) {
|
||||
@ -484,7 +468,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)
|
||||
lp.IgnoreRun(Whitespace)
|
||||
if !lp.AcceptRun(fileChars) {
|
||||
return inst.I{}, in.Errorf("Expecting filename, found '%c'", lp.Next())
|
||||
}
|
||||
@ -497,8 +481,8 @@ 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(macroNameChars) {
|
||||
lp.IgnoreRun(Whitespace)
|
||||
if !lp.AcceptRun(cmdChars) {
|
||||
return inst.I{}, in.Errorf("Expecting valid macro name, found '%c'", lp.Next())
|
||||
}
|
||||
in.TextArg = lp.Emit()
|
||||
|
@ -71,10 +71,10 @@ func newRedbook() *RedBook {
|
||||
"=": expr.OpEq,
|
||||
}
|
||||
|
||||
r.OnOff = map[string]bool{
|
||||
r.SetOnOffDefaults(map[string]bool{
|
||||
"MSB": true, // MSB defaults to true, as per manual
|
||||
"LST": true, // Display listing: not used
|
||||
}
|
||||
})
|
||||
|
||||
r.SetAsciiVariation = func(in *inst.I, lp *lines.Parse) {
|
||||
if in.Command == "ASC" {
|
||||
|
@ -80,6 +80,35 @@ func New() *SCMA {
|
||||
}
|
||||
}
|
||||
|
||||
// ParseMacroCall parses a macro call. We expect in.Command to hold
|
||||
// the "command column" value, which caused isMacroCall to return
|
||||
// true, and the lp to be pointing to the following character
|
||||
// (probably whitespace).
|
||||
a.ParseMacroCall = func(in inst.I, lp *lines.Parse) (inst.I, bool, error) {
|
||||
if in.Command == "" || in.Command[0] != '>' {
|
||||
// not a macro call
|
||||
return in, false, nil
|
||||
}
|
||||
|
||||
in.Type = inst.TypeMacroCall
|
||||
in.Command = in.Command[1:]
|
||||
|
||||
lp.Consume(oldschool.Whitespace)
|
||||
|
||||
for {
|
||||
s, err := a.ParseMacroArg(in, lp)
|
||||
if err != nil {
|
||||
return inst.I{}, true, err
|
||||
}
|
||||
in.MacroArgs = append(in.MacroArgs, s)
|
||||
if !lp.Consume(",") {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return in, true, nil
|
||||
}
|
||||
|
||||
return a
|
||||
}
|
||||
|
||||
|
@ -256,7 +256,7 @@ func TestMultiline(t *testing.T) {
|
||||
continue
|
||||
}
|
||||
if tt.b == "" && len(tt.ps) == 0 {
|
||||
t.Fatalf(`%d("%s"): test case must specify bytes or pieces`, i, tt.name)
|
||||
t.Fatalf(`%d("%s" - %T): test case must specify bytes or pieces`, i, tt.name, tt.a.Flavor)
|
||||
}
|
||||
tt.a.Reset()
|
||||
o.Clear()
|
||||
@ -265,46 +265,47 @@ func TestMultiline(t *testing.T) {
|
||||
o[k] = strings.Join(v, "\n")
|
||||
}
|
||||
if err := tt.a.Load("TESTFILE", 0); err != nil {
|
||||
t.Errorf(`%d("%s"): tt.a.Load("TESTFILE") failed: %s`, i, tt.name, err)
|
||||
t.Errorf(`%d("%s" - %T): tt.a.Load("TESTFILE") failed: %s`, i, tt.name, tt.a.Flavor, err)
|
||||
continue
|
||||
}
|
||||
if !tt.a.Flavor.SetWidthsOnFirstPass() {
|
||||
if _, err := tt.a.Pass(true, false); err != nil {
|
||||
t.Errorf(`%d("%s"): tt.a.Pass(true, false) failed: %s`, i, tt.name, err)
|
||||
t.Errorf(`%d("%s" - %T): tt.a.Pass(true, false) failed: %s`, i, tt.name, tt.a.Flavor, err)
|
||||
continue
|
||||
}
|
||||
}
|
||||
isFinal, err := tt.a.Pass(true, true)
|
||||
if err != nil {
|
||||
t.Errorf(`%d("%s"): tt.a.Pass(true, true) failed: %s`, i, tt.name, err)
|
||||
t.Errorf(`%d("%s" - %T): tt.a.Pass(true, true) failed: %s`, i, tt.name, tt.a.Flavor, err)
|
||||
continue
|
||||
}
|
||||
if !isFinal {
|
||||
t.Errorf(`%d("%s"): tt.a.Pass(true, true) couldn't finalize`, i, tt.name)
|
||||
t.Errorf(`%d("%s" - %T): tt.a.Pass(true, true) couldn't finalize`, i, tt.name, tt.a.Flavor)
|
||||
continue
|
||||
}
|
||||
|
||||
if tt.b != "" {
|
||||
bb, err := tt.a.RawBytes()
|
||||
if err != nil {
|
||||
t.Errorf(`%d("%s"): tt.a.RawBytes() failed: %s`, i, tt.name, err)
|
||||
t.Errorf(`%d("%s" - %T): tt.a.RawBytes() failed: %s`, i, tt.name, tt.a.Flavor, err)
|
||||
continue
|
||||
}
|
||||
hx := hex.EncodeToString(bb)
|
||||
if hx != tt.b {
|
||||
t.Errorf(`%d("%s"): tt.a.RawBytes()=[%s]; want [%s]`, i, tt.name, hx, tt.b)
|
||||
t.Errorf(`%d("%s" - %T): tt.a.RawBytes()=[%s]; want [%s]`, i, tt.name, tt.a.Flavor, hx, tt.b)
|
||||
continue
|
||||
}
|
||||
}
|
||||
if len(tt.ps) != 0 {
|
||||
m, err := tt.a.Membuf()
|
||||
if err != nil {
|
||||
t.Errorf(`%d("%s"): tt.a.Membuf() failed: %s`, i, tt.name, err)
|
||||
t.Errorf(`%d("%s" - %T): tt.a.Membuf() failed: %s`, i, tt.name, tt.a.Flavor, err)
|
||||
continue
|
||||
}
|
||||
ps := m.Pieces()
|
||||
if !reflect.DeepEqual(ps, tt.ps) {
|
||||
t.Errorf(`%d("%s"): tt.Membuf().Pieces() = %v; want %v`, i, tt.name, ps, tt.ps)
|
||||
t.Errorf(`%d("%s" - %T): tt.Membuf().Pieces() = %v; want %v`, i, tt.name, tt.a.Flavor, ps, tt.ps)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -169,7 +169,7 @@ func TestSimpleCommonFunctions(t *testing.T) {
|
||||
{ss, " .HS 0001FFAB", "{data/b}", "0001ffab"},
|
||||
{ss, " .IN FILE.NAME", "{inc 'FILE.NAME'}", ""},
|
||||
{ss, " .IN S.DEFS", "{inc 'S.DEFS'}", ""},
|
||||
{ss, " .MA MacroName", "{macro 'MacroName'}", ""},
|
||||
{ss, " .MA MacroName", `{macro "MacroName"}`, ""},
|
||||
{ss, " .OR $D000", "{org $d000}", ""},
|
||||
{ss, " .TF OUT.BIN", "{-}", ""},
|
||||
{ss, " .TI 76,Title here", "{-}", ""},
|
||||
|
@ -175,7 +175,7 @@ func (i I) String() string {
|
||||
s += fmt.Sprintf(" '%s'", i.Label)
|
||||
}
|
||||
if i.TextArg != "" {
|
||||
s += fmt.Sprintf(" '%s'", i.TextArg)
|
||||
s += fmt.Sprintf(` "%s"`, i.TextArg)
|
||||
}
|
||||
if len(i.MacroArgs) > 0 {
|
||||
ma := fmt.Sprintf("%#v", i.MacroArgs)[8:]
|
||||
|
Loading…
x
Reference in New Issue
Block a user