inscript-lang 1.9.6__tar.gz → 1.9.7__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.6 → inscript_lang-1.9.7}/PKG-INFO +1 -1
  2. {inscript_lang-1.9.6 → inscript_lang-1.9.7}/analyzer.py +29 -12
  3. {inscript_lang-1.9.6 → inscript_lang-1.9.7}/inscript.py +1 -1
  4. {inscript_lang-1.9.6 → inscript_lang-1.9.7}/inscript_lang.egg-info/PKG-INFO +1 -1
  5. {inscript_lang-1.9.6 → inscript_lang-1.9.7}/interpreter.py +50 -3
  6. {inscript_lang-1.9.6 → inscript_lang-1.9.7}/pyproject.toml +1 -1
  7. {inscript_lang-1.9.6 → inscript_lang-1.9.7}/repl.py +1 -1
  8. {inscript_lang-1.9.6 → inscript_lang-1.9.7}/stdlib_values.py +17 -1
  9. {inscript_lang-1.9.6 → inscript_lang-1.9.7}/README.md +0 -0
  10. {inscript_lang-1.9.6 → inscript_lang-1.9.7}/ast_nodes.py +0 -0
  11. {inscript_lang-1.9.6 → inscript_lang-1.9.7}/compiler.py +0 -0
  12. {inscript_lang-1.9.6 → inscript_lang-1.9.7}/environment.py +0 -0
  13. {inscript_lang-1.9.6 → inscript_lang-1.9.7}/errors.py +0 -0
  14. {inscript_lang-1.9.6 → inscript_lang-1.9.7}/inscript_fmt.py +0 -0
  15. {inscript_lang-1.9.6 → inscript_lang-1.9.7}/inscript_lang.egg-info/SOURCES.txt +0 -0
  16. {inscript_lang-1.9.6 → inscript_lang-1.9.7}/inscript_lang.egg-info/dependency_links.txt +0 -0
  17. {inscript_lang-1.9.6 → inscript_lang-1.9.7}/inscript_lang.egg-info/entry_points.txt +0 -0
  18. {inscript_lang-1.9.6 → inscript_lang-1.9.7}/inscript_lang.egg-info/requires.txt +0 -0
  19. {inscript_lang-1.9.6 → inscript_lang-1.9.7}/inscript_lang.egg-info/top_level.txt +0 -0
  20. {inscript_lang-1.9.6 → inscript_lang-1.9.7}/inscript_test.py +0 -0
  21. {inscript_lang-1.9.6 → inscript_lang-1.9.7}/lexer.py +0 -0
  22. {inscript_lang-1.9.6 → inscript_lang-1.9.7}/parser.py +0 -0
  23. {inscript_lang-1.9.6 → inscript_lang-1.9.7}/pygame_backend.py +0 -0
  24. {inscript_lang-1.9.6 → inscript_lang-1.9.7}/setup.cfg +0 -0
  25. {inscript_lang-1.9.6 → inscript_lang-1.9.7}/setup.py +0 -0
  26. {inscript_lang-1.9.6 → inscript_lang-1.9.7}/stdlib.py +0 -0
  27. {inscript_lang-1.9.6 → inscript_lang-1.9.7}/stdlib_extended.py +0 -0
  28. {inscript_lang-1.9.6 → inscript_lang-1.9.7}/stdlib_extended_2.py +0 -0
  29. {inscript_lang-1.9.6 → inscript_lang-1.9.7}/stdlib_game.py +0 -0
  30. {inscript_lang-1.9.6 → inscript_lang-1.9.7}/vm.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: inscript-lang
3
- Version: 1.9.6
3
+ Version: 1.9.7
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
@@ -65,12 +65,14 @@ T_TRANSFORM3D = InScriptType("Transform3D")
65
65
  T_TEXTURE = InScriptType("Texture")
66
66
 
67
67
  T_NEVER = InScriptType("never") # v1.8.3: bottom type — function never returns normally
