I am using text/scanner
package to parse some arbitrary expressions. I am currently trying to implement a not in
option, that is, if the current identifier is not
, and the next is in
, parse it using function notin(left, right)
, and otherwise we parse it as negate(right)
.
I've essentially got the code to manage these cases however, I am unable to rewind the scanner in case the next token is not in
. I've tried by recording the position and then reassigning it later, but to no avail and haven't been able to find a different solution.
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")
}
}
}
}
How can I rewind the scanner when I don't find a valid identifier?
Edit, I also tried using Peek()
as below, but that still changes the state to the point that I'd need to rewind as well.
// 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")
}
Note, the code I've got is a forked from Knetic/govaluate combined with a PR from GH user generikvault and some other forks. The full code can be found on my Github profile