inscript-lang 2.12.0__tar.gz → 3.0.0__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 (39) hide show
  1. {inscript_lang-2.12.0 → inscript_lang-3.0.0}/PKG-INFO +1 -1
  2. {inscript_lang-2.12.0 → inscript_lang-3.0.0}/export_pipeline.py +112 -1
  3. {inscript_lang-2.12.0 → inscript_lang-3.0.0}/inscript.py +63 -1
  4. {inscript_lang-2.12.0 → inscript_lang-3.0.0}/inscript_lang.egg-info/PKG-INFO +1 -1
  5. {inscript_lang-2.12.0 → inscript_lang-3.0.0}/setup.py +1 -0
  6. {inscript_lang-2.12.0 → inscript_lang-3.0.0}/stdlib.py +43 -0
  7. {inscript_lang-2.12.0 → inscript_lang-3.0.0}/stdlib_game.py +40 -0
  8. inscript_lang-3.0.0/studio_bridge.py +741 -0
  9. inscript_lang-2.12.0/studio_bridge.py +0 -239
  10. {inscript_lang-2.12.0 → inscript_lang-3.0.0}/README.md +0 -0
  11. {inscript_lang-2.12.0 → inscript_lang-3.0.0}/analyzer.py +0 -0
  12. {inscript_lang-2.12.0 → inscript_lang-3.0.0}/ast_nodes.py +0 -0
  13. {inscript_lang-2.12.0 → inscript_lang-3.0.0}/compiler.py +0 -0
  14. {inscript_lang-2.12.0 → inscript_lang-3.0.0}/environment.py +0 -0
  15. {inscript_lang-2.12.0 → inscript_lang-3.0.0}/errors.py +0 -0
  16. {inscript_lang-2.12.0 → inscript_lang-3.0.0}/hot_reload.py +0 -0
  17. {inscript_lang-2.12.0 → inscript_lang-3.0.0}/inscript_dap.py +0 -0
  18. {inscript_lang-2.12.0 → inscript_lang-3.0.0}/inscript_fmt.py +0 -0
  19. {inscript_lang-2.12.0 → inscript_lang-3.0.0}/inscript_lang.egg-info/SOURCES.txt +0 -0
  20. {inscript_lang-2.12.0 → inscript_lang-3.0.0}/inscript_lang.egg-info/dependency_links.txt +0 -0
  21. {inscript_lang-2.12.0 → inscript_lang-3.0.0}/inscript_lang.egg-info/entry_points.txt +0 -0
  22. {inscript_lang-2.12.0 → inscript_lang-3.0.0}/inscript_lang.egg-info/requires.txt +0 -0
  23. {inscript_lang-2.12.0 → inscript_lang-3.0.0}/inscript_lang.egg-info/top_level.txt +0 -0
  24. {inscript_lang-2.12.0 → inscript_lang-3.0.0}/inscript_studio_api.py +0 -0
  25. {inscript_lang-2.12.0 → inscript_lang-3.0.0}/inscript_test.py +0 -0
  26. {inscript_lang-2.12.0 → inscript_lang-3.0.0}/interpreter.py +0 -0
  27. {inscript_lang-2.12.0 → inscript_lang-3.0.0}/lexer.py +0 -0
  28. {inscript_lang-2.12.0 → inscript_lang-3.0.0}/parser.py +0 -0
  29. {inscript_lang-2.12.0 → inscript_lang-3.0.0}/pygame_backend.py +0 -0
  30. {inscript_lang-2.12.0 → inscript_lang-3.0.0}/pyproject.toml +0 -0
  31. {inscript_lang-2.12.0 → inscript_lang-3.0.0}/repl.py +0 -0
  32. {inscript_lang-2.12.0 → inscript_lang-3.0.0}/scene_tree.py +0 -0
  33. {inscript_lang-2.12.0 → inscript_lang-3.0.0}/setup.cfg +0 -0
  34. {inscript_lang-2.12.0 → inscript_lang-3.0.0}/stdlib_assets.py +0 -0
  35. {inscript_lang-2.12.0 → inscript_lang-3.0.0}/stdlib_extended.py +0 -0
  36. {inscript_lang-2.12.0 → inscript_lang-3.0.0}/stdlib_extended_2.py +0 -0
  37. {inscript_lang-2.12.0 → inscript_lang-3.0.0}/stdlib_values.py +0 -0
  38. {inscript_lang-2.12.0 → inscript_lang-3.0.0}/studio_readiness.py +0 -0
  39. {inscript_lang-2.12.0 → inscript_lang-3.0.0}/vm.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: inscript-lang
