diff --git a/clox/src/scanner.c b/clox/src/scanner.c index e125a99..15d6484 100644 --- a/clox/src/scanner.c +++ b/clox/src/scanner.c @@ -18,8 +18,32 @@ void initScanner(const char *source) { scanner.line = 1; } +static bool isDigit(char c) { return c >= '0' && c <= '9'; } + static bool isAtEnd() { return *scanner.current == '\0'; } +static char advance() { + scanner.current++; + return scanner.current[-1]; +} + +static char peek() { return *scanner.current; } + +static char peekNext() { + if (isAtEnd()) + return '\0'; + return scanner.current[1]; +} + +static bool match(char expected) { + if (isAtEnd()) + return false; + if (*scanner.current != expected) + return false; + scanner.current++; + return true; +} + static Token makeToken(TokenType type) { Token token; token.type = type; @@ -38,11 +62,111 @@ static Token errorToken(const char *message) { return token; } +static void skipWhitespace() { + for (;;) { + char c = peek(); + switch (c) { + case ' ': + case '\r': + case '\t': + advance(); + break; + case '\n': + scanner.line++; + advance(); + break; + case '/': + if (peekNext() == '/') { + // A comment goes until the end of the line. + while (peek() != '\n' && !isAtEnd()) + advance(); + } else { + return; + } + break; + + default: + return; + } + } +} + +static Token number() { + while (isDigit(peek())) + advance(); + + // Look for a fractional part. + if (peek() == '.' && isDigit(peekNext())) { + // Consume the "."; + advance(); + + while (isDigit(peek())) + advance(); + } + + return makeToken(TOKEN_NUMBER); +} + +static Token string() { + while (peek() != '"' && !isAtEnd()) { + if (peek() == '\n') + scanner.line++; + advance(); + } + + if (isAtEnd()) + return errorToken("Unterminated string."); + + // The closing quote. + advance(); + return makeToken(TOKEN_STRING); +} + Token scanToken() { + skipWhitespace(); scanner.start = scanner.current; if (isAtEnd()) return makeToken(TOKEN_EOF); + char c = advance(); + if (isDigit(c)) + return number(); + + switch (c) { + case '(': + return makeToken(TOKEN_LEFT_PAREN); + case ')': + return makeToken(TOKEN_RIGHT_PAREN); + case '{': + return makeToken(TOKEN_LEFT_BRACE); + case '}': + return makeToken(TOKEN_RIGHT_BRACE); + case ';': + return makeToken(TOKEN_SEMICOLON); + case ',': + return makeToken(TOKEN_COMMA); + case '.': + return makeToken(TOKEN_DOT); + case '-': + return makeToken(TOKEN_MINUS); + case '+': + return makeToken(TOKEN_PLUS); + case '/': + return makeToken(TOKEN_SLASH); + case '*': + return makeToken(TOKEN_STAR); + case '!': + return makeToken(match('=') ? TOKEN_BANG_EQUAL : TOKEN_BANG); + case '=': + return makeToken(match('=') ? TOKEN_EQUAL_EQUAL : TOKEN_EQUAL); + case '<': + return makeToken(match('=') ? TOKEN_LESS_EQUAL : TOKEN_LESS); + case '>': + return makeToken(match('=') ? TOKEN_GREATER_EQUAL : TOKEN_GREATER); + case '"': + return string(); + } + return errorToken("Unexpected character."); }