inscript-lang 1.9.7__tar.gz → 1.9.9__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.
- {inscript_lang-1.9.7 → inscript_lang-1.9.9}/PKG-INFO +1 -1
- {inscript_lang-1.9.7 → inscript_lang-1.9.9}/analyzer.py +12 -4
- {inscript_lang-1.9.7 → inscript_lang-1.9.9}/inscript.py +318 -14
- {inscript_lang-1.9.7 → inscript_lang-1.9.9}/inscript_lang.egg-info/PKG-INFO +1 -1
- {inscript_lang-1.9.7 → inscript_lang-1.9.9}/pyproject.toml +1 -1
- {inscript_lang-1.9.7 → inscript_lang-1.9.9}/repl.py +1 -1
- {inscript_lang-1.9.7 → inscript_lang-1.9.9}/README.md +0 -0
- {inscript_lang-1.9.7 → inscript_lang-1.9.9}/ast_nodes.py +0 -0
- {inscript_lang-1.9.7 → inscript_lang-1.9.9}/compiler.py +0 -0
- {inscript_lang-1.9.7 → inscript_lang-1.9.9}/environment.py +0 -0
- {inscript_lang-1.9.7 → inscript_lang-1.9.9}/errors.py +0 -0
- {inscript_lang-1.9.7 → inscript_lang-1.9.9}/inscript_fmt.py +0 -0
- {inscript_lang-1.9.7 → inscript_lang-1.9.9}/inscript_lang.egg-info/SOURCES.txt +0 -0
- {inscript_lang-1.9.7 → inscript_lang-1.9.9}/inscript_lang.egg-info/dependency_links.txt +0 -0
- {inscript_lang-1.9.7 → inscript_lang-1.9.9}/inscript_lang.egg-info/entry_points.txt +0 -0
- {inscript_lang-1.9.7 → inscript_lang-1.9.9}/inscript_lang.egg-info/requires.txt +0 -0
- {inscript_lang-1.9.7 → inscript_lang-1.9.9}/inscript_lang.egg-info/top_level.txt +0 -0
- {inscript_lang-1.9.7 → inscript_lang-1.9.9}/inscript_test.py +0 -0
- {inscript_lang-1.9.7 → inscript_lang-1.9.9}/interpreter.py +0 -0
- {inscript_lang-1.9.7 → inscript_lang-1.9.9}/lexer.py +0 -0
- {inscript_lang-1.9.7 → inscript_lang-1.9.9}/parser.py +0 -0
- {inscript_lang-1.9.7 → inscript_lang-1.9.9}/pygame_backend.py +0 -0
- {inscript_lang-1.9.7 → inscript_lang-1.9.9}/setup.cfg +0 -0
- {inscript_lang-1.9.7 → inscript_lang-1.9.9}/setup.py +0 -0
- {inscript_lang-1.9.7 → inscript_lang-1.9.9}/stdlib.py +0 -0
- {inscript_lang-1.9.7 → inscript_lang-1.9.9}/stdlib_extended.py +0 -0
- {inscript_lang-1.9.7 → inscript_lang-1.9.9}/stdlib_extended_2.py +0 -0
- {inscript_lang-1.9.7 → inscript_lang-1.9.9}/stdlib_game.py +0 -0
- {inscript_lang-1.9.7 → inscript_lang-1.9.9}/stdlib_values.py +0 -0
- {inscript_lang-1.9.7 → inscript_lang-1.9.9}/vm.py +0 -0
|
@@ -1151,13 +1151,21 @@ class Analyzer(Visitor):
|
|
|
1151
1151
|
def visit_ArrayLiteralExpr(self, node: ArrayLiteralExpr) -> InScriptType:
|
|
1152
1152
|
if not node.elements:
|
|
1153
1153
|
return array_type(T_ANY)
|
|
1154
|
+
# v1.9.8: infer element type; fall back to Array<any> on mixed types
|
|
1154
1155
|
first_type = self.visit(node.elements[0])
|
|
1156
|
+
# Normalise literal string types to T_STRING for array inference
|
|
1157
|
+
if is_literal_type(first_type):
|
|
1158
|
+
first_type = T_STRING
|
|
1159
|
+
mixed = False
|
|
1155
1160
|
for elem in node.elements[1:]:
|
|
1156
1161
|
et = self.visit(elem)
|
|
1157
|
-
if
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1162
|
+
if is_literal_type(et):
|
|
1163
|
+
et = T_STRING
|
|
1164
|
+
if not types_compatible(first_type, et) and not types_compatible(et, first_type):
|
|
1165
|
+
mixed = True
|
|
1166
|
+
if mixed:
|
|
1167
|
+
return array_type(T_ANY) # [1, "a", true] → Array<any>
|
|
1168
|
+
return array_type(first_type) # [1, 2, 3] → Array<int>
|
|
1161
1169
|
|
|
1162
1170
|
def visit_DictLiteralExpr(self, node: DictLiteralExpr) -> InScriptType:
|
|
1163
1171
|
if not node.pairs:
|
|
@@ -24,7 +24,7 @@ from errors import (InScriptError, LexerError, ParseError,
|
|
|
24
24
|
SemanticError, InScriptRuntimeError,
|
|
25
25
|
MultiError, InScriptWarning)
|
|
26
26
|
|
|
27
|
-
VERSION = "1.9.
|
|
27
|
+
VERSION = "1.9.9"
|
|
28
28
|
LANG = "InScript"
|
|
29
29
|
PACKAGES_DIR = os.path.join(os.path.expanduser("~"), ".inscript", "packages")
|
|
30
30
|
REGISTRY_URL = "https://raw.githubusercontent.com/authorss81/inscript-packages/main/registry.json"
|
|
@@ -34,24 +34,117 @@ REGISTRY_URL = "https://raw.githubusercontent.com/authorss81/inscript-packages/m
|
|
|
34
34
|
# RUN A FILE
|
|
35
35
|
# ─────────────────────────────────────────────────────────────────────────────
|
|
36
36
|
|
|
37
|
-
def
|
|
38
|
-
"""
|
|
37
|
+
def _parse_pkg_spec(spec: str):
|
|
38
|
+
"""
|
|
39
|
+
v1.9.9: Parse 'PKG@version' or just 'PKG'.
|
|
40
|
+
Returns (name, version_or_None).
|
|
41
|
+
"""
|
|
42
|
+
if "@" in spec:
|
|
43
|
+
idx = spec.index("@")
|
|
44
|
+
return spec[:idx].strip(), spec[idx+1:].strip()
|
|
45
|
+
return spec.strip(), None
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def _read_lock(lock_path: str) -> dict:
|
|
49
|
+
"""
|
|
50
|
+
v1.9.9: Read inscript.lock and return {pkg_name: version} mapping.
|
|
51
|
+
Returns empty dict if file doesn't exist or can't be parsed.
|
|
52
|
+
"""
|
|
53
|
+
if not os.path.isfile(lock_path):
|
|
54
|
+
return {}
|
|
55
|
+
import re as _re
|
|
56
|
+
result = {}
|
|
57
|
+
current_pkg = None
|
|
58
|
+
for line in open(lock_path, encoding="utf-8"):
|
|
59
|
+
m = _re.match(r'^\[package\.(.+)\]', line.strip())
|
|
60
|
+
if m:
|
|
61
|
+
current_pkg = m.group(1)
|
|
62
|
+
elif current_pkg:
|
|
63
|
+
m2 = _re.match(r'^version\s*=\s*"([^"]+)"', line.strip())
|
|
64
|
+
if m2:
|
|
65
|
+
result[current_pkg] = m2.group(1)
|
|
66
|
+
return result
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def _write_lock_entry(lock_path: str, pkg_name: str, version: str) -> None:
|
|
70
|
+
"""
|
|
71
|
+
v1.9.9: Add or update a single package entry in inscript.lock.
|
|
72
|
+
Creates the file if it doesn't exist.
|
|
73
|
+
"""
|
|
74
|
+
import hashlib, re as _re, datetime
|
|
75
|
+
# Read existing content
|
|
76
|
+
if os.path.isfile(lock_path):
|
|
77
|
+
content = open(lock_path, encoding="utf-8").read()
|
|
78
|
+
else:
|
|
79
|
+
content = (
|
|
80
|
+
"# InScript lockfile — do not edit manually\n"
|
|
81
|
+
f"# Generated by InScript {VERSION}\n\n"
|
|
82
|
+
"[metadata]\n"
|
|
83
|
+
f'inscript = "{VERSION}"\n'
|
|
84
|
+
"generated = \"" + datetime.datetime.utcnow().strftime("%Y-%m-%dT%H:%M:%SZ") + "\"\n\n"
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
sha256 = hashlib.sha256(f"{pkg_name}@{version}".encode()).hexdigest()
|
|
88
|
+
entry = "[package." + pkg_name + "]\nversion = \"" + version + "\"\nsha256 = \"" + sha256 + "\"\n\n"
|
|
89
|
+
|
|
90
|
+
# Remove old entry if present
|
|
91
|
+
content = _re.sub(
|
|
92
|
+
rf'\[package\.{_re.escape(pkg_name)}\][^\[]*', '', content
|
|
93
|
+
)
|
|
94
|
+
content = content.rstrip() + "\n\n" + entry
|
|
95
|
+
with open(lock_path, "w", encoding="utf-8") as f:
|
|
96
|
+
f.write(content)
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def _remove_lock_entry(lock_path: str, pkg_name: str) -> None:
|
|
100
|
+
"""v1.9.9: Remove a package entry from inscript.lock."""
|
|
101
|
+
import re as _re
|
|
102
|
+
if not os.path.isfile(lock_path):
|
|
103
|
+
return
|
|
104
|
+
content = open(lock_path, encoding="utf-8").read()
|
|
105
|
+
content = _re.sub(
|
|
106
|
+
rf'\[package\.{_re.escape(pkg_name)}\][^\[]*', '', content
|
|
107
|
+
)
|
|
108
|
+
with open(lock_path, "w", encoding="utf-8") as f:
|
|
109
|
+
f.write(content)
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
def install_package(pkg_spec: str, lock_path: str = None) -> int:
|
|
113
|
+
"""
|
|
114
|
+
v1.9.9: Download and install an InScript package to ~/.inscript/packages/
|
|
115
|
+
|
|
116
|
+
Supports 'PKG' and 'PKG@version' specs.
|
|
117
|
+
Writes the installed version to inscript.lock if lock_path is given.
|
|
118
|
+
Falls back to lock file version info on network failure.
|
|
119
|
+
"""
|
|
39
120
|
import urllib.request, json as _json, zipfile, io
|
|
40
121
|
|
|
122
|
+
pkg_name, requested_version = _parse_pkg_spec(pkg_spec)
|
|
41
123
|
os.makedirs(PACKAGES_DIR, exist_ok=True)
|
|
42
124
|
pkg_dir = os.path.join(PACKAGES_DIR, pkg_name)
|
|
43
125
|
|
|
44
|
-
if
|
|
126
|
+
# Check if already installed at requested version
|
|
127
|
+
if os.path.exists(pkg_dir) and not requested_version:
|
|
45
128
|
print(f"[InScript] Package '{pkg_name}' is already installed at {pkg_dir}")
|
|
46
129
|
return 0
|
|
47
130
|
|
|
48
|
-
|
|
131
|
+
# Try fetching registry
|
|
132
|
+
registry = None
|
|
49
133
|
try:
|
|
134
|
+
print(f"[InScript] Fetching registry...")
|
|
50
135
|
with urllib.request.urlopen(REGISTRY_URL, timeout=10) as resp:
|
|
51
136
|
registry = _json.loads(resp.read().decode())
|
|
52
137
|
except Exception as e:
|
|
138
|
+
# v1.9.9: Offline fallback — if lock file has this package, report it
|
|
139
|
+
if lock_path:
|
|
140
|
+
locked = _read_lock(lock_path)
|
|
141
|
+
if pkg_name in locked:
|
|
142
|
+
print(f"[InScript] Registry unreachable ({e})")
|
|
143
|
+
print(f"[InScript] Offline: '{pkg_name}' is pinned at v{locked[pkg_name]} in lock file.")
|
|
144
|
+
print(f"[InScript] Tip: manually place .ins files in {PACKAGES_DIR}/{pkg_name}/ to install offline.")
|
|
145
|
+
return 0
|
|
53
146
|
print(f"[InScript] Could not reach package registry: {e}", file=sys.stderr)
|
|
54
|
-
print(f"[InScript] Tip:
|
|
147
|
+
print(f"[InScript] Tip: place .ins files in {PACKAGES_DIR}/ or run 'inscript lock' first.",
|
|
55
148
|
file=sys.stderr)
|
|
56
149
|
return 1
|
|
57
150
|
|
|
@@ -62,22 +155,139 @@ def install_package(pkg_name: str) -> int:
|
|
|
62
155
|
return 1
|
|
63
156
|
|
|
64
157
|
pkg_info = registry[pkg_name]
|
|
65
|
-
|
|
66
|
-
|
|
158
|
+
latest_version = pkg_info.get("version", "?")
|
|
159
|
+
install_version = requested_version or latest_version
|
|
160
|
+
zip_url = pkg_info.get("url")
|
|
67
161
|
|
|
68
|
-
print(f"[InScript] Installing {pkg_name}@{
|
|
162
|
+
print(f"[InScript] Installing {pkg_name}@{install_version}...")
|
|
69
163
|
try:
|
|
70
164
|
with urllib.request.urlopen(zip_url, timeout=30) as resp:
|
|
71
165
|
data = resp.read()
|
|
72
166
|
with zipfile.ZipFile(io.BytesIO(data)) as zf:
|
|
73
167
|
zf.extractall(PACKAGES_DIR)
|
|
74
|
-
print(f"[InScript] ✅ {pkg_name}@{
|
|
168
|
+
print(f"[InScript] ✅ {pkg_name}@{install_version} installed to {pkg_dir}")
|
|
169
|
+
# v1.9.9: Write to lock file
|
|
170
|
+
if lock_path:
|
|
171
|
+
_write_lock_entry(lock_path, pkg_name, install_version)
|
|
172
|
+
print(f"[InScript] Pinned {pkg_name}@{install_version} in lock file.")
|
|
75
173
|
return 0
|
|
76
174
|
except Exception as e:
|
|
77
175
|
print(f"[InScript] Install failed: {e}", file=sys.stderr)
|
|
78
176
|
return 1
|
|
79
177
|
|
|
80
178
|
|
|
179
|
+
def install_all_from_toml(project_dir: str = ".") -> int:
|
|
180
|
+
"""
|
|
181
|
+
v1.9.9: `inscript install` (no args) — read inscript.toml [dependencies]
|
|
182
|
+
and install every listed package, respecting inscript.lock if present.
|
|
183
|
+
"""
|
|
184
|
+
manifest_path = os.path.join(project_dir, MANIFEST_FILENAME)
|
|
185
|
+
lock_path = os.path.join(project_dir, LOCK_FILENAME)
|
|
186
|
+
|
|
187
|
+
if not os.path.isfile(manifest_path):
|
|
188
|
+
print(f"[InScript install] No '{MANIFEST_FILENAME}' found in '{project_dir}'.", file=sys.stderr)
|
|
189
|
+
print(f"[InScript install] Run 'inscript init' to create one.", file=sys.stderr)
|
|
190
|
+
return 1
|
|
191
|
+
|
|
192
|
+
try:
|
|
193
|
+
with open(manifest_path, encoding="utf-8") as f:
|
|
194
|
+
content = f.read()
|
|
195
|
+
data = _parse_toml_simple(content)
|
|
196
|
+
except Exception as e:
|
|
197
|
+
print(f"[InScript install] Cannot parse '{manifest_path}': {e}", file=sys.stderr)
|
|
198
|
+
return 1
|
|
199
|
+
|
|
200
|
+
deps = data.get("dependencies", {})
|
|
201
|
+
if not deps:
|
|
202
|
+
print(f"[InScript install] No dependencies listed in '{manifest_path}'.")
|
|
203
|
+
return 0
|
|
204
|
+
|
|
205
|
+
# v1.9.9: If lock file exists, use its pinned versions
|
|
206
|
+
locked = _read_lock(lock_path) if os.path.isfile(lock_path) else {}
|
|
207
|
+
|
|
208
|
+
print(f"[InScript install] Installing {len(deps)} dependenc{'y' if len(deps)==1 else 'ies'}...")
|
|
209
|
+
failed = 0
|
|
210
|
+
for pkg_name, constraint in sorted(deps.items()):
|
|
211
|
+
# Prefer locked version over constraint range
|
|
212
|
+
pinned = locked.get(pkg_name)
|
|
213
|
+
spec = f"{pkg_name}@{pinned}" if pinned else pkg_name
|
|
214
|
+
ret = install_package(spec, lock_path=lock_path)
|
|
215
|
+
if ret != 0:
|
|
216
|
+
failed += 1
|
|
217
|
+
|
|
218
|
+
if failed:
|
|
219
|
+
print(f"[InScript install] {failed} package(s) failed to install.", file=sys.stderr)
|
|
220
|
+
return 1
|
|
221
|
+
print(f"[InScript install] ✅ All dependencies installed.")
|
|
222
|
+
return 0
|
|
223
|
+
|
|
224
|
+
|
|
225
|
+
def outdated_packages(project_dir: str = ".") -> int:
|
|
226
|
+
"""
|
|
227
|
+
v1.9.9: `inscript outdated` — compare inscript.lock versions against
|
|
228
|
+
the latest available in the registry. Reports which packages have updates.
|
|
229
|
+
"""
|
|
230
|
+
import urllib.request, json as _json
|
|
231
|
+
lock_path = os.path.join(project_dir, LOCK_FILENAME)
|
|
232
|
+
locked = _read_lock(lock_path)
|
|
233
|
+
|
|
234
|
+
if not locked:
|
|
235
|
+
print("[InScript outdated] No inscript.lock found or no packages locked.")
|
|
236
|
+
print("[InScript outdated] Run 'inscript lock' to generate one.")
|
|
237
|
+
return 0
|
|
238
|
+
|
|
239
|
+
try:
|
|
240
|
+
with urllib.request.urlopen(REGISTRY_URL, timeout=10) as resp:
|
|
241
|
+
registry = _json.loads(resp.read().decode())
|
|
242
|
+
except Exception as e:
|
|
243
|
+
print(f"[InScript outdated] Cannot reach registry: {e}", file=sys.stderr)
|
|
244
|
+
print("[InScript outdated] Showing locked versions (cannot check for updates):")
|
|
245
|
+
for pkg, ver in sorted(locked.items()):
|
|
246
|
+
print(f" • {pkg}@{ver} (locked)")
|
|
247
|
+
return 1
|
|
248
|
+
|
|
249
|
+
any_outdated = False
|
|
250
|
+
for pkg_name, current_ver in sorted(locked.items()):
|
|
251
|
+
if pkg_name not in registry:
|
|
252
|
+
print(f" ? {pkg_name}@{current_ver} — not found in registry")
|
|
253
|
+
continue
|
|
254
|
+
latest = registry[pkg_name].get("version", "?")
|
|
255
|
+
if latest != current_ver:
|
|
256
|
+
print(f" ↑ {pkg_name} {current_ver} → {latest}")
|
|
257
|
+
any_outdated = True
|
|
258
|
+
else:
|
|
259
|
+
print(f" ✓ {pkg_name}@{current_ver} (up to date)")
|
|
260
|
+
|
|
261
|
+
if not any_outdated:
|
|
262
|
+
print("[InScript outdated] All packages are up to date.")
|
|
263
|
+
return 0
|
|
264
|
+
|
|
265
|
+
|
|
266
|
+
def update_package(pkg_spec: str, project_dir: str = ".") -> int:
|
|
267
|
+
"""
|
|
268
|
+
v1.9.9: `inscript update PKG` or `inscript update PKG@version` —
|
|
269
|
+
remove the existing installation and reinstall at the specified (or latest) version.
|
|
270
|
+
Updates inscript.lock with the new pinned version.
|
|
271
|
+
"""
|
|
272
|
+
import shutil
|
|
273
|
+
pkg_name, new_version = _parse_pkg_spec(pkg_spec)
|
|
274
|
+
lock_path = os.path.join(project_dir, LOCK_FILENAME)
|
|
275
|
+
pkg_dir = os.path.join(PACKAGES_DIR, pkg_name)
|
|
276
|
+
|
|
277
|
+
# Remove existing installation
|
|
278
|
+
if os.path.exists(pkg_dir):
|
|
279
|
+
shutil.rmtree(pkg_dir)
|
|
280
|
+
print(f"[InScript update] Removed existing {pkg_name} installation.")
|
|
281
|
+
|
|
282
|
+
spec = f"{pkg_name}@{new_version}" if new_version else pkg_name
|
|
283
|
+
ret = install_package(spec, lock_path=lock_path)
|
|
284
|
+
if ret == 0:
|
|
285
|
+
print(f"[InScript update] ✅ {pkg_name} updated.")
|
|
286
|
+
return ret
|
|
287
|
+
|
|
288
|
+
|
|
289
|
+
|
|
290
|
+
|
|
81
291
|
def list_packages() -> int:
|
|
82
292
|
"""List installed packages."""
|
|
83
293
|
if not os.path.exists(PACKAGES_DIR):
|
|
@@ -315,6 +525,81 @@ def _compat_files(path: str) -> int:
|
|
|
315
525
|
return 1
|
|
316
526
|
|
|
317
527
|
|
|
528
|
+
def _infer_types_file(path: str) -> int:
|
|
529
|
+
"""
|
|
530
|
+
v1.9.8: `inscript --infer-types FILE`
|
|
531
|
+
Parse and type-check FILE, then print the inferred type for every
|
|
532
|
+
let/const declaration. Useful for understanding what the analyzer infers.
|
|
533
|
+
Returns 0 on success, 1 if file not found or parse error.
|
|
534
|
+
"""
|
|
535
|
+
import os
|
|
536
|
+
if not os.path.isfile(path):
|
|
537
|
+
print(f"[InScript infer-types] File not found: '{path}'", file=sys.stderr)
|
|
538
|
+
return 1
|
|
539
|
+
try:
|
|
540
|
+
with open(path, encoding="utf-8") as f:
|
|
541
|
+
src = f.read()
|
|
542
|
+
except OSError as e:
|
|
543
|
+
print(f"[InScript infer-types] Cannot read '{path}': {e}", file=sys.stderr)
|
|
544
|
+
return 1
|
|
545
|
+
|
|
546
|
+
from lexer import Lexer
|
|
547
|
+
from parser import Parser
|
|
548
|
+
from analyzer import Analyzer
|
|
549
|
+
from ast_nodes import VarDecl
|
|
550
|
+
|
|
551
|
+
try:
|
|
552
|
+
tokens = Lexer(src).tokenize()
|
|
553
|
+
tree = Parser(tokens).parse()
|
|
554
|
+
except Exception as e:
|
|
555
|
+
print(f"[InScript infer-types] Parse error: {e}", file=sys.stderr)
|
|
556
|
+
return 1
|
|
557
|
+
|
|
558
|
+
analyzer = Analyzer()
|
|
559
|
+
try:
|
|
560
|
+
analyzer.analyze(tree)
|
|
561
|
+
except Exception:
|
|
562
|
+
pass # best-effort — show what we have even if there are errors
|
|
563
|
+
|
|
564
|
+
# Walk all VarDecl / ConstDecl nodes and report inferred types
|
|
565
|
+
results = []
|
|
566
|
+
def _walk(node):
|
|
567
|
+
if node is None:
|
|
568
|
+
return
|
|
569
|
+
if isinstance(node, (VarDecl,)):
|
|
570
|
+
sym = analyzer._scope.lookup(node.name) if hasattr(analyzer, '_scope') else None
|
|
571
|
+
typ = sym.type_ if sym else None
|
|
572
|
+
type_str = str(typ) if typ else "any"
|
|
573
|
+
results.append((node.line, "let" if not node.is_const else "const",
|
|
574
|
+
node.name, type_str))
|
|
575
|
+
# Recurse into body of blocks, fns etc.
|
|
576
|
+
for attr in ("body", "then_branch", "else_branch", "value",
|
|
577
|
+
"initializer", "statements"):
|
|
578
|
+
child = getattr(node, attr, None)
|
|
579
|
+
if child is None:
|
|
580
|
+
continue
|
|
581
|
+
if hasattr(child, "__iter__") and not isinstance(child, str):
|
|
582
|
+
for c in child:
|
|
583
|
+
if hasattr(c, "__class__") and hasattr(c, "line"):
|
|
584
|
+
_walk(c)
|
|
585
|
+
elif hasattr(child, "line"):
|
|
586
|
+
_walk(child)
|
|
587
|
+
|
|
588
|
+
if hasattr(tree, "body"):
|
|
589
|
+
for stmt in tree.body:
|
|
590
|
+
_walk(stmt)
|
|
591
|
+
|
|
592
|
+
if not results:
|
|
593
|
+
print(f"[InScript infer-types] No let/const declarations found in '{path}'")
|
|
594
|
+
return 0
|
|
595
|
+
|
|
596
|
+
print(f"[InScript infer-types] Inferred types in '{path}':\n")
|
|
597
|
+
for line, kw, name, typ in results:
|
|
598
|
+
print(f" Line {line:3d}: {kw} {name:<20s} → {typ}")
|
|
599
|
+
print()
|
|
600
|
+
return 0
|
|
601
|
+
|
|
602
|
+
|
|
318
603
|
def _migrate_files(path: str) -> int:
|
|
319
604
|
"""v1.7.4: Auto-migrate deprecated InScript syntax in-place."""
|
|
320
605
|
import re, os
|
|
@@ -1573,6 +1858,8 @@ Examples:
|
|
|
1573
1858
|
parser.add_argument("--check", action="store_true", help="Type-check only, don't run")
|
|
1574
1859
|
parser.add_argument("--check-all", metavar="DIR",
|
|
1575
1860
|
help="v1.6.0: Check all .ins files in DIR recursively, exit 1 if any errors")
|
|
1861
|
+
parser.add_argument("--infer-types", metavar="FILE",
|
|
1862
|
+
help="v1.9.8: Print inferred type for every let/const declaration in FILE")
|
|
1576
1863
|
parser.add_argument("--migrate", metavar="DIR_OR_FILE",
|
|
1577
1864
|
help="v1.7.4: Auto-migrate deprecated syntax (null→nil, div→//)")
|
|
1578
1865
|
parser.add_argument("--compat", metavar="DIR_OR_FILE",
|
|
@@ -1630,8 +1917,12 @@ Examples:
|
|
|
1630
1917
|
parser.add_argument("--watch", action="store_true",
|
|
1631
1918
|
help="Watch file for changes and rerun: inscript --watch game.ins")
|
|
1632
1919
|
parser.add_argument("--version", action="store_true", help="Print version and exit")
|
|
1633
|
-
parser.add_argument("--install", metavar="PKG",
|
|
1634
|
-
help="Install a package
|
|
1920
|
+
parser.add_argument("--install", metavar="PKG", nargs="?", const="",
|
|
1921
|
+
help="v1.9.9: Install a package (PKG or PKG@version); no arg = install all from inscript.toml")
|
|
1922
|
+
parser.add_argument("--update", metavar="PKG",
|
|
1923
|
+
help="v1.9.9: Update a package to latest (or PKG@version)")
|
|
1924
|
+
parser.add_argument("--outdated", action="store_true",
|
|
1925
|
+
help="v1.9.9: Show packages with newer versions available")
|
|
1635
1926
|
parser.add_argument("--remove", metavar="PKG",
|
|
1636
1927
|
help="Remove a package: inscript --remove math-utils")
|
|
1637
1928
|
parser.add_argument("--packages", action="store_true",
|
|
@@ -1710,8 +2001,19 @@ Examples:
|
|
|
1710
2001
|
if args.packages:
|
|
1711
2002
|
return list_packages()
|
|
1712
2003
|
|
|
1713
|
-
if args
|
|
1714
|
-
|
|
2004
|
+
if getattr(args, 'install', None) is not None:
|
|
2005
|
+
# v1.9.9: `inscript install` (no args) → install from toml
|
|
2006
|
+
# `inscript install PKG[@version]` → install specific pkg
|
|
2007
|
+
lock_path = os.path.join(".", LOCK_FILENAME)
|
|
2008
|
+
if args.install == "":
|
|
2009
|
+
return install_all_from_toml(".")
|
|
2010
|
+
return install_package(args.install, lock_path=lock_path)
|
|
2011
|
+
|
|
2012
|
+
if getattr(args, 'update', None):
|
|
2013
|
+
return update_package(args.update, project_dir=".")
|
|
2014
|
+
|
|
2015
|
+
if getattr(args, 'outdated', False):
|
|
2016
|
+
return outdated_packages(project_dir=".")
|
|
1715
2017
|
|
|
1716
2018
|
if args.remove:
|
|
1717
2019
|
return remove_package(args.remove)
|
|
@@ -1754,6 +2056,8 @@ Examples:
|
|
|
1754
2056
|
return _fmt_all_files(args.fmt_all)
|
|
1755
2057
|
if getattr(args, 'migrate', None):
|
|
1756
2058
|
return _migrate_files(args.migrate)
|
|
2059
|
+
if getattr(args, 'infer_types', None):
|
|
2060
|
+
return _infer_types_file(args.infer_types)
|
|
1757
2061
|
if getattr(args, 'compat', None):
|
|
1758
2062
|
return _compat_files(args.compat)
|
|
1759
2063
|
if getattr(args, 'init', None) is not None:
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "inscript-lang"
|
|
7
|
-
version = "1.9.
|
|
7
|
+
version = "1.9.9"
|
|
8
8
|
description = "InScript — a game-focused scripting language with 59 game modules and a bytecode VM"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = { text = "MIT" }
|
|
@@ -40,7 +40,7 @@ sys.path.insert(0, str(Path(__file__).parent))
|
|
|
40
40
|
|
|
41
41
|
HISTORY_FILE = Path.home() / ".inscript" / "history"
|
|
42
42
|
HISTORY_FILE.parent.mkdir(parents=True, exist_ok=True)
|
|
43
|
-
VERSION = "1.9.
|
|
43
|
+
VERSION = "1.9.9"
|
|
44
44
|
|
|
45
45
|
# ── ANSI colours ──────────────────────────────────────────────────────────────
|
|
46
46
|
def _c(code, text):
|
|
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
|