gladlang 0.2.2__tar.gz → 0.2.4__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: gladlang
3
- Version: 0.2.2
3
+ Version: 0.2.4
4
4
  Summary: The GladLang Interpreter
5
5
  Author: Glad432
6
6
  License: Copyright (c) 2025 - present GLAD432
@@ -465,7 +465,7 @@ LET b = 3 # Binary 011
465
465
  PRINTLN a & b # 1 (AND)
466
466
  PRINTLN a | b # 7 (OR)
467
467
  PRINTLN a ^ b # 6 (XOR)
468
- PRINTLN ~a # -6 (NOT)
468
+ PRINTLN ~a # 4294967290 (bitwise NOT as unsigned 32‑bit integer)
469
469
  PRINTLN 1 << 2 # 4 (Left Shift)
470
470
  PRINTLN 8 >> 2 # 2 (Right Shift)
471
471
 
@@ -437,7 +437,7 @@ LET b = 3 # Binary 011
437
437
  PRINTLN a & b # 1 (AND)
438
438
  PRINTLN a | b # 7 (OR)
439
439
  PRINTLN a ^ b # 6 (XOR)
440
- PRINTLN ~a # -6 (NOT)
440
+ PRINTLN ~a # 4294967290 (bitwise NOT as unsigned 32‑bit integer)
441
441
  PRINTLN 1 << 2 # 4 (Left Shift)
442
442
  PRINTLN 8 >> 2 # 2 (Right Shift)
443
443
 
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "gladlang"
7
- version = "0.2.2"
7
+ version = "0.2.4"
8
8
  description = "The GladLang Interpreter"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10"
@@ -140,8 +140,10 @@ def is_complete(text):
140
140
  return False
141
141
 
142
142
  depth = 0
143
+ bracket_depth = 0
144
+ brace_depth = 0
143
145
 
144
- pattern = r"(\[|\]|\b(DEF|CLASS|ENUM|IF|ELSE\s+IF|ELSE|WHILE|FOR|TRY|SWITCH|ENDDEF|ENDCLASS|ENDENUM|ENDIF|ENDWHILE|ENDFOR|ENDTRY|ENDSWITCH)\b)"
146
+ pattern = r"(\[|\]|\{|\}|\b(DEF|CLASS|ENUM|IF|ELSE\s+IF|ELSE|WHILE|FOR|TRY|SWITCH|ENDDEF|ENDCLASS|ENDENUM|ENDIF|ENDWHILE|ENDFOR|ENDTRY|ENDSWITCH)\b)"
145
147
 
146
148
  tokens = re.finditer(pattern, temp_text)
147
149
 
@@ -161,9 +163,25 @@ def is_complete(text):
161
163
  for match in tokens:
162
164
  token = match.group()
163
165
 
166
+ if token == "[":
167
+ bracket_depth += 1
168
+ continue
169
+ elif token == "]":
170
+ bracket_depth -= 1
171
+ continue
172
+ elif token == "{":
173
+ brace_depth += 1
174
+ continue
175
+ elif token == "}":
176
+ brace_depth -= 1
177
+ continue
178
+
164
179
  if token in ("ELSE", "ELSE IF"):
165
180
  continue
166
181
 
182
+ if token == "FOR" and (bracket_depth > 0 or brace_depth > 0):
183
+ continue
184
+
167
185
  if token in start_keys:
168
186
  depth += 1
169
187
  elif token in end_keys:
@@ -174,11 +192,13 @@ def is_complete(text):
174
192
 
175
193
  def main():
176
194
  MAX_MEMORY_MB = 512
177
- MAX_INSTRUCTIONS = 100000
195
+ MAX_INSTRUCTIONS = 1_000_000
196
+ MAX_SOURCE_BYTES = 1_000_000
197
+ MAX_REPL_BUFFER = 100_000
178
198
 
179
199
  set_memory_limit(MAX_MEMORY_MB)
180
200
 
