bxz-lang 1.0.0__tar.gz
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.
- bxz_lang-1.0.0/LICENSE +0 -0
- bxz_lang-1.0.0/PKG-INFO +33 -0
- bxz_lang-1.0.0/README.md +4 -0
- bxz_lang-1.0.0/bxz.py +962 -0
- bxz_lang-1.0.0/bxz_lang.egg-info/PKG-INFO +33 -0
- bxz_lang-1.0.0/bxz_lang.egg-info/SOURCES.txt +9 -0
- bxz_lang-1.0.0/bxz_lang.egg-info/dependency_links.txt +1 -0
- bxz_lang-1.0.0/bxz_lang.egg-info/entry_points.txt +2 -0
- bxz_lang-1.0.0/bxz_lang.egg-info/top_level.txt +1 -0
- bxz_lang-1.0.0/setup.cfg +4 -0
- bxz_lang-1.0.0/setup.py +38 -0
bxz_lang-1.0.0/LICENSE
ADDED
|
File without changes
|
bxz_lang-1.0.0/PKG-INFO
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: bxz-lang
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: BXZ - A Cross-Platform Polyglot Programming Language
|
|
5
|
+
Author: BXZ Language Team
|
|
6
|
+
Keywords: programming language interpreter polyglot
|
|
7
|
+
Classifier: Programming Language :: Python :: 3
|
|
8
|
+
Classifier: Programming Language :: Python :: 3.6
|
|
9
|
+
Classifier: Programming Language :: Python :: 3.7
|
|
10
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
14
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
15
|
+
Classifier: Operating System :: OS Independent
|
|
16
|
+
Classifier: Environment :: Console
|
|
17
|
+
Classifier: Topic :: Software Development :: Interpreters
|
|
18
|
+
Requires-Python: >=3.6
|
|
19
|
+
Description-Content-Type: text/markdown
|
|
20
|
+
License-File: LICENSE
|
|
21
|
+
Dynamic: author
|
|
22
|
+
Dynamic: classifier
|
|
23
|
+
Dynamic: description
|
|
24
|
+
Dynamic: description-content-type
|
|
25
|
+
Dynamic: keywords
|
|
26
|
+
Dynamic: license-file
|
|
27
|
+
Dynamic: requires-python
|
|
28
|
+
Dynamic: summary
|
|
29
|
+
|
|
30
|
+
go to your cmd and type this
|
|
31
|
+
cd Desktop\bxz-lang
|
|
32
|
+
and type tis
|
|
33
|
+
run_bxz.bat --some-argument --another-flag
|
bxz_lang-1.0.0/README.md
ADDED
bxz_lang-1.0.0/bxz.py
ADDED
|
@@ -0,0 +1,962 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# bxz.py - BXZ Language v4.0 - Stable Version
|
|
3
|
+
|
|
4
|
+
import sys
|
|
5
|
+
import os
|
|
6
|
+
import re
|
|
7
|
+
import math
|
|
8
|
+
import json
|
|
9
|
+
import time
|
|
10
|
+
import random
|
|
11
|
+
import hashlib
|
|
12
|
+
import base64
|
|
13
|
+
import sqlite3
|
|
14
|
+
import threading
|
|
15
|
+
import subprocess
|
|
16
|
+
import tempfile
|
|
17
|
+
import webbrowser
|
|
18
|
+
from datetime import datetime
|
|
19
|
+
from pathlib import Path
|
|
20
|
+
from typing import Any, Dict, List, Optional, Tuple, Callable
|
|
21
|
+
|
|
22
|
+
# ============ VERSION ============
|
|
23
|
+
__version__ = "1.1.5"
|
|
24
|
+
|
|
25
|
+
# ============ TOKEN TYPES ============
|
|
26
|
+
class TokenType:
|
|
27
|
+
# Keywords
|
|
28
|
+
LET = "let"
|
|
29
|
+
CONST = "const"
|
|
30
|
+
FUNC = "func"
|
|
31
|
+
IF = "if"
|
|
32
|
+
ELSE = "else"
|
|
33
|
+
ELIF = "elif"
|
|
34
|
+
FOR = "for"
|
|
35
|
+
WHILE = "while"
|
|
36
|
+
BREAK = "break"
|
|
37
|
+
CONTINUE = "continue"
|
|
38
|
+
RETURN = "return"
|
|
39
|
+
TRY = "try"
|
|
40
|
+
CATCH = "catch"
|
|
41
|
+
FINALLY = "finally"
|
|
42
|
+
THROW = "throw"
|
|
43
|
+
IMPORT = "import"
|
|
44
|
+
FROM = "from"
|
|
45
|
+
AS = "as"
|
|
46
|
+
IN = "in"
|
|
47
|
+
IS = "is"
|
|
48
|
+
# Types
|
|
49
|
+
INT = "int"
|
|
50
|
+
FLOAT = "float"
|
|
51
|
+
STR = "str"
|
|
52
|
+
BOOL = "bool"
|
|
53
|
+
LIST = "list"
|
|
54
|
+
DICT = "dict"
|
|
55
|
+
# Literals
|
|
56
|
+
NUMBER = "NUMBER"
|
|
57
|
+
STRING = "STRING"
|
|
58
|
+
BOOLEAN = "BOOLEAN"
|
|
59
|
+
NULL = "NULL"
|
|
60
|
+
IDENTIFIER = "IDENTIFIER"
|
|
61
|
+
# Operators
|
|
62
|
+
PLUS = "+"
|
|
63
|
+
MINUS = "-"
|
|
64
|
+
MUL = "*"
|
|
65
|
+
DIV = "/"
|
|
66
|
+
MOD = "%"
|
|
67
|
+
POW = "**"
|
|
68
|
+
ASSIGN = "="
|
|
69
|
+
EQ = "=="
|
|
70
|
+
NE = "!="
|
|
71
|
+
LT = "<"
|
|
72
|
+
GT = ">"
|
|
73
|
+
LE = "<="
|
|
74
|
+
GE = ">="
|
|
75
|
+
AND = "&&"
|
|
76
|
+
OR = "||"
|
|
77
|
+
NOT = "!"
|
|
78
|
+
# Symbols
|
|
79
|
+
LPAREN = "("
|
|
80
|
+
RPAREN = ")"
|
|
81
|
+
LBRACE = "{"
|
|
82
|
+
RBRACE = "}"
|
|
83
|
+
LBRACK = "["
|
|
84
|
+
RBRACK = "]"
|
|
85
|
+
COMMA = ","
|
|
86
|
+
DOT = "."
|
|
87
|
+
COLON = ":"
|
|
88
|
+
SEMICOLON = ";"
|
|
89
|
+
ARROW = "->"
|
|
90
|
+
EOF = "EOF"
|
|
91
|
+
|
|
92
|
+
class Token:
|
|
93
|
+
def __init__(self, type_, value, line, col):
|
|
94
|
+
self.type = type_
|
|
95
|
+
self.value = value
|
|
96
|
+
self.line = line
|
|
97
|
+
self.col = col
|
|
98
|
+
|
|
99
|
+
def __repr__(self):
|
|
100
|
+
return f"Token({self.type}, {self.value})"
|
|
101
|
+
|
|
102
|
+
# ============ AST NODES ============
|
|
103
|
+
class ASTNode: pass
|
|
104
|
+
|
|
105
|
+
class Program(ASTNode):
|
|
106
|
+
def __init__(self, statements): self.statements = statements
|
|
107
|
+
|
|
108
|
+
class LetStatement(ASTNode):
|
|
109
|
+
def __init__(self, name, value, is_const=False): self.name = name; self.value = value; self.is_const = is_const
|
|
110
|
+
|
|
111
|
+
class PrintStatement(ASTNode):
|
|
112
|
+
def __init__(self, value): self.value = value
|
|
113
|
+
|
|
114
|
+
class IfStatement(ASTNode):
|
|
115
|
+
def __init__(self, condition, then_branch, else_branch=None): self.condition = condition; self.then_branch = then_branch; self.else_branch = else_branch
|
|
116
|
+
|
|
117
|
+
class WhileStatement(ASTNode):
|
|
118
|
+
def __init__(self, condition, body): self.condition = condition; self.body = body
|
|
119
|
+
|
|
120
|
+
class ForStatement(ASTNode):
|
|
121
|
+
def __init__(self, variable, iterable, body): self.variable = variable; self.iterable = iterable; self.body = body
|
|
122
|
+
|
|
123
|
+
class FunctionStatement(ASTNode):
|
|
124
|
+
def __init__(self, name, params, body): self.name = name; self.params = params; self.body = body
|
|
125
|
+
|
|
126
|
+
class ReturnStatement(ASTNode):
|
|
127
|
+
def __init__(self, value): self.value = value
|
|
128
|
+
|
|
129
|
+
class BreakStatement(ASTNode): pass
|
|
130
|
+
class ContinueStatement(ASTNode): pass
|
|
131
|
+
|
|
132
|
+
class TryStatement(ASTNode):
|
|
133
|
+
def __init__(self, try_body, catch_var, catch_body, finally_body): self.try_body = try_body; self.catch_var = catch_var; self.catch_body = catch_body; self.finally_body = finally_body
|
|
134
|
+
|
|
135
|
+
class ThrowStatement(ASTNode):
|
|
136
|
+
def __init__(self, value): self.value = value
|
|
137
|
+
|
|
138
|
+
class BlockStatement(ASTNode):
|
|
139
|
+
def __init__(self, statements): self.statements = statements
|
|
140
|
+
|
|
141
|
+
class ExpressionStatement(ASTNode):
|
|
142
|
+
def __init__(self, expression): self.expression = expression
|
|
143
|
+
|
|
144
|
+
class NumberLiteral(ASTNode):
|
|
145
|
+
def __init__(self, value): self.value = value
|
|
146
|
+
|
|
147
|
+
class StringLiteral(ASTNode):
|
|
148
|
+
def __init__(self, value): self.value = value
|
|
149
|
+
|
|
150
|
+
class BooleanLiteral(ASTNode):
|
|
151
|
+
def __init__(self, value): self.value = value
|
|
152
|
+
|
|
153
|
+
class Identifier(ASTNode):
|
|
154
|
+
def __init__(self, name): self.name = name
|
|
155
|
+
|
|
156
|
+
class BinaryOp(ASTNode):
|
|
157
|
+
def __init__(self, left, operator, right): self.left = left; self.operator = operator; self.right = right
|
|
158
|
+
|
|
159
|
+
class UnaryOp(ASTNode):
|
|
160
|
+
def __init__(self, operator, operand): self.operator = operator; self.operand = operand
|
|
161
|
+
|
|
162
|
+
class CallExpression(ASTNode):
|
|
163
|
+
def __init__(self, name, arguments): self.name = name; self.arguments = arguments
|
|
164
|
+
|
|
165
|
+
class ArrayLiteral(ASTNode):
|
|
166
|
+
def __init__(self, elements): self.elements = elements
|
|
167
|
+
|
|
168
|
+
class ObjectLiteral(ASTNode):
|
|
169
|
+
def __init__(self, properties): self.properties = properties
|
|
170
|
+
|
|
171
|
+
class LambdaExpression(ASTNode):
|
|
172
|
+
def __init__(self, params, body): self.params = params; self.body = body
|
|
173
|
+
|
|
174
|
+
# ============ LEXER ============
|
|
175
|
+
class Lexer:
|
|
176
|
+
def __init__(self, source):
|
|
177
|
+
self.source = source
|
|
178
|
+
self.pos = 0
|
|
179
|
+
self.line = 1
|
|
180
|
+
self.col = 1
|
|
181
|
+
|
|
182
|
+
self.keywords = {
|
|
183
|
+
'let': TokenType.LET, 'const': TokenType.CONST, 'func': TokenType.FUNC,
|
|
184
|
+
'if': TokenType.IF, 'else': TokenType.ELSE, 'elif': TokenType.ELIF,
|
|
185
|
+
'for': TokenType.FOR, 'while': TokenType.WHILE, 'break': TokenType.BREAK,
|
|
186
|
+
'continue': TokenType.CONTINUE, 'return': TokenType.RETURN, 'try': TokenType.TRY,
|
|
187
|
+
'catch': TokenType.CATCH, 'finally': TokenType.FINALLY, 'throw': TokenType.THROW,
|
|
188
|
+
'import': TokenType.IMPORT, 'from': TokenType.FROM, 'as': TokenType.AS,
|
|
189
|
+
'in': TokenType.IN, 'is': TokenType.IS,
|
|
190
|
+
'int': TokenType.INT, 'float': TokenType.FLOAT, 'str': TokenType.STR,
|
|
191
|
+
'bool': TokenType.BOOL, 'list': TokenType.LIST, 'dict': TokenType.DICT,
|
|
192
|
+
'true': TokenType.BOOLEAN, 'false': TokenType.BOOLEAN, 'null': TokenType.NULL,
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
def tokenize(self):
|
|
196
|
+
tokens = []
|
|
197
|
+
while self.pos < len(self.source):
|
|
198
|
+
ch = self.source[self.pos]
|
|
199
|
+
|
|
200
|
+
if ch in ' \t\r':
|
|
201
|
+
self._advance()
|
|
202
|
+
elif ch == '\n':
|
|
203
|
+
self.line += 1
|
|
204
|
+
self.col = 1
|
|
205
|
+
self.pos += 1
|
|
206
|
+
elif ch.isdigit():
|
|
207
|
+
tokens.append(self._read_number())
|
|
208
|
+
elif ch.isalpha() or ch == '_':
|
|
209
|
+
tokens.append(self._read_identifier())
|
|
210
|
+
elif ch == '"' or ch == "'":
|
|
211
|
+
tokens.append(self._read_string())
|
|
212
|
+
elif ch == '+':
|
|
213
|
+
self._advance()
|
|
214
|
+
tokens.append(Token(TokenType.PLUS, '+', self.line, self.col-1))
|
|
215
|
+
elif ch == '-':
|
|
216
|
+
self._advance()
|
|
217
|
+
tokens.append(Token(TokenType.MINUS, '-', self.line, self.col-1))
|
|
218
|
+
elif ch == '*':
|
|
219
|
+
self._advance()
|
|
220
|
+
tokens.append(Token(TokenType.MUL, '*', self.line, self.col-1))
|
|
221
|
+
elif ch == '/':
|
|
222
|
+
self._advance()
|
|
223
|
+
tokens.append(Token(TokenType.DIV, '/', self.line, self.col-1))
|
|
224
|
+
elif ch == '%':
|
|
225
|
+
self._advance()
|
|
226
|
+
tokens.append(Token(TokenType.MOD, '%', self.line, self.col-1))
|
|
227
|
+
elif ch == '=':
|
|
228
|
+
self._advance()
|
|
229
|
+
if self._peek() == '=':
|
|
230
|
+
self._advance()
|
|
231
|
+
tokens.append(Token(TokenType.EQ, '==', self.line, self.col-2))
|
|
232
|
+
else:
|
|
233
|
+
tokens.append(Token(TokenType.ASSIGN, '=', self.line, self.col-1))
|
|
234
|
+
elif ch == '!':
|
|
235
|
+
self._advance()
|
|
236
|
+
if self._peek() == '=':
|
|
237
|
+
self._advance()
|
|
238
|
+
tokens.append(Token(TokenType.NE, '!=', self.line, self.col-2))
|
|
239
|
+
else:
|
|
240
|
+
tokens.append(Token(TokenType.NOT, '!', self.line, self.col-1))
|
|
241
|
+
elif ch == '<':
|
|
242
|
+
self._advance()
|
|
243
|
+
if self._peek() == '=':
|
|
244
|
+
self._advance()
|
|
245
|
+
tokens.append(Token(TokenType.LE, '<=', self.line, self.col-2))
|
|
246
|
+
else:
|
|
247
|
+
tokens.append(Token(TokenType.LT, '<', self.line, self.col-1))
|
|
248
|
+
elif ch == '>':
|
|
249
|
+
self._advance()
|
|
250
|
+
if self._peek() == '=':
|
|
251
|
+
self._advance()
|
|
252
|
+
tokens.append(Token(TokenType.GE, '>=', self.line, self.col-2))
|
|
253
|
+
else:
|
|
254
|
+
tokens.append(Token(TokenType.GT, '>', self.line, self.col-1))
|
|
255
|
+
elif ch == '&':
|
|
256
|
+
self._advance()
|
|
257
|
+
if self._peek() == '&':
|
|
258
|
+
self._advance()
|
|
259
|
+
tokens.append(Token(TokenType.AND, '&&', self.line, self.col-2))
|
|
260
|
+
else:
|
|
261
|
+
tokens.append(Token(TokenType.BIT_AND, '&', self.line, self.col-1))
|
|
262
|
+
elif ch == '|':
|
|
263
|
+
self._advance()
|
|
264
|
+
if self._peek() == '|':
|
|
265
|
+
self._advance()
|
|
266
|
+
tokens.append(Token(TokenType.OR, '||', self.line, self.col-2))
|
|
267
|
+
else:
|
|
268
|
+
tokens.append(Token(TokenType.BIT_OR, '|', self.line, self.col-1))
|
|
269
|
+
elif ch == '(':
|
|
270
|
+
self._advance()
|
|
271
|
+
tokens.append(Token(TokenType.LPAREN, '(', self.line, self.col-1))
|
|
272
|
+
elif ch == ')':
|
|
273
|
+
self._advance()
|
|
274
|
+
tokens.append(Token(TokenType.RPAREN, ')', self.line, self.col-1))
|
|
275
|
+
elif ch == '{':
|
|
276
|
+
self._advance()
|
|
277
|
+
tokens.append(Token(TokenType.LBRACE, '{', self.line, self.col-1))
|
|
278
|
+
elif ch == '}':
|
|
279
|
+
self._advance()
|
|
280
|
+
tokens.append(Token(TokenType.RBRACE, '}', self.line, self.col-1))
|
|
281
|
+
elif ch == '[':
|
|
282
|
+
self._advance()
|
|
283
|
+
tokens.append(Token(TokenType.LBRACK, '[', self.line, self.col-1))
|
|
284
|
+
elif ch == ']':
|
|
285
|
+
self._advance()
|
|
286
|
+
tokens.append(Token(TokenType.RBRACK, ']', self.line, self.col-1))
|
|
287
|
+
elif ch == ',':
|
|
288
|
+
self._advance()
|
|
289
|
+
tokens.append(Token(TokenType.COMMA, ',', self.line, self.col-1))
|
|
290
|
+
elif ch == '.':
|
|
291
|
+
self._advance()
|
|
292
|
+
tokens.append(Token(TokenType.DOT, '.', self.line, self.col-1))
|
|
293
|
+
elif ch == ':':
|
|
294
|
+
self._advance()
|
|
295
|
+
tokens.append(Token(TokenType.COLON, ':', self.line, self.col-1))
|
|
296
|
+
elif ch == ';':
|
|
297
|
+
self._advance()
|
|
298
|
+
tokens.append(Token(TokenType.SEMICOLON, ';', self.line, self.col-1))
|
|
299
|
+
else:
|
|
300
|
+
raise SyntaxError(f"Unknown character '{ch}' at line {self.line}, col {self.col}")
|
|
301
|
+
|
|
302
|
+
tokens.append(Token(TokenType.EOF, None, self.line, self.col))
|
|
303
|
+
return tokens
|
|
304
|
+
|
|
305
|
+
def _advance(self):
|
|
306
|
+
self.pos += 1
|
|
307
|
+
self.col += 1
|
|
308
|
+
|
|
309
|
+
def _peek(self):
|
|
310
|
+
return self.source[self.pos] if self.pos < len(self.source) else None
|
|
311
|
+
|
|
312
|
+
def _read_number(self):
|
|
313
|
+
start = self.pos
|
|
314
|
+
has_dot = False
|
|
315
|
+
while self.pos < len(self.source) and (self.source[self.pos].isdigit() or self.source[self.pos] == '.'):
|
|
316
|
+
if self.source[self.pos] == '.':
|
|
317
|
+
has_dot = True
|
|
318
|
+
self._advance()
|
|
319
|
+
num_str = self.source[start:self.pos]
|
|
320
|
+
if has_dot:
|
|
321
|
+
value = float(num_str)
|
|
322
|
+
else:
|
|
323
|
+
value = int(num_str)
|
|
324
|
+
return Token(TokenType.NUMBER, value, self.line, self.col - (self.pos - start))
|
|
325
|
+
|
|
326
|
+
def _read_identifier(self):
|
|
327
|
+
start = self.pos
|
|
328
|
+
while self.pos < len(self.source) and (self.source[self.pos].isalnum() or self.source[self.pos] == '_'):
|
|
329
|
+
self._advance()
|
|
330
|
+
ident = self.source[start:self.pos]
|
|
331
|
+
token_type = self.keywords.get(ident, TokenType.IDENTIFIER)
|
|
332
|
+
if token_type == TokenType.BOOLEAN:
|
|
333
|
+
value = True if ident == 'true' else False
|
|
334
|
+
return Token(token_type, value, self.line, self.col - (self.pos - start))
|
|
335
|
+
return Token(token_type, ident, self.line, self.col - (self.pos - start))
|
|
336
|
+
|
|
337
|
+
def _read_string(self):
|
|
338
|
+
quote = self.source[self.pos]
|
|
339
|
+
self._advance()
|
|
340
|
+
start = self.pos
|
|
341
|
+
while self.pos < len(self.source) and self.source[self.pos] != quote:
|
|
342
|
+
if self.source[self.pos] == '\n':
|
|
343
|
+
raise SyntaxError("Unterminated string")
|
|
344
|
+
self._advance()
|
|
345
|
+
value = self.source[start:self.pos]
|
|
346
|
+
self._advance()
|
|
347
|
+
return Token(TokenType.STRING, value, self.line, self.col - (self.pos - start))
|
|
348
|
+
|
|
349
|
+
# ============ PARSER ============
|
|
350
|
+
class Parser:
|
|
351
|
+
def __init__(self, tokens):
|
|
352
|
+
self.tokens = tokens
|
|
353
|
+
self.pos = 0
|
|
354
|
+
|
|
355
|
+
def current(self):
|
|
356
|
+
return self.tokens[self.pos] if self.pos < len(self.tokens) else Token(TokenType.EOF, None, 0, 0)
|
|
357
|
+
|
|
358
|
+
def eat(self, token_type):
|
|
359
|
+
token = self.current()
|
|
360
|
+
if token.type == token_type:
|
|
361
|
+
self.pos += 1
|
|
362
|
+
return token
|
|
363
|
+
raise SyntaxError(f"Expected {token_type}, got {token.type}")
|
|
364
|
+
|
|
365
|
+
def parse(self):
|
|
366
|
+
statements = []
|
|
367
|
+
while self.current().type != TokenType.EOF:
|
|
368
|
+
stmt = self.parse_statement()
|
|
369
|
+
if stmt:
|
|
370
|
+
statements.append(stmt)
|
|
371
|
+
return Program(statements)
|
|
372
|
+
|
|
373
|
+
def parse_statement(self):
|
|
374
|
+
token = self.current()
|
|
375
|
+
|
|
376
|
+
if token.type == TokenType.LET:
|
|
377
|
+
return self.parse_let(False)
|
|
378
|
+
elif token.type == TokenType.CONST:
|
|
379
|
+
return self.parse_let(True)
|
|
380
|
+
elif token.type == TokenType.PRINT:
|
|
381
|
+
return self.parse_print()
|
|
382
|
+
elif token.type == TokenType.IF:
|
|
383
|
+
return self.parse_if()
|
|
384
|
+
elif token.type == TokenType.WHILE:
|
|
385
|
+
return self.parse_while()
|
|
386
|
+
elif token.type == TokenType.FOR:
|
|
387
|
+
return self.parse_for()
|
|
388
|
+
elif token.type == TokenType.FUNC:
|
|
389
|
+
return self.parse_function()
|
|
390
|
+
elif token.type == TokenType.RETURN:
|
|
391
|
+
return self.parse_return()
|
|
392
|
+
elif token.type == TokenType.BREAK:
|
|
393
|
+
self.eat(TokenType.BREAK)
|
|
394
|
+
return BreakStatement()
|
|
395
|
+
elif token.type == TokenType.CONTINUE:
|
|
396
|
+
self.eat(TokenType.CONTINUE)
|
|
397
|
+
return ContinueStatement()
|
|
398
|
+
elif token.type == TokenType.TRY:
|
|
399
|
+
return self.parse_try()
|
|
400
|
+
elif token.type == TokenType.THROW:
|
|
401
|
+
return self.parse_throw()
|
|
402
|
+
elif token.type == TokenType.LBRACE:
|
|
403
|
+
return self.parse_block()
|
|
404
|
+
else:
|
|
405
|
+
expr = self.parse_expression()
|
|
406
|
+
if self.current().type == TokenType.SEMICOLON:
|
|
407
|
+
self.eat(TokenType.SEMICOLON)
|
|
408
|
+
return ExpressionStatement(expr)
|
|
409
|
+
|
|
410
|
+
def parse_let(self, is_const):
|
|
411
|
+
self.eat(TokenType.LET if not is_const else TokenType.CONST)
|
|
412
|
+
name = self.eat(TokenType.IDENTIFIER).value
|
|
413
|
+
self.eat(TokenType.ASSIGN)
|
|
414
|
+
value = self.parse_expression()
|
|
415
|
+
if self.current().type == TokenType.SEMICOLON:
|
|
416
|
+
self.eat(TokenType.SEMICOLON)
|
|
417
|
+
return LetStatement(name, value, is_const)
|
|
418
|
+
|
|
419
|
+
def parse_print(self):
|
|
420
|
+
self.eat(TokenType.PRINT)
|
|
421
|
+
value = self.parse_expression()
|
|
422
|
+
if self.current().type == TokenType.SEMICOLON:
|
|
423
|
+
self.eat(TokenType.SEMICOLON)
|
|
424
|
+
return PrintStatement(value)
|
|
425
|
+
|
|
426
|
+
def parse_if(self):
|
|
427
|
+
self.eat(TokenType.IF)
|
|
428
|
+
self.eat(TokenType.LPAREN)
|
|
429
|
+
condition = self.parse_expression()
|
|
430
|
+
self.eat(TokenType.RPAREN)
|
|
431
|
+
then_branch = self.parse_block().statements
|
|
432
|
+
|
|
433
|
+
else_branch = None
|
|
434
|
+
if self.current().type == TokenType.ELSE:
|
|
435
|
+
self.eat(TokenType.ELSE)
|
|
436
|
+
else_branch = self.parse_block().statements
|
|
437
|
+
|
|
438
|
+
return IfStatement(condition, then_branch, else_branch)
|
|
439
|
+
|
|
440
|
+
def parse_while(self):
|
|
441
|
+
self.eat(TokenType.WHILE)
|
|
442
|
+
self.eat(TokenType.LPAREN)
|
|
443
|
+
condition = self.parse_expression()
|
|
444
|
+
self.eat(TokenType.RPAREN)
|
|
445
|
+
body = self.parse_block().statements
|
|
446
|
+
return WhileStatement(condition, body)
|
|
447
|
+
|
|
448
|
+
def parse_for(self):
|
|
449
|
+
self.eat(TokenType.FOR)
|
|
450
|
+
self.eat(TokenType.LPAREN)
|
|
451
|
+
var = self.eat(TokenType.IDENTIFIER).value
|
|
452
|
+
self.eat(TokenType.IN)
|
|
453
|
+
iterable = self.parse_expression()
|
|
454
|
+
self.eat(TokenType.RPAREN)
|
|
455
|
+
body = self.parse_block().statements
|
|
456
|
+
return ForStatement(var, iterable, body)
|
|
457
|
+
|
|
458
|
+
def parse_function(self):
|
|
459
|
+
self.eat(TokenType.FUNC)
|
|
460
|
+
name = self.eat(TokenType.IDENTIFIER).value
|
|
461
|
+
self.eat(TokenType.LPAREN)
|
|
462
|
+
|
|
463
|
+
params = []
|
|
464
|
+
if self.current().type != TokenType.RPAREN:
|
|
465
|
+
params.append(self.eat(TokenType.IDENTIFIER).value)
|
|
466
|
+
while self.current().type == TokenType.COMMA:
|
|
467
|
+
self.eat(TokenType.COMMA)
|
|
468
|
+
params.append(self.eat(TokenType.IDENTIFIER).value)
|
|
469
|
+
|
|
470
|
+
self.eat(TokenType.RPAREN)
|
|
471
|
+
body = self.parse_block().statements
|
|
472
|
+
return FunctionStatement(name, params, body)
|
|
473
|
+
|
|
474
|
+
def parse_return(self):
|
|
475
|
+
self.eat(TokenType.RETURN)
|
|
476
|
+
value = None
|
|
477
|
+
if self.current().type != TokenType.SEMICOLON:
|
|
478
|
+
value = self.parse_expression()
|
|
479
|
+
if self.current().type == TokenType.SEMICOLON:
|
|
480
|
+
self.eat(TokenType.SEMICOLON)
|
|
481
|
+
return ReturnStatement(value)
|
|
482
|
+
|
|
483
|
+
def parse_try(self):
|
|
484
|
+
self.eat(TokenType.TRY)
|
|
485
|
+
try_body = self.parse_block().statements
|
|
486
|
+
|
|
487
|
+
catch_var = None
|
|
488
|
+
catch_body = None
|
|
489
|
+
if self.current().type == TokenType.CATCH:
|
|
490
|
+
self.eat(TokenType.CATCH)
|
|
491
|
+
self.eat(TokenType.LPAREN)
|
|
492
|
+
catch_var = self.eat(TokenType.IDENTIFIER).value
|
|
493
|
+
self.eat(TokenType.RPAREN)
|
|
494
|
+
catch_body = self.parse_block().statements
|
|
495
|
+
|
|
496
|
+
finally_body = None
|
|
497
|
+
if self.current().type == TokenType.FINALLY:
|
|
498
|
+
self.eat(TokenType.FINALLY)
|
|
499
|
+
finally_body = self.parse_block().statements
|
|
500
|
+
|
|
501
|
+
return TryStatement(try_body, catch_var, catch_body, finally_body)
|
|
502
|
+
|
|
503
|
+
def parse_throw(self):
|
|
504
|
+
self.eat(TokenType.THROW)
|
|
505
|
+
value = self.parse_expression()
|
|
506
|
+
if self.current().type == TokenType.SEMICOLON:
|
|
507
|
+
self.eat(TokenType.SEMICOLON)
|
|
508
|
+
return ThrowStatement(value)
|
|
509
|
+
|
|
510
|
+
def parse_block(self):
|
|
511
|
+
self.eat(TokenType.LBRACE)
|
|
512
|
+
statements = []
|
|
513
|
+
while self.current().type != TokenType.RBRACE:
|
|
514
|
+
statements.append(self.parse_statement())
|
|
515
|
+
self.eat(TokenType.RBRACE)
|
|
516
|
+
return BlockStatement(statements)
|
|
517
|
+
|
|
518
|
+
def parse_expression(self):
|
|
519
|
+
return self.parse_assignment()
|
|
520
|
+
|
|
521
|
+
def parse_assignment(self):
|
|
522
|
+
left = self.parse_logical_or()
|
|
523
|
+
|
|
524
|
+
if self.current().type == TokenType.ASSIGN:
|
|
525
|
+
self.eat(TokenType.ASSIGN)
|
|
526
|
+
right = self.parse_assignment()
|
|
527
|
+
return BinaryOp(left, '=', right)
|
|
528
|
+
|
|
529
|
+
return left
|
|
530
|
+
|
|
531
|
+
def parse_logical_or(self):
|
|
532
|
+
left = self.parse_logical_and()
|
|
533
|
+
|
|
534
|
+
while self.current().type == TokenType.OR:
|
|
535
|
+
self.eat(TokenType.OR)
|
|
536
|
+
right = self.parse_logical_and()
|
|
537
|
+
left = BinaryOp(left, '||', right)
|
|
538
|
+
|
|
539
|
+
return left
|
|
540
|
+
|
|
541
|
+
def parse_logical_and(self):
|
|
542
|
+
left = self.parse_comparison()
|
|
543
|
+
|
|
544
|
+
while self.current().type == TokenType.AND:
|
|
545
|
+
self.eat(TokenType.AND)
|
|
546
|
+
right = self.parse_comparison()
|
|
547
|
+
left = BinaryOp(left, '&&', right)
|
|
548
|
+
|
|
549
|
+
return left
|
|
550
|
+
|
|
551
|
+
def parse_comparison(self):
|
|
552
|
+
left = self.parse_addition()
|
|
553
|
+
|
|
554
|
+
while self.current().type in [TokenType.EQ, TokenType.NE, TokenType.LT,
|
|
555
|
+
TokenType.GT, TokenType.LE, TokenType.GE]:
|
|
556
|
+
op = self.current()
|
|
557
|
+
self.eat(op.type)
|
|
558
|
+
right = self.parse_addition()
|
|
559
|
+
left = BinaryOp(left, op.value, right)
|
|
560
|
+
|
|
561
|
+
return left
|
|
562
|
+
|
|
563
|
+
def parse_addition(self):
|
|
564
|
+
left = self.parse_multiplication()
|
|
565
|
+
|
|
566
|
+
while self.current().type in [TokenType.PLUS, TokenType.MINUS]:
|
|
567
|
+
op = self.current()
|
|
568
|
+
self.eat(op.type)
|
|
569
|
+
right = self.parse_multiplication()
|
|
570
|
+
left = BinaryOp(left, op.value, right)
|
|
571
|
+
|
|
572
|
+
return left
|
|
573
|
+
|
|
574
|
+
def parse_multiplication(self):
|
|
575
|
+
left = self.parse_primary()
|
|
576
|
+
|
|
577
|
+
while self.current().type in [TokenType.MUL, TokenType.DIV, TokenType.MOD]:
|
|
578
|
+
op = self.current()
|
|
579
|
+
self.eat(op.type)
|
|
580
|
+
right = self.parse_primary()
|
|
581
|
+
left = BinaryOp(left, op.value, right)
|
|
582
|
+
|
|
583
|
+
return left
|
|
584
|
+
|
|
585
|
+
def parse_primary(self):
|
|
586
|
+
token = self.current()
|
|
587
|
+
|
|
588
|
+
if token.type == TokenType.NUMBER:
|
|
589
|
+
self.eat(TokenType.NUMBER)
|
|
590
|
+
return NumberLiteral(token.value)
|
|
591
|
+
elif token.type == TokenType.STRING:
|
|
592
|
+
self.eat(TokenType.STRING)
|
|
593
|
+
return StringLiteral(token.value)
|
|
594
|
+
elif token.type == TokenType.BOOLEAN:
|
|
595
|
+
self.eat(TokenType.BOOLEAN)
|
|
596
|
+
return BooleanLiteral(token.value)
|
|
597
|
+
elif token.type == TokenType.NULL:
|
|
598
|
+
self.eat(TokenType.NULL)
|
|
599
|
+
return None
|
|
600
|
+
elif token.type == TokenType.IDENTIFIER:
|
|
601
|
+
name = token.value
|
|
602
|
+
self.eat(TokenType.IDENTIFIER)
|
|
603
|
+
if self.current().type == TokenType.LPAREN:
|
|
604
|
+
return self.parse_call(name)
|
|
605
|
+
return Identifier(name)
|
|
606
|
+
elif token.type == TokenType.LPAREN:
|
|
607
|
+
self.eat(TokenType.LPAREN)
|
|
608
|
+
expr = self.parse_expression()
|
|
609
|
+
self.eat(TokenType.RPAREN)
|
|
610
|
+
return expr
|
|
611
|
+
elif token.type == TokenType.LBRACK:
|
|
612
|
+
return self.parse_array()
|
|
613
|
+
elif token.type == TokenType.LBRACE:
|
|
614
|
+
return self.parse_object()
|
|
615
|
+
|
|
616
|
+
raise SyntaxError(f"Unexpected token: {token.type}")
|
|
617
|
+
|
|
618
|
+
def parse_call(self, name):
|
|
619
|
+
self.eat(TokenType.LPAREN)
|
|
620
|
+
args = []
|
|
621
|
+
if self.current().type != TokenType.RPAREN:
|
|
622
|
+
args.append(self.parse_expression())
|
|
623
|
+
while self.current().type == TokenType.COMMA:
|
|
624
|
+
self.eat(TokenType.COMMA)
|
|
625
|
+
args.append(self.parse_expression())
|
|
626
|
+
self.eat(TokenType.RPAREN)
|
|
627
|
+
return CallExpression(name, args)
|
|
628
|
+
|
|
629
|
+
def parse_array(self):
|
|
630
|
+
self.eat(TokenType.LBRACK)
|
|
631
|
+
elements = []
|
|
632
|
+
if self.current().type != TokenType.RBRACK:
|
|
633
|
+
elements.append(self.parse_expression())
|
|
634
|
+
while self.current().type == TokenType.COMMA:
|
|
635
|
+
self.eat(TokenType.COMMA)
|
|
636
|
+
elements.append(self.parse_expression())
|
|
637
|
+
self.eat(TokenType.RBRACK)
|
|
638
|
+
return ArrayLiteral(elements)
|
|
639
|
+
|
|
640
|
+
def parse_object(self):
|
|
641
|
+
self.eat(TokenType.LBRACE)
|
|
642
|
+
properties = {}
|
|
643
|
+
if self.current().type != TokenType.RBRACE:
|
|
644
|
+
key = self.eat(TokenType.IDENTIFIER).value
|
|
645
|
+
self.eat(TokenType.COLON)
|
|
646
|
+
value = self.parse_expression()
|
|
647
|
+
properties[key] = value
|
|
648
|
+
while self.current().type == TokenType.COMMA:
|
|
649
|
+
self.eat(TokenType.COMMA)
|
|
650
|
+
key = self.eat(TokenType.IDENTIFIER).value
|
|
651
|
+
self.eat(TokenType.COLON)
|
|
652
|
+
value = self.parse_expression()
|
|
653
|
+
properties[key] = value
|
|
654
|
+
self.eat(TokenType.RBRACE)
|
|
655
|
+
return ObjectLiteral(properties)
|
|
656
|
+
|
|
657
|
+
# ============ INTERPRETER ============
|
|
658
|
+
class Interpreter:
|
|
659
|
+
def __init__(self, debug=False):
|
|
660
|
+
self.vars = {}
|
|
661
|
+
self.funcs = {}
|
|
662
|
+
self.debug = debug
|
|
663
|
+
self.return_val = None
|
|
664
|
+
self.break_flag = False
|
|
665
|
+
self.continue_flag = False
|
|
666
|
+
|
|
667
|
+
def log(self, msg):
|
|
668
|
+
if self.debug:
|
|
669
|
+
print(f"[DEBUG] {msg}")
|
|
670
|
+
|
|
671
|
+
def evaluate(self, node):
|
|
672
|
+
if isinstance(node, NumberLiteral):
|
|
673
|
+
return node.value
|
|
674
|
+
elif isinstance(node, StringLiteral):
|
|
675
|
+
return node.value
|
|
676
|
+
elif isinstance(node, BooleanLiteral):
|
|
677
|
+
return node.value
|
|
678
|
+
elif isinstance(node, Identifier):
|
|
679
|
+
if node.name in self.vars:
|
|
680
|
+
return self.vars[node.name]
|
|
681
|
+
raise RuntimeError(f"Undefined variable: {node.name}")
|
|
682
|
+
elif isinstance(node, BinaryOp):
|
|
683
|
+
left = self.evaluate(node.left)
|
|
684
|
+
right = self.evaluate(node.right)
|
|
685
|
+
|
|
686
|
+
ops = {
|
|
687
|
+
'+': lambda: left + right,
|
|
688
|
+
'-': lambda: left - right,
|
|
689
|
+
'*': lambda: left * right,
|
|
690
|
+
'/': lambda: left / right if right != 0 else 0,
|
|
691
|
+
'%': lambda: left % right,
|
|
692
|
+
'**': lambda: left ** right,
|
|
693
|
+
'==': lambda: left == right,
|
|
694
|
+
'!=': lambda: left != right,
|
|
695
|
+
'<': lambda: left < right,
|
|
696
|
+
'>': lambda: left > right,
|
|
697
|
+
'<=': lambda: left <= right,
|
|
698
|
+
'>=': lambda: left >= right,
|
|
699
|
+
'&&': lambda: left and right,
|
|
700
|
+
'||': lambda: left or right,
|
|
701
|
+
'=': lambda: right,
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
if node.operator in ops:
|
|
705
|
+
result = ops[node.operator]()
|
|
706
|
+
return result
|
|
707
|
+
raise RuntimeError(f"Unknown operator: {node.operator}")
|
|
708
|
+
elif isinstance(node, UnaryOp):
|
|
709
|
+
operand = self.evaluate(node.operand)
|
|
710
|
+
if node.operator == '!':
|
|
711
|
+
return not operand
|
|
712
|
+
elif node.operator == '-':
|
|
713
|
+
return -operand
|
|
714
|
+
return operand
|
|
715
|
+
elif isinstance(node, CallExpression):
|
|
716
|
+
if node.name == 'print':
|
|
717
|
+
args = [self.evaluate(arg) for arg in node.arguments]
|
|
718
|
+
print(*args)
|
|
719
|
+
return None
|
|
720
|
+
elif node.name == 'len':
|
|
721
|
+
return len(self.evaluate(node.arguments[0]))
|
|
722
|
+
elif node.name == 'range':
|
|
723
|
+
start = self.evaluate(node.arguments[0]) if len(node.arguments) > 0 else 0
|
|
724
|
+
end = self.evaluate(node.arguments[1]) if len(node.arguments) > 1 else start
|
|
725
|
+
step = self.evaluate(node.arguments[2]) if len(node.arguments) > 2 else 1
|
|
726
|
+
return list(range(start, end, step))
|
|
727
|
+
elif node.name == 'input':
|
|
728
|
+
prompt = self.evaluate(node.arguments[0]) if node.arguments else ""
|
|
729
|
+
return input(prompt)
|
|
730
|
+
elif node.name == 'int':
|
|
731
|
+
return int(self.evaluate(node.arguments[0]))
|
|
732
|
+
elif node.name == 'float':
|
|
733
|
+
return float(self.evaluate(node.arguments[0]))
|
|
734
|
+
elif node.name == 'str':
|
|
735
|
+
return str(self.evaluate(node.arguments[0]))
|
|
736
|
+
elif node.name == 'type':
|
|
737
|
+
return type(self.evaluate(node.arguments[0])).__name__
|
|
738
|
+
elif node.name in self.funcs:
|
|
739
|
+
return self._call_function(node.name, node.arguments)
|
|
740
|
+
elif node.name == 'server':
|
|
741
|
+
port = self.evaluate(node.arguments[0]) if node.arguments else 8080
|
|
742
|
+
self.start_server(port)
|
|
743
|
+
return None
|
|
744
|
+
elif node.name == 'json':
|
|
745
|
+
return json.dumps(self.evaluate(node.arguments[0]))
|
|
746
|
+
elif node.name == 'parse_json':
|
|
747
|
+
return json.loads(self.evaluate(node.arguments[0]))
|
|
748
|
+
raise RuntimeError(f"Undefined function: {node.name}")
|
|
749
|
+
elif isinstance(node, ArrayLiteral):
|
|
750
|
+
return [self.evaluate(elem) for elem in node.elements]
|
|
751
|
+
elif isinstance(node, ObjectLiteral):
|
|
752
|
+
result = {}
|
|
753
|
+
for key, value in node.properties.items():
|
|
754
|
+
result[key] = self.evaluate(value)
|
|
755
|
+
return result
|
|
756
|
+
return None
|
|
757
|
+
|
|
758
|
+
def _call_function(self, name, args_nodes):
|
|
759
|
+
func = self.funcs[name]
|
|
760
|
+
old_vars = self.vars.copy()
|
|
761
|
+
|
|
762
|
+
for i, param in enumerate(func.params):
|
|
763
|
+
if i < len(args_nodes):
|
|
764
|
+
self.vars[param] = self.evaluate(args_nodes[i])
|
|
765
|
+
|
|
766
|
+
self.return_val = None
|
|
767
|
+
for stmt in func.body:
|
|
768
|
+
self.execute(stmt)
|
|
769
|
+
if self.return_val is not None:
|
|
770
|
+
break
|
|
771
|
+
|
|
772
|
+
result = self.return_val
|
|
773
|
+
self.vars = old_vars
|
|
774
|
+
self.return_val = None
|
|
775
|
+
return result
|
|
776
|
+
|
|
777
|
+
def start_server(self, port):
|
|
778
|
+
try:
|
|
779
|
+
from http.server import HTTPServer, SimpleHTTPRequestHandler
|
|
780
|
+
server = HTTPServer(('localhost', port), SimpleHTTPRequestHandler)
|
|
781
|
+
url = f'http://localhost:{port}'
|
|
782
|
+
print(f"\n{'='*40}")
|
|
783
|
+
print(f"🚀 Server: {url}")
|
|
784
|
+
print(f"{'='*40}")
|
|
785
|
+
webbrowser.open(url)
|
|
786
|
+
server.serve_forever()
|
|
787
|
+
except KeyboardInterrupt:
|
|
788
|
+
print("\n👋 Server stopped")
|
|
789
|
+
|
|
790
|
+
def execute(self, node):
|
|
791
|
+
if isinstance(node, Program):
|
|
792
|
+
for stmt in node.statements:
|
|
793
|
+
self.execute(stmt)
|
|
794
|
+
if self.return_val is not None:
|
|
795
|
+
break
|
|
796
|
+
elif isinstance(node, LetStatement):
|
|
797
|
+
value = self.evaluate(node.value)
|
|
798
|
+
if node.is_const:
|
|
799
|
+
self.vars[node.name] = value
|
|
800
|
+
else:
|
|
801
|
+
self.vars[node.name] = value
|
|
802
|
+
elif isinstance(node, PrintStatement):
|
|
803
|
+
value = self.evaluate(node.value)
|
|
804
|
+
print(value)
|
|
805
|
+
elif isinstance(node, IfStatement):
|
|
806
|
+
condition = self.evaluate(node.condition)
|
|
807
|
+
if condition:
|
|
808
|
+
for stmt in node.then_branch:
|
|
809
|
+
self.execute(stmt)
|
|
810
|
+
elif node.else_branch:
|
|
811
|
+
for stmt in node.else_branch:
|
|
812
|
+
self.execute(stmt)
|
|
813
|
+
elif isinstance(node, WhileStatement):
|
|
814
|
+
self.break_flag = False
|
|
815
|
+
while self.evaluate(node.condition) and not self.break_flag:
|
|
816
|
+
for stmt in node.body:
|
|
817
|
+
self.execute(stmt)
|
|
818
|
+
if self.continue_flag:
|
|
819
|
+
self.continue_flag = False
|
|
820
|
+
break
|
|
821
|
+
elif isinstance(node, ForStatement):
|
|
822
|
+
iterable = self.evaluate(node.iterable)
|
|
823
|
+
self.break_flag = False
|
|
824
|
+
for item in iterable:
|
|
825
|
+
if self.break_flag:
|
|
826
|
+
break
|
|
827
|
+
if self.continue_flag:
|
|
828
|
+
self.continue_flag = False
|
|
829
|
+
continue
|
|
830
|
+
self.vars[node.variable] = item
|
|
831
|
+
for stmt in node.body:
|
|
832
|
+
self.execute(stmt)
|
|
833
|
+
elif isinstance(node, FunctionStatement):
|
|
834
|
+
self.funcs[node.name] = node
|
|
835
|
+
elif isinstance(node, ReturnStatement):
|
|
836
|
+
self.return_val = self.evaluate(node.value) if node.value else None
|
|
837
|
+
elif isinstance(node, BreakStatement):
|
|
838
|
+
self.break_flag = True
|
|
839
|
+
elif isinstance(node, ContinueStatement):
|
|
840
|
+
self.continue_flag = True
|
|
841
|
+
elif isinstance(node, TryStatement):
|
|
842
|
+
try:
|
|
843
|
+
for stmt in node.try_body:
|
|
844
|
+
self.execute(stmt)
|
|
845
|
+
except Exception as e:
|
|
846
|
+
if node.catch_var:
|
|
847
|
+
self.vars[node.catch_var] = str(e)
|
|
848
|
+
for stmt in node.catch_body:
|
|
849
|
+
self.execute(stmt)
|
|
850
|
+
else:
|
|
851
|
+
raise
|
|
852
|
+
finally:
|
|
853
|
+
if node.finally_body:
|
|
854
|
+
for stmt in node.finally_body:
|
|
855
|
+
self.execute(stmt)
|
|
856
|
+
elif isinstance(node, ThrowStatement):
|
|
857
|
+
raise RuntimeError(str(self.evaluate(node.value)))
|
|
858
|
+
elif isinstance(node, ExpressionStatement):
|
|
859
|
+
self.evaluate(node.expression)
|
|
860
|
+
elif isinstance(node, BlockStatement):
|
|
861
|
+
for stmt in node.statements:
|
|
862
|
+
self.execute(stmt)
|
|
863
|
+
|
|
864
|
+
# ============ MAIN ============
|
|
865
|
+
def run_file(filename, debug=False):
|
|
866
|
+
if not os.path.exists(filename):
|
|
867
|
+
print(f"Error: File '{filename}' not found!")
|
|
868
|
+
return False
|
|
869
|
+
|
|
870
|
+
try:
|
|
871
|
+
with open(filename, 'r', encoding='utf-8') as f:
|
|
872
|
+
source = f.read()
|
|
873
|
+
|
|
874
|
+
lexer = Lexer(source)
|
|
875
|
+
tokens = lexer.tokenize()
|
|
876
|
+
parser = Parser(tokens)
|
|
877
|
+
ast = parser.parse()
|
|
878
|
+
interpreter = Interpreter(debug)
|
|
879
|
+
interpreter.execute(ast)
|
|
880
|
+
return True
|
|
881
|
+
except Exception as e:
|
|
882
|
+
print(f"Error: {e}")
|
|
883
|
+
if debug:
|
|
884
|
+
import traceback
|
|
885
|
+
traceback.print_exc()
|
|
886
|
+
return False
|
|
887
|
+
|
|
888
|
+
def repl(debug=False):
|
|
889
|
+
print("=" * 60)
|
|
890
|
+
print(f"⚡ BXZ Language v{__version__}")
|
|
891
|
+
print("Commands: print(), len(), range(), input(), server()")
|
|
892
|
+
print("Type 'exit' to quit, 'help' for commands")
|
|
893
|
+
print("=" * 60)
|
|
894
|
+
|
|
895
|
+
interpreter = Interpreter(debug)
|
|
896
|
+
|
|
897
|
+
while True:
|
|
898
|
+
try:
|
|
899
|
+
code = input("\n>>> ")
|
|
900
|
+
if code.strip() in ['exit', 'quit']:
|
|
901
|
+
print("Goodbye!")
|
|
902
|
+
break
|
|
903
|
+
if code.strip() == 'help':
|
|
904
|
+
print("""
|
|
905
|
+
Available commands:
|
|
906
|
+
print("text") - Print text
|
|
907
|
+
let x = value - Create variable
|
|
908
|
+
x + y, x * y - Math operations
|
|
909
|
+
func name(p) {} - Define function
|
|
910
|
+
if (cond) {} - Conditional
|
|
911
|
+
while (cond) {} - Loop
|
|
912
|
+
for item in arr {} - For-each loop
|
|
913
|
+
server(port) - Start web server
|
|
914
|
+
json(obj) - Convert to JSON
|
|
915
|
+
parse_json(str) - Parse JSON
|
|
916
|
+
len(arr) - Get length
|
|
917
|
+
range(start, end) - Create range
|
|
918
|
+
input(prompt) - Get user input
|
|
919
|
+
int(), float(), str(), type()
|
|
920
|
+
""")
|
|
921
|
+
continue
|
|
922
|
+
if code.strip():
|
|
923
|
+
lexer = Lexer(code)
|
|
924
|
+
tokens = lexer.tokenize()
|
|
925
|
+
parser = Parser(tokens)
|
|
926
|
+
ast = parser.parse()
|
|
927
|
+
interpreter.execute(ast)
|
|
928
|
+
except KeyboardInterrupt:
|
|
929
|
+
print("\nGoodbye!")
|
|
930
|
+
break
|
|
931
|
+
except Exception as e:
|
|
932
|
+
print(f"Error: {e}")
|
|
933
|
+
if debug:
|
|
934
|
+
import traceback
|
|
935
|
+
traceback.print_exc()
|
|
936
|
+
|
|
937
|
+
def main():
|
|
938
|
+
import argparse
|
|
939
|
+
parser = argparse.ArgumentParser(description=f"BXZ Language v{__version__}")
|
|
940
|
+
parser.add_argument("file", nargs="?", help="BXZ file to execute")
|
|
941
|
+
parser.add_argument("-i", "--interactive", action="store_true", help="Interactive REPL")
|
|
942
|
+
parser.add_argument("-d", "--debug", action="store_true", help="Debug mode")
|
|
943
|
+
parser.add_argument("-v", "--version", action="store_true", help="Show version")
|
|
944
|
+
parser.add_argument("-s", "--server", action="store_true", help="Start web server")
|
|
945
|
+
parser.add_argument("-p", "--port", type=int, default=8080, help="Server port")
|
|
946
|
+
|
|
947
|
+
args = parser.parse_args()
|
|
948
|
+
|
|
949
|
+
if args.version:
|
|
950
|
+
print(f"BXZ Language v{__version__}")
|
|
951
|
+
return
|
|
952
|
+
|
|
953
|
+
if args.server:
|
|
954
|
+
Interpreter().start_server(args.port)
|
|
955
|
+
elif args.interactive or not args.file:
|
|
956
|
+
repl(args.debug)
|
|
957
|
+
else:
|
|
958
|
+
success = run_file(args.file, args.debug)
|
|
959
|
+
sys.exit(0 if success else 1)
|
|
960
|
+
|
|
961
|
+
if __name__ == "__main__":
|
|
962
|
+
main()
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: bxz-lang
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: BXZ - A Cross-Platform Polyglot Programming Language
|
|
5
|
+
Author: BXZ Language Team
|
|
6
|
+
Keywords: programming language interpreter polyglot
|
|
7
|
+
Classifier: Programming Language :: Python :: 3
|
|
8
|
+
Classifier: Programming Language :: Python :: 3.6
|
|
9
|
+
Classifier: Programming Language :: Python :: 3.7
|
|
10
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
14
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
15
|
+
Classifier: Operating System :: OS Independent
|
|
16
|
+
Classifier: Environment :: Console
|
|
17
|
+
Classifier: Topic :: Software Development :: Interpreters
|
|
18
|
+
Requires-Python: >=3.6
|
|
19
|
+
Description-Content-Type: text/markdown
|
|
20
|
+
License-File: LICENSE
|
|
21
|
+
Dynamic: author
|
|
22
|
+
Dynamic: classifier
|
|
23
|
+
Dynamic: description
|
|
24
|
+
Dynamic: description-content-type
|
|
25
|
+
Dynamic: keywords
|
|
26
|
+
Dynamic: license-file
|
|
27
|
+
Dynamic: requires-python
|
|
28
|
+
Dynamic: summary
|
|
29
|
+
|
|
30
|
+
go to your cmd and type this
|
|
31
|
+
cd Desktop\bxz-lang
|
|
32
|
+
and type tis
|
|
33
|
+
run_bxz.bat --some-argument --another-flag
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
bxz
|
bxz_lang-1.0.0/setup.cfg
ADDED
bxz_lang-1.0.0/setup.py
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# setup.py - Python package installer
|
|
2
|
+
from setuptools import setup
|
|
3
|
+
import sys
|
|
4
|
+
import platform
|
|
5
|
+
|
|
6
|
+
with open("README.md", "r", encoding="utf-8") as f:
|
|
7
|
+
long_description = f.read()
|
|
8
|
+
|
|
9
|
+
setup(
|
|
10
|
+
name="bxz-lang",
|
|
11
|
+
version="1.0.0",
|
|
12
|
+
author="BXZ Language Team",
|
|
13
|
+
description="BXZ - A Cross-Platform Polyglot Programming Language",
|
|
14
|
+
long_description=long_description,
|
|
15
|
+
long_description_content_type="text/markdown",
|
|
16
|
+
py_modules=["bxz"],
|
|
17
|
+
entry_points={
|
|
18
|
+
"console_scripts": [
|
|
19
|
+
"bxz=bxz:main",
|
|
20
|
+
],
|
|
21
|
+
},
|
|
22
|
+
install_requires=[],
|
|
23
|
+
python_requires=">=3.6",
|
|
24
|
+
classifiers=[
|
|
25
|
+
"Programming Language :: Python :: 3",
|
|
26
|
+
"Programming Language :: Python :: 3.6",
|
|
27
|
+
"Programming Language :: Python :: 3.7",
|
|
28
|
+
"Programming Language :: Python :: 3.8",
|
|
29
|
+
"Programming Language :: Python :: 3.9",
|
|
30
|
+
"Programming Language :: Python :: 3.10",
|
|
31
|
+
"Programming Language :: Python :: 3.11",
|
|
32
|
+
"License :: OSI Approved :: MIT License",
|
|
33
|
+
"Operating System :: OS Independent",
|
|
34
|
+
"Environment :: Console",
|
|
35
|
+
"Topic :: Software Development :: Interpreters",
|
|
36
|
+
],
|
|
37
|
+
keywords="programming language interpreter polyglot",
|
|
38
|
+
)
|