1 // Copyright 2015 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package lex 6 7 import ( 8 "fmt" 9 "os" 10 "path/filepath" 11 "strconv" 12 "strings" 13 "text/scanner" 14 15 "cmd/asm/internal/flags" 16 ) 17 18 // Input is the main input: a stack of readers and some macro definitions. 19 // It also handles #include processing (by pushing onto the input stack) 20 // and parses and instantiates macro definitions. 21 type Input struct { 22 Stack 23 includes []string 24 beginningOfLine bool 25 ifdefStack []bool 26 macros map[string]*Macro 27 text string // Text of last token returned by Next. 28 peek bool 29 peekToken ScanToken 30 peekText string 31 } 32 33 // NewInput returns a 34 func NewInput(name string) *Input { 35 return &Input{ 36 // include directories: look in source dir, then -I directories. 37 includes: append([]string{filepath.Dir(name)}, flags.I...), 38 beginningOfLine: true, 39 macros: predefine(flags.D), 40 } 41 } 42 43 // predefine installs the macros set by the -D flag on the command line. 44 func predefine(defines flags.MultiFlag) map[string]*Macro { 45 macros := make(map[string]*Macro) 46 for _, name := range defines { 47 value := "1" 48 i := strings.IndexRune(name, '=') 49 if i > 0 { 50 name, value = name[:i], name[i+1:] 51 } 52 tokens := Tokenize(name) 53 if len(tokens) != 1 || tokens[0].ScanToken != scanner.Ident { 54 fmt.Fprintf(os.Stderr, "asm: parsing -D: %q is not a valid identifier name\n", tokens[0]) 55 flags.Usage() 56 } 57 macros[name] = &Macro{ 58 name: name, 59 args: nil, 60 tokens: Tokenize(value), 61 } 62 } 63 return macros 64 } 65 66 func (in *Input) Error(args ...interface{}) { 67 fmt.Fprintf(os.Stderr, "%s:%d: %s", in.File(), in.Line(), fmt.Sprintln(args...)) 68 os.Exit(1) 69 } 70 71 // expectText is like Error but adds "got XXX" where XXX is a quoted representation of the most recent token. 72 func (in *Input) expectText(args ...interface{}) { 73 in.Error(append(args, "; got", strconv.Quote(in.Stack.Text()))...) 74 } 75 76 // enabled reports whether the input is enabled by an ifdef, or is at the top level. 77 func (in *Input) enabled() bool { 78 return len(in.ifdefStack) == 0 || in.ifdefStack[len(in.ifdefStack)-1] 79 } 80 81 func (in *Input) expectNewline(directive string) { 82 tok := in.Stack.Next() 83 if tok != '\n' { 84 in.expectText("expected newline after", directive) 85 } 86 } 87 88 func (in *Input) Next() ScanToken { 89 if in.peek { 90 in.peek = false 91 tok := in.peekToken 92 in.text = in.peekText 93 return tok 94 } 95 // If we cannot generate a token after 100 macro invocations, we're in trouble. 96 // The usual case is caught by Push, below, but be safe. 97 for nesting := 0; nesting < 100; { 98 tok := in.Stack.Next() 99 switch tok { 100 case '#': 101 if !in.beginningOfLine { 102 in.Error("'#' must be first item on line") 103 } 104 in.beginningOfLine = in.hash() 105 case scanner.Ident: 106 // Is it a macro name? 107 name := in.Stack.Text() 108 macro := in.macros[name] 109 if macro != nil { 110 nesting++ 111 in.invokeMacro(macro) 112 continue 113 } 114 fallthrough 115 default: 116 in.beginningOfLine = tok == '\n' 117 if in.enabled() { 118 in.text = in.Stack.Text() 119 return tok 120 } 121 } 122 } 123 in.Error("recursive macro invocation") 124 return 0 125 } 126 127 func (in *Input) Text() string { 128 return in.text 129 } 130 131 // hash processes a # preprocessor directive. It returns true iff it completes. 132 func (in *Input) hash() bool { 133 // We have a '#'; it must be followed by a known word (define, include, etc.). 134 tok := in.Stack.Next() 135 if tok != scanner.Ident { 136 in.expectText("expected identifier after '#'") 137 } 138 if !in.enabled() { 139 // Can only start including again if we are at #else or #endif. 140 // We let #line through because it might affect errors. 141 switch in.Stack.Text() { 142 case "else", "endif", "line": 143 // Press on. 144 default: 145 return false 146 } 147 } 148 switch in.Stack.Text() { 149 case "define": 150 in.define() 151 case "else": 152 in.else_() 153 case "endif": 154 in.endif() 155 case "ifdef": 156 in.ifdef(true) 157 case "ifndef": 158 in.ifdef(false) 159 case "include": 160 in.include() 161 case "line": 162 in.line() 163 case "undef": 164 in.undef() 165 default: 166 in.Error("unexpected token after '#':", in.Stack.Text()) 167 } 168 return true 169 } 170 171 // macroName returns the name for the macro being referenced. 172 func (in *Input) macroName() string { 173 // We use the Stack's input method; no macro processing at this stage. 174 tok := in.Stack.Next() 175 if tok != scanner.Ident { 176 in.expectText("expected identifier after # directive") 177 } 178 // Name is alphanumeric by definition. 179 return in.Stack.Text() 180 } 181 182 // #define processing. 183 func (in *Input) define() { 184 name := in.macroName() 185 args, tokens := in.macroDefinition(name) 186 in.defineMacro(name, args, tokens) 187 } 188 189 // defineMacro stores the macro definition in the Input. 190 func (in *Input) defineMacro(name string, args []string, tokens []Token) { 191 if in.macros[name] != nil { 192 in.Error("redefinition of macro:", name) 193 } 194 in.macros[name] = &Macro{ 195 name: name, 196 args: args, 197 tokens: tokens, 198 } 199 } 200 201 // macroDefinition returns the list of formals and the tokens of the definition. 202 // The argument list is nil for no parens on the definition; otherwise a list of 203 // formal argument names. 204 func (in *Input) macroDefinition(name string) ([]string, []Token) { 205 prevCol := in.Stack.Col() 206 tok := in.Stack.Next() 207 if tok == '\n' || tok == scanner.EOF { 208 return nil, nil // No definition for macro 209 } 210 var args []string 211 // The C preprocessor treats 212 // #define A(x) 213 // and 214 // #define A (x) 215 // distinctly: the first is a macro with arguments, the second without. 216 // Distinguish these cases using the column number, since we don't 217 // see the space itself. Note that text/scanner reports the position at the 218 // end of the token. It's where you are now, and you just read this token. 219 if tok == '(' && in.Stack.Col() == prevCol+1 { 220 // Macro has arguments. Scan list of formals. 221 acceptArg := true 222 args = []string{} // Zero length but not nil. 223 Loop: 224 for { 225 tok = in.Stack.Next() 226 switch tok { 227 case ')': 228 tok = in.Stack.Next() // First token of macro definition. 229 break Loop 230 case ',': 231 if acceptArg { 232 in.Error("bad syntax in definition for macro:", name) 233 } 234 acceptArg = true 235 case scanner.Ident: 236 if !acceptArg { 237 in.Error("bad syntax in definition for macro:", name) 238 } 239 arg := in.Stack.Text() 240 if i := lookup(args, arg); i >= 0 { 241 in.Error("duplicate argument", arg, "in definition for macro:", name) 242 } 243 args = append(args, arg) 244 acceptArg = false 245 default: 246 in.Error("bad definition for macro:", name) 247 } 248 } 249 } 250 var tokens []Token 251 // Scan to newline. Backslashes escape newlines. 252 for tok != '\n' { 253 if tok == '\\' { 254 tok = in.Stack.Next() 255 if tok != '\n' && tok != '\\' { 256 in.Error(`can only escape \ or \n in definition for macro:`, name) 257 } 258 } 259 tokens = append(tokens, Make(tok, in.Stack.Text())) 260 tok = in.Stack.Next() 261 } 262 return args, tokens 263 } 264 265 func lookup(args []string, arg string) int { 266 for i, a := range args { 267 if a == arg { 268 return i 269 } 270 } 271 return -1 272 } 273 274 // invokeMacro pushes onto the input Stack a Slice that holds the macro definition with the actual 275 // parameters substituted for the formals. 276 // Invoking a macro does not touch the PC/line history. 277 func (in *Input) invokeMacro(macro *Macro) { 278 // If the macro has no arguments, just substitute the text. 279 if macro.args == nil { 280 in.Push(NewSlice(in.File(), in.Line(), macro.tokens)) 281 return 282 } 283 tok := in.Stack.Next() 284 if tok != '(' { 285 // If the macro has arguments but is invoked without them, all we push is the macro name. 286 // First, put back the token. 287 in.peekToken = tok 288 in.peekText = in.text 289 in.peek = true 290 in.Push(NewSlice(in.File(), in.Line(), []Token{Make(macroName, macro.name)})) 291 return 292 } 293 actuals := in.argsFor(macro) 294 var tokens []Token 295 for _, tok := range macro.tokens { 296 if tok.ScanToken != scanner.Ident { 297 tokens = append(tokens, tok) 298 continue 299 } 300 substitution := actuals[tok.text] 301 if substitution == nil { 302 tokens = append(tokens, tok) 303 continue 304 } 305 tokens = append(tokens, substitution...) 306 } 307 in.Push(NewSlice(in.File(), in.Line(), tokens)) 308 } 309 310 // argsFor returns a map from formal name to actual value for this argumented macro invocation. 311 // The opening parenthesis has been absorbed. 312 func (in *Input) argsFor(macro *Macro) map[string][]Token { 313 var args [][]Token 314 // One macro argument per iteration. Collect them all and check counts afterwards. 315 for argNum := 0; ; argNum++ { 316 tokens, tok := in.collectArgument(macro) 317 args = append(args, tokens) 318 if tok == ')' { 319 break 320 } 321 } 322 // Zero-argument macros are tricky. 323 if len(macro.args) == 0 && len(args) == 1 && args[0] == nil { 324 args = nil 325 } else if len(args) != len(macro.args) { 326 in.Error("wrong arg count for macro", macro.name) 327 } 328 argMap := make(map[string][]Token) 329 for i, arg := range args { 330 argMap[macro.args[i]] = arg 331 } 332 return argMap 333 } 334 335 // collectArgument returns the actual tokens for a single argument of a macro. 336 // It also returns the token that terminated the argument, which will always 337 // be either ',' or ')'. The starting '(' has been scanned. 338 func (in *Input) collectArgument(macro *Macro) ([]Token, ScanToken) { 339 nesting := 0 340 var tokens []Token 341 for { 342 tok := in.Stack.Next() 343 if tok == scanner.EOF || tok == '\n' { 344 in.Error("unterminated arg list invoking macro:", macro.name) 345 } 346 if nesting == 0 && (tok == ')' || tok == ',') { 347 return tokens, tok 348 } 349 if tok == '(' { 350 nesting++ 351 } 352 if tok == ')' { 353 nesting-- 354 } 355 tokens = append(tokens, Make(tok, in.Stack.Text())) 356 } 357 } 358 359 // #ifdef and #ifndef processing. 360 func (in *Input) ifdef(truth bool) { 361 name := in.macroName() 362 in.expectNewline("#if[n]def") 363 if _, defined := in.macros[name]; !defined { 364 truth = !truth 365 } 366 in.ifdefStack = append(in.ifdefStack, truth) 367 } 368 369 // #else processing 370 func (in *Input) else_() { 371 in.expectNewline("#else") 372 if len(in.ifdefStack) == 0 { 373 in.Error("unmatched #else") 374 } 375 in.ifdefStack[len(in.ifdefStack)-1] = !in.ifdefStack[len(in.ifdefStack)-1] 376 } 377 378 // #endif processing. 379 func (in *Input) endif() { 380 in.expectNewline("#endif") 381 if len(in.ifdefStack) == 0 { 382 in.Error("unmatched #endif") 383 } 384 in.ifdefStack = in.ifdefStack[:len(in.ifdefStack)-1] 385 } 386 387 // #include processing. 388 func (in *Input) include() { 389 // Find and parse string. 390 tok := in.Stack.Next() 391 if tok != scanner.String { 392 in.expectText("expected string after #include") 393 } 394 name, err := strconv.Unquote(in.Stack.Text()) 395 if err != nil { 396 in.Error("unquoting include file name: ", err) 397 } 398 in.expectNewline("#include") 399 // Push tokenizer for file onto stack. 400 fd, err := os.Open(name) 401 if err != nil { 402 for _, dir := range in.includes { 403 fd, err = os.Open(filepath.Join(dir, name)) 404 if err == nil { 405 break 406 } 407 } 408 if err != nil { 409 in.Error("#include:", err) 410 } 411 } 412 in.Push(NewTokenizer(name, fd, fd)) 413 } 414 415 // #line processing. 416 func (in *Input) line() { 417 // Only need to handle Plan 9 format: #line 337 "filename" 418 tok := in.Stack.Next() 419 if tok != scanner.Int { 420 in.expectText("expected line number after #line") 421 } 422 line, err := strconv.Atoi(in.Stack.Text()) 423 if err != nil { 424 in.Error("error parsing #line (cannot happen):", err) 425 } 426 tok = in.Stack.Next() 427 if tok != scanner.String { 428 in.expectText("expected file name in #line") 429 } 430 file, err := strconv.Unquote(in.Stack.Text()) 431 if err != nil { 432 in.Error("unquoting #line file name: ", err) 433 } 434 tok = in.Stack.Next() 435 if tok != '\n' { 436 in.Error("unexpected token at end of #line: ", tok) 437 } 438 linkCtxt.LineHist.Update(histLine, file, line) 439 in.Stack.SetPos(line, file) 440 } 441 442 // #undef processing 443 func (in *Input) undef() { 444 name := in.macroName() 445 if in.macros[name] == nil { 446 in.Error("#undef for undefined macro:", name) 447 } 448 // Newline must be next. 449 tok := in.Stack.Next() 450 if tok != '\n' { 451 in.Error("syntax error in #undef for macro:", name) 452 } 453 delete(in.macros, name) 454 } 455 456 func (in *Input) Push(r TokenReader) { 457 if len(in.tr) > 100 { 458 in.Error("input recursion") 459 } 460 in.Stack.Push(r) 461 } 462 463 func (in *Input) Close() { 464 } 465