shell-lite 0.5.3__tar.gz → 0.5.3.2__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/shell_lite.egg-info → shell_lite-0.5.3.2}/PKG-INFO +1 -1
  2. {shell_lite-0.5.3 → shell_lite-0.5.3.2}/pyproject.toml +1 -1
  3. {shell_lite-0.5.3 → shell_lite-0.5.3.2}/shell_lite/interpreter.py +4 -2
  4. {shell_lite-0.5.3 → shell_lite-0.5.3.2}/shell_lite/main.py +7 -2
  5. {shell_lite-0.5.3 → shell_lite-0.5.3.2}/shell_lite/parser_gbp.py +133 -10
  6. {shell_lite-0.5.3 → shell_lite-0.5.3.2/shell_lite.egg-info}/PKG-INFO +1 -1
  7. {shell_lite-0.5.3 → shell_lite-0.5.3.2}/shell_lite.egg-info/SOURCES.txt +0 -11
  8. shell_lite-0.5.3/shell_lite/llvm_backend/__init__.py +0 -0
  9. shell_lite-0.5.3/shell_lite/llvm_backend/builder.py +0 -39
  10. shell_lite-0.5.3/shell_lite/llvm_backend/codegen.py +0 -311
  11. shell_lite-0.5.3/tests/__init__.py +0 -1
  12. shell_lite-0.5.3/tests/benchmark_driver.py +0 -43
  13. shell_lite-0.5.3/tests/compare_parsers.py +0 -31
  14. shell_lite-0.5.3/tests/debug_jit.py +0 -49
  15. shell_lite-0.5.3/tests/generate_actual_graph.py +0 -84
  16. shell_lite-0.5.3/tests/generate_perf_graph.py +0 -68
  17. shell_lite-0.5.3/tests/generate_runtime_graph.py +0 -58
  18. shell_lite-0.5.3/tests/run_jit.py +0 -70
  19. {shell_lite-0.5.3 → shell_lite-0.5.3.2}/LICENSE +0 -0
  20. {shell_lite-0.5.3 → shell_lite-0.5.3.2}/README.md +0 -0
  21. {shell_lite-0.5.3 → shell_lite-0.5.3.2}/setup.cfg +0 -0
  22. {shell_lite-0.5.3 → shell_lite-0.5.3.2}/setup.py +0 -0
  23. {shell_lite-0.5.3 → shell_lite-0.5.3.2}/shell_lite/__init__.py +0 -0
  24. {shell_lite-0.5.3 → shell_lite-0.5.3.2}/shell_lite/ast_nodes.py +0 -0
  25. {shell_lite-0.5.3 → shell_lite-0.5.3.2}/shell_lite/cli.py +0 -0
  26. {shell_lite-0.5.3 → shell_lite-0.5.3.2}/shell_lite/compiler.py +0 -0
  27. {shell_lite-0.5.3 → shell_lite-0.5.3.2}/shell_lite/js_compiler.py +0 -0
  28. {shell_lite-0.5.3 → shell_lite-0.5.3.2}/shell_lite/lexer.py +0 -0
  29. {shell_lite-0.5.3 → shell_lite-0.5.3.2}/shell_lite/parser.py +0 -0
  30. {shell_lite-0.5.3 → shell_lite-0.5.3.2}/shell_lite/runtime.py +0 -0
  31. {shell_lite-0.5.3 → shell_lite-0.5.3.2}/shell_lite.egg-info/dependency_links.txt +0 -0
  32. {shell_lite-0.5.3 → shell_lite-0.5.3.2}/shell_lite.egg-info/entry_points.txt +0 -0
  33. {shell_lite-0.5.3 → shell_lite-0.5.3.2}/shell_lite.egg-info/requires.txt +0 -0
  34. {shell_lite-0.5.3 → shell_lite-0.5.3.2}/shell_lite.egg-info/top_level.txt +0 -0
  35. {shell_lite-0.5.3 → shell_lite-0.5.3.2}/tests/test_gbp_standalone.py +0 -0
  36. {shell_lite-0.5.3 → shell_lite-0.5.3.2}/tests/test_interpreter.py +0 -0
  37. {shell_lite-0.5.3 → shell_lite-0.5.3.2}/tests/test_lexer.py +0 -0
  38. {shell_lite-0.5.3 → shell_lite-0.5.3.2}/tests/test_parser.py +0 -0
  39. {shell_lite-0.5.3 → shell_lite-0.5.3.2}/tests/test_phase1.py +0 -0
  40. {shell_lite-0.5.3 → shell_lite-0.5.3.2}/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
