ref 5
This commit is contained in:
parent
88c917210d
commit
e7584879a3
32
scanner.go
32
scanner.go
@ -7,6 +7,15 @@ import (
|
|||||||
"io"
|
"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
|
// Scanner handles the low-level parsing of the configuration format
|
||||||
type Scanner struct {
|
type Scanner struct {
|
||||||
reader *bufio.Reader
|
reader *bufio.Reader
|
||||||
@ -158,7 +167,7 @@ func (s *Scanner) NextToken() (Token, error) {
|
|||||||
// Just a single dash
|
// Just a single dash
|
||||||
_, _ = s.ReadByte() // consume dash
|
_, _ = s.ReadByte() // consume dash
|
||||||
return Token{Type: TokenError, Value: []byte("unexpected '-'")},
|
return Token{Type: TokenError, Value: []byte("unexpected '-'")},
|
||||||
fmt.Errorf("unexpected '-' at line %d, column %d", startLine, startColumn)
|
s.Error("unexpected '-'")
|
||||||
|
|
||||||
case b == '"':
|
case b == '"':
|
||||||
return s.scanString(startLine, startColumn)
|
return s.scanString(startLine, startColumn)
|
||||||
@ -171,8 +180,8 @@ func (s *Scanner) NextToken() (Token, error) {
|
|||||||
|
|
||||||
default:
|
default:
|
||||||
_, _ = s.ReadByte() // consume the unexpected character
|
_, _ = s.ReadByte() // consume the unexpected character
|
||||||
err := fmt.Errorf("unexpected character: %c", b)
|
return Token{Type: TokenError, Value: []byte(fmt.Sprintf("unexpected character: %c", b)), Line: startLine, Column: startColumn},
|
||||||
return Token{Type: TokenError, Value: []byte(err.Error()), Line: startLine, Column: startColumn}, err
|
s.Error(fmt.Sprintf("unexpected character: %c", b))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -190,7 +199,7 @@ func (s *Scanner) scanComment() error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if b != '-' {
|
if b != '-' {
|
||||||
return s.Error("invalid comment")
|
return ErrInvalidComment
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for block comment [[
|
// Check for block comment [[
|
||||||
@ -202,7 +211,7 @@ func (s *Scanner) scanComment() error {
|
|||||||
for {
|
for {
|
||||||
b, err := s.ReadByte()
|
b, err := s.ReadByte()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return s.Error("unclosed block comment")
|
return ErrUnterminatedComment
|
||||||
}
|
}
|
||||||
if b == ']' {
|
if b == ']' {
|
||||||
if n, err := s.PeekByte(); err == nil && n == ']' {
|
if n, err := s.PeekByte(); err == nil && n == ']' {
|
||||||
@ -243,7 +252,7 @@ func (s *Scanner) scanString(startLine, startColumn int) (Token, error) {
|
|||||||
for {
|
for {
|
||||||
b, err := s.ReadByte()
|
b, err := s.ReadByte()
|
||||||
if err != nil {
|
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 == '"' {
|
if b == '"' {
|
||||||
@ -254,7 +263,7 @@ func (s *Scanner) scanString(startLine, startColumn int) (Token, error) {
|
|||||||
if b == '\\' {
|
if b == '\\' {
|
||||||
escaped, err := s.ReadByte()
|
escaped, err := s.ReadByte()
|
||||||
if err != nil {
|
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 {
|
switch escaped {
|
||||||
case '"':
|
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{
|
return Token{
|
||||||
Type: TokenString,
|
Type: TokenString,
|
||||||
Value: s.buffer,
|
Value: s.buffer,
|
||||||
@ -294,7 +303,7 @@ func (s *Scanner) scanName(startLine, startColumn int) (Token, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if !isLetter(b) {
|
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)
|
s.buffer = append(s.buffer, b)
|
||||||
|
|
||||||
@ -314,10 +323,9 @@ func (s *Scanner) scanName(startLine, startColumn int) (Token, error) {
|
|||||||
_, _ = s.ReadByte()
|
_, _ = s.ReadByte()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if it's a boolean
|
// Check if it's a boolean - fixed comparison
|
||||||
tokenType := TokenName
|
tokenType := TokenName
|
||||||
if len(s.buffer) == 4 && (s.buffer[0] == 't' && s.buffer[1] == 'r' && s.buffer[2] == 'u' && s.buffer[3] == 'e' ||
|
if string(s.buffer) == "true" || string(s.buffer) == "false" {
|
||||||
s.buffer[0] == 'f' && s.buffer[1] == 'a' && s.buffer[2] == 'l' && s.buffer[3] == 's' && s.buffer[4] == 'e') {
|
|
||||||
tokenType = TokenBoolean
|
tokenType = TokenBoolean
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user