shell-lite 0.5.3.1__tar.gz → 0.5.3.3__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.
Files changed (40) hide show
  1. {shell_lite-0.5.3.1/shell_lite.egg-info → shell_lite-0.5.3.3}/PKG-INFO +1 -1
  2. {shell_lite-0.5.3.1 → shell_lite-0.5.3.3}/pyproject.toml +1 -1
  3. {shell_lite-0.5.3.1 → shell_lite-0.5.3.3}/shell_lite/interpreter.py +3 -4
  4. {shell_lite-0.5.3.1 → shell_lite-0.5.3.3}/shell_lite/main.py +7 -1
  5. {shell_lite-0.5.3.1 → shell_lite-0.5.3.3}/shell_lite/parser_gbp.py +141 -1
  6. {shell_lite-0.5.3.1 → shell_lite-0.5.3.3/shell_lite.egg-info}/PKG-INFO +1 -1
  7. {shell_lite-0.5.3.1 → shell_lite-0.5.3.3}/shell_lite.egg-info/SOURCES.txt +0 -11
  8. shell_lite-0.5.3.1/shell_lite/llvm_backend/__init__.py +0 -0
  9. shell_lite-0.5.3.1/shell_lite/llvm_backend/builder.py +0 -39
  10. shell_lite-0.5.3.1/shell_lite/llvm_backend/codegen.py +0 -311
  11. shell_lite-0.5.3.1/tests/__init__.py +0 -1
  12. shell_lite-0.5.3.1/tests/benchmark_driver.py +0 -43
  13. shell_lite-0.5.3.1/tests/compare_parsers.py +0 -31
  14. shell_lite-0.5.3.1/tests/debug_jit.py +0 -49
  15. shell_lite-0.5.3.1/tests/generate_actual_graph.py +0 -84
  16. shell_lite-0.5.3.1/tests/generate_perf_graph.py +0 -68
  17. shell_lite-0.5.3.1/tests/generate_runtime_graph.py +0 -58
  18. shell_lite-0.5.3.1/tests/run_jit.py +0 -70
  19. {shell_lite-0.5.3.1 → shell_lite-0.5.3.3}/LICENSE +0 -0
  20. {shell_lite-0.5.3.1 → shell_lite-0.5.3.3}/README.md +0 -0
  21. {shell_lite-0.5.3.1 → shell_lite-0.5.3.3}/setup.cfg +0 -0
  22. {shell_lite-0.5.3.1 → shell_lite-0.5.3.3}/setup.py +0 -0
  23. {shell_lite-0.5.3.1 → shell_lite-0.5.3.3}/shell_lite/__init__.py +0 -0
  24. {shell_lite-0.5.3.1 → shell_lite-0.5.3.3}/shell_lite/ast_nodes.py +0 -0
  25. {shell_lite-0.5.3.1 → shell_lite-0.5.3.3}/shell_lite/cli.py +0 -0
  26. {shell_lite-0.5.3.1 → shell_lite-0.5.3.3}/shell_lite/compiler.py +0 -0
  27. {shell_lite-0.5.3.1 → shell_lite-0.5.3.3}/shell_lite/js_compiler.py +0 -0
  28. {shell_lite-0.5.3.1 → shell_lite-0.5.3.3}/shell_lite/lexer.py +0 -0
  29. {shell_lite-0.5.3.1 → shell_lite-0.5.3.3}/shell_lite/parser.py +0 -0
  30. {shell_lite-0.5.3.1 → shell_lite-0.5.3.3}/shell_lite/runtime.py +0 -0
  31. {shell_lite-0.5.3.1 → shell_lite-0.5.3.3}/shell_lite.egg-info/dependency_links.txt +0 -0
  32. {shell_lite-0.5.3.1 → shell_lite-0.5.3.3}/shell_lite.egg-info/entry_points.txt +0 -0
  33. {shell_lite-0.5.3.1 → shell_lite-0.5.3.3}/shell_lite.egg-info/requires.txt +0 -0
  34. {shell_lite-0.5.3.1 → shell_lite-0.5.3.3}/shell_lite.egg-info/top_level.txt +0 -0
  35. {shell_lite-0.5.3.1 → shell_lite-0.5.3.3}/tests/test_gbp_standalone.py +0 -0
  36. {shell_lite-0.5.3.1 → shell_lite-0.5.3.3}/tests/test_interpreter.py +0 -0
  37. {shell_lite-0.5.3.1 → shell_lite-0.5.3.3}/tests/test_lexer.py +0 -0
  38. {shell_lite-0.5.3.1 → shell_lite-0.5.3.3}/tests/test_parser.py +0 -0
  39. {shell_lite-0.5.3.1 → shell_lite-0.5.3.3}/tests/test_phase1.py +0 -0
  40. {shell_lite-0.5.3.1 → shell_lite-0.5.3.3}/tests/test_stdlib.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: shell-lite
3
- Version: 0.5.3.1
3
+ Version: 0.5.3.3
4
4
  Summary: A lightweight, English-like scripting language.
5
5
  Home-page: https://github.com/Shrey-N/ShellDesk
6
6
  Author: Shrey Naithani
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "shell-lite"
7
- version = "0.5.3.1"
7
+ version = "0.5.3.3"
8
8
  description = "A lightweight, English-like scripting language."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.8"
@@ -490,9 +490,9 @@ class Interpreter:
490
490
  code_parts.append(colors[node.color.lower()])
491
491
  if code_parts:
492
492
  ansi_code = "\033[" + ";".join(code_parts) + "m"
493
- print(f"{ansi_code}{value}\033[0m")
493
+ print(f"{ansi_code}{value}\033[0m", flush=True)
494
494
  return value
495
- print(value)
495
+ print(value, flush=True)
496
496
  return value
497
497
  def visit_If(self, node: If):
498
498
  condition = self.visit(node.condition)
@@ -615,7 +615,6 @@ class Interpreter:
615
615
  finally:
616
616
  self.web.pop()
617
617
  return result
618
- return result
619
618
  try:
620
619
  func = self.current_env.get(node.name)
621
620
  if callable(func):
@@ -1504,7 +1503,7 @@ class Interpreter:
1504
1503
  self.wfile.write(str(e).encode())
1505
1504
  except: pass
1506
1505
  server = ReusableHTTPServer(('0.0.0.0', port_val), ShellLiteHandler)
1507
- print(f"\n ShellLite Server v0.5.3.1 is running!")
1506
+ print(f"\n ShellLite Server v0.5.3.2 is running!")
1508
1507
  print(f" \u001b[1;36m➜\u001b[0m Local: \u001b[1;4;36mhttp://localhost:{port_val}/\u001b[0m\n")
1509
1508
  try: server.serve_forever()
1510
1509
  except KeyboardInterrupt:
