shell-lite 0.4.3__tar.gz → 0.4.5__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 (34) hide show
  1. {shell_lite-0.4.3/shell_lite.egg-info → shell_lite-0.4.5}/PKG-INFO +1 -1
  2. {shell_lite-0.4.3 → shell_lite-0.4.5}/pyproject.toml +1 -1
  3. shell_lite-0.4.3/shell_lite/interpreter_final.py → shell_lite-0.4.5/shell_lite/interpreter.py +53 -28
  4. shell_lite-0.4.3/shell_lite/lexer_new.py → shell_lite-0.4.5/shell_lite/lexer.py +2 -1
  5. {shell_lite-0.4.3 → shell_lite-0.4.5}/shell_lite/main.py +8 -9
  6. shell_lite-0.4.3/shell_lite/parser_new.py → shell_lite-0.4.5/shell_lite/parser.py +18 -4
  7. {shell_lite-0.4.3 → shell_lite-0.4.5/shell_lite.egg-info}/PKG-INFO +1 -1
  8. {shell_lite-0.4.3 → shell_lite-0.4.5}/shell_lite.egg-info/SOURCES.txt +0 -10
  9. shell_lite-0.4.3/shell_lite/fix_nulls.py +0 -29
  10. shell_lite-0.4.3/shell_lite/formatter.py +0 -75
  11. shell_lite-0.4.3/shell_lite/interpreter.py +0 -1779
  12. shell_lite-0.4.3/shell_lite/interpreter_backup.py +0 -1781
  13. shell_lite-0.4.3/shell_lite/interpreter_new.py +0 -1773
  14. shell_lite-0.4.3/shell_lite/js_compiler.py +0 -220
  15. shell_lite-0.4.3/shell_lite/lexer.py +0 -245
  16. shell_lite-0.4.3/shell_lite/minimal_interpreter.py +0 -25
  17. shell_lite-0.4.3/shell_lite/parser.py +0 -2093
  18. shell_lite-0.4.3/shell_lite/patch_parser.py +0 -41
  19. {shell_lite-0.4.3 → shell_lite-0.4.5}/LICENSE +0 -0
  20. {shell_lite-0.4.3 → shell_lite-0.4.5}/README.md +0 -0
  21. {shell_lite-0.4.3 → shell_lite-0.4.5}/setup.cfg +0 -0
  22. {shell_lite-0.4.3 → shell_lite-0.4.5}/shell_lite/__init__.py +0 -0
  23. {shell_lite-0.4.3 → shell_lite-0.4.5}/shell_lite/ast_nodes.py +0 -0
  24. {shell_lite-0.4.3 → shell_lite-0.4.5}/shell_lite/cli.py +0 -0
  25. {shell_lite-0.4.3 → shell_lite-0.4.5}/shell_lite/compiler.py +0 -0
  26. {shell_lite-0.4.3 → shell_lite-0.4.5}/shell_lite/runtime.py +0 -0
  27. {shell_lite-0.4.3 → shell_lite-0.4.5}/shell_lite.egg-info/dependency_links.txt +0 -0
  28. {shell_lite-0.4.3 → shell_lite-0.4.5}/shell_lite.egg-info/entry_points.txt +0 -0
  29. {shell_lite-0.4.3 → shell_lite-0.4.5}/shell_lite.egg-info/requires.txt +0 -0
  30. {shell_lite-0.4.3 → shell_lite-0.4.5}/shell_lite.egg-info/top_level.txt +0 -0
  31. {shell_lite-0.4.3 → shell_lite-0.4.5}/tests/test_interpreter.py +0 -0
  32. {shell_lite-0.4.3 → shell_lite-0.4.5}/tests/test_lexer.py +0 -0
  33. {shell_lite-0.4.3 → shell_lite-0.4.5}/tests/test_parser.py +0 -0
  34. {shell_lite-0.4.3 → shell_lite-0.4.5}/tests/test_stdlib.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: shell-lite
3
- Version: 0.4.3
3
+ Version: 0.4.5
4
4
  Summary: A lightweight, English-like scripting language.
5
5
  Author-email: Shrey Naithani <contact@shelllite.tech>
6
6
  License: MIT
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "shell-lite"
7
- version = "0.04.3"
7
+ version = "0.04.5"
8
8
  description = "A lightweight, English-like scripting language."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.8"
@@ -1,8 +1,9 @@
1
1
  from typing import Any, Dict, List, Callable
