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  }