3
+ Version: 0.5.3.2
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"
7
+ version = "0.5.3.2"
8
8
  description = "A lightweight, English-like scripting language."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.8"
@@ -240,8 +240,10 @@ class Interpreter:
240
240
  for k, v in self.builtins.items():
241
241
  self.global_env.set(k, v)
242
242
  def _make_tag_fn(self, tag_name):
243
- def tag_fn(*args):
243
+ def tag_fn(*args, **kwargs):
244
244
  attrs = {}
245
+ # Add kwargs directly as attributes
246
+ attrs.update(kwargs)
245
247
  content = []
246
248
  for arg in args:
247
249
  if isinstance(arg, dict):
@@ -1502,7 +1504,7 @@ class Interpreter:
1502
1504
  self.wfile.write(str(e).encode())
1503
1505
  except: pass
1504
1506
  server = ReusableHTTPServer(('0.0.0.0', port_val), ShellLiteHandler)
1505
- print(f"\n ShellLite Server v0.5.3 is running!")
1507
+ print(f"\n ShellLite Server v0.5.3.2 is running!")
1506
1508
  print(f" \u001b[1;36m➜\u001b[0m Local: \u001b[1;4;36mhttp://localhost:{port_val}/\u001b[0m\n")
1507
1509
  try: server.serve_forever()
1508
1510
  except KeyboardInterrupt:
@@ -17,7 +17,6 @@ def execute_source(source: str, interpreter: Interpreter):
17
17
  lexer = Lexer(source)
18
18
  tokens = lexer.tokenize()
19
19
  if os.environ.get('USE_LEGACY_PARSER') == '1':
20
- from .parser import Parser
21
20
  parser = Parser(tokens)
22
21
  else:
23
22
  from .parser_gbp import GeometricBindingParser
@@ -151,7 +150,7 @@ def install_globally():
151
150
  return
152
151
  ps_cmd = f'$oldPath = [Environment]::GetEnvironmentVariable("Path", "User"); if ($oldPath -notlike "*ShellLite*") {{ [Environment]::SetEnvironmentVariable("Path", "$oldPath;{install_dir}", "User") }}'
153
152
  subprocess.run(["powershell", "-Command", ps_cmd], capture_output=True)
154
- print(f"\n[SUCCESS] ShellLite (v0.5.3) is installed!")
153
+ print(f"\n[SUCCESS] ShellLite (v0.5.3.2) is installed!")
155
154
  print(f"Location: {install_dir}")
156
155
  print("\nIMPORTANT STEP REQUIRED:")
157
156
  print("1. Close ALL open terminal windows (CMD, PowerShell, VS Code).")
@@ -444,6 +443,12 @@ def main():
444
443
  print("Usage: shl llvm <filename>")
445
444
  elif cmd == "help" or cmd == "--help" or cmd == "-h":
446
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")
447
452
  elif cmd == "get":
448
453
  if len(sys.argv) > 2:
449
454
  package_name = sys.argv[2]
@@ -43,20 +43,24 @@ class GeometricBindingParser:
43
43
  node_stack: List[GeoNode] = [] # The active parents
44
44
  current_tokens_accumulator = []
45
45
  current_node: Optional[GeoNode] = None
46
+ last_line_node: Optional[GeoNode] = None # Track the last completed line's node
46
47
  block_stack: List[GeoNode] = []
47
48
  for token in self.tokens:
48
49
  if token.type == 'EOF':
49
50
  break
50
51
  if token.type == 'INDENT':
51
- if current_node:
52
- block_stack.append(current_node)
53
- current_node = None # We are stepping "inside", so no current line active node yet
52
+ # Use last_line_node as parent if current_node is None (NEWLINE came before INDENT)
53
+ parent_to_push = current_node if current_node else last_line_node
54
+ if parent_to_push:
55
+ block_stack.append(parent_to_push)
56
+ current_node = None
54
57
  continue
