inscript-lang 1.7.4__tar.gz → 1.8.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.
Files changed (30) hide show
  1. {inscript_lang-1.7.4 → inscript_lang-1.8.2}/PKG-INFO +1 -1
  2. {inscript_lang-1.7.4 → inscript_lang-1.8.2}/analyzer.py +196 -7
  3. {inscript_lang-1.7.4 → inscript_lang-1.8.2}/ast_nodes.py +9 -3
  4. {inscript_lang-1.7.4 → inscript_lang-1.8.2}/inscript.py +1 -1
  5. {inscript_lang-1.7.4 → inscript_lang-1.8.2}/inscript_lang.egg-info/PKG-INFO +1 -1
  6. {inscript_lang-1.7.4 → inscript_lang-1.8.2}/interpreter.py +7 -3
  7. {inscript_lang-1.7.4 → inscript_lang-1.8.2}/parser.py +40 -12
  8. {inscript_lang-1.7.4 → inscript_lang-1.8.2}/pyproject.toml +1 -1
  9. {inscript_lang-1.7.4 → inscript_lang-1.8.2}/repl.py +1 -1
  10. {inscript_lang-1.7.4 → inscript_lang-1.8.2}/README.md +0 -0
  11. {inscript_lang-1.7.4 → inscript_lang-1.8.2}/compiler.py +0 -0
  12. {inscript_lang-1.7.4 → inscript_lang-1.8.2}/environment.py +0 -0
  13. {inscript_lang-1.7.4 → inscript_lang-1.8.2}/errors.py +0 -0
  14. {inscript_lang-1.7.4 → inscript_lang-1.8.2}/inscript_fmt.py +0 -0
  15. {inscript_lang-1.7.4 → inscript_lang-1.8.2}/inscript_lang.egg-info/SOURCES.txt +0 -0
  16. {inscript_lang-1.7.4 → inscript_lang-1.8.2}/inscript_lang.egg-info/dependency_links.txt +0 -0
  17. {inscript_lang-1.7.4 → inscript_lang-1.8.2}/inscript_lang.egg-info/entry_points.txt +0 -0
  18. {inscript_lang-1.7.4 → inscript_lang-1.8.2}/inscript_lang.egg-info/requires.txt +0 -0
  19. {inscript_lang-1.7.4 → inscript_lang-1.8.2}/inscript_lang.egg-info/top_level.txt +0 -0
  20. {inscript_lang-1.7.4 → inscript_lang-1.8.2}/inscript_test.py +0 -0
  21. {inscript_lang-1.7.4 → inscript_lang-1.8.2}/lexer.py +0 -0
  22. {inscript_lang-1.7.4 → inscript_lang-1.8.2}/pygame_backend.py +0 -0
  23. {inscript_lang-1.7.4 → inscript_lang-1.8.2}/setup.cfg +0 -0
  24. {inscript_lang-1.7.4 → inscript_lang-1.8.2}/setup.py +0 -0
  25. {inscript_lang-1.7.4 → inscript_lang-1.8.2}/stdlib.py +0 -0
  26. {inscript_lang-1.7.4 → inscript_lang-1.8.2}/stdlib_extended.py +0 -0
  27. {inscript_lang-1.7.4 → inscript_lang-1.8.2}/stdlib_extended_2.py +0 -0
  28. {inscript_lang-1.7.4 → inscript_lang-1.8.2}/stdlib_game.py +0 -0
  29. {inscript_lang-1.7.4 → inscript_lang-1.8.2}/stdlib_values.py +0 -0
  30. {inscript_lang-1.7.4 → inscript_lang-1.8.2}/vm.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: inscript-lang
3
- Version: 1.7.4
3
+ Version: 1.8.2
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
@@ -87,6 +87,45 @@ def array_type(elem: InScriptType) -> InScriptType:
87
87
  def dict_type(key: InScriptType, val: InScriptType) -> InScriptType:
88
88
  return InScriptType("Dict", [key, val])
89
89
 
