shell-lite 0.4.3__py3-none-any.whl → 0.4.5__py3-none-any.whl

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.
shell_lite/interpreter.py CHANGED
@@ -3,6 +3,7 @@ from .ast_nodes import *
3
3
  from .lexer import Token, Lexer
4
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,8 +129,23 @@ 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()
134
+ self.global_env.set('str', str)
135
+ self.global_env.set('int', int)
136
+ self.global_env.set('float', float)
137
+ self.global_env.set('list', list)
138
+ self.global_env.set('len', len)
139
+ self.global_env.set('input', input)
140
+ self.global_env.set('range', range)
141
+
142
+ # English-like helpers
143
+ self.global_env.set('wait', time.sleep)
144
+ self.global_env.set('append', lambda l, x: l.append(x))
145
+ self.global_env.set('remove', lambda l, x: l.remove(x))
146
+ self.global_env.set('empty', lambda l: len(l) == 0)
147
+ self.global_env.set('contains', lambda l, x: x in l)
148
+
133
149
  self.current_env = self.global_env
134
150
  self.functions: Dict[str, FunctionDef] = {}
135
151
  self.classes: Dict[str, ClassDef] = {}
@@ -263,6 +279,12 @@ class Interpreter:
263
279
  def _builtin_push(self, lst, item):
264
280
  lst.append(item)
265
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)))
266
288
  def _init_std_modules(self):