3
- Version: 2.12.0
3
+ Version: 3.0.0
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
@@ -42,7 +42,7 @@ MANIFEST_FILE = "inscript.toml"
42
42
  ENTRY_DEFAULT = os.path.join("src", "main.ins")
43
43
  BUILD_MANIFEST = "build.toml"
44
44
 
45
- VALID_TARGETS = ("desktop", "web", "android", "ibc")
45
+ VALID_TARGETS = ("desktop", "web", "android", "ios", "ibc")
46
46
 
47
47
  # Pyodide CDN base used in web builds
48
48
  PYODIDE_CDN = "https://cdn.jsdelivr.net/pyodide/v0.27.0/full/"
@@ -645,3 +645,114 @@ def run_build(target: str, project_dir: str = ".") -> int:
645
645
  for e in r.errors:
646
646
  print(f"[build] {e}", file=sys.stderr)
647
647
  return 0 if r.success else 1
648
+
649
+
650
+ # ─────────────────────────────────────────────────────────────────────────────
651
+ # v2.13.0: Target: iOS
652
+ # ─────────────────────────────────────────────────────────────────────────────
653
+
654
+ def build_ios(project_dir: str = ".") -> BuildResult:
655
+ """
656
+ Build an iOS app via BeeWare Briefcase.
657
+ Requires: macOS + Xcode + pip install briefcase
658
+
659
+ Output layout:
660
+ build/ios/
661
+ pyproject.toml — Briefcase config
662
+ app.py — InScript iOS entry point
663
+ build.toml — build manifest
664
+ """
665
+ import time, subprocess
666
+ t0 = time.perf_counter()
667
+ errors: List[str] = []
668
+ artifacts: List[str] = []
669
+
670
+ manifest = _read_manifest(project_dir)
671
+ pkg = manifest.get("package", {})
672
+ version = pkg.get("version", "0.0.0")
673
+ name = pkg.get("name", "mygame")
674
+ entry = pkg.get("entry", ENTRY_DEFAULT)
675
+ bundle = pkg.get("bundle", f"dev.inscript.{name}")
676
+
677
+ out_dir = os.path.join(project_dir, BUILD_DIR, "ios")
678
+ os.makedirs(out_dir, exist_ok=True)
679
+
680
+ bp_path = os.path.join(out_dir, "pyproject.toml")
681
+ with open(bp_path, "w") as f:
682
+ f.write(textwrap.dedent(f"""\
683
+ [tool.briefcase]
684
+ project_name = "{name}"
685
+ bundle = "{bundle}"
686
+ version = "{version}"
687
+ description = "Built with InScript"
688
+ license = "MIT"
689
+
690
+ [tool.briefcase.app.{name}]
691
+ formal_name = "{name}"
692
+ description = "Built with InScript"
693
+ sources = ["."]
694
+ requires = ["inscript-lang"]
695
+
696
+ [tool.briefcase.app.{name}.iOS]
697
+ requires = ["toga-iOS"]
698
+ """))
699
+ artifacts.append(bp_path)
700
+
701
+ app_py = os.path.join(out_dir, "app.py")
702
+ with open(app_py, "w") as f:
703
+ f.write(textwrap.dedent(f"""\
704
+ # InScript iOS entry — generated by inscript build --target ios
705
+ import sys
706
+ try:
707
+ import inscript
708
+ inscript.main(["{entry}"])
709
+ except Exception as e:
710
+ print(f"InScript launch error: {{e}}", file=sys.stderr)
711
+ """))
712
+ artifacts.append(app_py)
713
+
714
+ for sub in ("src", "assets", "scenes"):
715
+ s = os.path.join(project_dir, sub)
716
+ d = os.path.join(out_dir, sub)
717
+ if os.path.isdir(s):
718
+ if os.path.exists(d): shutil.rmtree(d)
719
+ shutil.copytree(s, d)
720
+
721
+ from inscript import VERSION as _IV
722
+ bm = _write_build_manifest(out_dir, {
723
+ "target": "ios", "version": version, "entry": entry,
724
+ "inscript_version": _IV, "bundle": bundle,
725
+ "artifacts": {"pyproject": "pyproject.toml", "app": "app.py"},
726
+ "asset_hashes": {},
727
+ })
728
+ artifacts.append(bm)
729
+
730
+ # Check platform and briefcase
731
+ if sys.platform != "darwin":
732
+ msg = "iOS builds require macOS. Current platform: " + sys.platform
733
+ errors.append(msg)
734
+ print(f"[build:ios] ⚠ {msg}")
735
+ elapsed = (time.perf_counter() - t0) * 1000
736
+ return BuildResult("ios", False, out_dir, artifacts, errors, elapsed)
737
+
738
+ briefcase_ok = shutil.which("briefcase") is not None
739
+ if not briefcase_ok:
740
+ msg = ("BeeWare Briefcase not installed. "
741
+ "Install: pip install briefcase "
742
+ "Then: cd build/ios && briefcase create iOS && briefcase build iOS")
743
+ errors.append(msg)
744
+ print(f"[build:ios] ⚠ {msg}")
745
+ elapsed = (time.perf_counter() - t0) * 1000
746
+ return BuildResult("ios", False, out_dir, artifacts, errors, elapsed)
747
+
748
+ try:
749
+ subprocess.run([sys.executable, "-m", "briefcase", "create", "iOS"],
750
+ cwd=out_dir, check=True)
751
+ subprocess.run([sys.executable, "-m", "briefcase", "build", "iOS"],
752
+ cwd=out_dir, check=True)
753
+ print(f"[build:ios] ✅ iOS app built → {out_dir}")
754
+ except subprocess.CalledProcessError as e:
755
+ errors.append(f"briefcase failed: {e}")
756
+
757
+ elapsed = (time.perf_counter() - t0) * 1000
758
+ return BuildResult("ios", len(errors) == 0, out_dir, artifacts, errors, elapsed)
@@ -24,7 +24,7 @@ from errors import (InScriptError, LexerError, ParseError,
24
24
  SemanticError, InScriptRuntimeError,
25
25
  MultiError, InScriptWarning)
