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