shell-lite 0.6.3__tar.gz → 0.6.3.2__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.6.3 → shell_lite-0.6.3.2}/PKG-INFO +1 -1
- {shell_lite-0.6.3 → shell_lite-0.6.3.2}/README.md +5 -5
- {shell_lite-0.6.3 → shell_lite-0.6.3.2}/pyproject.toml +1 -1
- {shell_lite-0.6.3 → shell_lite-0.6.3.2}/shell_lite/compiler/ast_compiler.py +6 -1
- {shell_lite-0.6.3 → shell_lite-0.6.3.2}/shell_lite/compiler/builtins.py +6 -6
- {shell_lite-0.6.3 → shell_lite-0.6.3.2}/shell_lite/compiler/linker.py +18 -1
- {shell_lite-0.6.3 → shell_lite-0.6.3.2}/shell_lite/compiler/runtime_lib.py +48 -5
- {shell_lite-0.6.3 → shell_lite-0.6.3.2}/shell_lite/compiler/semantic_analyzer.py +2 -2
- {shell_lite-0.6.3 → shell_lite-0.6.3.2}/shell_lite/interpreter.py +16 -4
- {shell_lite-0.6.3 → shell_lite-0.6.3.2}/shell_lite/lsp_server.py +1 -1
- {shell_lite-0.6.3 → shell_lite-0.6.3.2}/shell_lite/main.py +16 -8
- {shell_lite-0.6.3 → shell_lite-0.6.3.2}/shell_lite/parser.py +7 -3
- {shell_lite-0.6.3 → shell_lite-0.6.3.2}/shell_lite.egg-info/PKG-INFO +1 -1
- {shell_lite-0.6.3 → shell_lite-0.6.3.2}/LICENSE +0 -0
- {shell_lite-0.6.3 → shell_lite-0.6.3.2}/README_PYPI.md +0 -0
- {shell_lite-0.6.3 → shell_lite-0.6.3.2}/setup.cfg +0 -0
- {shell_lite-0.6.3 → shell_lite-0.6.3.2}/shell_lite/__init__.py +0 -0
- {shell_lite-0.6.3 → shell_lite-0.6.3.2}/shell_lite/__main__.py +0 -0
- {shell_lite-0.6.3 → shell_lite-0.6.3.2}/shell_lite/ast_nodes.py +0 -0
- {shell_lite-0.6.3 → shell_lite-0.6.3.2}/shell_lite/cli.py +0 -0
- {shell_lite-0.6.3 → shell_lite-0.6.3.2}/shell_lite/compiler/__init__.py +0 -0
- {shell_lite-0.6.3 → shell_lite-0.6.3.2}/shell_lite/compiler/base_visitor.py +0 -0
- {shell_lite-0.6.3 → shell_lite-0.6.3.2}/shell_lite/compiler/optimizer.py +0 -0
- {shell_lite-0.6.3 → shell_lite-0.6.3.2}/shell_lite/compiler/symbol_table.py +0 -0
- {shell_lite-0.6.3 → shell_lite-0.6.3.2}/shell_lite/lexer.py +0 -0
- {shell_lite-0.6.3 → shell_lite-0.6.3.2}/shell_lite/runtime_policy.py +0 -0
- {shell_lite-0.6.3 → shell_lite-0.6.3.2}/shell_lite/stdlib/automation.shl +0 -0
- {shell_lite-0.6.3 → shell_lite-0.6.3.2}/shell_lite/stdlib/db.shl +0 -0
- {shell_lite-0.6.3 → shell_lite-0.6.3.2}/shell_lite/stdlib/io.shl +0 -0
- {shell_lite-0.6.3 → shell_lite-0.6.3.2}/shell_lite/stdlib/json.shl +0 -0
- {shell_lite-0.6.3 → shell_lite-0.6.3.2}/shell_lite/stdlib/math.shl +0 -0
- {shell_lite-0.6.3 → shell_lite-0.6.3.2}/shell_lite/stdlib/net.shl +0 -0
- {shell_lite-0.6.3 → shell_lite-0.6.3.2}/shell_lite/stdlib/std.shl +0 -0
- {shell_lite-0.6.3 → shell_lite-0.6.3.2}/shell_lite/stdlib/web.shl +0 -0
- {shell_lite-0.6.3 → shell_lite-0.6.3.2}/shell_lite/test_runner.py +0 -0
- {shell_lite-0.6.3 → shell_lite-0.6.3.2}/shell_lite.egg-info/SOURCES.txt +0 -0
- {shell_lite-0.6.3 → shell_lite-0.6.3.2}/shell_lite.egg-info/dependency_links.txt +0 -0
- {shell_lite-0.6.3 → shell_lite-0.6.3.2}/shell_lite.egg-info/entry_points.txt +0 -0
- {shell_lite-0.6.3 → shell_lite-0.6.3.2}/shell_lite.egg-info/requires.txt +0 -0
- {shell_lite-0.6.3 → shell_lite-0.6.3.2}/shell_lite.egg-info/top_level.txt +0 -0
- {shell_lite-0.6.3 → shell_lite-0.6.3.2}/tests/test_interpreter.py +0 -0
- {shell_lite-0.6.3 → shell_lite-0.6.3.2}/tests/test_issue_imported_class.py +0 -0
- {shell_lite-0.6.3 → shell_lite-0.6.3.2}/tests/test_lexer.py +0 -0
- {shell_lite-0.6.3 → shell_lite-0.6.3.2}/tests/test_parser.py +0 -0
- {shell_lite-0.6.3 → shell_lite-0.6.3.2}/tests/test_safe_mode.py +0 -0
- {shell_lite-0.6.3 → shell_lite-0.6.3.2}/tests/test_stdlib.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: shell-lite
|
|
3
|
-
Version: 0.6.3
|
|
3
|
+
Version: 0.6.3.2
|
|
4
4
|
Summary: ShellLite is a programming language designed to be as readable as plain English. Code like you Think!
|
|
5
5
|
Author-email: contact@shelllite.tech
|
|
6
6
|
License: GPLv3 with classpath exception
|
|
@@ -65,14 +65,12 @@ while attempts < max_attempts
|
|
|
65
65
|
say in red "\nGame Over! The number was " + str(secret)
|
|
66
66
|
```
|
|
67
67
|
|
|
68
|
-
## Compilation
|
|
68
|
+
## Compilation
|
|
69
69
|
|
|
70
|
-
ShellLite can be compiled
|
|
70
|
+
ShellLite can be compiled for deploymebt
|
|
71
71
|
|
|
72
72
|
```bash
|
|
73
|
-
shl compile script.shl
|
|
74
|
-
shl compile script.shl --target js # Transpile to JavaScript
|
|
75
|
-
shl compile script.shl --target wasm # Build for WebAssembly
|
|
73
|
+
shl compile script.shl
|
|
76
74
|
```
|
|
77
75
|
|
|
78
76
|
## Research & Documentation
|
|
@@ -83,6 +81,8 @@ For a deep dive into the underlying parsing theory, please refer to the papers o
|
|
|
83
81
|
|
|
84
82
|
Detailed language guides can be found in the `docs/` directory. (TO BE RE WRITTEN)
|
|
85
83
|
|
|
84
|
+
### Official Discord:- [Discord](https://discord.gg/WgqrgvuGfs)
|
|
85
|
+
|
|
86
86
|
## License
|
|
87
87
|
|
|
88
88
|
GNU GPL V3 With Class Exception License - See [LICENSE](LICENSE) for details.
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "shell-lite"
|
|
7
|
-
version = "0.6.3"
|
|
7
|
+
version = "0.6.3.2"
|
|
8
8
|
description = "ShellLite is a programming language designed to be as readable as plain English. Code like you Think!"
|
|
9
9
|
readme = "README_PYPI.md"
|
|
10
10
|
requires-python = ">=3.8"
|
|
@@ -242,7 +242,12 @@ class ASTCompiler(BaseVisitor):
|
|
|
242
242
|
return ast.Compare(left=self.visit(node.args[1]), ops=[ast.In()], comparators=[self.visit(node.args[0])])
|
|
243
243
|
|
|
244
244
|
args = [self.visit(a) for a in node.args]
|
|
245
|
-
keywords = [
|
|
245
|
+
keywords = []
|
|
246
|
+
for k, v in (node.kwargs or []):
|
|
247
|
+
arg_name = k
|
|
248
|
+
if k in ("class", "for", "def", "if", "else", "while", "return", "import", "from", "try", "except", "lambda", "with", "as", "is", "in", "and", "or", "not", "pass", "raise", "yield", "async", "await", "break", "continue"):
|
|
249
|
+
arg_name = k + "_"
|
|
250
|
+
keywords.append(ast.keyword(arg=arg_name, value=self.visit(v)))
|
|
246
251
|
|
|
247
252
|
if node.body:
|
|
248
253
|
body_func_name = self.get_tmp() + "_body"
|
|
@@ -23,7 +23,7 @@ BUILTINS: Dict[str, BuiltinMapping] = {
|
|
|
23
23
|
# Core IO
|
|
24
24
|
"print": BuiltinMapping("print", 0, None, "print"),
|
|
25
25
|
"say": BuiltinMapping("say", 0, None, "print"),
|
|
26
|
-
"ask": BuiltinMapping("ask", 0, 1, "
|
|
26
|
+
"ask": BuiltinMapping("ask", 0, 1, "shl_ask"),
|
|
27
27
|
# Type Conversion
|
|
28
28
|
"str": BuiltinMapping("str", 1, 1, "str"),
|
|
29
29
|
"int": BuiltinMapping("int", 1, 1, "int"),
|
|
@@ -42,14 +42,14 @@ BUILTINS: Dict[str, BuiltinMapping] = {
|
|
|
42
42
|
"upper": BuiltinMapping("upper", 1, 1, "str.upper"),
|
|
43
43
|
"lower": BuiltinMapping("lower", 1, 1, "str.lower"),
|
|
44
44
|
"sort": BuiltinMapping("sort", 1, 1, "sorted"),
|
|
45
|
-
"count": BuiltinMapping("count", 1, 2),
|
|
46
|
-
"split": BuiltinMapping("split", 1, 2),
|
|
45
|
+
"count": BuiltinMapping("count", 1, 2, "shl_count"),
|
|
46
|
+
"split": BuiltinMapping("split", 1, 2, "shl_split"),
|
|
47
47
|
"add": BuiltinMapping("add", 2, 2),
|
|
48
48
|
"remove": BuiltinMapping("remove", 2, 2),
|
|
49
49
|
"pop": BuiltinMapping("pop", 1, 2),
|
|
50
|
-
"contains": BuiltinMapping("contains", 2, 2),
|
|
51
|
-
"empty": BuiltinMapping("empty", 1, 1),
|
|
52
|
-
"xor": BuiltinMapping("xor", 2, 2),
|
|
50
|
+
"contains": BuiltinMapping("contains", 2, 2, "shl_contains"),
|
|
51
|
+
"empty": BuiltinMapping("empty", 1, 1, "shl_empty"),
|
|
52
|
+
"xor": BuiltinMapping("xor", 2, 2, "shl_xor"),
|
|
53
53
|
# JSON
|
|
54
54
|
"json_parse": BuiltinMapping("json_parse", 1, 1, "py_json.loads"),
|
|
55
55
|
"json_stringify": BuiltinMapping("json_stringify", 1, 2, "py_json.dumps"),
|
|
@@ -57,7 +57,24 @@ class StaticLinker:
|
|
|
57
57
|
continue
|
|
58
58
|
|
|
59
59
|
mod_name = os.path.splitext(os.path.basename(fp))[0]
|
|
60
|
-
|
|
60
|
+
try:
|
|
61
|
+
rel_dir = os.path.relpath(os.path.dirname(fp), os.path.dirname(abs_p))
|
|
62
|
+
if rel_dir == ".":
|
|
63
|
+
dots = "."
|
|
64
|
+
pkg_path = ""
|
|
65
|
+
elif rel_dir.startswith(".."):
|
|
66
|
+
parts = rel_dir.split(os.sep)
|
|
67
|
+
dots = "." * (parts.count("..") + 1)
|
|
68
|
+
remaining = [p for p in parts if p != ".."]
|
|
69
|
+
pkg_path = ".".join(remaining) + "." if remaining else ""
|
|
70
|
+
else:
|
|
71
|
+
dots = "."
|
|
72
|
+
pkg_path = rel_dir.replace(os.sep, ".") + "."
|
|
73
|
+
except ValueError:
|
|
74
|
+
dots = "."
|
|
75
|
+
pkg_path = ""
|
|
76
|
+
|
|
77
|
+
current_module_ast.append(PythonImport(f"{dots}{pkg_path}{mod_name}", alias))
|
|
61
78
|
|
|
62
79
|
if fp not in self.module_asts:
|
|
63
80
|
with open(fp, "r", encoding="utf-8") as f:
|
|
@@ -122,6 +122,37 @@ def channel_receive(q):
|
|
|
122
122
|
return q.get()
|
|
123
123
|
|
|
124
124
|
|
|
125
|
+
def shl_split(s, sep=None):
|
|
126
|
+
if sep:
|
|
127
|
+
return str(s).split(str(sep))
|
|
128
|
+
return str(s).split()
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
def shl_count(obj, item=None):
|
|
132
|
+
if item is not None:
|
|
133
|
+
return obj.count(item)
|
|
134
|
+
return len(obj)
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
def shl_contains(obj, item):
|
|
138
|
+
return item in obj
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
def shl_empty(obj):
|
|
142
|
+
return len(obj) == 0
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
def shl_xor(a, b):
|
|
146
|
+
return a ^ b
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
def shl_ask(prompt=""):
|
|
150
|
+
try:
|
|
151
|
+
return builtins.input(prompt)
|
|
152
|
+
except EOFError:
|
|
153
|
+
return None
|
|
154
|
+
|
|
155
|
+
|
|
125
156
|
def shl_execute(cmd_str):
|
|
126
157
|
require_exec(str(cmd_str))
|
|
127
158
|
args = shlex.split(str(cmd_str))
|
|
@@ -162,7 +193,7 @@ def render(obj):
|
|
|
162
193
|
|
|
163
194
|
def __make_tag(tag_name):
|
|
164
195
|
def tag_handler(*args, body=None, **kwargs):
|
|
165
|
-
attrs = "".join(f' {k.replace("_", "-")}="{v}"' for k, v in kwargs.items())
|
|
196
|
+
attrs = "".join(f' {k.rstrip("_").replace("_", "-")}="{v}"' for k, v in kwargs.items())
|
|
166
197
|
__emit_html(f"<{tag_name}{attrs}>")
|
|
167
198
|
for a in args:
|
|
168
199
|
__emit_html(str(a))
|
|
@@ -376,9 +407,10 @@ _static_routes: Dict[str, str] = {}
|
|
|
376
407
|
|
|
377
408
|
class ShellLiteHTTPHandler(BaseHTTPRequestHandler):
|
|
378
409
|
def do_GET(self):
|
|
410
|
+
clean_path = self.path.split("?")[0].split("#")[0]
|
|
379
411
|
for pfx, fld in _static_routes.items():
|
|
380
|
-
if
|
|
381
|
-
rel_path =
|
|
412
|
+
if clean_path.startswith(pfx):
|
|
413
|
+
rel_path = clean_path[len(pfx) :].lstrip("/")
|
|
382
414
|
full_path = os.path.abspath(os.path.join(fld, rel_path))
|
|
383
415
|
if not full_path.startswith(os.path.abspath(fld)):
|
|
384
416
|
self.send_response(403)
|
|
@@ -395,9 +427,20 @@ class ShellLiteHTTPHandler(BaseHTTPRequestHandler):
|
|
|
395
427
|
self.wfile.write(f.read())
|
|
396
428
|
return
|
|
397
429
|
|
|
398
|
-
|
|
430
|
+
# Check dynamic routes
|
|
431
|
+
handler = _web_handlers.get(self.path)
|
|
432
|
+
if not handler:
|
|
433
|
+
# Try matching parameterized routes
|
|
434
|
+
for route, h in _web_handlers.items():
|
|
435
|
+
if ":" in route:
|
|
436
|
+
pattern = "^" + re.sub(r':[^/]+', r'([^/]+)', route) + "$"
|
|
437
|
+
if re.match(pattern, self.path):
|
|
438
|
+
handler = h
|
|
439
|
+
break
|
|
440
|
+
|
|
441
|
+
if handler:
|
|
399
442
|
try:
|
|
400
|
-
res =
|
|
443
|
+
res = handler()
|
|
401
444
|
self.send_response(200)
|
|
402
445
|
self.send_header("Content-type", "text/html")
|
|
403
446
|
self.end_headers()
|
|
@@ -343,7 +343,7 @@ class SemanticAnalyzer(BaseTransformer):
|
|
|
343
343
|
def visit_Assign(self, node: Assign) -> Type:
|
|
344
344
|
vt = self.visit(node.value)
|
|
345
345
|
existing = self.current_scope.resolve(node.name)
|
|
346
|
-
if existing and existing.is_global and self.current_scope != self.global_scope:
|
|
346
|
+
if existing and (existing.is_global or existing.is_property) and self.current_scope != self.global_scope:
|
|
347
347
|
node.symbol_ref = existing
|
|
348
348
|
return vt
|
|
349
349
|
|
|
@@ -356,7 +356,7 @@ class SemanticAnalyzer(BaseTransformer):
|
|
|
356
356
|
def visit_TypedAssign(self, node: TypedAssign) -> Type:
|
|
357
357
|
vt = self.visit(node.value)
|
|
358
358
|
existing = self.current_scope.resolve(node.name)
|
|
359
|
-
if existing and existing.is_global and self.current_scope != self.global_scope:
|
|
359
|
+
if existing and (existing.is_global or existing.is_property) and self.current_scope != self.global_scope:
|
|
360
360
|
node.symbol_ref = existing
|
|
361
361
|
return vt
|
|
362
362
|
|
|
@@ -277,13 +277,16 @@ def make_jit_tag_fn(name, interpreter):
|
|
|
277
277
|
attrs.update(arg)
|
|
278
278
|
elif isinstance(arg, LambdaFunction):
|
|
279
279
|
t = JITTag(name, attrs)
|
|
280
|
-
|
|
280
|
+
is_top = not interpreter.web_builder
|
|
281
|
+
if not is_top:
|
|
281
282
|
interpreter.web_builder[-1].add(t)
|
|
282
283
|
interpreter.web_builder.append(t)
|
|
283
284
|
try:
|
|
284
285
|
arg()
|
|
285
286
|
finally:
|
|
286
287
|
interpreter.web_builder.pop()
|
|
288
|
+
if is_top:
|
|
289
|
+
rt.__emit_html(str(t))
|
|
287
290
|
return t
|
|
288
291
|
elif isinstance(arg, str) and "=" in arg and " " not in arg:
|
|
289
292
|
k, v = arg.split("=", 1)
|
|
@@ -295,6 +298,8 @@ def make_jit_tag_fn(name, interpreter):
|
|
|
295
298
|
t.add(c)
|
|
296
299
|
if interpreter.web_builder:
|
|
297
300
|
interpreter.web_builder[-1].add(t)
|
|
301
|
+
else:
|
|
302
|
+
rt.__emit_html(str(t))
|
|
298
303
|
return t
|
|
299
304
|
|
|
300
305
|
return fn
|
|
@@ -439,7 +444,12 @@ def std_csv_export(data, path):
|
|
|
439
444
|
|
|
440
445
|
|
|
441
446
|
class Interpreter:
|
|
442
|
-
def __init__(self):
|
|
447
|
+
def __init__(self, search_paths: Optional[List[str]] = None):
|
|
448
|
+
stdlib_path = os.path.join(os.path.dirname(__file__), "stdlib")
|
|
449
|
+
if search_paths is not None:
|
|
450
|
+
self.search_paths = search_paths + [stdlib_path]
|
|
451
|
+
else:
|
|
452
|
+
self.search_paths = [os.getcwd(), stdlib_path]
|
|
443
453
|
self.policy = get_policy()
|
|
444
454
|
self.safe_mode = self.policy.safe_mode
|
|
445
455
|
self._thread_local = threading.local()
|
|
@@ -528,7 +538,7 @@ class Interpreter:
|
|
|
528
538
|
"convert": rt.convert,
|
|
529
539
|
"clear_dict": lambda d: d.clear(),
|
|
530
540
|
}
|
|
531
|
-
self.web_builder = []
|
|
541
|
+
self.web_builder: List[Any] = []
|
|
532
542
|
for t in [
|
|
533
543
|
"div",
|
|
534
544
|
"p",
|
|
@@ -603,7 +613,7 @@ class Interpreter:
|
|
|
603
613
|
path += ".shl"
|
|
604
614
|
|
|
605
615
|
if search_paths is None:
|
|
606
|
-
search_paths = [os.getcwd(), os.path.join(os.path.dirname(__file__), "stdlib")]
|
|
616
|
+
search_paths = getattr(self, "search_paths", [os.getcwd(), os.path.join(os.path.dirname(__file__), "stdlib")])
|
|
607
617
|
|
|
608
618
|
for p in search_paths:
|
|
609
619
|
full_path = os.path.join(p, path)
|
|
@@ -613,6 +623,8 @@ class Interpreter:
|
|
|
613
623
|
raise FileNotFoundError(f"Module {path} not found")
|
|
614
624
|
|
|
615
625
|
def _load_module_nodes(self, path: str, search_paths: Optional[List[str]] = None) -> List[Node]:
|
|
626
|
+
if search_paths is None:
|
|
627
|
+
search_paths = getattr(self, "search_paths", [os.getcwd(), os.path.join(os.path.dirname(__file__), "stdlib")])
|
|
616
628
|
abs_path = self._resolve_module_path(path, search_paths)
|
|
617
629
|
stdlib_root = os.path.abspath(os.path.join(os.path.dirname(__file__), "stdlib"))
|
|
618
630
|
if abs_path.startswith(stdlib_root):
|
|
@@ -320,7 +320,7 @@ class LSPServer:
|
|
|
320
320
|
"renameProvider": True,
|
|
321
321
|
"referencesProvider": True,
|
|
322
322
|
},
|
|
323
|
-
"serverInfo": {"name": "ShellLite Enhanced LSP", "version": "0.6.3"},
|
|
323
|
+
"serverInfo": {"name": "ShellLite Enhanced LSP", "version": "0.6.3.2"},
|
|
324
324
|
},
|
|
325
325
|
)
|
|
326
326
|
elif method == "textDocument/didOpen":
|
|
@@ -82,7 +82,7 @@ def run_repl():
|
|
|
82
82
|
interpreter = Interpreter()
|
|
83
83
|
print("\n" + "=" * 40)
|
|
84
84
|
print("=" * 40)
|
|
85
|
-
print("Version: v0.6.3")
|
|
85
|
+
print("Version: v0.6.3.2")
|
|
86
86
|
print("Commands: Type 'exit' to quit, 'help' for examples.")
|
|
87
87
|
print("Note: Terminal commands (like 'shl install') must be run outside the REPL.")
|
|
88
88
|
try:
|
|
@@ -214,7 +214,7 @@ def install_globally():
|
|
|
214
214
|
with open(rc, "a", encoding="utf-8") as f:
|
|
215
215
|
f.write(f"\n# Added by ShellLite Installer\n{export_line}\n")
|
|
216
216
|
|
|
217
|
-
print("\n[SUCCESS] ShellLite (v0.6.3) is installed!")
|
|
217
|
+
print("\n[SUCCESS] ShellLite (v0.6.3.2) is installed!")
|
|
218
218
|
print(f"Location: {install_dir}")
|
|
219
219
|
print("\nIMPORTANT STEP REQUIRED:")
|
|
220
220
|
print("1. Close ALL open terminal windows.")
|
|
@@ -231,7 +231,7 @@ def search_package(query: Optional[str] = None):
|
|
|
231
231
|
print("\n" + "=" * 50)
|
|
232
232
|
print(f" Searching for: {query if query else 'all'}")
|
|
233
233
|
print("=" * 50)
|
|
234
|
-
print("Notice: The package registry is being rebuilt for v0.6.3")
|
|
234
|
+
print("Notice: The package registry is being rebuilt for v0.6.3.2")
|
|
235
235
|
print("In the meantime, you can find packages at: https://github.com/topics/shell-lite")
|
|
236
236
|
print("=" * 50 + "\n")
|
|
237
237
|
|
|
@@ -446,11 +446,19 @@ def install_package(package_name: str, branch: str = "main", _visited=None):
|
|
|
446
446
|
print(f"Extraction failed for {package_name}: {e}")
|
|
447
447
|
|
|
448
448
|
|
|
449
|
-
def compile_file(
|
|
449
|
+
def compile_file(
|
|
450
|
+
filename: str,
|
|
451
|
+
target: str = "python",
|
|
452
|
+
project_name: Optional[str] = None,
|
|
453
|
+
project_dir: Optional[str] = None,
|
|
454
|
+
):
|
|
450
455
|
if not os.path.exists(filename):
|
|
451
456
|
print(f"Error: File '{filename}' not found.")
|
|
452
457
|
return
|
|
453
458
|
|
|
459
|
+
if project_dir is None:
|
|
460
|
+
project_dir = os.path.dirname(os.path.abspath(filename))
|
|
461
|
+
|
|
454
462
|
if target.lower() == "llvm":
|
|
455
463
|
try:
|
|
456
464
|
from .llvm_backend.builder import build_llvm # type: ignore[import-untyped, import-not-found]
|
|
@@ -497,8 +505,8 @@ def compile_file(filename: str, target: str = "python"):
|
|
|
497
505
|
compiler = ASTCompiler()
|
|
498
506
|
|
|
499
507
|
# Determine output directory
|
|
500
|
-
base_name = os.path.splitext(os.path.basename(filename))[0]
|
|
501
|
-
out_dir = os.path.join(
|
|
508
|
+
base_name = project_name or os.path.splitext(os.path.basename(filename))[0]
|
|
509
|
+
out_dir = os.path.join(project_dir, f"{base_name}_build")
|
|
502
510
|
if os.path.exists(out_dir):
|
|
503
511
|
shutil.rmtree(out_dir)
|
|
504
512
|
os.makedirs(out_dir)
|
|
@@ -640,7 +648,7 @@ def format_file(filename: str):
|
|
|
640
648
|
if not os.path.exists(filename):
|
|
641
649
|
print(f"Error: File '{filename}' not found.")
|
|
642
650
|
return
|
|
643
|
-
print("Notice: The formatting feature is being rebuilt for v0.6.3 and is temporarily unavailable.")
|
|
651
|
+
print("Notice: The formatting feature is being rebuilt for v0.6.3.2 and is temporarily unavailable.")
|
|
644
652
|
return
|
|
645
653
|
|
|
646
654
|
|
|
@@ -768,7 +776,7 @@ def main():
|
|
|
768
776
|
elif cmd == "help" or cmd == "--help" or cmd == "-h":
|
|
769
777
|
show_help()
|
|
770
778
|
elif cmd == "--version" or cmd == "-v":
|
|
771
|
-
print("ShellLite v0.6.3")
|
|
779
|
+
print("ShellLite v0.6.3.2")
|
|
772
780
|
elif cmd == "get":
|
|
773
781
|
if len(sys.argv) > 2:
|
|
774
782
|
package_name = sys.argv[2]
|
|
@@ -373,6 +373,12 @@ class Parser:
|
|
|
373
373
|
ast_node.end_col = last_tok.column + len(str(last_tok.value))
|
|
374
374
|
return ast_node
|
|
375
375
|
|
|
376
|
+
def collect_descendant_tokens(self, nodes: List[GeoNode], out: List[Token]):
|
|
377
|
+
for child in nodes:
|
|
378
|
+
out.extend(child.tokens)
|
|
379
|
+
if child.children:
|
|
380
|
+
self.collect_descendant_tokens(child.children, out)
|
|
381
|
+
|
|
376
382
|
def bind_node(self, node: GeoNode) -> Optional[Node]:
|
|
377
383
|
# Find first non-noise token for dispatch
|
|
378
384
|
effective_head = node.head_token
|
|
@@ -805,9 +811,7 @@ class Parser:
|
|
|
805
811
|
|
|
806
812
|
expr_tokens = tokens[assign_idx + 1 :]
|
|
807
813
|
if node.children:
|
|
808
|
-
|
|
809
|
-
expr_tokens.extend(child.tokens)
|
|
810
|
-
# print(f"DEBUG bind_assignment: expr_tokens={[t.value for t in expr_tokens]}")
|
|
814
|
+
self.collect_descendant_tokens(node.children, expr_tokens)
|
|
811
815
|
value = self.parse_expr_iterative(expr_tokens, node.children)
|
|
812
816
|
if value is None:
|
|
813
817
|
raise SyntaxError(f"Missing value in assignment at line {node.line}")
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: shell-lite
|
|
3
|
-
Version: 0.6.3
|
|
3
|
+
Version: 0.6.3.2
|
|
4
4
|
Summary: ShellLite is a programming language designed to be as readable as plain English. Code like you Think!
|
|
5
5
|
Author-email: contact@shelllite.tech
|
|
6
6
|
License: GPLv3 with classpath exception
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|