IncludeCPP 3.4.2__py3-none-any.whl → 3.4.10__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.
- includecpp/__init__.py +59 -59
- includecpp/__init__.pyi +4 -1
- includecpp/cli/commands.py +2 -3
- includecpp/core/cssl/__init__.py +6 -4
- includecpp/core/cssl/cssl_parser.py +451 -6
- includecpp/core/cssl/cssl_runtime.py +180 -13
- includecpp/core/cssl/cssl_types.py +164 -2
- includecpp/core/cssl_bridge.py +288 -2
- includecpp/core/cssl_bridge.pyi +311 -0
- {includecpp-3.4.2.dist-info → includecpp-3.4.10.dist-info}/METADATA +1 -1
- {includecpp-3.4.2.dist-info → includecpp-3.4.10.dist-info}/RECORD +15 -14
- {includecpp-3.4.2.dist-info → includecpp-3.4.10.dist-info}/WHEEL +0 -0
- {includecpp-3.4.2.dist-info → includecpp-3.4.10.dist-info}/entry_points.txt +0 -0
- {includecpp-3.4.2.dist-info → includecpp-3.4.10.dist-info}/licenses/LICENSE +0 -0
- {includecpp-3.4.2.dist-info → includecpp-3.4.10.dist-info}/top_level.txt +0 -0
|
@@ -216,6 +216,9 @@ class CSSLLexer:
|
|
|
216
216
|
self.column = 1
|
|
217
217
|
elif char in '"\'':
|
|
218
218
|
self._read_string(char)
|
|
219
|
+
elif char == '`':
|
|
220
|
+
# Raw string (no escape processing) - useful for JSON
|
|
221
|
+
self._read_raw_string()
|
|
219
222
|
elif char.isdigit() or (char == '-' and self._peek(1).isdigit()):
|
|
220
223
|
self._read_number()
|
|
221
224
|
elif char == 'r' and self._peek(1) == '@':
|
|
@@ -224,7 +227,7 @@ class CSSLLexer:
|
|
|
224
227
|
elif char == 's' and self._peek(1) == '@':
|
|
225
228
|
# s@<name> self-reference to global struct
|
|
226
229
|
self._read_self_ref()
|
|
227
|
-
elif char.isalpha() or char == '_'
|
|
230
|
+
elif char.isalpha() or char == '_':
|
|
228
231
|
self._read_identifier()
|
|
229
232
|
elif char == '@':
|
|
230
233
|
self._add_token(TokenType.AT, '@')
|
|
@@ -346,13 +349,52 @@ class CSSLLexer:
|
|
|
346
349
|
def _read_string(self, quote_char: str):
|
|
347
350
|
self._advance()
|
|
348
351
|
start = self.pos
|
|
352
|
+
result = []
|
|
349
353
|
while self.pos < len(self.source) and self.source[self.pos] != quote_char:
|
|
350
|
-
if self.source[self.pos] == '\\':
|
|
354
|
+
if self.source[self.pos] == '\\' and self.pos + 1 < len(self.source):
|
|
355
|
+
# Handle escape sequences
|
|
356
|
+
next_char = self.source[self.pos + 1]
|
|
357
|
+
if next_char == 'n':
|
|
358
|
+
result.append('\n')
|
|
359
|
+
elif next_char == 't':
|
|
360
|
+
result.append('\t')
|
|
361
|
+
elif next_char == 'r':
|
|
362
|
+
result.append('\r')
|
|
363
|
+
elif next_char == '\\':
|
|
364
|
+
result.append('\\')
|
|
365
|
+
elif next_char == quote_char:
|
|
366
|
+
result.append(quote_char)
|
|
367
|
+
elif next_char == '"':
|
|
368
|
+
result.append('"')
|
|
369
|
+
elif next_char == "'":
|
|
370
|
+
result.append("'")
|
|
371
|
+
else:
|
|
372
|
+
result.append(self.source[self.pos])
|
|
373
|
+
result.append(next_char)
|
|
374
|
+
self._advance()
|
|
375
|
+
self._advance()
|
|
376
|
+
else:
|
|
377
|
+
result.append(self.source[self.pos])
|
|
351
378
|
self._advance()
|
|
379
|
+
value = ''.join(result)
|
|
380
|
+
self._add_token(TokenType.STRING, value)
|
|
381
|
+
self._advance()
|
|
382
|
+
|
|
383
|
+
def _read_raw_string(self):
|
|
384
|
+
"""Read raw string with backticks - no escape processing.
|
|
385
|
+
|
|
386
|
+
Useful for JSON: `{"id": "2819e1", "name": "test"}`
|
|
387
|
+
"""
|
|
388
|
+
self._advance() # Skip opening backtick
|
|
389
|
+
start = self.pos
|
|
390
|
+
while self.pos < len(self.source) and self.source[self.pos] != '`':
|
|
391
|
+
if self.source[self.pos] == '\n':
|
|
392
|
+
self.line += 1
|
|
393
|
+
self.column = 0
|
|
352
394
|
self._advance()
|
|
353
395
|
value = self.source[start:self.pos]
|
|
354
396
|
self._add_token(TokenType.STRING, value)
|
|
355
|
-
self._advance()
|
|
397
|
+
self._advance() # Skip closing backtick
|
|
356
398
|
|
|
357
399
|
def _read_number(self):
|
|
358
400
|
start = self.pos
|
|
@@ -368,7 +410,7 @@ class CSSLLexer:
|
|
|
368
410
|
|
|
369
411
|
def _read_identifier(self):
|
|
370
412
|
start = self.pos
|
|
371
|
-
while self.pos < len(self.source) and (self.source[self.pos].isalnum() or self.source[self.pos]
|
|
413
|
+
while self.pos < len(self.source) and (self.source[self.pos].isalnum() or self.source[self.pos] == '_'):
|
|
372
414
|
self._advance()
|
|
373
415
|
value = self.source[start:self.pos]
|
|
374
416
|
|
|
@@ -588,6 +630,286 @@ class CSSLParser:
|
|
|
588
630
|
self._match(TokenType.BLOCK_END)
|
|
589
631
|
return root
|
|
590
632
|
|
|
633
|
+
def _is_function_modifier(self, value: str) -> bool:
|
|
634
|
+
"""Check if a keyword is a function modifier"""
|
|
635
|
+
return value in ('undefined', 'open', 'meta', 'super', 'closed', 'private', 'virtual', 'sqlbased')
|
|
636
|
+
|
|
637
|
+
def _is_type_keyword(self, value: str) -> bool:
|
|
638
|
+
"""Check if a keyword is a type declaration"""
|
|
639
|
+
return value in ('int', 'string', 'float', 'bool', 'void', 'json', 'array', 'vector', 'stack',
|
|
640
|
+
'dynamic', 'datastruct', 'dataspace', 'shuffled', 'iterator', 'combo', 'structure')
|
|
641
|
+
|
|
642
|
+
def _looks_like_function_declaration(self) -> bool:
|
|
643
|
+
"""Check if current position looks like a C-style function declaration.
|
|
644
|
+
|
|
645
|
+
Patterns:
|
|
646
|
+
- int funcName(...)
|
|
647
|
+
- undefined int funcName(...)
|
|
648
|
+
- vector<string> funcName(...)
|
|
649
|
+
- undefined void funcName(...)
|
|
650
|
+
"""
|
|
651
|
+
saved_pos = self.pos
|
|
652
|
+
|
|
653
|
+
# Skip modifiers (undefined, open, meta, super, closed, private, virtual)
|
|
654
|
+
while self._check(TokenType.KEYWORD) and self._is_function_modifier(self._current().value):
|
|
655
|
+
self._advance()
|
|
656
|
+
|
|
657
|
+
# Check for type keyword
|
|
658
|
+
if self._check(TokenType.KEYWORD) and self._is_type_keyword(self._current().value):
|
|
659
|
+
self._advance()
|
|
660
|
+
|
|
661
|
+
# Skip generic type parameters <T>
|
|
662
|
+
if self._check(TokenType.COMPARE_LT):
|
|
663
|
+
depth = 1
|
|
664
|
+
self._advance()
|
|
665
|
+
while depth > 0 and not self._is_at_end():
|
|
666
|
+
if self._check(TokenType.COMPARE_LT):
|
|
667
|
+
depth += 1
|
|
668
|
+
elif self._check(TokenType.COMPARE_GT):
|
|
669
|
+
depth -= 1
|
|
670
|
+
self._advance()
|
|
671
|
+
|
|
672
|
+
# Check for identifier followed by (
|
|
673
|
+
if self._check(TokenType.IDENTIFIER):
|
|
674
|
+
self._advance()
|
|
675
|
+
is_func = self._check(TokenType.PAREN_START)
|
|
676
|
+
self.pos = saved_pos
|
|
677
|
+
return is_func
|
|
678
|
+
|
|
679
|
+
self.pos = saved_pos
|
|
680
|
+
return False
|
|
681
|
+
|
|
682
|
+
def _looks_like_typed_variable(self) -> bool:
|
|
683
|
+
"""Check if current position looks like a typed variable declaration.
|
|
684
|
+
|
|
685
|
+
Patterns:
|
|
686
|
+
- int x;
|
|
687
|
+
- stack<string> myStack;
|
|
688
|
+
- vector<int> nums = [1,2,3];
|
|
689
|
+
|
|
690
|
+
Distinguishes from function declarations by checking for '(' after identifier.
|
|
691
|
+
"""
|
|
692
|
+
saved_pos = self.pos
|
|
693
|
+
|
|
694
|
+
# Check for type keyword
|
|
695
|
+
if self._check(TokenType.KEYWORD) and self._is_type_keyword(self._current().value):
|
|
696
|
+
self._advance()
|
|
697
|
+
|
|
698
|
+
# Skip generic type parameters <T>
|
|
699
|
+
if self._check(TokenType.COMPARE_LT):
|
|
700
|
+
depth = 1
|
|
701
|
+
self._advance()
|
|
702
|
+
while depth > 0 and not self._is_at_end():
|
|
703
|
+
if self._check(TokenType.COMPARE_LT):
|
|
704
|
+
depth += 1
|
|
705
|
+
elif self._check(TokenType.COMPARE_GT):
|
|
706
|
+
depth -= 1
|
|
707
|
+
self._advance()
|
|
708
|
+
|
|
709
|
+
# Check for identifier NOT followed by ( (that would be a function)
|
|
710
|
+
if self._check(TokenType.IDENTIFIER):
|
|
711
|
+
self._advance()
|
|
712
|
+
# If followed by '(' it's a function, not a variable
|
|
713
|
+
is_var = not self._check(TokenType.PAREN_START)
|
|
714
|
+
self.pos = saved_pos
|
|
715
|
+
return is_var
|
|
716
|
+
|
|
717
|
+
self.pos = saved_pos
|
|
718
|
+
return False
|
|
719
|
+
|
|
720
|
+
def _parse_typed_function(self) -> ASTNode:
|
|
721
|
+
"""Parse C-style typed function declaration.
|
|
722
|
+
|
|
723
|
+
Patterns:
|
|
724
|
+
- int Add(int a, int b) { }
|
|
725
|
+
- undefined int Func() { }
|
|
726
|
+
- open void Handler(open Params) { }
|
|
727
|
+
- vector<string> GetNames() { }
|
|
728
|
+
"""
|
|
729
|
+
modifiers = []
|
|
730
|
+
return_type = None
|
|
731
|
+
generic_type = None
|
|
732
|
+
|
|
733
|
+
# Collect modifiers (undefined, open, meta, super, closed, private, virtual)
|
|
734
|
+
while self._check(TokenType.KEYWORD) and self._is_function_modifier(self._current().value):
|
|
735
|
+
modifiers.append(self._advance().value)
|
|
736
|
+
|
|
737
|
+
# Get return type
|
|
738
|
+
if self._check(TokenType.KEYWORD) and self._is_type_keyword(self._current().value):
|
|
739
|
+
return_type = self._advance().value
|
|
740
|
+
|
|
741
|
+
# Check for generic type <T>
|
|
742
|
+
if self._check(TokenType.COMPARE_LT):
|
|
743
|
+
self._advance() # skip <
|
|
744
|
+
generic_parts = []
|
|
745
|
+
depth = 1
|
|
746
|
+
while depth > 0 and not self._is_at_end():
|
|
747
|
+
if self._check(TokenType.COMPARE_LT):
|
|
748
|
+
depth += 1
|
|
749
|
+
generic_parts.append('<')
|
|
750
|
+
elif self._check(TokenType.COMPARE_GT):
|
|
751
|
+
depth -= 1
|
|
752
|
+
if depth > 0:
|
|
753
|
+
generic_parts.append('>')
|
|
754
|
+
elif self._check(TokenType.COMMA):
|
|
755
|
+
generic_parts.append(',')
|
|
756
|
+
else:
|
|
757
|
+
generic_parts.append(self._current().value)
|
|
758
|
+
self._advance()
|
|
759
|
+
generic_type = ''.join(generic_parts)
|
|
760
|
+
|
|
761
|
+
# Get function name
|
|
762
|
+
name = self._advance().value
|
|
763
|
+
|
|
764
|
+
# Parse parameters
|
|
765
|
+
params = []
|
|
766
|
+
self._expect(TokenType.PAREN_START)
|
|
767
|
+
|
|
768
|
+
while not self._check(TokenType.PAREN_END) and not self._is_at_end():
|
|
769
|
+
param_info = {}
|
|
770
|
+
|
|
771
|
+
# Handle 'open' keyword for open parameters
|
|
772
|
+
if self._match_keyword('open'):
|
|
773
|
+
param_info['open'] = True
|
|
774
|
+
|
|
775
|
+
# Handle type annotations
|
|
776
|
+
if self._check(TokenType.KEYWORD) and self._is_type_keyword(self._current().value):
|
|
777
|
+
param_info['type'] = self._advance().value
|
|
778
|
+
|
|
779
|
+
# Check for generic type parameter <T>
|
|
780
|
+
if self._check(TokenType.COMPARE_LT):
|
|
781
|
+
self._advance()
|
|
782
|
+
generic_parts = []
|
|
783
|
+
depth = 1
|
|
784
|
+
while depth > 0 and not self._is_at_end():
|
|
785
|
+
if self._check(TokenType.COMPARE_LT):
|
|
786
|
+
depth += 1
|
|
787
|
+
generic_parts.append('<')
|
|
788
|
+
elif self._check(TokenType.COMPARE_GT):
|
|
789
|
+
depth -= 1
|
|
790
|
+
if depth > 0:
|
|
791
|
+
generic_parts.append('>')
|
|
792
|
+
elif self._check(TokenType.COMMA):
|
|
793
|
+
generic_parts.append(',')
|
|
794
|
+
else:
|
|
795
|
+
generic_parts.append(self._current().value)
|
|
796
|
+
self._advance()
|
|
797
|
+
param_info['generic'] = ''.join(generic_parts)
|
|
798
|
+
|
|
799
|
+
# Handle reference operator &
|
|
800
|
+
if self._match(TokenType.AMPERSAND):
|
|
801
|
+
param_info['ref'] = True
|
|
802
|
+
|
|
803
|
+
# Get parameter name
|
|
804
|
+
if self._check(TokenType.IDENTIFIER):
|
|
805
|
+
param_name = self._advance().value
|
|
806
|
+
if param_info:
|
|
807
|
+
params.append({'name': param_name, **param_info})
|
|
808
|
+
else:
|
|
809
|
+
params.append(param_name)
|
|
810
|
+
elif self._check(TokenType.KEYWORD):
|
|
811
|
+
# Parameter name could be a keyword
|
|
812
|
+
param_name = self._advance().value
|
|
813
|
+
if param_info:
|
|
814
|
+
params.append({'name': param_name, **param_info})
|
|
815
|
+
else:
|
|
816
|
+
params.append(param_name)
|
|
817
|
+
|
|
818
|
+
self._match(TokenType.COMMA)
|
|
819
|
+
|
|
820
|
+
self._expect(TokenType.PAREN_END)
|
|
821
|
+
|
|
822
|
+
# Parse function body
|
|
823
|
+
node = ASTNode('function', value={
|
|
824
|
+
'name': name,
|
|
825
|
+
'params': params,
|
|
826
|
+
'return_type': return_type,
|
|
827
|
+
'generic_type': generic_type,
|
|
828
|
+
'modifiers': modifiers
|
|
829
|
+
}, children=[])
|
|
830
|
+
|
|
831
|
+
self._expect(TokenType.BLOCK_START)
|
|
832
|
+
|
|
833
|
+
while not self._check(TokenType.BLOCK_END) and not self._is_at_end():
|
|
834
|
+
stmt = self._parse_statement()
|
|
835
|
+
if stmt:
|
|
836
|
+
node.children.append(stmt)
|
|
837
|
+
|
|
838
|
+
self._expect(TokenType.BLOCK_END)
|
|
839
|
+
return node
|
|
840
|
+
|
|
841
|
+
def _looks_like_typed_variable(self) -> bool:
|
|
842
|
+
"""Check if current position looks like a typed variable declaration:
|
|
843
|
+
type_name varName; or type_name<T> varName; or type_name varName = value;
|
|
844
|
+
"""
|
|
845
|
+
# Save position
|
|
846
|
+
saved_pos = self.pos
|
|
847
|
+
|
|
848
|
+
# Must start with a type keyword (int, string, stack, vector, etc.)
|
|
849
|
+
if not self._check(TokenType.KEYWORD):
|
|
850
|
+
return False
|
|
851
|
+
|
|
852
|
+
type_name = self._current().value
|
|
853
|
+
|
|
854
|
+
# Skip known type keywords
|
|
855
|
+
type_keywords = {'int', 'string', 'float', 'bool', 'dynamic', 'void',
|
|
856
|
+
'stack', 'vector', 'datastruct', 'dataspace', 'shuffled',
|
|
857
|
+
'iterator', 'combo', 'array', 'openquote', 'json'}
|
|
858
|
+
if type_name not in type_keywords:
|
|
859
|
+
return False
|
|
860
|
+
|
|
861
|
+
self._advance()
|
|
862
|
+
|
|
863
|
+
# Check for optional generic <T>
|
|
864
|
+
if self._match(TokenType.COMPARE_LT):
|
|
865
|
+
# Skip until >
|
|
866
|
+
depth = 1
|
|
867
|
+
while depth > 0 and not self._is_at_end():
|
|
868
|
+
if self._check(TokenType.COMPARE_LT):
|
|
869
|
+
depth += 1
|
|
870
|
+
elif self._check(TokenType.COMPARE_GT):
|
|
871
|
+
depth -= 1
|
|
872
|
+
self._advance()
|
|
873
|
+
|
|
874
|
+
# Next should be an identifier (variable name), not '(' (function) or ';'
|
|
875
|
+
result = self._check(TokenType.IDENTIFIER)
|
|
876
|
+
|
|
877
|
+
# Restore position
|
|
878
|
+
self.pos = saved_pos
|
|
879
|
+
return result
|
|
880
|
+
|
|
881
|
+
def _parse_typed_variable(self) -> Optional[ASTNode]:
|
|
882
|
+
"""Parse a typed variable declaration: type varName; or type<T> varName = value;"""
|
|
883
|
+
# Get type name
|
|
884
|
+
type_name = self._advance().value # Consume type keyword
|
|
885
|
+
|
|
886
|
+
# Check for generic type <T>
|
|
887
|
+
element_type = None
|
|
888
|
+
if self._match(TokenType.COMPARE_LT):
|
|
889
|
+
# Get element type
|
|
890
|
+
if self._check(TokenType.KEYWORD) or self._check(TokenType.IDENTIFIER):
|
|
891
|
+
element_type = self._advance().value
|
|
892
|
+
self._expect(TokenType.COMPARE_GT)
|
|
893
|
+
|
|
894
|
+
# Get variable name
|
|
895
|
+
if not self._check(TokenType.IDENTIFIER):
|
|
896
|
+
return None
|
|
897
|
+
var_name = self._advance().value
|
|
898
|
+
|
|
899
|
+
# Check for assignment or just declaration
|
|
900
|
+
value = None
|
|
901
|
+
if self._match(TokenType.EQUALS):
|
|
902
|
+
value = self._parse_expression()
|
|
903
|
+
|
|
904
|
+
self._match(TokenType.SEMICOLON)
|
|
905
|
+
|
|
906
|
+
return ASTNode('typed_declaration', value={
|
|
907
|
+
'type': type_name,
|
|
908
|
+
'element_type': element_type,
|
|
909
|
+
'name': var_name,
|
|
910
|
+
'value': value
|
|
911
|
+
})
|
|
912
|
+
|
|
591
913
|
def parse_program(self) -> ASTNode:
|
|
592
914
|
"""Parse a standalone program (no service wrapper)"""
|
|
593
915
|
root = ASTNode('program', children=[])
|
|
@@ -597,6 +919,39 @@ class CSSLParser:
|
|
|
597
919
|
root.children.append(self._parse_struct())
|
|
598
920
|
elif self._match_keyword('define'):
|
|
599
921
|
root.children.append(self._parse_define())
|
|
922
|
+
# Check for C-style typed function declarations
|
|
923
|
+
elif self._looks_like_function_declaration():
|
|
924
|
+
root.children.append(self._parse_typed_function())
|
|
925
|
+
# Check for typed variable declarations (int x;, stack<string> s;)
|
|
926
|
+
elif self._looks_like_typed_variable():
|
|
927
|
+
decl = self._parse_typed_variable()
|
|
928
|
+
if decl:
|
|
929
|
+
root.children.append(decl)
|
|
930
|
+
# Handle service blocks
|
|
931
|
+
elif self._match_keyword('service-init'):
|
|
932
|
+
root.children.append(self._parse_service_init())
|
|
933
|
+
elif self._match_keyword('service-include'):
|
|
934
|
+
root.children.append(self._parse_service_include())
|
|
935
|
+
elif self._match_keyword('service-run'):
|
|
936
|
+
root.children.append(self._parse_service_run())
|
|
937
|
+
elif self._match_keyword('package'):
|
|
938
|
+
root.children.append(self._parse_package())
|
|
939
|
+
elif self._match_keyword('package-includes'):
|
|
940
|
+
root.children.append(self._parse_package_includes())
|
|
941
|
+
# Handle global declarations
|
|
942
|
+
elif self._match_keyword('global'):
|
|
943
|
+
stmt = self._parse_expression_statement()
|
|
944
|
+
if stmt:
|
|
945
|
+
# Wrap in global_assignment to mark as global variable
|
|
946
|
+
global_stmt = ASTNode('global_assignment', value=stmt)
|
|
947
|
+
root.children.append(global_stmt)
|
|
948
|
+
elif self._check(TokenType.GLOBAL_REF):
|
|
949
|
+
stmt = self._parse_expression_statement()
|
|
950
|
+
if stmt:
|
|
951
|
+
# Wrap in global_assignment to mark as global variable (same as 'global' keyword)
|
|
952
|
+
global_stmt = ASTNode('global_assignment', value=stmt)
|
|
953
|
+
root.children.append(global_stmt)
|
|
954
|
+
# Handle statements
|
|
600
955
|
elif self._check(TokenType.IDENTIFIER) or self._check(TokenType.AT) or self._check(TokenType.SELF_REF):
|
|
601
956
|
stmt = self._parse_expression_statement()
|
|
602
957
|
if stmt:
|
|
@@ -609,6 +964,9 @@ class CSSLParser:
|
|
|
609
964
|
root.children.append(self._parse_for())
|
|
610
965
|
elif self._match_keyword('foreach'):
|
|
611
966
|
root.children.append(self._parse_foreach())
|
|
967
|
+
# Skip comments and newlines
|
|
968
|
+
elif self._check(TokenType.COMMENT) or self._check(TokenType.NEWLINE):
|
|
969
|
+
self._advance()
|
|
612
970
|
else:
|
|
613
971
|
self._advance()
|
|
614
972
|
|
|
@@ -837,8 +1195,31 @@ class CSSLParser:
|
|
|
837
1195
|
|
|
838
1196
|
if self._match(TokenType.PAREN_START):
|
|
839
1197
|
while not self._check(TokenType.PAREN_END):
|
|
1198
|
+
param_info = {}
|
|
1199
|
+
# Handle 'open' keyword for open parameters
|
|
1200
|
+
if self._match_keyword('open'):
|
|
1201
|
+
param_info['open'] = True
|
|
1202
|
+
# Handle type annotations (e.g., string, int, dynamic, etc.)
|
|
1203
|
+
if self._check(TokenType.KEYWORD):
|
|
1204
|
+
param_info['type'] = self._advance().value
|
|
1205
|
+
# Handle reference operator &
|
|
1206
|
+
if self._match(TokenType.AMPERSAND):
|
|
1207
|
+
param_info['ref'] = True
|
|
1208
|
+
# Get parameter name
|
|
840
1209
|
if self._check(TokenType.IDENTIFIER):
|
|
841
|
-
|
|
1210
|
+
param_name = self._advance().value
|
|
1211
|
+
if param_info:
|
|
1212
|
+
params.append({'name': param_name, **param_info})
|
|
1213
|
+
else:
|
|
1214
|
+
params.append(param_name)
|
|
1215
|
+
self._match(TokenType.COMMA)
|
|
1216
|
+
elif self._check(TokenType.KEYWORD):
|
|
1217
|
+
# Parameter name could be a keyword like 'Params'
|
|
1218
|
+
param_name = self._advance().value
|
|
1219
|
+
if param_info:
|
|
1220
|
+
params.append({'name': param_name, **param_info})
|
|
1221
|
+
else:
|
|
1222
|
+
params.append(param_name)
|
|
842
1223
|
self._match(TokenType.COMMA)
|
|
843
1224
|
else:
|
|
844
1225
|
break
|
|
@@ -878,6 +1259,15 @@ class CSSLParser:
|
|
|
878
1259
|
return self._parse_try()
|
|
879
1260
|
elif self._match_keyword('await'):
|
|
880
1261
|
return self._parse_await()
|
|
1262
|
+
elif self._match_keyword('define'):
|
|
1263
|
+
# Nested define function
|
|
1264
|
+
return self._parse_define()
|
|
1265
|
+
elif self._looks_like_typed_variable():
|
|
1266
|
+
# Typed variable declaration (e.g., stack<string> myStack;)
|
|
1267
|
+
return self._parse_typed_variable()
|
|
1268
|
+
elif self._looks_like_function_declaration():
|
|
1269
|
+
# Nested typed function (e.g., void Level2() { ... })
|
|
1270
|
+
return self._parse_typed_function()
|
|
881
1271
|
elif self._check(TokenType.IDENTIFIER) or self._check(TokenType.AT):
|
|
882
1272
|
return self._parse_expression_statement()
|
|
883
1273
|
else:
|
|
@@ -1077,6 +1467,9 @@ class CSSLParser:
|
|
|
1077
1467
|
# Check for define statements inside action block
|
|
1078
1468
|
if self._match_keyword('define'):
|
|
1079
1469
|
node.children.append(self._parse_define())
|
|
1470
|
+
# Check for typed function definitions (nested functions)
|
|
1471
|
+
elif self._looks_like_function_declaration():
|
|
1472
|
+
node.children.append(self._parse_typed_function())
|
|
1080
1473
|
else:
|
|
1081
1474
|
stmt = self._parse_statement()
|
|
1082
1475
|
if stmt:
|
|
@@ -1320,12 +1713,39 @@ class CSSLParser:
|
|
|
1320
1713
|
if self._match(TokenType.MINUS):
|
|
1321
1714
|
operand = self._parse_unary()
|
|
1322
1715
|
return ASTNode('unary', value={'op': '-', 'operand': operand})
|
|
1716
|
+
if self._match(TokenType.AMPERSAND):
|
|
1717
|
+
# Reference operator: &variable or &@module
|
|
1718
|
+
operand = self._parse_unary()
|
|
1719
|
+
return ASTNode('reference', value=operand)
|
|
1323
1720
|
|
|
1324
1721
|
return self._parse_primary()
|
|
1325
1722
|
|
|
1326
1723
|
def _parse_primary(self) -> ASTNode:
|
|
1327
1724
|
if self._match(TokenType.AT):
|
|
1328
|
-
|
|
1725
|
+
node = self._parse_module_reference()
|
|
1726
|
+
# Continue to check for calls, indexing, member access on module refs
|
|
1727
|
+
while True:
|
|
1728
|
+
if self._match(TokenType.PAREN_START):
|
|
1729
|
+
# Function call on module ref: @Module.method()
|
|
1730
|
+
args = []
|
|
1731
|
+
while not self._check(TokenType.PAREN_END) and not self._is_at_end():
|
|
1732
|
+
args.append(self._parse_expression())
|
|
1733
|
+
if not self._check(TokenType.PAREN_END):
|
|
1734
|
+
self._expect(TokenType.COMMA)
|
|
1735
|
+
self._expect(TokenType.PAREN_END)
|
|
1736
|
+
node = ASTNode('call', value={'callee': node, 'args': args})
|
|
1737
|
+
elif self._match(TokenType.DOT):
|
|
1738
|
+
# Member access: @Module.property
|
|
1739
|
+
member = self._advance().value
|
|
1740
|
+
node = ASTNode('member_access', value={'object': node, 'member': member})
|
|
1741
|
+
elif self._match(TokenType.BRACKET_START):
|
|
1742
|
+
# Index access: @Module[index]
|
|
1743
|
+
index = self._parse_expression()
|
|
1744
|
+
self._expect(TokenType.BRACKET_END)
|
|
1745
|
+
node = ASTNode('index_access', value={'object': node, 'index': index})
|
|
1746
|
+
else:
|
|
1747
|
+
break
|
|
1748
|
+
return node
|
|
1329
1749
|
|
|
1330
1750
|
if self._check(TokenType.SELF_REF):
|
|
1331
1751
|
# s@<name> self-reference to global struct
|
|
@@ -1342,6 +1762,31 @@ class CSSLParser:
|
|
|
1342
1762
|
node = ASTNode('call', value={'callee': node, 'args': args})
|
|
1343
1763
|
return node
|
|
1344
1764
|
|
|
1765
|
+
if self._check(TokenType.GLOBAL_REF):
|
|
1766
|
+
# r@<name> global variable reference/declaration
|
|
1767
|
+
token = self._advance()
|
|
1768
|
+
node = ASTNode('global_ref', value=token.value, line=token.line, column=token.column)
|
|
1769
|
+
# Check for member access, calls, indexing
|
|
1770
|
+
while True:
|
|
1771
|
+
if self._match(TokenType.PAREN_START):
|
|
1772
|
+
args = []
|
|
1773
|
+
while not self._check(TokenType.PAREN_END):
|
|
1774
|
+
args.append(self._parse_expression())
|
|
1775
|
+
if not self._check(TokenType.PAREN_END):
|
|
1776
|
+
self._expect(TokenType.COMMA)
|
|
1777
|
+
self._expect(TokenType.PAREN_END)
|
|
1778
|
+
node = ASTNode('call', value={'callee': node, 'args': args})
|
|
1779
|
+
elif self._match(TokenType.DOT):
|
|
1780
|
+
member = self._advance().value
|
|
1781
|
+
node = ASTNode('member_access', value={'object': node, 'member': member})
|
|
1782
|
+
elif self._match(TokenType.BRACKET_START):
|
|
1783
|
+
index = self._parse_expression()
|
|
1784
|
+
self._expect(TokenType.BRACKET_END)
|
|
1785
|
+
node = ASTNode('index_access', value={'object': node, 'index': index})
|
|
1786
|
+
else:
|
|
1787
|
+
break
|
|
1788
|
+
return node
|
|
1789
|
+
|
|
1345
1790
|
if self._check(TokenType.NUMBER):
|
|
1346
1791
|
return ASTNode('literal', value=self._advance().value)
|
|
1347
1792
|
|