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

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

View as plain text