aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--exprparser.go207
1 files changed, 207 insertions, 0 deletions
diff --git a/exprparser.go b/exprparser.go
new file mode 100644
index 0000000..0d62dbc
--- /dev/null
+++ b/exprparser.go
@@ -0,0 +1,207 @@
+package main
+
+import (
+ "bufio"
+ "bytes"
+ "io"
+)
+
+type Token int
+
+const eof = rune(0)
+
+const (
+ ILLEGAL Token = iota
+ EOF
+ WS
+
+ IDENT
+
+ ASTERISK
+ COMMA
+ SEMICOLON
+ GT
+ LT
+ GET
+ LET
+ EQ
+ PLUS
+ MINUS
+ DIVISION
+ R_PAREN
+ L_PAREN
+ PERIOD
+)
+
+type Scanner struct {
+ r *bufio.Reader
+}
+
+func newScanner(r io.Reader) *Scanner {
+ return &Scanner{r: bufio.NewReader(r)}
+}
+
+func (s *Scanner) read() rune {
+ ch, _, err := s.r.ReadRune()
+ if err != nil {
+ return eof
+ }
+
+ return ch
+}
+
+func isWhiteSpace(ch rune) bool {
+ if ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r' {
+ return true
+ }
+ return false
+}
+
+func isLetter(ch rune) bool {
+ if (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') {
+ return true
+ }
+ return false
+}
+
+func isDigit(ch rune) bool {
+ if ch >= '0' && ch <= '9' {
+ return true
+ }
+ return false
+}
+
+func isNumber(chs []rune) bool {
+ var dotCounter int
+ for i := range chs {
+ if chs[i] == '.' {
+ dotCounter++
+ if dotCounter > 1 {
+ return false
+ }
+ continue
+ }
+
+ if !isDigit(chs[i]) {
+ return false
+ }
+ }
+
+ return true
+}
+
+func (s *Scanner) unread() { _ = s.r.UnreadRune() }
+
+func (s *Scanner) scanWhitespace() (tok Token, lit string) {
+ var buf bytes.Buffer
+ buf.WriteRune(s.read())
+
+ for {
+ if ch := s.read(); ch == eof {
+ break
+ } else if !isWhiteSpace(ch) {
+ s.unread()
+ break
+ } else {
+ buf.WriteRune(ch)
+ }
+ }
+
+ return WS, buf.String()
+}
+
+func (s *Scanner) scanIdent() (tok Token, lit string) {
+ var buf bytes.Buffer
+ buf.WriteRune(s.read())
+
+ for {
+ if ch := s.read(); ch == eof {
+ break
+ } else if !isLetter(ch) && !isDigit(ch) && ch != '_' {
+ s.unread()
+ break
+ } else {
+ _, _ = buf.WriteRune(ch)
+ }
+ }
+
+ return IDENT, buf.String()
+}
+
+func (s *Scanner) scan() (tok Token, lit string) {
+ ch := s.read()
+
+ if isWhiteSpace(ch) {
+ s.unread()
+ return s.scanWhitespace()
+ } else if isLetter(ch) {
+ s.unread()
+ return s.scanIdent()
+ }
+
+ switch ch {
+ case eof:
+ return EOF, string(ch)
+ case '*':
+ return ASTERISK, string(ch)
+ case '+':
+ return PLUS, string(ch)
+ case '-':
+ return MINUS, string(ch)
+ case '/':
+ return ASTERISK, string(ch)
+ case '(':
+ return R_PAREN, string(ch)
+ case ')':
+ return L_PAREN, string(ch)
+ case ',':
+ return ASTERISK, string(ch)
+ case ';':
+ return ASTERISK, string(ch)
+ case '<':
+ return LT, string(ch)
+ case '>':
+ return GT, string(ch)
+ case '.':
+ return PERIOD, string(ch)
+ }
+
+ return ILLEGAL, string(ch)
+}
+
+type Parser struct {
+ s *Scanner
+ buf struct {
+ tok Token
+ lit string
+ n int
+ }
+}
+
+func newParser(r io.Reader) *Parser {
+ return &Parser{s: newScanner(r)}
+}
+
+func (p *Parser) scan() (tok Token, lit string) {
+ if p.buf.n != 0 {
+ p.buf.n = 0
+ return p.buf.tok, p.buf.lit
+ }
+
+ tok, lit = p.s.scan()
+ p.buf.tok, p.buf.lit = tok, lit
+
+ return
+}
+
+func (p *Parser) unscan() { p.buf.n = 1 }
+
+type ASTNode struct {
+ isIdentifier bool
+ isLiteral bool
+ isIllegal bool
+ isEOF bool
+ isWS bool
+ children []*ASTNode
+ value interface{}
+}