...

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

View as plain text