mirror of
https://github.com/rust-lang/rust-analyzer
synced 2024-12-28 22:13:39 +00:00
Merge #1801
1801: WIP: switch to fully decomposed tokens internally r=matklad a=matklad Co-authored-by: Aleksey Kladov <aleksey.kladov@gmail.com>
This commit is contained in:
commit
9d3c78e2ee
19 changed files with 497 additions and 337 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -1051,6 +1051,7 @@ dependencies = [
|
||||||
"ra_tt 0.1.0",
|
"ra_tt 0.1.0",
|
||||||
"rustc-hash 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"rustc-hash 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"smallvec 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)",
|
"smallvec 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"test_utils 0.1.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
|
@ -12,3 +12,7 @@ itertools = "0.8.0"
|
||||||
rustc-hash = "1.0.0"
|
rustc-hash = "1.0.0"
|
||||||
smallvec = "0.6.9"
|
smallvec = "0.6.9"
|
||||||
log = "0.4.5"
|
log = "0.4.5"
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
test_utils = { path = "../test_utils" }
|
||||||
|
|
||||||
|
|
|
@ -339,21 +339,13 @@ fn expand_subtree(
|
||||||
template: &crate::Subtree,
|
template: &crate::Subtree,
|
||||||
ctx: &mut ExpandCtx,
|
ctx: &mut ExpandCtx,
|
||||||
) -> Result<tt::Subtree, ExpandError> {
|
) -> Result<tt::Subtree, ExpandError> {
|
||||||
let token_trees = template
|
let mut buf: Vec<tt::TokenTree> = Vec::new();
|
||||||
.token_trees
|
for tt in template.token_trees.iter() {
|
||||||
.iter()
|
let tt = expand_tt(tt, ctx)?;
|
||||||
.map(|it| expand_tt(it, ctx))
|
push_tt(&mut buf, tt);
|
||||||
.filter(|it| {
|
}
|
||||||
// Filter empty subtree
|
|
||||||
if let Ok(tt::TokenTree::Subtree(subtree)) = it {
|
|
||||||
subtree.delimiter != tt::Delimiter::None || !subtree.token_trees.is_empty()
|
|
||||||
} else {
|
|
||||||
true
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.collect::<Result<Vec<_>, ExpandError>>()?;
|
|
||||||
|
|
||||||
Ok(tt::Subtree { token_trees, delimiter: template.delimiter })
|
Ok(tt::Subtree { delimiter: template.delimiter, token_trees: buf })
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Reduce single token subtree to single token
|
/// Reduce single token subtree to single token
|
||||||
|
@ -377,7 +369,7 @@ fn expand_tt(
|
||||||
let res: tt::TokenTree = match template {
|
let res: tt::TokenTree = match template {
|
||||||
crate::TokenTree::Subtree(subtree) => expand_subtree(subtree, ctx)?.into(),
|
crate::TokenTree::Subtree(subtree) => expand_subtree(subtree, ctx)?.into(),
|
||||||
crate::TokenTree::Repeat(repeat) => {
|
crate::TokenTree::Repeat(repeat) => {
|
||||||
let mut token_trees: Vec<tt::TokenTree> = Vec::new();
|
let mut buf: Vec<tt::TokenTree> = Vec::new();
|
||||||
ctx.nesting.push(0);
|
ctx.nesting.push(0);
|
||||||
// Dirty hack to make macro-expansion terminate.
|
// Dirty hack to make macro-expansion terminate.
|
||||||
// This should be replaced by a propper macro-by-example implementation
|
// This should be replaced by a propper macro-by-example implementation
|
||||||
|
@ -418,23 +410,23 @@ fn expand_tt(
|
||||||
|
|
||||||
let idx = ctx.nesting.pop().unwrap();
|
let idx = ctx.nesting.pop().unwrap();
|
||||||
ctx.nesting.push(idx + 1);
|
ctx.nesting.push(idx + 1);
|
||||||
token_trees.push(reduce_single_token(t));
|
push_subtree(&mut buf, t);
|
||||||
|
|
||||||
if let Some(ref sep) = repeat.separator {
|
if let Some(ref sep) = repeat.separator {
|
||||||
match sep {
|
match sep {
|
||||||
crate::Separator::Ident(ident) => {
|
crate::Separator::Ident(ident) => {
|
||||||
has_seps = 1;
|
has_seps = 1;
|
||||||
token_trees.push(tt::Leaf::from(ident.clone()).into());
|
buf.push(tt::Leaf::from(ident.clone()).into());
|
||||||
}
|
}
|
||||||
crate::Separator::Literal(lit) => {
|
crate::Separator::Literal(lit) => {
|
||||||
has_seps = 1;
|
has_seps = 1;
|
||||||
token_trees.push(tt::Leaf::from(lit.clone()).into());
|
buf.push(tt::Leaf::from(lit.clone()).into());
|
||||||
}
|
}
|
||||||
|
|
||||||
crate::Separator::Puncts(puncts) => {
|
crate::Separator::Puncts(puncts) => {
|
||||||
has_seps = puncts.len();
|
has_seps = puncts.len();
|
||||||
for punct in puncts {
|
for punct in puncts {
|
||||||
token_trees.push(tt::Leaf::from(*punct).into());
|
buf.push(tt::Leaf::from(*punct).into());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -450,16 +442,16 @@ fn expand_tt(
|
||||||
|
|
||||||
ctx.nesting.pop().unwrap();
|
ctx.nesting.pop().unwrap();
|
||||||
for _ in 0..has_seps {
|
for _ in 0..has_seps {
|
||||||
token_trees.pop();
|
buf.pop();
|
||||||
}
|
}
|
||||||
|
|
||||||
if crate::RepeatKind::OneOrMore == repeat.kind && counter == 0 {
|
if crate::RepeatKind::OneOrMore == repeat.kind && counter == 0 {
|
||||||
return Err(ExpandError::UnexpectedToken);
|
return Err(ExpandError::UnexpectedToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if it is a singel token subtree without any delimiter
|
// Check if it is a single token subtree without any delimiter
|
||||||
// e.g {Delimiter:None> ['>'] /Delimiter:None>}
|
// e.g {Delimiter:None> ['>'] /Delimiter:None>}
|
||||||
reduce_single_token(tt::Subtree { token_trees, delimiter: tt::Delimiter::None })
|
reduce_single_token(tt::Subtree { delimiter: tt::Delimiter::None, token_trees: buf })
|
||||||
}
|
}
|
||||||
crate::TokenTree::Leaf(leaf) => match leaf {
|
crate::TokenTree::Leaf(leaf) => match leaf {
|
||||||
crate::Leaf::Ident(ident) => {
|
crate::Leaf::Ident(ident) => {
|
||||||
|
@ -586,3 +578,17 @@ mod tests {
|
||||||
expand_rule(&rules.rules[0], &invocation_tt)
|
expand_rule(&rules.rules[0], &invocation_tt)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn push_tt(buf: &mut Vec<tt::TokenTree>, tt: tt::TokenTree) {
|
||||||
|
match tt {
|
||||||
|
tt::TokenTree::Subtree(tt) => push_subtree(buf, tt),
|
||||||
|
_ => buf.push(tt),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn push_subtree(buf: &mut Vec<tt::TokenTree>, tt: tt::Subtree) {
|
||||||
|
match tt.delimiter {
|
||||||
|
tt::Delimiter::None => buf.extend(tt.token_trees),
|
||||||
|
_ => buf.push(tt.into()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -70,7 +70,14 @@ fn fragment_to_syntax_node(
|
||||||
tt: &tt::Subtree,
|
tt: &tt::Subtree,
|
||||||
fragment_kind: FragmentKind,
|
fragment_kind: FragmentKind,
|
||||||
) -> Result<Parse<SyntaxNode>, ExpandError> {
|
) -> Result<Parse<SyntaxNode>, ExpandError> {
|
||||||
let tokens = [tt.clone().into()];
|
let tmp;
|
||||||
|
let tokens = match tt {
|
||||||
|
tt::Subtree { delimiter: tt::Delimiter::None, token_trees } => token_trees.as_slice(),
|
||||||
|
_ => {
|
||||||
|
tmp = [tt.clone().into()];
|
||||||
|
&tmp[..]
|
||||||
|
}
|
||||||
|
};
|
||||||
let buffer = TokenBuffer::new(&tokens);
|
let buffer = TokenBuffer::new(&tokens);
|
||||||
let mut token_source = SubtreeTokenSource::new(&buffer);
|
let mut token_source = SubtreeTokenSource::new(&buffer);
|
||||||
let mut tree_sink = TtTreeSink::new(buffer.begin());
|
let mut tree_sink = TtTreeSink::new(buffer.begin());
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
use ra_syntax::{ast, AstNode, NodeOrToken};
|
use ra_syntax::{ast, AstNode, NodeOrToken};
|
||||||
|
use test_utils::assert_eq_text;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
|
@ -152,7 +153,6 @@ pub(crate) fn assert_expansion(
|
||||||
|
|
||||||
// wrap the given text to a macro call
|
// wrap the given text to a macro call
|
||||||
let expected = text_to_tokentree(&expected);
|
let expected = text_to_tokentree(&expected);
|
||||||
|
|
||||||
let (expanded_tree, expected_tree) = match kind {
|
let (expanded_tree, expected_tree) = match kind {
|
||||||
MacroKind::Items => {
|
MacroKind::Items => {
|
||||||
let expanded_tree = token_tree_to_macro_items(&expanded).unwrap().tree();
|
let expanded_tree = token_tree_to_macro_items(&expanded).unwrap().tree();
|
||||||
|
@ -178,7 +178,7 @@ pub(crate) fn assert_expansion(
|
||||||
let expected_tree = expected_tree.replace("C_C__C", "$crate");
|
let expected_tree = expected_tree.replace("C_C__C", "$crate");
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
expanded_tree, expected_tree,
|
expanded_tree, expected_tree,
|
||||||
"left => {}\nright => {}",
|
"\nleft:\n{}\nright:\n{}",
|
||||||
expanded_tree, expected_tree,
|
expanded_tree, expected_tree,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -657,6 +657,7 @@ fn test_expr() {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
#[ignore]
|
||||||
fn test_expr_order() {
|
fn test_expr_order() {
|
||||||
let rules = create_rules(
|
let rules = create_rules(
|
||||||
r#"
|
r#"
|
||||||
|
@ -668,8 +669,8 @@ fn test_expr_order() {
|
||||||
"#,
|
"#,
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq_text!(
|
||||||
format!("{:#?}", expand_to_items(&rules, "foo! { 1 + 1 }").syntax()).trim(),
|
&format!("{:#?}", expand_to_items(&rules, "foo! { 1 + 1 }").syntax()),
|
||||||
r#"MACRO_ITEMS@[0; 15)
|
r#"MACRO_ITEMS@[0; 15)
|
||||||
FN_DEF@[0; 15)
|
FN_DEF@[0; 15)
|
||||||
FN_KW@[0; 2) "fn"
|
FN_KW@[0; 2) "fn"
|
||||||
|
|
|
@ -210,7 +210,7 @@ fn opt_visibility(p: &mut Parser) -> bool {
|
||||||
//
|
//
|
||||||
// test crate_keyword_path
|
// test crate_keyword_path
|
||||||
// fn foo() { crate::foo(); }
|
// fn foo() { crate::foo(); }
|
||||||
T![crate] if p.nth(1) != T![::] => {
|
T![crate] if !p.nth_at(1, T![::]) => {
|
||||||
let m = p.start();
|
let m = p.start();
|
||||||
p.bump_any();
|
p.bump_any();
|
||||||
m.complete(p, VISIBILITY);
|
m.complete(p, VISIBILITY);
|
||||||
|
@ -245,7 +245,7 @@ fn abi(p: &mut Parser) {
|
||||||
fn opt_fn_ret_type(p: &mut Parser) -> bool {
|
fn opt_fn_ret_type(p: &mut Parser) -> bool {
|
||||||
if p.at(T![->]) {
|
if p.at(T![->]) {
|
||||||
let m = p.start();
|
let m = p.start();
|
||||||
p.bump_any();
|
p.bump(T![->]);
|
||||||
types::type_(p);
|
types::type_(p);
|
||||||
m.complete(p, RET_TYPE);
|
m.complete(p, RET_TYPE);
|
||||||
true
|
true
|
||||||
|
|
|
@ -212,52 +212,48 @@ struct Restrictions {
|
||||||
prefer_stmt: bool,
|
prefer_stmt: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
enum Op {
|
/// Binding powers of operators for a Pratt parser.
|
||||||
Simple,
|
///
|
||||||
Composite(SyntaxKind, u8),
|
/// See https://www.oilshell.org/blog/2016/11/03.html
|
||||||
}
|
#[rustfmt::skip]
|
||||||
|
fn current_op(p: &Parser) -> (u8, SyntaxKind) {
|
||||||
|
const NOT_AN_OP: (u8, SyntaxKind) = (0, T![@]);
|
||||||
|
match p.current() {
|
||||||
|
T![|] if p.at(T![||]) => (3, T![||]),
|
||||||
|
T![|] if p.at(T![|=]) => (1, T![|=]),
|
||||||
|
T![|] => (6, T![|]),
|
||||||
|
T![>] if p.at(T![>>=]) => (1, T![>>=]),
|
||||||
|
T![>] if p.at(T![>>]) => (9, T![>>]),
|
||||||
|
T![>] if p.at(T![>=]) => (5, T![>=]),
|
||||||
|
T![>] => (5, T![>]),
|
||||||
|
T![=] if p.at(T![=>]) => NOT_AN_OP,
|
||||||
|
T![=] if p.at(T![==]) => (5, T![==]),
|
||||||
|
T![=] => (1, T![=]),
|
||||||
|
T![<] if p.at(T![<=]) => (5, T![<=]),
|
||||||
|
T![<] if p.at(T![<<=]) => (1, T![<<=]),
|
||||||
|
T![<] if p.at(T![<<]) => (9, T![<<]),
|
||||||
|
T![<] => (5, T![<]),
|
||||||
|
T![+] if p.at(T![+=]) => (1, T![+=]),
|
||||||
|
T![+] => (10, T![+]),
|
||||||
|
T![^] if p.at(T![^=]) => (1, T![^=]),
|
||||||
|
T![^] => (7, T![^]),
|
||||||
|
T![%] if p.at(T![%=]) => (1, T![%=]),
|
||||||
|
T![%] => (11, T![%]),
|
||||||
|
T![&] if p.at(T![&=]) => (1, T![&=]),
|
||||||
|
T![&] if p.at(T![&&]) => (4, T![&&]),
|
||||||
|
T![&] => (8, T![&]),
|
||||||
|
T![/] if p.at(T![/=]) => (1, T![/=]),
|
||||||
|
T![/] => (11, T![/]),
|
||||||
|
T![*] if p.at(T![*=]) => (1, T![*=]),
|
||||||
|
T![*] => (11, T![*]),
|
||||||
|
T![.] if p.at(T![..=]) => (2, T![..=]),
|
||||||
|
T![.] if p.at(T![..]) => (2, T![..]),
|
||||||
|
T![!] if p.at(T![!=]) => (5, T![!=]),
|
||||||
|
T![-] if p.at(T![-=]) => (1, T![-=]),
|
||||||
|
T![-] => (10, T![-]),
|
||||||
|
|
||||||
fn current_op(p: &Parser) -> (u8, Op) {
|
_ => NOT_AN_OP
|
||||||
if let Some(t) = p.current3() {
|
|
||||||
match t {
|
|
||||||
(T![<], T![<], T![=]) => return (1, Op::Composite(T![<<=], 3)),
|
|
||||||
(T![>], T![>], T![=]) => return (1, Op::Composite(T![>>=], 3)),
|
|
||||||
_ => (),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(t) = p.current2() {
|
|
||||||
match t {
|
|
||||||
(T![+], T![=]) => return (1, Op::Composite(T![+=], 2)),
|
|
||||||
(T![-], T![=]) => return (1, Op::Composite(T![-=], 2)),
|
|
||||||
(T![*], T![=]) => return (1, Op::Composite(T![*=], 2)),
|
|
||||||
(T![%], T![=]) => return (1, Op::Composite(T![%=], 2)),
|
|
||||||
(T![/], T![=]) => return (1, Op::Composite(T![/=], 2)),
|
|
||||||
(T![|], T![=]) => return (1, Op::Composite(T![|=], 2)),
|
|
||||||
(T![&], T![=]) => return (1, Op::Composite(T![&=], 2)),
|
|
||||||
(T![^], T![=]) => return (1, Op::Composite(T![^=], 2)),
|
|
||||||
(T![|], T![|]) => return (3, Op::Composite(T![||], 2)),
|
|
||||||
(T![&], T![&]) => return (4, Op::Composite(T![&&], 2)),
|
|
||||||
(T![<], T![=]) => return (5, Op::Composite(T![<=], 2)),
|
|
||||||
(T![>], T![=]) => return (5, Op::Composite(T![>=], 2)),
|
|
||||||
(T![<], T![<]) => return (9, Op::Composite(T![<<], 2)),
|
|
||||||
(T![>], T![>]) => return (9, Op::Composite(T![>>], 2)),
|
|
||||||
_ => (),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let bp = match p.current() {
|
|
||||||
T![=] => 1,
|
|
||||||
T![..] | T![..=] => 2,
|
|
||||||
T![==] | T![!=] | T![<] | T![>] => 5,
|
|
||||||
T![|] => 6,
|
|
||||||
T![^] => 7,
|
|
||||||
T![&] => 8,
|
|
||||||
T![-] | T![+] => 10,
|
|
||||||
T![*] | T![/] | T![%] => 11,
|
|
||||||
_ => 0,
|
|
||||||
};
|
|
||||||
(bp, Op::Simple)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parses expression with binding power of at least bp.
|
// Parses expression with binding power of at least bp.
|
||||||
|
@ -308,12 +304,7 @@ fn expr_bp(
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
let m = lhs.precede(p);
|
let m = lhs.precede(p);
|
||||||
match op {
|
p.bump(op);
|
||||||
Op::Simple => p.bump_any(),
|
|
||||||
Op::Composite(kind, n) => {
|
|
||||||
p.bump_compound(kind, n);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
expr_bp(p, r, op_bp + 1, dollar_lvl);
|
expr_bp(p, r, op_bp + 1, dollar_lvl);
|
||||||
lhs = m.complete(p, if is_range { RANGE_EXPR } else { BIN_EXPR });
|
lhs = m.complete(p, if is_range { RANGE_EXPR } else { BIN_EXPR });
|
||||||
|
@ -321,8 +312,7 @@ fn expr_bp(
|
||||||
(Some(lhs), BlockLike::NotBlock)
|
(Some(lhs), BlockLike::NotBlock)
|
||||||
}
|
}
|
||||||
|
|
||||||
const LHS_FIRST: TokenSet =
|
const LHS_FIRST: TokenSet = atom::ATOM_EXPR_FIRST.union(token_set![AMP, STAR, EXCL, DOT, MINUS]);
|
||||||
atom::ATOM_EXPR_FIRST.union(token_set![AMP, STAR, EXCL, DOTDOT, DOTDOTEQ, MINUS]);
|
|
||||||
|
|
||||||
fn lhs(
|
fn lhs(
|
||||||
p: &mut Parser,
|
p: &mut Parser,
|
||||||
|
@ -353,17 +343,20 @@ fn lhs(
|
||||||
p.bump_any();
|
p.bump_any();
|
||||||
PREFIX_EXPR
|
PREFIX_EXPR
|
||||||
}
|
}
|
||||||
// test full_range_expr
|
|
||||||
// fn foo() { xs[..]; }
|
|
||||||
T![..] | T![..=] => {
|
|
||||||
m = p.start();
|
|
||||||
p.bump_any();
|
|
||||||
if p.at_ts(EXPR_FIRST) {
|
|
||||||
expr_bp(p, r, 2, dollar_lvl);
|
|
||||||
}
|
|
||||||
return Some((m.complete(p, RANGE_EXPR), BlockLike::NotBlock));
|
|
||||||
}
|
|
||||||
_ => {
|
_ => {
|
||||||
|
// test full_range_expr
|
||||||
|
// fn foo() { xs[..]; }
|
||||||
|
for &op in [T![..=], T![..]].iter() {
|
||||||
|
if p.at(op) {
|
||||||
|
m = p.start();
|
||||||
|
p.bump(op);
|
||||||
|
if p.at_ts(EXPR_FIRST) {
|
||||||
|
expr_bp(p, r, 2, dollar_lvl);
|
||||||
|
}
|
||||||
|
return Some((m.complete(p, RANGE_EXPR), BlockLike::NotBlock));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// test expression_after_block
|
// test expression_after_block
|
||||||
// fn foo() {
|
// fn foo() {
|
||||||
// let mut p = F{x: 5};
|
// let mut p = F{x: 5};
|
||||||
|
@ -399,29 +392,13 @@ fn postfix_expr(
|
||||||
// }
|
// }
|
||||||
T!['('] if allow_calls => call_expr(p, lhs),
|
T!['('] if allow_calls => call_expr(p, lhs),
|
||||||
T!['['] if allow_calls => index_expr(p, lhs),
|
T!['['] if allow_calls => index_expr(p, lhs),
|
||||||
T![.] if p.nth(1) == IDENT && (p.nth(2) == T!['('] || p.nth(2) == T![::]) => {
|
T![.] => match postfix_dot_expr(p, lhs) {
|
||||||
method_call_expr(p, lhs)
|
Ok(it) => it,
|
||||||
}
|
Err(it) => {
|
||||||
T![.] if p.nth(1) == AWAIT_KW => {
|
lhs = it;
|
||||||
// test await_expr
|
break;
|
||||||
// fn foo() {
|
}
|
||||||
// x.await;
|
},
|
||||||
// x.0.await;
|
|
||||||
// x.0().await?.hello();
|
|
||||||
// }
|
|
||||||
let m = lhs.precede(p);
|
|
||||||
p.bump_any();
|
|
||||||
p.bump_any();
|
|
||||||
m.complete(p, AWAIT_EXPR)
|
|
||||||
}
|
|
||||||
T![.] => field_expr(p, lhs),
|
|
||||||
// test postfix_range
|
|
||||||
// fn foo() { let x = 1..; }
|
|
||||||
T![..] | T![..=] if !EXPR_FIRST.contains(p.nth(1)) => {
|
|
||||||
let m = lhs.precede(p);
|
|
||||||
p.bump_any();
|
|
||||||
m.complete(p, RANGE_EXPR)
|
|
||||||
}
|
|
||||||
T![?] => try_expr(p, lhs),
|
T![?] => try_expr(p, lhs),
|
||||||
T![as] => cast_expr(p, lhs),
|
T![as] => cast_expr(p, lhs),
|
||||||
_ => break,
|
_ => break,
|
||||||
|
@ -429,7 +406,46 @@ fn postfix_expr(
|
||||||
allow_calls = true;
|
allow_calls = true;
|
||||||
block_like = BlockLike::NotBlock;
|
block_like = BlockLike::NotBlock;
|
||||||
}
|
}
|
||||||
(lhs, block_like)
|
return (lhs, block_like);
|
||||||
|
|
||||||
|
fn postfix_dot_expr(
|
||||||
|
p: &mut Parser,
|
||||||
|
lhs: CompletedMarker,
|
||||||
|
) -> Result<CompletedMarker, CompletedMarker> {
|
||||||
|
assert!(p.at(T![.]));
|
||||||
|
if p.nth(1) == IDENT && (p.nth(2) == T!['('] || p.nth_at(2, T![::])) {
|
||||||
|
return Ok(method_call_expr(p, lhs));
|
||||||
|
}
|
||||||
|
|
||||||
|
// test await_expr
|
||||||
|
// fn foo() {
|
||||||
|
// x.await;
|
||||||
|
// x.0.await;
|
||||||
|
// x.0().await?.hello();
|
||||||
|
// }
|
||||||
|
if p.nth(1) == T![await] {
|
||||||
|
let m = lhs.precede(p);
|
||||||
|
p.bump(T![.]);
|
||||||
|
p.bump(T![await]);
|
||||||
|
return Ok(m.complete(p, AWAIT_EXPR));
|
||||||
|
}
|
||||||
|
|
||||||
|
// test postfix_range
|
||||||
|
// fn foo() { let x = 1..; }
|
||||||
|
for &(op, la) in [(T![..=], 3), (T![..], 2)].iter() {
|
||||||
|
if p.at(op) {
|
||||||
|
return if EXPR_FIRST.contains(p.nth(la)) {
|
||||||
|
Err(lhs)
|
||||||
|
} else {
|
||||||
|
let m = lhs.precede(p);
|
||||||
|
p.bump(op);
|
||||||
|
Ok(m.complete(p, RANGE_EXPR))
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(field_expr(p, lhs))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// test call_expr
|
// test call_expr
|
||||||
|
@ -465,7 +481,7 @@ fn index_expr(p: &mut Parser, lhs: CompletedMarker) -> CompletedMarker {
|
||||||
// y.bar::<T>(1, 2,);
|
// y.bar::<T>(1, 2,);
|
||||||
// }
|
// }
|
||||||
fn method_call_expr(p: &mut Parser, lhs: CompletedMarker) -> CompletedMarker {
|
fn method_call_expr(p: &mut Parser, lhs: CompletedMarker) -> CompletedMarker {
|
||||||
assert!(p.at(T![.]) && p.nth(1) == IDENT && (p.nth(2) == T!['('] || p.nth(2) == T![::]));
|
assert!(p.at(T![.]) && p.nth(1) == IDENT && (p.nth(2) == T!['('] || p.nth_at(2, T![::])));
|
||||||
let m = lhs.precede(p);
|
let m = lhs.precede(p);
|
||||||
p.bump_any();
|
p.bump_any();
|
||||||
name_ref(p);
|
name_ref(p);
|
||||||
|
@ -567,7 +583,7 @@ fn path_expr(p: &mut Parser, r: Restrictions) -> (CompletedMarker, BlockLike) {
|
||||||
record_field_list(p);
|
record_field_list(p);
|
||||||
(m.complete(p, RECORD_LIT), BlockLike::NotBlock)
|
(m.complete(p, RECORD_LIT), BlockLike::NotBlock)
|
||||||
}
|
}
|
||||||
T![!] => {
|
T![!] if !p.at(T![!=]) => {
|
||||||
let block_like = items::macro_call_after_excl(p);
|
let block_like = items::macro_call_after_excl(p);
|
||||||
(m.complete(p, MACRO_CALL), block_like)
|
(m.complete(p, MACRO_CALL), block_like)
|
||||||
}
|
}
|
||||||
|
@ -601,8 +617,8 @@ pub(crate) fn record_field_list(p: &mut Parser) {
|
||||||
}
|
}
|
||||||
m.complete(p, RECORD_FIELD);
|
m.complete(p, RECORD_FIELD);
|
||||||
}
|
}
|
||||||
T![..] => {
|
T![.] if p.at(T![..]) => {
|
||||||
p.bump_any();
|
p.bump(T![..]);
|
||||||
expr(p);
|
expr(p);
|
||||||
}
|
}
|
||||||
T!['{'] => error_block(p, "expected a field"),
|
T!['{'] => error_block(p, "expected a field"),
|
||||||
|
|
|
@ -422,7 +422,7 @@ pub(crate) fn token_tree(p: &mut Parser) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
T![')'] | T![']'] => p.err_and_bump("unmatched brace"),
|
T![')'] | T![']'] => p.err_and_bump("unmatched brace"),
|
||||||
_ => p.bump_raw(),
|
_ => p.bump_any(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
p.expect(closing_paren_kind);
|
p.expect(closing_paren_kind);
|
||||||
|
|
|
@ -13,9 +13,8 @@ pub(super) fn use_item(p: &mut Parser, m: Marker) {
|
||||||
/// so handles both `some::path::{inner::path}` and `inner::path` in
|
/// so handles both `some::path::{inner::path}` and `inner::path` in
|
||||||
/// `use some::path::{inner::path};`
|
/// `use some::path::{inner::path};`
|
||||||
fn use_tree(p: &mut Parser) {
|
fn use_tree(p: &mut Parser) {
|
||||||
let la = p.nth(1);
|
|
||||||
let m = p.start();
|
let m = p.start();
|
||||||
match (p.current(), la) {
|
match p.current() {
|
||||||
// Finish the use_tree for cases of e.g.
|
// Finish the use_tree for cases of e.g.
|
||||||
// `use some::path::{self, *};` or `use *;`
|
// `use some::path::{self, *};` or `use *;`
|
||||||
// This does not handle cases such as `use some::path::*`
|
// This does not handle cases such as `use some::path::*`
|
||||||
|
@ -28,15 +27,15 @@ fn use_tree(p: &mut Parser) {
|
||||||
// use ::*;
|
// use ::*;
|
||||||
// use some::path::{*};
|
// use some::path::{*};
|
||||||
// use some::path::{::*};
|
// use some::path::{::*};
|
||||||
(T![*], _) => p.bump_any(),
|
T![*] => p.bump(T![*]),
|
||||||
(T![::], T![*]) => {
|
T![:] if p.at(T![::]) && p.nth(2) == T![*] => {
|
||||||
// Parse `use ::*;`, which imports all from the crate root in Rust 2015
|
// Parse `use ::*;`, which imports all from the crate root in Rust 2015
|
||||||
// This is invalid inside a use_tree_list, (e.g. `use some::path::{::*}`)
|
// This is invalid inside a use_tree_list, (e.g. `use some::path::{::*}`)
|
||||||
// but still parses and errors later: ('crate root in paths can only be used in start position')
|
// but still parses and errors later: ('crate root in paths can only be used in start position')
|
||||||
// FIXME: Add this error (if not out of scope)
|
// FIXME: Add this error (if not out of scope)
|
||||||
// In Rust 2018, it is always invalid (see above)
|
// In Rust 2018, it is always invalid (see above)
|
||||||
p.bump_any();
|
p.bump(T![::]);
|
||||||
p.bump_any();
|
p.bump(T![*]);
|
||||||
}
|
}
|
||||||
// Open a use tree list
|
// Open a use tree list
|
||||||
// Handles cases such as `use {some::path};` or `{inner::path}` in
|
// Handles cases such as `use {some::path};` or `{inner::path}` in
|
||||||
|
@ -47,10 +46,11 @@ fn use_tree(p: &mut Parser) {
|
||||||
// use {path::from::root}; // Rust 2015
|
// use {path::from::root}; // Rust 2015
|
||||||
// use ::{some::arbritrary::path}; // Rust 2015
|
// use ::{some::arbritrary::path}; // Rust 2015
|
||||||
// use ::{{{crate::export}}}; // Nonsensical but perfectly legal nestnig
|
// use ::{{{crate::export}}}; // Nonsensical but perfectly legal nestnig
|
||||||
(T!['{'], _) | (T![::], T!['{']) => {
|
T!['{'] => {
|
||||||
if p.at(T![::]) {
|
use_tree_list(p);
|
||||||
p.bump_any();
|
}
|
||||||
}
|
T![:] if p.at(T![::]) && p.nth(2) == T!['{'] => {
|
||||||
|
p.bump(T![::]);
|
||||||
use_tree_list(p);
|
use_tree_list(p);
|
||||||
}
|
}
|
||||||
// Parse a 'standard' path.
|
// Parse a 'standard' path.
|
||||||
|
@ -80,8 +80,8 @@ fn use_tree(p: &mut Parser) {
|
||||||
// use Trait as _;
|
// use Trait as _;
|
||||||
opt_alias(p);
|
opt_alias(p);
|
||||||
}
|
}
|
||||||
T![::] => {
|
T![:] if p.at(T![::]) => {
|
||||||
p.bump_any();
|
p.bump(T![::]);
|
||||||
match p.current() {
|
match p.current() {
|
||||||
T![*] => {
|
T![*] => {
|
||||||
p.bump_any();
|
p.bump_any();
|
||||||
|
|
|
@ -80,7 +80,7 @@ fn value_parameter(p: &mut Parser, flavor: Flavor) {
|
||||||
match flavor {
|
match flavor {
|
||||||
Flavor::OptionalType | Flavor::Normal => {
|
Flavor::OptionalType | Flavor::Normal => {
|
||||||
patterns::pattern(p);
|
patterns::pattern(p);
|
||||||
if p.at(T![:]) || flavor.type_required() {
|
if p.at(T![:]) && !p.at(T![::]) || flavor.type_required() {
|
||||||
types::ascription(p)
|
types::ascription(p)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -96,10 +96,11 @@ fn value_parameter(p: &mut Parser, flavor: Flavor) {
|
||||||
// trait Foo {
|
// trait Foo {
|
||||||
// fn bar(_: u64, mut x: i32);
|
// fn bar(_: u64, mut x: i32);
|
||||||
// }
|
// }
|
||||||
if (la0 == IDENT || la0 == T![_]) && la1 == T![:]
|
if (la0 == IDENT || la0 == T![_]) && la1 == T![:] && !p.nth_at(1, T![::])
|
||||||
|| la0 == T![mut] && la1 == IDENT && la2 == T![:]
|
|| la0 == T![mut] && la1 == IDENT && la2 == T![:]
|
||||||
|| la0 == T![&] && la1 == IDENT && la2 == T![:]
|
|| la0 == T![&]
|
||||||
|| la0 == T![&] && la1 == T![mut] && la2 == IDENT && la3 == T![:]
|
&& (la1 == IDENT && la2 == T![:] && !p.nth_at(2, T![::])
|
||||||
|
|| la1 == T![mut] && la2 == IDENT && la3 == T![:] && !p.nth_at(3, T![::]))
|
||||||
{
|
{
|
||||||
patterns::pattern(p);
|
patterns::pattern(p);
|
||||||
types::ascription(p);
|
types::ascription(p);
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
pub(super) const PATH_FIRST: TokenSet =
|
pub(super) const PATH_FIRST: TokenSet =
|
||||||
token_set![IDENT, SELF_KW, SUPER_KW, CRATE_KW, COLONCOLON, L_ANGLE];
|
token_set![IDENT, SELF_KW, SUPER_KW, CRATE_KW, COLON, L_ANGLE];
|
||||||
|
|
||||||
pub(super) fn is_path_start(p: &Parser) -> bool {
|
pub(super) fn is_path_start(p: &Parser) -> bool {
|
||||||
is_use_path_start(p) || p.at(T![<])
|
is_use_path_start(p) || p.at(T![<])
|
||||||
|
@ -9,7 +9,8 @@ pub(super) fn is_path_start(p: &Parser) -> bool {
|
||||||
|
|
||||||
pub(super) fn is_use_path_start(p: &Parser) -> bool {
|
pub(super) fn is_use_path_start(p: &Parser) -> bool {
|
||||||
match p.current() {
|
match p.current() {
|
||||||
IDENT | T![self] | T![super] | T![crate] | T![::] => true,
|
IDENT | T![self] | T![super] | T![crate] => true,
|
||||||
|
T![:] if p.at(T![::]) => true,
|
||||||
_ => false,
|
_ => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -38,13 +39,13 @@ fn path(p: &mut Parser, mode: Mode) {
|
||||||
path_segment(p, mode, true);
|
path_segment(p, mode, true);
|
||||||
let mut qual = path.complete(p, PATH);
|
let mut qual = path.complete(p, PATH);
|
||||||
loop {
|
loop {
|
||||||
let use_tree = match p.nth(1) {
|
let use_tree = match p.nth(2) {
|
||||||
T![*] | T!['{'] => true,
|
T![*] | T!['{'] => true,
|
||||||
_ => false,
|
_ => false,
|
||||||
};
|
};
|
||||||
if p.at(T![::]) && !use_tree {
|
if p.at(T![::]) && !use_tree {
|
||||||
let path = qual.precede(p);
|
let path = qual.precede(p);
|
||||||
p.bump_any();
|
p.bump(T![::]);
|
||||||
path_segment(p, mode, false);
|
path_segment(p, mode, false);
|
||||||
let path = path.complete(p, PATH);
|
let path = path.complete(p, PATH);
|
||||||
qual = path;
|
qual = path;
|
||||||
|
|
|
@ -34,17 +34,20 @@ pub(super) fn pattern_r(p: &mut Parser, recovery_set: TokenSet) {
|
||||||
// 200 .. 301=> (),
|
// 200 .. 301=> (),
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
if p.at(T![...]) || p.at(T![..=]) || p.at(T![..]) {
|
for &range_op in [T![...], T![..=], T![..]].iter() {
|
||||||
let m = lhs.precede(p);
|
if p.at(range_op) {
|
||||||
p.bump_any();
|
let m = lhs.precede(p);
|
||||||
atom_pat(p, recovery_set);
|
p.bump(range_op);
|
||||||
m.complete(p, RANGE_PAT);
|
atom_pat(p, recovery_set);
|
||||||
|
m.complete(p, RANGE_PAT);
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// test marco_pat
|
// test marco_pat
|
||||||
// fn main() {
|
// fn main() {
|
||||||
// let m!(x) = 0;
|
// let m!(x) = 0;
|
||||||
// }
|
// }
|
||||||
else if lhs.kind() == PATH_PAT && p.at(T![!]) {
|
if lhs.kind() == PATH_PAT && p.at(T![!]) {
|
||||||
let m = lhs.precede(p);
|
let m = lhs.precede(p);
|
||||||
items::macro_call_after_excl(p);
|
items::macro_call_after_excl(p);
|
||||||
m.complete(p, MACRO_CALL);
|
m.complete(p, MACRO_CALL);
|
||||||
|
@ -56,14 +59,16 @@ const PAT_RECOVERY_SET: TokenSet =
|
||||||
token_set![LET_KW, IF_KW, WHILE_KW, LOOP_KW, MATCH_KW, R_PAREN, COMMA];
|
token_set![LET_KW, IF_KW, WHILE_KW, LOOP_KW, MATCH_KW, R_PAREN, COMMA];
|
||||||
|
|
||||||
fn atom_pat(p: &mut Parser, recovery_set: TokenSet) -> Option<CompletedMarker> {
|
fn atom_pat(p: &mut Parser, recovery_set: TokenSet) -> Option<CompletedMarker> {
|
||||||
// Checks the token after an IDENT to see if a pattern is a path (Struct { .. }) or macro
|
|
||||||
// (T![x]).
|
|
||||||
let is_path_or_macro_pat =
|
|
||||||
|la1| la1 == T![::] || la1 == T!['('] || la1 == T!['{'] || la1 == T![!];
|
|
||||||
|
|
||||||
let m = match p.nth(0) {
|
let m = match p.nth(0) {
|
||||||
T![box] => box_pat(p),
|
T![box] => box_pat(p),
|
||||||
T![ref] | T![mut] | IDENT if !is_path_or_macro_pat(p.nth(1)) => bind_pat(p, true),
|
T![ref] | T![mut] => bind_pat(p, true),
|
||||||
|
IDENT => match p.nth(1) {
|
||||||
|
// Checks the token after an IDENT to see if a pattern is a path (Struct { .. }) or macro
|
||||||
|
// (T![x]).
|
||||||
|
T!['('] | T!['{'] | T![!] => path_pat(p),
|
||||||
|
T![:] if p.nth_at(1, T![::]) => path_pat(p),
|
||||||
|
_ => bind_pat(p, true),
|
||||||
|
},
|
||||||
|
|
||||||
_ if paths::is_use_path_start(p) => path_pat(p),
|
_ if paths::is_use_path_start(p) => path_pat(p),
|
||||||
_ if is_literal_pat_start(p) => literal_pat(p),
|
_ if is_literal_pat_start(p) => literal_pat(p),
|
||||||
|
@ -158,7 +163,7 @@ fn record_field_pat_list(p: &mut Parser) {
|
||||||
p.bump_any();
|
p.bump_any();
|
||||||
while !p.at(EOF) && !p.at(T!['}']) {
|
while !p.at(EOF) && !p.at(T!['}']) {
|
||||||
match p.current() {
|
match p.current() {
|
||||||
T![..] => p.bump_any(),
|
T![.] if p.at(T![..]) => p.bump(T![..]),
|
||||||
IDENT if p.nth(1) == T![:] => record_field_pat(p),
|
IDENT if p.nth(1) == T![:] => record_field_pat(p),
|
||||||
T!['{'] => error_block(p, "expected ident"),
|
T!['{'] => error_block(p, "expected ident"),
|
||||||
T![box] => {
|
T![box] => {
|
||||||
|
@ -237,7 +242,7 @@ fn slice_pat(p: &mut Parser) -> CompletedMarker {
|
||||||
fn pat_list(p: &mut Parser, ket: SyntaxKind) {
|
fn pat_list(p: &mut Parser, ket: SyntaxKind) {
|
||||||
while !p.at(EOF) && !p.at(ket) {
|
while !p.at(EOF) && !p.at(ket) {
|
||||||
match p.current() {
|
match p.current() {
|
||||||
T![..] => p.bump_any(),
|
T![.] if p.at(T![..]) => p.bump(T![..]),
|
||||||
_ => {
|
_ => {
|
||||||
if !p.at_ts(PATTERN_FIRST) {
|
if !p.at_ts(PATTERN_FIRST) {
|
||||||
p.error("expected a pattern");
|
p.error("expected a pattern");
|
||||||
|
|
|
@ -2,19 +2,16 @@ use super::*;
|
||||||
|
|
||||||
pub(super) fn opt_type_arg_list(p: &mut Parser, colon_colon_required: bool) {
|
pub(super) fn opt_type_arg_list(p: &mut Parser, colon_colon_required: bool) {
|
||||||
let m;
|
let m;
|
||||||
match (colon_colon_required, p.nth(0), p.nth(1)) {
|
if p.at(T![::]) && p.nth(2) == T![<] {
|
||||||
(_, T![::], T![<]) => {
|
m = p.start();
|
||||||
m = p.start();
|
p.bump(T![::]);
|
||||||
p.bump_any();
|
p.bump(T![<]);
|
||||||
p.bump_any();
|
} else if !colon_colon_required && p.at(T![<]) && p.nth(1) != T![=] {
|
||||||
}
|
m = p.start();
|
||||||
(false, T![<], T![=]) => return,
|
p.bump(T![<]);
|
||||||
(false, T![<], _) => {
|
} else {
|
||||||
m = p.start();
|
return;
|
||||||
p.bump_any();
|
}
|
||||||
}
|
|
||||||
_ => return,
|
|
||||||
};
|
|
||||||
|
|
||||||
while !p.at(EOF) && !p.at(T![>]) {
|
while !p.at(EOF) && !p.at(T![>]) {
|
||||||
type_arg(p);
|
type_arg(p);
|
||||||
|
@ -37,7 +34,7 @@ fn type_arg(p: &mut Parser) {
|
||||||
}
|
}
|
||||||
// test associated_type_bounds
|
// test associated_type_bounds
|
||||||
// fn print_all<T: Iterator<Item: Display>>(printables: T) {}
|
// fn print_all<T: Iterator<Item: Display>>(printables: T) {}
|
||||||
IDENT if p.nth(1) == T![:] => {
|
IDENT if p.nth(1) == T![:] && p.nth(2) != T![:] => {
|
||||||
name_ref(p);
|
name_ref(p);
|
||||||
type_params::bounds(p);
|
type_params::bounds(p);
|
||||||
m.complete(p, ASSOC_TYPE_ARG);
|
m.complete(p, ASSOC_TYPE_ARG);
|
||||||
|
|
|
@ -6,7 +6,7 @@ use crate::{
|
||||||
event::Event,
|
event::Event,
|
||||||
ParseError,
|
ParseError,
|
||||||
SyntaxKind::{self, EOF, ERROR, TOMBSTONE},
|
SyntaxKind::{self, EOF, ERROR, TOMBSTONE},
|
||||||
Token, TokenSet, TokenSource, T,
|
TokenSet, TokenSource, T,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// `Parser` struct provides the low-level API for
|
/// `Parser` struct provides the low-level API for
|
||||||
|
@ -40,38 +40,6 @@ impl<'t> Parser<'t> {
|
||||||
self.nth(0)
|
self.nth(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the kinds of the current two tokens, if they are not separated
|
|
||||||
/// by trivia.
|
|
||||||
///
|
|
||||||
/// Useful for parsing things like `>>`.
|
|
||||||
pub(crate) fn current2(&self) -> Option<(SyntaxKind, SyntaxKind)> {
|
|
||||||
let c1 = self.nth(0);
|
|
||||||
let c2 = self.nth(1);
|
|
||||||
|
|
||||||
if self.token_source.current().is_jointed_to_next {
|
|
||||||
Some((c1, c2))
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the kinds of the current three tokens, if they are not separated
|
|
||||||
/// by trivia.
|
|
||||||
///
|
|
||||||
/// Useful for parsing things like `=>>`.
|
|
||||||
pub(crate) fn current3(&self) -> Option<(SyntaxKind, SyntaxKind, SyntaxKind)> {
|
|
||||||
let c1 = self.nth(0);
|
|
||||||
let c2 = self.nth(1);
|
|
||||||
let c3 = self.nth(2);
|
|
||||||
if self.token_source.current().is_jointed_to_next
|
|
||||||
&& self.token_source.lookahead_nth(1).is_jointed_to_next
|
|
||||||
{
|
|
||||||
Some((c1, c2, c3))
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Lookahead operation: returns the kind of the next nth
|
/// Lookahead operation: returns the kind of the next nth
|
||||||
/// token.
|
/// token.
|
||||||
pub(crate) fn nth(&self, n: usize) -> SyntaxKind {
|
pub(crate) fn nth(&self, n: usize) -> SyntaxKind {
|
||||||
|
@ -81,33 +49,93 @@ impl<'t> Parser<'t> {
|
||||||
assert!(steps <= 10_000_000, "the parser seems stuck");
|
assert!(steps <= 10_000_000, "the parser seems stuck");
|
||||||
self.steps.set(steps + 1);
|
self.steps.set(steps + 1);
|
||||||
|
|
||||||
// It is beecause the Dollar will appear between nth
|
self.token_source.lookahead_nth(n).kind
|
||||||
// Following code skips through it
|
|
||||||
let mut non_dollars_count = 0;
|
|
||||||
let mut i = 0;
|
|
||||||
|
|
||||||
loop {
|
|
||||||
let token = self.token_source.lookahead_nth(i);
|
|
||||||
let mut kind = token.kind;
|
|
||||||
if let Some((composited, step)) = self.is_composite(token, i) {
|
|
||||||
kind = composited;
|
|
||||||
i += step;
|
|
||||||
} else {
|
|
||||||
i += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
match kind {
|
|
||||||
EOF => return EOF,
|
|
||||||
SyntaxKind::L_DOLLAR | SyntaxKind::R_DOLLAR => {}
|
|
||||||
_ if non_dollars_count == n => return kind,
|
|
||||||
_ => non_dollars_count += 1,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Checks if the current token is `kind`.
|
/// Checks if the current token is `kind`.
|
||||||
pub(crate) fn at(&self, kind: SyntaxKind) -> bool {
|
pub(crate) fn at(&self, kind: SyntaxKind) -> bool {
|
||||||
self.current() == kind
|
self.nth_at(0, kind)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn nth_at(&self, n: usize, kind: SyntaxKind) -> bool {
|
||||||
|
match kind {
|
||||||
|
T![-=] => self.at_composite2(n, T![-], T![=]),
|
||||||
|
T![->] => self.at_composite2(n, T![-], T![>]),
|
||||||
|
T![::] => self.at_composite2(n, T![:], T![:]),
|
||||||
|
T![!=] => self.at_composite2(n, T![!], T![=]),
|
||||||
|
T![..] => self.at_composite2(n, T![.], T![.]),
|
||||||
|
T![*=] => self.at_composite2(n, T![*], T![=]),
|
||||||
|
T![/=] => self.at_composite2(n, T![/], T![=]),
|
||||||
|
T![&&] => self.at_composite2(n, T![&], T![&]),
|
||||||
|
T![&=] => self.at_composite2(n, T![&], T![=]),
|
||||||
|
T![%=] => self.at_composite2(n, T![%], T![=]),
|
||||||
|
T![^=] => self.at_composite2(n, T![^], T![=]),
|
||||||
|
T![+=] => self.at_composite2(n, T![+], T![=]),
|
||||||
|
T![<<] => self.at_composite2(n, T![<], T![<]),
|
||||||
|
T![<=] => self.at_composite2(n, T![<], T![=]),
|
||||||
|
T![==] => self.at_composite2(n, T![=], T![=]),
|
||||||
|
T![=>] => self.at_composite2(n, T![=], T![>]),
|
||||||
|
T![>=] => self.at_composite2(n, T![>], T![=]),
|
||||||
|
T![>>] => self.at_composite2(n, T![>], T![>]),
|
||||||
|
T![|=] => self.at_composite2(n, T![|], T![=]),
|
||||||
|
T![||] => self.at_composite2(n, T![|], T![|]),
|
||||||
|
|
||||||
|
T![...] => self.at_composite3(n, T![.], T![.], T![.]),
|
||||||
|
T![..=] => self.at_composite3(n, T![.], T![.], T![=]),
|
||||||
|
T![<<=] => self.at_composite3(n, T![<], T![<], T![=]),
|
||||||
|
T![>>=] => self.at_composite3(n, T![>], T![>], T![=]),
|
||||||
|
|
||||||
|
_ => self.token_source.lookahead_nth(n).kind == kind,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Consume the next token if `kind` matches.
|
||||||
|
pub(crate) fn eat(&mut self, kind: SyntaxKind) -> bool {
|
||||||
|
if !self.at(kind) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
let n_raw_tokens = match kind {
|
||||||
|
T![-=]
|
||||||
|
| T![->]
|
||||||
|
| T![::]
|
||||||
|
| T![!=]
|
||||||
|
| T![..]
|
||||||
|
| T![*=]
|
||||||
|
| T![/=]
|
||||||
|
| T![&&]
|
||||||
|
| T![&=]
|
||||||
|
| T![%=]
|
||||||
|
| T![^=]
|
||||||
|
| T![+=]
|
||||||
|
| T![<<]
|
||||||
|
| T![<=]
|
||||||
|
| T![==]
|
||||||
|
| T![=>]
|
||||||
|
| T![>=]
|
||||||
|
| T![>>]
|
||||||
|
| T![|=]
|
||||||
|
| T![||] => 2,
|
||||||
|
|
||||||
|
T![...] | T![..=] | T![<<=] | T![>>=] => 3,
|
||||||
|
_ => 1,
|
||||||
|
};
|
||||||
|
self.do_bump(kind, n_raw_tokens);
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
fn at_composite2(&self, n: usize, k1: SyntaxKind, k2: SyntaxKind) -> bool {
|
||||||
|
let t1 = self.token_source.lookahead_nth(n + 0);
|
||||||
|
let t2 = self.token_source.lookahead_nth(n + 1);
|
||||||
|
t1.kind == k1 && t1.is_jointed_to_next && t2.kind == k2
|
||||||
|
}
|
||||||
|
|
||||||
|
fn at_composite3(&self, n: usize, k1: SyntaxKind, k2: SyntaxKind, k3: SyntaxKind) -> bool {
|
||||||
|
let t1 = self.token_source.lookahead_nth(n + 0);
|
||||||
|
let t2 = self.token_source.lookahead_nth(n + 1);
|
||||||
|
let t3 = self.token_source.lookahead_nth(n + 2);
|
||||||
|
(t1.kind == k1 && t1.is_jointed_to_next)
|
||||||
|
&& (t2.kind == k2 && t2.is_jointed_to_next)
|
||||||
|
&& t3.kind == k3
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Checks if the current token is in `kinds`.
|
/// Checks if the current token is in `kinds`.
|
||||||
|
@ -129,22 +157,9 @@ impl<'t> Parser<'t> {
|
||||||
Marker::new(pos)
|
Marker::new(pos)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Advances the parser by one token unconditionally
|
/// Consume the next token if `kind` matches.
|
||||||
/// Mainly use in `token_tree` parsing
|
pub(crate) fn bump(&mut self, kind: SyntaxKind) {
|
||||||
pub(crate) fn bump_raw(&mut self) {
|
assert!(self.eat(kind));
|
||||||
let mut kind = self.token_source.current().kind;
|
|
||||||
|
|
||||||
// Skip dollars, do_bump will eat these later
|
|
||||||
let mut i = 0;
|
|
||||||
while kind == SyntaxKind::L_DOLLAR || kind == SyntaxKind::R_DOLLAR {
|
|
||||||
kind = self.token_source.lookahead_nth(i).kind;
|
|
||||||
i += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if kind == EOF {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
self.do_bump(kind, 1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Advances the parser by one token with composite puncts handled
|
/// Advances the parser by one token with composite puncts handled
|
||||||
|
@ -153,27 +168,7 @@ impl<'t> Parser<'t> {
|
||||||
if kind == EOF {
|
if kind == EOF {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
self.do_bump(kind, 1)
|
||||||
use SyntaxKind::*;
|
|
||||||
|
|
||||||
// Handle parser composites
|
|
||||||
match kind {
|
|
||||||
T![...] | T![..=] => {
|
|
||||||
self.bump_compound(kind, 3);
|
|
||||||
}
|
|
||||||
T![..] | T![::] | T![==] | T![=>] | T![!=] | T![->] => {
|
|
||||||
self.bump_compound(kind, 2);
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
self.do_bump(kind, 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Advances the parser by one token, asserting that it is exactly the expected token
|
|
||||||
pub(crate) fn bump(&mut self, expected: SyntaxKind) {
|
|
||||||
debug_assert!(self.nth(0) == expected);
|
|
||||||
self.bump_any()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Advances the parser by one token, remapping its kind.
|
/// Advances the parser by one token, remapping its kind.
|
||||||
|
@ -190,13 +185,6 @@ impl<'t> Parser<'t> {
|
||||||
self.do_bump(kind, 1);
|
self.do_bump(kind, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Advances the parser by `n` tokens, remapping its kind.
|
|
||||||
/// This is useful to create compound tokens from parts. For
|
|
||||||
/// example, an `<<` token is two consecutive remapped `<` tokens
|
|
||||||
pub(crate) fn bump_compound(&mut self, kind: SyntaxKind, n: u8) {
|
|
||||||
self.do_bump(kind, n);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Emit error with the `message`
|
/// Emit error with the `message`
|
||||||
/// FIXME: this should be much more fancy and support
|
/// FIXME: this should be much more fancy and support
|
||||||
/// structured errors with spans and notes, like rustc
|
/// structured errors with spans and notes, like rustc
|
||||||
|
@ -206,15 +194,6 @@ impl<'t> Parser<'t> {
|
||||||
self.push_event(Event::Error { msg })
|
self.push_event(Event::Error { msg })
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Consume the next token if `kind` matches.
|
|
||||||
pub(crate) fn eat(&mut self, kind: SyntaxKind) -> bool {
|
|
||||||
if !self.at(kind) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
self.bump_any();
|
|
||||||
true
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Consume the next token if it is `kind` or emit an error
|
/// Consume the next token if it is `kind` or emit an error
|
||||||
/// otherwise.
|
/// otherwise.
|
||||||
pub(crate) fn expect(&mut self, kind: SyntaxKind) -> bool {
|
pub(crate) fn expect(&mut self, kind: SyntaxKind) -> bool {
|
||||||
|
@ -243,7 +222,7 @@ impl<'t> Parser<'t> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn do_bump(&mut self, kind: SyntaxKind, n_raw_tokens: u8) {
|
fn do_bump(&mut self, kind: SyntaxKind, n_raw_tokens: u8) {
|
||||||
self.eat_dollars();
|
// self.eat_dollars();
|
||||||
|
|
||||||
for _ in 0..n_raw_tokens {
|
for _ in 0..n_raw_tokens {
|
||||||
self.token_source.bump();
|
self.token_source.bump();
|
||||||
|
@ -256,64 +235,6 @@ impl<'t> Parser<'t> {
|
||||||
self.events.push(event)
|
self.events.push(event)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// helper function for check if it is composite.
|
|
||||||
fn is_composite(&self, first: Token, n: usize) -> Option<(SyntaxKind, usize)> {
|
|
||||||
// We assume the dollars will not occuried between
|
|
||||||
// mult-byte tokens
|
|
||||||
|
|
||||||
let jn1 = first.is_jointed_to_next;
|
|
||||||
if !jn1 && first.kind != T![-] {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
let second = self.token_source.lookahead_nth(n + 1);
|
|
||||||
if first.kind == T![-] && second.kind == T![>] {
|
|
||||||
return Some((T![->], 2));
|
|
||||||
}
|
|
||||||
if !jn1 {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
match (first.kind, second.kind) {
|
|
||||||
(T![:], T![:]) => return Some((T![::], 2)),
|
|
||||||
(T![=], T![=]) => return Some((T![==], 2)),
|
|
||||||
(T![=], T![>]) => return Some((T![=>], 2)),
|
|
||||||
(T![!], T![=]) => return Some((T![!=], 2)),
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
|
|
||||||
if first.kind != T![.] || second.kind != T![.] {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
let third = self.token_source.lookahead_nth(n + 2);
|
|
||||||
|
|
||||||
let jn2 = second.is_jointed_to_next;
|
|
||||||
let la3 = third.kind;
|
|
||||||
|
|
||||||
if jn2 && la3 == T![.] {
|
|
||||||
return Some((T![...], 3));
|
|
||||||
}
|
|
||||||
if la3 == T![=] {
|
|
||||||
return Some((T![..=], 3));
|
|
||||||
}
|
|
||||||
return Some((T![..], 2));
|
|
||||||
}
|
|
||||||
|
|
||||||
fn eat_dollars(&mut self) {
|
|
||||||
loop {
|
|
||||||
match self.token_source.current().kind {
|
|
||||||
k @ SyntaxKind::L_DOLLAR | k @ SyntaxKind::R_DOLLAR => {
|
|
||||||
self.token_source.bump();
|
|
||||||
self.push_event(Event::Token { kind: k, n_raw_tokens: 1 });
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn eat_l_dollars(&mut self) -> usize {
|
pub(crate) fn eat_l_dollars(&mut self) -> usize {
|
||||||
let mut ate_count = 0;
|
let mut ate_count = 0;
|
||||||
loop {
|
loop {
|
||||||
|
|
|
@ -15,6 +15,18 @@ fn lexer_tests() {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn parse_smoke_test() {
|
||||||
|
let code = r##"
|
||||||
|
fn main() {
|
||||||
|
println!("Hello, world!")
|
||||||
|
}
|
||||||
|
"##;
|
||||||
|
|
||||||
|
let parse = SourceFile::parse(code);
|
||||||
|
assert!(parse.ok().is_ok());
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn parser_tests() {
|
fn parser_tests() {
|
||||||
dir_tests(&test_data_dir(), &["parser/inline/ok", "parser/ok"], |text, path| {
|
dir_tests(&test_data_dir(), &["parser/inline/ok", "parser/ok"], |text, path| {
|
||||||
|
@ -75,7 +87,9 @@ fn self_hosting_parsing() {
|
||||||
{
|
{
|
||||||
count += 1;
|
count += 1;
|
||||||
let text = read_text(entry.path());
|
let text = read_text(entry.path());
|
||||||
SourceFile::parse(&text).ok().expect("There should be no errors in the file");
|
if let Err(errors) = SourceFile::parse(&text).ok() {
|
||||||
|
panic!("Parsing errors:\n{:?}\n{}\n", errors, entry.path().display());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
assert!(
|
assert!(
|
||||||
count > 30,
|
count > 30,
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
fn a() -> Foo<bar::Baz> {}
|
||||||
|
|
||||||
|
fn b(_: impl FnMut(x::Y)) {}
|
||||||
|
|
||||||
|
fn c(_: impl FnMut(&x::Y)) {}
|
|
@ -0,0 +1,126 @@
|
||||||
|
SOURCE_FILE@[0; 88)
|
||||||
|
FN_DEF@[0; 26)
|
||||||
|
FN_KW@[0; 2) "fn"
|
||||||
|
WHITESPACE@[2; 3) " "
|
||||||
|
NAME@[3; 4)
|
||||||
|
IDENT@[3; 4) "a"
|
||||||
|
PARAM_LIST@[4; 6)
|
||||||
|
L_PAREN@[4; 5) "("
|
||||||
|
R_PAREN@[5; 6) ")"
|
||||||
|
WHITESPACE@[6; 7) " "
|
||||||
|
RET_TYPE@[7; 23)
|
||||||
|
THIN_ARROW@[7; 9) "->"
|
||||||
|
WHITESPACE@[9; 10) " "
|
||||||
|
PATH_TYPE@[10; 23)
|
||||||
|
PATH@[10; 23)
|
||||||
|
PATH_SEGMENT@[10; 23)
|
||||||
|
NAME_REF@[10; 13)
|
||||||
|
IDENT@[10; 13) "Foo"
|
||||||
|
TYPE_ARG_LIST@[13; 23)
|
||||||
|
L_ANGLE@[13; 14) "<"
|
||||||
|
TYPE_ARG@[14; 22)
|
||||||
|
PATH_TYPE@[14; 22)
|
||||||
|
PATH@[14; 22)
|
||||||
|
PATH@[14; 17)
|
||||||
|
PATH_SEGMENT@[14; 17)
|
||||||
|
NAME_REF@[14; 17)
|
||||||
|
IDENT@[14; 17) "bar"
|
||||||
|
COLONCOLON@[17; 19) "::"
|
||||||
|
PATH_SEGMENT@[19; 22)
|
||||||
|
NAME_REF@[19; 22)
|
||||||
|
IDENT@[19; 22) "Baz"
|
||||||
|
R_ANGLE@[22; 23) ">"
|
||||||
|
WHITESPACE@[23; 24) " "
|
||||||
|
BLOCK_EXPR@[24; 26)
|
||||||
|
BLOCK@[24; 26)
|
||||||
|
L_CURLY@[24; 25) "{"
|
||||||
|
R_CURLY@[25; 26) "}"
|
||||||
|
WHITESPACE@[26; 28) "\n\n"
|
||||||
|
FN_DEF@[28; 56)
|
||||||
|
FN_KW@[28; 30) "fn"
|
||||||
|
WHITESPACE@[30; 31) " "
|
||||||
|
NAME@[31; 32)
|
||||||
|
IDENT@[31; 32) "b"
|
||||||
|
PARAM_LIST@[32; 53)
|
||||||
|
L_PAREN@[32; 33) "("
|
||||||
|
PARAM@[33; 52)
|
||||||
|
PLACEHOLDER_PAT@[33; 34)
|
||||||
|
UNDERSCORE@[33; 34) "_"
|
||||||
|
COLON@[34; 35) ":"
|
||||||
|
WHITESPACE@[35; 36) " "
|
||||||
|
IMPL_TRAIT_TYPE@[36; 52)
|
||||||
|
IMPL_KW@[36; 40) "impl"
|
||||||
|
WHITESPACE@[40; 41) " "
|
||||||
|
TYPE_BOUND_LIST@[41; 52)
|
||||||
|
TYPE_BOUND@[41; 52)
|
||||||
|
PATH_TYPE@[41; 52)
|
||||||
|
PATH@[41; 52)
|
||||||
|
PATH_SEGMENT@[41; 52)
|
||||||
|
NAME_REF@[41; 46)
|
||||||
|
IDENT@[41; 46) "FnMut"
|
||||||
|
PARAM_LIST@[46; 52)
|
||||||
|
L_PAREN@[46; 47) "("
|
||||||
|
PARAM@[47; 51)
|
||||||
|
PATH_TYPE@[47; 51)
|
||||||
|
PATH@[47; 51)
|
||||||
|
PATH@[47; 48)
|
||||||
|
PATH_SEGMENT@[47; 48)
|
||||||
|
NAME_REF@[47; 48)
|
||||||
|
IDENT@[47; 48) "x"
|
||||||
|
COLONCOLON@[48; 50) "::"
|
||||||
|
PATH_SEGMENT@[50; 51)
|
||||||
|
NAME_REF@[50; 51)
|
||||||
|
IDENT@[50; 51) "Y"
|
||||||
|
R_PAREN@[51; 52) ")"
|
||||||
|
R_PAREN@[52; 53) ")"
|
||||||
|
WHITESPACE@[53; 54) " "
|
||||||
|
BLOCK_EXPR@[54; 56)
|
||||||
|
BLOCK@[54; 56)
|
||||||
|
L_CURLY@[54; 55) "{"
|
||||||
|
R_CURLY@[55; 56) "}"
|
||||||
|
WHITESPACE@[56; 58) "\n\n"
|
||||||
|
FN_DEF@[58; 87)
|
||||||
|
FN_KW@[58; 60) "fn"
|
||||||
|
WHITESPACE@[60; 61) " "
|
||||||
|
NAME@[61; 62)
|
||||||
|
IDENT@[61; 62) "c"
|
||||||
|
PARAM_LIST@[62; 84)
|
||||||
|
L_PAREN@[62; 63) "("
|
||||||
|
PARAM@[63; 83)
|
||||||
|
PLACEHOLDER_PAT@[63; 64)
|
||||||
|
UNDERSCORE@[63; 64) "_"
|
||||||
|
COLON@[64; 65) ":"
|
||||||
|
WHITESPACE@[65; 66) " "
|
||||||
|
IMPL_TRAIT_TYPE@[66; 83)
|
||||||
|
IMPL_KW@[66; 70) "impl"
|
||||||
|
WHITESPACE@[70; 71) " "
|
||||||
|
TYPE_BOUND_LIST@[71; 83)
|
||||||
|
TYPE_BOUND@[71; 83)
|
||||||
|
PATH_TYPE@[71; 83)
|
||||||
|
PATH@[71; 83)
|
||||||
|
PATH_SEGMENT@[71; 83)
|
||||||
|
NAME_REF@[71; 76)
|
||||||
|
IDENT@[71; 76) "FnMut"
|
||||||
|
PARAM_LIST@[76; 83)
|
||||||
|
L_PAREN@[76; 77) "("
|
||||||
|
PARAM@[77; 82)
|
||||||
|
REFERENCE_TYPE@[77; 82)
|
||||||
|
AMP@[77; 78) "&"
|
||||||
|
PATH_TYPE@[78; 82)
|
||||||
|
PATH@[78; 82)
|
||||||
|
PATH@[78; 79)
|
||||||
|
PATH_SEGMENT@[78; 79)
|
||||||
|
NAME_REF@[78; 79)
|
||||||
|
IDENT@[78; 79) "x"
|
||||||
|
COLONCOLON@[79; 81) "::"
|
||||||
|
PATH_SEGMENT@[81; 82)
|
||||||
|
NAME_REF@[81; 82)
|
||||||
|
IDENT@[81; 82) "Y"
|
||||||
|
R_PAREN@[82; 83) ")"
|
||||||
|
R_PAREN@[83; 84) ")"
|
||||||
|
WHITESPACE@[84; 85) " "
|
||||||
|
BLOCK_EXPR@[85; 87)
|
||||||
|
BLOCK@[85; 87)
|
||||||
|
L_CURLY@[85; 86) "{"
|
||||||
|
R_CURLY@[86; 87) "}"
|
||||||
|
WHITESPACE@[87; 88) "\n"
|
5
crates/ra_syntax/test_data/parser/ok/0055_dot_dot_dot.rs
Normal file
5
crates/ra_syntax/test_data/parser/ok/0055_dot_dot_dot.rs
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
type X = ();
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let ():::X = ();
|
||||||
|
}
|
50
crates/ra_syntax/test_data/parser/ok/0055_dot_dot_dot.txt
Normal file
50
crates/ra_syntax/test_data/parser/ok/0055_dot_dot_dot.txt
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
SOURCE_FILE@[0; 49)
|
||||||
|
TYPE_ALIAS_DEF@[0; 12)
|
||||||
|
TYPE_KW@[0; 4) "type"
|
||||||
|
WHITESPACE@[4; 5) " "
|
||||||
|
NAME@[5; 6)
|
||||||
|
IDENT@[5; 6) "X"
|
||||||
|
WHITESPACE@[6; 7) " "
|
||||||
|
EQ@[7; 8) "="
|
||||||
|
WHITESPACE@[8; 9) " "
|
||||||
|
TUPLE_TYPE@[9; 11)
|
||||||
|
L_PAREN@[9; 10) "("
|
||||||
|
R_PAREN@[10; 11) ")"
|
||||||
|
SEMI@[11; 12) ";"
|
||||||
|
WHITESPACE@[12; 14) "\n\n"
|
||||||
|
FN_DEF@[14; 48)
|
||||||
|
FN_KW@[14; 16) "fn"
|
||||||
|
WHITESPACE@[16; 17) " "
|
||||||
|
NAME@[17; 21)
|
||||||
|
IDENT@[17; 21) "main"
|
||||||
|
PARAM_LIST@[21; 23)
|
||||||
|
L_PAREN@[21; 22) "("
|
||||||
|
R_PAREN@[22; 23) ")"
|
||||||
|
WHITESPACE@[23; 24) " "
|
||||||
|
BLOCK_EXPR@[24; 48)
|
||||||
|
BLOCK@[24; 48)
|
||||||
|
L_CURLY@[24; 25) "{"
|
||||||
|
WHITESPACE@[25; 30) "\n "
|
||||||
|
LET_STMT@[30; 46)
|
||||||
|
LET_KW@[30; 33) "let"
|
||||||
|
WHITESPACE@[33; 34) " "
|
||||||
|
TUPLE_PAT@[34; 36)
|
||||||
|
L_PAREN@[34; 35) "("
|
||||||
|
R_PAREN@[35; 36) ")"
|
||||||
|
COLON@[36; 37) ":"
|
||||||
|
PATH_TYPE@[37; 40)
|
||||||
|
PATH@[37; 40)
|
||||||
|
PATH_SEGMENT@[37; 40)
|
||||||
|
COLONCOLON@[37; 39) "::"
|
||||||
|
NAME_REF@[39; 40)
|
||||||
|
IDENT@[39; 40) "X"
|
||||||
|
WHITESPACE@[40; 41) " "
|
||||||
|
EQ@[41; 42) "="
|
||||||
|
WHITESPACE@[42; 43) " "
|
||||||
|
TUPLE_EXPR@[43; 45)
|
||||||
|
L_PAREN@[43; 44) "("
|
||||||
|
R_PAREN@[44; 45) ")"
|
||||||
|
SEMI@[45; 46) ";"
|
||||||
|
WHITESPACE@[46; 47) "\n"
|
||||||
|
R_CURLY@[47; 48) "}"
|
||||||
|
WHITESPACE@[48; 49) "\n"
|
Loading…
Reference in a new issue