diff options
| author | 2026-03-08 18:25:56 +0300 | |
|---|---|---|
| committer | 2026-03-08 18:25:56 +0300 | |
| commit | e4b0856634f78f6ddd1f4d925043264bf0d736d0 (patch) | |
| tree | 88b91c200bef075dc7908dee640595f6b652a92c /lexer.go | |
| parent | Начальный релиз (diff) | |
| download | lex-master.tar.gz lex-master.tar.bz2 lex-master.tar.xz lex-master.zip | |
Diffstat (limited to 'lexer.go')
| -rw-r--r-- | lexer.go | 153 |
1 files changed, 153 insertions, 0 deletions
diff --git a/lexer.go b/lexer.go new file mode 100644 index 0000000..2652ef0 --- /dev/null +++ b/lexer.go @@ -0,0 +1,153 @@ +// Package lex представляет собой достаточно простой лексер произвольных выражений. +// Практически полностью аналогичен лексеру от Роба Пайка. +package lex + +import ( + "bufio" + "strings" +) + +func Do(initState StateFunc, input *bufio.Reader) <-chan Token { + t := &Lexer{ + input: input, + ch: make(chan Token), + buf: []rune{}, + } + + go func() { + defer close(t.ch) + for state := initState; state != nil; { + state = state(t) + } + }() + + return t.ch +} + +type Lexer struct { + input *bufio.Reader + width int + ch chan Token + buf []rune + pos int +} + +func (l *Lexer) Next() (ch rune, err error) { + ch, l.width, err = l.input.ReadRune() + if err != nil { + return 0, err + } + l.buf = append(l.buf, ch) + return ch, nil +} + +func (l *Lexer) Peek() (rune, error) { + ch, err := l.Next() + if err != nil { + return 0, err + } + return ch, l.input.UnreadRune() +} + +func (l *Lexer) Back() error { + if len(l.buf) == 0 { + return nil + } + l.pos -= l.width + l.buf = l.buf[:len(l.buf)-1] + return l.input.UnreadRune() +} + +func (l *Lexer) Skip() { + l.buf = l.buf[:] +} + +func (l *Lexer) EmitToken(typ Typ) { + l.ch <- Token{ + Typ: typ, + Value: string(l.buf), + Pos: l.pos, + } + l.buf = l.buf[:] +} + +func (l *Lexer) EmitError(err error) { + l.ch <- Token{ + Error: err, + Value: string(l.buf), + Pos: l.pos, + } + l.buf = l.buf[:] +} + +func (l *Lexer) Accept(valid string) bool { + ch, err := l.Next() + if err != nil { + l.Back() + return false + } + if strings.ContainsRune(valid, ch) { + return true + } + l.Back() + return false +} + +func (l *Lexer) AcceptRun(valid string) bool { + ok := false + for l.Accept(valid) { + ok = true + } + return ok +} + +func (l *Lexer) AcceptNotRun(invalid string, greedy bool) bool { + ok := false + for { + ch, err := l.Next() + if err != nil { + return ok + } + if strings.ContainsRune(invalid, ch) { + if !greedy { + l.Back() + } + return ok + } + ok = true + } +} + +func (l *Lexer) AcceptAlph() bool { + ch, err := l.Next() + if err != nil { + l.Back() + return false + } + if 'a' <= ch && ch <= 'z' || 'A' <= ch && ch <= 'Z' { + return true + } + l.Back() + return false +} + +func (l *Lexer) AcceptDigit() bool { + ch, err := l.Next() + if err != nil { + l.Back() + return false + } + if '0' <= ch && ch <= '9' { + return true + } + l.Back() + return false +} + +func (l *Lexer) AcceptSpace() bool { + return l.Accept(" \r\n\t") +} + +func (l *Lexer) Empty() bool { + return len(l.buf) == 0 +} |