267
289
  self.std_modules = {
268
290
  'math': {
@@ -755,7 +777,12 @@ class Interpreter:
755
777
  if node.path in self.std_modules:
756
778
  self.current_env.set(node.path, self.std_modules[node.path])
757
779
  return
780
+
781
+ # 1. Check File System (ShellLite modules)
758
782
  import os
783
+ import importlib
784
+ target_path = None
785
+
759
786
  if os.path.exists(node.path):
760
787
  target_path = node.path
761
788
  else:
@@ -768,32 +795,45 @@ class Interpreter:
768
795
  global_path_ext = global_path + ".shl"
769
796
  if os.path.exists(global_path_ext):
770
797
  target_path = global_path_ext
771
- else:
772
- 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
773
808
  else:
774
- raise FileNotFoundError(f"Could not find imported file: {node.path} (searched local and global modules)")
775
- if os.path.isdir(target_path):
776
- main_shl = os.path.join(target_path, "main.shl")
777
- pkg_shl = os.path.join(target_path, f"{os.path.basename(target_path)}.shl")
778
- if os.path.exists(main_shl):
779
- target_path = main_shl
780
- elif os.path.exists(pkg_shl):
781
- target_path = pkg_shl
782
- else:
783
- 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
784
828
  try:
785
- with open(target_path, 'r', encoding='utf-8') as f:
786
- code = f.read()
787
- except FileNotFoundError:
788
- raise FileNotFoundError(f"Could not find imported file: {node.path}")
789
- from .lexer import Lexer
790
- from .parser import Parser
791
- lexer = Lexer(code)
792
- tokens = lexer.tokenize()
793
- parser = Parser(tokens)
794
- statements = parser.parse()
795
- for stmt in statements:
796
- 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
+
797
837
  def _get_class_properties(self, class_def: ClassDef) -> List[tuple[str, Optional[Node]]]:
798
838
  if not hasattr(class_def, 'properties'): return []
799
839
  # Support both old string list and new tuple list for backward compat if needed, though we updated AST
@@ -1519,7 +1559,7 @@ class Interpreter:
1519
1559
  self.wfile.write(str(e).encode())
1520
1560
  except: pass
1521
1561
  server = HTTPServer(('0.0.0.0', port_val), ShellLiteHandler)
1522
- print(f"\n ShellLite Server v0.04.3 is running!")
1562
+ print(f"\n ShellLite Server v0.04.5 is running!")
1523
1563
  print(f" \u001b[1;36m➜\u001b[0m Local: \u001b[1;4;36mhttp://localhost:{port_val}/\u001b[0m\n")
1524
1564
  try: server.serve_forever()
1525
1565
  except KeyboardInterrupt:
@@ -1688,27 +1728,6 @@ class Interpreter:
1688
1728
  except FileNotFoundError:
1689
1729
  raise FileNotFoundError(f"File '{path}' not found.")
1690
1730
  raise RuntimeError(f"Read failed: {e}")
1691
- if __name__ == '__main__':
1692
- import sys
1693
- if len(sys.argv) < 2:
1694
- print("Usage: python -m src.interpreter <file.shl>")
1695
- sys.exit(1)
1696
- filename = sys.argv[1]
1697
- try:
1698
- with open(filename, 'r', encoding='utf-8') as f:
1699
- code = f.read()
1700
- lexer = Lexer(code)
1701
- tokens = lexer.tokenize()
1702
- parser = Parser(tokens)
1703
- ast = parser.parse()
1704
- interpreter = Interpreter()
1705
- for stmt in ast:
1706
- interpreter.visit(stmt)
1707
- except Exception as e:
1708
- print(f"Error: {e}")
1709
- import traceback
1710
- traceback.print_exc()
1711
-
1712
1731
  def _builtin_upper(self, s, only_letters=False):
1713
1732
  if not only_letters:
1714
1733
  return s.upper()
shell_lite/lexer.py CHANGED
@@ -25,8 +25,8 @@ class Lexer:
25
25
  continue
26
26
  indent_level = len(line) - len(line.lstrip())
27
27
  if stripped_line.startswith('#'):
28
- self.tokens.append(Token('COMMENT', stripped_line, self.line_number, indent_level + 1))
29
- self.tokens.append(Token('NEWLINE', '', self.line_number, len(line) + 1))
28
+ # self.tokens.append(Token('COMMENT', stripped_line, self.line_number, indent_level + 1))
29
+ # self.tokens.append(Token('NEWLINE', '', self.line_number, len(line) + 1))
30
30
  continue
31
31
  if indent_level > self.indent_stack[-1]:
32
32
  self.indent_stack.append(indent_level)
@@ -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',
@@ -182,7 +183,6 @@ class Lexer:
182
183
  'const': 'CONST',
183
184
  'and': 'AND', 'or': 'OR', 'not': 'NOT',
184
185
  'try': 'TRY', 'catch': 'CATCH', 'always': 'ALWAYS',
185
- 'error': 'ERROR',
186
186
  'use': 'USE', 'as': 'AS', 'share': 'SHARE',
187
187
  'execute': 'EXECUTE', 'run': 'EXECUTE',
188
188
  'alert': 'ALERT', 'prompt': 'PROMPT', 'confirm': 'CONFIRM',
@@ -225,14 +225,22 @@ class Lexer:
225
225
  'placeholder': 'PLACEHOLDER',
226
226
  'app': 'APP', 'title': 'ID', 'size': 'SIZE',
227
227
  'column': 'COLUMN', 'row': 'ROW',
228
- 'button': 'BUTTON', 'heading': 'HEADING', 'text': 'TEXT',
228
+ 'button': 'BUTTON', 'heading': 'HEADING',
229
229
  'sum': 'SUM', 'upper': 'UPPER', 'lower': 'LOWER',
230
- 'only': 'ONLY', 'letters': 'LETTERS',
231
- 'numbers': 'NUMBERS', 'digits': 'DIGITS',
232
- 'that': 'THAT', 'are': 'ARE', 'prime': 'PRIME',
233
230
  'increment': 'INCREMENT', 'decrement': 'DECREMENT',
234
231
  'multiply': 'MULTIPLY', 'divide': 'DIVIDE',
235
232
  'be': 'BE', 'by': 'BY',
233
+ 'plus': 'PLUS', 'minus': 'MINUS', 'divided': 'DIV',
234
+ 'greater': 'GREATER', 'less': 'LESS', 'equal': 'EQUAL',
235
+ 'define': 'DEFINE', 'function': 'FUNCTION',
236
+ 'contains': 'CONTAINS', 'empty': 'EMPTY',
237
+ 'remove': 'REMOVE',
238
+ 'than': 'THAN',
239
+ 'doing': 'DOING',
240
+ 'make': 'MAKE', 'be': 'BE',
241
+ 'as': 'AS', 'long': 'LONG',
242
+ 'otherwise': 'OTHERWISE',
243
+ 'ask': 'ASK',
236
244
  }
237
245
  token_type = keywords.get(value, 'ID')
238
246
  self.tokens.append(Token(token_type, value, self.line_number, current_col))
shell_lite/main.py CHANGED
@@ -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).")