mirror of
https://github.com/golang/go.git
synced 2025-12-08 06:10:04 +00:00
This change mechanically replaces all occurrences of interface{}
by 'any' (where deemed safe by the 'any' modernizer) throughout
std and cmd, minus their vendor trees.
Since this fix is relatively numerous, it gets its own CL.
Also, 'go generate go/types'.
Change-Id: I14a6b52856c3291c1d27935409bca8d5fd4242a2
Reviewed-on: https://go-review.googlesource.com/c/go/+/719702
Commit-Queue: Alan Donovan <adonovan@google.com>
Reviewed-by: Dmitri Shuralyov <dmitshur@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Dmitri Shuralyov <dmitshur@golang.org>
Auto-Submit: Alan Donovan <adonovan@google.com>
478 lines
12 KiB
Go
478 lines
12 KiB
Go
// Copyright 2015 The Go Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
package lex
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
"slices"
|
|
"strconv"
|
|
"strings"
|
|
"text/scanner"
|
|
|
|
"cmd/asm/internal/flags"
|
|
"cmd/internal/objabi"
|
|
"cmd/internal/src"
|
|
)
|
|
|
|
// Input is the main input: a stack of readers and some macro definitions.
|
|
// It also handles #include processing (by pushing onto the input stack)
|
|
// and parses and instantiates macro definitions.
|
|
type Input struct {
|
|
Stack
|
|
includes []string
|
|
beginningOfLine bool
|
|
ifdefStack []bool
|
|
macros map[string]*Macro
|
|
text string // Text of last token returned by Next.
|
|
peek bool
|
|
peekToken ScanToken
|
|
peekText string
|
|
}
|
|
|
|
// NewInput returns an Input from the given path.
|
|
func NewInput(name string) *Input {
|
|
return &Input{
|
|
// include directories: look in source dir, then -I directories.
|
|
includes: append([]string{filepath.Dir(name)}, flags.I...),
|
|
beginningOfLine: true,
|
|
macros: predefine(flags.D),
|
|
}
|
|
}
|
|
|
|
// predefine installs the macros set by the -D flag on the command line.
|
|
func predefine(defines flags.MultiFlag) map[string]*Macro {
|
|
macros := make(map[string]*Macro)
|
|
for _, name := range defines {
|
|
value := "1"
|
|
i := strings.IndexRune(name, '=')
|
|
if i > 0 {
|
|
name, value = name[:i], name[i+1:]
|
|
}
|
|
tokens := Tokenize(name)
|
|
if len(tokens) != 1 || tokens[0].ScanToken != scanner.Ident {
|
|
fmt.Fprintf(os.Stderr, "asm: parsing -D: %q is not a valid identifier name\n", tokens[0])
|
|
flags.Usage()
|
|
}
|
|
macros[name] = &Macro{
|
|
name: name,
|
|
args: nil,
|
|
tokens: Tokenize(value),
|
|
}
|
|
}
|
|
return macros
|
|
}
|
|
|
|
var panicOnError bool // For testing.
|
|
|
|
func (in *Input) Error(args ...any) {
|
|
if panicOnError {
|
|
panic(fmt.Errorf("%s:%d: %s", in.File(), in.Line(), fmt.Sprintln(args...)))
|
|
}
|
|
fmt.Fprintf(os.Stderr, "%s:%d: %s", in.File(), in.Line(), fmt.Sprintln(args...))
|
|
os.Exit(1)
|
|
}
|
|
|
|
// expectText is like Error but adds "got XXX" where XXX is a quoted representation of the most recent token.
|
|
func (in *Input) expectText(args ...any) {
|
|
in.Error(append(args, "; got", strconv.Quote(in.Stack.Text()))...)
|
|
}
|
|
|
|
// enabled reports whether the input is enabled by an ifdef, or is at the top level.
|
|
func (in *Input) enabled() bool {
|
|
return len(in.ifdefStack) == 0 || in.ifdefStack[len(in.ifdefStack)-1]
|
|
}
|
|
|
|
func (in *Input) expectNewline(directive string) {
|
|
tok := in.Stack.Next()
|
|
if tok != '\n' {
|
|
in.expectText("expected newline after", directive)
|
|
}
|
|
}
|
|
|
|
func (in *Input) Next() ScanToken {
|
|
if in.peek {
|
|
in.peek = false
|
|
tok := in.peekToken
|
|
in.text = in.peekText
|
|
return tok
|
|
}
|
|
// If we cannot generate a token after 100 macro invocations, we're in trouble.
|
|
// The usual case is caught by Push, below, but be safe.
|
|
for nesting := 0; nesting < 100; {
|
|
tok := in.Stack.Next()
|
|
switch tok {
|
|
case '#':
|
|
if !in.beginningOfLine {
|
|
in.Error("'#' must be first item on line")
|
|
}
|
|
in.beginningOfLine = in.hash()
|
|
in.text = "#"
|
|
return '#'
|
|
|
|
case scanner.Ident:
|
|
// Is it a macro name?
|
|
name := in.Stack.Text()
|
|
macro := in.macros[name]
|
|
if macro != nil {
|
|
nesting++
|
|
in.invokeMacro(macro)
|
|
continue
|
|
}
|
|
fallthrough
|
|
default:
|
|
if tok == scanner.EOF && len(in.ifdefStack) > 0 {
|
|
// We're skipping text but have run out of input with no #endif.
|
|
in.Error("unclosed #ifdef or #ifndef")
|
|
}
|
|
in.beginningOfLine = tok == '\n'
|
|
if in.enabled() {
|
|
in.text = in.Stack.Text()
|
|
return tok
|
|
}
|
|
}
|
|
}
|
|
in.Error("recursive macro invocation")
|
|
return 0
|
|
}
|
|
|
|
func (in *Input) Text() string {
|
|
return in.text
|
|
}
|
|
|
|
// hash processes a # preprocessor directive. It reports whether it completes.
|
|
func (in *Input) hash() bool {
|
|
// We have a '#'; it must be followed by a known word (define, include, etc.).
|
|
tok := in.Stack.Next()
|
|
if tok != scanner.Ident {
|
|
in.expectText("expected identifier after '#'")
|
|
}
|
|
if !in.enabled() {
|
|
// Can only start including again if we are at #else or #endif but also
|
|
// need to keep track of nested #if[n]defs.
|
|
// We let #line through because it might affect errors.
|
|
switch in.Stack.Text() {
|
|
case "else", "endif", "ifdef", "ifndef", "line":
|
|
// Press on.
|
|
default:
|
|
return false
|
|
}
|
|
}
|
|
switch in.Stack.Text() {
|
|
case "define":
|
|
in.define()
|
|
case "else":
|
|
in.else_()
|
|
case "endif":
|
|
in.endif()
|
|
case "ifdef":
|
|
in.ifdef(true)
|
|
case "ifndef":
|
|
in.ifdef(false)
|
|
case "include":
|
|
in.include()
|
|
case "line":
|
|
in.line()
|
|
case "undef":
|
|
in.undef()
|
|
default:
|
|
in.Error("unexpected token after '#':", in.Stack.Text())
|
|
}
|
|
return true
|
|
}
|
|
|
|
// macroName returns the name for the macro being referenced.
|
|
func (in *Input) macroName() string {
|
|
// We use the Stack's input method; no macro processing at this stage.
|
|
tok := in.Stack.Next()
|
|
if tok != scanner.Ident {
|
|
in.expectText("expected identifier after # directive")
|
|
}
|
|
// Name is alphanumeric by definition.
|
|
return in.Stack.Text()
|
|
}
|
|
|
|
// #define processing.
|
|
func (in *Input) define() {
|
|
name := in.macroName()
|
|
args, tokens := in.macroDefinition(name)
|
|
in.defineMacro(name, args, tokens)
|
|
}
|
|
|
|
// defineMacro stores the macro definition in the Input.
|
|
func (in *Input) defineMacro(name string, args []string, tokens []Token) {
|
|
if in.macros[name] != nil {
|
|
in.Error("redefinition of macro:", name)
|
|
}
|
|
in.macros[name] = &Macro{
|
|
name: name,
|
|
args: args,
|
|
tokens: tokens,
|
|
}
|
|
}
|
|
|
|
// macroDefinition returns the list of formals and the tokens of the definition.
|
|
// The argument list is nil for no parens on the definition; otherwise a list of
|
|
// formal argument names.
|
|
func (in *Input) macroDefinition(name string) ([]string, []Token) {
|
|
prevCol := in.Stack.Col()
|
|
tok := in.Stack.Next()
|
|
if tok == '\n' || tok == scanner.EOF {
|
|
return nil, nil // No definition for macro
|
|
}
|
|
var args []string
|
|
// The C preprocessor treats
|
|
// #define A(x)
|
|
// and
|
|
// #define A (x)
|
|
// distinctly: the first is a macro with arguments, the second without.
|
|
// Distinguish these cases using the column number, since we don't
|
|
// see the space itself. Note that text/scanner reports the position at the
|
|
// end of the token. It's where you are now, and you just read this token.
|
|
if tok == '(' && in.Stack.Col() == prevCol+1 {
|
|
// Macro has arguments. Scan list of formals.
|
|
acceptArg := true
|
|
args = []string{} // Zero length but not nil.
|
|
Loop:
|
|
for {
|
|
tok = in.Stack.Next()
|
|
switch tok {
|
|
case ')':
|
|
tok = in.Stack.Next() // First token of macro definition.
|
|
break Loop
|
|
case ',':
|
|
if acceptArg {
|
|
in.Error("bad syntax in definition for macro:", name)
|
|
}
|
|
acceptArg = true
|
|
case scanner.Ident:
|
|
if !acceptArg {
|
|
in.Error("bad syntax in definition for macro:", name)
|
|
}
|
|
arg := in.Stack.Text()
|
|
if slices.Contains(args, arg) {
|
|
in.Error("duplicate argument", arg, "in definition for macro:", name)
|
|
}
|
|
args = append(args, arg)
|
|
acceptArg = false
|
|
default:
|
|
in.Error("bad definition for macro:", name)
|
|
}
|
|
}
|
|
}
|
|
var tokens []Token
|
|
// Scan to newline. Backslashes escape newlines.
|
|
for tok != '\n' {
|
|
if tok == scanner.EOF {
|
|
in.Error("missing newline in definition for macro:", name)
|
|
}
|
|
if tok == '\\' {
|
|
tok = in.Stack.Next()
|
|
if tok != '\n' && tok != '\\' {
|
|
in.Error(`can only escape \ or \n in definition for macro:`, name)
|
|
}
|
|
}
|
|
tokens = append(tokens, Make(tok, in.Stack.Text()))
|
|
tok = in.Stack.Next()
|
|
}
|
|
return args, tokens
|
|
}
|
|
|
|
// invokeMacro pushes onto the input Stack a Slice that holds the macro definition with the actual
|
|
// parameters substituted for the formals.
|
|
// Invoking a macro does not touch the PC/line history.
|
|
func (in *Input) invokeMacro(macro *Macro) {
|
|
// If the macro has no arguments, just substitute the text.
|
|
if macro.args == nil {
|
|
in.Push(NewSlice(in.Base(), in.Line(), macro.tokens))
|
|
return
|
|
}
|
|
tok := in.Stack.Next()
|
|
if tok != '(' {
|
|
// If the macro has arguments but is invoked without them, all we push is the macro name.
|
|
// First, put back the token.
|
|
in.peekToken = tok
|
|
in.peekText = in.text
|
|
in.peek = true
|
|
in.Push(NewSlice(in.Base(), in.Line(), []Token{Make(macroName, macro.name)}))
|
|
return
|
|
}
|
|
actuals := in.argsFor(macro)
|
|
var tokens []Token
|
|
for _, tok := range macro.tokens {
|
|
if tok.ScanToken != scanner.Ident {
|
|
tokens = append(tokens, tok)
|
|
continue
|
|
}
|
|
substitution := actuals[tok.text]
|
|
if substitution == nil {
|
|
tokens = append(tokens, tok)
|
|
continue
|
|
}
|
|
tokens = append(tokens, substitution...)
|
|
}
|
|
in.Push(NewSlice(in.Base(), in.Line(), tokens))
|
|
}
|
|
|
|
// argsFor returns a map from formal name to actual value for this argumented macro invocation.
|
|
// The opening parenthesis has been absorbed.
|
|
func (in *Input) argsFor(macro *Macro) map[string][]Token {
|
|
var args [][]Token
|
|
// One macro argument per iteration. Collect them all and check counts afterwards.
|
|
for argNum := 0; ; argNum++ {
|
|
tokens, tok := in.collectArgument(macro)
|
|
args = append(args, tokens)
|
|
if tok == ')' {
|
|
break
|
|
}
|
|
}
|
|
// Zero-argument macros are tricky.
|
|
if len(macro.args) == 0 && len(args) == 1 && args[0] == nil {
|
|
args = nil
|
|
} else if len(args) != len(macro.args) {
|
|
in.Error("wrong arg count for macro", macro.name)
|
|
}
|
|
argMap := make(map[string][]Token)
|
|
for i, arg := range args {
|
|
argMap[macro.args[i]] = arg
|
|
}
|
|
return argMap
|
|
}
|
|
|
|
// collectArgument returns the actual tokens for a single argument of a macro.
|
|
// It also returns the token that terminated the argument, which will always
|
|
// be either ',' or ')'. The starting '(' has been scanned.
|
|
func (in *Input) collectArgument(macro *Macro) ([]Token, ScanToken) {
|
|
nesting := 0
|
|
var tokens []Token
|
|
for {
|
|
tok := in.Stack.Next()
|
|
if tok == scanner.EOF || tok == '\n' {
|
|
in.Error("unterminated arg list invoking macro:", macro.name)
|
|
}
|
|
if nesting == 0 && (tok == ')' || tok == ',') {
|
|
return tokens, tok
|
|
}
|
|
if tok == '(' {
|
|
nesting++
|
|
}
|
|
if tok == ')' {
|
|
nesting--
|
|
}
|
|
tokens = append(tokens, Make(tok, in.Stack.Text()))
|
|
}
|
|
}
|
|
|
|
// #ifdef and #ifndef processing.
|
|
func (in *Input) ifdef(truth bool) {
|
|
name := in.macroName()
|
|
in.expectNewline("#if[n]def")
|
|
if !in.enabled() {
|
|
truth = false
|
|
} else if _, defined := in.macros[name]; !defined {
|
|
truth = !truth
|
|
}
|
|
in.ifdefStack = append(in.ifdefStack, truth)
|
|
}
|
|
|
|
// #else processing
|
|
func (in *Input) else_() {
|
|
in.expectNewline("#else")
|
|
if len(in.ifdefStack) == 0 {
|
|
in.Error("unmatched #else")
|
|
}
|
|
if len(in.ifdefStack) == 1 || in.ifdefStack[len(in.ifdefStack)-2] {
|
|
in.ifdefStack[len(in.ifdefStack)-1] = !in.ifdefStack[len(in.ifdefStack)-1]
|
|
}
|
|
}
|
|
|
|
// #endif processing.
|
|
func (in *Input) endif() {
|
|
in.expectNewline("#endif")
|
|
if len(in.ifdefStack) == 0 {
|
|
in.Error("unmatched #endif")
|
|
}
|
|
in.ifdefStack = in.ifdefStack[:len(in.ifdefStack)-1]
|
|
}
|
|
|
|
// #include processing.
|
|
func (in *Input) include() {
|
|
// Find and parse string.
|
|
tok := in.Stack.Next()
|
|
if tok != scanner.String {
|
|
in.expectText("expected string after #include")
|
|
}
|
|
name, err := strconv.Unquote(in.Stack.Text())
|
|
if err != nil {
|
|
in.Error("unquoting include file name: ", err)
|
|
}
|
|
in.expectNewline("#include")
|
|
// Push tokenizer for file onto stack.
|
|
fd, err := os.Open(name)
|
|
if err != nil {
|
|
for _, dir := range in.includes {
|
|
fd, err = os.Open(filepath.Join(dir, name))
|
|
if err == nil {
|
|
break
|
|
}
|
|
}
|
|
if err != nil {
|
|
in.Error("#include:", err)
|
|
}
|
|
}
|
|
in.Push(NewTokenizer(name, fd, fd))
|
|
}
|
|
|
|
// #line processing.
|
|
func (in *Input) line() {
|
|
// Only need to handle Plan 9 format: #line 337 "filename"
|
|
tok := in.Stack.Next()
|
|
if tok != scanner.Int {
|
|
in.expectText("expected line number after #line")
|
|
}
|
|
line, err := strconv.Atoi(in.Stack.Text())
|
|
if err != nil {
|
|
in.Error("error parsing #line (cannot happen):", err)
|
|
}
|
|
tok = in.Stack.Next()
|
|
if tok != scanner.String {
|
|
in.expectText("expected file name in #line")
|
|
}
|
|
file, err := strconv.Unquote(in.Stack.Text())
|
|
if err != nil {
|
|
in.Error("unquoting #line file name: ", err)
|
|
}
|
|
tok = in.Stack.Next()
|
|
if tok != '\n' {
|
|
in.Error("unexpected token at end of #line: ", tok)
|
|
}
|
|
pos := src.MakePos(in.Base(), uint(in.Line())+1, 1) // +1 because #line nnn means line nnn starts on next line
|
|
in.Stack.SetBase(src.NewLinePragmaBase(pos, file, objabi.AbsFile(objabi.WorkingDir(), file, *flags.TrimPath), uint(line), 1))
|
|
}
|
|
|
|
// #undef processing
|
|
func (in *Input) undef() {
|
|
name := in.macroName()
|
|
if in.macros[name] == nil {
|
|
in.Error("#undef for undefined macro:", name)
|
|
}
|
|
// Newline must be next.
|
|
tok := in.Stack.Next()
|
|
if tok != '\n' {
|
|
in.Error("syntax error in #undef for macro:", name)
|
|
}
|
|
delete(in.macros, name)
|
|
}
|
|
|
|
func (in *Input) Push(r TokenReader) {
|
|
if len(in.tr) > 100 {
|
|
in.Error("input recursion")
|
|
}
|
|
in.Stack.Push(r)
|
|
}
|
|
|
|
func (in *Input) Close() {
|
|
}
|