Black Lives Matter. Support the Equal Justice Initiative.

Source file src/cmd/asm/internal/lex/input.go

Documentation: cmd/asm/internal/lex

     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  	"internal/buildcfg"
    10  	"os"
    11  	"path/filepath"
    12  	"strconv"
    13  	"strings"
    14  	"text/scanner"
    15  
    16  	"cmd/asm/internal/flags"
    17  	"cmd/internal/objabi"
    18  	"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.EnabledExperiments() {
    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  }
   503  

View as plain text