lucidaflow 1.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of lucidaflow might be problematic. Click here for more details.
- lucidaflow/__init__.py +0 -0
- lucidaflow/lucida_analyzer.py +1043 -0
- lucidaflow/lucida_ast.py +321 -0
- lucidaflow/lucida_errors.py +26 -0
- lucidaflow/lucida_interpreter.py +821 -0
- lucidaflow/lucida_lexer.py +248 -0
- lucidaflow/lucida_parser.py +584 -0
- lucidaflow/lucida_stdlib.py +249 -0
- lucidaflow/lucida_symbols.py +176 -0
- lucidaflow-1.0.0.dist-info/METADATA +1567 -0
- lucidaflow-1.0.0.dist-info/RECORD +14 -0
- lucidaflow-1.0.0.dist-info/WHEEL +5 -0
- lucidaflow-1.0.0.dist-info/licenses/LICENSE +21 -0
- lucidaflow-1.0.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
T_INT, T_FLOAT, T_STRING, T_IDENTIFIER, T_KEYWORD = 'INT', 'FLOAT', 'STRING', 'IDENTIFIER', 'KEYWORD'
|
|
2
|
+
T_PLUS, T_MINUS, T_MUL, T_DIV, T_POW, T_MOD = 'PLUS', 'MINUS', 'MUL', 'DIV', 'POW', 'MOD'
|
|
3
|
+
T_ASSIGN, T_EQ, T_NE, T_LT, T_GT, T_LTE, T_GTE = 'ASSIGN', 'EQ', 'NE', 'LT', 'GT', 'LTE', 'GTE'
|
|
4
|
+
T_PLUS_ASSIGN, T_MINUS_ASSIGN, T_MUL_ASSIGN, T_DIV_ASSIGN, T_POW_ASSIGN, T_MOD_ASSIGN = 'PLUS_ASSIGN', 'MINUS_ASSIGN', 'MUL_ASSIGN', 'DIV_ASSIGN', 'POW_ASSIGN', 'MOD_ASSIGN'
|
|
5
|
+
T_LPAREN, T_RPAREN, T_LBRACE, T_RBRACE, T_LBRACKET, T_RBRACKET = 'LPAREN', 'RPAREN', 'LBRACE', 'RBRACE', 'LBRACKET', 'RBRACKET'
|
|
6
|
+
T_COMMA, T_COLON, T_ARROW, T_DOT, T_QUESTION, T_EOF = 'COMMA', 'COLON', 'ARROW', 'DOT', 'QUESTION', 'EOF'
|
|
7
|
+
T_AMPERSAND, T_PIPE, T_CARET, T_TILDE, T_LSHIFT, T_RSHIFT = 'AMPERSAND', 'PIPE', 'CARET', 'TILDE', 'LSHIFT', 'RSHIFT'
|
|
8
|
+
T_F_STRING = 'F_STRING'
|
|
9
|
+
|
|
10
|
+
class Token:
|
|
11
|
+
"""Um Token agora carrega seu tipo, valor e sua localização no código-fonte."""
|
|
12
|
+
def __init__(self, type, value=None, line=0, col=0):
|
|
13
|
+
self.type = type
|
|
14
|
+
self.value = value
|
|
15
|
+
self.line = line
|
|
16
|
+
self.col = col
|
|
17
|
+
|
|
18
|
+
def __repr__(self):
|
|
19
|
+
# A nova representação ajuda muito no debugging!
|
|
20
|
+
return f'Token({self.type}, {repr(self.value)}, L{self.line}:C{self.col})'
|
|
21
|
+
|
|
22
|
+
class Lexer:
|
|
23
|
+
def __init__(self, text):
|
|
24
|
+
self.text = text
|
|
25
|
+
self.pos = 0
|
|
26
|
+
self.current_char = self.text[self.pos] if self.pos < len(self.text) else None
|
|
27
|
+
# Adicionamos os contadores de linha e coluna
|
|
28
|
+
self.line = 1
|
|
29
|
+
self.col = 1
|
|
30
|
+
self.KEYWORDS = {
|
|
31
|
+
'let': Token(T_KEYWORD, 'let'),
|
|
32
|
+
'const': Token(T_KEYWORD, 'const'),
|
|
33
|
+
'when': Token(T_KEYWORD, 'when'),
|
|
34
|
+
'else': Token(T_KEYWORD, 'else'),
|
|
35
|
+
'otherwise': Token(T_KEYWORD, 'otherwise'),
|
|
36
|
+
'for': Token(T_KEYWORD, 'for'),
|
|
37
|
+
'each': Token(T_KEYWORD, 'each'),
|
|
38
|
+
'in': Token(T_KEYWORD, 'in'),
|
|
39
|
+
'while': Token(T_KEYWORD, 'while'),
|
|
40
|
+
'define': Token(T_KEYWORD, 'define'),
|
|
41
|
+
'process': Token(T_KEYWORD, 'process'),
|
|
42
|
+
'type': Token(T_KEYWORD, 'type'),
|
|
43
|
+
'return': Token(T_KEYWORD, 'return'),
|
|
44
|
+
'true': Token(T_KEYWORD, 'true'),
|
|
45
|
+
'false': Token(T_KEYWORD, 'false'),
|
|
46
|
+
'and': Token(T_KEYWORD, 'and'),
|
|
47
|
+
'or': Token(T_KEYWORD, 'or'),
|
|
48
|
+
'not': Token(T_KEYWORD, 'not'),
|
|
49
|
+
'null': Token(T_KEYWORD, 'null'),
|
|
50
|
+
'break': Token(T_KEYWORD, 'break'),
|
|
51
|
+
'continue': Token(T_KEYWORD, 'continue'),
|
|
52
|
+
'import': Token(T_KEYWORD, 'import'),
|
|
53
|
+
'as': Token(T_KEYWORD, 'as'),
|
|
54
|
+
'self': Token(T_KEYWORD, 'self'),
|
|
55
|
+
'super': Token(T_KEYWORD, 'super'),
|
|
56
|
+
'enum': Token(T_KEYWORD, 'enum'),
|
|
57
|
+
'try': Token(T_KEYWORD, 'try'), # <--- ADICIONE
|
|
58
|
+
'catch': Token(T_KEYWORD, 'catch'), # <--- ADICIONE
|
|
59
|
+
'finally': Token(T_KEYWORD, 'finally') # <--- ADICIONE
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
def error(self, msg='Caractere inválido'):
|
|
63
|
+
raise Exception(f'{msg}: {self.current_char}')
|
|
64
|
+
|
|
65
|
+
def advance(self):
|
|
66
|
+
"""Avança o ponteiro e atualiza os contadores de linha/coluna."""
|
|
67
|
+
if self.current_char is not None:
|
|
68
|
+
if self.current_char == '\n':
|
|
69
|
+
self.line += 1
|
|
70
|
+
self.col = 0 # A coluna é resetada após a quebra de linha
|
|
71
|
+
|
|
72
|
+
self.pos += 1
|
|
73
|
+
self.col += 1 # A coluna sempre avança com o caractere
|
|
74
|
+
|
|
75
|
+
self.current_char = self.text[self.pos] if self.pos < len(self.text) else None
|
|
76
|
+
|
|
77
|
+
def peek(self):
|
|
78
|
+
peek_pos = self.pos + 1
|
|
79
|
+
return self.text[peek_pos] if peek_pos < len(self.text) else None
|
|
80
|
+
|
|
81
|
+
def skip_whitespace(self):
|
|
82
|
+
while self.current_char is not None and self.current_char.isspace():
|
|
83
|
+
self.advance()
|
|
84
|
+
|
|
85
|
+
def skip_comment(self):
|
|
86
|
+
while self.current_char is not None and self.current_char != '\n':
|
|
87
|
+
self.advance()
|
|
88
|
+
|
|
89
|
+
def _number(self):
|
|
90
|
+
result = ''
|
|
91
|
+
while self.current_char is not None and self.current_char.isdigit():
|
|
92
|
+
result += self.current_char
|
|
93
|
+
self.advance()
|
|
94
|
+
if self.current_char == '.':
|
|
95
|
+
result += '.'
|
|
96
|
+
self.advance()
|
|
97
|
+
while self.current_char is not None and self.current_char.isdigit():
|
|
98
|
+
result += self.current_char
|
|
99
|
+
self.advance()
|
|
100
|
+
if '.' in result:
|
|
101
|
+
return Token(T_FLOAT, float(result))
|
|
102
|
+
else:
|
|
103
|
+
return Token(T_INT, int(result))
|
|
104
|
+
|
|
105
|
+
def _string(self):
|
|
106
|
+
result = ''
|
|
107
|
+
self.advance() # Pula o " inicial
|
|
108
|
+
while self.current_char is not None and self.current_char != '"':
|
|
109
|
+
result += self.current_char
|
|
110
|
+
self.advance()
|
|
111
|
+
self.advance() # Pula o " final
|
|
112
|
+
return Token(T_STRING, result)
|
|
113
|
+
|
|
114
|
+
def _identifier(self):
|
|
115
|
+
start_line, start_col = self.line, self.col # Guarda a posição inicial
|
|
116
|
+
result = ''
|
|
117
|
+
while self.current_char is not None and (self.current_char.isalnum() or self.current_char == '_'):
|
|
118
|
+
result += self.current_char
|
|
119
|
+
self.advance()
|
|
120
|
+
|
|
121
|
+
# Pega o token do keyword ou cria um novo IDENTIFIER, passando a posição
|
|
122
|
+
token = self.KEYWORDS.get(result, Token(T_IDENTIFIER, result, start_line, start_col))
|
|
123
|
+
|
|
124
|
+
# Se o token veio do dicionário, ele não tem posição, então atualizamos
|
|
125
|
+
token.line = start_line
|
|
126
|
+
token.col = start_col
|
|
127
|
+
|
|
128
|
+
return token
|
|
129
|
+
|
|
130
|
+
def peek_token(self):
|
|
131
|
+
saved_pos = self.pos
|
|
132
|
+
saved_char = self.current_char
|
|
133
|
+
token = self.get_next_token()
|
|
134
|
+
self.pos = saved_pos
|
|
135
|
+
self.current_char = saved_char
|
|
136
|
+
return token
|
|
137
|
+
|
|
138
|
+
def get_next_token(self):
|
|
139
|
+
while self.current_char is not None:
|
|
140
|
+
if self.current_char.isspace():
|
|
141
|
+
self.skip_whitespace()
|
|
142
|
+
continue
|
|
143
|
+
if (self.current_char == '/' and self.peek() == '/') or self.current_char == '#':
|
|
144
|
+
self.skip_comment()
|
|
145
|
+
continue
|
|
146
|
+
if self.current_char == 'f' and self.peek() == '"':
|
|
147
|
+
return self._f_string()
|
|
148
|
+
if self.current_char.isdigit():
|
|
149
|
+
return self._number()
|
|
150
|
+
if self.current_char in ('"', "'"):
|
|
151
|
+
return self._string()
|
|
152
|
+
if self.current_char.isalnum() or self.current_char == '_':
|
|
153
|
+
return self._identifier()
|
|
154
|
+
|
|
155
|
+
# Operadores de múltiplos caracteres
|
|
156
|
+
if self.current_char == '=' and self.peek() == '=':
|
|
157
|
+
self.advance(); self.advance(); return Token(T_EQ, '==')
|
|
158
|
+
if self.current_char == '!' and self.peek() == '=':
|
|
159
|
+
self.advance(); self.advance(); return Token(T_NE, '!=')
|
|
160
|
+
if self.current_char == '<' and self.peek() == '=':
|
|
161
|
+
self.advance(); self.advance(); return Token(T_LTE, '<=')
|
|
162
|
+
if self.current_char == '>' and self.peek() == '=':
|
|
163
|
+
self.advance(); self.advance(); return Token(T_GTE, '>=')
|
|
164
|
+
if self.current_char == '-' and self.peek() == '>':
|
|
165
|
+
self.advance(); self.advance(); return Token(T_ARROW, '->')
|
|
166
|
+
if self.current_char == '+' and self.peek() == '=':
|
|
167
|
+
self.advance(); self.advance(); return Token(T_PLUS_ASSIGN, '+=')
|
|
168
|
+
if self.current_char == '-' and self.peek() == '=':
|
|
169
|
+
self.advance(); self.advance(); return Token(T_MINUS_ASSIGN, '-=')
|
|
170
|
+
if self.current_char == '*' and self.peek() == '=':
|
|
171
|
+
self.advance(); self.advance(); return Token(T_MUL_ASSIGN, '*=')
|
|
172
|
+
if self.current_char == '/' and self.peek() == '=':
|
|
173
|
+
self.advance(); self.advance(); return Token(T_DIV_ASSIGN, '/=')
|
|
174
|
+
if self.current_char == '*' and self.peek() == '*':
|
|
175
|
+
self.advance()
|
|
176
|
+
if self.peek() == '=':
|
|
177
|
+
self.advance(); self.advance(); return Token(T_POW_ASSIGN, '**=')
|
|
178
|
+
self.advance(); return Token(T_POW, '**')
|
|
179
|
+
if self.current_char == '%' and self.peek() == '=':
|
|
180
|
+
self.advance(); self.advance(); return Token(T_MOD_ASSIGN, '%=')
|
|
181
|
+
if self.current_char == '<' and self.peek() == '<':
|
|
182
|
+
self.advance(); self.advance(); return Token(T_LSHIFT, '<<')
|
|
183
|
+
if self.current_char == '>' and self.peek() == '>':
|
|
184
|
+
self.advance(); self.advance(); return Token(T_RSHIFT, '>>')
|
|
185
|
+
|
|
186
|
+
start_line, start_col = self.line, self.col
|
|
187
|
+
if self.current_char == '=' and self.peek() == '=': self.advance(); self.advance(); return Token(T_EQ, '==', start_line, start_col)
|
|
188
|
+
|
|
189
|
+
# Operadores de um caractere
|
|
190
|
+
token_map = {'+':T_PLUS,'-':T_MINUS,'*':T_MUL,'/':T_DIV,'%':T_MOD,'(':T_LPAREN,')':T_RPAREN,'{':T_LBRACE,'}':T_RBRACE,'[':T_LBRACKET,']':T_RBRACKET,',':T_COMMA,':':T_COLON,'.':T_DOT,'=':T_ASSIGN,'<':T_LT,'>':T_GT, '&':T_AMPERSAND, '|':T_PIPE, '^':T_CARET, '~':T_TILDE, '?':T_QUESTION}
|
|
191
|
+
if self.current_char in token_map:
|
|
192
|
+
char = self.current_char
|
|
193
|
+
start_line, start_col = self.line, self.col # Guarda a posição
|
|
194
|
+
self.advance()
|
|
195
|
+
return Token(token_map[char], char, start_line, start_col)
|
|
196
|
+
|
|
197
|
+
self.error()
|
|
198
|
+
return Token(T_EOF)
|
|
199
|
+
|
|
200
|
+
def _string(self):
|
|
201
|
+
start_line, start_col = self.line, self.col
|
|
202
|
+
result = ''
|
|
203
|
+
quote_char = self.current_char # Salva o caractere de aspas (' ou ")
|
|
204
|
+
self.advance() # Pula a aspa inicial
|
|
205
|
+
|
|
206
|
+
while self.current_char is not None and self.current_char != quote_char:
|
|
207
|
+
# --- LÓGICA DE ESCAPE ---
|
|
208
|
+
if self.current_char == '\\':
|
|
209
|
+
self.advance() # Pula a barra
|
|
210
|
+
if self.current_char == 'n':
|
|
211
|
+
result += '\n'
|
|
212
|
+
elif self.current_char == 't':
|
|
213
|
+
result += '\t'
|
|
214
|
+
elif self.current_char == quote_char: # Escapando a própria aspa
|
|
215
|
+
result += quote_char
|
|
216
|
+
elif self.current_char == '\\':
|
|
217
|
+
result += '\\'
|
|
218
|
+
else:
|
|
219
|
+
# Se for uma sequência desconhecida, apenas adiciona o caractere literal
|
|
220
|
+
result += self.current_char
|
|
221
|
+
else:
|
|
222
|
+
result += self.current_char
|
|
223
|
+
# --- FIM DA LÓGICA DE ESCAPE ---
|
|
224
|
+
self.advance()
|
|
225
|
+
|
|
226
|
+
if self.current_char is None:
|
|
227
|
+
self.error(f"String iniciada com {quote_char} não foi fechada.")
|
|
228
|
+
|
|
229
|
+
self.advance() # Pula a aspa final
|
|
230
|
+
return Token(T_STRING, result, start_line, start_col)
|
|
231
|
+
|
|
232
|
+
def _f_string(self):
|
|
233
|
+
start_line, start_col = self.line, self.col
|
|
234
|
+
self.advance() # Consome o 'f'
|
|
235
|
+
self.advance() # Consome o '"' inicial
|
|
236
|
+
|
|
237
|
+
result = ''
|
|
238
|
+
while self.current_char is not None and self.current_char != '"':
|
|
239
|
+
result += self.current_char
|
|
240
|
+
self.advance()
|
|
241
|
+
|
|
242
|
+
if self.current_char is None:
|
|
243
|
+
self.error("F-string não foi fechada.")
|
|
244
|
+
|
|
245
|
+
self.advance() # Consome o '"' final
|
|
246
|
+
|
|
247
|
+
# O token T_F_STRING foi definido em um passo anterior
|
|
248
|
+
return Token(T_F_STRING, result, start_line, start_col)
|