2
2
  from .ast_nodes import *
3
- from .lexer_new import Token, Lexer
4
- from .parser_new import Parser
3
+ from .lexer import Token, Lexer
4
+ from .parser import Parser
5
5
  import importlib
6
+ import types
6
7
  import operator
7
8
  import re
8
9
  import os
@@ -128,7 +129,7 @@ class WebBuilder:
128
129
  pass
129
130
  class Interpreter:
130
131
  def __init__(self):
131
- print('DEBUG: VERSION 2 LOADED')
132
+ # print('DEBUG: ShellLite v0.04.5')
132
133
  self.global_env = Environment()
133
134
  self.global_env.set('str', str)
134
135
  self.global_env.set('int', int)
@@ -278,6 +279,12 @@ class Interpreter:
278
279
  def _builtin_push(self, lst, item):
279
280
  lst.append(item)
280
281
  return None
282
+ def _builtin_upper(self, s):
283
+ return str(s).upper()
284
+ def _builtin_sum_range(self, start, end):
285
+ return sum(range(int(start), int(end)))
286
+ def _builtin_range_list(self, start, end):
287
+ return list(range(int(start), int(end)))
281
288
  def _init_std_modules(self):
282
289
  self.std_modules = {
283
290
  'math': {
@@ -770,7 +777,12 @@ class Interpreter:
770
777
  if node.path in self.std_modules:
771
778
  self.current_env.set(node.path, self.std_modules[node.path])
772
779
  return
780
+
781
+ # 1. Check File System (ShellLite modules)
773
782
  import os
783
+ import importlib
784
+ target_path = None
785
+
774
786
  if os.path.exists(node.path):
775
787
  target_path = node.path
776
788
  else:
@@ -783,32 +795,45 @@ class Interpreter:
783
795
  global_path_ext = global_path + ".shl"
784
796
  if os.path.exists(global_path_ext):
785
797
  target_path = global_path_ext
786
- else:
787
- raise FileNotFoundError(f"Could not find imported file: {node.path} (searched local and global modules)")
798
+
799
+ # 2. If found on FS, load as ShellLite
800
+ if target_path:
801
+ if os.path.isdir(target_path):
802
+ main_shl = os.path.join(target_path, "main.shl")
803
+ pkg_shl = os.path.join(target_path, f"{os.path.basename(target_path)}.shl")
804
+ if os.path.exists(main_shl):
805
+ target_path = main_shl
806
+ elif os.path.exists(pkg_shl):
807
+ target_path = pkg_shl
788
808
  else:
789
- raise FileNotFoundError(f"Could not find imported file: {node.path} (searched local and global modules)")
790
- if os.path.isdir(target_path):
791
- main_shl = os.path.join(target_path, "main.shl")
792
- pkg_shl = os.path.join(target_path, f"{os.path.basename(target_path)}.shl")
793
- if os.path.exists(main_shl):
794
- target_path = main_shl
795
- elif os.path.exists(pkg_shl):
796
- target_path = pkg_shl
797
- else:
798
- raise FileNotFoundError(f"Package '{node.path}' is a folder but has no 'main.shl' or '{os.path.basename(target_path)}.shl'.")
809
+ raise FileNotFoundError(f"Package '{node.path}' is a folder but has no 'main.shl' or '{os.path.basename(target_path)}.shl'.")
810
+
811
+ try:
812
+ with open(target_path, 'r', encoding='utf-8') as f:
813
+ code = f.read()
814
+ except FileNotFoundError:
815
+ raise FileNotFoundError(f"Could not find imported file: {node.path}")
816
+
817
+ from .lexer import Lexer
818
+ from .parser import Parser
819
+ lexer = Lexer(code)
820
+ tokens = lexer.tokenize()
821
+ parser = Parser(tokens)
822
+ statements = parser.parse()
823
+ for stmt in statements:
824
+ self.visit(stmt)
825
+ return
826
+
827
+ # 3. BRIDGE: Try importing as a raw Python module
799
828
  try:
800
- with open(target_path, 'r', encoding='utf-8') as f:
801
- code = f.read()
802
- except FileNotFoundError:
803
- raise FileNotFoundError(f"Could not find imported file: {node.path}")
804
- from .lexer import Lexer
805
- from .parser import Parser
806
- lexer = Lexer(code)
807
- tokens = lexer.tokenize()
808
- parser = Parser(tokens)
809
- statements = parser.parse()
810
- for stmt in statements:
811
- self.visit(stmt)
829
+ py_module = importlib.import_module(node.path)
830
+ self.current_env.set(node.path, py_module)
831
+ return
832
+ except ImportError:
833
+ pass # Fall through to error
834
+
835
+ raise FileNotFoundError(f"Could not find module '{node.path}'. Searched:\n - ShellLite Local/Global\n - Python Site-Packages (The Bridge)")
836
+
812
837
  def _get_class_properties(self, class_def: ClassDef) -> List[tuple[str, Optional[Node]]]:
813
838
  if not hasattr(class_def, 'properties'): return []
814
839
  # Support both old string list and new tuple list for backward compat if needed, though we updated AST
@@ -1534,7 +1559,7 @@ class Interpreter:
1534
1559
  self.wfile.write(str(e).encode())
1535
1560
  except: pass
1536
1561
  server = HTTPServer(('0.0.0.0', port_val), ShellLiteHandler)
1537
- print(f"\n ShellLite Server v0.04.3 is running!")
1562
+ print(f"\n ShellLite Server v0.04.5 is running!")
1538
1563
  print(f" \u001b[1;36m➜\u001b[0m Local: \u001b[1;4;36mhttp://localhost:{port_val}/\u001b[0m\n")
1539
1564
  try: server.serve_forever()
1540
1565
  except KeyboardInterrupt:
@@ -165,7 +165,8 @@ class Lexer:
165
165
  'while': 'WHILE', 'until': 'UNTIL',
166
166
  'repeat': 'REPEAT', 'forever': 'FOREVER',
167
167
  'stop': 'STOP', 'skip': 'SKIP', 'exit': 'EXIT',
168
- 'each': 'FOR',
168
+ 'each': 'EACH',
169
+ 'check': 'CHECK',
169
170
  'unless': 'UNLESS', 'when': 'WHEN', 'otherwise': 'OTHERWISE',
170
171
  'then': 'THEN', 'do': 'DO',
171
172
  'print': 'PRINT', 'say': 'SAY', 'show': 'SAY',
@@ -5,9 +5,9 @@ import urllib.request
5
5
  import zipfile
6
6
  import io
7
7
  import subprocess
8
- from .lexer_new import Lexer
9
- from .parser_new import Parser
10
- from .interpreter_new import Interpreter
8
+ from .lexer import Lexer
9
+ from .parser import Parser
10
+ from .interpreter import Interpreter
11
11
  from .ast_nodes import *
12
12
  import json
13
13
  def execute_source(source: str, interpreter: Interpreter):
@@ -58,10 +58,9 @@ def run_file(filename: str):
58
58
  print(f"Error: File '{filename}' not found.")
59
59
  return
60
60
  import sys
61
- if 'shell_lite.interpreter' in sys.modules:
62
- print(f"DEBUG: shell_lite.interpreter file: {sys.modules['shell_lite.interpreter'].__file__}")
63
- from .interpreter_final import Interpreter
64
- print(f"DEBUG: Interpreter class: {Interpreter}")
61
+
62
+ # Debug prints removed for production
63
+ from .interpreter import Interpreter
65
64
 
66
65
  with open(filename, 'r', encoding='utf-8') as f:
67
66
  source = f.read()
@@ -72,7 +71,7 @@ def run_repl():
72
71
  print("\n" + "="*40)
73
72
  print(" ShellLite REPL - English Syntax")
74
73
  print("="*40)
75
- print("Version: v0.04.3 | Made by Shrey Naithani")
74
+ print("Version: v0.04.5 | Made by Shrey Naithani")
76
75
  print("Commands: Type 'exit' to quit, 'help' for examples.")
77
76
  print("Note: Terminal commands (like 'shl install') must be run in CMD/PowerShell, not here.")
78
77
 
@@ -203,7 +202,7 @@ def install_globally():
203
202
  ps_cmd = f'$oldPath = [Environment]::GetEnvironmentVariable("Path", "User"); if ($oldPath -notlike "*ShellLite*") {{ [Environment]::SetEnvironmentVariable("Path", "$oldPath;{install_dir}", "User") }}'
204
203
  subprocess.run(["powershell", "-Command", ps_cmd], capture_output=True)
205
204
 
206
- print(f"\n[SUCCESS] ShellLite (v0.04.3) is installed!")
205
+ print(f"\n[SUCCESS] ShellLite (v0.04.5) is installed!")
207
206
  print(f"Location: {install_dir}")
208
207
  print("\nIMPORTANT STEP REQUIRED:")
209
208
  print("1. Close ALL open terminal windows (CMD, PowerShell, VS Code).")
@@ -1,5 +1,5 @@
1
1
  from typing import List, Optional
2
- from .lexer_new import Token, Lexer
2
+ from .lexer import Token, Lexer
3
3
  from .ast_nodes import *
4
4
  import re
5
5
  class Parser:
@@ -80,10 +80,20 @@ class Parser:
80
80
  return self.parse_make()
81
81
  elif self.check('INPUT'):
82
82
  next_t = self.peek(1)
83
- if next_t.type in ('ID', 'TYPE', 'STRING', 'NAME', 'VALUE', 'CLASS', 'STYLE', 'ONCLICK', 'SRC', 'HREF', 'ACTION', 'METHOD'):
83
+ if next_t.type in ('ID', 'TYPE', 'STRING', 'NAME', 'VALUE', 'CLASS', 'STYLE', 'ONCLICK', 'SRC', 'HREF', 'ACTION', 'METHOD', 'PLACEHOLDER'):
84
84
  input_token = self.consume()
85
85
  return self.parse_id_start_statement(passed_name_token=input_token)
86
86
  return self.parse_expression_stmt()
87
+ elif self.check('BUTTON'):
88
+ return self.parse_id_start_statement(passed_name_token=self.consume('BUTTON'))
89
+ elif self.check('COLUMN'):
90
+ return self.parse_id_start_statement(passed_name_token=self.consume('COLUMN'))
91
+ elif self.check('ROW'):
92
+ return self.parse_id_start_statement(passed_name_token=self.consume('ROW'))
93
+ elif self.check('IMAGE'):
94
+ return self.parse_id_start_statement(passed_name_token=self.consume('IMAGE'))
95
+ elif self.check('SIZE'):
96
+ return self.parse_id_start_statement(passed_name_token=self.consume('SIZE'))
87
97
  elif self.check('ID'):
88
98
  return self.parse_id_start_statement()
89
99
  elif self.check('SPAWN'):
@@ -173,6 +183,9 @@ class Parser:
173
183
  # Standalone ask statement? e.g. ask "Questions?"
174
184
  # Or ask is expression. If statement, maybe just expression statement.
175
185
  return self.parse_expression_statement()
186
+ elif self.check('CHECK'):
187
+ self.consume('CHECK')
188
+ return self.parse_if()
176
189
  elif self.check('SET'):
177
190
  return self.parse_set()
178
191
  else:
@@ -940,8 +953,8 @@ class Parser:
940
953
  return node
941
954
  def parse_start_server(self) -> Node:
942
955
  token = self.consume('START')
943
- if self.check('SERVER'):
944
- self.consume('SERVER')
956
+ if self.check('SERVER') or self.check('WEBSITE') or (self.check('ID') and self.peek().value == 'website'):
957
+ self.consume()
945
958
  port = Number(8080)
946
959
  if self.check('ON'):
947
960
  self.consume('ON')
@@ -1667,6 +1680,7 @@ class Parser:
1667
1680
  node.line = start_token.line
1668
1681
  return node
1669
1682
  start_token = self.consume('FOR')
1683
+ if self.check('EACH'): self.consume('EACH')
1670
1684
  if self.check('ID') and self.peek(1).type == 'IN':
1671
1685
  var_name = self.consume('ID').value
1672
1686
  self.consume('IN')
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: shell-lite
3
- Version: 0.4.3
3
+ Version: 0.4.5
4
4
  Summary: A lightweight, English-like scripting language.
5
5
  Author-email: Shrey Naithani <contact@shelllite.tech>
6
6
  License: MIT
@@ -5,20 +5,10 @@ shell_lite/__init__.py
5
5
  shell_lite/ast_nodes.py
6
6
  shell_lite/cli.py
7
7
  shell_lite/compiler.py
8
- shell_lite/fix_nulls.py
9
- shell_lite/formatter.py
10
8
  shell_lite/interpreter.py
11
- shell_lite/interpreter_backup.py
12
- shell_lite/interpreter_final.py
13
- shell_lite/interpreter_new.py
14
- shell_lite/js_compiler.py
15
9
  shell_lite/lexer.py
16
- shell_lite/lexer_new.py
17
10
  shell_lite/main.py
18
- shell_lite/minimal_interpreter.py
19
11
  shell_lite/parser.py
20
- shell_lite/parser_new.py
21
- shell_lite/patch_parser.py
22
12
  shell_lite/runtime.py
23
13
  shell_lite.egg-info/PKG-INFO
24
14
  shell_lite.egg-info/SOURCES.txt
@@ -1,29 +0,0 @@
1
-
2
- import sys
3
- import glob
4
- import os
5
-
6
- files = [
7
- r'c:\Users\shrey\OneDrive\Desktop\oka\shell-lite\shell_lite\parser.py',
8
- r'c:\Users\shrey\OneDrive\Desktop\oka\shell-lite\shell_lite\lexer.py',
9
- r'c:\Users\shrey\OneDrive\Desktop\oka\shell-lite\shell_lite\interpreter.py',
10
- r'c:\Users\shrey\OneDrive\Desktop\oka\shell-lite\shell_lite\main.py',
11
- r'c:\Users\shrey\OneDrive\Desktop\oka\shell-lite\shell_lite\ast_nodes.py',
12
- r'c:\Users\shrey\OneDrive\Desktop\oka\tests_suite\repro_issues.shl'
13
- ]
14
-
15
- for path in files:
16
- try:
17
- with open(path, 'rb') as f:
18
- content = f.read()
19
-
20
- if b'\x00' in content:
21
- print(f"Null bytes found in {path}! Fixing...")
22
- new_content = content.replace(b'\x00', b'')
23
- with open(path, 'wb') as f:
24
- f.write(new_content)
25
- print(f"Fixed {path}.")
26
- else:
27
- print(f"No null bytes in {path}.")
28
- except Exception as e:
29
- print(f"Error checking {path}: {e}")
@@ -1,75 +0,0 @@
1
- from typing import List
2
- from .lexer import Lexer, Token
3
- class Formatter:
4
- def __init__(self, source_code: str):
5
- self.source_code = source_code
6
- self.indent_size = 4
7
- def format(self) -> str:
8
- lexer = Lexer(self.source_code)
9
- try:
10
- tokens = lexer.tokenize()
11
- except Exception:
12
- raise
13
- formatted_lines = []
14
- current_indent = 0
15
- current_line_tokens: List[Token] = []
16
- def flush_line():
17
- nonlocal current_line_tokens
18
- if not current_line_tokens:
19
- pass
20
- line_str = self._format_line_tokens(current_line_tokens, current_indent)
21
- formatted_lines.append(line_str)
22
- current_line_tokens.clear()
23
- for token in tokens:
24
- if token.type == 'EOF':
25
- if current_line_tokens:
26
- flush_line()
27
- break
28
- elif token.type == 'INDENT':
29
- current_indent += 1
30
- elif token.type == 'DEDENT':
31
- current_indent -= 1
32
- if current_indent < 0: current_indent = 0
33
- elif token.type == 'NEWLINE':
34
- flush_line()
35
- pass
36
- else:
37
- current_line_tokens.append(token)
38
- return '\n'.join(formatted_lines)
39
- def _format_line_tokens(self, tokens: List[Token], indent_level: int) -> str:
40
- if not tokens:
41
- return ''
42
- line_parts = []
43
- line_parts.append(' ' * (indent_level * self.indent_size))
44
- for i, token in enumerate(tokens):
45
- val = token.value
46
- type = token.type
47
- if type == 'STRING':
48
- if '"' in val and "'" not in val:
49
- val = f"'{val}'"
50
- else:
51
- val = val.replace('"', '\\"')
52
- val = f'"{val}"'
53
- elif type == 'REGEX':
54
- val = f"/{val}/"
55
- if i > 0:
56
- prev = tokens[i-1]
57
- need_space = True
58
- if prev.type in ('LPAREN', 'LBRACKET', 'LBRACE', 'DOT', 'AT'):
59
- need_space = False
60
- if type in ('RPAREN', 'RBRACKET', 'RBRACE', 'DOT', 'COMMA', 'COLON'):
61
- need_space = False
62
- if type == 'LPAREN':
63
- if prev.type == 'ID':
64
- need_space = False
65
- elif prev.type in ('RPAREN', 'RBRACKET', 'STRING'):
66
- need_space = False
67
- else:
68
- pass
69
- if type == 'LBRACKET':
70
- if prev.type in ('ID', 'STRING', 'RPAREN', 'RBRACKET'):
71
- need_space = False
72
- if need_space:
73
- line_parts.append(' ')
74
- line_parts.append(val)
75
- return "".join(line_parts).rstrip()