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