55
58
  if token.type == 'DEDENT':
56
59
  if block_stack:
57
60
  block_stack.pop()
58
61
  continue
59
62
  if token.type == 'NEWLINE':
63
+ last_line_node = current_node # Save before resetting
60
64
  current_node = None
61
65
  continue
62
66
  if current_node is None:
@@ -83,7 +87,17 @@ class GeometricBindingParser:
83
87
  return self.bind_while(node)
84
88
  elif head_type == 'FOR' or head_type == 'LOOP':
85
89
  return self.bind_for(node)
86
- elif head_type == 'FUNCTION' or head_type == 'TO' or (head_type == 'DEFINE' and self.peek_type(node, 1) == 'FUNCTION'):
90
+ elif head_type == 'USE':
91
+ return self.bind_use(node)
92
+ elif head_type == 'SERVE':
93
+ return self.bind_serve(node)
94
+ elif head_type == 'DEFINE':
95
+ return self.bind_define(node)
96
+ elif head_type == 'WHEN':
97
+ return self.bind_when(node)
98
+ elif head_type == 'ON':
99
+ return self.bind_on(node)
100
+ elif head_type == 'FUNCTION' or head_type == 'TO':
87
101
  return self.bind_func(node)
88
102
  elif head_type == 'PRINT' or head_type == 'SAY':
89
103
  return self.bind_print(node)
@@ -96,11 +110,13 @@ class GeometricBindingParser:
96
110
  elif head_type == 'LISTEN':
97
111
  return self.bind_listen(node)
98
112
  elif head_type == 'ID':
99
- if any(t.type == 'ASSIGN' for t in node.tokens):
113
+ # Assignment: ID ASSIGN ... (second token must be ASSIGN)
114
+ # Function call with kwargs: ID ID ASSIGN ... (ASSIGN comes later)
115
+ if len(node.tokens) >= 2 and node.tokens[1].type == 'ASSIGN':
100
116
  return self.bind_assignment(node)
101
- return self.bind_expression_stmt(node)
117
+ return self.bind_call_or_expr(node)
102
118
  else:
103
- return self.bind_expression_stmt(node)
119
+ return self.bind_call_or_expr(node)
104
120
  def peek_type(self, node: GeoNode, offset: int) -> str:
105
121
  if offset < len(node.tokens):
106
122
  return node.tokens[offset].type
@@ -159,12 +175,119 @@ class GeometricBindingParser:
159
175
  if node.tokens[0].type == 'DEFINE': start = 2
160
176
  name = node.tokens[start].value
161
177
  args = []
178
+ # Look for 'using' keyword (which is tokenized as ID)
179
+ collecting_args = False
162
180
  for t in node.tokens[start+1:]:
163
- if t.type == 'ID':
164
- args.append((t.value, None, None))
165
- elif t.type == 'COLON': break # End of signature
181
+ if t.type == 'USING':
182
+ collecting_args = True
183
+ continue
184
+ if t.type == 'ID' and collecting_args:
185
+ args.append((t.value, None, None))
186
+ elif t.type == 'COLON': break
187
+ elif t.type == 'COMMA': continue
188
+ body = [self.bind_node(child) for child in node.children]
189
+ return FunctionDef(name, args, body)
190
+ def bind_use(self, node: GeoNode) -> Import:
191
+ # 'use "filename.shl"'
192
+ for t in node.tokens:
193
+ if t.type == 'STRING':
194
+ return Import(t.value)
195
+ return Import(node.tokens[1].value if len(node.tokens) > 1 else '')
196
+ def bind_serve(self, node: GeoNode) -> ServeStatic:
197
+ # 'serve files from "public" at "/static"'
198
+ folder = String('public')
199
+ url = String('/static')
200
+ tokens = node.tokens
201
+ for i, t in enumerate(tokens):
202
+ if t.type == 'FROM' and i+1 < len(tokens):
203
+ folder = self.parse_expr_iterative([tokens[i+1]])
204
+ if t.type == 'AT' and i+1 < len(tokens):
205
+ url = self.parse_expr_iterative([tokens[i+1]])
206
+ return ServeStatic(folder, url)
207
+ def bind_define(self, node: GeoNode) -> FunctionDef:
208
+ # 'define page Name using arg1, arg2'
209
+ tokens = node.tokens
210
+ name = ''
211
+ args = []
212
+ i = 1
213
+ # Skip 'page' or 'component' if present
214
+ if i < len(tokens) and tokens[i].type == 'PAGE':
215
+ i += 1
216
+ if i < len(tokens) and tokens[i].type == 'ID':
217
+ name = tokens[i].value
218
+ i += 1
219
+ # Parse 'using' args
220
+ if i < len(tokens) and tokens[i].type == 'USING':
221
+ i += 1
222
+ while i < len(tokens):
223
+ if tokens[i].type == 'ID':
224
+ args.append((tokens[i].value, None, None))
225
+ elif tokens[i].type == 'COMMA':
226
+ pass
227
+ else:
228
+ break
229
+ i += 1
166
230
  body = [self.bind_node(child) for child in node.children]