26
26
 
27
- VERSION = "2.12.0"
27
+ VERSION = "3.0.0"
28
28
 
29
29
  MANIFEST_FILENAME = "inscript.toml"
30
30
  LOCK_FILENAME = "inscript.lock"
@@ -2752,6 +2752,17 @@ Examples:
2752
2752
  help="v2.12.0: Start Electron bridge JSON-RPC server")
2753
2753
  parser.add_argument("--bridge-port", type=int, default=8765,
2754
2754
  help="v2.12.0: Port for --studio-bridge (default: 8765)")
2755
+ # v3.0.0: Studio + Visual Scripting
2756
+ parser.add_argument("--studio", action="store_true",
2757
+ help="v3.0.0: Launch InScript Studio web IDE in browser")
2758
+ parser.add_argument("--studio-port", type=int, default=8080,
2759
+ help="v3.0.0: Port for Studio web app (default: 8080)")
2760
+ parser.add_argument("--visual-compile", metavar="FILE",
2761
+ help="v3.0.0: Compile a .vins visual script to .ins source")
2762
+ parser.add_argument("--vins-output", metavar="FILE",
2763
+ help="v3.0.0: Output path for --visual-compile (default: <input>.ins)")
2764
+ parser.add_argument("--vins-template", metavar="NAME",
2765
+ help="v3.0.0: Generate a .vins template for visual scripting")
2755
2766
  parser.add_argument("--lsp", action="store_true",
2756
2767
  help="Start the Language Server (requires: pip install pygls)")
