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 +66 -47
- shell_lite/lexer.py +16 -8
- shell_lite/main.py +8 -9
- shell_lite/parser.py +264 -114
- {shell_lite-0.4.3.dist-info → shell_lite-0.4.5.dist-info}/METADATA +1 -1
- shell_lite-0.4.5.dist-info/RECORD +15 -0
- shell_lite/fix_nulls.py +0 -29
- shell_lite/formatter.py +0 -75
- shell_lite/interpreter_backup.py +0 -1781
- shell_lite/interpreter_final.py +0 -1773
- shell_lite/interpreter_new.py +0 -1773
- shell_lite/js_compiler.py +0 -220
- shell_lite/lexer_new.py +0 -252
- shell_lite/minimal_interpreter.py +0 -25
- shell_lite/parser_new.py +0 -2229
- shell_lite/patch_parser.py +0 -41
- shell_lite-0.4.3.dist-info/RECORD +0 -25
- {shell_lite-0.4.3.dist-info → shell_lite-0.4.5.dist-info}/LICENSE +0 -0
- {shell_lite-0.4.3.dist-info → shell_lite-0.4.5.dist-info}/WHEEL +0 -0
- {shell_lite-0.4.3.dist-info → shell_lite-0.4.5.dist-info}/entry_points.txt +0 -0
- {shell_lite-0.4.3.dist-info → shell_lite-0.4.5.dist-info}/top_level.txt +0 -0
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:
|
|
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
|
-
|
|
772
|
-
|
|
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
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
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
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
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.
|
|
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': '
|
|
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',
|
|
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 .
|
|
9
|
-
from .
|
|
10
|
-
from .
|
|
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
|
-
|
|
62
|
-
|
|
63
|
-
from .
|
|
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.
|
|
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.
|
|
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).")
|