* Deejay files, split them to achieve go-style project layout * Replace logrus with log/slog module to reduce dependencies * Replace gopkg.in/ini.v1 with github.com/wlevene/ini to make sure all modules BSD licensed * Update linter config && fix tons of linter warnings * Update .gitignore * Update README.md
235 lines
3.7 KiB
Go
235 lines
3.7 KiB
Go
package lexer
|
|
|
|
import (
|
|
"github.com/wlevene/ini/token"
|
|
)
|
|
|
|
const (
|
|
LineBreak = '\n'
|
|
)
|
|
|
|
type Lexer struct {
|
|
Input []byte
|
|
char byte
|
|
position int
|
|
read_position int
|
|
line int
|
|
}
|
|
|
|
func New(input string) *Lexer {
|
|
l := &Lexer{Input: []byte(input)}
|
|
l.readChar()
|
|
return l
|
|
}
|
|
|
|
func (l *Lexer) peekChar() byte {
|
|
if l.read_position >= len(l.Input) {
|
|
return 0
|
|
} else {
|
|
return l.Input[l.read_position]
|
|
}
|
|
}
|
|
|
|
func (l *Lexer) readChar() {
|
|
if l.read_position >= len(l.Input) {
|
|
l.char = 0
|
|
} else {
|
|
l.char = l.Input[l.read_position]
|
|
}
|
|
|
|
if l.char == LineBreak {
|
|
l.line++
|
|
}
|
|
|
|
l.position = l.read_position
|
|
l.read_position++
|
|
|
|
}
|
|
|
|
func (l *Lexer) readLine() []byte {
|
|
position := l.position
|
|
for {
|
|
if l.char == LineBreak {
|
|
break
|
|
}
|
|
l.readChar()
|
|
}
|
|
|
|
return l.Input[position:l.position]
|
|
}
|
|
|
|
func (l *Lexer) skipWhitespace() {
|
|
for l.char == ' ' ||
|
|
l.char == '\t' ||
|
|
l.char == LineBreak ||
|
|
l.char == '\r' {
|
|
l.readChar()
|
|
}
|
|
}
|
|
|
|
func (l *Lexer) skipspace() {
|
|
for l.char == ' ' {
|
|
l.readChar()
|
|
}
|
|
|
|
// for l.char == ' ' ||
|
|
// l.char == '\t' {
|
|
// l.readChar()
|
|
// }
|
|
}
|
|
|
|
func (l *Lexer) skipline() {
|
|
|
|
for {
|
|
if l.char == LineBreak {
|
|
// l.line++
|
|
break
|
|
}
|
|
l.readChar()
|
|
}
|
|
}
|
|
|
|
func (l *Lexer) NextToken() token.Token {
|
|
var tok token.Token
|
|
var ch byte
|
|
l.skipWhitespace()
|
|
|
|
switch l.char {
|
|
case '[':
|
|
tok = newToken(token.TokenType_LBRACKET, l.char, l.line)
|
|
case '=':
|
|
tok = newToken(token.TokenType_ASSIGN, l.char, l.line)
|
|
case ']':
|
|
tok = newToken(token.TokenType_RBRACKET, l.char, l.line)
|
|
case 0:
|
|
tok.Literal = ""
|
|
tok.Type = token.TokenType_EOF
|
|
return tok
|
|
case '#':
|
|
fallthrough
|
|
case ';':
|
|
tok.Line = l.line
|
|
tok.Literal = string(l.readLine())
|
|
tok.Type = token.TokenType_COMMENT
|
|
|
|
default:
|
|
tok.Line = l.line
|
|
tok.Literal = string(l.readStatement())
|
|
l.skipspace()
|
|
|
|
ch = l.char
|
|
|
|
if ch == '=' {
|
|
tok.Type = token.TokenType_KEY
|
|
} else if ch == ']' {
|
|
tok.Type = token.TokenType_SECTION
|
|
} else if ch == LineBreak {
|
|
tok.Type = token.TokenType_VALUE
|
|
}
|
|
}
|
|
|
|
l.readChar()
|
|
return tok
|
|
}
|
|
|
|
/*
|
|
|
|
func (s *lexer) getNextToken() token.Token {
|
|
reToken:
|
|
ch := s.peek()
|
|
switch {
|
|
case isSpace(ch):
|
|
s.skipWhitespace()
|
|
goto reToken
|
|
case isEOF(ch):
|
|
return s.NewToken(token.EOF).Lit(string(s.read()))
|
|
case ch == ';':
|
|
return s.NewToken(token.Semicolon).Lit(string(s.read()))
|
|
case ch == '{':
|
|
return s.NewToken(token.BlockStart).Lit(string(s.read()))
|
|
case ch == '}':
|
|
return s.NewToken(token.BlockEnd).Lit(string(s.read()))
|
|
case ch == '#':
|
|
return s.scanComment()
|
|
case ch == '$':
|
|
return s.scanVariable()
|
|
case isQuote(ch):
|
|
return s.scanQuotedString(ch)
|
|
default:
|
|
return s.scanKeyword()
|
|
}
|
|
}
|
|
|
|
*/
|
|
|
|
func isLetter(ch byte) bool {
|
|
return 'a' <= ch && ch <= 'z' || 'A' <= ch && ch <= 'Z' || ch == '_'
|
|
}
|
|
|
|
func isDigit(ch byte) bool {
|
|
return ('0' <= ch && ch <= '9') || ch == '.'
|
|
}
|
|
|
|
func newToken(tokenType token.TokenType, ch byte, line int) token.Token {
|
|
return token.Token{Type: tokenType,
|
|
Literal: string(ch),
|
|
Line: line,
|
|
}
|
|
}
|
|
|
|
func (l *Lexer) readStatement() []byte {
|
|
position := l.position
|
|
for l.char != LineBreak &&
|
|
l.char != '=' &&
|
|
l.char != ']' &&
|
|
l.char != ' ' {
|
|
|
|
l.readChar()
|
|
}
|
|
return l.Input[position:l.position]
|
|
}
|
|
|
|
func (l *Lexer) readSection() token.Token {
|
|
var tok token.Token
|
|
if l.char != '[' {
|
|
return tok
|
|
}
|
|
position := l.position
|
|
|
|
for {
|
|
if l.char != ']' {
|
|
l.readChar()
|
|
}
|
|
|
|
if l.char == LineBreak {
|
|
break
|
|
}
|
|
}
|
|
|
|
return token.Token{
|
|
Type: token.TokenType_SECTION,
|
|
Literal: string(l.Input[position:l.position]),
|
|
Line: l.line,
|
|
}
|
|
|
|
}
|
|
|
|
/*
|
|
|
|
func (s *lexer) read() rune {
|
|
ch, _, err := s.reader.ReadRune()
|
|
if err != nil {
|
|
return rune(token.EOF)
|
|
}
|
|
|
|
if ch == '\n' {
|
|
s.column = 1
|
|
s.line++
|
|
} else {
|
|
s.column++
|
|
}
|
|
return ch
|
|
}
|
|
|
|
*/
|