Skip to content

Commit b183627

Browse files
committed
complete lexer
1 parent a7d46ea commit b183627

File tree

4 files changed

+129
-13
lines changed

4 files changed

+129
-13
lines changed

lexer/lexer.go

+27-1
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,23 @@ func (l *Lexer) NextToken() token.Token {
4848
l.skipWhitespace()
4949
switch l.ch {
5050
case '=':
51-
tok = newToken(token.ASSIGN, l.ch)
51+
if l.peekChar() == '=' {
52+
ch := l.ch
53+
l.readChar()
54+
literal := string(ch) + string(l.ch)
55+
tok = token.Token{Type: token.EQ, Literal: literal}
56+
} else {
57+
tok = newToken(token.ASSIGN, l.ch)
58+
}
59+
case '!':
60+
if l.peekChar() == '=' {
61+
ch := l.ch
62+
l.readChar()
63+
literal := string(ch) + string(l.ch)
64+
tok = token.Token{Type: token.NOT_EQ, Literal: literal}
65+
} else {
66+
tok = newToken(token.BANG, l.ch)
67+
}
5268
case ';':
5369
tok = newToken(token.SEMICOLON, l.ch)
5470
case '(':
@@ -59,6 +75,16 @@ func (l *Lexer) NextToken() token.Token {
5975
tok = newToken(token.COMMA, l.ch)
6076
case '+':
6177
tok = newToken(token.PLUS, l.ch)
78+
case '-':
79+
tok = newToken(token.MINUS, l.ch)
80+
case '/':
81+
tok = newToken(token.SLASH, l.ch)
82+
case '*':
83+
tok = newToken(token.ASTERISK, l.ch)
84+
case '<':
85+
tok = newToken(token.LT, l.ch)
86+
case '>':
87+
tok = newToken(token.GT, l.ch)
6288
case '{':
6389
tok = newToken(token.LBRACE, l.ch)
6490
case '}':

lexer/lexer_helpers.go

+8
Original file line numberDiff line numberDiff line change
@@ -12,3 +12,11 @@ func (l *Lexer) skipWhitespace() {
1212
func isLetter(ch byte) bool {
1313
return 'a' <= ch && ch <= 'z' || 'A' <= ch && ch <= 'Z' || ch == '_'
1414
}
15+
16+
func (l *Lexer) peekChar() byte {
17+
if l.readPosition >= len(l.input) {
18+
return 0
19+
} else {
20+
return l.input[l.readPosition]
21+
}
22+
}

lexer/lexer_test.go

+66-8
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,32 @@ import (
88

99
func TestNextToken(t *testing.T) {
1010
// input := `=+(){},;`
11+
// input := `var five = 5;
12+
// var ten = 10;
13+
// var add = fn(x, y) {
14+
// x + y;
15+
// };
16+
// var result = add(five, ten);`
1117
input := `var five = 5;
12-
var ten = 10;
13-
var add = fn(x, y) {
14-
x + y;
15-
};
16-
var result = add(five, ten);`
18+
var ten = 10;
19+
20+
var add = fn(x, y) {
21+
x + y;
22+
};
23+
24+
var result = add(five, ten);
25+
!-/*5;
26+
5 < 10 > 5;
27+
28+
if (5 < 10) {
29+
return true;
30+
} else {
31+
return false;
32+
}
33+
34+
10 == 10;
35+
10 != 9;
36+
`
1737

1838
tests := []struct {
1939
expectedType token.TokenType
@@ -55,6 +75,43 @@ func TestNextToken(t *testing.T) {
5575
{token.IDENT, "ten"},
5676
{token.RPAREN, ")"},
5777
{token.SEMICOLON, ";"},
78+
{token.BANG, "!"},
79+
{token.MINUS, "-"},
80+
{token.SLASH, "/"},
81+
{token.ASTERISK, "*"},
82+
{token.INT, "5"},
83+
{token.SEMICOLON, ";"},
84+
{token.INT, "5"},
85+
{token.LT, "<"},
86+
{token.INT, "10"},
87+
{token.GT, ">"},
88+
{token.INT, "5"},
89+
{token.SEMICOLON, ";"},
90+
{token.IF, "if"},
91+
{token.LPAREN, "("},
92+
{token.INT, "5"},
93+
{token.LT, "<"},
94+
{token.INT, "10"},
95+
{token.RPAREN, ")"},
96+
{token.LBRACE, "{"},
97+
{token.RETURN, "return"},
98+
{token.TRUE, "true"},
99+
{token.SEMICOLON, ";"},
100+
{token.RBRACE, "}"},
101+
{token.ELSE, "else"},
102+
{token.LBRACE, "{"},
103+
{token.RETURN, "return"},
104+
{token.FALSE, "false"},
105+
{token.SEMICOLON, ";"},
106+
{token.RBRACE, "}"},
107+
{token.INT, "10"},
108+
{token.EQ, "=="},
109+
{token.INT, "10"},
110+
{token.SEMICOLON, ";"},
111+
{token.INT, "10"},
112+
{token.NOT_EQ, "!="},
113+
{token.INT, "9"},
114+
{token.SEMICOLON, ";"},
58115
{token.EOF, ""},
59116
}
60117

@@ -64,12 +121,13 @@ func TestNextToken(t *testing.T) {
64121
tok := l.NextToken()
65122

66123
if tok.Type != tt.expectedType {
67-
t.Fatalf("tests[%d] - tokentype wrong. expected=%q, got=%q", i, tt.expectedType, tok.Type)
124+
t.Fatalf("tests[%d] - tokentype wrong. expected=%q, got=%q",
125+
i, tt.expectedType, tok.Type)
68126
}
69127

70128
if tok.Literal != tt.expectedLiteral {
71-
t.Fatalf("tests[%d] - literal wrong. expected=%q, got=%q", i, tt.expectedLiteral, tok.Literal)
129+
t.Fatalf("tests[%d] - literal wrong. expected=%q, got=%q",
130+
i, tt.expectedLiteral, tok.Literal)
72131
}
73132
}
74-
75133
}

token/token.go

+28-4
Original file line numberDiff line numberDiff line change
@@ -10,27 +10,51 @@ type Token struct {
1010
const (
1111
ILLEGAL = "ILLEGAL"
1212
EOF = "EOF"
13+
1314
// Identifiers + literals
1415
IDENT = "IDENT" // add, foobar, x, y, ...
1516
INT = "INT" // 1343456
17+
1618
// Operators
17-
ASSIGN = "="
18-
PLUS = "+"
19+
ASSIGN = "="
20+
PLUS = "+"
21+
MINUS = "-"
22+
BANG = "!"
23+
ASTERISK = "*"
24+
SLASH = "/"
25+
26+
LT = "<"
27+
GT = ">"
28+
29+
EQ = "=="
30+
NOT_EQ = "!="
31+
1932
// Delimiters
2033
COMMA = ","
2134
SEMICOLON = ";"
2235
LPAREN = "("
2336
RPAREN = ")"
2437
LBRACE = "{"
2538
RBRACE = "}"
39+
2640
// Keywords
2741
FUNCTION = "FUNCTION"
2842
VAR = "VAR"
43+
TRUE = "TRUE"
44+
FALSE = "FALSE"
45+
IF = "IF"
46+
ELSE = "ELSE"
47+
RETURN = "RETURN"
2948
)
3049

3150
var keywords = map[string]TokenType{
32-
"fn": FUNCTION,
33-
"var": VAR,
51+
"fn": FUNCTION,
52+
"var": VAR,
53+
"true": TRUE,
54+
"false": FALSE,
55+
"if": IF,
56+
"else": ELSE,
57+
"return": RETURN,
3458
}
3559

3660
func LookupIndent(ident string) TokenType {

0 commit comments

Comments
 (0)