181
- GLADLANG_VERSION = "0.2.2"
201
+ GLADLANG_VERSION = "0.2.4"
182
202
  GLADLANG_HELP = f"""
183
203
  Usage: gladlang [command] [filename/code] [args...]
184
204
 
@@ -221,6 +241,13 @@ Commands:
221
241
 
222
242
  full_text += line + "\n"
223
243
 
244
+ if len(full_text) > MAX_REPL_BUFFER:
245
+ sys.stdout.write(
246
+ "Error: Input buffer limit exceeded. Clearing buffer.\n"
247
+ )
248
+ full_text = ""
249
+ continue
250
+
224
251
  if is_complete(full_text):
225
252
  if full_text.strip() == "":
226
253
  full_text = ""
@@ -308,8 +335,9 @@ Commands:
308
335
  try:
309
336
  candidate = Path(arg_input).resolve()
310
337
  allowed_root = Path.cwd().resolve()
311
- if not str(candidate).startswith(str(allowed_root)):
338
+ if not candidate.is_relative_to(allowed_root):
312
339
  raise PermissionError(f"Access denied: '{arg_input}'")
340
+
313
341
  if candidate.is_file():
314
342
  is_file = True
315
343
  resolved = candidate
@@ -326,7 +354,27 @@ Commands:
326
354
  f"File must have .glad extension: '{arg_input}'\n"
327
355
  )
328
356
  sys.exit(1)
329
- text = path_to_read.read_text(encoding="utf-8")
357
+
358
+ file_size = path_to_read.stat().st_size
359
+ if file_size > MAX_SOURCE_BYTES:
360
+ sys.stderr.write(
361
+ f"File too large: '{arg_input}' ({file_size:,} bytes). "
362
+ f"Maximum allowed: {MAX_SOURCE_BYTES:,} bytes.\n"
363
+ )
364
+ sys.exit(1)
365
+
366
+ try:
367
+ text = path_to_read.read_text(encoding="utf-8")
368
+ except UnicodeDecodeError:
369
+ sys.stderr.write(
370
+ f"Encoding error: '{arg_input}' is not valid UTF-8. "
371
+ "Save the file as UTF-8 and try again.\n"
372
+ )
373
+ sys.exit(1)
374
+ except Exception as e:
375
+ sys.stderr.write(f"An unexpected error occurred: {e}\n")
376
+ sys.exit(1)
377
+
330
378
  source_name = str(path_to_read)
331
379
  else:
332
380
  text = arg_input
@@ -1,4 +1,6 @@
1
1
  class Position:
2
+ __slots__ = ("idx", "ln", "col", "fn", "ftxt")
3
+
2
4
  def __init__(self, idx, ln, col, fn, ftxt):
3
5
  self.idx = idx
4
6
  self.ln = ln
@@ -12,6 +12,8 @@ from .values import (
12
12
  Instance,
13
13
  Enum,
14
14
  Value,
15
+ TailCall,
16
+ BoundMethod,
15
17
  )
16
18
  from .nodes import *
17
19
  from .errors import RTError
@@ -20,10 +22,15 @@ from .constants import *
20
22
 
21
23
  def is_final_anywhere(table, name):
22
24
  while table:
25
+ if table._finals_count == 0:
26
+ table = table.parent
27
+ continue
28
+
23
29
  with table._lock:
24
30
  if name in table.finals:
25
31
  return True
26
32
  table = table.parent
33
+
27
34
  return False
28
35
 
29
36
 
@@ -32,6 +39,27 @@ class Interpreter:
32
39
  self.dispatch_cache = {}
33
40
  self.instruction_limit = instruction_limit
34
41
 
42
+ self._binop_dispatch = {
43
+ GL_PLUS: lambda l, r: l.added_to(r),
44
+ GL_MINUS: lambda l, r: l.subbed_by(r),
45
+ GL_MUL: lambda l, r: l.multed_by(r),
46
+ GL_DIV: lambda l, r: l.dived_by(r),
47
+ GL_MOD: lambda l, r: l.modded_by(r),
48
+ GL_FLOORDIV: lambda l, r: l.floordived_by(r),
49
+ GL_POW: lambda l, r: l.powed_by(r),
50
+ GL_EE: lambda l, r: l.get_comparison_eq(r),
51
+ GL_NE: lambda l, r: l.get_comparison_ne(r),
52
+ GL_LT: lambda l, r: l.get_comparison_lt(r),
53
+ GL_GT: lambda l, r: l.get_comparison_gt(r),
54
+ GL_LTE: lambda l, r: l.get_comparison_lte(r),
55
+ GL_GTE: lambda l, r: l.get_comparison_gte(r),
56
+ GL_BIT_AND: lambda l, r: l.bitted_and_by(r),
57
+ GL_BIT_OR: lambda l, r: l.bitted_or_by(r),
58
+ GL_BIT_XOR: lambda l, r: l.bitted_xor_by(r),
59
+ GL_LSHIFT: lambda l, r: l.lshifted_by(r),
60
+ GL_RSHIFT: lambda l, r: l.rshifted_by(r),
61
+ }
62
+
35
63
  def visit(self, node, context):
36
64
  if self.instruction_limit is not None:
37
65
  self.instruction_limit -= 1
@@ -204,7 +232,11 @@ class Interpreter:
204
232
  comp_context = Context("COMPREHENSION", context, node.pos_start)
205
233
  comp_context.symbol_table = SymbolTable(context.symbol_table)
206
234
 
235
+ had_error = False
236
+
207
237
  def evaluate_loops(spec_index):
238
+ nonlocal had_error
239
+
208
240
  if spec_index >= len(node.iteration_specs):
209
241
  val = res.register(self.visit(node.output_expr_node, comp_context))
210
242
  if not res.error:
@@ -222,6 +254,7 @@ class Interpreter:
222
254
  )
223
255
  if error:
224
256
  res.failure(error)
257
+ had_error = True
225
258
  return
226
259
 
227
260
  for element in iterator:
@@ -244,7 +277,7 @@ class Interpreter:
244
277
 
245
278
  evaluate_loops(0)
246
279
 
247
- if res.error:
280
+ if res.error or had_error:
248
281
  return res
249
282
 
250
283
  return res.success(
@@ -388,16 +421,17 @@ class Interpreter:
388
421
 
389
422
  visibility = getattr(node, "target_visibility", "PUBLIC")
390
423
 
391
- if node.is_declaration:
392
- if is_final_anywhere(context.symbol_table, var_name):
393
- return res.failure(
394
- RTError(
395
- node.var_name_tok.pos_start,
396
- node.var_name_tok.pos_end,
397
- f"Cannot reassign constant '{var_name}'",
398
- context,
399
- )
424
+ if is_final_anywhere(context.symbol_table, var_name):
425
+ return res.failure(
426
+ RTError(
427
+ node.var_name_tok.pos_start,
428
+ node.var_name_tok.pos_end,
429
+ f"Cannot reassign constant '{var_name}'",
430
+ context,
400
431
  )
432
+ )
433
+
434
+ if node.is_declaration:
401
435
  context.symbol_table.set(var_name, value, visibility=visibility)
402
436
  else:
403
437
  err = context.symbol_table.update(var_name, value)
@@ -556,7 +590,11 @@ class Interpreter:
556
590
  comp_context = Context("DICT_COMPREHENSION", context, node.pos_start)
557
591
  comp_context.symbol_table = SymbolTable(context.symbol_table)
558
592
 
593
+ had_error = False
594
+
559
595
  def evaluate_loops(spec_index):
596
+ nonlocal had_error
597
+
560
598
  if spec_index >= len(node.iteration_specs):
561
599
  key_val = res.register(self.visit(node.key_expr_node, comp_context))
562
600
  if res.error:
@@ -577,6 +615,7 @@ class Interpreter:
577
615
  comp_context,
578
616
  )
579
617
  )
618
+ had_error = True
580
619
  return
581
620
  return
582
621
 
@@ -591,6 +630,7 @@ class Interpreter:
591
630
  )
592
631
  if error:
593
632
  res.failure(error)
633
+ had_error = True
594
634
  return
595
635
 
596
636
  for element in iterator:
@@ -613,7 +653,7 @@ class Interpreter:
613
653
 
614
654
  evaluate_loops(0)
615
655
 
616
- if res.error:
656
+ if res.error or had_error:
617
657
  return res
618
658
 
619
659
  return res.success(
@@ -797,7 +837,7 @@ class Interpreter:
797
837
  if res.error:
798
838
  return res
799
839
 
800
- value_to_call = value_to_call.copy().set_pos(node.pos_start, node.pos_end)
840
+ value_to_call = value_to_call.set_pos(node.pos_start, node.pos_end)
801
841
 
802
842
  for arg_node in node.arg_nodes:
803
843
  args.append(res.register(self.visit(arg_node, context)))
@@ -813,10 +853,31 @@ class Interpreter:
813
853
  def visit_ReturnNode(self, node, context):
814
854
  res = RTResult()
815
855
 
856
+ tco_func = getattr(context, "_tco_func", None)
857
+ if tco_func is not None and isinstance(node.node_to_return, CallNode):
858
+ ret_node = node.node_to_return
859
+
860
+ callee = res.register(self.visit(ret_node.node_to_call, context))
861
+ if res.error:
862
+ return res
863
+
864
+ if isinstance(callee, BoundMethod):
865
+ underlying = callee.function_to_bind
866
+ else:
867
+ underlying = callee
868
+
869
+ if underlying is tco_func or callee is tco_func:
870
+ tc_args = []
871
+ for arg_node in ret_node.arg_nodes:
872
+ arg_val = res.register(self.visit(arg_node, context))
873
+ if res.error:
874
+ return res
875
+ tc_args.append(arg_val)
876
+ return res.success_return(TailCall(callee, tc_args))
877
+
816
878
  value = res.register(self.visit(node.node_to_return, context))
817
879
  if res.error:
818
880
  return res
819
-
820
881
  return res.success_return(value)
821
882
 
822
883
  def visit_ClassNode(self, node, context):
@@ -1217,49 +1278,20 @@ class Interpreter:
1217
1278
  if res.error:
1218
1279
  return res
1219
1280
 
1220
- result, error = None, None
1221
-
1222
- if node.op_tok.type == GL_PLUS:
1223
- result, error = left.added_to(right)
1224
- elif node.op_tok.type == GL_MINUS:
1225
- result, error = left.subbed_by(right)
1226
- elif node.op_tok.type == GL_MUL:
1227
- result, error = left.multed_by(right)
1228
- elif node.op_tok.type == GL_DIV:
1229
- result, error = left.dived_by(right)
1230
- elif node.op_tok.type == GL_MOD:
1231
- result, error = left.modded_by(right)
1232
- elif node.op_tok.type == GL_FLOORDIV:
1233
- result, error = left.floordived_by(right)
1234
- elif node.op_tok.type == GL_POW:
1235
- result, error = left.powed_by(right)
1236
- elif node.op_tok.type == GL_EE:
1237
- result, error = left.get_comparison_eq(right)
1238
- elif node.op_tok.type == GL_NE:
1239
- result, error = left.get_comparison_ne(right)
1240
- elif node.op_tok.type == GL_LT:
1241
- result, error = left.get_comparison_lt(right)
1242
- elif node.op_tok.type == GL_GT:
1243
- result, error = left.get_comparison_gt(right)
1244
- elif node.op_tok.type == GL_LTE:
1245
- result, error = left.get_comparison_lte(right)
1246
- elif node.op_tok.type == GL_GTE:
1247
- result, error = left.get_comparison_gte(right)
1248
- elif node.op_tok.matches(GL_KEYWORD, "IS"):
1281
+ if node.op_tok.matches(GL_KEYWORD, "IS"):
1249
1282
  result, error = left.get_comparison_is(right)
1283
+ if error:
1284
+ return res.failure(error)
1285
+ return res.success(result.set_pos(node.pos_start, node.pos_end))
1286
+
1250
1287
  elif node.op_tok.matches(GL_KEYWORD, "INSTANCEOF"):
1251
1288
  result, error = left.get_comparison_instanceof(right)
1252
- elif node.op_tok.type == GL_BIT_AND:
1253
- result, error = left.bitted_and_by(right)
1254
- elif node.op_tok.type == GL_BIT_OR:
1255
- result, error = left.bitted_or_by(right)
1256
- elif node.op_tok.type == GL_BIT_XOR:
1257
- result, error = left.bitted_xor_by(right)
1258
- elif node.op_tok.type == GL_LSHIFT:
1259
- result, error = left.lshifted_by(right)
1260
- elif node.op_tok.type == GL_RSHIFT:
1261
- result, error = left.rshifted_by(right)
1262
- else:
1289
+ if error:
1290
+ return res.failure(error)
1291
+ return res.success(result.set_pos(node.pos_start, node.pos_end))
1292
+
1293
+ op = self._binop_dispatch.get(node.op_tok.type)
1294
+ if op is None:
1263
1295
  return res.failure(
1264
1296
  RTError(
1265
1297
  node.op_tok.pos_start,
@@ -1268,11 +1300,11 @@ class Interpreter:
1268
1300
  context,
1269
1301
  )
1270
1302
  )
1271
-
1303
+ result, error = op(left, right)
1272
1304
  if error:
1273
1305
  return res.failure(error)
1274
- else:
1275
- return res.success(result.set_pos(node.pos_start, node.pos_end))
1306
+
1307
+ return res.success(result.set_pos(node.pos_start, node.pos_end))
1276
1308
 
1277
1309
  def visit_TernaryOpNode(self, node, context):
1278
1310
  res = RTResult()
@@ -3,6 +3,8 @@ from .errors import Position, IllegalCharError, InvalidSyntaxError
3
3
 
4
4
 
5
5
  class Token:
6
+ __slots__ = ("type", "value", "pos_start", "pos_end")
7
+
6
8
  def __init__(self, type_, value=None, pos_start=None, pos_end=None):
7
9
  self.type = type_
8
10
  self.value = value
@@ -386,16 +388,17 @@ class Lexer:
386
388
 
387
389
  tokens.append(Token(GL_LPAREN, pos_start=pos_start))
388
390
 
389
- string_part = ""
391
+ chars = []
390
392
  escape_character = False
391
393
 
392
- while self.current_char != None and (
394
+ while self.current_char is not None and (
393
395
  self.current_char != "`" or escape_character
394
396
  ):
395
397
 
396
398
  if not escape_character and self.current_char == "$" and self.peek() == "{":
399
+ string_part = "".join(chars)
397
400
  tokens.append(Token(GL_STRING, string_part, pos_start=pos_start))
398
- string_part = ""
401
+ chars = []
399
402
 
400
403
  tokens.append(Token(GL_PLUS, pos_start=self.pos))
401
404
  tokens.append(Token(GL_IDENTIFIER, "STR", pos_start=self.pos))
@@ -450,24 +453,23 @@ class Lexer:
450
453
 
451
454
  elif escape_character:
452
455
  if self.current_char == "n":
453
- string_part += "\n"
456
+ chars.append("\n")
454
457
  elif self.current_char == "t":
455
- string_part += "\t"
458
+ chars.append("\t")
456
459
  elif self.current_char == "r":
457
- string_part += "\r"
460
+ chars.append("\r")
458
461
  elif self.current_char == "`":
459
- string_part += "`"
462
+ chars.append("`")
460
463
  elif self.current_char == "\\":
461
- string_part += "\\"
464
+ chars.append("\\")
462
465
  elif self.current_char == '"':
463
- string_part += '"'
466
+ chars.append('"')
464
467
  elif self.current_char == "'":
465
- string_part += "'"
468
+ chars.append("'")
466
469
  elif self.current_char == "$":
467
- string_part += "$"
470
+ chars.append("$")
468
471
  else:
469
- string_part += self.current_char
470
-
472
+ chars.append(self.current_char)
471
473
  escape_character = False
472
474
  self.advance()
473
475
 
@@ -476,9 +478,10 @@ class Lexer:
476
478
  self.advance()
477
479
 
478
480
  else:
479
- string_part += self.current_char
481
+ chars.append(self.current_char)
480
482
  self.advance()
481
483
 
484
+ string_part = "".join(chars)
482
485
  tokens.append(Token(GL_STRING, string_part, pos_start=pos_start))
483
486
  tokens.append(Token(GL_RPAREN, pos_start=self.pos))
484
487
 
@@ -486,7 +489,7 @@ class Lexer:
486
489
  return tokens
487
490
 
488
491
  def make_string(self):
489
- string = ""
492
+ chars = []
490
493
  pos_start = self.pos.copy()
491
494
 
492
495
  is_multiline = False
@@ -500,20 +503,20 @@ class Lexer:
500
503
 
501
504
  escape_character = False
502
505
 
503
- while self.current_char != None:
506
+ while self.current_char is not None:
504
507
  if escape_character:
505
508
  if self.current_char == "n":
506
- string += "\n"
509
+ chars.append("\n")
507
510
  elif self.current_char == "t":
508
- string += "\t"
511
+ chars.append("\t")
509
512
  elif self.current_char == "r":
510
- string += "\r"
513
+ chars.append("\r")
511
514
  elif self.current_char == '"':
512
- string += '"'
515
+ chars.append('"')
513
516
  elif self.current_char == "\\":
514
- string += "\\"
517
+ chars.append("\\")
515
518
  else:
516
- string += self.current_char
519
+ chars.append(self.current_char)
517
520
  escape_character = False
518
521
  elif self.current_char == "\\":
519
522
  escape_character = True
@@ -525,13 +528,13 @@ class Lexer:
525
528
  self.advance()
526
529
  break
527
530
  else:
528
- string += '""'
531
+ chars.append('""')
529
532
  else:
530
- string += '"'
533
+ chars.append('"')
531
534
  else:
532
535
  break
533
536
  else:
534
- string += self.current_char
537
+ chars.append(self.current_char)
535
538
 
536
539
  self.advance()
537
540
 
@@ -540,5 +543,6 @@ class Lexer:
540
543
  pos_start, self.pos, "Unterminated string literal"
541
544
  )
542
545
 
546
+ string = "".join(chars)
543
547
  self.advance()
544
548
  return Token(GL_STRING, string, pos_start, self.pos)
@@ -1,23 +1,40 @@
1
1
  from threading import Lock
2
2
 
3
3
 
4
+ class _NoLock:
5
+ __slots__ = ()
6
+
7
+ def __enter__(self):
8
+ return self
9
+
10
+ def __exit__(self, *args):
11
+ pass
12
+
13
+
4
14
  class SymbolTable:
15
+ _THREADING_ENABLED = False
16
+
5
17
  def __init__(self, parent=None):
6
18
  self.symbols = {}
7
19
  self.parent = parent
8
20
  self.finals = set()
9
21
  self.visibilities = {}
10
22
  self.defining_classes = {}
11
- self._lock = Lock()
23
+ self._lock = Lock() if SymbolTable._THREADING_ENABLED else _NoLock()
24
+ self._finals_count = 0
12
25
 
13
26
  def set(
14
27
  self, name, value, visibility="PUBLIC", as_final=False, defining_class=None
15
28
  ):
16
29
  with self._lock:
17
30
  self.symbols[name] = value
18
- self.visibilities[name] = visibility
31
+ if visibility != "PUBLIC":
32
+ self.visibilities[name] = visibility
33
+
19
34
  if as_final:
20
35
  self.finals.add(name)
36
+ self._finals_count += 1
37
+
21
38
  if defining_class:
22
39
  self.defining_classes[name] = defining_class
23
40
 
@@ -34,20 +51,28 @@ class SymbolTable:
34
51
  with self._lock:
35
52
  if name in self.symbols:
36
53
  return f"Variable '{name}' is already defined"
54
+
37
55
  if as_final and self.is_final_in_ancestors(name):
38
56
  return f"Cannot declare constant '{name}' because it is already defined as constant in outer scope"
57
+
39
58
  self.symbols[name] = value
40
- self.visibilities[name] = visibility
59
+ if visibility != "PUBLIC":
60
+ self.visibilities[name] = visibility
61
+
41
62
  if as_final:
42
63
  self.finals.add(name)
64
+ self._finals_count += 1
65
+
43
66
  return None
44
67
 
45
68
  def get(self, name):
46
69
  with self._lock:
47
70
  value = self.symbols.get(name)
48
- if value is None and self.parent:
49
- return self.parent.get(name)
50
- return value
71
+ has_parent = self.parent is not None
72
+
73
+ if value is None and has_parent:
74
+ return self.parent.get(name)
75
+ return value
51
76
 
52
77
  def update(self, name, value):
53
78
  with self._lock:
@@ -56,9 +81,11 @@ class SymbolTable:
56
81
  if name in self.symbols:
57
82
  self.symbols[name] = value
58
83
  return None
59
- if self.parent:
60
- return self.parent.update(name, value)
61
- return f"'{name}' is not defined"
84
+ has_parent = self.parent is not None
85
+
86
+ if has_parent:
87
+ return self.parent.update(name, value)
88
+ return f"'{name}' is not defined"
62
89
 
63
90
  def remove(self, name):
64
91
  with self._lock:
@@ -78,10 +105,22 @@ class SymbolTable:
78
105
  new_table.visibilities = self.visibilities.copy()
79
106
  new_table.finals = self.finals.copy()
80
107
  new_table.defining_classes = self.defining_classes.copy()
108
+ new_table._finals_count = len(new_table.finals)
81
109
  return new_table
82
110
 
83
111
 
84
112
  class Context:
113
+ __slots__ = (
114
+ "display_name",
115
+ "parent",
116
+ "parent_entry_pos",
117
+ "symbol_table",
118
+ "depth",
119
+ "active_class",
120
+ "is_static",
121
+ "_tco_func",
122
+ )
123
+
85
124
  def __init__(self, display_name, parent=None, parent_entry_pos=None):
86
125
  self.display_name = display_name
87
126
  self.parent = parent
@@ -93,7 +132,19 @@ class Context:
93
132
 
94
133
 
95
134
  class RTResult:
135
+ __slots__ = (
136
+ "value",
137
+ "error",
138
+ "return_value",
139
+ "should_return",
140
+ "should_break",
141
+ "should_continue",
142
+ )
143
+
96
144
  def __init__(self):
145
+ self.reset()
146
+
147
+ def reset(self):
97
148
  self.value = None
98
149
  self.error = None
99
150
  self.return_value = None
@@ -101,7 +152,14 @@ class RTResult:
101
152
  self.should_break = False
102
153
  self.should_continue = False
103
154
 
155
+ return self
156
+
104
157
  def register(self, res):
158
+ if res.error is None and not (
159
+ res.should_return or res.should_break or res.should_continue
160
+ ):
161
+ return res.value
162
+
105
163
  if res.error:
106
164
  self.error = res.error
107
165
  if res.should_return:
@@ -111,6 +169,7 @@ class RTResult:
111
169
  self.should_break = True
112
170
  if res.should_continue:
113
171
  self.should_continue = True
172
+
114
173
  return res.value
115
174
 
116
175
  def success(self, value):
@@ -7,10 +7,21 @@ from .lexer import Token
7
7
  from .nodes import *
8
8
 
9
9
 
10
+ class TailCall:
11
+ __slots__ = ("function", "args")
12
+
13
+ def __init__(self, function, args):
14
+ self.function = function
15
+ self.args = args
16
+
17
+
10
18
  class Value:
19
+ __slots__ = ("pos_start", "pos_end", "context")
20
+
11
21
  def __init__(self):
12
- self.set_pos()
13
- self.set_context()
22
+ self.pos_start = None
23
+ self.pos_end = None
24
+ self.context = None
14
25
 
15
26
  def set_pos(self, pos_start=None, pos_end=None):
16
27
  self.pos_start = pos_start
@@ -152,13 +163,32 @@ class Value:
152
163
 
153
164
 
154
165
  class Number(Value):
166
+ MAX_INT_BITS = 100_000
167
+
168
+ __slots__ = ("value",)
169
+
155
170
  def __init__(self, value):
156
171
  super().__init__()
157
172
  self.value = value
158
173
 
159
174
  def added_to(self, other):
160
175
  if isinstance(other, Number):
161
- return Number(self.value + other.value).set_context(self.context), None
176
+ result = self.value + other.value
177
+ if isinstance(result, int) and result.bit_length() > Number.MAX_INT_BITS:
178
+ return None, RTError(
179
+ other.pos_start,
180
+ other.pos_end,
181
+ "Arithmetic result too large (exceeds integer size limit)",
182
+ self.context,
183
+ )
184
+ if isinstance(result, float) and math.isinf(result):
185
+ return None, RTError(
186
+ other.pos_start,
187
+ other.pos_end,
188
+ "Arithmetic result is infinite (float overflow)",
189
+ self.context,
190
+ )
191
+ return Number(result).set_context(self.context), None
162
192
  elif isinstance(other, String):
163
193
  return String(str(self.value) + other.value).set_context(self.context), None
164
194
  else:
@@ -166,13 +196,43 @@ class Number(Value):
166
196
 
167
197
  def subbed_by(self, other):
168
198
  if isinstance(other, Number):
169
- return Number(self.value - other.value).set_context(self.context), None
199
+ result = self.value - other.value
200
+ if isinstance(result, int) and result.bit_length() > Number.MAX_INT_BITS:
201
+ return None, RTError(
202
+ other.pos_start,
203
+ other.pos_end,
204
+ "Arithmetic result too large (exceeds integer size limit)",
205
+ self.context,
206
+ )
207
+ if isinstance(result, float) and math.isinf(result):
208
+ return None, RTError(
209
+ other.pos_start,
210
+ other.pos_end,
211
+ "Arithmetic result is infinite (float overflow)",
212
+ self.context,
213
+ )
214
+ return Number(result).set_context(self.context), None
170
215
  else:
171
216
  return None, Value.illegal_operation(self, other)
172
217
 
173
218
  def multed_by(self, other):
174
219
  if isinstance(other, Number):
175
- return Number(self.value * other.value).set_context(self.context), None
220
+ result = self.value * other.value
221
+ if isinstance(result, int) and result.bit_length() > Number.MAX_INT_BITS:
222
+ return None, RTError(
223
+ other.pos_start,
224
+ other.pos_end,
225
+ "Arithmetic result too large (exceeds integer size limit)",
226
+ self.context,
227
+ )
228
+ if isinstance(result, float) and math.isinf(result):
229
+ return None, RTError(
230
+ other.pos_start,
231
+ other.pos_end,
232
+ "Arithmetic result is infinite (float overflow)",
233
+ self.context,
234
+ )
235
+ return Number(result).set_context(self.context), None
176
236
  else:
177
237
  return None, Value.illegal_operation(self, other)
178
238
 
@@ -220,7 +280,7 @@ class Number(Value):
220
280
  )
221
281
  try:
222
282
  result = self.value**other.value
223
- except (ValueError, ZeroDivisionError) as e:
283
+ except (ValueError, ZeroDivisionError, OverflowError) as e:
224
284
  return None, RTError(
225
285
  other.pos_start, other.pos_end, str(e), self.context
226
286
  )
@@ -240,6 +300,13 @@ class Number(Value):
240
300
  "Math domain error: result is NaN",
241
301
  self.context,
242
302
  )
303
+ if isinstance(result, float) and math.isinf(result):
304
+ return None, RTError(
305
+ other.pos_start,
306
+ other.pos_end,
307
+ f"Arithmetic result is infinite (float overflow)",
308
+ self.context,
309
+ )
243
310
  return Number(result).set_context(self.context), None
244
311
  else:
245
312
  return None, Value.illegal_operation(self, other)
@@ -294,24 +361,27 @@ class Number(Value):
294
361
 
295
362
  def anded_by(self, other):
296
363
  if isinstance(other, Number):
297
- return (
298
- Number(int(self.value and other.value)).set_context(self.context),
299
- None,
300
- )
364
+ if self.is_true() and other.is_true():
365
+ return Number(1), None
366
+ else:
367
+ return Number(0), None
301
368
  else:
302
- return None, Value.illegal_operation(self, other)
369
+ return None, self.illegal_operation(other)
303
370
 
304
371
  def ored_by(self, other):
305
372
  if isinstance(other, Number):
306
- return (
307
- Number(int(self.value or other.value)).set_context(self.context),
308
- None,
309
- )
373
+ if self.is_true() or other.is_true():
374
+ return Number(1), None
375
+ else:
376
+ return Number(0), None
310
377
  else:
311
- return None, Value.illegal_operation(self, other)
378
+ return None, self.illegal_operation(other)
312
379
 
313
380
  def notted(self):
314
- return Number(1 if self.value == 0 else 0).set_context(self.context), None
381
+ if self.is_true():
382
+ return Number(0), None
383
+ else:
384
+ return Number(1), None
315
385
 
316
386
  def bitted_and_by(self, other):
317
387
  if isinstance(other, Number):
@@ -369,21 +439,55 @@ class Number(Value):
369
439
  return str(self.value)
370
440
 
371
441
 
372
- Number.false = Number(0)
373
- Number.true = Number(1)
374
- Number.null = Number.false.copy()
442
+ class _FrozenNull(Number):
443
+ __slots__ = ()
444
+
445
+ def set_pos(self, *args):
446
+ return self
447
+
448
+ def set_context(self, *args):
449
+ return self
450
+
451
+ def copy(self):
452
+ return Number(self.value)
453
+
454
+
455
+ Number.false = _FrozenNull(0)
456
+ Number.true = _FrozenNull(1)
457
+ Number.null = _FrozenNull(0)
375
458
 
376
459
 
377
460
  class String(Value):
461
+ MAX_STRING_SIZE = 10_000_000
462
+
463
+ __slots__ = ("value",)
464
+
378
465
  def __init__(self, value):
379
466
  super().__init__()
380
467
  self.value = value
381
468
 
382
469
  def added_to(self, other):
383
470
  if isinstance(other, String):
471
+ new_len = len(self.value) + len(other.value)
472
+ if new_len > String.MAX_STRING_SIZE:
473
+ return None, RTError(
474
+ other.pos_start,
475
+ other.pos_end,
476
+ f"String concatenation result ({new_len:,} chars) exceeds maximum allowed size ({String.MAX_STRING_SIZE:,} chars)",
477
+ self.context,
478
+ )
384
479
  return String(self.value + other.value).set_context(self.context), None
385
480
  elif isinstance(other, Number):
386
- return String(self.value + str(other.value)).set_context(self.context), None
481
+ suffix = str(other.value)
482
+ new_len = len(self.value) + len(suffix)
483
+ if new_len > String.MAX_STRING_SIZE:
484
+ return None, RTError(
485
+ other.pos_start,
486
+ other.pos_end,
487
+ f"String concatenation result exceeds maximum allowed size ({String.MAX_STRING_SIZE:,} chars)",
488
+ self.context,
489
+ )
490
+ return String(self.value + suffix).set_context(self.context), None
387
491
  else:
388
492
  return None, self.illegal_operation(other)
389
493
 
@@ -471,6 +575,8 @@ class String(Value):
471
575
  class List(Value):
472
576
  MAX_LIST_SIZE = 1_000_000
473
577
 
578
+ __slots__ = ("elements",)
579
+
474
580
  def __init__(self, elements):
475
581
  super().__init__()
476
582
  self.elements = elements
@@ -480,6 +586,15 @@ class List(Value):
480
586
 
481
587
  def added_to(self, other):
482
588
  if isinstance(other, List):
589
+ new_len = len(self.elements) + len(other.elements)
590
+ if new_len > List.MAX_LIST_SIZE:
591
+ return None, RTError(
592
+ other.pos_start,
593
+ other.pos_end,
594
+ f"List concatenation result ({new_len}) exceeds maximum "
595
+ f"allowed size ({List.MAX_LIST_SIZE})",
596
+ self.context,
597
+ )
483
598
  new_list = List(self.elements + other.elements)
484
599
  new_list.set_context(self.context)
485
600
  return new_list, None
@@ -605,6 +720,8 @@ class List(Value):
605
720
  class Dict(Value):
606
721
  MAX_DICT_SIZE = 1_000_000
607
722
 
723
+ __slots__ = ("elements",)
724
+
608
725
  def __init__(self, elements):
609
726
  super().__init__()
610
727
  self.elements = elements
@@ -661,6 +778,14 @@ class Dict(Value):
661
778
  self.context,
662
779
  )
663
780
 
781
+ if key.value not in self.elements and len(self.elements) >= Dict.MAX_DICT_SIZE:
782
+ return None, RTError(
783
+ self.pos_start,
784
+ self.pos_end,
785
+ f"Dict size limit ({Dict.MAX_DICT_SIZE:,} entries) reached. Cannot insert more keys.",
786
+ self.context,
787
+ )
788
+
664
789
  self.elements[key.value] = value
665
790
  return value, None
666
791
 
@@ -731,6 +856,8 @@ class Dict(Value):
731
856
 
732
857
 
733
858
  class BaseFunction(Value):
859
+ __slots__ = ("name",)
860
+
734
861
  def __init__(self, name):
735
862
  super().__init__()
736
863
  self.name = name or "<anonymous>"
@@ -793,6 +920,16 @@ class BaseFunction(Value):
793
920
 
794
921
 
795
922
  class Function(BaseFunction):
923
+ __slots__ = (
924
+ "body_node",
925
+ "arg_name_toks",
926
+ "arg_names",
927
+ "context",
928
+ "visibility",
929
+ "defining_class",
930
+ "is_static",
931
+ )
932
+
796
933
  def __init__(
797
934
  self,
798
935
  name,
@@ -814,35 +951,60 @@ class Function(BaseFunction):
814
951
 
815
952
  def execute(self, args, interpreter):
816
953
  res = RTResult()
954
+ current_func = self
955
+ current_args = args
956
+ base_depth = None
957
+
958
+ while True:
959
+ new_context = current_func.generate_new_context()
960
+ new_context.active_class = current_func.defining_class
961
+ new_context.is_static = current_func.is_static
962
+
963
+ if base_depth is None:
964
+ base_depth = new_context.depth
965
+ if base_depth > 2000:
966
+ return res.failure(
967
+ RTError(
968
+ current_func.pos_start,
969
+ current_func.pos_end,
970
+ "Recursion limit exceeded",
971
+ current_func.context,
972
+ )
973
+ )
974
+ else:
975
+ new_context.depth = base_depth
817
976
 
818
- new_context = self.generate_new_context()
819
-
820
- new_context.active_class = self.defining_class
821
- new_context.is_static = self.is_static
977
+ new_context._tco_func = current_func
822
978
 
823
- if new_context.depth > 250:
824
- return res.failure(
825
- RTError(
826
- self.pos_start,
827
- self.pos_end,
828
- "Recursion limit exceeded",
829
- self.context,
979
+ res.register(
980
+ current_func.check_and_populate_args(
981
+ current_func.arg_names, current_args, new_context
830
982
  )
831
983
  )
832
984
 
833
- res.register(self.check_and_populate_args(self.arg_names, args, new_context))
834
- if res.error:
835
- return res
985
+ if res.error:
986
+ return res
987
+
988
+ value_result = interpreter.visit(current_func.body_node, new_context)
989
+ if value_result.error:
990
+ return value_result
836
991
 
837
- value_result = interpreter.visit(self.body_node, new_context)
992
+ if value_result.should_return:
993
+ ret_val = value_result.return_value
994
+ if isinstance(ret_val, TailCall):
995
+ callee = ret_val.function
838
996
 
839
- if value_result.error:
840
- return value_result
997
+ if isinstance(callee, BoundMethod):
998
+ current_func = callee.function_to_bind
999
+ else:
1000
+ current_func = callee
841
1001
 
842
- if value_result.should_return:
843
- return res.success(value_result.return_value)
1002
+ current_args = ret_val.args
1003
+ res = RTResult()
1004
+ continue
1005
+ return res.success(ret_val)
844
1006
 
845
- return res.success(value_result.value or Number.null.copy())
1007
+ return res.success(value_result.value or Number.null.copy())
846
1008
 
847
1009
  def copy(self):
848
1010
  copy = Function(
@@ -861,6 +1023,8 @@ class Function(BaseFunction):
861
1023
 
862
1024
 
863
1025
  class FunctionGroup(BaseFunction):
1026
+ __slots__ = ("functions", "visibility", "is_static", "defining_class")
1027
+
864
1028
  def __init__(self, name):
865
1029
  super().__init__(name)
866
1030
  self.functions = {}
@@ -934,6 +1098,8 @@ class FunctionGroup(BaseFunction):
934
1098
 
935
1099
 
936
1100
  class Class(BaseFunction):
1101
+ __slots__ = ("superclasses", "methods", "static_symbol_table", "mro")
1102
+
937
1103
  def __init__(self, name, superclasses, methods, static_symbol_table=None, mro=None):
938
1104
  super().__init__(name)
939
1105
  self.superclasses = superclasses
@@ -1135,6 +1301,8 @@ class Class(BaseFunction):
1135
1301
 
1136
1302
 
1137
1303
  class Instance(Value):
1304
+ __slots__ = ("class_ref", "symbol_table")
1305
+
1138
1306
  def __init__(self, class_ref):
1139
1307
  super().__init__()
1140
1308
  self.class_ref = class_ref
@@ -1337,6 +1505,8 @@ class Instance(Value):
1337
1505
 
1338
1506
 
1339
1507
  class Super(Value):
1508
+ __slots__ = ("instance", "start_class")
1509
+
1340
1510
  def __init__(self, instance, start_class):
1341
1511
  super().__init__()
1342
1512
  self.instance = instance
@@ -1418,6 +1588,8 @@ class Super(Value):
1418
1588
 
1419
1589
 
1420
1590
  class Type(Value):
1591
+ __slots__ = ("name",)
1592
+
1421
1593
  def __init__(self, name):
1422
1594
  super().__init__()
1423
1595
  self.name = name
@@ -1434,6 +1606,8 @@ class Type(Value):
1434
1606
 
1435
1607
 
1436
1608
  class Enum(Value):
1609
+ __slots__ = ("name", "elements_dict")
1610
+
1437
1611
  def __init__(self, name, elements_dict):
1438
1612
  super().__init__()
1439
1613
  self.name = name
@@ -1480,13 +1654,21 @@ class Enum(Value):
1480
1654
 
1481
1655
 
1482
1656
  class BoundMethod(BaseFunction):
1657
+ __slots__ = (
1658
+ "function_to_bind",
1659
+ "instance",
1660
+ "context",
1661
+ "visibility",
1662
+ "defining_class",
1663
+ "is_static",
1664
+ )
1665
+
1483
1666
  def __init__(self, name, function_to_bind, instance):
1484
1667
  super().__init__(name)
1485
1668
  self.function_to_bind = function_to_bind
1486
1669
  self.instance = instance
1487
1670
  self.context = function_to_bind.context
1488
1671
  self.set_pos(function_to_bind.pos_start, function_to_bind.pos_end)
1489
-
1490
1672
  self.visibility = getattr(function_to_bind, "visibility", "PUBLIC")
1491
1673
  self.defining_class = getattr(function_to_bind, "defining_class", None)
1492
1674
  self.is_static = getattr(function_to_bind, "is_static", False)
@@ -1501,43 +1683,74 @@ class BoundMethod(BaseFunction):
1501
1683
  full_args = [self.instance] + args
1502
1684
  return self.function_to_bind.execute(full_args, interpreter)
1503
1685
 
1504
- new_context = self.function_to_bind.generate_new_context()
1505
-
1506
- new_context.active_class = self.function_to_bind.defining_class
1507
-
1508
- new_context.is_static = self.function_to_bind.is_static
1686
+ current_func = self.function_to_bind
1687
+ current_instance = self.instance
1688
+ current_args = args
1689
+ base_depth = None
1690
+
1691
+ while True:
1692
+ new_context = current_func.generate_new_context()
1693
+ new_context.active_class = current_func.defining_class
1694
+ new_context.is_static = current_func.is_static
1695
+
1696
+ if base_depth is None:
1697
+ base_depth = new_context.depth
1698
+ if base_depth > 2000:
1699
+ return res.failure(
1700
+ RTError(
1701
+ current_func.pos_start,
1702
+ current_func.pos_end,
1703
+ "Recursion limit exceeded",
1704
+ current_func.context,
1705
+ )
1706
+ )
1707
+ else:
1708
+ new_context.depth = base_depth
1509
1709
 
1510
- original_arg_names = self.function_to_bind.arg_names
1710
+ new_context.symbol_table.set("THIS", current_instance)
1711
+ new_context._tco_func = current_func
1511
1712
 
1512
- new_context.symbol_table.set("THIS", self.instance)
1713
+ original_arg_names = current_func.arg_names
1714
+ actual_args = current_args
1513
1715
 
1514
- actual_args = args
1515
- if len(args) > 0 and args[0] is self.instance:
1516
- actual_args = args[1:]
1716
+ if len(actual_args) > 0 and actual_args[0] is current_instance:
1717
+ actual_args = actual_args[1:]
1517
1718
 
1518
- if len(original_arg_names) > 0 and original_arg_names[0] == "THIS":
1519
- expected_arg_names = original_arg_names[1:]
1520
- else:
1521
- expected_arg_names = original_arg_names
1719
+ if len(original_arg_names) > 0 and original_arg_names[0] == "THIS":
1720
+ expected_arg_names = original_arg_names[1:]
1721
+ else:
1722
+ expected_arg_names = original_arg_names
1522
1723
 
1523
- res.register(
1524
- self.function_to_bind.check_and_populate_args(
1525
- expected_arg_names, actual_args, new_context
1724
+ res.register(
1725
+ current_func.check_and_populate_args(
1726
+ expected_arg_names, actual_args, new_context
1727
+ )
1526
1728
  )
1527
- )
1528
1729
 
1529
- if res.error:
1530
- return res
1730
+ if res.error:
1731
+ return res
1531
1732
 
1532
- value_result = interpreter.visit(self.function_to_bind.body_node, new_context)
1733
+ value_result = interpreter.visit(current_func.body_node, new_context)
1734
+ if value_result.error:
1735
+ return value_result
1533
1736
 
1534
- if value_result.error:
1535
- return value_result
1737
+ if value_result.should_return:
1738
+ ret_val = value_result.return_value
1739
+ if isinstance(ret_val, TailCall):
1740
+ callee = ret_val.function
1536
1741
 
1537
- if value_result.should_return:
1538
- return res.success(value_result.return_value)
1742
+ if isinstance(callee, BoundMethod):
1743
+ current_func = callee.function_to_bind
1744
+ current_instance = callee.instance
1745
+ else:
1746
+ current_func = callee
1539
1747
 
1540
- return res.success(value_result.value or Number.null.copy())
1748
+ current_args = ret_val.args
1749
+ res = RTResult()
1750
+ continue
1751
+ return res.success(ret_val)
1752
+
1753
+ return res.success(value_result.value or Number.null.copy())
1541
1754
 
1542
1755
  def copy(self):
1543
1756
  return (
@@ -1548,6 +1761,8 @@ class BoundMethod(BaseFunction):
1548
1761
 
1549
1762
 
1550
1763
  class BuiltInFunction(BaseFunction):
1764
+ __slots__ = ()
1765
+
1551
1766
  def __init__(self, name):
1552
1767
  super().__init__(name)
1553
1768
 
@@ -1672,8 +1887,10 @@ class BuiltInFunction(BaseFunction):
1672
1887
  if res.error:
1673
1888
  return res
1674
1889
 
1675
- is_true = args[0].is_true()
1676
- return res.success(Number.true.copy() if is_true else Number.false.copy())
1890
+ if args[0].is_true():
1891
+ return res.success(Number(1))
1892
+ else:
1893
+ return res.success(Number(0))
1677
1894
 
1678
1895
  elif self.name == "LEN":
1679
1896
  res.register(self.check_args(["value"], args))
@@ -1703,6 +1920,14 @@ class BuiltInFunction(BaseFunction):
1703
1920
  self.context,
1704
1921
  )
1705
1922
  )
1923
+ return res.failure(
1924
+ RTError(
1925
+ self.pos_start,
1926
+ self.pos_end,
1927
+ f"Unknown built-in function '{self.name}'",
1928
+ self.context,
1929
+ )
1930
+ )
1706
1931
 
1707
1932
  def copy(self):
1708
1933
  copy = BuiltInFunction(self.name)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: gladlang
3
- Version: 0.2.2
3
+ Version: 0.2.4
4
4
  Summary: The GladLang Interpreter
5
5
  Author: Glad432
6
6
  License: Copyright (c) 2025 - present GLAD432
@@ -465,7 +465,7 @@ LET b = 3 # Binary 011
465
465
  PRINTLN a & b # 1 (AND)
466
466
  PRINTLN a | b # 7 (OR)
467
467
  PRINTLN a ^ b # 6 (XOR)
468
- PRINTLN ~a # -6 (NOT)
468
+ PRINTLN ~a # 4294967290 (bitwise NOT as unsigned 32‑bit integer)
469
469
  PRINTLN 1 << 2 # 4 (Left Shift)
470
470
  PRINTLN 8 >> 2 # 2 (Right Shift)
471
471
 
File without changes
File without changes
File without changes