shell-lite 0.5.2__tar.gz → 0.5.3.1__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.2/shell_lite.egg-info → shell_lite-0.5.3.1}/PKG-INFO +1 -1
  2. {shell_lite-0.5.2 → shell_lite-0.5.3.1}/pyproject.toml +1 -1
  3. {shell_lite-0.5.2 → shell_lite-0.5.3.1}/shell_lite/interpreter.py +4 -2
  4. {shell_lite-0.5.2 → shell_lite-0.5.3.1}/shell_lite/main.py +4 -4
  5. {shell_lite-0.5.2 → shell_lite-0.5.3.1}/shell_lite/parser_gbp.py +133 -10
  6. {shell_lite-0.5.2 → shell_lite-0.5.3.1/shell_lite.egg-info}/PKG-INFO +1 -1
  7. {shell_lite-0.5.2 → shell_lite-0.5.3.1}/LICENSE +0 -0
  8. {shell_lite-0.5.2 → shell_lite-0.5.3.1}/README.md +0 -0
  9. {shell_lite-0.5.2 → shell_lite-0.5.3.1}/setup.cfg +0 -0
  10. {shell_lite-0.5.2 → shell_lite-0.5.3.1}/setup.py +0 -0
  11. {shell_lite-0.5.2 → shell_lite-0.5.3.1}/shell_lite/__init__.py +0 -0
  12. {shell_lite-0.5.2 → shell_lite-0.5.3.1}/shell_lite/ast_nodes.py +0 -0
  13. {shell_lite-0.5.2 → shell_lite-0.5.3.1}/shell_lite/cli.py +0 -0
  14. {shell_lite-0.5.2 → shell_lite-0.5.3.1}/shell_lite/compiler.py +0 -0
  15. {shell_lite-0.5.2 → shell_lite-0.5.3.1}/shell_lite/js_compiler.py +0 -0
  16. {shell_lite-0.5.2 → shell_lite-0.5.3.1}/shell_lite/lexer.py +0 -0
  17. {shell_lite-0.5.2 → shell_lite-0.5.3.1}/shell_lite/llvm_backend/__init__.py +0 -0
  18. {shell_lite-0.5.2 → shell_lite-0.5.3.1}/shell_lite/llvm_backend/builder.py +0 -0
  19. {shell_lite-0.5.2 → shell_lite-0.5.3.1}/shell_lite/llvm_backend/codegen.py +0 -0
  20. {shell_lite-0.5.2 → shell_lite-0.5.3.1}/shell_lite/parser.py +0 -0
  21. {shell_lite-0.5.2 → shell_lite-0.5.3.1}/shell_lite/runtime.py +0 -0
  22. {shell_lite-0.5.2 → shell_lite-0.5.3.1}/shell_lite.egg-info/SOURCES.txt +0 -0
  23. {shell_lite-0.5.2 → shell_lite-0.5.3.1}/shell_lite.egg-info/dependency_links.txt +0 -0
  24. {shell_lite-0.5.2 → shell_lite-0.5.3.1}/shell_lite.egg-info/entry_points.txt +0 -0
  25. {shell_lite-0.5.2 → shell_lite-0.5.3.1}/shell_lite.egg-info/requires.txt +0 -0
  26. {shell_lite-0.5.2 → shell_lite-0.5.3.1}/shell_lite.egg-info/top_level.txt +0 -0
  27. {shell_lite-0.5.2 → shell_lite-0.5.3.1}/tests/__init__.py +0 -0
  28. {shell_lite-0.5.2 → shell_lite-0.5.3.1}/tests/benchmark_driver.py +0 -0
  29. {shell_lite-0.5.2 → shell_lite-0.5.3.1}/tests/compare_parsers.py +0 -0
  30. {shell_lite-0.5.2 → shell_lite-0.5.3.1}/tests/debug_jit.py +0 -0
  31. {shell_lite-0.5.2 → shell_lite-0.5.3.1}/tests/generate_actual_graph.py +0 -0
  32. {shell_lite-0.5.2 → shell_lite-0.5.3.1}/tests/generate_perf_graph.py +0 -0
  33. {shell_lite-0.5.2 → shell_lite-0.5.3.1}/tests/generate_runtime_graph.py +0 -0
  34. {shell_lite-0.5.2 → shell_lite-0.5.3.1}/tests/run_jit.py +0 -0
  35. {shell_lite-0.5.2 → shell_lite-0.5.3.1}/tests/test_gbp_standalone.py +0 -0
  36. {shell_lite-0.5.2 → shell_lite-0.5.3.1}/tests/test_interpreter.py +0 -0
  37. {shell_lite-0.5.2 → shell_lite-0.5.3.1}/tests/test_lexer.py +0 -0
  38. {shell_lite-0.5.2 → shell_lite-0.5.3.1}/tests/test_parser.py +0 -0
  39. {shell_lite-0.5.2 → shell_lite-0.5.3.1}/tests/test_phase1.py +0 -0
  40. {shell_lite-0.5.2 → shell_lite-0.5.3.1}/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.2
3
+ Version: 0.5.3.1
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.2"
7
+ version = "0.5.3.1"
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.2 is running!")
1507
+ print(f"\n ShellLite Server v0.5.3.1 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:
@@ -16,11 +16,11 @@ def execute_source(source: str, interpreter: Interpreter):
16
16
  try:
17
17
  lexer = Lexer(source)
18
18
  tokens = lexer.tokenize()
19
- if os.environ.get('USE_GBP') == '1':
19
+ if os.environ.get('USE_LEGACY_PARSER') == '1':
20
+ parser = Parser(tokens)
21
+ else:
20
22
  from .parser_gbp import GeometricBindingParser
21
23
  parser = GeometricBindingParser(tokens)
22
- else:
23
- parser = Parser(tokens)
24
24
  statements = parser.parse()
25
25
  for stmt in statements:
26
26
  interpreter.visit(stmt)
@@ -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.2) is installed!")
153
+ print(f"\n[SUCCESS] ShellLite (v0.5.3) 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).")
@@ -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.2
3
+ Version: 0.5.3.1
4
4
  Summary: A lightweight, English-like scripting language.
5
5
  Home-page: https://github.com/Shrey-N/ShellDesk
6
6
  Author: Shrey Naithani
File without changes
File without changes
File without changes
File without changes