Phasor 3.3.0
Stack VM based Programming Language
Loading...
Searching...
No Matches
Lexer.cpp
Go to the documentation of this file.
1#include "Lexer.hpp"
2#include <cctype>
3#include <sstream>
4#include <algorithm>
5#include <stdexcept>
6#include <utility>
7#include <phsint.hpp>
8
9namespace Phasor
10{
11
12Lexer::Lexer(std::string source) : source(std::move(source))
13{
14 std::erase(this->source, '\r');
15}
16
18{
19 if (position == 0 && peek() == '#' && position + 1 < source.length() && source[position + 1] == '!')
20 {
21 while (!isAtEnd() && peek() != '\n')
22 {
23 advance();
24 }
25 }
26}
27
28std::vector<Token> Lexer::tokenize()
29{
30 std::vector<Token> tokens;
32 while (!isAtEnd())
33 {
35 if (isAtEnd())
36 {
37 break;
38 }
39 tokens.push_back(scanToken());
40 }
41 tokens.push_back({Phasor::TokenType::EndOfFile, "", line, column});
42 return tokens;
43}
44
46{
47 if (isAtEnd())
48 {
49 return '\0';
50 }
51 return source[position];
52}
53
55{
56 char c = source[position++];
57 column++;
58 if (c == '\n')
59 {
60 line++;
61 column = 1;
62 }
63 return c;
64}
65
67{
68 return position >= source.length();
69}
70
72{
73 while (!isAtEnd())
74 {
75 char c = peek();
76 if (std::isspace(static_cast<unsigned char>(c)) != 0)
77 {
78 advance();
79 }
80 else if (c == '/' && position + 1 < source.length() && source[position + 1] == '/')
81 {
82 // Skip single-line comment
83 while (!isAtEnd() && peek() != '\n')
84 {
85 advance();
86 }
87 }
88 else
89 {
90 break;
91 }
92 }
93}
94
96{
97 char c = peek();
98 if (std::isalpha(static_cast<unsigned char>(c)) != 0)
99 {
100 return identifier();
101 }
102 if (std::isdigit(static_cast<unsigned char>(c)) != 0)
103 {
104 return number();
105 }
106 if (c == '"')
107 {
108 return string();
109 }
110 if (c == '`')
111 {
112 return complexString();
113 }
114
115 // Multi-character operators
116 if (c == '+' && position + 1 < source.length() && source[position + 1] == '+')
117 {
118 advance();
119 advance();
120 return {Phasor::TokenType::Symbol, "++", line, column};
121 }
122 if (c == '-' && position + 1 < source.length() && source[position + 1] == '-')
123 {
124 advance();
125 advance();
126 return {Phasor::TokenType::Symbol, "--", line, column};
127 }
128 if (c == '=' && position + 1 < source.length() && source[position + 1] == '=')
129 {
130 advance();
131 advance();
132 return {Phasor::TokenType::Symbol, "==", line, column};
133 }
134 if (c == '!' && position + 1 < source.length() && source[position + 1] == '=')
135 {
136 advance();
137 advance();
138 return {Phasor::TokenType::Symbol, "!=", line, column};
139 }
140 if (c == '-' && position + 1 < source.length() && source[position + 1] == '>')
141 {
142 advance();
143 advance();
144 return {Phasor::TokenType::Symbol, "->", line, column};
145 }
146 if (c == '<' && position + 1 < source.length() && source[position + 1] == '=')
147 {
148 advance();
149 advance();
150 return {Phasor::TokenType::Symbol, "<=", line, column};
151 }
152 if (c == '>' && position + 1 < source.length() && source[position + 1] == '=')
153 {
154 advance();
155 advance();
156 return {Phasor::TokenType::Symbol, ">=", line, column};
157 }
158 if (c == '&' && position + 1 < source.length() && source[position + 1] == '&')
159 {
160 advance();
161 advance();
162 return {Phasor::TokenType::Symbol, "&&", line, column};
163 }
164 if (c == '|' && position + 1 < source.length() && source[position + 1] == '|')
165 {
166 advance();
167 advance();
168 return {Phasor::TokenType::Symbol, "||", line, column};
169 }
170
171 // Single-character symbols (parentheses, operators, punctuation, etc.)
172 if (std::string("()+-*/%<>=!&|.{}:;,[]").find(c) != std::string::npos)
173 {
174 advance();
175 return {Phasor::TokenType::Symbol, std::string(1, c), line, column};
176 }
177
178 advance();
179 return {Phasor::TokenType::Unknown, std::string(1, c), line, column};
180}
181
183{
184 size_t start = position;
185 while ((std::isalnum(static_cast<unsigned char>(peek())) != 0) || peek() == '_')
186 {
187 advance();
188 }
189 std::string text = source.substr(start, position - start);
190
191 static const std::vector<std::string> keywords = {"var", "fn", "if", "else", "while", "for",
192 "return", "true", "false", "null", "throw", "print",
193 "break", "continue", "switch", "case", "default", "include", "struct"};
194
195 for (const auto &kw : keywords)
196 {
197 if (text == kw)
198 {
199 return {Phasor::TokenType::Keyword, text, line, column};
200 }
201 }
202
204}
205
207{
208 size_t start = position;
209 while (std::isdigit(static_cast<unsigned char>(peek())) != 0)
210 {
211 advance();
212 }
213 if (peek() == '.' && position + 1 < source.length() &&
214 (std::isdigit(static_cast<unsigned char>(source[position + 1])) != 0))
215 {
216 advance();
217 while (std::isdigit(static_cast<unsigned char>(peek())) != 0)
218 {
219 advance();
220 }
221 }
222 return {Phasor::TokenType::Number, source.substr(start, position - start), line, column};
223}
224
225static int hexValue(char c)
226{
227 if (c >= '0' && c <= '9')
228 {
229 return c - '0';
230 }
231 if (c >= 'a' && c <= 'f')
232 {
233 return 10 + (c - 'a');
234 }
235 if (c >= 'A' && c <= 'F')
236 {
237 return 10 + (c - 'A');
238 }
239 return -1;
240}
241
243{
244 size_t tokenLine = line;
245 size_t tokenColumn = column;
246 std::ostringstream out;
247 advance(); // Skip opening quote
248
249 while (!isAtEnd())
250 {
251 char c = advance();
252
253 if (c == '\n')
254 return {Phasor::TokenType::Unknown, std::string(), tokenLine, tokenColumn};
255
256 if (c == '\\')
257 {
258 if (isAtEnd())
259 return {Phasor::TokenType::Unknown, std::string(), tokenLine, tokenColumn};
260
261 char esc = advance();
262 switch (esc)
263 {
264 case 'a': out << '\a'; break;
265 case 'b': out << '\b'; break;
266 case 'f': out << '\f'; break;
267 case 'n': out << '\n'; break;
268 case 'r': out << '\r'; break;
269 case 't': out << '\t'; break;
270 case 'v': out << '\v'; break;
271 case '\\': out << '\\'; break;
272 case '\'': out << '\''; break;
273 case '"': out << '"'; break;
274
275 case 'e':
276 case 'E':
277 out << '\x1b';
278 break;
279
280 case '0': case '1': case '2': case '3':
281 case '4': case '5': case '6': case '7':
282 {
283 u32 val = static_cast<u32>(esc - '0');
284 for (int i = 1; i < 3 && !isAtEnd(); ++i)
285 {
286 char d = peek();
287 if (d < '0' || d > '7') break;
288 advance();
289 val = val * 8 + static_cast<u32>(d - '0');
290 }
291 if (val > 0xFF)
292 return {Phasor::TokenType::Unknown, std::string(), tokenLine, tokenColumn};
293 out << static_cast<char>(val);
294 break;
295 }
296
297 case 'x':
298 {
299 if (isAtEnd() || hexValue(peek()) < 0)
300 return {Phasor::TokenType::Unknown, std::string(), tokenLine, tokenColumn};
301 int val = hexValue(advance());
302 if (!isAtEnd() && hexValue(peek()) >= 0)
303 val = (val << 4) | hexValue(advance());
304 out << static_cast<char>(val);
305 break;
306 }
307
308 case 'u':
309 case 'U':
310 {
311 int ndigits = (esc == 'u') ? 4 : 8;
312 u32 cp = 0;
313 for (int i = 0; i < ndigits; ++i)
314 {
315 if (isAtEnd() || hexValue(peek()) < 0)
316 return {Phasor::TokenType::Unknown, std::string(), tokenLine, tokenColumn};
317 cp = (cp << 4) | static_cast<u32>(hexValue(advance()));
318 }
319 if (cp > 0x10FFFF || (cp >= 0xD800 && cp <= 0xDFFF))
320 return {Phasor::TokenType::Unknown, std::string(), tokenLine, tokenColumn};
321 if (cp <= 0x7F) { out << static_cast<char>(cp); }
322 else if (cp <= 0x7FF) { out << static_cast<char>(0xC0 | (cp >> 6))
323 << static_cast<char>(0x80 | (cp & 0x3F)); }
324 else if (cp <= 0xFFFF) { out << static_cast<char>(0xE0 | (cp >> 12))
325 << static_cast<char>(0x80 | ((cp >> 6) & 0x3F))
326 << static_cast<char>(0x80 | (cp & 0x3F)); }
327 else { out << static_cast<char>(0xF0 | (cp >> 18))
328 << static_cast<char>(0x80 | ((cp >> 12) & 0x3F))
329 << static_cast<char>(0x80 | ((cp >> 6) & 0x3F))
330 << static_cast<char>(0x80 | (cp & 0x3F)); }
331 break;
332 }
333
334 default:
335 out << esc;
336 break;
337 }
338 }
339 else if (c == '"')
340 {
341 return {Phasor::TokenType::String, out.str(), tokenLine, tokenColumn};
342 }
343 else
344 {
345 out << c;
346 }
347 }
348
349 return {Phasor::TokenType::Unknown, std::string(), tokenLine, tokenColumn};
350}
351
353{
354 size_t tokenLine = line;
355 size_t tokenColumn = column;
356 std::ostringstream out;
357 advance(); // Skip opening backtick
358
359 // Not even attempting ${} syntax for now. Just read as a raw string.
360
361 while (!isAtEnd())
362 {
363 char c = advance();
364
365 if (c == '`')
366 {
367 // Closing backtick
368 return {Phasor::TokenType::String, out.str(), tokenLine, tokenColumn};
369 }
370
371 out << c;
372 }
373
374 // If we get here, string was unterminated
375 return {Phasor::TokenType::Unknown, std::string(), tokenLine, tokenColumn};
376}
377} // namespace Phasor
Lexer(std::string source)
Definition Lexer.cpp:12
Token number()
Definition Lexer.cpp:206
bool isAtEnd()
Definition Lexer.cpp:66
void skipShebang()
Definition Lexer.cpp:17
void skipWhitespace()
Definition Lexer.cpp:71
Token complexString()
Definition Lexer.cpp:352
std::string source
Definition Lexer.hpp:30
Token identifier()
Definition Lexer.cpp:182
size_t position
Definition Lexer.hpp:31
size_t column
Definition Lexer.hpp:33
char advance()
Definition Lexer.cpp:54
std::vector< Token > tokenize()
Definition Lexer.cpp:28
Token string()
Definition Lexer.cpp:242
Token scanToken()
Definition Lexer.cpp:95
size_t line
Definition Lexer.hpp:32
char peek()
Definition Lexer.cpp:45
The Phasor Programming Language and Runtime.
Definition AST.hpp:13
static int hexValue(char c)
Definition Lexer.cpp:225
uint32_t u32
Definition phsint.hpp:11
Token structure.
Definition AST.hpp:27