2757
2768
  parser.add_argument("--game", action="store_true",
@@ -3067,6 +3078,57 @@ Examples:
3067
3078
  bridge.stop()
3068
3079
  return 0
3069
3080
 
3081
+ # ── v3.0.0 Studio + Visual Scripting ──────────────────────────────────
3082
+ if getattr(args, 'studio', False):
3083
+ import time as _time
3084
+ studio_port = getattr(args, 'studio_port', 8080)
3085
+ bridge_port = getattr(args, 'bridge_port', 8765)
3086
+ proj_dir = getattr(args, 'project_dir', '.') or '.'
3087
+ from studio_app import StudioApp
3088
+ app_s = StudioApp(proj_dir, studio_port=studio_port, bridge_port=bridge_port)
3089
+ app_s.start()
3090
+ url = app_s.url
3091
+ print(f"[InScript Studio] ✅ Running at {url}")
3092
+ print(f"[InScript Studio] Project: {os.path.abspath(proj_dir)}")
3093
+ print(f"[InScript Studio] Ctrl+C to stop")
3094
+ # Open browser
3095
+ try:
3096
+ import webbrowser as _wb
3097
+ _wb.open(url)
3098
+ except Exception:
3099
+ pass
3100
+ try:
3101
+ while True: _time.sleep(1)
3102
+ except KeyboardInterrupt:
3103
+ app_s.stop()
3104
+ print(f"[InScript Studio] Stopped.")
3105
+ return 0
3106
+
3107
+ if getattr(args, 'visual_compile', None) is not None:
3108
+ from visual_script import compile_file
3109
+ vins_path = args.visual_compile
3110
+ out_path = getattr(args, 'vins_output', None)
3111
+ if not os.path.isfile(vins_path):
3112
+ print(f"[visual] File not found: {vins_path}", file=sys.stderr); return 1
3113
+ try:
3114
+ result_path = compile_file(vins_path, out_path)
3115
+ print(f"[visual] Written to: {result_path}")
3116
+ except Exception as _ve:
3117
+ print(f"[visual] Compile error: {_ve}", file=sys.stderr); return 1
3118
+ return 0
3119
+
3120
+ if getattr(args, 'vins_template', None) is not None:
3121
+ import json as _j
3122
+ from visual_script import make_template
3123
+ name = args.vins_template
3124
+ out_path = f"{name}.vins"
3125
+ tmpl = make_template(name)
3126
+ with open(out_path, "w") as f:
3127
+ _j.dump(tmpl, f, indent=2)
3128
+ print(f"[visual] Template written to {out_path}")
3129
+ print(f"[visual] Compile with: inscript --visual-compile {out_path}")
3130
+ return 0
3131
+
3070
3132
  # v1.9.1: --no-typecheck is deprecated — emit a warning and honour it
3071
3133
  if getattr(args, 'no_typecheck', False):
3072
3134
  print(
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: inscript-lang
3
- Version: 2.12.0
3
+ Version: 3.0.0
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
@@ -57,6 +57,7 @@ setup(
57
57
  # ── v2.7.0+ runtime modules ───────────────────────────────────────────
58
58
  "scene_tree", "hot_reload", "export_pipeline",
59
59
  "studio_bridge", "inscript_studio_api", "studio_readiness",
60
+ "visual_script", "studio_app", "vins_editor",
60
61
  ],
61
62
  package_data = {"": ["examples/*.ins", "lsp/*.py", "*.md"]},
62
63
  install_requires = [],
@@ -1163,3 +1163,46 @@ try:
1163
1163
  import stdlib_assets # noqa: F401
1164
1164
  except Exception as _e4:
1165
1165
  import sys; print(f'[stdlib_assets load error] {_e4}', file=sys.stderr)
1166
+
1167
+ # ── Studio IPC (v3.0.0): allows subprocess games to publish scene state ──────
1168
+ try:
1169
+ from studio_bridge import _IPC_STATE_FILE, _ipc_write_scene
1170
+
1171
+ def _studio_publish_scene(scene_manager_or_dict=None):
1172
+ """
1173
+ Called from .ins: import "studio_ipc" as ipc; ipc.publish_scene()
1174
+ Writes current scene state to the IPC file so Studio can inspect it.
1175
+ """
1176
+ import json as _j
1177
+ data = {"nodes": [], "count": 0, "source": "ipc"}
1178
+ if scene_manager_or_dict is not None:
1179
+ try:
1180
+ from scene_tree import SceneManager, SceneTree
1181
+ if hasattr(scene_manager_or_dict, 'current'):
1182
+ sm = scene_manager_or_dict
1183
+ if sm.current:
1184
+ nodes = []
1185
+ def _walk(inst, parent=None):
1186
+ nodes.append({
1187
+ "name": inst.name,
1188
+ "blueprint": inst.blueprint.name,
1189
+ "parent": parent,
1190
+ "children": [c.name for c in inst.get_children()],
1191
+ "props": {k: str(v) for k, v in inst._props.items()
1192
+ if not callable(v)},
1193
+ })
1194
+ for child in inst.get_children():
1195
+ _walk(child, inst.name)
1196
+ _walk(sm.current.root)
1197
+ data = {"nodes": nodes, "count": len(nodes), "source": "ipc"}
1198
+ except Exception:
1199
+ pass
1200
+ _ipc_write_scene(data)
1201
+
1202
+ from stdlib import register_module as _rm
1203
+ _rm("studio_ipc", {
1204
+ "publish_scene": _studio_publish_scene,
1205
+ "ipc_file": _IPC_STATE_FILE,
1206
+ })
1207
+ except Exception as _se:
1208
+ pass
@@ -1121,6 +1121,36 @@ class _InputManager:
1121
1121
  self._key_state = {} # key -> (pressed_this_frame, held, released_this_frame)
1122
1122
  self._mouse_x = 0.0; self._mouse_y = 0.0
1123
1123
  self._mouse_buttons = {}
1124
+ # v2.13.0: headless emulation — inject key/mouse state without pygame
1125
+ self._emulated_keys: dict = {} # key_name -> bool (held)
1126
+ self._emulated_mouse: tuple = (0.0, 0.0)
1127
+ self._emulated_buttons: dict = {}
1128
+ self._headless = False # set True when pygame unavailable
1129
+
1130
+ def _try_pygame(self) -> bool:
1131
+ try:
1132
+ import pygame
1133
+ return pygame.get_init()
1134
+ except Exception:
1135
+ return False
1136
+
1137
+ # v2.13.0: headless injection API
1138
+ def emulate_key(self, key: str, held: bool = True):
1139
+ """Inject a key state for headless/Studio preview mode."""
1140
+ self._emulated_keys[key.lower()] = bool(held)
1141
+ self._headless = True
1142
+
1143
+ def emulate_mouse(self, x: float, y: float, buttons: dict | None = None):
1144
+ """Inject mouse position and button state."""
1145
+ self._emulated_mouse = (float(x), float(y))
1146
+ self._emulated_buttons = buttons or {}
1147
+ self._headless = True
1148
+
1149
+ def clear_emulation(self):
1150
+ """Clear all emulated input state."""
1151
+ self._emulated_keys.clear()
1152
+ self._emulated_buttons.clear()
1153
+ self._headless = False
1124
1154
 
1125
1155
  def map(self, action, keys=None, axes=None, gamepad=None, gamepad_axis=None):
1126
1156
  self._actions[action] = {
@@ -1129,6 +1159,9 @@ class _InputManager:
1129
1159
  }
1130
1160
 
1131
1161
  def _is_key_down(self, key):
1162
+ # v2.13.0: headless emulation takes priority over pygame
1163
+ if self._headless or self._emulated_keys:
1164
+ return self._emulated_keys.get(key.lower(), False)
1132
1165
  try:
1133
1166
  import pygame
1134
1167
  kmap = {
@@ -1169,12 +1202,19 @@ class _InputManager:
1169
1202
  return max(-1.0, min(1.0, value))
1170
1203
 
1171
1204
  def mouse_pos(self):
1205
+ # v2.13.0: headless emulation
1206
+ if self._headless or self._emulated_mouse != (0.0, 0.0):
1207
+ x, y = self._emulated_mouse
1208
+ return {"x": float(x), "y": float(y)}
1172
1209
  try:
1173
1210
  import pygame; x, y = pygame.mouse.get_pos(); return {"x": float(x), "y": float(y)}
1174
1211
  except Exception:
1175
1212
  return {"x": self._mouse_x, "y": self._mouse_y}
1176
1213
 
1177
1214
  def mouse_pressed(self, button=0):
1215
+ # v2.13.0: headless emulation
1216
+ if self._headless or self._emulated_buttons:
1217
+ return self._emulated_buttons.get(int(button), False)
1178
1218
  try:
1179
1219
  import pygame; return bool(pygame.mouse.get_pressed()[int(button)])
1180
1220
  except Exception: