beginningOfLine bool
ifdefStack []bool
macros map[string]*Macro
+ text string // Text of last token returned by Next.
+ peek bool
+ peekToken ScanToken
+ peekText string
}
// NewInput returns a
// 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{}) {
- 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.
}
func (in *Input) Next() ScanToken {
+ if in.peek {
+ in.peek = false
+ tok := in.peekToken
+ in.text = in.peekText
+ return tok
+ }
for {
tok := in.Stack.Next()
switch tok {
default:
in.beginningOfLine = tok == '\n'
if in.enabled() {
+ in.text = in.Stack.Text()
return tok
}
}
return 0
}
+func (in *Input) Text() string {
+ return in.text
+}
+
// hash processes a # preprocessor directive. It returns true iff it completes.
func (in *Input) hash() bool {
// We have a '#'; it must be followed by a known word (define, include, etc.).
if !in.enabled() {
// Can only start including again if we are at #else or #endif.
// We let #line through because it might affect errors.
- switch in.Text() {
+ switch in.Stack.Text() {
case "else", "endif", "line":
// Press on.
default:
return false
}
}
- switch in.Text() {
+ switch in.Stack.Text() {
case "define":
in.define()
case "else":
case "undef":
in.undef()
default:
- in.Error("unexpected identifier after '#':", in.Text())
+ in.Error("unexpected token after '#':", in.Stack.Text())
}
return true
}
in.expectText("expected identifier after # directive")
}
// Name is alphanumeric by definition.
- return in.Text()
+ return in.Stack.Text()
}
// #define processing.
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()
}
return args, tokens
// parameters substituted for the formals.
// Invoking a macro does not touch the PC/line history.
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)
var tokens []Token
for _, tok := range macro.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 {
- 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
// One macro argument per iteration. Collect them all and check counts afterwards.
for argNum := 0; ; argNum++ {
if tok != scanner.String {
in.expectText("expected string after #include")
}
- name, err := strconv.Unquote(in.Text())
+ name, err := strconv.Unquote(in.Stack.Text())
if err != nil {
in.Error("unquoting include file name: ", err)
}
const (
// Asm defines some two-character lexemes. We make up
// a rune/ScanToken value for them - ugly but simple.
- LSH ScanToken = -1000 - iota // << Left shift.
- RSH // >> Logical right shift.
- ARR // -> Used on ARM for shift type 3, arithmetic right shift.
- ROT // @> Used on ARM for shift type 4, rotate right.
+ LSH ScanToken = -1000 - iota // << Left shift.
+ RSH // >> Logical right shift.
+ ARR // -> Used on ARM for shift type 3, arithmetic right shift.
+ ROT // @> Used on ARM for shift type 4, rotate right.
+ macroName // name of macro that should not be expanded
)
func (t ScanToken) String() string {