Skip to content

Commit 3374750

Browse files
committed
AST: Added extends infer keywords and implemented conditional types.
1 parent d607e5d commit 3374750

File tree

7 files changed

+297
-6
lines changed

7 files changed

+297
-6
lines changed

crates/emmylua_parser/src/grammar/doc/tag.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -135,16 +135,21 @@ fn parse_generic_decl_list(p: &mut LuaDocParser, allow_angle_brackets: bool) ->
135135
}
136136

137137
// A : type
138+
// A extends type
138139
// A
139140
// A ...
140141
// A ... : type
142+
// A ... extends type
141143
fn parse_generic_param(p: &mut LuaDocParser) -> DocParseResult {
142144
let m = p.mark(LuaSyntaxKind::DocGenericParameter);
143145
expect_token(p, LuaTokenKind::TkName)?;
144146
if p.current_token() == LuaTokenKind::TkDots {
145147
p.bump();
146148
}
147-
if p.current_token() == LuaTokenKind::TkColon {
149+
if matches!(
150+
p.current_token(),
151+
LuaTokenKind::TkColon | LuaTokenKind::TkDocExtends
152+
) {
148153
p.bump();
149154
parse_type(p)?;
150155
}

crates/emmylua_parser/src/grammar/doc/test.rs

Lines changed: 204 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2867,4 +2867,208 @@ Syntax(Chunk)@0..137
28672867

28682868
assert_ast_eq!(code, result);
28692869
}
2870+
2871+
#[test]
2872+
fn test_infer_keyword() {
2873+
// 只有在 extends 后的 infer 才能被视为关键词
2874+
{
2875+
let code = r#"
2876+
---@alias Foo infer
2877+
"#;
2878+
let result = r#"
2879+
Syntax(Chunk)@0..37
2880+
Syntax(Block)@0..37
2881+
Token(TkEndOfLine)@0..1 "\n"
2882+
Token(TkWhitespace)@1..9 " "
2883+
Syntax(Comment)@9..28
2884+
Token(TkDocStart)@9..13 "---@"
2885+
Syntax(DocTagAlias)@13..28
2886+
Token(TkTagAlias)@13..18 "alias"
2887+
Token(TkWhitespace)@18..19 " "
2888+
Token(TkName)@19..22 "Foo"
2889+
Token(TkWhitespace)@22..23 " "
2890+
Syntax(TypeName)@23..28
2891+
Token(TkName)@23..28 "infer"
2892+
Token(TkEndOfLine)@28..29 "\n"
2893+
Token(TkWhitespace)@29..37 " "
2894+
"#;
2895+
assert_ast_eq!(code, result);
2896+
}
2897+
{
2898+
let code = r#"---@alias ConstructorParameters<T> T extends infer P and P or unknown"#;
2899+
let result = r#"
2900+
Syntax(Chunk)@0..69
2901+
Syntax(Block)@0..69
2902+
Syntax(Comment)@0..69
2903+
Token(TkDocStart)@0..4 "---@"
2904+
Syntax(DocTagAlias)@4..69
2905+
Token(TkTagAlias)@4..9 "alias"
2906+
Token(TkWhitespace)@9..10 " "
2907+
Token(TkName)@10..31 "ConstructorParameters"
2908+
Syntax(DocGenericDeclareList)@31..34
2909+
Token(TkLt)@31..32 "<"
2910+
Syntax(DocGenericParameter)@32..33
2911+
Token(TkName)@32..33 "T"
2912+
Token(TkGt)@33..34 ">"
2913+
Token(TkWhitespace)@34..35 " "
2914+
Syntax(TypeConditional)@35..69
2915+
Syntax(TypeBinary)@35..52
2916+
Syntax(TypeName)@35..36
2917+
Token(TkName)@35..36 "T"
2918+
Token(TkWhitespace)@36..37 " "
2919+
Token(TkDocExtends)@37..44 "extends"
2920+
Token(TkWhitespace)@44..45 " "
2921+
Syntax(TypeInfer)@45..52
2922+
Token(TkName)@45..50 "infer"
2923+
Token(TkWhitespace)@50..51 " "
2924+
Syntax(DocGenericParameter)@51..52
2925+
Token(TkName)@51..52 "P"
2926+
Token(TkWhitespace)@52..53 " "
2927+
Token(TkAnd)@53..56 "and"
2928+
Token(TkWhitespace)@56..57 " "
2929+
Syntax(TypeName)@57..58
2930+
Token(TkName)@57..58 "P"
2931+
Token(TkWhitespace)@58..59 " "
2932+
Token(TkOr)@59..61 "or"
2933+
Token(TkWhitespace)@61..62 " "
2934+
Syntax(TypeName)@62..69
2935+
Token(TkName)@62..69 "unknown"
2936+
"#;
2937+
assert_ast_eq!(code, result);
2938+
}
2939+
}
2940+
2941+
#[test]
2942+
fn test_alias_conditional_infer() {
2943+
let code = r#"
2944+
---@alias ConstructorParameters<T> T extends (fun(infer: infer P): any) and P or unknown
2945+
"#;
2946+
2947+
let result = r#"
2948+
Syntax(Chunk)@0..106
2949+
Syntax(Block)@0..106
2950+
Token(TkEndOfLine)@0..1 "\n"
2951+
Token(TkWhitespace)@1..9 " "
2952+
Syntax(Comment)@9..97
2953+
Token(TkDocStart)@9..13 "---@"
2954+
Syntax(DocTagAlias)@13..97
2955+
Token(TkTagAlias)@13..18 "alias"
2956+
Token(TkWhitespace)@18..19 " "
2957+
Token(TkName)@19..40 "ConstructorParameters"
2958+
Syntax(DocGenericDeclareList)@40..43
2959+
Token(TkLt)@40..41 "<"
2960+
Syntax(DocGenericParameter)@41..42
2961+
Token(TkName)@41..42 "T"
2962+
Token(TkGt)@42..43 ">"
2963+
Token(TkWhitespace)@43..44 " "
2964+
Syntax(TypeConditional)@44..97
2965+
Syntax(TypeBinary)@44..80
2966+
Syntax(TypeName)@44..45
2967+
Token(TkName)@44..45 "T"
2968+
Token(TkWhitespace)@45..46 " "
2969+
Token(TkDocExtends)@46..53 "extends"
2970+
Token(TkWhitespace)@53..54 " "
2971+
Token(TkLeftParen)@54..55 "("
2972+
Syntax(TypeFun)@55..79
2973+
Token(TkName)@55..58 "fun"
2974+
Token(TkLeftParen)@58..59 "("
2975+
Syntax(DocTypedParameter)@59..73
2976+
Token(TkName)@59..64 "infer"
2977+
Token(TkColon)@64..65 ":"
2978+
Token(TkWhitespace)@65..66 " "
2979+
Syntax(TypeInfer)@66..73
2980+
Token(TkName)@66..71 "infer"
2981+
Token(TkWhitespace)@71..72 " "
2982+
Syntax(DocGenericParameter)@72..73
2983+
Token(TkName)@72..73 "P"
2984+
Token(TkRightParen)@73..74 ")"
2985+
Token(TkColon)@74..75 ":"
2986+
Token(TkWhitespace)@75..76 " "
2987+
Syntax(DocTypeList)@76..79
2988+
Syntax(DocNamedReturnType)@76..79
2989+
Syntax(TypeName)@76..79
2990+
Token(TkName)@76..79 "any"
2991+
Token(TkRightParen)@79..80 ")"
2992+
Token(TkWhitespace)@80..81 " "
2993+
Token(TkAnd)@81..84 "and"
2994+
Token(TkWhitespace)@84..85 " "
2995+
Syntax(TypeName)@85..86
2996+
Token(TkName)@85..86 "P"
2997+
Token(TkWhitespace)@86..87 " "
2998+
Token(TkOr)@87..89 "or"
2999+
Token(TkWhitespace)@89..90 " "
3000+
Syntax(TypeName)@90..97
3001+
Token(TkName)@90..97 "unknown"
3002+
Token(TkEndOfLine)@97..98 "\n"
3003+
Token(TkWhitespace)@98..106 " "
3004+
"#;
3005+
3006+
assert_ast_eq!(code, result);
3007+
}
3008+
3009+
#[test]
3010+
fn test_alias_nested_conditional() {
3011+
let code = r#"
3012+
---@alias IsFortyTwo<T> T extends number and T extends 42 and true or false or false
3013+
"#;
3014+
3015+
let result = r#"
3016+
Syntax(Chunk)@0..102
3017+
Syntax(Block)@0..102
3018+
Token(TkEndOfLine)@0..1 "\n"
3019+
Token(TkWhitespace)@1..9 " "
3020+
Syntax(Comment)@9..93
3021+
Token(TkDocStart)@9..13 "---@"
3022+
Syntax(DocTagAlias)@13..93
3023+
Token(TkTagAlias)@13..18 "alias"
3024+
Token(TkWhitespace)@18..19 " "
3025+
Token(TkName)@19..29 "IsFortyTwo"
3026+
Syntax(DocGenericDeclareList)@29..32
3027+
Token(TkLt)@29..30 "<"
3028+
Syntax(DocGenericParameter)@30..31
3029+
Token(TkName)@30..31 "T"
3030+
Token(TkGt)@31..32 ">"
3031+
Token(TkWhitespace)@32..33 " "
3032+
Syntax(TypeConditional)@33..93
3033+
Syntax(TypeBinary)@33..49
3034+
Syntax(TypeName)@33..34
3035+
Token(TkName)@33..34 "T"
3036+
Token(TkWhitespace)@34..35 " "
3037+
Token(TkDocExtends)@35..42 "extends"
3038+
Token(TkWhitespace)@42..43 " "
3039+
Syntax(TypeName)@43..49
3040+
Token(TkName)@43..49 "number"
3041+
Token(TkWhitespace)@49..50 " "
3042+
Token(TkAnd)@50..53 "and"
3043+
Token(TkWhitespace)@53..54 " "
3044+
Syntax(TypeConditional)@54..84
3045+
Syntax(TypeBinary)@54..66
3046+
Syntax(TypeName)@54..55
3047+
Token(TkName)@54..55 "T"
3048+
Token(TkWhitespace)@55..56 " "
3049+
Token(TkDocExtends)@56..63 "extends"
3050+
Token(TkWhitespace)@63..64 " "
3051+
Syntax(TypeLiteral)@64..66
3052+
Token(TkInt)@64..66 "42"
3053+
Token(TkWhitespace)@66..67 " "
3054+
Token(TkAnd)@67..70 "and"
3055+
Token(TkWhitespace)@70..71 " "
3056+
Syntax(TypeLiteral)@71..75
3057+
Token(TkTrue)@71..75 "true"
3058+
Token(TkWhitespace)@75..76 " "
3059+
Token(TkOr)@76..78 "or"
3060+
Token(TkWhitespace)@78..79 " "
3061+
Syntax(TypeLiteral)@79..84
3062+
Token(TkFalse)@79..84 "false"
3063+
Token(TkWhitespace)@84..85 " "
3064+
Token(TkOr)@85..87 "or"
3065+
Token(TkWhitespace)@87..88 " "
3066+
Syntax(TypeLiteral)@88..93
3067+
Token(TkFalse)@88..93 "false"
3068+
Token(TkEndOfLine)@93..94 "\n"
3069+
Token(TkWhitespace)@94..102 " "
3070+
"#;
3071+
3072+
assert_ast_eq!(code, result);
3073+
}
28703074
}

