github.com/expr-lang/expr@v1.16.9/parser/lexer/state.go (about) 1 package lexer 2 3 import ( 4 "strings" 5 6 "github.com/expr-lang/expr/parser/utils" 7 ) 8 9 type stateFn func(*lexer) stateFn 10 11 func root(l *lexer) stateFn { 12 switch r := l.next(); { 13 case r == eof: 14 l.emitEOF() 15 return nil 16 case utils.IsSpace(r): 17 l.skip() 18 return root 19 case r == '\'' || r == '"': 20 l.scanString(r) 21 str, err := unescape(l.word()) 22 if err != nil { 23 l.error("%v", err) 24 } 25 l.emitValue(String, str) 26 case r == '`': 27 l.scanRawString(r) 28 case '0' <= r && r <= '9': 29 l.backup() 30 return number 31 case r == '?': 32 return questionMark 33 case r == '/': 34 return slash 35 case r == '#': 36 return pointer 37 case r == '|': 38 l.accept("|") 39 l.emit(Operator) 40 case r == ':': 41 l.accept(":") 42 l.emit(Operator) 43 case strings.ContainsRune("([{", r): 44 l.emit(Bracket) 45 case strings.ContainsRune(")]}", r): 46 l.emit(Bracket) 47 case strings.ContainsRune(",;%+-^", r): // single rune operator 48 l.emit(Operator) 49 case strings.ContainsRune("&!=*<>", r): // possible double rune operator 50 l.accept("&=*") 51 l.emit(Operator) 52 case r == '.': 53 l.backup() 54 return dot 55 case utils.IsAlphaNumeric(r): 56 l.backup() 57 return identifier 58 default: 59 return l.error("unrecognized character: %#U", r) 60 } 61 return root 62 } 63 64 func number(l *lexer) stateFn { 65 if !l.scanNumber() { 66 return l.error("bad number syntax: %q", l.word()) 67 } 68 l.emit(Number) 69 return root 70 } 71 72 func (l *lexer) scanNumber() bool { 73 digits := "0123456789_" 74 // Is it hex? 75 if l.accept("0") { 76 // Note: Leading 0 does not mean octal in floats. 77 if l.accept("xX") { 78 digits = "0123456789abcdefABCDEF_" 79 } else if l.accept("oO") { 80 digits = "01234567_" 81 } else if l.accept("bB") { 82 digits = "01_" 83 } 84 } 85 l.acceptRun(digits) 86 end := l.end 87 if l.accept(".") { 88 // Lookup for .. operator: if after dot there is another dot (1..2), it maybe a range operator. 89 if l.peek() == '.' { 90 // We can't backup() here, as it would require two backups, 91 // and backup() func supports only one for now. So, save and 92 // restore it here. 93 l.end = end 94 return true 95 } 96 l.acceptRun(digits) 97 } 98 if l.accept("eE") { 99 l.accept("+-") 100 l.acceptRun(digits) 101 } 102 // Next thing mustn't be alphanumeric. 103 if utils.IsAlphaNumeric(l.peek()) { 104 l.next() 105 return false 106 } 107 return true 108 } 109 110 func dot(l *lexer) stateFn { 111 l.next() 112 if l.accept("0123456789") { 113 l.backup() 114 return number 115 } 116 l.accept(".") 117 l.emit(Operator) 118 return root 119 } 120 121 func identifier(l *lexer) stateFn { 122 loop: 123 for { 124 switch r := l.next(); { 125 case utils.IsAlphaNumeric(r): 126 // absorb 127 default: 128 l.backup() 129 switch l.word() { 130 case "not": 131 return not 132 case "in", "or", "and", "matches", "contains", "startsWith", "endsWith": 133 l.emit(Operator) 134 case "let": 135 l.emit(Operator) 136 default: 137 l.emit(Identifier) 138 } 139 break loop 140 } 141 } 142 return root 143 } 144 145 func not(l *lexer) stateFn { 146 l.emit(Operator) 147 148 l.skipSpaces() 149 150 end := l.end 151 152 // Get the next word. 153 for { 154 r := l.next() 155 if utils.IsAlphaNumeric(r) { 156 // absorb 157 } else { 158 l.backup() 159 break 160 } 161 } 162 163 switch l.word() { 164 case "in", "matches", "contains", "startsWith", "endsWith": 165 l.emit(Operator) 166 default: 167 l.end = end 168 } 169 return root 170 } 171 172 func questionMark(l *lexer) stateFn { 173 l.accept(".?") 174 l.emit(Operator) 175 return root 176 } 177 178 func slash(l *lexer) stateFn { 179 if l.accept("/") { 180 return singleLineComment 181 } 182 if l.accept("*") { 183 return multiLineComment 184 } 185 l.emit(Operator) 186 return root 187 } 188 189 func singleLineComment(l *lexer) stateFn { 190 for { 191 r := l.next() 192 if r == eof || r == '\n' { 193 break 194 } 195 } 196 l.skip() 197 return root 198 } 199 200 func multiLineComment(l *lexer) stateFn { 201 for { 202 r := l.next() 203 if r == eof { 204 return l.error("unclosed comment") 205 } 206 if r == '*' && l.accept("/") { 207 break 208 } 209 } 210 l.skip() 211 return root 212 } 213 214 func pointer(l *lexer) stateFn { 215 l.accept("#") 216 l.emit(Operator) 217 for { 218 switch r := l.next(); { 219 case utils.IsAlphaNumeric(r): // absorb 220 default: 221 l.backup() 222 if l.word() != "" { 223 l.emit(Identifier) 224 } 225 return root 226 } 227 } 228 }