68
+ T_PROMISE = InScriptType("Promise") # v1.9.7: async fn return type
68
69
 
69
70
  BUILTIN_TYPES: Dict[str, InScriptType] = {
70
71
  # canonical names
71
72
  "int": T_INT, "float": T_FLOAT, "bool": T_BOOL,
72
73
  "string": T_STRING, "void": T_VOID, "null": T_NULL, "any": T_ANY,
73
74
  "never": T_NEVER, # v1.8.3
75
+ "Promise": T_PROMISE, # v1.9.7: async fn return type
74
76
  # common aliases
75
77
  "str": T_STRING, "boolean": T_BOOL, "number": T_FLOAT,
76
78
  "nil": T_NULL, "object": T_ANY, "auto": T_ANY,
@@ -648,21 +650,36 @@ class Analyzer(Visitor):
648
650
  def visit_FunctionDecl(self, node: FunctionDecl) -> InScriptType:
649
651
  ret_type = self._resolve_type_ann(node.return_type)
650
652
 
653
+ # v1.9.7: async fn — external return type is Promise<T>, but the body
654
+ # is checked against the inner type T (what the fn actually returns).
655
+ # E.g.: `async fn f() -> string` → body checked vs `string`, external type = Promise<string>
656
+ is_async = getattr(node, 'is_async', False)
657
+ if is_async:
658
+ inner_ret_type = ret_type if ret_type not in (T_ANY, T_VOID) else T_ANY
659
+ external_ret_type = InScriptType("Promise", [inner_ret_type])
660
+ else:
661
+ inner_ret_type = ret_type
662
+ external_ret_type = ret_type
663
+
651
664
  # v1.8.4: return type inference — if no annotation and body has a single
652
- # unambiguous return type, infer it automatically
653
- if node.return_type is None and ret_type == T_ANY and node.body:
665
+ # unambiguous return type, infer it automatically (use inner type for async)
666
+ if node.return_type is None and inner_ret_type == T_ANY and node.body:
654
667
  inferred = self._infer_fn_return_type(node)
655
668
  if inferred not in (T_ANY, T_VOID, T_NULL):
656
- ret_type = inferred
669
+ inner_ret_type = inferred
670
+ if is_async:
671
+ external_ret_type = InScriptType("Promise", [inner_ret_type])
672
+ else:
673
+ external_ret_type = inner_ret_type
657
674
  # Update the hoisted symbol's type to the inferred one
658
675
  existing = self._scope.lookup(node.name)
659
676
  if existing and existing.kind == "fn":
660
- existing.type_ = ret_type
677
+ existing.type_ = external_ret_type
661
678
 
662
- # Register in current scope (if not already hoisted)
679
+ # Register in current scope using external type (Promise<T> for async)
663
680
  if not self._scope.lookup_local(node.name):
664
681
  self._define(Symbol(
665
- node.name, ret_type, kind="fn",
682
+ node.name, external_ret_type, kind="fn",
666
683
  fn_node=node, line=node.line, col=node.col
667
684
  ))
668
685
 
@@ -676,10 +693,10 @@ class Analyzer(Visitor):
676
693
 
677
694
  # Warn if function declares a non-void return type but body may not return
678
695
  if (node.return_type is not None and
679
- ret_type.name not in ("void", "nil", "any", "") and
696
+ inner_ret_type.name not in ("void", "nil", "any", "") and
680
697
  not node.is_native if hasattr(node, 'is_native') else True):
681
698
  if node.body and not self._body_always_returns(node.body):
682
- if ret_type == T_NEVER:
699
+ if inner_ret_type == T_NEVER:
683
700
  # v1.8.3: `-> never` functions MUST always throw / diverge
684
701
  self._error(
685
702
  f"Function '{node.name}' is declared '-> never' "
@@ -689,15 +706,15 @@ class Analyzer(Visitor):
689
706
  else:
690
707
  self._warn(
691
708
  "missing-return",
692
- f"Function '{node.name}' declares return type '{ret_type.name}' "
709
+ f"Function '{node.name}' declares return type '{inner_ret_type.name}' "
693
710
  f"but not all code paths return a value",
694
711
  node.line
695
712
  )
696
713
 
697
- # Analyze body in a new scope
714
+ # Analyze body in a new scope — body return type is inner (not Promise<T>)
698
715
  self._push_scope("fn")
699
716
  prev_ret = self._current_fn_return_type
700
- self._current_fn_return_type = ret_type
717
+ self._current_fn_return_type = inner_ret_type
701
718
 
702
719
  # Register parameters — mark used=True so we never warn about unused params
703
720
  for param in node.params:
@@ -714,7 +731,7 @@ class Analyzer(Visitor):
714
731
 
715
732
  self._current_fn_return_type = prev_ret
716
733
  self._pop_scope()
717
- return ret_type
734
+ return external_ret_type
718
735
 
719
736
  def _body_always_returns(self, block) -> bool:
720
737
  """Return True if every code path through block ends with a return/throw."""
@@ -24,7 +24,7 @@ from errors import (InScriptError, LexerError, ParseError,
24
24
  SemanticError, InScriptRuntimeError,
25
25
  MultiError, InScriptWarning)
26
26
 
27
- VERSION = "1.9.6"
27
+ VERSION = "1.9.7"
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"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: inscript-lang
3
- Version: 1.9.6
3
+ Version: 1.9.7
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
@@ -5,14 +5,15 @@
5
5
  # After Lexer → Parser → Analyzer pass, this is what runs InScript programs.
6
6
 
7
7
  from __future__ import annotations
8
- import math, random, time
8
+ import math, random, time, asyncio
9
9
  from typing import Any, Dict, List, Optional
10
10
 
11
11
  from ast_nodes import *
12
12
  from environment import Environment
13
13
  from stdlib_values import (
14
14
  Vec2, Vec3, Color, Rect,
15
- InScriptFunction, InScriptInstance, InScriptRange, InScriptGenerator
15
+ InScriptFunction, InScriptInstance, InScriptRange, InScriptGenerator,
16
+ InScriptCoroutine,
16
17
  )
17
18
  import stdlib as _stdlib # loads all built-in modules
18
19
  from errors import (
@@ -526,6 +527,8 @@ class Interpreter(Visitor):
526
527
  # v1.4.0: preserve generic metadata for constraint checking at call time
527
528
  fn.type_params = getattr(node, 'type_params', [])
528
529
  fn.constraints = getattr(node, 'constraints', {})
530
+ # v1.9.7: mark async functions
531
+ fn.is_async = getattr(node, 'is_async', False)
529
532
  self._env.define(node.name, fn)
530
533
  return fn
531
534
 
@@ -2319,6 +2322,33 @@ class Interpreter(Visitor):
2319
2322
 
2320
2323
  _bind_args(call_env, arg_vals, arg_names)
2321
2324
 
2325
+ # v1.9.7: async fn — wrap body execution in a Python coroutine
2326
+ if getattr(fn, 'is_async', False):
2327
+ async def _async_body():
2328
+ nonlocal result
2329
+ prev_fn2 = self._current_fn
2330
+ prev_def2 = getattr(self, '_deferred', None)
2331
+ self._deferred = []
2332
+ self._current_fn = fn
2333
+ try:
2334
+ for stmt in fn.body.body:
2335
+ self.visit(stmt)
2336
+ except ReturnSignal as r:
2337
+ result = r.value
2338
+ except PropagateSignal as sig:
2339
+ result = sig.err_val
2340
+ finally:
2341
+ for deferred_expr in reversed(self._deferred):
2342
+ try: self.visit(deferred_expr)
2343
+ except Exception: pass
2344
+ self._deferred = prev_def2 if prev_def2 is not None else []
2345
+ self._current_fn = prev_fn2
2346
+ self._env = prev_env
2347
+ self._call_depth -= 1
2348
+ self._call_stack.pop()
2349
+ return result
2350
+ return InScriptCoroutine(_async_body(), fn_name=fn.name or "<async>")
2351
+
2322
2352
  # v1.3.0: TCO trampoline — self-recursive tail calls loop instead of recurse
2323
2353
  result = None
2324
2354
  prev_fn = self._current_fn
@@ -2443,7 +2473,24 @@ class Interpreter(Visitor):
2443
2473
  return InScriptRange(start, end, inclusive=node.inclusive)
2444
2474
 
2445
2475
  def visit_AwaitExpr(self, node: AwaitExpr) -> Any:
2446
- return self.visit(node.expr) # synchronous in Phase 4
2476
+ """
2477
+ v1.9.7: True async/await.
2478
+ - InScriptCoroutine → drive via Python coroutine protocol (sync driver).
2479
+ - Plain value → passthrough (backwards compatible).
2480
+
2481
+ We use a synchronous driver (coro.send(None)) rather than asyncio.run()
2482
+ because InScript coroutine bodies contain no real Python await points —
2483
+ they are synchronous interpreter execution wrapped in `async def`.
2484
+ This also safely handles nested async calls without event-loop conflicts.
2485
+ """
2486
+ val = self.visit(node.expr)
2487
+ if isinstance(val, InScriptCoroutine):
2488
+ try:
2489
+ val.coro.send(None)
2490
+ return None # coroutine yielded unexpectedly — shouldn't happen
2491
+ except StopIteration as e:
2492
+ return e.value
2493
+ return val # plain value: passthrough
2447
2494
 
2448
2495
  def visit_SpawnExpr(self, node: SpawnExpr) -> Any:
2449
2496
  return None # Phase 6 (ECS)
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "inscript-lang"
7
- version = "1.9.6"
7
+ version = "1.9.7"
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.6"
43
+ VERSION = "1.9.7"
44
44
 
45
45
  # ── ANSI colours ──────────────────────────────────────────────────────────────
46
46
  def _c(code, text):
@@ -220,7 +220,7 @@ class Rect:
220
220
 
221
221
  class InScriptFunction:
222
222
  """A user-defined function or lambda at runtime."""
223
- def __init__(self, name, params, body, closure, is_native=False, native_fn=None, return_type=None):
223
+ def __init__(self, name, params, body, closure, is_native=False, native_fn=None, return_type=None, is_async=False):
224
224
  self.name = name
225
225
  self.params = params # List[Param] AST nodes
226
226
  self.body = body # BlockStmt AST node
@@ -228,6 +228,7 @@ class InScriptFunction:
228
228
  self.is_native = is_native
229
229
  self.native_fn = native_fn # Python callable for native fns
230
230
  self.return_type = return_type # Optional type annotation
231
+ self.is_async = is_async # v1.9.7: true for `async fn`
231
232
  self._interp = None # Set by interpreter so stdlib can call us
232
233
 
233
234
  def __repr__(self): return f"<fn {self.name}>"
@@ -239,6 +240,21 @@ class InScriptFunction:
239
240
  raise TypeError(f"InScriptFunction '{self.name}' cannot be called without an interpreter context")
240
241
 
241
242
 
243
+ class InScriptCoroutine:
244
+ """
245
+ v1.9.7: Wraps the result of calling an `async fn`.
246
+ Holds a Python coroutine that runs the function body.
247
+ Driven to completion by `await` via asyncio.
248
+ """
249
+ def __init__(self, coro, fn_name: str = "<async>"):
250
+ self.coro = coro # Python coroutine object
251
+ self.fn_name = fn_name # for repr / error messages
252
+
253
+ def __repr__(self):
254
+ return f"<coroutine {self.fn_name}>"
255
+
256
+
257
+
242
258
  class InScriptInstance:
243
259
  """An instance of a user-defined struct at runtime."""
244
260
  def __init__(self, struct_name: str, fields: Dict[str, Any]):
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