[dev.cc] cmd/asm: handle the case where a macro is named without arguments

Given
	#define X() foo
	X()
	X
cpp produces
	foo
	X
Asm does now as well.

Change-Id: Ia36b88a23ce1660e6a02559c4f730593d62066f1
Reviewed-on: https://go-review.googlesource.com/3611
Reviewed-by: Russ Cox <rsc@golang.org>
This commit is contained in:
Rob Pike 2015-01-30 09:57:11 -08:00
parent d6eb21e331
commit 5beec6a699
3 changed files with 53 additions and 19 deletions

View file

@ -25,6 +25,10 @@ type Input struct {
beginningOfLine bool beginningOfLine bool
ifdefStack []bool ifdefStack []bool
macros map[string]*Macro macros map[string]*Macro
text string // Text of last token returned by Next.
peek bool
peekToken ScanToken
peekText string
} }
// NewInput returns a // NewInput returns a
@ -67,7 +71,7 @@ func (in *Input) Error(args ...interface{}) {
// expectText is like Error but adds "got XXX" where XXX is a quoted representation of the most recent token. // expectText is like Error but adds "got XXX" where XXX is a quoted representation of the most recent token.
func (in *Input) expectText(args ...interface{}) { func (in *Input) expectText(args ...interface{}) {
in.Error(append(args, "; got", strconv.Quote(in.Text()))...) in.Error(append(args, "; got", strconv.Quote(in.Stack.Text()))...)
} }
// enabled reports whether the input is enabled by an ifdef, or is at the top level. // enabled reports whether the input is enabled by an ifdef, or is at the top level.
@ -83,6 +87,12 @@ func (in *Input) expectNewline(directive string) {
} }
func (in *Input) Next() ScanToken { func (in *Input) Next() ScanToken {
if in.peek {
in.peek = false
tok := in.peekToken
in.text = in.peekText
return tok
}
for { for {
tok := in.Stack.Next() tok := in.Stack.Next()
switch tok { switch tok {
@ -103,6 +113,7 @@ func (in *Input) Next() ScanToken {
default: default:
in.beginningOfLine = tok == '\n' in.beginningOfLine = tok == '\n'
if in.enabled() { if in.enabled() {
in.text = in.Stack.Text()
return tok return tok
} }
} }
@ -111,6 +122,10 @@ func (in *Input) Next() ScanToken {
return 0 return 0
} }
func (in *Input) Text() string {
return in.text
}
// hash processes a # preprocessor directive. It returns true iff it completes. // hash processes a # preprocessor directive. It returns true iff it completes.
func (in *Input) hash() bool { func (in *Input) hash() bool {
// We have a '#'; it must be followed by a known word (define, include, etc.). // We have a '#'; it must be followed by a known word (define, include, etc.).
@ -121,14 +136,14 @@ func (in *Input) hash() bool {
if !in.enabled() { if !in.enabled() {
// Can only start including again if we are at #else or #endif. // Can only start including again if we are at #else or #endif.
// We let #line through because it might affect errors. // We let #line through because it might affect errors.
switch in.Text() { switch in.Stack.Text() {
case "else", "endif", "line": case "else", "endif", "line":
// Press on. // Press on.
default: default:
return false return false
} }
} }
switch in.Text() { switch in.Stack.Text() {
case "define": case "define":
in.define() in.define()
case "else": case "else":
@ -146,7 +161,7 @@ func (in *Input) hash() bool {
case "undef": case "undef":
in.undef() in.undef()
default: default:
in.Error("unexpected identifier after '#':", in.Text()) in.Error("unexpected token after '#':", in.Stack.Text())
} }
return true return true
} }
@ -159,7 +174,7 @@ func (in *Input) macroName() string {
in.expectText("expected identifier after # directive") in.expectText("expected identifier after # directive")
} }
// Name is alphanumeric by definition. // Name is alphanumeric by definition.
return in.Text() return in.Stack.Text()
} }
// #define processing. // #define processing.
@ -230,7 +245,7 @@ func (in *Input) macroDefinition(name string) ([]string, []Token) {
in.Error(`can only escape \ or \n in definition for macro:`, name) in.Error(`can only escape \ or \n in definition for macro:`, name)
} }
} }
tokens = append(tokens, Make(tok, in.Text())) tokens = append(tokens, Make(tok, in.Stack.Text()))
tok = in.Stack.Next() tok = in.Stack.Next()
} }
return args, tokens return args, tokens
@ -249,6 +264,21 @@ func lookup(args []string, arg string) int {
// parameters substituted for the formals. // parameters substituted for the formals.
// Invoking a macro does not touch the PC/line history. // Invoking a macro does not touch the PC/line history.
func (in *Input) invokeMacro(macro *Macro) { func (in *Input) invokeMacro(macro *Macro) {
// If the macro has no arguments, just substitute the text.
if macro.args == nil {
in.Push(NewSlice(in.File(), in.Line(), macro.tokens))
return
}
tok := in.Stack.Next()
if tok != '(' {
// If the macro has arguments but is invoked without them, all we push is the macro name.
// First, put back the token.
in.peekToken = tok
in.peekText = in.text
in.peek = true
in.Push(NewSlice(in.File(), in.Line(), []Token{Make(macroName, macro.name)}))
return
}
actuals := in.argsFor(macro) actuals := in.argsFor(macro)
var tokens []Token var tokens []Token
for _, tok := range macro.tokens { for _, tok := range macro.tokens {
@ -266,15 +296,9 @@ func (in *Input) invokeMacro(macro *Macro) {
in.Push(NewSlice(in.File(), in.Line(), tokens)) in.Push(NewSlice(in.File(), in.Line(), tokens))
} }
// argsFor returns a map from formal name to actual value for this macro invocation. // argsFor returns a map from formal name to actual value for this argumented macro invocation.
// The opening parenthesis has been absorbed.
func (in *Input) argsFor(macro *Macro) map[string][]Token { func (in *Input) argsFor(macro *Macro) map[string][]Token {
if macro.args == nil {
return nil
}
tok := in.Stack.Next()
if tok != '(' {
in.Error("missing arguments for invocation of macro:", macro.name)
}
var args [][]Token var args [][]Token
// One macro argument per iteration. Collect them all and check counts afterwards. // One macro argument per iteration. Collect them all and check counts afterwards.
for argNum := 0; ; argNum++ { for argNum := 0; ; argNum++ {
@ -356,7 +380,7 @@ func (in *Input) include() {
if tok != scanner.String { if tok != scanner.String {
in.expectText("expected string after #include") in.expectText("expected string after #include")
} }
name, err := strconv.Unquote(in.Text()) name, err := strconv.Unquote(in.Stack.Text())
if err != nil { if err != nil {
in.Error("unquoting include file name: ", err) in.Error("unquoting include file name: ", err)
} }

View file

@ -22,10 +22,11 @@ type ScanToken rune
const ( const (
// Asm defines some two-character lexemes. We make up // Asm defines some two-character lexemes. We make up
// a rune/ScanToken value for them - ugly but simple. // a rune/ScanToken value for them - ugly but simple.
LSH ScanToken = -1000 - iota // << Left shift. LSH ScanToken = -1000 - iota // << Left shift.
RSH // >> Logical right shift. RSH // >> Logical right shift.
ARR // -> Used on ARM for shift type 3, arithmetic right shift. ARR // -> Used on ARM for shift type 3, arithmetic right shift.
ROT // @> Used on ARM for shift type 4, rotate right. ROT // @> Used on ARM for shift type 4, rotate right.
macroName // name of macro that should not be expanded
) )
func (t ScanToken) String() string { func (t ScanToken) String() string {

View file

@ -46,6 +46,15 @@ var lexTests = []lexTest{
"#define A(x, y, z) x+z+y\n" + "A(1, 2, 3)\n", "#define A(x, y, z) x+z+y\n" + "A(1, 2, 3)\n",
"1.+.3.+.2.\n", "1.+.3.+.2.\n",
}, },
{
"argumented macro invoked without arguments",
lines(
"#define X() foo ",
"X()",
"X",
),
"foo.\n.X.\n",
},
{ {
"multiline macro without arguments", "multiline macro without arguments",
lines( lines(