90
+ def union_type(*members: InScriptType) -> InScriptType:
91
+ """v1.8.1: Create a Union type from two or more member types.
92
+ Flattens nested unions; deduplicates; single-member unions collapse."""
93
+ flat = []
94
+ seen = set()
95
+ for m in members:
96
+ if m.name == "Union":
97
+ for p in m.params:
98
+ if p not in seen:
99
+ flat.append(p); seen.add(p)
100
+ else:
101
+ if m not in seen:
102
+ flat.append(m); seen.add(m)
103
+ if len(flat) == 1:
104
+ return flat[0]
105
+ return InScriptType("Union", flat)
106
+
107
+ def optional_type(inner: InScriptType) -> InScriptType:
108
+ """v1.8.1: `T?` is sugar for `T | nil`."""
109
+ return union_type(inner, T_NULL)
110
+
111
+ def union_members(t: InScriptType) -> list:
112
+ """v1.8.1: Return list of member types (works on non-unions too)."""
113
+ return t.params if t.name == "Union" else [t]
114
+
115
+ def literal_type(value: str) -> InScriptType:
116
+ """v1.8.2: A string-literal type like `"left"` or `"right"`."""
117
+ return InScriptType("__literal__", [value]) # params[0] is the string value
118
+
119
+ def fn_type(param_types: list, return_type: InScriptType) -> InScriptType:
120
+ """v1.8.2: A function type `fn(int,string)->bool`."""
121
+ return InScriptType("__fn__", param_types + [return_type])
122
+
123
+ def is_literal_type(t: InScriptType) -> bool:
124
+ return t.name == "__literal__"
125
+
126
+ def literal_value(t: InScriptType) -> str:
127
+ return t.params[0] if t.params else ""
128
+
90
129
  def is_numeric(t: InScriptType) -> bool:
91
130
  return t in (T_INT, T_FLOAT)
92
131
 
@@ -104,8 +143,36 @@ def types_compatible(expected: InScriptType, got: InScriptType) -> bool:
104
143
  if expected == got: return True
105
144
  # int can widen to float
106
145
  if expected == T_FLOAT and got == T_INT: return True
107
- # null can be assigned to any type
108
- if got == T_NULL: return True
146
+ # null/nil can be assigned to any nullable or union containing nil
147
+ if got == T_NULL:
148
+ if expected == T_NULL: return True
149
+ if expected.name == "Union" and T_NULL in expected.params: return True
150
+ return False
151
+ # v1.8.1: if expected is a Union, got must be a member (or compatible with one)
152
+ if expected.name == "Union":
153
+ return any(types_compatible(m, got) for m in expected.params)
154
+ # v1.8.1: if got is a Union, every member must be compatible with expected
155
+ if got.name == "Union":
156
+ return all(types_compatible(expected, m) for m in got.params)
157
+ # v1.8.2: string literal type — `"left"` is compatible with string
158
+ if is_literal_type(expected):
159
+ # Expected a specific literal: got must be the same literal
160
+ if is_literal_type(got):
161
+ return literal_value(expected) == literal_value(got)
162
+ # A plain string variable can fill a literal slot (runtime check)
163
+ return got == T_STRING
164
+ if is_literal_type(got):
165
+ # A literal can always fill a plain string slot
166
+ if expected == T_STRING: return True
167
+ # A literal can fill a Union<string, ...> slot
168
+ if expected.name == "Union":
169
+ return any(types_compatible(m, got) for m in expected.params)
170
+ return False
171
+ # v1.8.2: fn types — compatible if same signature, or got is T_ANY/Function (lambda)
172
+ if expected.name == "__fn__":
173
+ if got == T_ANY: return True
174
+ if got.name in ("Function", "__fn__"): return True
175
+ return False
109
176
  # Array<X> compatible with Array<any> and vice versa
110
177
  if expected.name == "Array" and got.name == "Array": return True
111
178
  # Dict<K,V> compatible with Dict<any,any>
@@ -199,6 +266,7 @@ class Analyzer(Visitor):
199
266
  self._in_scene: bool = False
200
267
  self._in_match_arm: bool = False
201
268
  self._struct_defs: Dict[str, StructDecl] = {}
269
+ self._type_aliases: Dict[str, "InScriptType"] = {} # v1.8.2: registered aliases
202
270
 
203
271
  # Pre-register built-in global functions
204
272
  self._register_builtins()
