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.
Files changed (26) hide show
  1. {shell_lite-0.4.2/shell_lite.egg-info → shell_lite-0.4.4}/PKG-INFO +1 -1
  2. {shell_lite-0.4.2 → shell_lite-0.4.4}/pyproject.toml +1 -1
  3. {shell_lite-0.4.2 → shell_lite-0.4.4}/shell_lite/interpreter.py +94 -25
  4. {shell_lite-0.4.2 → shell_lite-0.4.4}/shell_lite/lexer.py +22 -7
  5. {shell_lite-0.4.2 → shell_lite-0.4.4}/shell_lite/main.py +13 -2
  6. {shell_lite-0.4.2 → shell_lite-0.4.4}/shell_lite/parser.py +467 -40
  7. {shell_lite-0.4.2 → shell_lite-0.4.4/shell_lite.egg-info}/PKG-INFO +1 -1
  8. {shell_lite-0.4.2 → shell_lite-0.4.4}/shell_lite.egg-info/SOURCES.txt +0 -2
  9. shell_lite-0.4.2/shell_lite/formatter.py +0 -75
  10. shell_lite-0.4.2/shell_lite/js_compiler.py +0 -220
  11. {shell_lite-0.4.2 → shell_lite-0.4.4}/LICENSE +0 -0
  12. {shell_lite-0.4.2 → shell_lite-0.4.4}/README.md +0 -0
  13. {shell_lite-0.4.2 → shell_lite-0.4.4}/setup.cfg +0 -0
  14. {shell_lite-0.4.2 → shell_lite-0.4.4}/shell_lite/__init__.py +0 -0
  15. {shell_lite-0.4.2 → shell_lite-0.4.4}/shell_lite/ast_nodes.py +0 -0
  16. {shell_lite-0.4.2 → shell_lite-0.4.4}/shell_lite/cli.py +0 -0
  17. {shell_lite-0.4.2 → shell_lite-0.4.4}/shell_lite/compiler.py +0 -0
  18. {shell_lite-0.4.2 → shell_lite-0.4.4}/shell_lite/runtime.py +0 -0
  19. {shell_lite-0.4.2 → shell_lite-0.4.4}/shell_lite.egg-info/dependency_links.txt +0 -0
  20. {shell_lite-0.4.2 → shell_lite-0.4.4}/shell_lite.egg-info/entry_points.txt +0 -0
  21. {shell_lite-0.4.2 → shell_lite-0.4.4}/shell_lite.egg-info/requires.txt +0 -0
  22. {shell_lite-0.4.2 → shell_lite-0.4.4}/shell_lite.egg-info/top_level.txt +0 -0
  23. {shell_lite-0.4.2 → shell_lite-0.4.4}/tests/test_interpreter.py +0 -0
  24. {shell_lite-0.4.2 → shell_lite-0.4.4}/tests/test_lexer.py +0 -0
  25. {shell_lite-0.4.2 → shell_lite-0.4.4}/tests/test_parser.py +0 -0
  26. {shell_lite-0.4.2 → shell_lite-0.4.4}/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.2
3
+ Version: 0.4.4
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.2"
7
+ version = "0.04.4"
8
8
  description = "A lightweight, English-like scripting language."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.8"
@@ -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': lambda s: s.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.1 is running!")
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
- 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()
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': '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',
@@ -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': 'ID',
227
- 'column': 'ID', 'row': 'ID',
228
- 'button': 'ID', 'heading': 'HEADING', 'text': 'ID',
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.2 | Made by Shrey Naithani")
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.1) is installed!")
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).")