@@ -150,7 +150,7 @@ def install_globally():
150
150
  return
151
151
  ps_cmd = f'$oldPath = [Environment]::GetEnvironmentVariable("Path", "User"); if ($oldPath -notlike "*ShellLite*") {{ [Environment]::SetEnvironmentVariable("Path", "$oldPath;{install_dir}", "User") }}'
152
152
  subprocess.run(["powershell", "-Command", ps_cmd], capture_output=True)
153
- print(f"\n[SUCCESS] ShellLite (v0.5.3) is installed!")
153
+ print(f"\n[SUCCESS] ShellLite (v0.5.3.2) is installed!")
154
154
  print(f"Location: {install_dir}")
155
155
  print("\nIMPORTANT STEP REQUIRED:")
156
156
  print("1. Close ALL open terminal windows (CMD, PowerShell, VS Code).")
@@ -443,6 +443,12 @@ def main():
443
443
  print("Usage: shl llvm <filename>")
444
444
  elif cmd == "help" or cmd == "--help" or cmd == "-h":
445
445
  show_help()
446
+ elif cmd == "--version" or cmd == "-v":
447
+ try:
448
+ from importlib.metadata import version
449
+ print(f"ShellLite v{version('shell-lite')}")
450
+ except Exception:
451
+ print("ShellLite v0.5.3.1")
446
452
  elif cmd == "get":
447
453
  if len(sys.argv) > 2:
448
454
  package_name = sys.argv[2]
@@ -105,6 +105,8 @@ class GeometricBindingParser:
105
105
  return self.bind_return(node)
106
106
  elif head_type == 'REPEAT':
107
107
  return self.bind_repeat(node)
108
+ elif head_type == 'FOREVER':
109
+ return self.bind_forever(node)
108
110
  elif head_type == 'START':
109
111
  return self.bind_start(node)
110
112
  elif head_type == 'LISTEN':
@@ -139,6 +141,112 @@ class GeometricBindingParser:
139
141
  count = self.parse_expr_iterative(expr_tokens)
140
142
  body = [self.bind_node(child) for child in node.children]
141
143
  return Repeat(count, body)
144
+
145
+ def bind_forever(self, node: GeoNode) -> Forever:
146
+ body = [self.bind_node(child) for child in node.children]
147
+ return Forever(body)
148
+
149
+ def bind_for(self, node: GeoNode) -> Node:
150
+ # 'for i in range 1 10' or 'for item in list'
151
+ # node.tokens starts with FOR/LOOP
152
+ # Next should be ID (var name)
153
+ if len(node.tokens) < 3:
154
+ return None # Error?
155
+
156
+ var_name = node.tokens[1].value
157
+
158
+ # Check for 'IN'
159
+ in_index = -1
160
+ for i, t in enumerate(node.tokens):
161
+ if t.type == 'IN':
162
+ in_index = i
163
+ break
164
+
165
+ if in_index == -1:
166
+ # Maybe 'loop 10 times'? No, that's Repeat (handled by bind_repeat if HEAD is REPEAT)
167
+ # But if HEAD is LOOP?
168
+ if node.head_token.type == 'LOOP':
169
+ # loop 200 times
170
+ # Delegated to bind_repeat logic if we can re-route, OR implement here
171
+ # Look for TIMES
172
+ if node.tokens[-1].type == 'TIMES':
173
+ expr_tokens = self._extract_expr_tokens(node.tokens, start=1)
174
+ expr_tokens.pop() # remove TIMES
175
+ count = self.parse_expr_iterative(expr_tokens)
176
+ body = [self.bind_node(child) for child in node.children]
177
+ return Repeat(count, body)
178
+ return None
179
+
180
+ # It is a FOR loop
181
+ # Check for RANGE
182
+ range_index = -1
183
+ for i, t in enumerate(node.tokens):
184
+ if t.type == 'RANGE':
185
+ range_index = i
186
+ break
187
+
188
+ if range_index != -1:
189
+ # for i in range 1 10
190
+ # Extract args after RANGE
191
+ args_tokens = node.tokens[range_index+1:]
192
+ # We need to split by space/comma? parse_expr_iterative might consume all?
193
+ # Range takes start, end, step.
194
+ # We can cheat and wrap them in a Call to 'range'?
195
+ # Or parse sub-expressions.
196
+ # Simplification: Assume numbers/vars separated by nothing (since lexer doesn't produce commas for spaces)
197
+ # But parse_expr_iterative consumes everything.
198
+ # We need to split manually if they are distinct expressions.
199
+ # Let's try to parse one expr, see where it ends? Not easy with shunting yard.
200
+
201
+ # Fallback: Create a Call('range', ...)
202
+ # Actually, interpreter expects 'count' for For loop?
203
+ # No, AST For node: For(count, body) -> interpreted as Repeat?
204
+ # Wait, AST For(count, body) vs ForIn(var_name, iterable, body)
205
+
206
+ # Let's see AST definition.
207
+ pass
208
+
209
+ # It is likely a ForIn
210
+ # iterable is everything after IN
211
+ iterable_tokens = self._extract_expr_tokens(node.tokens, start=in_index+1)
212
+ iterable = self.parse_expr_iterative(iterable_tokens)
213
+ body = [self.bind_node(child) for child in node.children]
214
+
215
+ # Handle 'range 1 10' as an iterable (Call to range)
216
+ if node.tokens[in_index+1].type == 'RANGE':
217
+ # tokens: FOR i IN RANGE 1 10
218
+ # We want Call('range', [1, 10])
219
+ # Extract numbers after RANGE
220
+ args_tokens = self._extract_expr_tokens(node.tokens, start=in_index+2)
221
+ # Assumption: args are space separated expressions.
222
+ # parse_expr_iterative consumes all.
223
+ # We need to manually split if there are multiple args?
224
+ # But 'range 1 10' in ShellLite usually means two numbers.
225
+ # If we just Pass all tokens to parse_expr_iterative, it might just return the first one if not connected by operator.
226
+ # For 'range 1 10', we have NUMBER 1, NUMBER 10.
227
+ # We need to build a list of args.
228
+ range_args = []
229
+ k = 0
230
+ while k < len(args_tokens):
231
+ # Try to parse one expression?
232
+ # This is hard with current parser structure.
233
+ # SIMPLE HACK: Just take the next two tokens as numbers?
234
+ # Or treat them as separate expression?
235
+ if args_tokens[k].type in ('NUMBER', 'STRING', 'ID'):
236
+ t = args_tokens[k]
237
+ val = None
238
+ if t.type == 'NUMBER':
239
+ val = Number(int(t.value) if '.' not in t.value else float(t.value))
240
+ elif t.type == 'STRING':
241
+ val = String(t.value)
242
+ elif t.type == 'ID':
243
+ val = VarAccess(t.value)
244
+ if val: range_args.append(val)
245
+ k += 1
246
+
247
+ iterable = Call('range', range_args)
248
+
249
+ return ForIn(var_name, iterable, body)
142
250
  def bind_print(self, node: GeoNode) -> Print:
143
251
  expr_tokens = self._extract_expr_tokens(node.tokens, start=1)
144
252
  expr = self.parse_expr_iterative(expr_tokens)
@@ -352,7 +460,39 @@ class GeometricBindingParser:
352
460
  i = j # Advance past list
353
461
  elif t.type == 'ID':
354
462
  if i+1 < len(tokens) and tokens[i+1].type == 'LPAREN':
355
- values.append(VarAccess(t.value))
463
+ # Function call: ID(args)
464
+ name = t.value
465
+
466
+ # Find matching RPAREN
467
+ depth = 1
468
+ j = i + 2 # Skip ID and LPAREN
469
+ elements_tokens = []
470
+ current_elem = []
471
+
472
+ arg_tokens_start = j
473
+ # Check for empty call "func()"
474
+ if j < len(tokens) and tokens[j].type == 'RPAREN':
475
+ i = j # Advance to RPAREN
476
+ values.append(Call(name, []))
477
+ else:
478
+ while j < len(tokens):
479
+ if tokens[j].type == 'LPAREN': depth += 1
480
+ elif tokens[j].type == 'RPAREN': depth -= 1
481
+
482
+ if depth == 0:
483
+ if current_elem: elements_tokens.append(current_elem)
484
+ break
485
+
486
+ if tokens[j].type == 'COMMA' and depth == 1:
487
+ elements_tokens.append(current_elem)
488
+ current_elem = []
489
+ else:
490
+ current_elem.append(tokens[j])
491
+ j += 1
492
+
493
+ args = [self.parse_expr_iterative(elem) for elem in elements_tokens if elem]
494
+ values.append(Call(name, args))
495
+ i = j # Advance to RPAREN
356
496
  else:
357
497
  values.append(VarAccess(t.value))
358
498
  elif t.type == 'LPAREN':
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: shell-lite
3
- Version: 0.5.3.1
3
+ Version: 0.5.3.3
4
4
  Summary: A lightweight, English-like scripting language.
5
5
  Home-page: https://github.com/Shrey-N/ShellDesk
6
6
  Author: Shrey Naithani
@@ -19,17 +19,6 @@ shell_lite.egg-info/dependency_links.txt
19
19
  shell_lite.egg-info/entry_points.txt
20
20
  shell_lite.egg-info/requires.txt
21
21
  shell_lite.egg-info/top_level.txt
22
- shell_lite/llvm_backend/__init__.py
23
- shell_lite/llvm_backend/builder.py
24
- shell_lite/llvm_backend/codegen.py
25
- tests/__init__.py
26
- tests/benchmark_driver.py
27
- tests/compare_parsers.py
28
- tests/debug_jit.py
29
- tests/generate_actual_graph.py
30
- tests/generate_perf_graph.py
31
- tests/generate_runtime_graph.py
32
- tests/run_jit.py
33
22
  tests/test_gbp_standalone.py
34
23
  tests/test_interpreter.py
35
24
  tests/test_lexer.py
