inscript-lang 1.9.8__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.
Files changed (30) hide show
  1. {inscript_lang-1.9.8 → inscript_lang-1.9.9}/PKG-INFO +1 -1
  2. {inscript_lang-1.9.8 → inscript_lang-1.9.9}/inscript.py +239 -14
  3. {inscript_lang-1.9.8 → inscript_lang-1.9.9}/inscript_lang.egg-info/PKG-INFO +1 -1
  4. {inscript_lang-1.9.8 → inscript_lang-1.9.9}/pyproject.toml +1 -1
  5. {inscript_lang-1.9.8 → inscript_lang-1.9.9}/repl.py +1 -1
  6. {inscript_lang-1.9.8 → inscript_lang-1.9.9}/README.md +0 -0
  7. {inscript_lang-1.9.8 → inscript_lang-1.9.9}/analyzer.py +0 -0
  8. {inscript_lang-1.9.8 → inscript_lang-1.9.9}/ast_nodes.py +0 -0
  9. {inscript_lang-1.9.8 → inscript_lang-1.9.9}/compiler.py +0 -0
  10. {inscript_lang-1.9.8 → inscript_lang-1.9.9}/environment.py +0 -0
  11. {inscript_lang-1.9.8 → inscript_lang-1.9.9}/errors.py +0 -0
  12. {inscript_lang-1.9.8 → inscript_lang-1.9.9}/inscript_fmt.py +0 -0
  13. {inscript_lang-1.9.8 → inscript_lang-1.9.9}/inscript_lang.egg-info/SOURCES.txt +0 -0
  14. {inscript_lang-1.9.8 → inscript_lang-1.9.9}/inscript_lang.egg-info/dependency_links.txt +0 -0
  15. {inscript_lang-1.9.8 → inscript_lang-1.9.9}/inscript_lang.egg-info/entry_points.txt +0 -0
  16. {inscript_lang-1.9.8 → inscript_lang-1.9.9}/inscript_lang.egg-info/requires.txt +0 -0
  17. {inscript_lang-1.9.8 → inscript_lang-1.9.9}/inscript_lang.egg-info/top_level.txt +0 -0
  18. {inscript_lang-1.9.8 → inscript_lang-1.9.9}/inscript_test.py +0 -0
  19. {inscript_lang-1.9.8 → inscript_lang-1.9.9}/interpreter.py +0 -0
  20. {inscript_lang-1.9.8 → inscript_lang-1.9.9}/lexer.py +0 -0
  21. {inscript_lang-1.9.8 → inscript_lang-1.9.9}/parser.py +0 -0
  22. {inscript_lang-1.9.8 → inscript_lang-1.9.9}/pygame_backend.py +0 -0
  23. {inscript_lang-1.9.8 → inscript_lang-1.9.9}/setup.cfg +0 -0
  24. {inscript_lang-1.9.8 → inscript_lang-1.9.9}/setup.py +0 -0
  25. {inscript_lang-1.9.8 → inscript_lang-1.9.9}/stdlib.py +0 -0
  26. {inscript_lang-1.9.8 → inscript_lang-1.9.9}/stdlib_extended.py +0 -0
  27. {inscript_lang-1.9.8 → inscript_lang-1.9.9}/stdlib_extended_2.py +0 -0
  28. {inscript_lang-1.9.8 → inscript_lang-1.9.9}/stdlib_game.py +0 -0
  29. {inscript_lang-1.9.8 → inscript_lang-1.9.9}/stdlib_values.py +0 -0
  30. {inscript_lang-1.9.8 → inscript_lang-1.9.9}/vm.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: inscript-lang
3
- Version: 1.9.8
3
+ Version: 1.9.9
4
4
  Summary: InScript — a game-focused scripting language with 59 game modules and a bytecode VM
5
5
  Author: Shreyasi Sarkar
6
6
  License: MIT
@@ -24,7 +24,7 @@ from errors import (InScriptError, LexerError, ParseError,
24
24
  SemanticError, InScriptRuntimeError,
25
25
  MultiError, InScriptWarning)
26
26
 
27
- VERSION = "1.9.8"
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 install_package(pkg_name: str) -> int:
38
- """Download and install an InScript package to ~/.inscript/packages/"""
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 os.path.exists(pkg_dir):
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
- print(f"[InScript] Fetching registry...")
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: you can also install manually by placing .ins files in {PACKAGES_DIR}/",
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
- zip_url = pkg_info.get("url")
66
- version = pkg_info.get("version", "?")
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}@{version}...")
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}@{version} installed to {pkg_dir}")
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):
@@ -1707,8 +1917,12 @@ Examples:
1707
1917
  parser.add_argument("--watch", action="store_true",
1708
1918
  help="Watch file for changes and rerun: inscript --watch game.ins")
1709
1919
  parser.add_argument("--version", action="store_true", help="Print version and exit")
1710
- parser.add_argument("--install", metavar="PKG",
1711
- help="Install a package: inscript install math-utils")
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")
1712
1926
  parser.add_argument("--remove", metavar="PKG",
1713
1927
  help="Remove a package: inscript --remove math-utils")
1714
1928
  parser.add_argument("--packages", action="store_true",
@@ -1787,8 +2001,19 @@ Examples:
1787
2001
  if args.packages:
1788
2002
  return list_packages()
1789
2003
 
1790
- if args.install:
1791
- return install_package(args.install)
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=".")
1792
2017
 
1793
2018
  if args.remove:
1794
2019
  return remove_package(args.remove)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: inscript-lang
3
- Version: 1.9.8
3
+ Version: 1.9.9
4
4
  Summary: InScript — a game-focused scripting language with 59 game modules and a bytecode VM
5
5
  Author: Shreyasi Sarkar
6
6
  License: MIT
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "inscript-lang"
7
- version = "1.9.8"
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.8"
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