diff options
| -rw-r--r-- | exprparser.go | 207 | 
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{} +} | 