@@ -291,9 +359,33 @@ class Analyzer(Visitor):
291
359
  v = self._resolve_type_ann(ann.generics[0]) if ann.generics else T_ANY
292
360
  return dict_type(k, v)
293
361
 
362
+ # v1.8.1: `T?` — nullable shorthand
363
+ if ann.is_nullable or ann.name == "Optional":
364
+ inner = self._resolve_type_ann(ann.generics[0]) if ann.generics else T_ANY
365
+ return optional_type(inner)
366
+
367
+ # v1.8.1: `A | B | C` — union type
368
+ if ann.name == "Union":
369
+ members = [self._resolve_type_ann(g) for g in ann.generics]
370
+ return union_type(*members)
371
+
372
+ # v1.8.2: string literal type — `"left"`
373
+ if ann.name == "__literal__":
374
+ return literal_type(ann.literal_value or "")
375
+
376
+ # v1.8.2: fn type — `fn(int) -> bool`
377
+ if ann.name == "__fn__":
378
+ params = [self._resolve_type_ann(p) for p in (ann.fn_params or [])]
379
+ ret = self._resolve_type_ann(ann.fn_return) if ann.fn_return else T_ANY
380
+ return fn_type(params, ret)
381
+
294
382
  if ann.name in BUILTIN_TYPES:
295
383
  return BUILTIN_TYPES[ann.name]
296
384
 
385
+ # v1.8.2: registered type aliases
386
+ if ann.name in self._type_aliases:
387
+ return self._type_aliases[ann.name]
388
+
297
389
  # Check user-defined structs
298
390
  if ann.name in self._struct_defs:
299
391
  return InScriptType(ann.name)
@@ -428,7 +520,9 @@ class Analyzer(Visitor):
428
520
  return self._scope.symbols
429
521
 
430
522
  def _hoist_top_level(self, program: Program):
431
- """Register structs, scenes, and top-level functions before checking bodies."""
523
+ """Register structs, scenes, top-level functions, and type aliases before checking bodies."""
524
+ from ast_nodes import TypeAliasDecl
525
+ # First sub-pass: register type aliases and structs (order-independent)
432
526
  for node in program.body:
433
527
  if isinstance(node, StructDecl):
434
528
  self._struct_defs[node.name] = node
@@ -437,7 +531,21 @@ class Analyzer(Visitor):
437
531
  kind="struct", struct_node=node,
438
532
  line=node.line, col=node.col
439
533
  )
440
- elif isinstance(node, FunctionDecl):
534
+ elif isinstance(node, TypeAliasDecl):
535
+ # v1.8.2: register alias immediately so functions can use it
536
+ type_ann = getattr(node, 'type_ann', None)
537
+ if type_ann is not None:
538
+ resolved = self._resolve_type_ann(type_ann)
539
+ else:
540
+ resolved = BUILTIN_TYPES.get(node.target, InScriptType(node.target))
541
+ self._type_aliases[node.name] = resolved
542
+ self._scope.symbols[node.name] = Symbol(
543
+ node.name, resolved, kind="type",
544
+ line=node.line, col=node.col
545
+ )
546
+ # Second sub-pass: hoist functions, scenes, enums (can now reference aliases)
547
+ for node in program.body:
548
+ if isinstance(node, FunctionDecl):
441
549
  ret = self._resolve_type_ann(node.return_type)
442
550
  self._scope.symbols[node.name] = Symbol(
443
551
  node.name, ret, kind="fn", fn_node=node,
@@ -451,7 +559,7 @@ class Analyzer(Visitor):
451
559
  elif isinstance(node, EnumDecl):
452
560
  self._scope.symbols[node.name] = Symbol(
453
561
  node.name, InScriptType(node.name), kind="enum",
454
- fn_node=node, # store EnumDecl for exhaustiveness checks
562
+ fn_node=node,
455
563
  line=node.line, col=node.col
456
564
  )
457
565
 
@@ -649,6 +757,19 @@ class Analyzer(Visitor):
649
757
  )
650
758
  return enum_type
651
759
 
