GSC Interpreter
A Turing-complete interpreter developed for a compiler course
Loading...
Searching...
No Matches
scanner.cpp
Go to the documentation of this file.
1#include "gsc/scanner.hpp"
2#include "gsc/error.hpp"
3
4bool isDigit(const char c) { return c >= '0' && c <= '9'; }
5
6bool isAlpha(const char c) {
7 return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_';
8}
9
10bool isAlphaNumeric(const char c) { return isAlpha(c) || isDigit(c); }
11
12void Scanner::addToken(TokenType type) { addToken(type, nullptr); }
13
14void Scanner::addToken(TokenType type, std::any literal) {
15 std::string text{program.substr(start, current - start)};
16 tokens.emplace_back(type, std::move(text), std::move(literal), line);
17}
18
19void Scanner::scanToken() {
20 char c = advance();
21 switch (c) {
22 // One or two character tokens
23 case '(':
24 addToken(LEFT_PAREN);
25 break;
26 case ')':
27 addToken(RIGHT_PAREN);
28 break;
29 case '{':
30 addToken(LEFT_BRACE);
31 break;
32 case '}':
33 addToken(RIGHT_BRACE);
34 break;
35 case '-':
36 addToken(MINUS);
37 break;
38 case '+':
39 addToken(PLUS);
40 break;
41 case ';':
42 addToken(SEMICOLON);
43 break;
44 case '*':
45 addToken(STAR);
46 break;
47 case '!':
48 addToken(match('=') ? BANG_EQUAL : BANG);
49 break;
50 case '=':
51 addToken(match('=') ? EQUAL_EQUAL : EQUAL);
52 break;
53 case '<':
54 addToken(match('=') ? LESS_EQUAL : LESS);
55 break;
56 case '>':
57 addToken(match('=') ? GREATER_EQUAL : GREATER);
58 break;
59
60 // Integer division vs. C-style comments
61 case '/':
62 if (match('/')) {
63 while (peek() != '\n' && !isAtEnd())
64 advance();
65 } else {
66 addToken(SLASH);
67 }
68 break;
69
70 // Ignore white spaces
71 case ' ':
72 case '\r':
73 case '\t':
74 break;
75
76 // New line
77 case '\n':
78 line++;
79 break;
80
81 // Identifiers
82 case '"':
83 string();
84 break;
85 default:
86 if (isDigit(c)) {
87 number();
88 } else if (isAlpha(c)) {
89 identifier();
90 } else {
91 // Report error but continue scanning
92 error(line, "Unexpected character.");
93 }
94 break;
95 };
96}
97
98void Scanner::identifier() {
99 while (isAlphaNumeric(peek())) {
100 advance();
101 }
102
103 std::string text = std::string{program.substr(start, current - start)};
104
105 TokenType type = keywords.count(text) ? keywords.at(text) : IDENTIFIER;
106 addToken(type);
107}
108
109void Scanner::number() {
110 while (isDigit(peek())) {
111 advance();
112 }
113
114 int numberLiteral =
115 std::stoi(std::string{program.substr(start, current - start)});
116 addToken(NUMBER, numberLiteral);
117}
118
119void Scanner::string() {
120 while (peek() != '"' && !isAtEnd()) {
121 if (peek() == '\n')
122 line++;
123 advance();
124 }
125
126 if (isAtEnd()) {
127 // Report error but finish scanning without crashing
128 error(line, "Unterminated string.");
129 return;
130 }
131
132 advance();
133
134 std::string word{program.substr(start + 1, current - start - 2)};
135 addToken(STRING, std::move(word));
136}
137
138bool Scanner::match(const char &expected) {
139 if (isAtEnd() || program[current] != expected) {
140 return false;
141 }
142
143 current++;
144 return true;
145}
146
147char Scanner::peek() const {
148 if (isAtEnd()) {
149 return '\0';
150 }
151 return program[current];
152}
153
154char Scanner::peekNext() const {
155 if (current + 1 >= static_cast<int>(program.size())) {
156 return '\0';
157 }
158 return program[current + 1];
159}
160
161bool Scanner::isAtEnd() const {
162 return current >= static_cast<int>(program.size());
163}
164
165char Scanner::advance() { return program[current++]; }
166
167Scanner::Scanner(std::string_view program) : program{program} {
168 tokens.reserve(256);
169}
170
171void Scanner::scanTokens() {
172 while (!isAtEnd()) {
173 start = current;
174 scanToken();
175 }
176
177 tokens.emplace_back(END_OF_FILE, "", nullptr, line);
178}
179
180std::vector<Token> Scanner::getTokens() const { return tokens; }
bool isDigit(const char c)
Definition scanner.cpp:4
bool isAlpha(const char c)
Definition scanner.cpp:6
bool isAlphaNumeric(const char c)
Definition scanner.cpp:10