crates/emmylua_parser/src/grammar/doc/types.rs

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,9 @@ pub fn parse_type(p: &mut LuaDocParser) -> DocParseResult {
2828
LuaTokenKind::TkAnd => {
2929
let m = cm.precede(p, LuaSyntaxKind::TypeConditional);
3030
p.bump();
31-
parse_sub_type(p, 0)?;
31+
parse_type(p)?;
3232
expect_token(p, LuaTokenKind::TkOr)?;
33-
parse_sub_type(p, 0)?;
33+
parse_type(p)?;
3434
cm = m.complete(p);
3535
break;
3636
}
@@ -82,7 +82,16 @@ fn parse_sub_type(p: &mut LuaDocParser, limit: i32) -> DocParseResult {
8282
let m = cm.precede(p, LuaSyntaxKind::TypeBinary);
8383
p.bump();
8484
if p.current_token() != LuaTokenKind::TkDocQuestion {
85-
match parse_sub_type(p, bop.get_priority().right) {
85+
// infer 只有在条件类型中才能被解析为关键词
86+
let parse_result = if bop == LuaTypeBinaryOperator::Extends {
87+
p.infer_depth += 1;
88+
let res = parse_sub_type(p, bop.get_priority().right);
89+
p.infer_depth = p.infer_depth.saturating_sub(1);
90+
res
91+
} else {
92+
parse_sub_type(p, bop.get_priority().right)
93+
};
94+
match parse_result {
8695
Ok(_) => {}
8796
Err(err) => {
8897
p.push_error(LuaParseError::doc_error_from(
@@ -131,7 +140,13 @@ fn parse_primary_type(p: &mut LuaDocParser) -> DocParseResult {
131140
| LuaTokenKind::TkInt
132141
| LuaTokenKind::TkTrue
133142
| LuaTokenKind::TkFalse => parse_literal_type(p),
134-
LuaTokenKind::TkName => parse_name_or_func_type(p),
143+
LuaTokenKind::TkName => {
144+
if p.is_infer_context() && p.current_token_text() == "infer" {
145+
parse_infer_type(p)
146+
} else {
147+
parse_name_or_func_type(p)
148+
}
149+
}
135150
LuaTokenKind::TkStringTemplateType => parse_string_template_type(p),
136151
LuaTokenKind::TkDots => parse_vararg_type(p),
137152
_ => Err(LuaParseError::doc_error_from(
@@ -348,6 +363,15 @@ fn parse_name_type(p: &mut LuaDocParser) -> DocParseResult {
348363
Ok(m.complete(p))
349364
}
350365

366+
fn parse_infer_type(p: &mut LuaDocParser) -> DocParseResult {
367+
let m = p.mark(LuaSyntaxKind::TypeInfer);
368+
p.bump();
369+
let param = p.mark(LuaSyntaxKind::DocGenericParameter);
370+
expect_token(p, LuaTokenKind::TkName)?;
371+
param.complete(p);
372+
Ok(m.complete(p))
373+
}
374+
351375
// `<name type>`
352376
fn parse_string_template_type(p: &mut LuaDocParser) -> DocParseResult {
353377
let m = p.mark(LuaSyntaxKind::TypeStringTemplate);

crates/emmylua_parser/src/kind/lua_syntax_kind.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,7 @@ pub enum LuaSyntaxKind {
104104
TypeObject, // { a: aType, b: bType } or { [1]: aType, [2]: bType } or { a: aType, b: bType, [number]: string }
105105
TypeLiteral, // "string" or <integer> or true or false
106106
TypeName, // name
107+
TypeInfer, // infer T
107108
TypeVariadic, // type...
108109
TypeNullable, // <Type>?
109110
TypeStringTemplate, // prefixName.`T`

crates/emmylua_parser/src/parser/lua_doc_parser.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ pub struct LuaDocParser<'a, 'b> {
1515
current_token: LuaTokenKind,
1616
current_token_range: SourceRange,
1717
origin_token_index: usize,
18+
pub infer_depth: usize,
1819
}
1920

2021
impl MarkerEventContainer for LuaDocParser<'_, '_> {
@@ -46,6 +47,7 @@ impl<'b> LuaDocParser<'_, 'b> {
4647
current_token: LuaTokenKind::None,
4748
current_token_range: SourceRange::EMPTY,
4849
origin_token_index: 0,
50+
infer_depth: 0,
4951
};
5052

5153
parser.init();
@@ -184,6 +186,10 @@ impl<'b> LuaDocParser<'_, 'b> {
184186
self.lua_parser.origin_text()
185187
}
186188

189+
pub fn is_infer_context(&self) -> bool {
190+
self.infer_depth > 0
191+
}
192+
187193
pub fn set_state(&mut self, state: LuaDocLexerState) {
188194
match state {
189195
LuaDocLexerState::Description => {

0 commit comments

Comments
 (0)