@@ -36,8 +36,47 @@ fn parse_sub_expr(p: &mut LuaParser, limit: i32) -> ParseResult {
3636 parse_simple_expr ( p) ?
3737 } ;
3838
39- let mut bop = LuaOpKind :: to_binary_operator ( p. current_token ( ) ) ;
40- while bop != BinaryOperator :: OpNop && bop. get_priority ( ) . left > limit {
39+ const TERNARY_LEFT : i32 = 1 ;
40+
41+ loop {
42+ if p. current_token ( ) == LuaTokenKind :: TkTernary && TERNARY_LEFT > limit {
43+ let m = cm. precede ( p, LuaSyntaxKind :: TernaryExpr ) ;
44+ p. bump ( ) ; // consume '?'
45+ match parse_sub_expr ( p, 0 ) {
46+ Ok ( _) => { }
47+ Err ( _) => {
48+ p. push_error ( LuaParseError :: syntax_error_from (
49+ & t ! ( "expected expression after '?'" ) ,
50+ p. current_token_range ( ) ,
51+ ) ) ;
52+ }
53+ }
54+ match expect_token ( p, LuaTokenKind :: TkColon ) {
55+ Ok ( _) => { }
56+ Err ( _) => {
57+ p. push_error ( LuaParseError :: syntax_error_from (
58+ & t ! ( "expected ':' in ternary expression" ) ,
59+ p. current_token_range ( ) ,
60+ ) ) ;
61+ }
62+ }
63+ match parse_sub_expr ( p, 0 ) {
64+ Ok ( _) => { }
65+ Err ( _) => {
66+ p. push_error ( LuaParseError :: syntax_error_from (
67+ & t ! ( "expected expression after ':'" ) ,
68+ p. current_token_range ( ) ,
69+ ) ) ;
70+ }
71+ }
72+ cm = m. complete ( p) ;
73+ continue ;
74+ }
75+
76+ let bop = LuaOpKind :: to_binary_operator ( p. current_token ( ) ) ;
77+ if bop == BinaryOperator :: OpNop || bop. get_priority ( ) . left <= limit {
78+ break ;
79+ }
4180 let op_range = p. current_token_range ( ) ;
4281 let op_token = p. current_token ( ) ;
4382 let m = cm. precede ( p, LuaSyntaxKind :: BinaryExpr ) ;
@@ -58,7 +97,6 @@ fn parse_sub_expr(p: &mut LuaParser, limit: i32) -> ParseResult {
5897 }
5998
6099 cm = m. complete ( p) ;
61- bop = LuaOpKind :: to_binary_operator ( p. current_token ( ) ) ;
62100 }
63101
64102 Ok ( cm)
@@ -526,6 +564,15 @@ fn parse_suffixed_expr(p: &mut LuaParser) -> ParseResult {
526564 }
527565 cm = m. complete ( p) ;
528566 }
567+ LuaTokenKind :: TkSafeNavigation => {
568+ let m = cm. precede ( p, LuaSyntaxKind :: SafeIndexExpr ) ;
569+ p. bump ( ) ; // consume '?.'
570+ if let Err ( err) = parse_safe_index_struct ( p) {
571+ m. complete ( p) ;
572+ return Err ( err) ;
573+ }
574+ cm = m. complete ( p) ;
575+ }
529576 LuaTokenKind :: TkLeftParen
530577 | LuaTokenKind :: TkLongString
531578 | LuaTokenKind :: TkString
@@ -660,6 +707,87 @@ fn parse_index_struct(p: &mut LuaParser) -> Result<(), ParseFailReason> {
660707 Ok ( ( ) )
661708}
662709
710+ /// Parses a safe index structure, which is used in safe navigation expressions (e.g., `?.`).
711+ /// like `?.[expr]`, `?.name`, or `?.:method()`.
712+ /// call like `?.(`, `?. ""`, or `?. {}`.
713+ fn parse_safe_index_struct ( p : & mut LuaParser ) -> Result < ( ) , ParseFailReason > {
714+ let index_op_range = p. current_token_range ( ) ;
715+ match p. current_token ( ) {
716+ LuaTokenKind :: TkLeftBracket => {
717+ p. bump ( ) ;
718+ match parse_expr ( p) {
719+ Ok ( _) => { }
720+ Err ( err) => {
721+ p. push_error ( LuaParseError :: syntax_error_from (
722+ & t ! ( "expected expression inside table index brackets" ) ,
723+ index_op_range,
724+ ) ) ;
725+ return Err ( err) ;
726+ }
727+ }
728+ match expect_token ( p, LuaTokenKind :: TkRightBracket ) {
729+ Ok ( _) => { }
730+ Err ( err) => {
731+ p. push_error ( LuaParseError :: syntax_error_from (
732+ & t ! ( "expected ']' to close table index" ) ,
733+ index_op_range,
734+ ) ) ;
735+ return Err ( err) ;
736+ }
737+ }
738+ }
739+ LuaTokenKind :: TkName => {
740+ p. bump ( ) ;
741+ }
742+ LuaTokenKind :: TkColon => {
743+ p. bump ( ) ;
744+ let name_token_range = p. current_token_range ( ) ;
745+ match expect_token ( p, LuaTokenKind :: TkName ) {
746+ Ok ( _) => { }
747+ Err ( err) => {
748+ p. push_error ( LuaParseError :: syntax_error_from (
749+ & t ! ( "expected method name after ':'" ) ,
750+ index_op_range,
751+ ) ) ;
752+ return Err ( err) ;
753+ }
754+ }
755+ if !matches ! (
756+ p. current_token( ) ,
757+ LuaTokenKind :: TkLeftParen
758+ | LuaTokenKind :: TkLeftBrace
759+ | LuaTokenKind :: TkString
760+ | LuaTokenKind :: TkLongString
761+ ) {
762+ p. push_error ( LuaParseError :: syntax_error_from (
763+ & t ! (
764+ "colon accessor must be followed by a function call or table constructor or string literal"
765+ ) ,
766+ name_token_range,
767+ ) ) ;
768+
769+ return Err ( ParseFailReason :: UnexpectedToken ) ;
770+ }
771+ }
772+ LuaTokenKind :: TkString
773+ | LuaTokenKind :: TkLongString
774+ | LuaTokenKind :: TkLeftParen
775+ | LuaTokenKind :: TkLeftBrace => {
776+ // allow these tokens to be part of a safe navigation call expression
777+ }
778+ _ => {
779+ p. push_error ( LuaParseError :: syntax_error_from (
780+ & t ! ( "expect safe index struct after '?.'" ) ,
781+ p. current_token_range ( ) ,
782+ ) ) ;
783+
784+ return Err ( ParseFailReason :: UnexpectedToken ) ;
785+ }
786+ }
787+
788+ Ok ( ( ) )
789+ }
790+
663791fn parse_args ( p : & mut LuaParser ) -> ParseResult {
664792 let m = p. mark ( LuaSyntaxKind :: CallArgList ) ;
665793 match p. current_token ( ) {
0 commit comments