shell-lite 0.6.3__tar.gz → 0.6.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 (46) hide show
  1. {shell_lite-0.6.3 → shell_lite-0.6.3.2}/PKG-INFO +1 -1
  2. {shell_lite-0.6.3 → shell_lite-0.6.3.2}/README.md +5 -5
  3. {shell_lite-0.6.3 → shell_lite-0.6.3.2}/pyproject.toml +1 -1
  4. {shell_lite-0.6.3 → shell_lite-0.6.3.2}/shell_lite/compiler/ast_compiler.py +6 -1
  5. {shell_lite-0.6.3 → shell_lite-0.6.3.2}/shell_lite/compiler/builtins.py +6 -6
  6. {shell_lite-0.6.3 → shell_lite-0.6.3.2}/shell_lite/compiler/linker.py +18 -1
  7. {shell_lite-0.6.3 → shell_lite-0.6.3.2}/shell_lite/compiler/runtime_lib.py +48 -5
  8. {shell_lite-0.6.3 → shell_lite-0.6.3.2}/shell_lite/compiler/semantic_analyzer.py +2 -2
  9. {shell_lite-0.6.3 → shell_lite-0.6.3.2}/shell_lite/interpreter.py +16 -4
  10. {shell_lite-0.6.3 → shell_lite-0.6.3.2}/shell_lite/lsp_server.py +1 -1
  11. {shell_lite-0.6.3 → shell_lite-0.6.3.2}/shell_lite/main.py +16 -8
  12. {shell_lite-0.6.3 → shell_lite-0.6.3.2}/shell_lite/parser.py +7 -3
  13. {shell_lite-0.6.3 → shell_lite-0.6.3.2}/shell_lite.egg-info/PKG-INFO +1 -1
  14. {shell_lite-0.6.3 → shell_lite-0.6.3.2}/LICENSE +0 -0
  15. {shell_lite-0.6.3 → shell_lite-0.6.3.2}/README_PYPI.md +0 -0
  16. {shell_lite-0.6.3 → shell_lite-0.6.3.2}/setup.cfg +0 -0
  17. {shell_lite-0.6.3 → shell_lite-0.6.3.2}/shell_lite/__init__.py +0 -0
  18. {shell_lite-0.6.3 → shell_lite-0.6.3.2}/shell_lite/__main__.py +0 -0
  19. {shell_lite-0.6.3 → shell_lite-0.6.3.2}/shell_lite/ast_nodes.py +0 -0
  20. {shell_lite-0.6.3 → shell_lite-0.6.3.2}/shell_lite/cli.py +0 -0
  21. {shell_lite-0.6.3 → shell_lite-0.6.3.2}/shell_lite/compiler/__init__.py +0 -0
  22. {shell_lite-0.6.3 → shell_lite-0.6.3.2}/shell_lite/compiler/base_visitor.py +0 -0
  23. {shell_lite-0.6.3 → shell_lite-0.6.3.2}/shell_lite/compiler/optimizer.py +0 -0
  24. {shell_lite-0.6.3 → shell_lite-0.6.3.2}/shell_lite/compiler/symbol_table.py +0 -0
  25. {shell_lite-0.6.3 → shell_lite-0.6.3.2}/shell_lite/lexer.py +0 -0
  26. {shell_lite-0.6.3 → shell_lite-0.6.3.2}/shell_lite/runtime_policy.py +0 -0
  27. {shell_lite-0.6.3 → shell_lite-0.6.3.2}/shell_lite/stdlib/automation.shl +0 -0
  28. {shell_lite-0.6.3 → shell_lite-0.6.3.2}/shell_lite/stdlib/db.shl +0 -0
  29. {shell_lite-0.6.3 → shell_lite-0.6.3.2}/shell_lite/stdlib/io.shl +0 -0
  30. {shell_lite-0.6.3 → shell_lite-0.6.3.2}/shell_lite/stdlib/json.shl +0 -0
  31. {shell_lite-0.6.3 → shell_lite-0.6.3.2}/shell_lite/stdlib/math.shl +0 -0
  32. {shell_lite-0.6.3 → shell_lite-0.6.3.2}/shell_lite/stdlib/net.shl +0 -0
  33. {shell_lite-0.6.3 → shell_lite-0.6.3.2}/shell_lite/stdlib/std.shl +0 -0
  34. {shell_lite-0.6.3 → shell_lite-0.6.3.2}/shell_lite/stdlib/web.shl +0 -0
  35. {shell_lite-0.6.3 → shell_lite-0.6.3.2}/shell_lite/test_runner.py +0 -0
  36. {shell_lite-0.6.3 → shell_lite-0.6.3.2}/shell_lite.egg-info/SOURCES.txt +0 -0
  37. {shell_lite-0.6.3 → shell_lite-0.6.3.2}/shell_lite.egg-info/dependency_links.txt +0 -0
  38. {shell_lite-0.6.3 → shell_lite-0.6.3.2}/shell_lite.egg-info/entry_points.txt +0 -0
  39. {shell_lite-0.6.3 → shell_lite-0.6.3.2}/shell_lite.egg-info/requires.txt +0 -0
  40. {shell_lite-0.6.3 → shell_lite-0.6.3.2}/shell_lite.egg-info/top_level.txt +0 -0
  41. {shell_lite-0.6.3 → shell_lite-0.6.3.2}/tests/test_interpreter.py +0 -0
  42. {shell_lite-0.6.3 → shell_lite-0.6.3.2}/tests/test_issue_imported_class.py +0 -0
  43. {shell_lite-0.6.3 → shell_lite-0.6.3.2}/tests/test_lexer.py +0 -0
  44. {shell_lite-0.6.3 → shell_lite-0.6.3.2}/tests/test_parser.py +0 -0
  45. {shell_lite-0.6.3 → shell_lite-0.6.3.2}/tests/test_safe_mode.py +0 -0
  46. {shell_lite-0.6.3 → shell_lite-0.6.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.6.3
3
+ Version: 0.6.3.2
4
4
  Summary: ShellLite is a programming language designed to be as readable as plain English. Code like you Think!
5
5
  Author-email: contact@shelllite.tech
6
6
  License: GPLv3 with classpath exception
@@ -65,14 +65,12 @@ while attempts < max_attempts
65
65
  say in red "\nGame Over! The number was " + str(secret)
66
66
  ```
67
67
 
68
- ## Compilation (WIP)
68
+ ## Compilation
69
69
 
70
- ShellLite can be compiled to various targets for deployment:
70
+ ShellLite can be compiled for deploymebt
71
71
 
72
72
  ```bash
73
- shl compile script.shl --target c # Transpile to C
74
- shl compile script.shl --target js # Transpile to JavaScript
75
- shl compile script.shl --target wasm # Build for WebAssembly
73
+ shl compile script.shl
76
74
  ```
77
75
 
78
76
  ## Research & Documentation
@@ -83,6 +81,8 @@ For a deep dive into the underlying parsing theory, please refer to the papers o
83
81
 
84
82
  Detailed language guides can be found in the `docs/` directory. (TO BE RE WRITTEN)
85
83
 
84
+ ### Official Discord:- [Discord](https://discord.gg/WgqrgvuGfs)
85
+
86
86
  ## License
87
87
 
88
88
  GNU GPL V3 With Class Exception License - See [LICENSE](LICENSE) for details.
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "shell-lite"
7
- version = "0.6.3"
7
+ version = "0.6.3.2"
8
8
  description = "ShellLite is a programming language designed to be as readable as plain English. Code like you Think!"
9
9
  readme = "README_PYPI.md"
10
10
  requires-python = ">=3.8"
@@ -242,7 +242,12 @@ class ASTCompiler(BaseVisitor):
242
242
  return ast.Compare(left=self.visit(node.args[1]), ops=[ast.In()], comparators=[self.visit(node.args[0])])
243
243
 
244
244
  args = [self.visit(a) for a in node.args]
245
- keywords = [ast.keyword(arg=k, value=self.visit(v)) for k, v in (node.kwargs or [])]
245
+ keywords = []
246
+ for k, v in (node.kwargs or []):
247
+ arg_name = k
248
+ if k in ("class", "for", "def", "if", "else", "while", "return", "import", "from", "try", "except", "lambda", "with", "as", "is", "in", "and", "or", "not", "pass", "raise", "yield", "async", "await", "break", "continue"):
249
+ arg_name = k + "_"
250
+ keywords.append(ast.keyword(arg=arg_name, value=self.visit(v)))
246
251
 
247
252
  if node.body:
248
253
  body_func_name = self.get_tmp() + "_body"
@@ -23,7 +23,7 @@ BUILTINS: Dict[str, BuiltinMapping] = {
23
23
  # Core IO
24
24
  "print": BuiltinMapping("print", 0, None, "print"),
25
25
  "say": BuiltinMapping("say", 0, None, "print"),
26
- "ask": BuiltinMapping("ask", 0, 1, "input"),
26
+ "ask": BuiltinMapping("ask", 0, 1, "shl_ask"),
27
27
  # Type Conversion
28
28
  "str": BuiltinMapping("str", 1, 1, "str"),
29
29
  "int": BuiltinMapping("int", 1, 1, "int"),
@@ -42,14 +42,14 @@ BUILTINS: Dict[str, BuiltinMapping] = {
42
42
  "upper": BuiltinMapping("upper", 1, 1, "str.upper"),
43
43
  "lower": BuiltinMapping("lower", 1, 1, "str.lower"),
44
44
  "sort": BuiltinMapping("sort", 1, 1, "sorted"),
45
- "count": BuiltinMapping("count", 1, 2),
46
- "split": BuiltinMapping("split", 1, 2),
45
+ "count": BuiltinMapping("count", 1, 2, "shl_count"),
46
+ "split": BuiltinMapping("split", 1, 2, "shl_split"),
47
47
  "add": BuiltinMapping("add", 2, 2),
48
48
  "remove": BuiltinMapping("remove", 2, 2),
49
49
  "pop": BuiltinMapping("pop", 1, 2),
50
- "contains": BuiltinMapping("contains", 2, 2),
51
- "empty": BuiltinMapping("empty", 1, 1),
52
- "xor": BuiltinMapping("xor", 2, 2),
50
+ "contains": BuiltinMapping("contains", 2, 2, "shl_contains"),
51
+ "empty": BuiltinMapping("empty", 1, 1, "shl_empty"),
52
+ "xor": BuiltinMapping("xor", 2, 2, "shl_xor"),
53
53
  # JSON
54
54
  "json_parse": BuiltinMapping("json_parse", 1, 1, "py_json.loads"),
55
55
  "json_stringify": BuiltinMapping("json_stringify", 1, 2, "py_json.dumps"),
@@ -57,7 +57,24 @@ class StaticLinker:
57
57
  continue
58
58
 
59
59
  mod_name = os.path.splitext(os.path.basename(fp))[0]
60
- current_module_ast.append(PythonImport(f".{mod_name}", alias))
60
+ try:
61
+ rel_dir = os.path.relpath(os.path.dirname(fp), os.path.dirname(abs_p))
62
+ if rel_dir == ".":
63
+ dots = "."
64
+ pkg_path = ""
65
+ elif rel_dir.startswith(".."):
66
+ parts = rel_dir.split(os.sep)
67
+ dots = "." * (parts.count("..") + 1)
68
+ remaining = [p for p in parts if p != ".."]
69
+ pkg_path = ".".join(remaining) + "." if remaining else ""
70
+ else:
71
+ dots = "."
72
+ pkg_path = rel_dir.replace(os.sep, ".") + "."
73
+ except ValueError:
74
+ dots = "."
75
+ pkg_path = ""
76
+
77
+ current_module_ast.append(PythonImport(f"{dots}{pkg_path}{mod_name}", alias))
61
78
 
62
79
  if fp not in self.module_asts:
63
80
  with open(fp, "r", encoding="utf-8") as f:
@@ -122,6 +122,37 @@ def channel_receive(q):
122
122
  return q.get()
123
123
 
124
124
 
125
+ def shl_split(s, sep=None):
126
+ if sep:
127
+ return str(s).split(str(sep))
128
+ return str(s).split()
129
+
130
+
131
+ def shl_count(obj, item=None):
132
+ if item is not None:
133
+ return obj.count(item)
134
+ return len(obj)
135
+
136
+
137
+ def shl_contains(obj, item):
138
+ return item in obj
139
+
140
+
141
+ def shl_empty(obj):
142
+ return len(obj) == 0
143
+
144
+
145
+ def shl_xor(a, b):
146
+ return a ^ b
147
+
148
+
149
+ def shl_ask(prompt=""):
150
+ try:
151
+ return builtins.input(prompt)
152
+ except EOFError:
153
+ return None
154
+
155
+
125
156
  def shl_execute(cmd_str):
126
157
  require_exec(str(cmd_str))
127
158
  args = shlex.split(str(cmd_str))
@@ -162,7 +193,7 @@ def render(obj):
162
193
 
163
194
  def __make_tag(tag_name):
164
195
  def tag_handler(*args, body=None, **kwargs):
165
- attrs = "".join(f' {k.replace("_", "-")}="{v}"' for k, v in kwargs.items())
196
+ attrs = "".join(f' {k.rstrip("_").replace("_", "-")}="{v}"' for k, v in kwargs.items())
166
197
  __emit_html(f"<{tag_name}{attrs}>")
167
198
  for a in args:
168
199
  __emit_html(str(a))
@@ -376,9 +407,10 @@ _static_routes: Dict[str, str] = {}
376
407
 
377
408
  class ShellLiteHTTPHandler(BaseHTTPRequestHandler):
378
409
  def do_GET(self):
410
+ clean_path = self.path.split("?")[0].split("#")[0]
379
411
  for pfx, fld in _static_routes.items():
380
- if self.path.startswith(pfx):
381
- rel_path = self.path[len(pfx) :].lstrip("/")
412
+ if clean_path.startswith(pfx):
413
+ rel_path = clean_path[len(pfx) :].lstrip("/")
382
414
  full_path = os.path.abspath(os.path.join(fld, rel_path))
383
415
  if not full_path.startswith(os.path.abspath(fld)):
384
416
  self.send_response(403)
@@ -395,9 +427,20 @@ class ShellLiteHTTPHandler(BaseHTTPRequestHandler):
395
427
  self.wfile.write(f.read())
396
428
  return
397
429
 
398
- if self.path in _web_handlers:
430
+ # Check dynamic routes
431
+ handler = _web_handlers.get(self.path)
432
+ if not handler:
433
+ # Try matching parameterized routes
434
+ for route, h in _web_handlers.items():
435
+ if ":" in route:
436
+ pattern = "^" + re.sub(r':[^/]+', r'([^/]+)', route) + "$"
437
+ if re.match(pattern, self.path):
438
+ handler = h
439
+ break
440
+
441
+ if handler:
399
442
  try:
400
- res = _web_handlers[self.path]()
443
+ res = handler()
401
444
  self.send_response(200)
402
445
  self.send_header("Content-type", "text/html")
403
446
  self.end_headers()
@@ -343,7 +343,7 @@ class SemanticAnalyzer(BaseTransformer):
343
343
  def visit_Assign(self, node: Assign) -> Type:
344
344
  vt = self.visit(node.value)
345
345
  existing = self.current_scope.resolve(node.name)
346
- if existing and existing.is_global and self.current_scope != self.global_scope:
346
+ if existing and (existing.is_global or existing.is_property) and self.current_scope != self.global_scope:
347
347
  node.symbol_ref = existing
348
348
  return vt
349
349
 
@@ -356,7 +356,7 @@ class SemanticAnalyzer(BaseTransformer):
356
356
  def visit_TypedAssign(self, node: TypedAssign) -> Type:
357
357
  vt = self.visit(node.value)
358
358
  existing = self.current_scope.resolve(node.name)
359
- if existing and existing.is_global and self.current_scope != self.global_scope:
359
+ if existing and (existing.is_global or existing.is_property) and self.current_scope != self.global_scope:
360
360
  node.symbol_ref = existing
361
361
  return vt
362
362
 
@@ -277,13 +277,16 @@ def make_jit_tag_fn(name, interpreter):
277
277
  attrs.update(arg)
278
278
  elif isinstance(arg, LambdaFunction):
279
279
  t = JITTag(name, attrs)
280
- if interpreter.web_builder:
280
+ is_top = not interpreter.web_builder
281
+ if not is_top:
281
282
  interpreter.web_builder[-1].add(t)
282
283
  interpreter.web_builder.append(t)
283
284
  try:
284
285
  arg()
285
286
  finally:
286
287
  interpreter.web_builder.pop()
288
+ if is_top:
289
+ rt.__emit_html(str(t))
287
290
  return t
288
291
  elif isinstance(arg, str) and "=" in arg and " " not in arg:
289
292
  k, v = arg.split("=", 1)
@@ -295,6 +298,8 @@ def make_jit_tag_fn(name, interpreter):
295
298
  t.add(c)
296
299
  if interpreter.web_builder:
297
300
  interpreter.web_builder[-1].add(t)
301
+ else:
302
+ rt.__emit_html(str(t))
298
303
  return t
299
304
 
300
305
  return fn
@@ -439,7 +444,12 @@ def std_csv_export(data, path):
439
444
 
440
445
 
441
446
  class Interpreter:
442
- def __init__(self):
447
+ def __init__(self, search_paths: Optional[List[str]] = None):
448
+ stdlib_path = os.path.join(os.path.dirname(__file__), "stdlib")
449
+ if search_paths is not None:
450
+ self.search_paths = search_paths + [stdlib_path]
451
+ else:
452
+ self.search_paths = [os.getcwd(), stdlib_path]
443
453
  self.policy = get_policy()
444
454
  self.safe_mode = self.policy.safe_mode
445
455
  self._thread_local = threading.local()
@@ -528,7 +538,7 @@ class Interpreter:
528
538
  "convert": rt.convert,
529
539
  "clear_dict": lambda d: d.clear(),
530
540
  }
531
- self.web_builder = []
541
+ self.web_builder: List[Any] = []
532
542
  for t in [
533
543
  "div",
534
544
  "p",
@@ -603,7 +613,7 @@ class Interpreter:
603
613
  path += ".shl"
604
614
 
605
615
  if search_paths is None:
606
- search_paths = [os.getcwd(), os.path.join(os.path.dirname(__file__), "stdlib")]
616
+ search_paths = getattr(self, "search_paths", [os.getcwd(), os.path.join(os.path.dirname(__file__), "stdlib")])
607
617
 
608
618
  for p in search_paths:
609
619
  full_path = os.path.join(p, path)
@@ -613,6 +623,8 @@ class Interpreter:
613
623
  raise FileNotFoundError(f"Module {path} not found")
614
624
 
615
625
  def _load_module_nodes(self, path: str, search_paths: Optional[List[str]] = None) -> List[Node]:
626
+ if search_paths is None:
627
+ search_paths = getattr(self, "search_paths", [os.getcwd(), os.path.join(os.path.dirname(__file__), "stdlib")])
616
628
  abs_path = self._resolve_module_path(path, search_paths)
617
629
  stdlib_root = os.path.abspath(os.path.join(os.path.dirname(__file__), "stdlib"))
618
630
  if abs_path.startswith(stdlib_root):
@@ -320,7 +320,7 @@ class LSPServer:
320
320
  "renameProvider": True,
321
321
  "referencesProvider": True,
322
322
  },
323
- "serverInfo": {"name": "ShellLite Enhanced LSP", "version": "0.6.3"},
323
+ "serverInfo": {"name": "ShellLite Enhanced LSP", "version": "0.6.3.2"},
324
324
  },
325
325
  )
326
326
  elif method == "textDocument/didOpen":
@@ -82,7 +82,7 @@ def run_repl():
82
82
  interpreter = Interpreter()
83
83
  print("\n" + "=" * 40)
84
84
  print("=" * 40)
85
- print("Version: v0.6.3")
85
+ print("Version: v0.6.3.2")
86
86
  print("Commands: Type 'exit' to quit, 'help' for examples.")
87
87
  print("Note: Terminal commands (like 'shl install') must be run outside the REPL.")
88
88
  try:
@@ -214,7 +214,7 @@ def install_globally():
214
214
  with open(rc, "a", encoding="utf-8") as f:
215
215
  f.write(f"\n# Added by ShellLite Installer\n{export_line}\n")
216
216
 
217
- print("\n[SUCCESS] ShellLite (v0.6.3) is installed!")
217
+ print("\n[SUCCESS] ShellLite (v0.6.3.2) is installed!")
218
218
  print(f"Location: {install_dir}")
219
219
  print("\nIMPORTANT STEP REQUIRED:")
220
220
  print("1. Close ALL open terminal windows.")
@@ -231,7 +231,7 @@ def search_package(query: Optional[str] = None):
231
231
  print("\n" + "=" * 50)
232
232
  print(f" Searching for: {query if query else 'all'}")
233
233
  print("=" * 50)
234
- print("Notice: The package registry is being rebuilt for v0.6.3")
234
+ print("Notice: The package registry is being rebuilt for v0.6.3.2")
235
235
  print("In the meantime, you can find packages at: https://github.com/topics/shell-lite")
236
236
  print("=" * 50 + "\n")
237
237
 
@@ -446,11 +446,19 @@ def install_package(package_name: str, branch: str = "main", _visited=None):
446
446
  print(f"Extraction failed for {package_name}: {e}")
447
447
 
448
448
 
449
- def compile_file(filename: str, target: str = "python"):
449
+ def compile_file(
450
+ filename: str,
451
+ target: str = "python",
452
+ project_name: Optional[str] = None,
453
+ project_dir: Optional[str] = None,
454
+ ):
450
455
  if not os.path.exists(filename):
451
456
  print(f"Error: File '{filename}' not found.")
452
457
  return
453
458
 
459
+ if project_dir is None:
460
+ project_dir = os.path.dirname(os.path.abspath(filename))
461
+
454
462
  if target.lower() == "llvm":
455
463
  try:
456
464
  from .llvm_backend.builder import build_llvm # type: ignore[import-untyped, import-not-found]
@@ -497,8 +505,8 @@ def compile_file(filename: str, target: str = "python"):
497
505
  compiler = ASTCompiler()
498
506
 
499
507
  # Determine output directory
500
- base_name = os.path.splitext(os.path.basename(filename))[0]
501
- out_dir = os.path.join(os.getcwd(), f"{base_name}_build")
508
+ base_name = project_name or os.path.splitext(os.path.basename(filename))[0]
509
+ out_dir = os.path.join(project_dir, f"{base_name}_build")
502
510
  if os.path.exists(out_dir):
503
511
  shutil.rmtree(out_dir)
504
512
  os.makedirs(out_dir)
@@ -640,7 +648,7 @@ def format_file(filename: str):
640
648
  if not os.path.exists(filename):
641
649
  print(f"Error: File '{filename}' not found.")
642
650
  return
643
- print("Notice: The formatting feature is being rebuilt for v0.6.3 and is temporarily unavailable.")
651
+ print("Notice: The formatting feature is being rebuilt for v0.6.3.2 and is temporarily unavailable.")
644
652
  return
645
653
 
646
654
 
@@ -768,7 +776,7 @@ def main():
768
776
  elif cmd == "help" or cmd == "--help" or cmd == "-h":
769
777
  show_help()
770
778
  elif cmd == "--version" or cmd == "-v":
771
- print("ShellLite v0.6.3")
779
+ print("ShellLite v0.6.3.2")
772
780
  elif cmd == "get":
773
781
  if len(sys.argv) > 2:
774
782
  package_name = sys.argv[2]
@@ -373,6 +373,12 @@ class Parser:
373
373
  ast_node.end_col = last_tok.column + len(str(last_tok.value))
374
374
  return ast_node
375
375
 
376
+ def collect_descendant_tokens(self, nodes: List[GeoNode], out: List[Token]):
377
+ for child in nodes:
378
+ out.extend(child.tokens)
379
+ if child.children:
380
+ self.collect_descendant_tokens(child.children, out)
381
+
376
382
  def bind_node(self, node: GeoNode) -> Optional[Node]:
377
383
  # Find first non-noise token for dispatch
378
384
  effective_head = node.head_token
@@ -805,9 +811,7 @@ class Parser:
805
811
 
806
812
  expr_tokens = tokens[assign_idx + 1 :]
807
813
  if node.children:
808
- for child in node.children:
809
- expr_tokens.extend(child.tokens)
810
- # print(f"DEBUG bind_assignment: expr_tokens={[t.value for t in expr_tokens]}")
814
+ self.collect_descendant_tokens(node.children, expr_tokens)
811
815
  value = self.parse_expr_iterative(expr_tokens, node.children)
812
816
  if value is None:
813
817
  raise SyntaxError(f"Missing value in assignment at line {node.line}")
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: shell-lite
3
- Version: 0.6.3
3
+ Version: 0.6.3.2
4
4
  Summary: ShellLite is a programming language designed to be as readable as plain English. Code like you Think!
5
5
  Author-email: contact@shelllite.tech
6
6
  License: GPLv3 with classpath exception
File without changes
File without changes
File without changes