mirror of
https://github.com/luxfi/geth.git
synced 2026-10-30 15:05:39 +00:00
275 lines
5.4 KiB
Go
275 lines
5.4 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"
|
|
)
|
|
|
|
// stateFn is the state function type.
|
|
type stateFn func(*lexer) stateFn
|
|
|
|
// token is emitted to the client.
|
|
type token struct {
|
|
typ tokenType
|
|
lineno int
|
|
text string
|
|
}
|
|
|
|
// tokenType is the type of the token.
|
|
type tokenType int
|
|
|
|
const (
|
|
eof tokenType = iota // end of file
|
|
lineStart
|
|
lineEnd
|
|
invalidStatement
|
|
element
|
|
label
|
|
labelDef
|
|
number
|
|
stringValue
|
|
|
|
Numbers = "1234567890"
|
|
HexadecimalNumbers = Numbers + "aAbBcCdDeEfF"
|
|
)
|
|
|
|
// lexer is the lexical analyzer.
|
|
type lexer struct {
|
|
input string
|
|
start int
|
|
pos int
|
|
width int
|
|
state stateFn
|
|
lineno int
|
|
tokens chan token
|
|
debug bool
|
|
}
|
|
|
|
// lex lexes the program by name with the given source.
|
|
func Lex(source []byte, debug bool) <-chan token {
|
|
ch := make(chan token)
|
|
l := &lexer{
|
|
input: string(source),
|
|
tokens: ch,
|
|
state: lexLine,
|
|
debug: debug,
|
|
}
|
|
go func() {
|
|
l.run()
|
|
close(l.tokens)
|
|
}()
|
|
return ch
|
|
}
|
|
|
|
// LexProgram lexes the given source and returns tokens.
|
|
func LexProgram(source string) ([]token, error) {
|
|
ch := Lex([]byte(source), false)
|
|
var tokens []token
|
|
for tok := range ch {
|
|
if tok.typ == eof {
|
|
break
|
|
}
|
|
tokens = append(tokens, tok)
|
|
}
|
|
return tokens, nil
|
|
}
|
|
|
|
// next returns the next rune in the input.
|
|
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 backs up one rune.
|
|
func (l *lexer) backup() {
|
|
l.pos -= l.width
|
|
}
|
|
|
|
// peek returns but does not consume the next rune.
|
|
func (l *lexer) peek() rune {
|
|
r := l.next()
|
|
l.backup()
|
|
return r
|
|
}
|
|
|
|
// emit emits a token of the given type.
|
|
func (l *lexer) emit(t tokenType) {
|
|
l.tokens <- token{t, l.lineno, l.input[l.start:l.pos]}
|
|
l.start = l.pos
|
|
}
|
|
|
|
// ignore skips the current position.
|
|
func (l *lexer) ignore() {
|
|
l.start = l.pos
|
|
}
|
|
|
|
// run runs the lexer state machine.
|
|
func (l *lexer) run() {
|
|
for l.state != nil {
|
|
l.state = l.state(l)
|
|
}
|
|
l.emit(eof)
|
|
}
|
|
|
|
// lexLine is the main lexing state.
|
|
func lexLine(l *lexer) stateFn {
|
|
for {
|
|
switch r := l.next(); {
|
|
case r == '\n':
|
|
l.emit(lineEnd)
|
|
l.ignore()
|
|
l.lineno++
|
|
case r == ';' && l.peek() == ';':
|
|
return lexComment
|
|
case isSpace(r):
|
|
l.ignore()
|
|
case isAlphaNumeric(r) || r == '_':
|
|
return lexElement
|
|
case isNumber(r):
|
|
return lexNumber
|
|
case r == '"':
|
|
return lexString
|
|
case r == 0:
|
|
l.emit(eof)
|
|
return nil
|
|
default:
|
|
return l.errorf("invalid character: %q", r)
|
|
}
|
|
}
|
|
}
|
|
|
|
// lexComment lexes a comment.
|
|
func lexComment(l *lexer) stateFn {
|
|
for {
|
|
switch r := l.next(); r {
|
|
case '\n':
|
|
l.backup()
|
|
l.ignore()
|
|
return lexLine
|
|
case 0:
|
|
l.ignore()
|
|
return lexLine
|
|
}
|
|
}
|
|
}
|
|
|
|
// lexElement lexes an element.
|
|
func lexElement(l *lexer) stateFn {
|
|
for {
|
|
switch r := l.next(); {
|
|
case isAlphaNumeric(r) || r == '_':
|
|
// absorb
|
|
case r == ':':
|
|
l.backup()
|
|
if l.input[l.start:l.pos] == l.input[l.start:l.pos] {
|
|
l.emit(labelDef)
|
|
} else {
|
|
l.emit(label)
|
|
}
|
|
l.next()
|
|
l.ignore()
|
|
return lexLine
|
|
default:
|
|
l.backup()
|
|
l.emit(element)
|
|
return lexLine
|
|
}
|
|
}
|
|
}
|
|
|
|
// lexNumber lexes a number.
|
|
func lexNumber(l *lexer) stateFn {
|
|
acceptance := Numbers
|
|
if l.accept("0") && l.accept("xX") {
|
|
acceptance = HexadecimalNumbers
|
|
}
|
|
l.acceptRun(acceptance)
|
|
l.emit(number)
|
|
return lexLine
|
|
}
|
|
|
|
// lexString lexes a string.
|
|
func lexString(l *lexer) stateFn {
|
|
for {
|
|
switch r := l.next(); r {
|
|
case '"':
|
|
l.emit(stringValue)
|
|
return lexLine
|
|
case 0, '\n':
|
|
return l.errorf("unterminated string")
|
|
case '\\':
|
|
if l.next() == 0 {
|
|
return l.errorf("unterminated string")
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// accept accepts a rune if it's in the valid string.
|
|
func (l *lexer) accept(valid string) bool {
|
|
if strings.ContainsRune(valid, l.next()) {
|
|
return true
|
|
}
|
|
l.backup()
|
|
return false
|
|
}
|
|
|
|
// acceptRun accepts a run of runes from the valid string.
|
|
func (l *lexer) acceptRun(valid string) {
|
|
for strings.ContainsRune(valid, l.next()) {
|
|
}
|
|
l.backup()
|
|
}
|
|
|
|
// errorf emits an error token.
|
|
func (l *lexer) errorf(format string, args ...interface{}) stateFn {
|
|
l.tokens <- token{
|
|
invalidStatement,
|
|
l.lineno,
|
|
fmt.Sprintf(format, args...),
|
|
}
|
|
if l.debug {
|
|
fmt.Fprintf(os.Stderr, format+"\n", args...)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// isAlphaNumeric returns true if the rune is alphanumeric.
|
|
func isAlphaNumeric(r rune) bool {
|
|
return unicode.IsLetter(r) || unicode.IsDigit(r)
|
|
}
|
|
|
|
// isSpace returns true if the rune is a space.
|
|
func isSpace(r rune) bool {
|
|
return r == ' ' || r == '\t'
|
|
}
|
|
|
|
// isNumber returns true if the rune is a number.
|
|
func isNumber(r rune) bool {
|
|
return strings.ContainsRune(Numbers, r)
|
|
}
|