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