diff --git a/src/grammar.ron b/src/grammar.ron index 10955656168..8e8a7101020 100644 --- a/src/grammar.ron +++ b/src/grammar.ron @@ -70,6 +70,7 @@ Grammar( "ref", "let", "move", + "return", ], contextual_keywords: [ "auto", @@ -138,6 +139,8 @@ Grammar( "STRUCT_LIT", "STRUCT_LIT_FIELD", "IF_EXPR", + "BLOCK_EXPR", + "RETURN_EXPR", "EXTERN_BLOCK_EXPR", "ENUM_VARIANT", @@ -155,7 +158,6 @@ Grammar( "ABI", "NAME", "NAME_REF", - "BLOCK_EXPR", "LET_STMT", "EXPR_STMT", diff --git a/src/grammar/expressions.rs b/src/grammar/expressions.rs index 5c59843a42d..423e1a95ac0 100644 --- a/src/grammar/expressions.rs +++ b/src/grammar/expressions.rs @@ -13,18 +13,20 @@ // let _ = b"e"; // let _ = br"f"; // } +const LITERAL_FIRST: TokenSet = + token_set![TRUE_KW, FALSE_KW, INT_NUMBER, FLOAT_NUMBER, BYTE, CHAR, + STRING, RAW_STRING, BYTE_STRING, RAW_BYTE_STRING]; + pub(super) fn literal(p: &mut Parser) -> Option { - match p.current() { - TRUE_KW | FALSE_KW | INT_NUMBER | FLOAT_NUMBER | BYTE | CHAR | STRING | RAW_STRING - | BYTE_STRING | RAW_BYTE_STRING => { - let m = p.start(); - p.bump(); - Some(m.complete(p, LITERAL)) - } - _ => None, + if !LITERAL_FIRST.contains(p.current()) { + return None; } + let m = p.start(); + p.bump(); + Some(m.complete(p, LITERAL)) } +const EXPR_FIRST: TokenSet = PREFIX_EXPR_FIRST; pub(super) fn expr(p: &mut Parser) { let mut lhs = match prefix_expr(p) { Some(lhs) => lhs, @@ -80,6 +82,11 @@ fn let_stmt(p: &mut Parser) { m.complete(p, LET_STMT); } +const PREFIX_EXPR_FIRST: TokenSet = + token_set_union![ + token_set![AMPERSAND, STAR, EXCL], + ATOM_EXPR_FIRST, + ]; fn prefix_expr(p: &mut Parser) -> Option { let done = match p.current() { AMPERSAND => ref_expr(p), @@ -128,6 +135,11 @@ fn not_expr(p: &mut Parser) -> CompletedMarker { m.complete(p, NOT_EXPR) } +const ATOM_EXPR_FIRST: TokenSet = + token_set_union![ + LITERAL_FIRST, + token_set![L_PAREN, PIPE, MOVE_KW, IF_KW, UNSAFE_KW, L_CURLY, RETURN_KW], + ]; fn atom_expr(p: &mut Parser) -> Option { match literal(p) { Some(m) => return Some(m), @@ -144,6 +156,7 @@ fn atom_expr(p: &mut Parser) -> Option { IF_KW => if_expr(p), UNSAFE_KW if la == L_CURLY => block_expr(p), L_CURLY => block_expr(p), + RETURN_KW => return_expr(p), _ => { p.err_and_bump("expected expression"); return None; @@ -237,6 +250,21 @@ fn block_expr(p: &mut Parser) -> CompletedMarker { m.complete(p, BLOCK_EXPR) } +// test return_expr +// fn foo() { +// return; +// return 92; +// } +fn return_expr(p: &mut Parser) -> CompletedMarker { + assert!(p.at(RETURN_KW)); + let m = p.start(); + p.bump(); + if EXPR_FIRST.contains(p.current()) { + expr(p); + } + m.complete(p, RETURN_EXPR) +} + // test call_expr // fn foo() { // let _ = f(); diff --git a/src/parser_api.rs b/src/parser_api.rs index 5a0da32c950..d12f773b2ce 100644 --- a/src/parser_api.rs +++ b/src/parser_api.rs @@ -3,22 +3,22 @@ SyntaxKind::{self, ERROR}, }; -pub(crate) struct TokenSet { - pub tokens: &'static [SyntaxKind], +#[derive(Clone, Copy)] +pub(crate) struct TokenSet(pub(crate) u128); +fn mask(kind: SyntaxKind) -> u128 { + 1u128 << (kind as usize) } impl TokenSet { pub fn contains(&self, kind: SyntaxKind) -> bool { - self.tokens.contains(&kind) + self.0 & mask(kind) != 0 } } #[macro_export] macro_rules! token_set { ($($t:ident),*) => { - TokenSet { - tokens: &[$($t),*], - } + TokenSet($(1u128 << ($t as usize))|*) }; ($($t:ident),* ,) => { @@ -26,6 +26,17 @@ macro_rules! token_set { }; } +#[macro_export] +macro_rules! token_set_union { + ($($ts:expr),*) => { + TokenSet($($ts.0)|*) + }; + + ($($ts:expr),* ,) => { + token_set_union!($($ts),*) + }; +} + /// `Parser` struct provides the low-level API for /// navigating through the stream of tokens and /// constructing the parse tree. The actual parsing diff --git a/src/syntax_kinds/generated.rs b/src/syntax_kinds/generated.rs index 5f26cbf0a89..50aa2b580b3 100644 --- a/src/syntax_kinds/generated.rs +++ b/src/syntax_kinds/generated.rs @@ -71,6 +71,7 @@ pub enum SyntaxKind { REF_KW, LET_KW, MOVE_KW, + RETURN_KW, AUTO_KW, DEFAULT_KW, UNION_KW, @@ -129,6 +130,8 @@ pub enum SyntaxKind { STRUCT_LIT, STRUCT_LIT_FIELD, IF_EXPR, + BLOCK_EXPR, + RETURN_EXPR, EXTERN_BLOCK_EXPR, ENUM_VARIANT, NAMED_FIELD, @@ -145,7 +148,6 @@ pub enum SyntaxKind { ABI, NAME, NAME_REF, - BLOCK_EXPR, LET_STMT, EXPR_STMT, TYPE_PARAM_LIST, @@ -202,6 +204,7 @@ pub fn is_keyword(self) -> bool { | REF_KW | LET_KW | MOVE_KW + | RETURN_KW | AUTO_KW | DEFAULT_KW | UNION_KW @@ -278,6 +281,7 @@ pub(crate) fn info(self) -> &'static SyntaxInfo { REF_KW => &SyntaxInfo { name: "REF_KW" }, LET_KW => &SyntaxInfo { name: "LET_KW" }, MOVE_KW => &SyntaxInfo { name: "MOVE_KW" }, + RETURN_KW => &SyntaxInfo { name: "RETURN_KW" }, AUTO_KW => &SyntaxInfo { name: "AUTO_KW" }, DEFAULT_KW => &SyntaxInfo { name: "DEFAULT_KW" }, UNION_KW => &SyntaxInfo { name: "UNION_KW" }, @@ -336,6 +340,8 @@ pub(crate) fn info(self) -> &'static SyntaxInfo { STRUCT_LIT => &SyntaxInfo { name: "STRUCT_LIT" }, STRUCT_LIT_FIELD => &SyntaxInfo { name: "STRUCT_LIT_FIELD" }, IF_EXPR => &SyntaxInfo { name: "IF_EXPR" }, + BLOCK_EXPR => &SyntaxInfo { name: "BLOCK_EXPR" }, + RETURN_EXPR => &SyntaxInfo { name: "RETURN_EXPR" }, EXTERN_BLOCK_EXPR => &SyntaxInfo { name: "EXTERN_BLOCK_EXPR" }, ENUM_VARIANT => &SyntaxInfo { name: "ENUM_VARIANT" }, NAMED_FIELD => &SyntaxInfo { name: "NAMED_FIELD" }, @@ -352,7 +358,6 @@ pub(crate) fn info(self) -> &'static SyntaxInfo { ABI => &SyntaxInfo { name: "ABI" }, NAME => &SyntaxInfo { name: "NAME" }, NAME_REF => &SyntaxInfo { name: "NAME_REF" }, - BLOCK_EXPR => &SyntaxInfo { name: "BLOCK_EXPR" }, LET_STMT => &SyntaxInfo { name: "LET_STMT" }, EXPR_STMT => &SyntaxInfo { name: "EXPR_STMT" }, TYPE_PARAM_LIST => &SyntaxInfo { name: "TYPE_PARAM_LIST" }, @@ -403,6 +408,7 @@ pub(crate) fn from_keyword(ident: &str) -> Option { "ref" => REF_KW, "let" => LET_KW, "move" => MOVE_KW, + "return" => RETURN_KW, _ => return None, }; Some(kw) @@ -506,6 +512,7 @@ pub(crate) fn static_text(self) -> Option<&'static str> { REF_KW => "ref", LET_KW => "let", MOVE_KW => "move", + RETURN_KW => "return", AUTO_KW => "auto", DEFAULT_KW => "default", UNION_KW => "union", diff --git a/tests/data/lexer/0011_keywords.rs b/tests/data/lexer/0011_keywords.rs index 22b06356796..e6bf64d4d61 100644 --- a/tests/data/lexer/0011_keywords.rs +++ b/tests/data/lexer/0011_keywords.rs @@ -1,3 +1,3 @@ fn use struct trait enum impl true false as extern crate mod pub self super in where for loop while if match const -static mut type ref let else move +static mut type ref let else move return diff --git a/tests/data/lexer/0011_keywords.txt b/tests/data/lexer/0011_keywords.txt index 7cfc7671665..d6a1abe8ac6 100644 --- a/tests/data/lexer/0011_keywords.txt +++ b/tests/data/lexer/0011_keywords.txt @@ -57,4 +57,6 @@ WHITESPACE 1 " " ELSE_KW 4 "else" WHITESPACE 1 " " MOVE_KW 4 "move" +WHITESPACE 1 " " +RETURN_KW 6 "return" WHITESPACE 1 "\n" diff --git a/tests/data/parser/inline/0068_return_expr.rs b/tests/data/parser/inline/0068_return_expr.rs new file mode 100644 index 00000000000..5733666b605 --- /dev/null +++ b/tests/data/parser/inline/0068_return_expr.rs @@ -0,0 +1,4 @@ +fn foo() { + return; + return 92; +} diff --git a/tests/data/parser/inline/0068_return_expr.txt b/tests/data/parser/inline/0068_return_expr.txt new file mode 100644 index 00000000000..61a075fc168 --- /dev/null +++ b/tests/data/parser/inline/0068_return_expr.txt @@ -0,0 +1,28 @@ +FILE@[0; 40) + FN_ITEM@[0; 40) + FN_KW@[0; 2) + NAME@[2; 6) + WHITESPACE@[2; 3) + IDENT@[3; 6) "foo" + PARAM_LIST@[6; 9) + L_PAREN@[6; 7) + R_PAREN@[7; 8) + WHITESPACE@[8; 9) + BLOCK_EXPR@[9; 40) + L_CURLY@[9; 10) + EXPR_STMT@[10; 27) + RETURN_EXPR@[10; 21) + WHITESPACE@[10; 15) + RETURN_KW@[15; 21) + SEMI@[21; 22) + WHITESPACE@[22; 27) + EXPR_STMT@[27; 38) + RETURN_EXPR@[27; 36) + RETURN_KW@[27; 33) + LITERAL@[33; 36) + WHITESPACE@[33; 34) + INT_NUMBER@[34; 36) "92" + SEMI@[36; 37) + WHITESPACE@[37; 38) + R_CURLY@[38; 39) + WHITESPACE@[39; 40)