shell-lite 0.4.2__tar.gz → 0.4.4__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.
- {shell_lite-0.4.2/shell_lite.egg-info → shell_lite-0.4.4}/PKG-INFO +1 -1
- {shell_lite-0.4.2 → shell_lite-0.4.4}/pyproject.toml +1 -1
- {shell_lite-0.4.2 → shell_lite-0.4.4}/shell_lite/interpreter.py +94 -25
- {shell_lite-0.4.2 → shell_lite-0.4.4}/shell_lite/lexer.py +22 -7
- {shell_lite-0.4.2 → shell_lite-0.4.4}/shell_lite/main.py +13 -2
- {shell_lite-0.4.2 → shell_lite-0.4.4}/shell_lite/parser.py +467 -40
- {shell_lite-0.4.2 → shell_lite-0.4.4/shell_lite.egg-info}/PKG-INFO +1 -1
- {shell_lite-0.4.2 → shell_lite-0.4.4}/shell_lite.egg-info/SOURCES.txt +0 -2
- shell_lite-0.4.2/shell_lite/formatter.py +0 -75
- shell_lite-0.4.2/shell_lite/js_compiler.py +0 -220
- {shell_lite-0.4.2 → shell_lite-0.4.4}/LICENSE +0 -0
- {shell_lite-0.4.2 → shell_lite-0.4.4}/README.md +0 -0
- {shell_lite-0.4.2 → shell_lite-0.4.4}/setup.cfg +0 -0
- {shell_lite-0.4.2 → shell_lite-0.4.4}/shell_lite/__init__.py +0 -0
- {shell_lite-0.4.2 → shell_lite-0.4.4}/shell_lite/ast_nodes.py +0 -0
- {shell_lite-0.4.2 → shell_lite-0.4.4}/shell_lite/cli.py +0 -0
- {shell_lite-0.4.2 → shell_lite-0.4.4}/shell_lite/compiler.py +0 -0
- {shell_lite-0.4.2 → shell_lite-0.4.4}/shell_lite/runtime.py +0 -0
- {shell_lite-0.4.2 → shell_lite-0.4.4}/shell_lite.egg-info/dependency_links.txt +0 -0
- {shell_lite-0.4.2 → shell_lite-0.4.4}/shell_lite.egg-info/entry_points.txt +0 -0
- {shell_lite-0.4.2 → shell_lite-0.4.4}/shell_lite.egg-info/requires.txt +0 -0
- {shell_lite-0.4.2 → shell_lite-0.4.4}/shell_lite.egg-info/top_level.txt +0 -0
- {shell_lite-0.4.2 → shell_lite-0.4.4}/tests/test_interpreter.py +0 -0
- {shell_lite-0.4.2 → shell_lite-0.4.4}/tests/test_lexer.py +0 -0
- {shell_lite-0.4.2 → shell_lite-0.4.4}/tests/test_parser.py +0 -0
- {shell_lite-0.4.2 → shell_lite-0.4.4}/tests/test_stdlib.py +0 -0
|
@@ -128,7 +128,23 @@ class WebBuilder:
|
|
|
128
128
|
pass
|
|
129
129
|
class Interpreter:
|
|
130
130
|
def __init__(self):
|
|
131
|
+
print('DEBUG: ShellLite v0.04.4')
|
|
131
132
|
self.global_env = Environment()
|
|
133
|
+
self.global_env.set('str', str)
|
|
134
|
+
self.global_env.set('int', int)
|
|
135
|
+
self.global_env.set('float', float)
|
|
136
|
+
self.global_env.set('list', list)
|
|
137
|
+
self.global_env.set('len', len)
|
|
138
|
+
self.global_env.set('input', input)
|
|
139
|
+
self.global_env.set('range', range)
|
|
140
|
+
|
|
141
|
+
# English-like helpers
|
|
142
|
+
self.global_env.set('wait', time.sleep)
|
|
143
|
+
self.global_env.set('append', lambda l, x: l.append(x))
|
|
144
|
+
self.global_env.set('remove', lambda l, x: l.remove(x))
|
|
145
|
+
self.global_env.set('empty', lambda l: len(l) == 0)
|
|
146
|
+
self.global_env.set('contains', lambda l, x: x in l)
|
|
147
|
+
|
|
132
148
|
self.current_env = self.global_env
|
|
133
149
|
self.functions: Dict[str, FunctionDef] = {}
|
|
134
150
|
self.classes: Dict[str, ClassDef] = {}
|
|
@@ -153,11 +169,13 @@ class Interpreter:
|
|
|
153
169
|
'split': lambda s, d=" ": s.split(d),
|
|
154
170
|
'join': lambda lst, d="": d.join(str(x) for x in lst),
|
|
155
171
|
'replace': lambda s, old, new: s.replace(old, new),
|
|
156
|
-
'upper':
|
|
172
|
+
'upper': self._builtin_upper,
|
|
157
173
|
'lower': lambda s: s.lower(),
|
|
158
174
|
'trim': lambda s: s.strip(),
|
|
159
175
|
'startswith': lambda s, p: s.startswith(p),
|
|
160
176
|
'endswith': lambda s, p: s.endswith(p),
|
|
177
|
+
'sum_range': self._builtin_sum_range,
|
|
178
|
+
'range_list': self._builtin_range_list,
|
|
161
179
|
'find': lambda s, sub: s.find(sub),
|
|
162
180
|
'char': chr, 'ord': ord,
|
|
163
181
|
'append': lambda l, x: (l.append(x), l)[1],
|
|
@@ -172,9 +190,6 @@ class Interpreter:
|
|
|
172
190
|
'slice': lambda l, start, end=None: l[start:end],
|
|
173
191
|
'contains': lambda l, x: x in l,
|
|
174
192
|
'index': lambda l, x: l.index(x) if x in l else -1,
|
|
175
|
-
'map': self._builtin_map,
|
|
176
|
-
'filter': self._builtin_filter,
|
|
177
|
-
'reduce': self._builtin_reduce,
|
|
178
193
|
'exists': os.path.exists,
|
|
179
194
|
'delete': os.remove,
|
|
180
195
|
'copy': shutil.copy,
|
|
@@ -263,6 +278,12 @@ class Interpreter:
|
|
|
263
278
|
def _builtin_push(self, lst, item):
|
|
264
279
|
lst.append(item)
|
|
265
280
|
return None
|
|
281
|
+
def _builtin_upper(self, s):
|
|
282
|
+
return str(s).upper()
|
|
283
|
+
def _builtin_sum_range(self, start, end):
|
|
284
|
+
return sum(range(int(start), int(end)))
|
|
285
|
+
def _builtin_range_list(self, start, end):
|
|
286
|
+
return list(range(int(start), int(end)))
|
|
266
287
|
def _init_std_modules(self):
|
|
267
288
|
self.std_modules = {
|
|
268
289
|
'math': {
|
|
@@ -1519,7 +1540,7 @@ class Interpreter:
|
|
|
1519
1540
|
self.wfile.write(str(e).encode())
|
|
1520
1541
|
except: pass
|
|
1521
1542
|
server = HTTPServer(('0.0.0.0', port_val), ShellLiteHandler)
|
|
1522
|
-
print(f"\n ShellLite Server v0.04.
|
|
1543
|
+
print(f"\n ShellLite Server v0.04.4 is running!")
|
|
1523
1544
|
print(f" \u001b[1;36m➜\u001b[0m Local: \u001b[1;4;36mhttp://localhost:{port_val}/\u001b[0m\n")
|
|
1524
1545
|
try: server.serve_forever()
|
|
1525
1546
|
except KeyboardInterrupt:
|
|
@@ -1688,23 +1709,71 @@ class Interpreter:
|
|
|
1688
1709
|
except FileNotFoundError:
|
|
1689
1710
|
raise FileNotFoundError(f"File '{path}' not found.")
|
|
1690
1711
|
raise RuntimeError(f"Read failed: {e}")
|
|
1691
|
-
|
|
1692
|
-
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
|
|
1712
|
+
def _builtin_upper(self, s, only_letters=False):
|
|
1713
|
+
if not only_letters:
|
|
1714
|
+
return s.upper()
|
|
1715
|
+
# "UPPER words ONLY LETTERS" -> Uppercase normal letters, leave others?
|
|
1716
|
+
# Or maybe it means "Only extract uppercase letters"?
|
|
1717
|
+
# User output shows: "HELLO WORLD 123" -> "HELLO WORLD 123" (normal)
|
|
1718
|
+
# Wait, user screenshot says:
|
|
1719
|
+
# text = "hello world 123"
|
|
1720
|
+
# words = split text
|
|
1721
|
+
# say upper words only letters
|
|
1722
|
+
# Error: Variable 'only' is not defined.
|
|
1723
|
+
# So maybe they want to UPPERCASE ONLY LETTERS? digits remain same? .upper() does that.
|
|
1724
|
+
# But maybe they mean "remove non-letters"?
|
|
1725
|
+
# "upper words only letters" -> "HELLO WORLD".
|
|
1726
|
+
# If "only letters" means filter?
|
|
1727
|
+
# Let's assume it means "uppercase only the letters" which is standard behavior?
|
|
1728
|
+
# Or maybe "uppercase, and keep only letters".
|
|
1729
|
+
# Let's look at user intent. "upper words only letters".
|
|
1730
|
+
# Likely: Uppercase and remove numbers/symbols?
|
|
1731
|
+
# If input is "hello world 123", output might be "HELLO WORLD".
|
|
1732
|
+
if only_letters:
|
|
1733
|
+
import re
|
|
1734
|
+
return re.sub(r'[^a-zA-Z\s]', '', s).upper()
|
|
1735
|
+
return s.upper()
|
|
1736
|
+
|
|
1737
|
+
def _builtin_sum_range(self, start, end, condition=None):
|
|
1738
|
+
# condition is a string, e.g. "even", "odd", "prime", "digits"
|
|
1739
|
+
total = 0
|
|
1740
|
+
s = int(start)
|
|
1741
|
+
e = int(end)
|
|
1742
|
+
for i in range(s, e + 1):
|
|
1743
|
+
include = True
|
|
1744
|
+
if condition == 'even' and i % 2 != 0: include = False
|
|
1745
|
+
elif condition == 'odd' and i % 2 == 0: include = False
|
|
1746
|
+
elif condition == 'prime':
|
|
1747
|
+
if i < 2: include = False
|
|
1748
|
+
else:
|
|
1749
|
+
for k in range(2, int(i ** 0.5) + 1):
|
|
1750
|
+
if i % k == 0:
|
|
1751
|
+
include = False; break
|
|
1752
|
+
elif condition == 'digits':
|
|
1753
|
+
# sum of digits? Or sum of numbers that are single digits?
|
|
1754
|
+
# "sum of numbers from 1 to 10 when digits" -> unclear.
|
|
1755
|
+
# Assuming "digits" meant specific property.
|
|
1756
|
+
pass
|
|
1757
|
+
|
|
1758
|
+
if include:
|
|
1759
|
+
total += i
|
|
1760
|
+
return total
|
|
1761
|
+
|
|
1762
|
+
def _builtin_range_list(self, start, end, condition=None):
|
|
1763
|
+
res = []
|
|
1764
|
+
s = int(start)
|
|
1765
|
+
e = int(end)
|
|
1766
|
+
for i in range(s, e + 1):
|
|
1767
|
+
include = True
|
|
1768
|
+
if condition == 'even' and i % 2 != 0: include = False
|
|
1769
|
+
elif condition == 'odd' and i % 2 == 0: include = False
|
|
1770
|
+
elif condition == 'prime':
|
|
1771
|
+
if i < 2: include = False
|
|
1772
|
+
else:
|
|
1773
|
+
for k in range(2, int(i ** 0.5) + 1):
|
|
1774
|
+
if i % k == 0:
|
|
1775
|
+
include = False; break
|
|
1776
|
+
|
|
1777
|
+
if include:
|
|
1778
|
+
res.append(i)
|
|
1779
|
+
return res
|
|
@@ -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',
|
|
@@ -223,9 +223,24 @@ class Lexer:
|
|
|
223
223
|
'count': 'COUNT', 'many': 'MANY', 'how': 'HOW',
|
|
224
224
|
'field': 'FIELD', 'submit': 'SUBMIT', 'named': 'NAMED',
|
|
225
225
|
'placeholder': 'PLACEHOLDER',
|
|
226
|
-
'app': 'APP', 'title': 'ID', 'size': '
|
|
227
|
-
'column': '
|
|
228
|
-
'button': '
|
|
226
|
+
'app': 'APP', 'title': 'ID', 'size': 'SIZE',
|
|
227
|
+
'column': 'COLUMN', 'row': 'ROW',
|
|
228
|
+
'button': 'BUTTON', 'heading': 'HEADING',
|
|
229
|
+
'sum': 'SUM', 'upper': 'UPPER', 'lower': 'LOWER',
|
|
230
|
+
'increment': 'INCREMENT', 'decrement': 'DECREMENT',
|
|
231
|
+
'multiply': 'MULTIPLY', 'divide': 'DIVIDE',
|
|
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',
|
|
229
244
|
}
|
|
230
245
|
token_type = keywords.get(value, 'ID')
|
|
231
246
|
self.tokens.append(Token(token_type, value, self.line_number, current_col))
|
|
@@ -12,6 +12,11 @@ from .ast_nodes import *
|
|
|
12
12
|
import json
|
|
13
13
|
def execute_source(source: str, interpreter: Interpreter):
|
|
14
14
|
lines = source.split('\n')
|
|
15
|
+
import sys
|
|
16
|
+
if 'shell_lite.interpreter' in sys.modules:
|
|
17
|
+
print(f"DEBUG: Loaded interpreter from {sys.modules['shell_lite.interpreter'].__file__}")
|
|
18
|
+
else:
|
|
19
|
+
print("DEBUG: shell_lite.interpreter not in sys.modules yet?")
|
|
15
20
|
import difflib
|
|
16
21
|
try:
|
|
17
22
|
lexer = Lexer(source)
|
|
@@ -52,6 +57,12 @@ def run_file(filename: str):
|
|
|
52
57
|
if not os.path.exists(filename):
|
|
53
58
|
print(f"Error: File '{filename}' not found.")
|
|
54
59
|
return
|
|
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}")
|
|
65
|
+
|
|
55
66
|
with open(filename, 'r', encoding='utf-8') as f:
|
|
56
67
|
source = f.read()
|
|
57
68
|
interpreter = Interpreter()
|
|
@@ -61,7 +72,7 @@ def run_repl():
|
|
|
61
72
|
print("\n" + "="*40)
|
|
62
73
|
print(" ShellLite REPL - English Syntax")
|
|
63
74
|
print("="*40)
|
|
64
|
-
print("Version: v0.04.
|
|
75
|
+
print("Version: v0.04.4 | Made by Shrey Naithani")
|
|
65
76
|
print("Commands: Type 'exit' to quit, 'help' for examples.")
|
|
66
77
|
print("Note: Terminal commands (like 'shl install') must be run in CMD/PowerShell, not here.")
|
|
67
78
|
|
|
@@ -192,7 +203,7 @@ def install_globally():
|
|
|
192
203
|
ps_cmd = f'$oldPath = [Environment]::GetEnvironmentVariable("Path", "User"); if ($oldPath -notlike "*ShellLite*") {{ [Environment]::SetEnvironmentVariable("Path", "$oldPath;{install_dir}", "User") }}'
|
|
193
204
|
subprocess.run(["powershell", "-Command", ps_cmd], capture_output=True)
|
|
194
205
|
|
|
195
|
-
print(f"\n[SUCCESS] ShellLite (v0.04.
|
|
206
|
+
print(f"\n[SUCCESS] ShellLite (v0.04.4) is installed!")
|
|
196
207
|
print(f"Location: {install_dir}")
|
|
197
208
|
print("\nIMPORTANT STEP REQUIRED:")
|
|
198
209
|
print("1. Close ALL open terminal windows (CMD, PowerShell, VS Code).")
|