From e7584879a3cbec471c6d08b4fb7d4788fd1394ec Mon Sep 17 00:00:00 2001 From: Sky Johnson Date: Sun, 2 Mar 2025 20:36:52 -0600 Subject: [PATCH] ref 5 --- scanner.go | 32 ++++++++++++++++++++------------ 1 file changed, 20 insertions(+), 12 deletions(-) diff --git a/scanner.go b/scanner.go index 0d1bf17..7b9fc72 100644 --- a/scanner.go +++ b/scanner.go @@ -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 }