Я использую пакет text/scanner
для разбора произвольных выражений. В настоящее время я пытаюсь реализовать параметр not in
, то есть, если текущий идентификатор not
, а следующий - in
, проанализируйте его с помощью функции notin(left, right)
, а в противном случае мы проанализируем его как negate(right)
.
Я, по сути, получил код для управления этими случаями, однако я не могу перемотать сканер, если следующий токен не in
. Я попытался записать положение, а затем переназначить его позже, но безрезультатно и не смог найти другое решение.
func readToken(stream *scanner.Scanner) {
switch stream.Scan() {
case scanner.Ident:
switch stream.TokenText() {
case "in":
in(left, right)
case "not":
oldPosition := stream.Position
nextToken := stream.Scan()
if nextToken == scanner.Ident {
switch stream.TokenText() {
case "in":
fmt.Println("notin")
default:
// how do we rewind the scanner?
stream.Position = oldPosition
fmt.Println("negate default")
}
} else {
fmt.Println("negate no-ident")
}
}
}
}
Как можно перемотать сканер, если я ненайти действительный идентификатор?
Редактировать, я также попытался использовать Peek()
, как показано ниже, но это все еще меняет состояние до точки, которую мне также нужно перематывать.
// other code
case "not":
nextIdent, err := getNextIdent(stream)
if err != nil {
fmt.Println("negate no-ident")
} else {
switch nextIdent {
case "in":
fmt.Println("notin")
default:
fmt.Println("negate default")
}
}
// other code
func getNextIdent(s *scanner.Scanner) (string, error) {
var nextIdent string
ch := s.Peek()
// skip white space
for s.Whitespace&(1<<uint(ch)) != 0 {
ch = s.Next()
}
if isIdentRune(ch, 0) {
nextIdent = string(ch)
ch = s.Next()
nextIdent += string(ch)
for i := 1; isIdentRune(ch, i); i++ {
ch = s.Next()
if s.Whitespace&(1<<uint(ch)) != 0 {
break
}
nextIdent += string(ch)
}
return nextIdent, nil
}
return "",errors.New("not a ident")
}
Обратите внимание, код, который я получил, - это вилка из Knetic / govaluate в сочетании с PR от пользователя GH generikvault и некоторыми другими вилками. Полный код можно найти в моем профиле Github