This commit is contained in:
Sky Johnson 2025-03-02 20:36:52 -06:00
parent 88c917210d
commit e7584879a3

View File

@ -7,6 +7,15 @@ import (
"io"
)
// Pre-declared errors to reduce allocations
var (
ErrUnterminatedString = errors.New("unterminated string")
ErrUnterminatedEscape = errors.New("unterminated escape sequence")
ErrUnterminatedComment = errors.New("unclosed block comment")
ErrInvalidComment = errors.New("invalid comment")
ErrNameStartWithLetter = errors.New("name must start with letter")
)
// Scanner handles the low-level parsing of the configuration format
type Scanner struct {
reader *bufio.Reader
@ -158,7 +167,7 @@ func (s *Scanner) NextToken() (Token, error) {
// Just a single dash
_, _ = s.ReadByte() // consume dash
return Token{Type: TokenError, Value: []byte("unexpected '-'")},
fmt.Errorf("unexpected '-' at line %d, column %d", startLine, startColumn)
s.Error("unexpected '-'")
case b == '"':
return s.scanString(startLine, startColumn)
@ -171,8 +180,8 @@ func (s *Scanner) NextToken() (Token, error) {
default:
_, _ = s.ReadByte() // consume the unexpected character
err := fmt.Errorf("unexpected character: %c", b)
return Token{Type: TokenError, Value: []byte(err.Error()), Line: startLine, Column: startColumn}, err
return Token{Type: TokenError, Value: []byte(fmt.Sprintf("unexpected character: %c", b)), Line: startLine, Column: startColumn},
s.Error(fmt.Sprintf("unexpected character: %c", b))
}
}
@ -190,7 +199,7 @@ func (s *Scanner) scanComment() error {
return err
}
if b != '-' {
return s.Error("invalid comment")
return ErrInvalidComment
}
// Check for block comment [[
@ -202,7 +211,7 @@ func (s *Scanner) scanComment() error {
for {
b, err := s.ReadByte()
if err != nil {
return s.Error("unclosed block comment")
return ErrUnterminatedComment
}
if b == ']' {
if n, err := s.PeekByte(); err == nil && n == ']' {
@ -243,7 +252,7 @@ func (s *Scanner) scanString(startLine, startColumn int) (Token, error) {
for {
b, err := s.ReadByte()
if err != nil {
return Token{Type: TokenError, Value: []byte("unterminated string")}, errors.New("unterminated string")
return Token{Type: TokenError, Value: []byte(ErrUnterminatedString.Error())}, ErrUnterminatedString
}
if b == '"' {
@ -254,7 +263,7 @@ func (s *Scanner) scanString(startLine, startColumn int) (Token, error) {
if b == '\\' {
escaped, err := s.ReadByte()
if err != nil {
return Token{Type: TokenError, Value: []byte("unterminated escape sequence")}, errors.New("unterminated escape sequence")
return Token{Type: TokenError, Value: []byte(ErrUnterminatedEscape.Error())}, ErrUnterminatedEscape
}
switch escaped {
case '"':
@ -273,7 +282,7 @@ func (s *Scanner) scanString(startLine, startColumn int) (Token, error) {
}
}
// Return token using buffer directly - we'll copy in NextToken if needed
// Use the buffer directly - consumer is responsible for copying if needed
return Token{
Type: TokenString,
Value: s.buffer,
@ -294,7 +303,7 @@ func (s *Scanner) scanName(startLine, startColumn int) (Token, error) {
}
if !isLetter(b) {
return Token{Type: TokenError, Value: []byte("name must start with letter")}, s.Error("name must start with letter")
return Token{Type: TokenError, Value: []byte(ErrNameStartWithLetter.Error())}, ErrNameStartWithLetter
}
s.buffer = append(s.buffer, b)
@ -314,10 +323,9 @@ func (s *Scanner) scanName(startLine, startColumn int) (Token, error) {
_, _ = s.ReadByte()
}
// Check if it's a boolean
// Check if it's a boolean - fixed comparison
tokenType := TokenName
if len(s.buffer) == 4 && (s.buffer[0] == 't' && s.buffer[1] == 'r' && s.buffer[2] == 'u' && s.buffer[3] == 'e' ||
s.buffer[0] == 'f' && s.buffer[1] == 'a' && s.buffer[2] == 'l' && s.buffer[3] == 's' && s.buffer[4] == 'e') {
if string(s.buffer) == "true" || string(s.buffer) == "false" {
tokenType = TokenBoolean
}