278 lines
6.7 KiB
Go
278 lines
6.7 KiB
Go
// Copyright 2017 The go-ethereum Authors
|
|
// This file is part of the go-ethereum library.
|
|
//
|
|
// The go-ethereum library is free software: you can redistribute it and/or modify
|
|
// it under the terms of the GNU Lesser General Public License as published by
|
|
// the Free Software Foundation, either version 3 of the License, or
|
|
// (at your option) any later version.
|
|
//
|
|
// The go-ethereum library is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU Lesser General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU Lesser General Public License
|
|
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
package asm
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"strings"
|
|
"unicode"
|
|
"unicode/utf8"
|
|
|
|
"github.com/ethereum/go-ethereum/common/gopool"
|
|
)
|
|
|
|
// stateFn is used through the lifetime of the
|
|
// lexer to parse the different values at the
|
|
// current state.
|
|
type stateFn func(*lexer) stateFn
|
|
|
|
// token is emitted when the lexer has discovered
|
|
// a new parsable token. These are delivered over
|
|
// the tokens channels of the lexer
|
|
type token struct {
|
|
typ tokenType
|
|
lineno int
|
|
text string
|
|
}
|
|
|
|
// tokenType are the different types the lexer
|
|
// is able to parse and return.
|
|
type tokenType int
|
|
|
|
//go:generate go run golang.org/x/tools/cmd/stringer -type tokenType
|
|
|
|
const (
|
|
eof tokenType = iota // end of file
|
|
lineStart // emitted when a line starts
|
|
lineEnd // emitted when a line ends
|
|
invalidStatement // any invalid statement
|
|
element // any element during element parsing
|
|
label // label is emitted when a label is found
|
|
labelDef // label definition is emitted when a new label is found
|
|
number // number is emitted when a number is found
|
|
stringValue // stringValue is emitted when a string has been found
|
|
)
|
|
|
|
const (
|
|
decimalNumbers = "1234567890" // characters representing any decimal number
|
|
hexNumbers = decimalNumbers + "aAbBcCdDeEfF" // characters representing any hexadecimal
|
|
alpha = "abcdefghijklmnopqrstuwvxyzABCDEFGHIJKLMNOPQRSTUWVXYZ" // characters representing alphanumeric
|
|
)
|
|
|
|
// lexer is the basic construct for parsing
|
|
// source code and turning them in to tokens.
|
|
// Tokens are interpreted by the compiler.
|
|
type lexer struct {
|
|
input string // input contains the source code of the program
|
|
|
|
tokens chan token // tokens is used to deliver tokens to the listener
|
|
state stateFn // the current state function
|
|
|
|
lineno int // current line number in the source file
|
|
start, pos, width int // positions for lexing and returning value
|
|
|
|
debug bool // flag for triggering debug output
|
|
}
|
|
|
|
// Lex lexes the program by name with the given source. It returns a
|
|
// channel on which the tokens are delivered.
|
|
func Lex(source []byte, debug bool) <-chan token {
|
|
ch := make(chan token)
|
|
l := &lexer{
|
|
input: string(source),
|
|
tokens: ch,
|
|
state: lexLine,
|
|
debug: debug,
|
|
}
|
|
gopool.Submit(func() {
|
|
l.emit(lineStart)
|
|
for l.state != nil {
|
|
l.state = l.state(l)
|
|
}
|
|
l.emit(eof)
|
|
close(l.tokens)
|
|
})
|
|
|
|
return ch
|
|
}
|
|
|
|
// next returns the next rune in the program's source.
|
|
func (l *lexer) next() (rune rune) {
|
|
if l.pos >= len(l.input) {
|
|
l.width = 0
|
|
return 0
|
|
}
|
|
rune, l.width = utf8.DecodeRuneInString(l.input[l.pos:])
|
|
l.pos += l.width
|
|
return rune
|
|
}
|
|
|
|
// backup backsup the last parsed element (multi-character)
|
|
func (l *lexer) backup() {
|
|
l.pos -= l.width
|
|
}
|
|
|
|
// peek returns the next rune but does not advance the seeker
|
|
func (l *lexer) peek() rune {
|
|
r := l.next()
|
|
l.backup()
|
|
return r
|
|
}
|
|
|
|
// ignore advances the seeker and ignores the value
|
|
func (l *lexer) ignore() {
|
|
l.start = l.pos
|
|
}
|
|
|
|
// Accepts checks whether the given input matches the next rune
|
|
func (l *lexer) accept(valid string) bool {
|
|
if strings.ContainsRune(valid, l.next()) {
|
|
return true
|
|
}
|
|
|
|
l.backup()
|
|
|
|
return false
|
|
}
|
|
|
|
// acceptRun will continue to advance the seeker until valid
|
|
// can no longer be met.
|
|
func (l *lexer) acceptRun(valid string) {
|
|
for strings.ContainsRune(valid, l.next()) {
|
|
}
|
|
l.backup()
|
|
}
|
|
|
|
// acceptRunUntil is the inverse of acceptRun and will continue
|
|
// to advance the seeker until the rune has been found.
|
|
func (l *lexer) acceptRunUntil(until rune) bool {
|
|
// Continues running until a rune is found
|
|
for i := l.next(); !strings.ContainsRune(string(until), i); i = l.next() {
|
|
if i == 0 {
|
|
return false
|
|
}
|
|
}
|
|
|
|
return true
|
|
}
|
|
|
|
// blob returns the current value
|
|
func (l *lexer) blob() string {
|
|
return l.input[l.start:l.pos]
|
|
}
|
|
|
|
// Emits a new token on to token channel for processing
|
|
func (l *lexer) emit(t tokenType) {
|
|
token := token{t, l.lineno, l.blob()}
|
|
|
|
if l.debug {
|
|
fmt.Fprintf(os.Stderr, "%04d: (%-20v) %s\n", token.lineno, token.typ, token.text)
|
|
}
|
|
|
|
l.tokens <- token
|
|
l.start = l.pos
|
|
}
|
|
|
|
// lexLine is state function for lexing lines
|
|
func lexLine(l *lexer) stateFn {
|
|
for {
|
|
switch r := l.next(); {
|
|
case r == '\n':
|
|
l.emit(lineEnd)
|
|
l.ignore()
|
|
l.lineno++
|
|
l.emit(lineStart)
|
|
case r == ';' && l.peek() == ';':
|
|
return lexComment
|
|
case isSpace(r):
|
|
l.ignore()
|
|
case isLetter(r) || r == '_':
|
|
return lexElement
|
|
case isNumber(r):
|
|
return lexNumber
|
|
case r == '@':
|
|
l.ignore()
|
|
return lexLabel
|
|
case r == '"':
|
|
return lexInsideString
|
|
default:
|
|
return nil
|
|
}
|
|
}
|
|
}
|
|
|
|
// lexComment parses the current position until the end
|
|
// of the line and discards the text.
|
|
func lexComment(l *lexer) stateFn {
|
|
l.acceptRunUntil('\n')
|
|
l.backup()
|
|
l.ignore()
|
|
|
|
return lexLine
|
|
}
|
|
|
|
// lexLabel parses the current label, emits and returns
|
|
// the lex text state function to advance the parsing
|
|
// process.
|
|
func lexLabel(l *lexer) stateFn {
|
|
l.acceptRun(alpha + "_" + decimalNumbers)
|
|
|
|
l.emit(label)
|
|
|
|
return lexLine
|
|
}
|
|
|
|
// lexInsideString lexes the inside of a string until
|
|
// the state function finds the closing quote.
|
|
// It returns the lex text state function.
|
|
func lexInsideString(l *lexer) stateFn {
|
|
if l.acceptRunUntil('"') {
|
|
l.emit(stringValue)
|
|
}
|
|
|
|
return lexLine
|
|
}
|
|
|
|
func lexNumber(l *lexer) stateFn {
|
|
acceptance := decimalNumbers
|
|
if l.accept("xX") {
|
|
acceptance = hexNumbers
|
|
}
|
|
l.acceptRun(acceptance)
|
|
|
|
l.emit(number)
|
|
|
|
return lexLine
|
|
}
|
|
|
|
func lexElement(l *lexer) stateFn {
|
|
l.acceptRun(alpha + "_" + decimalNumbers)
|
|
|
|
if l.peek() == ':' {
|
|
l.emit(labelDef)
|
|
|
|
l.accept(":")
|
|
l.ignore()
|
|
} else {
|
|
l.emit(element)
|
|
}
|
|
return lexLine
|
|
}
|
|
|
|
func isLetter(t rune) bool {
|
|
return unicode.IsLetter(t)
|
|
}
|
|
|
|
func isSpace(t rune) bool {
|
|
return unicode.IsSpace(t)
|
|
}
|
|
|
|
func isNumber(t rune) bool {
|
|
return unicode.IsNumber(t)
|
|
}
|