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  }