github.com/go-asm/go@v1.21.1-0.20240213172139-40c5ead50c48/cmd/asm/lex/lex.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 implements lexical analysis for the assembler.
     6  package lex
     7  
     8  import (
     9  	"fmt"
    10  	"log"
    11  	"os"
    12  	"strings"
    13  	"text/scanner"
    14  
    15  	"github.com/go-asm/go/cmd/src"
    16  )
    17  
    18  // A ScanToken represents an input item. It is a simple wrapping of rune, as
    19  // returned by text/scanner.Scanner, plus a couple of extra values.
    20  type ScanToken rune
    21  
    22  const (
    23  	// Asm defines some two-character lexemes. We make up
    24  	// a rune/ScanToken value for them - ugly but simple.
    25  	LSH          ScanToken = -1000 - iota // << Left shift.
    26  	RSH                                   // >> Logical right shift.
    27  	ARR                                   // -> Used on ARM for shift type 3, arithmetic right shift.
    28  	ROT                                   // @> Used on ARM for shift type 4, rotate right.
    29  	Include                               // included file started here
    30  	BuildComment                          // //go:build or +build comment
    31  	macroName                             // name of macro that should not be expanded
    32  )
    33  
    34  // IsRegisterShift reports whether the token is one of the ARM register shift operators.
    35  func IsRegisterShift(r ScanToken) bool {
    36  	return ROT <= r && r <= LSH // Order looks backwards because these are negative.
    37  }
    38  
    39  func (t ScanToken) String() string {
    40  	switch t {
    41  	case scanner.EOF:
    42  		return "EOF"
    43  	case scanner.Ident:
    44  		return "identifier"
    45  	case scanner.Int:
    46  		return "integer constant"
    47  	case scanner.Float:
    48  		return "float constant"
    49  	case scanner.Char:
    50  		return "rune constant"
    51  	case scanner.String:
    52  		return "string constant"
    53  	case scanner.RawString:
    54  		return "raw string constant"
    55  	case scanner.Comment:
    56  		return "comment"
    57  	default:
    58  		return fmt.Sprintf("%q", rune(t))
    59  	}
    60  }
    61  
    62  // NewLexer returns a lexer for the named file and the given link context.
    63  func NewLexer(name string) TokenReader {
    64  	input := NewInput(name)
    65  	fd, err := os.Open(name)
    66  	if err != nil {
    67  		log.Fatalf("%s\n", err)
    68  	}
    69  	input.Push(NewTokenizer(name, fd, fd))
    70  	return input
    71  }
    72  
    73  // The other files in this directory each contain an implementation of TokenReader.
    74  
    75  // A TokenReader is like a reader, but returns lex tokens of type Token. It also can tell you what
    76  // the text of the most recently returned token is, and where it was found.
    77  // The underlying scanner elides all spaces except newline, so the input looks like a stream of
    78  // Tokens; original spacing is lost but we don't need it.
    79  type TokenReader interface {
    80  	// Next returns the next token.
    81  	Next() ScanToken
    82  	// The following methods all refer to the most recent token returned by Next.
    83  	// Text returns the original string representation of the token.
    84  	Text() string
    85  	// File reports the source file name of the token.
    86  	File() string
    87  	// Base reports the position base of the token.
    88  	Base() *src.PosBase
    89  	// SetBase sets the position base.
    90  	SetBase(*src.PosBase)
    91  	// Line reports the source line number of the token.
    92  	Line() int
    93  	// Col reports the source column number of the token.
    94  	Col() int
    95  	// Close does any teardown required.
    96  	Close()
    97  }
    98  
    99  // A Token is a scan token plus its string value.
   100  // A macro is stored as a sequence of Tokens with spaces stripped.
   101  type Token struct {
   102  	ScanToken
   103  	text string
   104  }
   105  
   106  // Make returns a Token with the given rune (ScanToken) and text representation.
   107  func Make(token ScanToken, text string) Token {
   108  	// Substitute the substitutes for . and /.
   109  	text = strings.ReplaceAll(text, "\u00B7", ".")
   110  	text = strings.ReplaceAll(text, "\u2215", "/")
   111  	return Token{ScanToken: token, text: text}
   112  }
   113  
   114  func (l Token) String() string {
   115  	return l.text
   116  }
   117  
   118  // A Macro represents the definition of a #defined macro.
   119  type Macro struct {
   120  	name   string   // The #define name.
   121  	args   []string // Formal arguments.
   122  	tokens []Token  // Body of macro.
   123  }
   124  
   125  // Tokenize turns a string into a list of Tokens; used to parse the -D flag and in tests.
   126  func Tokenize(str string) []Token {
   127  	t := NewTokenizer("command line", strings.NewReader(str), nil)
   128  	var tokens []Token
   129  	for {
   130  		tok := t.Next()
   131  		if tok == scanner.EOF {
   132  			break
   133  		}
   134  		tokens = append(tokens, Make(tok, t.Text()))
   135  	}
   136  	return tokens
   137  }