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