760
+ def visit_TypeAliasDecl(self, node) -> InScriptType:
761
+ """v1.8.2: Register a type alias so annotations can reference it by name."""
762
+ if hasattr(node, 'type_ann') and node.type_ann is not None:
763
+ resolved = self._resolve_type_ann(node.type_ann)
764
+ else:
765
+ # Legacy alias with only a target string
766
+ resolved = BUILTIN_TYPES.get(node.target, InScriptType(node.target))
767
+ self._type_aliases[node.name] = resolved
768
+ # Also register in scope so the name is "used" and not flagged undefined
769
+ self._scope.symbols[node.name] = Symbol(node.name, resolved, kind="type",
770
+ line=node.line, col=node.col)
771
+ return T_VOID
772
+
652
773
  def visit_ImportDecl(self, node: ImportDecl) -> InScriptType:
653
774
  """Register imported symbols so the type checker knows about them."""
654
775
  try:
@@ -727,6 +848,36 @@ class Analyzer(Visitor):
727
848
  self._error("'continue' outside of loop", node.line, node.col)
728
849
  return T_VOID
729
850
 
851
+ def _extract_typeof_narrowing(self, condition) -> tuple:
852
+ """v1.8.1: If condition is `typeof(x) == "T"` return (var_name, narrowed_type).
853
+ Returns (None, None) if the condition is not that pattern."""
854
+ if not (hasattr(condition, 'op') and condition.op == "=="):
855
+ return (None, None)
856
+ left, right = condition.left, condition.right
857
+ # Support both sides for the string literal
858
+ if (hasattr(right, 'args') and isinstance(getattr(right, 'callee', None), object)
859
+ and hasattr(left, 'value') and isinstance(left.value, str)):
860
+ left, right = right, left
861
+ # left must be a CallExpr(callee=IdentExpr("typeof"|"type"), args=[IdentExpr])
862
+ if not hasattr(left, 'callee') or not hasattr(left, 'args'):
863
+ return (None, None)
864
+ callee_name = getattr(left.callee, 'name', None)
865
+ if callee_name not in ("typeof", "type"):
866
+ return (None, None)
867
+ if not left.args:
868
+ return (None, None)
869
+ arg0 = left.args[0]
870
+ arg_val = getattr(arg0, 'value', arg0) # Argument node wraps value
871
+ var_name = getattr(arg_val, 'name', None)
872
+ if var_name is None:
873
+ return (None, None)
874
+ # right must be a StringLiteralExpr
875
+ if not hasattr(right, 'value') or not isinstance(right.value, str):
876
+ return (None, None)
877
+ type_name = right.value
878
+ narrow_to = BUILTIN_TYPES.get(type_name, InScriptType(type_name))
879
+ return (var_name, narrow_to)
880
+
730
881
  def visit_IfStmt(self, node: IfStmt) -> InScriptType:
731
882
  cond_type = self.visit(node.condition)
732
883
  if cond_type not in (T_BOOL, T_ANY):
@@ -735,7 +886,29 @@ class Analyzer(Visitor):
735
886
  f"non-bool conditions will be truthy/falsy at runtime",
736
887
  node.line
737
888
  )
738
- self.visit(node.then_branch)
889
+
890
+ # v1.8.1: union narrowing — `if typeof(x) == "int" { }` narrows x to int
891
+ var_name, narrow_to = self._extract_typeof_narrowing(node.condition)
892
+ if var_name and narrow_to:
893
+ # Push a scope, inject narrowed binding, then visit the BODY STATEMENTS
894
+ # directly so the narrowed type is visible for the entire then-branch.
895
+ self._push_scope("block")
896
+ sym = self._scope.parent.lookup(var_name) if self._scope.parent else None
897
+ if sym:
898
+ narrowed_sym = Symbol(var_name, narrow_to, sym.kind,
899
+ line=sym.line, col=sym.col)
900
+ narrowed_sym.used = True
901
+ self._define(narrowed_sym)
902
+ # Visit body stmts without letting visit_BlockStmt push another scope
903
+ from ast_nodes import BlockStmt
904
+ body = node.then_branch
905
+ stmts = body.body if isinstance(body, BlockStmt) else [body]
906
+ for stmt in stmts:
907
+ self.visit(stmt)
908
+ self._pop_scope()
909
+ else:
910
+ self.visit(node.then_branch)
911
+
739
912
  if node.else_branch:
740
913
  self.visit(node.else_branch)
741
914
  return T_VOID
