[dev.cc] cmd/asm: rewrite the macro processor

The previous one was too broken, so just rewrite the code that invokes
a macro. Basically it was evaluating things too early, and mishandling
nested invocations. It's also easier to understand now.

Keep backslash-newline around in macro definitions. They get
processed when the body is evaluated.

Write some golden tests.

Change-Id: I27435f77f258a0873f80932bdc8d13ad39821ac1
Reviewed-on: https://go-review.googlesource.com/3550
Reviewed-by: Russ Cox <rsc@golang.org>
This commit is contained in:
Rob Pike 2015-01-29 16:33:36 -08:00
parent b528063099
commit d6eb21e331
4 changed files with 174 additions and 33 deletions

View file

@ -229,10 +229,6 @@ func (in *Input) macroDefinition(name string) ([]string, []Token) {
if tok != '\n' && tok != '\\' {
in.Error(`can only escape \ or \n in definition for macro:`, name)
}
if tok == '\n' { // backslash-newline is discarded
tok = in.Stack.Next()
continue
}
}
tokens = append(tokens, Make(tok, in.Text()))
tok = in.Stack.Next()
@ -279,35 +275,50 @@ func (in *Input) argsFor(macro *Macro) map[string][]Token {
if tok != '(' {
in.Error("missing arguments for invocation of macro:", macro.name)
}
var tokens []Token
args := make(map[string][]Token)
argNum := 0
for {
tok = in.Stack.Next()
switch tok {
case scanner.EOF, '\n':
in.Error("unterminated arg list invoking macro:", macro.name)
case ',', ')':
if argNum >= len(macro.args) {
in.Error("too many arguments for macro:", macro.name)
}
if len(macro.args) == 0 && argNum == 0 && len(tokens) == 0 {
// Zero-argument macro invoked with no arguments.
return args
}
args[macro.args[argNum]] = tokens
tokens = nil
argNum++
if tok == ')' {
if argNum != len(macro.args) {
in.Error("too few arguments for macro:", macro.name)
}
return args
}
default:
tokens = append(tokens, Make(tok, in.Stack.Text()))
var args [][]Token
// One macro argument per iteration. Collect them all and check counts afterwards.
for argNum := 0; ; argNum++ {
tokens, tok := in.collectArgument(macro)
args = append(args, tokens)
if tok == ')' {
break
}
}
// Zero-argument macros are tricky.
if len(macro.args) == 0 && len(args) == 1 && args[0] == nil {
args = nil
} else if len(args) != len(macro.args) {
in.Error("wrong arg count for macro", macro.name)
}
argMap := make(map[string][]Token)
for i, arg := range args {
argMap[macro.args[i]] = arg
}
return argMap
}
// collectArgument returns the actual tokens for a single argument of a macro.
// It also returns the token that terminated the argument, which will always
// be either ',' or ')'. The starting '(' has been scanned.
func (in *Input) collectArgument(macro *Macro) ([]Token, ScanToken) {
nesting := 0
var tokens []Token
for {
tok := in.Stack.Next()
if tok == scanner.EOF || tok == '\n' {
in.Error("unterminated arg list invoking macro:", macro.name)
}
if nesting == 0 && (tok == ')' || tok == ',') {
return tokens, tok
}
if tok == '(' {
nesting++
}
if tok == ')' {
nesting--
}
tokens = append(tokens, Make(tok, in.Stack.Text()))
}
}
// #ifdef and #ifndef processing.