Skip to content

Commit

Permalink
Return error instead of panicking when tokenizing a character literal
Browse files Browse the repository at this point in the history
  • Loading branch information
aleury committed Sep 2, 2023
1 parent 3a18625 commit e82c156
Show file tree
Hide file tree
Showing 2 changed files with 32 additions and 10 deletions.
22 changes: 14 additions & 8 deletions lexer/lexer.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
package lexer

import (
"errors"
"gmachine/token"
"unicode"
"unicode/utf8"
)

var ErrInvalidCharacterLiteral error = errors.New("invalid character literal, missing closing '")

type Lexer struct {
input string
position int // current position in input (points to current char)
Expand All @@ -20,17 +23,21 @@ func New(input string) *Lexer {
return l
}

func (l *Lexer) NextToken() token.Token {
func (l *Lexer) NextToken() (token.Token, error) {
for {
l.skipWhitespace()
switch l.ch {
case ';':
l.readUntil('\n')
continue
case '\'':
return token.Token{Type: token.CHAR, Literal: l.readCharacter()}
char, err := l.readCharacter()
if err != nil {
return token.Token{}, err
}
return token.Token{Type: token.CHAR, Literal: char}, nil
case 0:
return token.Token{Type: token.EOF, Literal: ""}
return token.Token{Type: token.EOF, Literal: ""}, nil
default:
var tok token.Token
if unicode.IsDigit(l.ch) {
Expand All @@ -43,7 +50,7 @@ func (l *Lexer) NextToken() token.Token {
tok.Type = token.ILLEGAL
tok.Literal = string(l.ch)
}
return tok
return tok, nil
}
}
}
Expand All @@ -64,16 +71,15 @@ func (l *Lexer) peek() rune {
return nextChar
}

func (l *Lexer) readCharacter() string {
func (l *Lexer) readCharacter() (string, error) {
start := l.position
l.readChar()
if l.peek() != '\'' {
// TODO(adam): Handle this better by returning error?
panic("invalid character literal")
return "", ErrInvalidCharacterLiteral
}
l.readChar()
l.readChar()
return l.input[start:l.position]
return l.input[start:l.position], nil
}

func (l *Lexer) readInt() string {
Expand Down
20 changes: 18 additions & 2 deletions lexer/lexer_test.go
Original file line number Diff line number Diff line change
@@ -1,12 +1,25 @@
package lexer_test

import (
"errors"
"gmachine/lexer"
"gmachine/token"
"testing"
)

func TestNextToken(t *testing.T) {
func TestNextToken_ReturnsErrorForInvalidCharacterLiteral(t *testing.T) {
t.Parallel()
l := lexer.New("'c")
_, err := l.NextToken()
if err == nil {
t.Fatal("expected an error, but didn't receive one")
}
if !errors.Is(err, lexer.ErrInvalidCharacterLiteral) {
t.Errorf("error wrong, wanted=%q, got=%q", lexer.ErrInvalidCharacterLiteral, err)
}
}

func TestNextToken_TokenizesValidCode(t *testing.T) {
input := `
; this is a comment
JUMP 2
Expand Down Expand Up @@ -62,7 +75,10 @@ HALT

l := lexer.New(input)
for i, tt := range tests {
tok := l.NextToken()
tok, err := l.NextToken()
if err != nil {
t.Fatalf("tests[%d] - didn't expect an error: %q", i, err)
}
if tok.Type != tt.expectedType {
t.Fatalf("tests[%d] - tokentype wrong. wanted=%q, got=%q", i, tt.expectedType, tok.Type)
}
Expand Down

0 comments on commit e82c156

Please sign in to comment.