|  | package calc | 
|  |  | 
|  | import ( | 
|  | "fmt" | 
|  | "strings" | 
|  | "unicode" | 
|  | ) | 
|  |  | 
|  | type itemType int | 
|  |  | 
|  | const ( | 
|  | itemError itemType = iota | 
|  | itemIdentifier | 
|  | itemNum | 
|  | itemString | 
|  | itemLParen | 
|  | itemRParen | 
|  | itemComma | 
|  | itemEOF | 
|  | ) | 
|  |  | 
|  | const eof byte = 0xff | 
|  |  | 
|  | // item is returned by the lexer.nextItem() as it parses in the input. | 
|  | type item struct { | 
|  | typ itemType | 
|  | val string | 
|  | } | 
|  |  | 
|  | // stateFn is a function that represents the current state of the lexer. | 
|  | type stateFn func(*lexer) stateFn | 
|  |  | 
|  | // lexer parses an input string and returns items for each lexeme that's found. | 
|  | type lexer struct { | 
|  | input      string    // The string being parsed. | 
|  | start      int       // The offset of the current lexical item. | 
|  | pos        int       // Current position in input. | 
|  | items      chan item // Channel by which items are delivered. | 
|  | state      stateFn   // The next lexing function. | 
|  | peekBuffer []item    // A peekBuffer for peek'd items. | 
|  | } | 
|  |  | 
|  | // nextItem returns the next item from the input. | 
|  | func (l *lexer) nextItem() item { | 
|  | if len(l.peekBuffer) > 0 { | 
|  | item := l.peekBuffer[0] | 
|  | l.peekBuffer = l.peekBuffer[:0] | 
|  | return item | 
|  | } | 
|  | item := <-l.items | 
|  | return item | 
|  | } | 
|  |  | 
|  | // peekItem allows the caller to look ahead and see the next item that | 
|  | // nextItem() will return. | 
|  | func (l *lexer) peekItem() item { | 
|  | item := <-l.items | 
|  | l.peekBuffer = append(l.peekBuffer, item) | 
|  | return item | 
|  | } | 
|  |  | 
|  | // accept consumes the next char if it's from the valid set. | 
|  | func (l *lexer) accept(valid string) bool { | 
|  | if strings.IndexByte(valid, l.next()) >= 0 { | 
|  | return true | 
|  | } | 
|  | l.backUp() | 
|  | return false | 
|  | } | 
|  |  | 
|  | // acceptRun consumes a run of chars from the valid set. | 
|  | func (l *lexer) acceptRun(valid string) { | 
|  | for strings.IndexByte(valid, l.next()) >= 0 { | 
|  | } | 
|  | l.backUp() | 
|  | } | 
|  |  | 
|  | // ingore the current text. | 
|  | func (l *lexer) ignore() { | 
|  | l.start = l.pos | 
|  | } | 
|  |  | 
|  | // errorf returns an error token and terminates the scan by passing | 
|  | // back a nil pointer that will be the next state, terminating l.run. | 
|  | func (l *lexer) errorf(format string, args ...interface{}) stateFn { | 
|  | l.items <- item{typ: itemError, val: fmt.Sprintf(format, args...)} | 
|  | return nil | 
|  | } | 
|  |  | 
|  | // newLexer returns a new lexer for the given string. | 
|  | func newLexer(input string) *lexer { | 
|  | l := &lexer{ | 
|  | input:      input, | 
|  | start:      0, | 
|  | pos:        0, | 
|  | items:      make(chan item, 2), | 
|  | state:      lexExp, | 
|  | peekBuffer: []item{}, | 
|  | } | 
|  | go l.run() | 
|  | return l | 
|  | } | 
|  |  | 
|  | // next returns the next char in the input. | 
|  | func (l *lexer) next() byte { | 
|  | if int(l.pos) >= len(l.input) { | 
|  | return eof | 
|  | } | 
|  | ch := l.input[l.pos] | 
|  | l.pos += 1 | 
|  | return ch | 
|  | } | 
|  |  | 
|  | // backUp steps back one rune. Can only be called once per call of next. | 
|  | func (l *lexer) backUp() { | 
|  | l.pos -= 1 | 
|  | } | 
|  |  | 
|  | // run runs the state machine for the lexer. | 
|  | func (l *lexer) run() { | 
|  | for l.state = lexExp; l.state != nil; l.state = l.state(l) { | 
|  | } | 
|  | } | 
|  |  | 
|  | // emit puts a new item on the channel. | 
|  | func (l *lexer) emit(t itemType) { | 
|  | l.items <- item{ | 
|  | typ: t, | 
|  | val: l.input[l.start:l.pos], | 
|  | } | 
|  | l.start = l.pos | 
|  | } | 
|  |  | 
|  | // lexExp parses the input expression. | 
|  | func lexExp(l *lexer) stateFn { | 
|  | switch r := l.next(); { | 
|  | case r == eof: | 
|  | l.emit(itemEOF) | 
|  | return nil | 
|  | case r == '"': | 
|  | return lexString | 
|  | case unicode.IsLetter(rune(r)): | 
|  | return lexIdentifier | 
|  | case r == ')': | 
|  | l.emit(itemRParen) | 
|  | return lexExp | 
|  | case r == '(': | 
|  | l.emit(itemLParen) | 
|  | return lexExp | 
|  | case r == ',': | 
|  | l.emit(itemComma) | 
|  | return lexExp | 
|  | case unicode.IsSpace(rune(r)): | 
|  | l.ignore() | 
|  | return lexExp | 
|  | case r == '+' || r == '-' || ('0' <= r && r <= '9'): | 
|  | l.backUp() | 
|  | return lexNumber | 
|  | default: | 
|  | return l.errorf("unrecognized char: %#U", r) | 
|  | } | 
|  | } | 
|  |  | 
|  | // lexString parses double-quote delimited strings. | 
|  | func lexString(l *lexer) stateFn { | 
|  | l.ignore() | 
|  | r := l.next() | 
|  | for ; r != eof; r = l.next() { | 
|  | if r == '"' { | 
|  | l.backUp() | 
|  | l.emit(itemString) | 
|  | l.next() | 
|  | l.ignore() | 
|  | break | 
|  | } | 
|  | } | 
|  | if r == eof { | 
|  | l.errorf("Unterminated string: %s", l.input[l.start:l.pos]) | 
|  | } | 
|  | return lexExp | 
|  | } | 
|  |  | 
|  | // lexNumber parses numbers, things that looks like ints and floats. | 
|  | func lexNumber(l *lexer) stateFn { | 
|  | // Optional leading sign. | 
|  | l.accept("+-") | 
|  | // Is it hex? | 
|  | digits := "0123456789" | 
|  | if l.accept("0") && l.accept("xX") { | 
|  | digits = "0123456789abcdefABCDEF" | 
|  | } | 
|  | l.acceptRun(digits) | 
|  | if l.accept(".") { | 
|  | l.acceptRun(digits) | 
|  | } | 
|  | if l.accept("eE") { | 
|  | l.accept("+-") | 
|  | l.acceptRun("0123456789") | 
|  | } | 
|  | l.emit(itemNum) | 
|  | return lexExp | 
|  | } | 
|  |  | 
|  | // lexIdentifier parses function names. | 
|  | func lexIdentifier(l *lexer) stateFn { | 
|  | for { | 
|  | r := l.next() | 
|  | if !unicode.IsLetter(rune(r)) && !unicode.IsDigit(rune(r)) && r != '_' { | 
|  | l.backUp() | 
|  | break | 
|  | } | 
|  | } | 
|  | l.emit(itemIdentifier) | 
|  | return lexExp | 
|  | } |