You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
180 lines
2.9 KiB
Go
180 lines
2.9 KiB
Go
package parser
|
|
|
|
import (
|
|
"bufio"
|
|
"bytes"
|
|
"io"
|
|
)
|
|
|
|
type Token struct {
|
|
kind int
|
|
val string
|
|
}
|
|
|
|
type scanner struct {
|
|
r *bufio.Reader
|
|
}
|
|
|
|
var eof rune = rune(0)
|
|
|
|
const (
|
|
Number int = iota + 1
|
|
Text
|
|
Opperator
|
|
Whitespace
|
|
Comment
|
|
Newline
|
|
Separator
|
|
End
|
|
Illegal
|
|
)
|
|
|
|
// - ~ + - ~ + //
|
|
// Receivers //
|
|
// - ~ + - ~ + //
|
|
|
|
func (s *scanner) read() rune {
|
|
ch, _, err := s.r.ReadRune()
|
|
if err != nil {
|
|
return eof
|
|
}
|
|
return ch
|
|
}
|
|
|
|
|
|
func (s *scanner) unread() {
|
|
_ = s.r.UnreadRune()
|
|
}
|
|
|
|
|
|
func (s *scanner) scan() Token {
|
|
for {
|
|
char := s.read()
|
|
|
|
if isWhitespace(char) {
|
|
s.unread()
|
|
s.scanWhitespace()
|
|
} else if isLetter(char) {
|
|
s.unread()
|
|
return s.scanText()
|
|
} else if isNumber(char) {
|
|
s.unread()
|
|
return s.scanNumber()
|
|
} else if char == '#' {
|
|
s.unread()
|
|
s.scanComment()
|
|
} else if isOpperator(char) {
|
|
return Token{Opperator, string(char)}
|
|
} else if char == '\n' {
|
|
return Token{Newline,"New Line"}
|
|
} else if char == ':' {
|
|
return Token{Separator,":"}
|
|
} else if char == eof {
|
|
return Token{End, "eof"}
|
|
} else {
|
|
return Token{Illegal, string(char)}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
func (s *scanner) scanWhitespace() Token {
|
|
var buf bytes.Buffer
|
|
buf.WriteRune(s.read())
|
|
|
|
for {
|
|
if ch := s.read(); ch == eof {
|
|
s.unread()
|
|
break
|
|
} else if !isWhitespace(ch) {
|
|
s.unread()
|
|
break
|
|
} else {
|
|
_, _ = buf.WriteRune(ch)
|
|
}
|
|
}
|
|
|
|
return Token{Whitespace, buf.String()}
|
|
}
|
|
|
|
func (s *scanner) scanNumber() Token {
|
|
var buf bytes.Buffer
|
|
buf.WriteRune(s.read())
|
|
|
|
for {
|
|
if ch := s.read(); ch == eof {
|
|
s.unread()
|
|
break
|
|
} else if !isNumber(ch) {
|
|
s.unread()
|
|
break
|
|
} else {
|
|
_, _ = buf.WriteRune(ch)
|
|
}
|
|
}
|
|
|
|
return Token{Number, buf.String()}
|
|
}
|
|
|
|
|
|
func (s *scanner) scanText() Token {
|
|
var buf bytes.Buffer
|
|
buf.WriteRune(s.read())
|
|
|
|
for {
|
|
if ch := s.read(); ch == eof {
|
|
s.unread()
|
|
break
|
|
} else if !isLetter(ch) && !isNumber(ch) {
|
|
s.unread()
|
|
break
|
|
} else {
|
|
_, _ = buf.WriteRune(ch)
|
|
}
|
|
}
|
|
|
|
return Token{Text, buf.String()}
|
|
}
|
|
|
|
|
|
func (s *scanner) scanComment() Token {
|
|
var buf bytes.Buffer
|
|
buf.WriteRune(s.read())
|
|
|
|
for {
|
|
if ch := s.read(); ch == eof || ch == '\n' {
|
|
s.unread()
|
|
break
|
|
} else {
|
|
_, _ = buf.WriteRune(ch)
|
|
}
|
|
}
|
|
|
|
return Token{Comment, buf.String()}
|
|
}
|
|
|
|
|
|
// - ~ + - ~ + //
|
|
// Functions //
|
|
// - ~ + - ~ + //
|
|
|
|
func isWhitespace(ch rune) bool {
|
|
return ch == ' ' || ch == '\t' || ch == '\r'
|
|
}
|
|
|
|
func isLetter(ch rune) bool {
|
|
return ch >= 'A' && ch <= 'Z' || ch >= 'a' && ch <= 'z'
|
|
}
|
|
|
|
func isNumber(ch rune) bool {
|
|
return ch >= '0' && ch <= '9'
|
|
}
|
|
|
|
func isOpperator(ch rune) bool {
|
|
return ch == '=' || ch == '+' || ch == '-' || ch == '*' || ch == '/' || ch == '^' || ch == '%'
|
|
}
|
|
|
|
func NewScanner(r io.Reader) *scanner {
|
|
return &scanner{r: bufio.NewReader(r)}
|
|
}
|