summaryrefslogtreecommitdiff
path: root/lexer.go
diff options
context:
space:
mode:
author2026-03-08 18:25:56 +0300
committer2026-03-08 18:25:56 +0300
commite4b0856634f78f6ddd1f4d925043264bf0d736d0 (patch)
tree88b91c200bef075dc7908dee640595f6b652a92c /lexer.go
parentНачальный релиз (diff)
downloadlex-e4b0856634f78f6ddd1f4d925043264bf0d736d0.tar.gz
lex-e4b0856634f78f6ddd1f4d925043264bf0d736d0.tar.bz2
lex-e4b0856634f78f6ddd1f4d925043264bf0d736d0.tar.xz
lex-e4b0856634f78f6ddd1f4d925043264bf0d736d0.zip
Add lexer funcHEADv0.0.2master
Diffstat (limited to 'lexer.go')
-rw-r--r--lexer.go153
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
+}