0
Files
eleksir 79d9a2f604 Change layout to more go-style-ish, replace some modules
* 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
2025-11-09 22:32:50 +03:00

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
}
*/