File without changes
@@ -1,39 +0,0 @@
1
- import llvmlite.binding as llvm
2
- from .codegen import LLVMCompiler
3
- from ..lexer import Lexer
4
- from ..parser import Parser
5
- import os
6
- def build_llvm(filename: str):
7
- print(f"Compiling {filename} with LLVM Backend...")
8
- with open(filename, 'r', encoding='utf-8') as f:
9
- source = f.read()
10
- lexer = Lexer(source)
11
- tokens = lexer.tokenize()
12
- parser = Parser(tokens)
13
- statements = parser.parse()
14
- compiler = LLVMCompiler()
15
- module = compiler.compile(statements)
16
- llvm_ir = str(module)
17
- print("\n--- Generated LLVM IR ---")
18
- print(llvm_ir)
19
- print("-------------------------\n")
20
- ll_filename = os.path.splitext(filename)[0] + ".ll"
21
- with open(ll_filename, 'w') as f:
22
- f.write(llvm_ir)
23
- print(f"[SUCCESS] Generated LLVM IR: {ll_filename}")
24
- print("\nTo compile to executable, you can use Clang:")
25
- print(f" clang {ll_filename} -o {os.path.splitext(filename)[0]}.exe")
26
- """
27
- try:
28
- print("Initializing LLVM...")
29
- llvm.initialize()
30
- llvm.initialize_native_target()
31
- llvm.initialize_native_asmprinter()
32
- print("LLVM Initialized.")
33
- print("Creating Target...")
34
- target = llvm.Target.from_default_triple()
35
- print(f"Target Triple: {target}")
36
- target_machine = target.create_target_machine()
37
- except Exception as e:
38
- print(f"Native binding skipped due to: {e}")
39
- """
@@ -1,311 +0,0 @@
1
- from llvmlite import ir
2
- from ..ast_nodes import *
3
- class LLVMCompiler:
4
- def __init__(self):
5
- self.module = ir.Module(name="shell_lite_module")
6
- self.module.triple = "x86_64-pc-windows-msvc" # Assume Windows for now based on user OS
7
- self.int32 = ir.IntType(32)
8
- self.char_ptr = ir.IntType(8).as_pointer()
9
- voidptr_ty = ir.IntType(8).as_pointer()
10
- printf_ty = ir.FunctionType(self.int32, [voidptr_ty], var_arg=True)
11
- self.printf = ir.Function(self.module, printf_ty, name="printf")
12
- malloc_ty = ir.FunctionType(voidptr_ty, [self.int32])
13
- self.malloc = ir.Function(self.module, malloc_ty, name="malloc")
14
- free_ty = ir.FunctionType(ir.VoidType(), [voidptr_ty])
15
- self.free = ir.Function(self.module, free_ty, name="free")
16
- strlen_ty = ir.FunctionType(self.int32, [voidptr_ty])
17
- self.strlen = ir.Function(self.module, strlen_ty, name="strlen")
18
- strcpy_ty = ir.FunctionType(voidptr_ty, [voidptr_ty, voidptr_ty])
19
- self.strcpy = ir.Function(self.module, strcpy_ty, name="strcpy")
20
- strcat_ty = ir.FunctionType(voidptr_ty, [voidptr_ty, voidptr_ty])
21
- self.strcat = ir.Function(self.module, strcat_ty, name="strcat")
22
- fopen_ty = ir.FunctionType(voidptr_ty, [voidptr_ty, voidptr_ty])
23
- self.fopen = ir.Function(self.module, fopen_ty, name="fopen")
24
- fclose_ty = ir.FunctionType(self.int32, [voidptr_ty])
25
- self.fclose = ir.Function(self.module, fclose_ty, name="fclose")
26
- fwrite_ty = ir.FunctionType(self.int32, [voidptr_ty, self.int32, self.int32, voidptr_ty])
27
- self.fwrite = ir.Function(self.module, fwrite_ty, name="fwrite")
28
- fread_ty = ir.FunctionType(self.int32, [voidptr_ty, self.int32, self.int32, voidptr_ty])
29
- self.fread = ir.Function(self.module, fread_ty, name="fread")
30
- fgets_ty = ir.FunctionType(voidptr_ty, [voidptr_ty, self.int32, voidptr_ty])
31
- self.fgets = ir.Function(self.module, fgets_ty, name="fgets")
32
- fseek_ty = ir.FunctionType(self.int32, [voidptr_ty, self.int32, self.int32])
33
- self.fseek = ir.Function(self.module, fseek_ty, name="fseek")
34
- ftell_ty = ir.FunctionType(self.int32, [voidptr_ty])
35
- self.ftell = ir.Function(self.module, ftell_ty, name="ftell")
36
- rewind_ty = ir.FunctionType(ir.VoidType(), [voidptr_ty])
37
- self.rewind = ir.Function(self.module, rewind_ty, name="rewind")
38
- get_stdin_ty = ir.FunctionType(voidptr_ty, [self.int32])
39
- self.get_stdin = ir.Function(self.module, get_stdin_ty, name="__acrt_iob_func")
40
- system_ty = ir.FunctionType(self.int32, [voidptr_ty])
41
- self.system = ir.Function(self.module, system_ty, name="system")
42
- func_type = ir.FunctionType(self.int32, [], var_arg=False)
43
- self.main_func = ir.Function(self.module, func_type, name="main")
44
- block = self.main_func.append_basic_block(name="entry")
45
- self.builder = ir.IRBuilder(block)
46
- self.scopes = [{}]
47
- self.loop_stack = []
48
- self.str_constants = {}
49
- def _get_scope(self):
50
- return self.scopes[-1]
51
- def _alloca(self, name, typ=None):
52
- if typ is None: typ = self.int32
53
- with self.builder.goto_entry_block():
54
- ptr = self.builder.alloca(typ, size=None, name=name)
55
- return ptr
56
- def compile(self, statements):
57
- for stmt in statements:
58
- self.visit(stmt)
59
- self.builder.ret(ir.Constant(self.int32, 0))
60
- return self.module
61
- def visit(self, node):
62
- method_name = f'visit_{type(node).__name__}'
63
- visitor = getattr(self, method_name, self.generic_visit)
64
- return visitor(node)
65
- def generic_visit(self, node):
66
- print(f"Warning: LLVM Backend does not support {type(node).__name__} yet.")
67
- return None
68
- def visit_Number(self, node: Number):
69
- return ir.Constant(self.int32, int(node.value))
70
- def visit_String(self, node: String):
71
- return self._get_string_constant(node.value)
72
- def visit_BinOp(self, node: BinOp):
73
- left = self.visit(node.left)
74
- right = self.visit(node.right)
75
- op = node.op
76
- if op == '+':
77
- is_str_op = False
78
- if left.type == self.char_ptr or right.type == self.char_ptr:
79
- is_str_op = True
80
- if is_str_op:
81
- if left.type == self.int32:
82
- left = self.builder.inttoptr(left, self.char_ptr, name="cast_l")
83
- if right.type == self.int32:
84
- right = self.builder.inttoptr(right, self.char_ptr, name="cast_r")
85
- if is_str_op:
86
- len1 = self.builder.call(self.strlen, [left], name="len1")
87
- len2 = self.builder.call(self.strlen, [right], name="len2")
88
- total_len = self.builder.add(len1, len2, name="total_len")
89
- total_len_null = self.builder.add(total_len, ir.Constant(self.int32, 1), name="alloc_len")
90
- new_str = self.builder.call(self.malloc, [total_len_null], name="new_str")
91
- self.builder.call(self.strcpy, [new_str, left])
92
- self.builder.call(self.strcat, [new_str, right])
93
- return new_str
94
- return self.builder.add(left, right, name="addtmp")
95
- elif op == '-':
96
- return self.builder.sub(left, right, name="subtmp")
97
- elif op == '*':
98
- return self.builder.mul(left, right, name="multmp")
99
- elif op == '/':
100
- return self.builder.sdiv(left, right, name="divtmp")
101
- elif op == '==' or op == 'is':
102
- return self.builder.icmp_signed('==', left, right, name="eqtmp")
103
- elif op == '!=' or op == 'is not':
104
- return self.builder.icmp_signed('!=', left, right, name="netmp")
105
- elif op == '<':
106
- return self.builder.icmp_signed('<', left, right, name="lttmp")
107
- elif op == '<=':
108
- return self.builder.icmp_signed('<=', left, right, name="letmp")
109
- elif op == '>':
110
- return self.builder.icmp_signed('>', left, right, name="gttmp")
111
- elif op == '>=':
112
- return self.builder.icmp_signed('>=', left, right, name="getmp")
113
- else:
114
- raise Exception(f"Unknown operator: {op}")
115
- def visit_If(self, node: If):
116
- cond_val = self.visit(node.condition)
117
- if cond_val.type != ir.IntType(1):
118
- cond_val = self.builder.icmp_signed('!=', cond_val, ir.Constant(self.int32, 0), name="ifcond")
119
- then_bb = self.builder.append_basic_block(name="then")
120
- else_bb = self.builder.append_basic_block(name="else")
121
- merge_bb = self.builder.append_basic_block(name="ifcont")
122
- self.builder.cbranch(cond_val, then_bb, else_bb)
123
- self.builder.position_at_end(then_bb)
124
- for stmt in node.body:
125
- self.visit(stmt)
126
- if not self.builder.block.is_terminated:
127
- self.builder.branch(merge_bb)
128
- self.builder.position_at_end(else_bb)
129
- if node.else_body:
130
- for stmt in node.else_body:
131
- self.visit(stmt)
132
- if not self.builder.block.is_terminated:
133
- self.builder.branch(merge_bb)
134
- self.builder.position_at_end(merge_bb)
135
- def visit_While(self, node: While):
136
- cond_bb = self.builder.append_basic_block(name="loop.cond")
137
- body_bb = self.builder.append_basic_block(name="loop.body")
138
- after_bb = self.builder.append_basic_block(name="loop.after")
139
- self.loop_stack.append((cond_bb, after_bb))
140
- self.builder.branch(cond_bb)
141
- self.builder.position_at_end(cond_bb)
142
- cond_val = self.visit(node.condition)
143
- if cond_val.type != ir.IntType(1):
144
- cond_val = self.builder.icmp_signed('!=', cond_val, ir.Constant(self.int32, 0), name="loopcond")
145
- self.builder.cbranch(cond_val, body_bb, after_bb)
146
- self.builder.position_at_end(body_bb)
147
- for stmt in node.body:
148
- self.visit(stmt)
149
- self.builder.branch(cond_bb) # Loop back
150
- self.builder.position_at_end(after_bb)
151
- self.loop_stack.pop()
152
- def visit_Repeat(self, node: Repeat):
153
- count_val = self.visit(node.count)
154
- import random
155
- uid = random.randint(0, 10000)
156
- i_ptr = self._alloca(f"_loop_i_{uid}")
157
- self.builder.store(ir.Constant(self.int32, 0), i_ptr)
158
- cond_bb = self.builder.append_basic_block(name="repeat.cond")
159
- body_bb = self.builder.append_basic_block(name="repeat.body")
160
- after_bb = self.builder.append_basic_block(name="repeat.after")
161
- self.loop_stack.append((cond_bb, after_bb))
162
- self.builder.branch(cond_bb)
163
- self.builder.position_at_end(cond_bb)
164
- curr_i = self.builder.load(i_ptr, name="i_load")
165
- cmp = self.builder.icmp_signed('<', curr_i, count_val, name="loopcheck")
166
- self.builder.cbranch(cmp, body_bb, after_bb)
167
- self.builder.position_at_end(body_bb)
168
- for stmt in node.body:
169
- self.visit(stmt)
170
- curr_i_Body = self.builder.load(i_ptr)
171
- next_i = self.builder.add(curr_i_Body, ir.Constant(self.int32, 1), name="inc_i")
172
- self.builder.store(next_i, i_ptr)
173
- self.builder.branch(cond_bb)
174
- self.builder.position_at_end(after_bb)
175
- self.loop_stack.pop()
176
- def visit_FunctionDef(self, node: FunctionDef):
177
- arg_types = [self.int32] * len(node.args)
178
- arg_types = []
179
- for arg in node.args:
180
- name, _, hint = arg # arg is tuple
181
- if True: # FORCE ALL ARGS TO STRINGS FOR SHELL LITE (Phase 7 Fix)
182
- arg_types.append(self.char_ptr)
183
- else:
184
- arg_types.append(self.int32)
185
- func_ty = ir.FunctionType(self.int32, arg_types) # Return int32 (or pointer casted)
186
- func = ir.Function(self.module, func_ty, name=node.name)
187
- old_builder = self.builder
188
- block = func.append_basic_block(name="entry")
189
- self.builder = ir.IRBuilder(block)
190
- self.scopes.append({}) # New scope
191
- for i, arg in enumerate(func.args):
192
- arg_name = node.args[i][0]
193
- arg.name = arg_name
194
- ptr = self.builder.alloca(arg.type, name=arg_name)
195
- self.builder.store(arg, ptr)
196
- self.scopes[-1][arg_name] = ptr
197
- for stmt in node.body:
198
- self.visit(stmt)
199
- if not self.builder.block.is_terminated:
200
- self.builder.ret(ir.Constant(self.int32, 0))
201
- self.scopes.pop()
202
- self.builder = old_builder
203
- def visit_Return(self, node: Return):
204
- val = self.visit(node.value)
205
- if val.type == self.char_ptr:
206
- val = self.builder.ptrtoint(val, self.int32)
207
- self.builder.ret(val)
208
- def visit_Call(self, node: Call):
209
- if node.name in self.module.globals:
210
- func = self.module.globals[node.name]
211
- elif node.name == 'read':
212
- if len(node.args) > 0:
213
- return self.visit_FileRead(FileRead(node.args[0]))
214
- else:
215
- return ir.Constant(self.int32, 0)
216
- else:
217
- print(f"Warning: Function {node.name} not found")
218
- return ir.Constant(self.int32, 0)
219
- args = [self.visit(a) for a in node.args]
220
- return self.builder.call(func, args, name="calltmp")
221
- def visit_Assign(self, node: Assign):
222
- value = self.visit(node.value)
223
- scope = self._get_scope()
224
- if node.name not in scope:
225
- ptr = self._alloca(node.name, typ=value.type)
226
- scope[node.name] = ptr
227
- else:
228
- ptr = scope[node.name]
229
- self.builder.store(value, ptr)
230
- return value
231
- def visit_Execute(self, node: Execute):
232
- cmd = self.visit(node.code)
233
- self.builder.call(self.system, [cmd])
234
- return ir.Constant(self.int32, 0)
235
- def visit_Stop(self, node: Stop):
236
- if not self.loop_stack:
237
- print("Error: stop used outside of loop")
238
- return
239
- after_bb = self.loop_stack[-1][1]
240
- self.builder.branch(after_bb)
241
- dead_bb = self.builder.append_basic_block(name="dead")
242
- self.builder.position_at_end(dead_bb)
243
- def visit_Skip(self, node: Skip):
244
- if not self.loop_stack:
245
- print("Error: skip used outside of loop")
246
- return
247
- cond_bb = self.loop_stack[-1][0]
248
- self.builder.branch(cond_bb)
249
- dead_bb = self.builder.append_basic_block(name="dead")
250
- self.builder.position_at_end(dead_bb)
251
- def _get_stdin_handle(self):
252
- return self.builder.call(self.get_stdin, [ir.Constant(self.int32, 0)])
253
- def visit_Input(self, node: Input):
254
- buffer_len = ir.Constant(self.int32, 256)
255
- buffer = self.builder.call(self.malloc, [buffer_len], name="input_buf")
256
- stdin = self._get_stdin_handle()
257
- self.builder.call(self.fgets, [buffer, buffer_len, stdin])
258
- return buffer
259
- def visit_FileWrite(self, node: FileWrite):
260
- path = self.visit(node.path)
261
- content = self.visit(node.content)
262
- mode = self._get_string_constant("w")
263
- fp = self.builder.call(self.fopen, [path, mode], name="fp")
264
- length = self.builder.call(self.strlen, [content])
265
- self.builder.call(self.fwrite, [content, ir.Constant(self.int32, 1), length, fp])
266
- self.builder.call(self.fclose, [fp])
267
- return ir.Constant(self.int32, 0)
268
- def visit_FileRead(self, node: FileRead):
269
- path = self.visit(node.path)
270
- mode = self._get_string_constant("rb") # Binary to avoid text translation issues? Or "r"
271
- fp = self.builder.call(self.fopen, [path, mode], name="fp")
272
- self.builder.call(self.fseek, [fp, ir.Constant(self.int32, 0), ir.Constant(self.int32, 2)])
273
- size = self.builder.call(self.ftell, [fp], name="fsize")
274
- self.builder.call(self.rewind, [fp])
275
- alloc_size = self.builder.add(size, ir.Constant(self.int32, 1))
276
- buffer = self.builder.call(self.malloc, [alloc_size], name="fbuf")
277
- self.builder.call(self.fread, [buffer, ir.Constant(self.int32, 1), size, fp])
278
- null_term_ptr = self.builder.gep(buffer, [size])
279
- self.builder.store(ir.Constant(ir.IntType(8), 0), null_term_ptr)
280
- self.builder.call(self.fclose, [fp])
281
- return buffer
282
- def visit_VarAccess(self, node: VarAccess):
283
- scope = self._get_scope()
284
- if node.name in scope:
285
- ptr = scope[node.name]
286
- return self.builder.load(ptr, name=node.name)
287
- else:
288
- raise Exception(f"Variable '{node.name}' not defined")
289
- def _get_string_constant(self, text):
290
- text += '\0'
291
- if text in self.str_constants:
292
- return self.str_constants[text]
293
- byte_arr = bytearray(text.encode("utf8"))
294
- c_str_ty = ir.ArrayType(ir.IntType(8), len(byte_arr))
295
- global_var = ir.GlobalVariable(self.module, c_str_ty, name=f".str_{len(self.str_constants)}")
296
- global_var.linkage = 'internal'
297
- global_var.global_constant = True
298
- global_var.initializer = ir.Constant(c_str_ty, byte_arr)
299
- ptr = self.builder.bitcast(global_var, self.char_ptr)
300
- self.str_constants[text] = ptr
301
- return ptr
302
- def visit_Print(self, node: Print):
303
- value = self.visit(node.expression)
304
- if value.type == self.char_ptr:
305
- fmt_str = self._get_string_constant("%s\n")
306
- self.builder.call(self.printf, [fmt_str, value])
307
- else:
308
- if value.type != self.int32:
309
- pass
310
- fmt_str = self._get_string_constant("%d\n")
311
- self.builder.call(self.printf, [fmt_str, value])
@@ -1 +0,0 @@
1
-
@@ -1,43 +0,0 @@
1
- import os
2
- import time
3
- import subprocess
4
- import sys
5
- SHL_CMD = ["python", "shell_lite/main.py"]
6
- def run_benchmark():
7
- print("Generating LLVM IR...")
8
- subprocess.run(SHL_CMD + ["llvm", "tests/runtime_benchmark.shl"], check=True)
9
- print("Compiling with Clang...")
10
- subprocess.run(["clang", "tests/runtime_benchmark.ll", "-o", "tests/runtime_benchmark.exe", "-O3"], check=True)
11
- print("\n[Running Native Executable...]")
12
- start = time.perf_counter()
13
- subprocess.run(["tests/runtime_benchmark.exe"], check=True)
14
- native_time = time.perf_counter() - start
15
- print(f"Native Time: {native_time:.4f}s")
16
- print("\n[Running Interpreter (GBP)...]")
17
- env = os.environ.copy()
18
- env["USE_GBP"] = "1"
19
- start = time.perf_counter()
20
- subprocess.run(SHL_CMD + ["run", "tests/runtime_benchmark.shl"], env=env, check=True)
21
- interp_time = time.perf_counter() - start
22
- print(f"Interpreter Time: {interp_time:.4f}s")
23
- print("\n[Running Python Equivalent...]")
24
- py_code = """
25
- i = 0
26
- sum = 0
27
- count = 100000000
28
- while i < count:
29
- sum = sum + 1
30
- i = i + 1
31
- print("Done")
32
- print(sum)
33
- """
34
- start = time.perf_counter()
35
- exec(py_code)
36
- py_time = time.perf_counter() - start
37
- print(f"Python Time: {py_time:.4f}s")
38
- print("\n" + "="*30)
39
- print(f"Native Speedup vs Interpreter: {interp_time/native_time:.2f}x")
40
- print(f"Native Speedup vs Python: {py_time/native_time:.2f}x")
41
- print("="*30)
42
- if __name__ == "__main__":
43
- run_benchmark()
@@ -1,31 +0,0 @@
1
- import sys
2
- import os
3
- import time
4
- sys.path.append(os.path.join(os.path.dirname(__file__), '..'))
5
- from shell_lite.lexer import Lexer
6
- from shell_lite.parser import Parser
7
- from shell_lite.parser_gbp import GeometricBindingParser
8
- def benchmark(filename):
9
- with open(filename, 'r') as f:
10
- source = f.read()
11
- long_source = source * 500
12
- print(f"Benchmarking on {len(long_source)} chars of code...")
13
- lexer = Lexer(long_source)
14
- tokens = lexer.tokenize()
15
- tokens_copy = list(tokens)
16
- start = time.perf_counter()
17
- p_old = Parser(list(tokens)) # fresh copy? Parser consumes? yes
18
- ast_old = p_old.parse()
19
- end = time.perf_counter()
20
- t_old = end - start
21
- print(f"Recursive Descent: {t_old:.4f}s")
22
- start = time.perf_counter()
23
- p_new = GeometricBindingParser(list(tokens))
24
- ast_new = p_new.parse()
25
- end = time.perf_counter()
26
- t_new = end - start
27
- print(f"Geometric-Binding: {t_new:.4f}s")
28
- diff = t_old / t_new if t_new > 0 else 0
29
- print(f"Speedup: {diff:.2f}x")
30
- if __name__ == "__main__":
31
- benchmark("tests/benchmark.shl")
@@ -1,49 +0,0 @@
1
- import llvmlite.binding as llvm
2
- import sys
3
- def debug():
4
- try:
5
- llvm.initialize()
6
- llvm.initialize_native_target()
7
- llvm.initialize_native_asmprinter()
8
- print("LLVM Initialized.")
9
- except Exception as e:
10
- print(f"Init Failed: {e}")
11
- return
12
- print("Available in llvm.binding:")
13
- ops = [x for x in dir(llvm) if 'create' in x or 'jit' in x.lower() or 'engine' in x.lower()]
14
- print(ops)
15
- try:
16
- mod = llvm.parse_assembly('define i32 @answer() { ret i32 42 }')
17
- mod.verify()
18
- print("Module parsed.")
19
- target = llvm.Target.from_default_triple()
20
- target_machine = target.create_target_machine()
21
- engine = None
22
- if hasattr(llvm, 'create_mcjit_compiler'):
23
- print("Attempting MCJIT...")
24
- try:
25
- engine = llvm.create_mcjit_compiler(mod, target_machine)
26
- print("MCJIT Created!")
27
- except Exception as e:
28
- print(f"MCJIT Failed: {e}")
29
- if not engine and hasattr(llvm, 'create_execution_engine'):
30
- print("Attempting create_execution_engine...")
31
- try:
32
- engine = llvm.create_execution_engine()
33
- engine.add_module(mod)
34
- print("ExecEngine Created!")
35
- except Exception as e:
36
- print(f"ExecEngine Failed: {e}")
37
- if engine:
38
- engine.finalize_object()
39
- print("Engine Finalized.")
40
- addr = engine.get_function_address("answer")
41
- print(f"Function Address: {addr}")
42
- import ctypes
43
- cfunc = ctypes.CFUNCTYPE(ctypes.c_int)(addr)
44
- res = cfunc()
45
- print(f"Result: {res}")
46
- except Exception as e:
47
- print(f"Module/Engine Error: {e}")
48
- if __name__ == "__main__":
49
- debug()
@@ -1,84 +0,0 @@
1
- import sys
2
- import os
3
- import time
4
- import matplotlib.pyplot as plt
5
- import subprocess
6
- import llvmlite.binding as llvm
7
- sys.path.append(os.path.join(os.path.dirname(__file__), '..'))
8
- try:
9
- from tests.run_jit import compile_and_run_jit
10
- except ImportError:
11
- sys.path.append(os.path.join(os.path.dirname(__file__)))
12
- from run_jit import compile_and_run_jit
13
- def run_benchmark():
14
- counts = [1000000, 10000000, 50000000, 100000000, 200000000]
15
- t_interp = []
16
- t_python = []
17
- t_llvm = []
18
- print("Running Massive Runtime Benchmark...")
19
- for n in counts:
20
- print(f"\n--- n = {n} ---")
21
- if n <= 100000: # Reduced limit for massive scale run
22
- shl_code = f"i = 0\nwhile i < {n}:\n i = i + 1\n"
23
- shl_file = f"tests/temp_{n}.shl"
24
- with open(shl_file, "w") as f: f.write(shl_code)
25
- env = os.environ.copy()
26
- env["USE_GBP"] = "1"
27
- start = time.perf_counter()
28
- try:
29
- subprocess.run(["python", "shell_lite/main.py", "run", shl_file], env=env, check=True, stdout=subprocess.DEVNULL, stderr=subprocess.PIPE)
30
- dur = time.perf_counter() - start
31
- except subprocess.CalledProcessError as e:
32
- print(f"Interp failed: {e.stderr.decode()}")
33
- dur = None
34
- except Exception as e:
35
- print(f"Interp failed: {e}")
36
- dur = None
37
- t_interp.append(dur)
38
- if os.path.exists(shl_file): os.remove(shl_file)
39
- if dur is not None:
40
- print(f"Interpreter: {dur:.4f}s")
41
- else:
42
- print("Interpreter: Failed")
43
- else:
44
- t_interp.append(None)
45
- print("Interpreter: Skipped (Too Slow)")
46
- start = time.perf_counter()
47
- i = 0
48
- while i < n:
49
- i += 1
50
- dur = time.perf_counter() - start
51
- t_python.append(dur)
52
- print(f"Python: {dur:.6f}s")
53
- jit_code = f"""
54
- i = 0
55
- count = {n}
56
- while i < count:
57
- i = i + 1
58
- """
59
- try:
60
- _, dur, _ = compile_and_run_jit(jit_code)
61
- if dur < 1e-7: dur = 1e-7
62
- t_llvm.append(dur)
63
- print(f"LLVM JIT: {dur:.8f}s")
64
- except Exception as e:
65
- print(f"JIT Failed: {e}")
66
- t_llvm.append(None)
67
- plt.figure(figsize=(10, 6))
68
- x_interp = [x for x, y in zip(counts, t_interp) if y is not None]
69
- y_interp = [y for y in t_interp if y is not None]
70
- if x_interp:
71
- plt.plot(x_interp, y_interp, label='ShellLite Interpreter', marker='o', color='orange')
72
- plt.plot(counts, t_python, label='Python Native', marker='s', color='green')
73
- plt.plot(counts, t_llvm, label='LLVM JIT', marker='^', color='purple', linestyle='-')
74
- plt.xlabel('Iterations')
75
- plt.ylabel('Time (seconds)')
76
- plt.title('GBP+LLVM Runtime Performance (Massive Scale)')
77
- plt.yscale('log')
78
- plt.legend()
79
- plt.grid(True, which="both", ls="-", alpha=0.2)
80
- output_path = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..', 'benchmark_final.png'))
81
- plt.savefig(output_path)
82
- print(f"Graph saved to {output_path}")
83
- if __name__ == "__main__":
84
- run_benchmark()
@@ -1,68 +0,0 @@
1
- import sys
2
- import os
3
- import time
4
- import matplotlib.pyplot as plt
5
- sys.path.append(os.path.join(os.path.dirname(__file__), '..'))
6
- from shell_lite.lexer import Lexer
7
- from shell_lite.parser import Parser
8
- from shell_lite.parser_gbp import GeometricBindingParser
9
- HAS_LLVM = False
10
- try:
11
- from shell_lite.llvm_backend.codegen import LLVMCompiler
12
- HAS_LLVM = True
13
- except ImportError:
14
- print("LLVM backend (llvmlite) not found. Skipping LLVM benchmark.")
15
- def generate_large_file(lines=1000):
16
- code = "x = 0\n"
17
- for i in range(lines):
18
- code += f"if x < {i}:\n"
19
- code += f" x = x + 1\n"
20
- return code
21
- def run_benchmark():
22
- sizes = [1000, 5000, 10000, 20000, 50000]
23
- times_old = []
24
- times_gbp = []
25
- times_py = []
26
- times_llvm = []
27
- for size in sizes:
28
- print(f"Benchmarking size: {size} lines...")
29
- source_shl = generate_large_file(size)
30
- source_py = source_shl.replace("x = x + 1", "x += 1")
31
- start = time.perf_counter()
32
- l = Lexer(source_shl)
33
- toks = l.tokenize()
34
- Parser(toks).parse()
35
- times_old.append(time.perf_counter() - start)
36
- start = time.perf_counter()
37
- l = Lexer(source_shl)
38
- toks = l.tokenize()
39
- GeometricBindingParser(toks).parse()
40
- times_gbp.append(time.perf_counter() - start)
41
- start = time.perf_counter()
42
- compile(source_py, '<string>', 'exec')
43
- times_py.append(time.perf_counter() - start)
44
- start = time.perf_counter()
45
- if HAS_LLVM:
46
- l = Lexer(source_shl)
47
- toks = l.tokenize()
48
- ast = GeometricBindingParser(toks).parse()
49
- LLVMCompiler().compile(ast)
50
- times_llvm.append(time.perf_counter() - start)
51
- else:
52
- times_llvm.append(0)
53
- plt.figure(figsize=(10, 6))
54
- plt.plot(sizes, times_old, label='Old Parser', marker='o', color='red')
55
- plt.plot(sizes, times_gbp, label='GBP Parser', marker='s', color='blue')
56
- plt.plot(sizes, times_py, label='Python Native', marker='^', color='green')
57
- if HAS_LLVM and any(times_llvm):
58
- plt.plot(sizes, times_llvm, label='LLVM Compile (via GBP)', marker='x', color='purple', linestyle='--')
59
- plt.xlabel('Lines of Code')
60
- plt.ylabel('Time (seconds)')
61
- plt.title('Parsing/Compilation Speed vs Code Size')
62
- plt.legend()
63
- plt.grid(True)
64
- output_path = os.path.join(os.path.dirname(__file__), 'benchmark_scaling.png')
65
- plt.savefig(output_path)
66
- print(f"Graph saved to {output_path}")
67
- if __name__ == "__main__":
68
- run_benchmark()
@@ -1,58 +0,0 @@
1
- import sys
2
- import os
3
- import time
4
- import matplotlib.pyplot as plt
5
- import subprocess
6
- sys.path.append(os.path.join(os.path.dirname(__file__), '..'))
7
- def run_benchmark():
8
- iterations = 1000000 # 1 Million
9
- print(f"Benchmarking Runtime (Count: {iterations})...")
10
- shl_code = f"""
11
- i = 0
12
- while i < {iterations}:
13
- i = i + 1
14
- """
15
- shl_file = "tests/temp_loop.shl"
16
- with open(shl_file, "w") as f:
17
- f.write(shl_code)
18
- env = os.environ.copy()
19
- env["USE_GBP"] = "1"
20
- start = time.perf_counter()
21
- try:
22
- subprocess.run(["python", "shell_lite/main.py", "run", shl_file], env=env, check=True, stdout=subprocess.DEVNULL, stderr=subprocess.PIPE)
23
- except subprocess.CalledProcessError as e:
24
- print(f"Interpreter Crash:\n{e.stderr.decode()}")
25
- t_interp = 10.0 # Dummy
26
- else:
27
- t_interp = time.perf_counter() - start
28
- print(f"Interpreter: {t_interp:.4f}s")
29
- start = time.perf_counter()
30
- i = 0
31
- while i < iterations:
32
- i = i + 1
33
- t_python = time.perf_counter() - start
34
- print(f"Python: {t_python:.4f}s")
35
- t_llvm_est = t_python / 20.0
36
- print(f"LLVM (Est): {t_llvm_est:.4f}s")
37
- labels = ['ShellLite Interpreter', 'Python Native', 'LLVM Native (Projected)']
38
- times = [t_interp, t_python, t_llvm_est]
39
- colors = ['orange', 'green', 'purple']
40
- plt.figure(figsize=(10, 6))
41
- bars = plt.bar(labels, times, color=colors)
42
- plt.ylabel('Execution Time (seconds)')
43
- plt.title(f'Runtime Speed Comparison ({iterations} Iterations)')
44
- plt.yscale('log') # Log scale because difference is massive
45
- for bar in bars:
46
- height = bar.get_height()
47
- plt.text(bar.get_x() + bar.get_width()/2., height,
48
- f'{height:.4f}s',
49
- ha='center', va='bottom')
50
- plt.figtext(0.5, 0.01,
51
- "Note: Logarithmic Scale used due to massive speed difference.",
52
- ha="center", fontsize=10)
53
- output_path = os.path.join(os.path.dirname(__file__), 'benchmark_runtime.png')
54
- plt.savefig(output_path)
55
- print(f"Graph saved to {output_path}")
56
- if os.path.exists(shl_file): os.remove(shl_file)
57
- if __name__ == "__main__":
58
- run_benchmark()
@@ -1,70 +0,0 @@
1
- import sys
2
- import os
3
- import time
4
- import ctypes
5
- import llvmlite.binding as llvm
6
- try:
7
- from llvmlite.binding.orcjit import create_lljit_compiler, JITLibraryBuilder
8
- except ImportError:
9
- import llvmlite.binding.orcjit as orc
10
- create_lljit_compiler = orc.create_lljit_compiler
11
- JITLibraryBuilder = orc.JITLibraryBuilder
12
- sys.path.append(os.path.join(os.path.dirname(__file__), '..'))
13
- from shell_lite.lexer import Lexer
14
- from shell_lite.parser_gbp import GeometricBindingParser
15
- from shell_lite.llvm_backend.codegen import LLVMCompiler
16
- def compile_and_run_jit(source_code):
17
- llvm.initialize_native_target()
18
- llvm.initialize_native_asmprinter()
19
- start_compile = time.perf_counter()
20
- lexer = Lexer(source_code)
21
- tokens = lexer.tokenize()
22
- ast = GeometricBindingParser(tokens).parse()
23
- compiler = LLVMCompiler()
24
- ir_module = compiler.compile(ast)
25
- llvm_ir = str(ir_module)
26
- start_jit = time.perf_counter()
27
- lljit = create_lljit_compiler()
28
- builder = JITLibraryBuilder()
29
- builder.add_ir(llvm_ir)
30
- tracker = builder.link(lljit, "shell_lite_lib")
31
- try:
32
- addr = tracker["main"]
33
- except KeyError:
34
- try:
35
- addr = tracker["_main"]
36
- except KeyError:
37
- print("Error: Could not find 'main' symbol.")
38
- return 0, 0, -1
39
- jit_time = time.perf_counter() - start_jit
40
- cfunc = ctypes.CFUNCTYPE(ctypes.c_int)(addr)
41
- start_run = time.perf_counter()
42
- res = cfunc()
43
- run_time = time.perf_counter() - start_run
44
- return start_jit - start_compile, run_time, res
45
- if __name__ == "__main__":
46
- code = """
47
- sum = 0
48
- i = 0
49
- count = 10000000
50
- while i < count:
51
- sum = sum + 1
52
- i = i + 1
53
- print sum
54
- """
55
- print("Running JIT Speed Test (10M iterations)...")
56
- try:
57
- c_time, r_time, res = compile_and_run_jit(code)
58
- print(f"Result: {res}")
59
- print(f"JIT Exec Time: {r_time:.6f}s")
60
- start_py = time.perf_counter()
61
- s, i, c = 0, 0, 10000000
62
- while i < c:
63
- s += 1
64
- i += 1
65
- py_time = time.perf_counter() - start_py
66
- print(f"Python Exec: {py_time:.6f}s")
67
- print(f"Speedup: {py_time / r_time:.2f}x")
68
- except Exception as e:
69
- import traceback
70
- traceback.print_exc()
File without changes
File without changes
File without changes
File without changes