167
231
  return FunctionDef(name, args, body)
232
+ def bind_when(self, node: GeoNode) -> OnRequest:
233
+ # 'when someone visits "/path"'
234
+ tokens = node.tokens
235
+ path = String('/')
236
+ for i, t in enumerate(tokens):
237
+ if t.type == 'STRING':
238
+ path = String(t.value)
239
+ break
240
+ body = [self.bind_node(child) for child in node.children]
241
+ return OnRequest(path, body)
242
+ def bind_on(self, node: GeoNode) -> OnRequest:
243
+ # 'on request to "/path"'
244
+ tokens = node.tokens
245
+ path = String('/')
246
+ for t in tokens:
247
+ if t.type == 'STRING':
248
+ path = String(t.value)
249
+ break
250
+ body = [self.bind_node(child) for child in node.children]
251
+ return OnRequest(path, body)
252
+ def bind_call_or_expr(self, node: GeoNode) -> Any:
253
+ # Handle function calls like 'Head "title"' or 'Navbar'
254
+ tokens = node.tokens
255
+ if len(tokens) >= 1 and tokens[0].type == 'ID':
256
+ name = tokens[0].value
257
+ args = []
258
+ kwargs = []
259
+ # Parse remaining tokens as arguments, handling key=value for HTML attributes
260
+ i = 1
261
+ while i < len(tokens):
262
+ t = tokens[i]
263
+ # Check for key=value pattern (e.g. class="container", style="...", href="...")
264
+ # HTML attributes may be tokenized as various types: ID, STRUCTURE, HREF, REL, NAME, etc.
265
+ is_attr_key = t.type in ('ID', 'STRUCTURE', 'HREF', 'REL', 'NAME', 'STYLE', 'CONTENT', 'CHARSET', 'SRC', 'ALT', 'TYPE', 'VALUE', 'PLACEHOLDER', 'METHOD', 'ACTION')
266
+ is_kwarg = (is_attr_key and i + 2 < len(tokens) and tokens[i + 1].type == 'ASSIGN')
267
+ if is_kwarg:
268
+ key = t.value
269
+ val_token = tokens[i + 2]
270
+ if val_token.type == 'STRING':
271
+ kwargs.append((key, String(val_token.value)))
272
+ elif val_token.type == 'NUMBER':
273
+ kwargs.append((key, Number(int(val_token.value) if '.' not in val_token.value else float(val_token.value))))
274
+ else:
275
+ kwargs.append((key, VarAccess(val_token.value)))
276
+ i += 3
277
+ continue
278
+ elif t.type == 'STRING':
279
+ args.append(String(t.value))
280
+ elif t.type == 'NUMBER':
281
+ args.append(Number(int(t.value) if '.' not in t.value else float(t.value)))
282
+ elif t.type == 'ID':
283
+ args.append(VarAccess(t.value))
284
+ i += 1
285
+ # Check for children (indented body)
286
+ body = None
287
+ if node.children:
288
+ body = [self.bind_node(child) for child in node.children]
289
+ return Call(name, args, kwargs=kwargs, body=body)
290
+ return self.parse_expr_iterative(tokens)
168
291
  def _extract_expr_tokens(self, tokens: List[Token], start: int = 0) -> List[Token]:
169
292
  end = len(tokens)
170
293
  if tokens[-1].type == 'COLON':
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: shell-lite
3
- Version: 0.5.3
3
+ Version: 0.5.3.2
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