@@ -854,7 +1027,9 @@ class Analyzer(Visitor):
854
1027
 
855
1028
  def visit_IntLiteralExpr(self, node: IntLiteralExpr) -> InScriptType: return T_INT
856
1029
  def visit_FloatLiteralExpr(self, node: FloatLiteralExpr) -> InScriptType: return T_FLOAT
857
- def visit_StringLiteralExpr(self, node: StringLiteralExpr) -> InScriptType: return T_STRING
1030
+ def visit_StringLiteralExpr(self, node: StringLiteralExpr) -> InScriptType:
1031
+ # v1.8.2: return a specific literal type so union-of-literals is enforced
1032
+ return literal_type(node.value)
858
1033
  def visit_BoolLiteralExpr(self, node: BoolLiteralExpr) -> InScriptType: return T_BOOL
859
1034
  def visit_NullLiteralExpr(self, node: NullLiteralExpr) -> InScriptType: return T_NULL
860
1035
 
@@ -1024,6 +1199,20 @@ class Analyzer(Visitor):
1024
1199
  f"{n_total} arg(s), got {n_args}",
1025
1200
  node.line
1026
1201
  )
1202
+ # v1.8.1: check argument types against declared param types
1203
+ for i, arg in enumerate(node.args):
1204
+ if i >= len(params):
1205
+ break
1206
+ p_type = self._resolve_type_ann(params[i].type_ann)
1207
+ if p_type == T_ANY:
1208
+ continue
1209
+ arg_type = self.visit(arg.value)
1210
+ if not types_compatible(p_type, arg_type):
1211
+ self._error(
1212
+ f"Argument {i+1} to '{node.callee.name}': "
1213
+ f"expected '{p_type}', got '{arg_type}'",
1214
+ getattr(arg.value, 'line', node.line)
1215
+ )
1027
1216
  return self._resolve_type_ann(fn.return_type)
1028
1217
  if sym:
1029
1218
  if sym.kind == "struct":
@@ -49,6 +49,11 @@ class TypeAnnotation(Node):
49
49
  is_nullable: bool = False
50
50
  key_type: Optional["TypeAnnotation"] = None
51
51
  nullable: bool = False
52
+ # v1.8.2: for string literal types `"left"` in unions / params
53
+ literal_value: Optional[str] = None # set when name == "__literal__"
54
+ # v1.8.2: for fn type aliases `fn(int) -> bool`
55
+ fn_params: Optional[list] = None # list of TypeAnnotation
56
+ fn_return: Optional["TypeAnnotation"] = None
52
57
 
53
58
 
54
59
  # ─────────────────────────────────────────────────────────────────────────────
@@ -717,9 +722,10 @@ class DecoratedDecl(Node):
717
722
 
718
723
  @dataclass
719
724
  class TypeAliasDecl(Node):
720
- """type ID = ExistingType type alias (annotation only at runtime)."""
721
- name: str
722
- target: str
725
+ """type ID = ExistingType | fn(...)->T | "lit1"|"lit2" type alias."""
726
+ name: str
727
+ target: str # kept for backwards compat with interpreter
728
+ type_ann: object = None # TypeAnnotation (None for legacy string-only aliases)
723
729
 
724
730
  @dataclass
725
731
  class SelectStmt(Node):
@@ -24,7 +24,7 @@ from errors import (InScriptError, LexerError, ParseError,
24
24
  SemanticError, InScriptRuntimeError,
25
25
  MultiError, InScriptWarning)
26
26
 
27
- VERSION = "1.7.4"
27
+ VERSION = "1.8.2"
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.7.4
3
+ Version: 1.8.2
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
@@ -937,9 +937,13 @@ class Interpreter(Visitor):
937
937
  return iface
938
938
 
939
939
  def visit_TypeAliasDecl(self, node) -> Any:
940
- """type ID = ExistingType store alias so type annotations can reference it."""
941
- # Runtime effect: register the alias name so it can be used in is/as checks
942
- self._env.define(node.name, {"__type_alias__": True, "__target__": node.target})
940
+ """v1.8.2: Register alias so it can be used in typeof/is checks at runtime."""
941
+ type_ann = getattr(node, 'type_ann', None)
942
+ self._env.define(node.name, {
943
+ "__type_alias__": True,
944
+ "__target__": node.target,
945
+ "__type_ann__": type_ann,
946
+ })
943
947
  return None
