IncludeCPP 3.4.8__py3-none-any.whl → 3.4.21__py3-none-any.whl

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.
@@ -99,6 +99,7 @@ class TokenType(Enum):
99
99
  AT = auto()
100
100
  GLOBAL_REF = auto() # r@<name> global variable declaration
101
101
  SELF_REF = auto() # s@<name> self-reference to global struct
102
+ SHARED_REF = auto() # $<name> shared object reference
102
103
  PACKAGE = auto()
103
104
  PACKAGE_INCLUDES = auto()
104
105
  AS = auto()
@@ -154,6 +155,11 @@ TYPE_GENERICS = {
154
155
  'vector', 'stack', 'array', 'openquote'
155
156
  }
156
157
 
158
+ # Functions that accept type parameters: FuncName<type>(args)
159
+ TYPE_PARAM_FUNCTIONS = {
160
+ 'OpenFind' # OpenFind<string>(0)
161
+ }
162
+
157
163
  # Injection helper prefixes (type::helper=value)
158
164
  INJECTION_HELPERS = {
159
165
  'string', 'integer', 'json', 'array', 'vector', 'combo', 'dynamic', 'sql'
@@ -232,6 +238,9 @@ class CSSLLexer:
232
238
  elif char == '@':
233
239
  self._add_token(TokenType.AT, '@')
234
240
  self._advance()
241
+ elif char == '$':
242
+ # $<name> shared object reference
243
+ self._read_shared_ref()
235
244
  elif char == '&':
236
245
  # & for references
237
246
  if self._peek(1) == '&':
@@ -470,6 +479,20 @@ class CSSLLexer:
470
479
  value = self.source[name_start:self.pos]
471
480
  self._add_token(TokenType.GLOBAL_REF, value)
472
481
 
482
+ def _read_shared_ref(self):
483
+ """Read $<name> shared object reference"""
484
+ self._advance() # skip '$'
485
+
486
+ # Read the identifier (shared object name)
487
+ name_start = self.pos
488
+ while self.pos < len(self.source) and (self.source[self.pos].isalnum() or self.source[self.pos] == '_'):
489
+ self._advance()
490
+
491
+ value = self.source[name_start:self.pos]
492
+ if not value:
493
+ self.error("Expected identifier after '$'")
494
+ self._add_token(TokenType.SHARED_REF, value)
495
+
473
496
  def _read_less_than(self):
474
497
  # Check for <<== (code infusion left)
475
498
  if self._peek(1) == '<' and self._peek(2) == '=' and self._peek(3) == '=':
@@ -679,6 +702,44 @@ class CSSLParser:
679
702
  self.pos = saved_pos
680
703
  return False
681
704
 
705
+ def _looks_like_typed_variable(self) -> bool:
706
+ """Check if current position looks like a typed variable declaration.
707
+
708
+ Patterns:
709
+ - int x;
710
+ - stack<string> myStack;
711
+ - vector<int> nums = [1,2,3];
712
+
713
+ Distinguishes from function declarations by checking for '(' after identifier.
714
+ """
715
+ saved_pos = self.pos
716
+
717
+ # Check for type keyword
718
+ if self._check(TokenType.KEYWORD) and self._is_type_keyword(self._current().value):
719
+ self._advance()
720
+
721
+ # Skip generic type parameters <T>
722
+ if self._check(TokenType.COMPARE_LT):
723
+ depth = 1
724
+ self._advance()
725
+ while depth > 0 and not self._is_at_end():
726
+ if self._check(TokenType.COMPARE_LT):
727
+ depth += 1
728
+ elif self._check(TokenType.COMPARE_GT):
729
+ depth -= 1
730
+ self._advance()
731
+
732
+ # Check for identifier NOT followed by ( (that would be a function)
733
+ if self._check(TokenType.IDENTIFIER):
734
+ self._advance()
735
+ # If followed by '(' it's a function, not a variable
736
+ is_var = not self._check(TokenType.PAREN_START)
737
+ self.pos = saved_pos
738
+ return is_var
739
+
740
+ self.pos = saved_pos
741
+ return False
742
+
682
743
  def _parse_typed_function(self) -> ASTNode:
683
744
  """Parse C-style typed function declaration.
684
745
 
@@ -800,6 +861,78 @@ class CSSLParser:
800
861
  self._expect(TokenType.BLOCK_END)
801
862
  return node
802
863
 
864
+ def _looks_like_typed_variable(self) -> bool:
865
+ """Check if current position looks like a typed variable declaration:
866
+ type_name varName; or type_name<T> varName; or type_name varName = value;
867
+ """
868
+ # Save position
869
+ saved_pos = self.pos
870
+
871
+ # Must start with a type keyword (int, string, stack, vector, etc.)
872
+ if not self._check(TokenType.KEYWORD):
873
+ return False
874
+
875
+ type_name = self._current().value
876
+
877
+ # Skip known type keywords
878
+ type_keywords = {'int', 'string', 'float', 'bool', 'dynamic', 'void',
879
+ 'stack', 'vector', 'datastruct', 'dataspace', 'shuffled',
880
+ 'iterator', 'combo', 'array', 'openquote', 'json'}
881
+ if type_name not in type_keywords:
882
+ return False
883
+
884
+ self._advance()
885
+
886
+ # Check for optional generic <T>
887
+ if self._match(TokenType.COMPARE_LT):
888
+ # Skip until >
889
+ depth = 1
890
+ while depth > 0 and not self._is_at_end():
891
+ if self._check(TokenType.COMPARE_LT):
892
+ depth += 1
893
+ elif self._check(TokenType.COMPARE_GT):
894
+ depth -= 1
895
+ self._advance()
896
+
897
+ # Next should be an identifier (variable name), not '(' (function) or ';'
898
+ result = self._check(TokenType.IDENTIFIER)
899
+
900
+ # Restore position
901
+ self.pos = saved_pos
902
+ return result
903
+
904
+ def _parse_typed_variable(self) -> Optional[ASTNode]:
905
+ """Parse a typed variable declaration: type varName; or type<T> varName = value;"""
906
+ # Get type name
907
+ type_name = self._advance().value # Consume type keyword
908
+
909
+ # Check for generic type <T>
910
+ element_type = None
911
+ if self._match(TokenType.COMPARE_LT):
912
+ # Get element type
913
+ if self._check(TokenType.KEYWORD) or self._check(TokenType.IDENTIFIER):
914
+ element_type = self._advance().value
915
+ self._expect(TokenType.COMPARE_GT)
916
+
917
+ # Get variable name
918
+ if not self._check(TokenType.IDENTIFIER):
919
+ return None
920
+ var_name = self._advance().value
921
+
922
+ # Check for assignment or just declaration
923
+ value = None
924
+ if self._match(TokenType.EQUALS):
925
+ value = self._parse_expression()
926
+
927
+ self._match(TokenType.SEMICOLON)
928
+
929
+ return ASTNode('typed_declaration', value={
930
+ 'type': type_name,
931
+ 'element_type': element_type,
932
+ 'name': var_name,
933
+ 'value': value
934
+ })
935
+
803
936
  def parse_program(self) -> ASTNode:
804
937
  """Parse a standalone program (no service wrapper)"""
805
938
  root = ASTNode('program', children=[])
@@ -812,6 +945,11 @@ class CSSLParser:
812
945
  # Check for C-style typed function declarations
813
946
  elif self._looks_like_function_declaration():
814
947
  root.children.append(self._parse_typed_function())
948
+ # Check for typed variable declarations (int x;, stack<string> s;)
949
+ elif self._looks_like_typed_variable():
950
+ decl = self._parse_typed_variable()
951
+ if decl:
952
+ root.children.append(decl)
815
953
  # Handle service blocks
816
954
  elif self._match_keyword('service-init'):
817
955
  root.children.append(self._parse_service_init())
@@ -827,13 +965,17 @@ class CSSLParser:
827
965
  elif self._match_keyword('global'):
828
966
  stmt = self._parse_expression_statement()
829
967
  if stmt:
830
- root.children.append(stmt)
968
+ # Wrap in global_assignment to mark as global variable
969
+ global_stmt = ASTNode('global_assignment', value=stmt)
970
+ root.children.append(global_stmt)
831
971
  elif self._check(TokenType.GLOBAL_REF):
832
972
  stmt = self._parse_expression_statement()
833
973
  if stmt:
834
- root.children.append(stmt)
974
+ # Wrap in global_assignment to mark as global variable (same as 'global' keyword)
975
+ global_stmt = ASTNode('global_assignment', value=stmt)
976
+ root.children.append(global_stmt)
835
977
  # Handle statements
836
- elif self._check(TokenType.IDENTIFIER) or self._check(TokenType.AT) or self._check(TokenType.SELF_REF):
978
+ elif self._check(TokenType.IDENTIFIER) or self._check(TokenType.AT) or self._check(TokenType.SELF_REF) or self._check(TokenType.SHARED_REF):
837
979
  stmt = self._parse_expression_statement()
838
980
  if stmt:
839
981
  root.children.append(stmt)
@@ -1143,6 +1285,9 @@ class CSSLParser:
1143
1285
  elif self._match_keyword('define'):
1144
1286
  # Nested define function
1145
1287
  return self._parse_define()
1288
+ elif self._looks_like_typed_variable():
1289
+ # Typed variable declaration (e.g., stack<string> myStack;)
1290
+ return self._parse_typed_variable()
1146
1291
  elif self._looks_like_function_declaration():
1147
1292
  # Nested typed function (e.g., void Level2() { ... })
1148
1293
  return self._parse_typed_function()
@@ -1153,6 +1298,7 @@ class CSSLParser:
1153
1298
  return None
1154
1299
 
1155
1300
  def _parse_if(self) -> ASTNode:
1301
+ """Parse if statement with support for else if AND elif syntax."""
1156
1302
  self._expect(TokenType.PAREN_START)
1157
1303
  condition = self._parse_expression()
1158
1304
  self._expect(TokenType.PAREN_END)
@@ -1168,7 +1314,13 @@ class CSSLParser:
1168
1314
  self._expect(TokenType.BLOCK_END)
1169
1315
  node.children.append(then_block)
1170
1316
 
1171
- if self._match_keyword('else'):
1317
+ # Support both 'else if' AND 'elif' syntax
1318
+ if self._match_keyword('elif'):
1319
+ # elif is shorthand for else if
1320
+ else_block = ASTNode('else', children=[])
1321
+ else_block.children.append(self._parse_if())
1322
+ node.children.append(else_block)
1323
+ elif self._match_keyword('else'):
1172
1324
  else_block = ASTNode('else', children=[])
1173
1325
  if self._match_keyword('if'):
1174
1326
  else_block.children.append(self._parse_if())
@@ -1200,18 +1352,186 @@ class CSSLParser:
1200
1352
  return node
1201
1353
 
1202
1354
  def _parse_for(self) -> ASTNode:
1355
+ """Parse for loop - supports both syntaxes:
1356
+
1357
+ Python-style: for (i in range(0, n)) { }
1358
+ C-style: for (int i = 0; i < n; i = i + 1) { }
1359
+ for (i = 0; i < n; i++) { }
1360
+ """
1203
1361
  self._expect(TokenType.PAREN_START)
1362
+
1363
+ # Detect C-style by checking for semicolons in the for header
1364
+ # Look ahead without consuming tokens
1365
+ is_c_style = self._detect_c_style_for()
1366
+
1367
+ if is_c_style:
1368
+ # C-style: for (init; condition; update) { }
1369
+ return self._parse_c_style_for()
1370
+ else:
1371
+ # Python-style: for (var in range(start, end)) { }
1372
+ return self._parse_python_style_for()
1373
+
1374
+ def _detect_c_style_for(self) -> bool:
1375
+ """Detect if this is a C-style for loop by looking for semicolons."""
1376
+ # Scan the tokens list directly without modifying self.pos
1377
+ pos = self.pos
1378
+ paren_depth = 1
1379
+
1380
+ while pos < len(self.tokens) and paren_depth > 0:
1381
+ token = self.tokens[pos]
1382
+ if token.type == TokenType.PAREN_START:
1383
+ paren_depth += 1
1384
+ elif token.type == TokenType.PAREN_END:
1385
+ paren_depth -= 1
1386
+ elif token.type == TokenType.SEMICOLON and paren_depth == 1:
1387
+ # Found semicolon at top level - C-style
1388
+ return True
1389
+ elif token.type == TokenType.KEYWORD and token.value == 'in':
1390
+ # Found 'in' keyword - Python-style
1391
+ return False
1392
+ pos += 1
1393
+
1394
+ return False # Default to Python-style
1395
+
1396
+ def _parse_c_style_for(self) -> ASTNode:
1397
+ """Parse C-style for loop: for (init; condition; update) { }"""
1398
+ # Parse init statement
1399
+ init = None
1400
+ if not self._check(TokenType.SEMICOLON):
1401
+ # Check if it's a typed declaration: int i = 0
1402
+ if self._check(TokenType.KEYWORD) and self._peek().value in ('int', 'float', 'string', 'bool', 'dynamic'):
1403
+ type_name = self._advance().value
1404
+ var_name = self._advance().value
1405
+ self._expect(TokenType.EQUALS)
1406
+ value = self._parse_expression()
1407
+ init = ASTNode('c_for_init', value={
1408
+ 'type': type_name,
1409
+ 'var': var_name,
1410
+ 'value': value
1411
+ })
1412
+ else:
1413
+ # Simple assignment: i = 0
1414
+ var_name = self._advance().value
1415
+ self._expect(TokenType.EQUALS)
1416
+ value = self._parse_expression()
1417
+ init = ASTNode('c_for_init', value={
1418
+ 'type': None,
1419
+ 'var': var_name,
1420
+ 'value': value
1421
+ })
1422
+
1423
+ self._expect(TokenType.SEMICOLON)
1424
+
1425
+ # Parse condition
1426
+ condition = None
1427
+ if not self._check(TokenType.SEMICOLON):
1428
+ condition = self._parse_expression()
1429
+
1430
+ self._expect(TokenType.SEMICOLON)
1431
+
1432
+ # Parse update statement
1433
+ update = None
1434
+ if not self._check(TokenType.PAREN_END):
1435
+ # Could be: i = i + 1, i++, ++i, i += 1
1436
+ update = self._parse_c_for_update()
1437
+
1438
+ self._expect(TokenType.PAREN_END)
1439
+
1440
+ node = ASTNode('c_for', value={
1441
+ 'init': init,
1442
+ 'condition': condition,
1443
+ 'update': update
1444
+ }, children=[])
1445
+
1446
+ self._expect(TokenType.BLOCK_START)
1447
+
1448
+ while not self._check(TokenType.BLOCK_END) and not self._is_at_end():
1449
+ stmt = self._parse_statement()
1450
+ if stmt:
1451
+ node.children.append(stmt)
1452
+
1453
+ self._expect(TokenType.BLOCK_END)
1454
+ return node
1455
+
1456
+ def _parse_c_for_update(self) -> ASTNode:
1457
+ """Parse the update part of a C-style for loop.
1458
+
1459
+ Supports: i = i + 1, i++, ++i, i += 1, i -= 1
1460
+ """
1461
+ # Check for prefix increment/decrement: ++i or --i
1462
+ if self._check(TokenType.PLUS) or self._check(TokenType.MINUS):
1463
+ op_token = self._advance()
1464
+ # Check for double operator (++ or --)
1465
+ if self._check(op_token.type):
1466
+ self._advance()
1467
+ var_name = self._advance().value
1468
+ op = 'increment' if op_token.type == TokenType.PLUS else 'decrement'
1469
+ return ASTNode('c_for_update', value={'var': var_name, 'op': op})
1470
+
1471
+ # Regular variable assignment or postfix
1472
+ var_name = self._advance().value
1473
+
1474
+ # Check for postfix increment/decrement: i++ or i--
1475
+ if self._check(TokenType.PLUS):
1476
+ self._advance()
1477
+ if self._check(TokenType.PLUS):
1478
+ self._advance()
1479
+ return ASTNode('c_for_update', value={'var': var_name, 'op': 'increment'})
1480
+ else:
1481
+ # i += value
1482
+ if self._check(TokenType.EQUALS):
1483
+ self._advance()
1484
+ value = self._parse_expression()
1485
+ return ASTNode('c_for_update', value={'var': var_name, 'op': 'add', 'value': value})
1486
+ elif self._check(TokenType.MINUS):
1487
+ self._advance()
1488
+ if self._check(TokenType.MINUS):
1489
+ self._advance()
1490
+ return ASTNode('c_for_update', value={'var': var_name, 'op': 'decrement'})
1491
+ else:
1492
+ # i -= value
1493
+ if self._check(TokenType.EQUALS):
1494
+ self._advance()
1495
+ value = self._parse_expression()
1496
+ return ASTNode('c_for_update', value={'var': var_name, 'op': 'subtract', 'value': value})
1497
+
1498
+ # Regular assignment: i = expression
1499
+ if self._check(TokenType.EQUALS):
1500
+ self._advance()
1501
+ value = self._parse_expression()
1502
+ return ASTNode('c_for_update', value={'var': var_name, 'op': 'assign', 'value': value})
1503
+
1504
+ # Just the variable (shouldn't happen but handle it)
1505
+ return ASTNode('c_for_update', value={'var': var_name, 'op': 'none'})
1506
+
1507
+ def _parse_python_style_for(self) -> ASTNode:
1508
+ """Parse Python-style for loop: for (i in range(start, end)) { }"""
1204
1509
  var_name = self._advance().value
1205
- self._expect(TokenType.KEYWORD)
1206
- self._expect(TokenType.KEYWORD)
1510
+ self._expect(TokenType.KEYWORD) # 'in'
1511
+
1512
+ # 'range' can be keyword or identifier
1513
+ if self._check(TokenType.KEYWORD) and self._peek().value == 'range':
1514
+ self._advance() # consume 'range' keyword
1515
+ elif self._check(TokenType.IDENTIFIER) and self._peek().value == 'range':
1516
+ self._advance() # consume 'range' identifier
1517
+ else:
1518
+ self.error(f"Expected 'range', got {self._peek().value}")
1519
+
1207
1520
  self._expect(TokenType.PAREN_START)
1208
1521
  start = self._parse_expression()
1209
1522
  self._expect(TokenType.COMMA)
1210
1523
  end = self._parse_expression()
1524
+
1525
+ # Optional step parameter: range(start, end, step)
1526
+ step = None
1527
+ if self._check(TokenType.COMMA):
1528
+ self._advance() # consume comma
1529
+ step = self._parse_expression()
1530
+
1211
1531
  self._expect(TokenType.PAREN_END)
1212
1532
  self._expect(TokenType.PAREN_END)
1213
1533
 
1214
- node = ASTNode('for', value={'var': var_name, 'start': start, 'end': end}, children=[])
1534
+ node = ASTNode('for', value={'var': var_name, 'start': start, 'end': end, 'step': step}, children=[])
1215
1535
  self._expect(TokenType.BLOCK_START)
1216
1536
 
1217
1537
  while not self._check(TokenType.BLOCK_END) and not self._is_at_end():
@@ -1640,6 +1960,56 @@ class CSSLParser:
1640
1960
  node = ASTNode('call', value={'callee': node, 'args': args})
1641
1961
  return node
1642
1962
 
1963
+ if self._check(TokenType.GLOBAL_REF):
1964
+ # r@<name> global variable reference/declaration
1965
+ token = self._advance()
1966
+ node = ASTNode('global_ref', value=token.value, line=token.line, column=token.column)
1967
+ # Check for member access, calls, indexing
1968
+ while True:
1969
+ if self._match(TokenType.PAREN_START):
1970
+ args = []
1971
+ while not self._check(TokenType.PAREN_END):
1972
+ args.append(self._parse_expression())
1973
+ if not self._check(TokenType.PAREN_END):
1974
+ self._expect(TokenType.COMMA)
1975
+ self._expect(TokenType.PAREN_END)
1976
+ node = ASTNode('call', value={'callee': node, 'args': args})
1977
+ elif self._match(TokenType.DOT):
1978
+ member = self._advance().value
1979
+ node = ASTNode('member_access', value={'object': node, 'member': member})
1980
+ elif self._match(TokenType.BRACKET_START):
1981
+ index = self._parse_expression()
1982
+ self._expect(TokenType.BRACKET_END)
1983
+ node = ASTNode('index_access', value={'object': node, 'index': index})
1984
+ else:
1985
+ break
1986
+ return node
1987
+
1988
+ if self._check(TokenType.SHARED_REF):
1989
+ # $<name> shared object reference
1990
+ token = self._advance()
1991
+ node = ASTNode('shared_ref', value=token.value, line=token.line, column=token.column)
1992
+ # Check for member access, calls, indexing
1993
+ while True:
1994
+ if self._match(TokenType.PAREN_START):
1995
+ args = []
1996
+ while not self._check(TokenType.PAREN_END):
1997
+ args.append(self._parse_expression())
1998
+ if not self._check(TokenType.PAREN_END):
1999
+ self._expect(TokenType.COMMA)
2000
+ self._expect(TokenType.PAREN_END)
2001
+ node = ASTNode('call', value={'callee': node, 'args': args})
2002
+ elif self._match(TokenType.DOT):
2003
+ member = self._advance().value
2004
+ node = ASTNode('member_access', value={'object': node, 'member': member})
2005
+ elif self._match(TokenType.BRACKET_START):
2006
+ index = self._parse_expression()
2007
+ self._expect(TokenType.BRACKET_END)
2008
+ node = ASTNode('index_access', value={'object': node, 'index': index})
2009
+ else:
2010
+ break
2011
+ return node
2012
+
1643
2013
  if self._check(TokenType.NUMBER):
1644
2014
  return ASTNode('literal', value=self._advance().value)
1645
2015
 
@@ -1675,16 +2045,81 @@ class CSSLParser:
1675
2045
  return ASTNode('literal', value=None)
1676
2046
 
1677
2047
  def _parse_module_reference(self) -> ASTNode:
1678
- parts = []
1679
- parts.append(self._advance().value)
2048
+ """Parse @name, handling method calls and property access.
2049
+
2050
+ @name alone -> module_ref
2051
+ @name.method() -> call with member_access
2052
+ @name.property -> member_access
2053
+ """
2054
+ # Get base name
2055
+ name = self._advance().value
2056
+ node = ASTNode('module_ref', value=name)
1680
2057
 
1681
- while self._match(TokenType.DOT):
1682
- parts.append(self._advance().value)
2058
+ # Continue to handle member access, calls, and indexing
2059
+ while True:
2060
+ if self._match(TokenType.DOT):
2061
+ member = self._advance().value
2062
+ node = ASTNode('member_access', value={'object': node, 'member': member})
2063
+ elif self._match(TokenType.PAREN_START):
2064
+ # Function call
2065
+ args = []
2066
+ while not self._check(TokenType.PAREN_END):
2067
+ args.append(self._parse_expression())
2068
+ if not self._check(TokenType.PAREN_END):
2069
+ self._expect(TokenType.COMMA)
2070
+ self._expect(TokenType.PAREN_END)
2071
+ node = ASTNode('call', value={'callee': node, 'args': args})
2072
+ elif self._match(TokenType.BRACKET_START):
2073
+ # Index access
2074
+ index = self._parse_expression()
2075
+ self._expect(TokenType.BRACKET_END)
2076
+ node = ASTNode('index_access', value={'object': node, 'index': index})
2077
+ else:
2078
+ break
1683
2079
 
1684
- return ASTNode('module_ref', value='.'.join(parts))
2080
+ return node
1685
2081
 
1686
2082
  def _parse_identifier_or_call(self) -> ASTNode:
1687
2083
  name = self._advance().value
2084
+
2085
+ # Check for type generic instantiation: stack<string>, vector<int>, etc.
2086
+ # This creates a new instance of the type with the specified element type
2087
+ if name in TYPE_GENERICS and self._check(TokenType.COMPARE_LT):
2088
+ self._advance() # consume <
2089
+ element_type = 'dynamic'
2090
+ if self._check(TokenType.KEYWORD) or self._check(TokenType.IDENTIFIER):
2091
+ element_type = self._advance().value
2092
+ self._expect(TokenType.COMPARE_GT) # consume >
2093
+ return ASTNode('type_instantiation', value={
2094
+ 'type': name,
2095
+ 'element_type': element_type
2096
+ })
2097
+
2098
+ # Check for type-parameterized function call: OpenFind<string>(0)
2099
+ if name in TYPE_PARAM_FUNCTIONS and self._check(TokenType.COMPARE_LT):
2100
+ self._advance() # consume <
2101
+ type_param = 'dynamic'
2102
+ if self._check(TokenType.KEYWORD) or self._check(TokenType.IDENTIFIER):
2103
+ type_param = self._advance().value
2104
+ self._expect(TokenType.COMPARE_GT) # consume >
2105
+
2106
+ # Must be followed by ()
2107
+ if self._check(TokenType.PAREN_START):
2108
+ self._advance() # consume (
2109
+ args = []
2110
+ while not self._check(TokenType.PAREN_END):
2111
+ args.append(self._parse_expression())
2112
+ if not self._check(TokenType.PAREN_END):
2113
+ self._expect(TokenType.COMMA)
2114
+ self._expect(TokenType.PAREN_END)
2115
+
2116
+ # Return as typed function call
2117
+ return ASTNode('typed_call', value={
2118
+ 'name': name,
2119
+ 'type_param': type_param,
2120
+ 'args': args
2121
+ })
2122
+
1688
2123
  node = ASTNode('identifier', value=name)
1689
2124
 
1690
2125
  while True: