src

Go monorepo.
git clone git://code.dwrz.net/src
Log | Files | Refs

lexer.go (4006B)


      1 package pattern
      2 
      3 import (
      4 	"fmt"
      5 	"go/token"
      6 	"iter"
      7 	"unicode"
      8 	"unicode/utf8"
      9 )
     10 
     11 // lex returns the sequence of tokens in the input.
     12 func lex(f *token.File, input string) iter.Seq[item] {
     13 	return func(yield func(item) bool) {
     14 		lex := &lexer{
     15 			f:     f,
     16 			input: input,
     17 			yield: yield,
     18 		}
     19 		lex.run()
     20 	}
     21 }
     22 
     23 // lexer holds the state of a single [lex] iteration.
     24 type lexer struct {
     25 	f *token.File
     26 
     27 	input string
     28 	start int
     29 	pos   int
     30 	width int
     31 
     32 	yield func(item) bool
     33 }
     34 
     35 type itemType int
     36 
     37 const eof = -1
     38 
     39 const (
     40 	itemError itemType = iota
     41 	itemLeftParen
     42 	itemRightParen
     43 	itemLeftBracket
     44 	itemRightBracket
     45 	itemTypeName
     46 	itemVariable
     47 	itemAt
     48 	itemColon
     49 	itemBlank
     50 	itemString
     51 	itemEOF
     52 )
     53 
     54 func (typ itemType) String() string {
     55 	switch typ {
     56 	case itemError:
     57 		return "ERROR"
     58 	case itemLeftParen:
     59 		return "("
     60 	case itemRightParen:
     61 		return ")"
     62 	case itemLeftBracket:
     63 		return "["
     64 	case itemRightBracket:
     65 		return "]"
     66 	case itemTypeName:
     67 		return "TYPE"
     68 	case itemVariable:
     69 		return "VAR"
     70 	case itemAt:
     71 		return "@"
     72 	case itemColon:
     73 		return ":"
     74 	case itemBlank:
     75 		return "_"
     76 	case itemString:
     77 		return "STRING"
     78 	case itemEOF:
     79 		return "EOF"
     80 	default:
     81 		return fmt.Sprintf("itemType(%d)", typ)
     82 	}
     83 }
     84 
     85 type item struct {
     86 	typ itemType
     87 	val string
     88 	pos int
     89 }
     90 
     91 type stateFn func(*lexer) stateFn
     92 
     93 func (l *lexer) run() {
     94 	for state := lexStart; state != nil; {
     95 		state = state(l)
     96 	}
     97 }
     98 
     99 func (l *lexer) emitValue(t itemType, value string) bool {
    100 	ok := l.yield(item{t, value, l.start})
    101 	l.start = l.pos
    102 	return ok
    103 }
    104 
    105 func (l *lexer) emit(t itemType) bool {
    106 	ok := l.yield(item{t, l.input[l.start:l.pos], l.start})
    107 	l.start = l.pos
    108 	return ok
    109 }
    110 
    111 func lexStart(l *lexer) stateFn {
    112 	switch r := l.next(); {
    113 	case r == eof:
    114 		_ = l.emit(itemEOF)
    115 		return nil
    116 	case unicode.IsSpace(r):
    117 		l.ignore()
    118 	case r == '(':
    119 		if !l.emit(itemLeftParen) {
    120 			return nil
    121 		}
    122 	case r == ')':
    123 		if !l.emit(itemRightParen) {
    124 			return nil
    125 		}
    126 	case r == '[':
    127 		if !l.emit(itemLeftBracket) {
    128 			return nil
    129 		}
    130 	case r == ']':
    131 		if !l.emit(itemRightBracket) {
    132 			return nil
    133 		}
    134 	case r == '@':
    135 		if !l.emit(itemAt) {
    136 			return nil
    137 		}
    138 	case r == ':':
    139 		if !l.emit(itemColon) {
    140 			return nil
    141 		}
    142 	case r == '_':
    143 		if !l.emit(itemBlank) {
    144 			return nil
    145 		}
    146 	case r == '"':
    147 		l.backup()
    148 		return lexString
    149 	case unicode.IsUpper(r):
    150 		l.backup()
    151 		return lexType
    152 	case unicode.IsLower(r):
    153 		l.backup()
    154 		return lexVariable
    155 	default:
    156 		return l.errorf("unexpected character %c", r)
    157 	}
    158 	return lexStart
    159 }
    160 
    161 func (l *lexer) next() (r rune) {
    162 	if l.pos >= len(l.input) {
    163 		l.width = 0
    164 		return eof
    165 	}
    166 	r, l.width = utf8.DecodeRuneInString(l.input[l.pos:])
    167 
    168 	if r == '\n' {
    169 		l.f.AddLine(l.pos)
    170 	}
    171 
    172 	l.pos += l.width
    173 
    174 	return r
    175 }
    176 
    177 func (l *lexer) ignore() {
    178 	l.start = l.pos
    179 }
    180 
    181 func (l *lexer) backup() {
    182 	l.pos -= l.width
    183 }
    184 
    185 func (l *lexer) errorf(format string, args ...any) stateFn {
    186 	// TODO(dh): emit position information in errors
    187 	_ = l.yield(item{
    188 		itemError,
    189 		fmt.Sprintf(format, args...),
    190 		l.start,
    191 	})
    192 	return nil
    193 }
    194 
    195 func isAlphaNumeric(r rune) bool {
    196 	return r >= '0' && r <= '9' ||
    197 		r >= 'a' && r <= 'z' ||
    198 		r >= 'A' && r <= 'Z'
    199 }
    200 
    201 func lexString(l *lexer) stateFn {
    202 	l.next() // skip quote
    203 	escape := false
    204 
    205 	var runes []rune
    206 	for {
    207 		switch r := l.next(); r {
    208 		case eof:
    209 			return l.errorf("unterminated string")
    210 		case '"':
    211 			if !escape {
    212 				if !l.emitValue(itemString, string(runes)) {
    213 					return nil
    214 				}
    215 				return lexStart
    216 			} else {
    217 				runes = append(runes, '"')
    218 				escape = false
    219 			}
    220 		case '\\':
    221 			if escape {
    222 				runes = append(runes, '\\')
    223 				escape = false
    224 			} else {
    225 				escape = true
    226 			}
    227 		default:
    228 			runes = append(runes, r)
    229 		}
    230 	}
    231 }
    232 
    233 func lexType(l *lexer) stateFn {
    234 	l.next()
    235 	for {
    236 		if !isAlphaNumeric(l.next()) {
    237 			l.backup()
    238 			if !l.emit(itemTypeName) {
    239 				return nil
    240 			}
    241 			return lexStart
    242 		}
    243 	}
    244 }
    245 
    246 func lexVariable(l *lexer) stateFn {
    247 	l.next()
    248 	for {
    249 		if !isAlphaNumeric(l.next()) {
    250 			l.backup()
    251 			if !l.emit(itemVariable) {
    252 				return nil
    253 			}
    254 			return lexStart
    255 		}
    256 	}
    257 }