944
948
 
945
949
  def visit_ImplDecl(self, node) -> Any:
@@ -216,23 +216,17 @@ class Parser:
216
216
  # ─────────────────────────────────────────────
217
217
 
218
218
  def parse_type_alias(self):
219
- """type ID = ExistingTypetype alias declaration (annotation only)."""
219
+ """type ID = TypeAnnotationsupports simple, union, literal union, fn types."""
220
220
  line, col = self.current.line, self.current.col
221
221
  self.advance() # consume 'type'
222
222
  name = self.expect(TT.IDENT, "Expected alias name after 'type'").value
223
223
  self.expect(TT.ASSIGN, "Expected '=' in type alias")
224
- # Consume the type expression (could be int, string, ident, etc.)
225
- target_tok = self.current
226
- target_name = target_tok.value or target_tok.type.name
227
- self.advance()
228
- # Handle qualified names like MyModule.MyType
229
- while self.check(TT.DOT):
230
- self.advance()
231
- target_name += "." + (self.current.value or "")
232
- self.advance()
233
- # Return a simple VarDecl so the interpreter stores name in env as a type tag
224
+ # v1.8.2: parse a full type annotation (including string literal unions, fn types)
225
+ type_ann = self.parse_type_annotation()
226
+ # target: for backwards compat with interpreter, derive a string name
227
+ target = type_ann.name if type_ann.name not in ("__literal__", "__fn__", "Union") else name
234
228
  from ast_nodes import TypeAliasDecl
235
- return TypeAliasDecl(name=name, target=target_name, line=line, col=col)
229
+ return TypeAliasDecl(name=name, target=target, type_ann=type_ann, line=line, col=col)
236
230
 
237
231
  def parse_import(self) -> ImportDecl:
238
232
  """
@@ -447,6 +441,40 @@ class Parser:
447
441
  elif tok.type == TT.NIL: # v1.7.2: nil as type annotation
448
442
  name = "nil"
449
443
  self.advance()
444
+ # v1.8.2: string literal type — `"left"` in union / param
445
+ elif tok.type == TT.STRING:
446
+ lit = tok.value
447
+ self.advance()
448
+ ann = TypeAnnotation(name="__literal__", literal_value=lit,
449
+ line=line, col=col)
450
+ # Union suffix handled below — return early for nullable/union
451
+ if self.current.type == TT.QUESTION:
452
+ self.advance()
453
+ ann = TypeAnnotation(name="Optional", is_nullable=True,
454
+ generics=[ann], line=line, col=col)
455
+ if self.current.type == TT.BIT_OR:
456
+ union_types = [ann]
457
+ while self.current.type == TT.BIT_OR:
458
+ self.advance()
459
+ union_types.append(self.parse_type_annotation())
460
+ ann = TypeAnnotation(name="Union", generics=union_types, line=line, col=col)
461
+ return ann
462
+ # v1.8.2: fn type alias — `fn(int, string) -> bool`
463
+ elif tok.type == TT.FN:
464
+ self.advance() # consume 'fn'
465
+ self.expect(TT.LPAREN, "Expected '(' in fn type")
466
+ fn_params = []
467
+ if not self.check(TT.RPAREN):
468
+ fn_params.append(self.parse_type_annotation())
469
+ while self.match(TT.COMMA):
470
+ fn_params.append(self.parse_type_annotation())
471
+ self.expect(TT.RPAREN, "Expected ')' in fn type")
472
+ fn_return = None
473
+ if self.match(TT.ARROW):
474
+ fn_return = self.parse_type_annotation()
475
+ ann = TypeAnnotation(name="__fn__", fn_params=fn_params,
476
+ fn_return=fn_return, line=line, col=col)
477
+ return ann
450
478
  else:
451
479
  self._error(f"Expected type name, got '{tok.value}'")
452
480
  ann = TypeAnnotation(name=name, line=line, col=col)
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "inscript-lang"
7
- version = "1.7.4"
7
+ version = "1.8.2"
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.7.4"
43
+ VERSION = "1.8.2"
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