organizize

This commit is contained in:
Aleksey Kladov
2018-08-10 22:33:29 +03:00
parent 26262aaf05
commit 7c67612b8a
376 changed files with 27 additions and 145 deletions

View File

@@ -0,0 +1,79 @@
use super::*;
pub(super) fn inner_attributes(p: &mut Parser) {
while p.current() == POUND && p.nth(1) == EXCL {
attribute(p, true)
}
}
pub(super) fn outer_attributes(p: &mut Parser) {
while p.at(POUND) {
attribute(p, false)
}
}
fn attribute(p: &mut Parser, inner: bool) {
let attr = p.start();
assert!(p.at(POUND));
p.bump();
if inner {
assert!(p.at(EXCL));
p.bump();
}
if p.expect(L_BRACK) {
meta_item(p);
p.expect(R_BRACK);
}
attr.complete(p, ATTR);
}
fn meta_item(p: &mut Parser) {
if p.at(IDENT) {
let meta_item = p.start();
p.bump();
match p.current() {
EQ => {
p.bump();
if expressions::literal(p).is_none() {
p.error("expected literal");
}
}
L_PAREN => meta_item_arg_list(p),
_ => (),
}
meta_item.complete(p, META_ITEM);
} else {
p.error("expected attribute value");
}
}
fn meta_item_arg_list(p: &mut Parser) {
assert!(p.at(L_PAREN));
p.bump();
loop {
match p.current() {
EOF | R_PAREN => break,
IDENT => meta_item(p),
c => if expressions::literal(p).is_none() {
let message = "expected attribute";
if items::ITEM_FIRST.contains(c) {
p.error(message);
return;
}
let err = p.start();
p.error(message);
p.bump();
err.complete(p, ERROR);
continue;
},
}
if !p.at(R_PAREN) {
p.expect(COMMA);
}
}
p.expect(R_PAREN);
}

View File

@@ -0,0 +1,348 @@
use super::*;
// test expr_literals
// fn foo() {
// let _ = true;
// let _ = false;
// let _ = 1;
// let _ = 2.0;
// let _ = b'a';
// let _ = 'b';
// let _ = "c";
// let _ = r"d";
// 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(crate) fn literal(p: &mut Parser) -> Option<CompletedMarker> {
if !LITERAL_FIRST.contains(p.current()) {
return None;
}
let m = p.start();
p.bump();
Some(m.complete(p, LITERAL))
}
pub(super) const ATOM_EXPR_FIRST: TokenSet =
token_set_union![
LITERAL_FIRST,
token_set![L_PAREN, PIPE, MOVE_KW, IF_KW, WHILE_KW, MATCH_KW, UNSAFE_KW, L_CURLY, RETURN_KW,
IDENT, SELF_KW, SUPER_KW, COLONCOLON ],
];
pub(super) fn atom_expr(p: &mut Parser, r: Restrictions) -> Option<CompletedMarker> {
match literal(p) {
Some(m) => return Some(m),
None => (),
}
if paths::is_path_start(p) {
return Some(path_expr(p, r));
}
let la = p.nth(1);
let done = match p.current() {
L_PAREN => tuple_expr(p),
L_BRACK => array_expr(p),
PIPE => lambda_expr(p),
MOVE_KW if la == PIPE => lambda_expr(p),
IF_KW => if_expr(p),
WHILE_KW => while_expr(p),
LOOP_KW => loop_expr(p),
FOR_KW => for_expr(p),
MATCH_KW => match_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;
}
};
Some(done)
}
// test tuple_expr
// fn foo() {
// ();
// (1);
// (1,);
// }
fn tuple_expr(p: &mut Parser) -> CompletedMarker {
assert!(p.at(L_PAREN));
let m = p.start();
p.expect(L_PAREN);
let mut saw_comma = false;
let mut saw_expr = false;
while !p.at(EOF) && !p.at(R_PAREN) {
saw_expr = true;
expr(p);
if !p.at(R_PAREN) {
saw_comma = true;
p.expect(COMMA);
}
}
p.expect(R_PAREN);
m.complete(p, if saw_expr && !saw_comma { PAREN_EXPR } else { TUPLE_EXPR })
}
// test array_expr
// fn foo() {
// [];
// [1];
// [1, 2,];
// [1; 2];
// }
fn array_expr(p: &mut Parser) -> CompletedMarker {
assert!(p.at(L_BRACK));
let m = p.start();
p.bump();
if p.eat(R_BRACK) {
return m.complete(p, ARRAY_EXPR);
}
expr(p);
if p.eat(SEMI) {
expr(p);
p.expect(R_BRACK);
return m.complete(p, ARRAY_EXPR);
}
while !p.at(EOF) && !p.at(R_BRACK) {
p.expect(COMMA);
if !p.at(R_BRACK) {
expr(p);
}
}
p.expect(R_BRACK);
m.complete(p, ARRAY_EXPR)
}
// test lambda_expr
// fn foo() {
// || ();
// || -> i32 { 92 };
// |x| x;
// move |x: i32,| x;
// }
fn lambda_expr(p: &mut Parser) -> CompletedMarker {
assert!(p.at(PIPE) || (p.at(MOVE_KW) && p.nth(1) == PIPE));
let m = p.start();
p.eat(MOVE_KW);
params::param_list_opt_types(p);
if fn_ret_type(p) {
block(p);
} else {
expr(p);
}
m.complete(p, LAMBDA_EXPR)
}
// test if_expr
// fn foo() {
// if true {};
// if true {} else {};
// if true {} else if false {} else {};
// if S {};
// }
fn if_expr(p: &mut Parser) -> CompletedMarker {
assert!(p.at(IF_KW));
let m = p.start();
p.bump();
cond(p);
block(p);
if p.at(ELSE_KW) {
p.bump();
if p.at(IF_KW) {
if_expr(p);
} else {
block(p);
}
}
m.complete(p, IF_EXPR)
}
// test while_expr
// fn foo() {
// while true {};
// while let Some(x) = it.next() {};
// }
fn while_expr(p: &mut Parser) -> CompletedMarker {
assert!(p.at(WHILE_KW));
let m = p.start();
p.bump();
cond(p);
block(p);
m.complete(p, WHILE_EXPR)
}
// test loop_expr
// fn foo() {
// loop {};
// }
fn loop_expr(p: &mut Parser) -> CompletedMarker {
assert!(p.at(LOOP_KW));
let m = p.start();
p.bump();
block(p);
m.complete(p, LOOP_EXPR)
}
// test for_expr
// fn foo() {
// for x in [] {};
// }
fn for_expr(p: &mut Parser) -> CompletedMarker {
assert!(p.at(FOR_KW));
let m = p.start();
p.bump();
patterns::pattern(p);
p.expect(IN_KW);
expr_no_struct(p);
block(p);
m.complete(p, FOR_EXPR)
}
// test cond
// fn foo() { if let Some(_) = None {} }
fn cond(p: &mut Parser) {
if p.eat(LET_KW) {
patterns::pattern(p);
p.expect(EQ);
}
expr_no_struct(p)
}
// test match_expr
// fn foo() {
// match () { };
// match S {};
// }
fn match_expr(p: &mut Parser) -> CompletedMarker {
assert!(p.at(MATCH_KW));
let m = p.start();
p.bump();
expr_no_struct(p);
p.eat(L_CURLY);
while !p.at(EOF) && !p.at(R_CURLY) {
// test match_arms_commas
// fn foo() {
// match () {
// _ => (),
// _ => {}
// _ => ()
// }
// }
if match_arm(p).is_block() {
p.eat(COMMA);
} else if !p.at(R_CURLY) {
p.expect(COMMA);
}
}
p.expect(R_CURLY);
m.complete(p, MATCH_EXPR)
}
// test match_arm
// fn foo() {
// match () {
// _ => (),
// X | Y if Z => (),
// };
// }
fn match_arm(p: &mut Parser) -> BlockLike {
let m = p.start();
loop {
patterns::pattern(p);
if !p.eat(PIPE) {
break;
}
}
if p.eat(IF_KW) {
expr_no_struct(p);
}
p.expect(FAT_ARROW);
let ret = expr_stmt(p);
m.complete(p, MATCH_ARM);
ret
}
// test block_expr
// fn foo() {
// {};
// unsafe {};
// }
pub(super) fn block_expr(p: &mut Parser) -> CompletedMarker {
assert!(p.at(L_CURLY) || p.at(UNSAFE_KW) && p.nth(1) == L_CURLY);
let m = p.start();
p.eat(UNSAFE_KW);
p.bump();
while !p.at(EOF) && !p.at(R_CURLY) {
match p.current() {
LET_KW => let_stmt(p),
_ => {
// test block_items
// fn a() { fn b() {} }
let m = p.start();
match items::maybe_item(p) {
items::MaybeItem::Item(kind) => {
m.complete(p, kind);
}
items::MaybeItem::Modifiers => {
m.abandon(p);
p.error("expected an item");
}
// test pub_expr
// fn foo() { pub 92; } //FIXME
items::MaybeItem::None => {
let is_blocklike = expressions::expr_stmt(p) == BlockLike::Block;
if p.eat(SEMI) || (is_blocklike && !p.at(R_CURLY)) {
m.complete(p, EXPR_STMT);
} else {
m.abandon(p);
}
}
}
}
}
}
p.expect(R_CURLY);
m.complete(p, BLOCK_EXPR)
}
// test let_stmt;
// fn foo() {
// let a;
// let b: i32;
// let c = 92;
// let d: i32 = 92;
// }
fn let_stmt(p: &mut Parser) {
assert!(p.at(LET_KW));
let m = p.start();
p.bump();
patterns::pattern(p);
if p.at(COLON) {
types::ascription(p);
}
if p.eat(EQ) {
expressions::expr(p);
}
p.expect(SEMI);
m.complete(p, LET_STMT);
}
// 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)
}

View File

@@ -0,0 +1,379 @@
mod atom;
use super::*;
pub(super) use self::atom::literal;
const EXPR_FIRST: TokenSet = LHS_FIRST;
pub(super) fn expr(p: &mut Parser) -> BlockLike {
let r = Restrictions { forbid_structs: false, prefer_stmt: false };
expr_bp(p, r, 1)
}
pub(super) fn expr_stmt(p: &mut Parser) -> BlockLike {
let r = Restrictions { forbid_structs: false, prefer_stmt: true };
expr_bp(p, r, 1)
}
fn expr_no_struct(p: &mut Parser) {
let r = Restrictions { forbid_structs: true, prefer_stmt: false };
expr_bp(p, r, 1);
}
// test block
// fn a() {}
// fn b() { let _ = 1; }
// fn c() { 1; 2; }
// fn d() { 1; 2 }
pub(super) fn block(p: &mut Parser) {
if !p.at(L_CURLY) {
p.error("expected block");
return;
}
atom::block_expr(p);
}
#[derive(Clone, Copy)]
struct Restrictions {
forbid_structs: bool,
prefer_stmt: bool,
}
enum Op {
Simple,
Composite(SyntaxKind, u8),
}
fn current_op(p: &Parser) -> (u8, Op) {
if p.at_compound2(PLUS, EQ) {
return (1, Op::Composite(PLUSEQ, 2));
}
if p.at_compound2(MINUS, EQ) {
return (1, Op::Composite(MINUSEQ, 2));
}
if p.at_compound3(L_ANGLE, L_ANGLE, EQ) {
return (1, Op::Composite(SHLEQ, 3));
}
if p.at_compound3(R_ANGLE, R_ANGLE, EQ) {
return (1, Op::Composite(SHREQ, 3));
}
if p.at_compound2(PIPE, PIPE) {
return (3, Op::Composite(PIPEPIPE, 2));
}
if p.at_compound2(AMP, AMP) {
return (4, Op::Composite(AMPAMP, 2));
}
if p.at_compound2(L_ANGLE, EQ) {
return (5, Op::Composite(LTEQ, 2));
}
if p.at_compound2(R_ANGLE, EQ) {
return (5, Op::Composite(GTEQ, 2));
}
if p.at_compound2(L_ANGLE, L_ANGLE) {
return (9, Op::Composite(SHL, 2));
}
if p.at_compound2(R_ANGLE, R_ANGLE) {
return (9, Op::Composite(SHR, 2));
}
let bp = match p.current() {
EQ => 1,
DOTDOT => 2,
EQEQ | NEQ | L_ANGLE | R_ANGLE => 5,
PIPE => 6,
CARET => 7,
AMP => 8,
MINUS | PLUS => 10,
STAR | SLASH | PERCENT => 11,
_ => 0,
};
(bp, Op::Simple)
}
// Parses expression with binding power of at least bp.
fn expr_bp(p: &mut Parser, r: Restrictions, bp: u8) -> BlockLike {
let mut lhs = match lhs(p, r) {
Some(lhs) => {
// test stmt_bin_expr_ambiguity
// fn foo() {
// let _ = {1} & 2;
// {1} &2;
// }
if r.prefer_stmt && is_block(lhs.kind()) {
return BlockLike::Block;
}
lhs
}
None => return BlockLike::NotBlock,
};
loop {
let is_range = p.current() == DOTDOT;
let (op_bp, op) = current_op(p);
if op_bp < bp {
break;
}
let m = lhs.precede(p);
match op {
Op::Simple => p.bump(),
Op::Composite(kind, n) => {
p.bump_compound(kind, n);
}
}
expr_bp(p, r, op_bp + 1);
lhs = m.complete(p, if is_range { RANGE_EXPR } else { BIN_EXPR });
}
BlockLike::NotBlock
}
// test no_semi_after_block
// fn foo() {
// if true {}
// loop {}
// match () {}
// while true {}
// for _ in () {}
// {}
// {}
// }
fn is_block(kind: SyntaxKind) -> bool {
match kind {
IF_EXPR | WHILE_EXPR | FOR_EXPR | LOOP_EXPR | MATCH_EXPR | BLOCK_EXPR => true,
_ => false,
}
}
const LHS_FIRST: TokenSet =
token_set_union![
token_set![AMP, STAR, EXCL, DOTDOT, MINUS],
atom::ATOM_EXPR_FIRST,
];
fn lhs(p: &mut Parser, r: Restrictions) -> Option<CompletedMarker> {
let m;
let kind = match p.current() {
// test ref_expr
// fn foo() {
// let _ = &1;
// let _ = &mut &f();
// }
AMP => {
m = p.start();
p.bump();
p.eat(MUT_KW);
REF_EXPR
}
// test unary_expr
// fn foo() {
// **&1;
// !!true;
// --1;
// }
STAR | EXCL | MINUS => {
m = p.start();
p.bump();
PREFIX_EXPR
}
DOTDOT => {
m = p.start();
p.bump();
expr_bp(p, r, 2);
return Some(m.complete(p, RANGE_EXPR));
}
_ => {
let lhs = atom::atom_expr(p, r)?;
return Some(postfix_expr(p, r, lhs));
}
};
expr_bp(p, r, 255);
Some(m.complete(p, kind))
}
fn postfix_expr(p: &mut Parser, r: Restrictions, mut lhs: CompletedMarker) -> CompletedMarker {
let mut allow_calls = !r.prefer_stmt || !is_block(lhs.kind());
loop {
lhs = match p.current() {
// test stmt_postfix_expr_ambiguity
// fn foo() {
// match () {
// _ => {}
// () => {}
// [] => {}
// }
// }
L_PAREN if allow_calls => call_expr(p, lhs),
L_BRACK if allow_calls => index_expr(p, lhs),
DOT if p.nth(1) == IDENT => if p.nth(2) == L_PAREN || p.nth(2) == COLONCOLON {
method_call_expr(p, lhs)
} else {
field_expr(p, lhs)
},
DOT if p.nth(1) == INT_NUMBER => field_expr(p, lhs),
// test postfix_range
// fn foo() { let x = 1..; }
DOTDOT if !EXPR_FIRST.contains(p.nth(1)) => {
let m = lhs.precede(p);
p.bump();
m.complete(p, RANGE_EXPR)
}
QUESTION => try_expr(p, lhs),
AS_KW => cast_expr(p, lhs),
_ => break,
};
allow_calls = true
}
lhs
}
// test call_expr
// fn foo() {
// let _ = f();
// let _ = f()(1)(1, 2,);
// }
fn call_expr(p: &mut Parser, lhs: CompletedMarker) -> CompletedMarker {
assert!(p.at(L_PAREN));
let m = lhs.precede(p);
arg_list(p);
m.complete(p, CALL_EXPR)
}
// test index_expr
// fn foo() {
// x[1][2];
// }
fn index_expr(p: &mut Parser, lhs: CompletedMarker) -> CompletedMarker {
assert!(p.at(L_BRACK));
let m = lhs.precede(p);
p.bump();
expr(p);
p.expect(R_BRACK);
m.complete(p, INDEX_EXPR)
}
// test method_call_expr
// fn foo() {
// x.foo();
// y.bar::<T>(1, 2,);
// }
fn method_call_expr(p: &mut Parser, lhs: CompletedMarker) -> CompletedMarker {
assert!(
p.at(DOT) && p.nth(1) == IDENT
&& (p.nth(2) == L_PAREN || p.nth(2) == COLONCOLON)
);
let m = lhs.precede(p);
p.bump();
name_ref(p);
type_args::type_arg_list(p, true);
arg_list(p);
m.complete(p, METHOD_CALL_EXPR)
}
// test field_expr
// fn foo() {
// x.foo;
// x.0.bar;
// }
fn field_expr(p: &mut Parser, lhs: CompletedMarker) -> CompletedMarker {
assert!(p.at(DOT) && (p.nth(1) == IDENT || p.nth(1) == INT_NUMBER));
let m = lhs.precede(p);
p.bump();
if p.at(IDENT) {
name_ref(p)
} else {
p.bump()
}
m.complete(p, FIELD_EXPR)
}
// test try_expr
// fn foo() {
// x?;
// }
fn try_expr(p: &mut Parser, lhs: CompletedMarker) -> CompletedMarker {
assert!(p.at(QUESTION));
let m = lhs.precede(p);
p.bump();
m.complete(p, TRY_EXPR)
}
// test cast_expr
// fn foo() {
// 82 as i32;
// }
fn cast_expr(p: &mut Parser, lhs: CompletedMarker) -> CompletedMarker {
assert!(p.at(AS_KW));
let m = lhs.precede(p);
p.bump();
types::type_(p);
m.complete(p, CAST_EXPR)
}
fn arg_list(p: &mut Parser) {
assert!(p.at(L_PAREN));
let m = p.start();
p.bump();
while !p.at(R_PAREN) && !p.at(EOF) {
expr(p);
if !p.at(R_PAREN) && !p.expect(COMMA) {
break;
}
}
p.eat(R_PAREN);
m.complete(p, ARG_LIST);
}
// test path_expr
// fn foo() {
// let _ = a;
// let _ = a::b;
// let _ = ::a::<b>;
// let _ = format!();
// }
fn path_expr(p: &mut Parser, r: Restrictions) -> CompletedMarker {
assert!(paths::is_path_start(p));
let m = p.start();
paths::expr_path(p);
match p.current() {
L_CURLY if !r.forbid_structs => {
struct_lit(p);
m.complete(p, STRUCT_LIT)
}
EXCL => {
items::macro_call_after_excl(p);
m.complete(p, MACRO_CALL)
}
_ => m.complete(p, PATH_EXPR)
}
}
// test struct_lit
// fn foo() {
// S {};
// S { x, y: 32, };
// S { x, y: 32, ..Default::default() };
// }
fn struct_lit(p: &mut Parser) {
assert!(p.at(L_CURLY));
p.bump();
while !p.at(EOF) && !p.at(R_CURLY) {
match p.current() {
IDENT => {
let m = p.start();
name_ref(p);
if p.eat(COLON) {
expr(p);
}
m.complete(p, STRUCT_LIT_FIELD);
}
DOTDOT => {
p.bump();
expr(p);
}
_ => p.err_and_bump("expected identifier"),
}
if !p.at(R_CURLY) {
p.expect(COMMA);
}
}
p.expect(R_CURLY);
}

View File

@@ -0,0 +1,21 @@
use super::*;
pub(super) fn static_item(p: &mut Parser) {
const_or_static(p, STATIC_KW)
}
pub(super) fn const_item(p: &mut Parser) {
const_or_static(p, CONST_KW)
}
fn const_or_static(p: &mut Parser, kw: SyntaxKind) {
assert!(p.at(kw));
p.bump();
p.eat(MUT_KW); // TODO: validator to forbid const mut
name(p);
types::ascription(p);
if p.eat(EQ) {
expressions::expr(p);
}
p.expect(SEMI);
}

View File

@@ -0,0 +1,332 @@
use super::*;
mod consts;
mod structs;
mod traits;
mod use_item;
// test mod_contents
// fn foo() {}
// macro_rules! foo {}
// foo::bar!();
// super::baz! {}
// struct S;
pub(super) fn mod_contents(p: &mut Parser, stop_on_r_curly: bool) {
attributes::inner_attributes(p);
while !p.at(EOF) && !(stop_on_r_curly && p.at(R_CURLY)) {
item_or_macro(p, stop_on_r_curly)
}
}
pub(super) fn item_or_macro(p: &mut Parser, stop_on_r_curly: bool) {
let m = p.start();
match maybe_item(p) {
MaybeItem::Item(kind) => {
m.complete(p, kind);
}
MaybeItem::None => {
if paths::is_path_start(p) {
match macro_call(p) {
BlockLike::Block => (),
BlockLike::NotBlock => {
p.expect(SEMI);
}
}
m.complete(p, MACRO_CALL);
} else {
m.abandon(p);
if p.at(L_CURLY) {
error_block(p, "expected an item");
} else if !p.at(EOF) && !(stop_on_r_curly && p.at(R_CURLY)) {
p.err_and_bump("expected an item");
} else {
p.error("expected an item");
}
}
}
MaybeItem::Modifiers => {
p.error("expected fn, trait or impl");
m.complete(p, ERROR);
}
}
}
pub(super) const ITEM_FIRST: TokenSet =
token_set![EXTERN_KW, MOD_KW, USE_KW, STRUCT_KW, ENUM_KW, FN_KW, PUB_KW, POUND];
pub(super) enum MaybeItem {
None,
Item(SyntaxKind),
Modifiers,
}
pub(super) fn maybe_item(p: &mut Parser) -> MaybeItem {
attributes::outer_attributes(p);
visibility(p);
if let Some(kind) = items_without_modifiers(p) {
return MaybeItem::Item(kind);
}
let mut has_mods = false;
// modifiers
has_mods |= p.eat(CONST_KW);
// test unsafe_block_in_mod
// fn foo(){} unsafe { } fn bar(){}
if p.at(UNSAFE_KW) && p.nth(1) != L_CURLY {
p.eat(UNSAFE_KW);
has_mods = true;
}
if p.at(EXTERN_KW) {
has_mods = true;
abi(p);
}
if p.at(IDENT) && p.at_contextual_kw("auto") && p.nth(1) == TRAIT_KW {
p.bump_remap(AUTO_KW);
has_mods = true;
}
if p.at(IDENT) && p.at_contextual_kw("default") && p.nth(1) == IMPL_KW {
p.bump_remap(DEFAULT_KW);
has_mods = true;
}
// items
let kind = match p.current() {
// test extern_fn
// extern fn foo() {}
// test const_fn
// const fn foo() {}
// test const_unsafe_fn
// const unsafe fn foo() {}
// test unsafe_extern_fn
// unsafe extern "C" fn foo() {}
// test unsafe_fn
// unsafe fn foo() {}
FN_KW => {
function(p);
FUNCTION
}
// test unsafe_trait
// unsafe trait T {}
// test auto_trait
// auto trait T {}
// test unsafe_auto_trait
// unsafe auto trait T {}
TRAIT_KW => {
traits::trait_item(p);
TRAIT_ITEM
}
// test unsafe_impl
// unsafe impl Foo {}
// test default_impl
// default impl Foo {}
// test unsafe_default_impl
// unsafe default impl Foo {}
IMPL_KW => {
traits::impl_item(p);
IMPL_ITEM
}
_ => return if has_mods {
MaybeItem::Modifiers
} else {
MaybeItem::None
}
};
MaybeItem::Item(kind)
}
fn items_without_modifiers(p: &mut Parser) -> Option<SyntaxKind> {
let la = p.nth(1);
let kind = match p.current() {
// test extern_crate
// extern crate foo;
EXTERN_KW if la == CRATE_KW => {
extern_crate_item(p);
EXTERN_CRATE_ITEM
}
TYPE_KW => {
type_item(p);
TYPE_ITEM
}
MOD_KW => {
mod_item(p);
MOD_ITEM
}
STRUCT_KW => {
structs::struct_item(p);
if p.at(SEMI) {
p.err_and_bump(
"expected item, found `;`\n\
consider removing this semicolon"
);
}
STRUCT_ITEM
}
ENUM_KW => {
structs::enum_item(p);
ENUM_ITEM
}
USE_KW => {
use_item::use_item(p);
USE_ITEM
}
CONST_KW if (la == IDENT || la == MUT_KW) => {
consts::const_item(p);
CONST_ITEM
}
STATIC_KW => {
consts::static_item(p);
STATIC_ITEM
}
// test extern_block
// extern {}
EXTERN_KW if la == L_CURLY || ((la == STRING || la == RAW_STRING) && p.nth(2) == L_CURLY) => {
abi(p);
extern_block(p);
EXTERN_BLOCK_EXPR
}
_ => return None,
};
Some(kind)
}
fn extern_crate_item(p: &mut Parser) {
assert!(p.at(EXTERN_KW));
p.bump();
assert!(p.at(CRATE_KW));
p.bump();
name(p);
alias(p);
p.expect(SEMI);
}
fn extern_block(p: &mut Parser) {
assert!(p.at(L_CURLY));
p.bump();
p.expect(R_CURLY);
}
fn function(p: &mut Parser) {
assert!(p.at(FN_KW));
p.bump();
name(p);
// test function_type_params
// fn foo<T: Clone + Copy>(){}
type_params::type_param_list(p);
if p.at(L_PAREN) {
params::param_list(p);
} else {
p.error("expected function arguments");
}
// test function_ret_type
// fn foo() {}
// fn bar() -> () {}
fn_ret_type(p);
// test function_where_clause
// fn foo<T>() where T: Copy {}
type_params::where_clause(p);
// test fn_decl
// trait T { fn foo(); }
if !p.eat(SEMI) {
expressions::block(p);
}
}
// test type_item
// type Foo = Bar;
fn type_item(p: &mut Parser) {
assert!(p.at(TYPE_KW));
p.bump();
name(p);
// test type_item_type_params
// type Result<T> = ();
type_params::type_param_list(p);
if p.at(COLON) {
type_params::bounds(p);
}
// test type_item_where_clause
// type Foo where Foo: Copy = ();
type_params::where_clause(p);
if p.eat(EQ) {
types::type_(p);
}
p.expect(SEMI);
}
fn mod_item(p: &mut Parser) {
assert!(p.at(MOD_KW));
p.bump();
name(p);
if !p.eat(SEMI) {
if p.expect(L_CURLY) {
mod_contents(p, true);
p.expect(R_CURLY);
}
}
}
fn macro_call(p: &mut Parser) -> BlockLike {
assert!(paths::is_path_start(p));
paths::use_path(p);
macro_call_after_excl(p)
}
pub(super) fn macro_call_after_excl(p: &mut Parser) -> BlockLike {
p.expect(EXCL);
p.eat(IDENT);
let flavor = match p.current() {
L_CURLY => {
token_tree(p);
BlockLike::Block
}
L_PAREN | L_BRACK => {
token_tree(p);
BlockLike::NotBlock
}
_ => {
p.error("expected `{`, `[`, `(`");
BlockLike::NotBlock
},
};
flavor
}
fn token_tree(p: &mut Parser) {
let closing_paren_kind = match p.current() {
L_CURLY => R_CURLY,
L_PAREN => R_PAREN,
L_BRACK => R_BRACK,
_ => unreachable!(),
};
p.bump();
while !p.at(EOF) && !p.at(closing_paren_kind) {
match p.current() {
L_CURLY | L_PAREN | L_BRACK => token_tree(p),
R_CURLY | R_PAREN | R_BRACK => p.err_and_bump("unmatched brace"),
_ => p.bump()
}
};
p.expect(closing_paren_kind);
}

View File

@@ -0,0 +1,116 @@
use super::*;
pub(super) fn struct_item(p: &mut Parser) {
assert!(p.at(STRUCT_KW));
p.bump();
name(p);
type_params::type_param_list(p);
match p.current() {
WHERE_KW => {
type_params::where_clause(p);
match p.current() {
SEMI => {
p.bump();
return;
}
L_CURLY => named_fields(p),
_ => {
//TODO: special case `(` error message
p.error("expected `;` or `{`");
return;
}
}
}
SEMI => {
p.bump();
return;
}
L_CURLY => named_fields(p),
L_PAREN => {
pos_fields(p);
p.expect(SEMI);
}
_ => {
p.error("expected `;`, `{`, or `(`");
return;
}
}
}
pub(super) fn enum_item(p: &mut Parser) {
assert!(p.at(ENUM_KW));
p.bump();
name(p);
type_params::type_param_list(p);
type_params::where_clause(p);
if p.expect(L_CURLY) {
while !p.at(EOF) && !p.at(R_CURLY) {
let var = p.start();
attributes::outer_attributes(p);
if p.at(IDENT) {
name(p);
match p.current() {
L_CURLY => named_fields(p),
L_PAREN => pos_fields(p),
EQ => {
p.bump();
expressions::expr(p);
}
_ => (),
}
var.complete(p, ENUM_VARIANT);
} else {
var.abandon(p);
p.err_and_bump("expected enum variant");
}
if !p.at(R_CURLY) {
p.expect(COMMA);
}
}
p.expect(R_CURLY);
}
}
fn named_fields(p: &mut Parser) {
assert!(p.at(L_CURLY));
p.bump();
while !p.at(R_CURLY) && !p.at(EOF) {
named_field(p);
if !p.at(R_CURLY) {
p.expect(COMMA);
}
}
p.expect(R_CURLY);
fn named_field(p: &mut Parser) {
let field = p.start();
visibility(p);
if p.at(IDENT) {
name(p);
p.expect(COLON);
types::type_(p);
field.complete(p, NAMED_FIELD);
} else {
field.abandon(p);
p.err_and_bump("expected field declaration");
}
}
}
fn pos_fields(p: &mut Parser) {
if !p.expect(L_PAREN) {
return;
}
while !p.at(R_PAREN) && !p.at(EOF) {
let pos_field = p.start();
visibility(p);
types::type_(p);
pos_field.complete(p, POS_FIELD);
if !p.at(R_PAREN) {
p.expect(COMMA);
}
}
p.expect(R_PAREN);
}

View File

@@ -0,0 +1,87 @@
use super::*;
// test trait_item
// trait T<U>: Hash + Clone where U: Copy {}
pub(super) fn trait_item(p: &mut Parser) {
assert!(p.at(TRAIT_KW));
p.bump();
name(p);
type_params::type_param_list(p);
if p.at(COLON) {
type_params::bounds(p);
}
type_params::where_clause(p);
p.expect(L_CURLY);
// test trait_item_items
// impl F {
// type A: Clone;
// const B: i32;
// fn foo() {}
// fn bar(&self);
// }
while !p.at(EOF) && !p.at(R_CURLY) {
item_or_macro(p, true);
}
p.expect(R_CURLY);
}
// test impl_item
// impl Foo {}
pub(super) fn impl_item(p: &mut Parser) {
assert!(p.at(IMPL_KW));
p.bump();
if choose_type_params_over_qpath(p) {
type_params::type_param_list(p);
}
// TODO: never type
// impl ! {}
// test impl_item_neg
// impl !Send for X {}
p.eat(EXCL);
types::type_(p);
if p.eat(FOR_KW) {
types::type_(p);
}
type_params::where_clause(p);
p.expect(L_CURLY);
// test impl_item_items
// impl F {
// type A = i32;
// const B: i32 = 92;
// fn foo() {}
// fn bar(&self) {}
// }
while !p.at(EOF) && !p.at(R_CURLY) {
item_or_macro(p, true);
}
p.expect(R_CURLY);
}
fn choose_type_params_over_qpath(p: &Parser) -> bool {
// There's an ambiguity between generic parameters and qualified paths in impls.
// If we see `<` it may start both, so we have to inspect some following tokens.
// The following combinations can only start generics,
// but not qualified paths (with one exception):
// `<` `>` - empty generic parameters
// `<` `#` - generic parameters with attributes
// `<` (LIFETIME|IDENT) `>` - single generic parameter
// `<` (LIFETIME|IDENT) `,` - first generic parameter in a list
// `<` (LIFETIME|IDENT) `:` - generic parameter with bounds
// `<` (LIFETIME|IDENT) `=` - generic parameter with a default
// The only truly ambiguous case is
// `<` IDENT `>` `::` IDENT ...
// we disambiguate it in favor of generics (`impl<T> ::absolute::Path<T> { ... }`)
// because this is what almost always expected in practice, qualified paths in impls
// (`impl <Type>::AssocTy { ... }`) aren't even allowed by type checker at the moment.
if !p.at(L_ANGLE) {
return false;
}
if p.nth(1) == POUND || p.nth(1) == R_ANGLE {
return true;
}
(p.nth(1) == LIFETIME || p.nth(1) == IDENT)
&& (p.nth(2) == R_ANGLE || p.nth(2) == COMMA || p.nth(2) == COLON || p.nth(2) == EQ)
}

View File

@@ -0,0 +1,66 @@
use super::*;
pub(super) fn use_item(p: &mut Parser) {
assert!(p.at(USE_KW));
p.bump();
use_tree(p);
p.expect(SEMI);
}
fn use_tree(p: &mut Parser) {
let la = p.nth(1);
let m = p.start();
match (p.current(), la) {
(STAR, _) => p.bump(),
(COLONCOLON, STAR) => {
p.bump();
p.bump();
}
(L_CURLY, _) | (COLONCOLON, L_CURLY) => {
if p.at(COLONCOLON) {
p.bump();
}
nested_trees(p);
}
_ if paths::is_path_start(p) => {
paths::use_path(p);
match p.current() {
AS_KW => {
alias(p);
}
COLONCOLON => {
p.bump();
match p.current() {
STAR => {
p.bump();
}
L_CURLY => nested_trees(p),
_ => {
// is this unreachable?
p.error("expected `{` or `*`");
}
}
}
_ => (),
}
}
_ => {
m.abandon(p);
p.err_and_bump("expected one of `*`, `::`, `{`, `self`, `super`, `indent`");
return;
}
}
m.complete(p, USE_TREE);
}
fn nested_trees(p: &mut Parser) {
assert!(p.at(L_CURLY));
p.bump();
while !p.at(EOF) && !p.at(R_CURLY) {
use_tree(p);
if !p.at(R_CURLY) {
p.expect(COMMA);
}
}
p.expect(R_CURLY);
}

View File

@@ -0,0 +1,161 @@
//! This is the actual "grammar" of the Rust language.
//!
//! Each function in this module and its children corresponds
//! to a production of the format grammar. Submodules roughly
//! correspond to different *areas* of the grammar. By convention,
//! each submodule starts with `use super::*` import and exports
//! "public" productions via `pub(super)`.
//!
//! See docs for `Parser` to learn about API, available to the grammar,
//! and see docs for `Event` to learn how this actually manages to
//! produce parse trees.
//!
//! Code in this module also contains inline tests, which start with
//! `// test name-of-the-test` comment and look like this:
//!
//! ```
//! // test function_with_zero_parameters
//! // fn foo() {}
//! ```
//!
//! After adding a new inline-test, run `cargo collect-tests` to extract
//! it as a standalone text-fixture into `tests/data/parser/inline`, and
//! run `cargo test` once to create the "gold" value.
mod attributes;
mod expressions;
mod items;
mod params;
mod paths;
mod patterns;
mod type_args;
mod type_params;
mod types;
use {
parser_api::{CompletedMarker, Parser, TokenSet},
SyntaxKind::{self, *},
};
pub(crate) fn file(p: &mut Parser) {
let file = p.start();
p.eat(SHEBANG);
items::mod_contents(p, false);
file.complete(p, FILE);
}
#[derive(Clone, Copy, PartialEq, Eq)]
enum BlockLike {
Block,
NotBlock,
}
impl BlockLike {
fn is_block(self) -> bool { self == BlockLike::Block }
}
fn visibility(p: &mut Parser) {
match p.current() {
PUB_KW => {
let m = p.start();
p.bump();
if p.at(L_PAREN) {
match p.nth(1) {
// test crate_visibility
// pub(crate) struct S;
// pub(self) struct S;
// pub(self) struct S;
// pub(self) struct S;
CRATE_KW | SELF_KW | SUPER_KW => {
p.bump();
p.bump();
p.expect(R_PAREN);
}
IN_KW => {
p.bump();
p.bump();
paths::use_path(p);
p.expect(R_PAREN);
}
_ => (),
}
}
m.complete(p, VISIBILITY);
}
// test crate_keyword_vis
// crate fn main() { }
CRATE_KW => {
let m = p.start();
p.bump();
m.complete(p, VISIBILITY);
}
_ => (),
}
}
fn alias(p: &mut Parser) -> bool {
if p.at(AS_KW) {
let alias = p.start();
p.bump();
name(p);
alias.complete(p, ALIAS);
}
true //FIXME: return false if three are errors
}
fn abi(p: &mut Parser) {
assert!(p.at(EXTERN_KW));
let abi = p.start();
p.bump();
match p.current() {
STRING | RAW_STRING => p.bump(),
_ => (),
}
abi.complete(p, ABI);
}
fn fn_ret_type(p: &mut Parser) -> bool {
if p.at(THIN_ARROW) {
p.bump();
types::type_(p);
true
} else {
false
}
}
fn name(p: &mut Parser) {
if p.at(IDENT) {
let m = p.start();
p.bump();
m.complete(p, NAME);
} else {
p.error("expected a name");
}
}
fn name_ref(p: &mut Parser) {
if p.at(IDENT) {
let m = p.start();
p.bump();
m.complete(p, NAME_REF);
} else {
p.error("expected identifier");
}
}
fn error_block(p: &mut Parser, message: &str) {
assert!(p.at(L_CURLY));
let err = p.start();
p.error(message);
p.bump();
let mut level: u32 = 1;
while level > 0 && !p.at(EOF) {
match p.current() {
L_CURLY => level += 1,
R_CURLY => level -= 1,
_ => (),
}
p.bump();
}
err.complete(p, ERROR);
}

View File

@@ -0,0 +1,116 @@
use super::*;
// test param_list
// fn a() {}
// fn b(x: i32) {}
// fn c(x: i32, ) {}
// fn d(x: i32, y: ()) {}
pub(super) fn param_list(p: &mut Parser) {
list_(p, Flavor::Normal)
}
// test param_list_opt_patterns
// fn foo<F: FnMut(&mut Foo<'a>)>(){}
pub(super) fn param_list_opt_patterns(p: &mut Parser) {
list_(p, Flavor::OptionalPattern)
}
pub(super) fn param_list_opt_types(p: &mut Parser) {
list_(p, Flavor::OptionalType)
}
#[derive(Clone, Copy, Eq, PartialEq)]
enum Flavor {
OptionalType,
OptionalPattern,
Normal,
}
impl Flavor {
fn type_required(self) -> bool {
match self {
Flavor::OptionalType => false,
_ => true,
}
}
}
fn list_(p: &mut Parser, flavor: Flavor) {
let (bra, ket) = if flavor.type_required() {
(L_PAREN, R_PAREN)
} else {
(PIPE, PIPE)
};
assert!(p.at(bra));
let m = p.start();
p.bump();
if flavor.type_required() {
self_param(p);
}
while !p.at(EOF) && !p.at(ket) {
value_parameter(p, flavor);
if !p.at(ket) {
p.expect(COMMA);
}
}
p.expect(ket);
m.complete(p, PARAM_LIST);
}
fn value_parameter(p: &mut Parser, flavor: Flavor) {
let m = p.start();
match flavor {
Flavor::OptionalType | Flavor::Normal => {
patterns::pattern(p);
if p.at(COLON) || flavor.type_required() {
types::ascription(p)
}
},
// test value_parameters_no_patterns
// type F = Box<Fn(a: i32, &b: &i32, &mut c: &i32, ())>;
Flavor::OptionalPattern => {
let la0 = p.current();
let la1 = p.nth(1);
let la2 = p.nth(2);
let la3 = p.nth(3);
if la0 == IDENT && la1 == COLON
|| la0 == AMP && la1 == IDENT && la2 == COLON
|| la0 == AMP && la1 == MUT_KW && la2 == IDENT && la3 == COLON {
patterns::pattern(p);
types::ascription(p);
} else {
types::type_(p);
}
},
}
m.complete(p, PARAM);
}
// test self_param
// impl S {
// fn a(self) {}
// fn b(&self,) {}
// fn c(&'a self,) {}
// fn d(&'a mut self, x: i32) {}
// }
fn self_param(p: &mut Parser) {
let la1 = p.nth(1);
let la2 = p.nth(2);
let la3 = p.nth(3);
let n_toks = match (p.current(), la1, la2, la3) {
(SELF_KW, _, _, _) => 1,
(AMP, SELF_KW, _, _) => 2,
(AMP, MUT_KW, SELF_KW, _) => 3,
(AMP, LIFETIME, SELF_KW, _) => 3,
(AMP, LIFETIME, MUT_KW, SELF_KW) => 4,
_ => return,
};
let m = p.start();
for _ in 0..n_toks {
p.bump();
}
m.complete(p, SELF_PARAM);
if !p.at(R_PAREN) {
p.expect(COMMA);
}
}

View File

@@ -0,0 +1,86 @@
use super::*;
pub(super) fn is_path_start(p: &Parser) -> bool {
match p.current() {
IDENT | SELF_KW | SUPER_KW | COLONCOLON => true,
_ => false,
}
}
pub(super) fn use_path(p: &mut Parser) {
path(p, Mode::Use)
}
pub(super) fn type_path(p: &mut Parser) {
path(p, Mode::Type)
}
pub(super) fn expr_path(p: &mut Parser) {
path(p, Mode::Expr)
}
#[derive(Clone, Copy, Eq, PartialEq)]
enum Mode {
Use,
Type,
Expr,
}
fn path(p: &mut Parser, mode: Mode) {
if !is_path_start(p) {
return;
}
let path = p.start();
path_segment(p, mode, true);
let mut qual = path.complete(p, PATH);
loop {
let use_tree = match p.nth(1) {
STAR | L_CURLY => true,
_ => false,
};
if p.at(COLONCOLON) && !use_tree {
let path = qual.precede(p);
p.bump();
path_segment(p, mode, false);
let path = path.complete(p, PATH);
qual = path;
} else {
break;
}
}
}
fn path_segment(p: &mut Parser, mode: Mode, first: bool) {
let segment = p.start();
if first {
p.eat(COLONCOLON);
}
match p.current() {
IDENT => {
name_ref(p);
path_generic_args(p, mode);
}
SELF_KW | SUPER_KW => p.bump(),
_ => {
p.error("expected identifier");
}
};
segment.complete(p, PATH_SEGMENT);
}
fn path_generic_args(p: &mut Parser, mode: Mode) {
match mode {
Mode::Use => return,
Mode::Type => {
// test path_fn_trait_args
// type F = Box<Fn(x: i32) -> ()>;
if p.at(L_PAREN) {
params::param_list_opt_patterns(p);
fn_ret_type(p);
} else {
type_args::type_arg_list(p, false)
}
},
Mode::Expr => type_args::type_arg_list(p, true),
}
}

View File

@@ -0,0 +1,204 @@
use super::*;
pub(super) fn pattern(p: &mut Parser) {
if let Some(lhs) = atom_pat(p) {
// test range_pat
// fn main() {
// match 92 { 0 ... 100 => () }
// }
if p.at(DOTDOTDOT) {
let m = lhs.precede(p);
p.bump();
atom_pat(p);
m.complete(p, RANGE_PAT);
}
}
}
fn atom_pat(p: &mut Parser) -> Option<CompletedMarker> {
let la0 = p.nth(0);
let la1 = p.nth(1);
if la0 == REF_KW || la0 == MUT_KW
|| (la0 == IDENT && !(la1 == COLONCOLON || la1 == L_PAREN || la1 == L_CURLY)) {
return Some(bind_pat(p, true));
}
if paths::is_path_start(p) {
return Some(path_pat(p));
}
// test literal_pattern
// fn main() {
// match () {
// 92 => (),
// 'c' => (),
// "hello" => (),
// }
// }
match expressions::literal(p) {
Some(m) => return Some(m),
None => (),
}
let m = match la0 {
UNDERSCORE => placeholder_pat(p),
AMP => ref_pat(p),
L_PAREN => tuple_pat(p),
L_BRACK => slice_pat(p),
_ => {
p.err_and_bump("expected pattern");
return None;
}
};
Some(m)
}
// test path_part
// fn foo() {
// let foo::Bar = ();
// let ::Bar = ();
// let Bar { .. } = ();
// let Bar(..) = ();
// }
fn path_pat(p: &mut Parser) -> CompletedMarker {
let m = p.start();
paths::expr_path(p);
let kind = match p.current() {
L_PAREN => {
tuple_pat_fields(p);
TUPLE_STRUCT_PAT
}
L_CURLY => {
struct_pat_fields(p);
STRUCT_PAT
}
_ => PATH_PAT
};
m.complete(p, kind)
}
// test tuple_pat_fields
// fn foo() {
// let S() = ();
// let S(_) = ();
// let S(_,) = ();
// let S(_, .. , x) = ();
// }
fn tuple_pat_fields(p: &mut Parser) {
assert!(p.at(L_PAREN));
p.bump();
while !p.at(EOF) && !p.at(R_PAREN) {
match p.current() {
DOTDOT => p.bump(),
_ => pattern(p),
}
if !p.at(R_PAREN) {
p.expect(COMMA);
}
}
p.expect(R_PAREN);
}
// test struct_pat_fields
// fn foo() {
// let S {} = ();
// let S { f, ref mut g } = ();
// let S { h: _, ..} = ();
// let S { h: _, } = ();
// }
fn struct_pat_fields(p: &mut Parser) {
assert!(p.at(L_CURLY));
p.bump();
while !p.at(EOF) && !p.at(R_CURLY) {
match p.current() {
DOTDOT => p.bump(),
IDENT if p.nth(1) == COLON => {
p.bump();
p.bump();
pattern(p);
}
_ => {
bind_pat(p, false);
}
}
if !p.at(R_CURLY) {
p.expect(COMMA);
}
}
p.expect(R_CURLY);
}
// test placeholder_pat
// fn main() { let _ = (); }
fn placeholder_pat(p: &mut Parser) -> CompletedMarker {
assert!(p.at(UNDERSCORE));
let m = p.start();
p.bump();
m.complete(p, PLACEHOLDER_PAT)
}
// test ref_pat
// fn main() {
// let &a = ();
// let &mut b = ();
// }
fn ref_pat(p: &mut Parser) -> CompletedMarker {
assert!(p.at(AMP));
let m = p.start();
p.bump();
p.eat(MUT_KW);
pattern(p);
m.complete(p, REF_PAT)
}
// test tuple_pat
// fn main() {
// let (a, b, ..) = ();
// }
fn tuple_pat(p: &mut Parser) -> CompletedMarker {
assert!(p.at(L_PAREN));
let m = p.start();
tuple_pat_fields(p);
m.complete(p, TUPLE_PAT)
}
// test slice_pat
// fn main() {
// let [a, b, ..] = [];
// }
fn slice_pat(p: &mut Parser) -> CompletedMarker {
assert!(p.at(L_BRACK));
let m = p.start();
p.bump();
while !p.at(EOF) && !p.at(R_BRACK) {
match p.current() {
DOTDOT => p.bump(),
_ => pattern(p),
}
if !p.at(R_BRACK) {
p.expect(COMMA);
}
}
p.expect(R_BRACK);
m.complete(p, SLICE_PAT)
}
// test bind_pat
// fn main() {
// let a = ();
// let mut b = ();
// let ref c = ();
// let ref mut d = ();
// let e @ _ = ();
// let ref mut f @ g @ _ = ();
// }
fn bind_pat(p: &mut Parser, with_at: bool) -> CompletedMarker {
let m = p.start();
p.eat(REF_KW);
p.eat(MUT_KW);
name(p);
if with_at && p.eat(AT) {
pattern(p);
}
m.complete(p, BIND_PAT)
}

View File

@@ -0,0 +1,48 @@
use super::*;
pub(super) fn type_arg_list(p: &mut Parser, colon_colon_required: bool) {
let m;
match (colon_colon_required, p.nth(0), p.nth(1)) {
(_, COLONCOLON, L_ANGLE) => {
m = p.start();
p.bump();
p.bump();
}
(false, L_ANGLE, _) => {
m = p.start();
p.bump();
}
_ => return,
};
while !p.at(EOF) && !p.at(R_ANGLE) {
type_arg(p);
if !p.at(R_ANGLE) && !p.expect(COMMA) {
break;
}
}
p.expect(R_ANGLE);
m.complete(p, TYPE_ARG_LIST);
}
// test type_arg
// type A = B<'static, i32, Item=u64>
fn type_arg(p: &mut Parser) {
let m = p.start();
match p.current() {
LIFETIME => {
p.bump();
m.complete(p, LIFETIME_ARG);
}
IDENT if p.nth(1) == EQ => {
name_ref(p);
p.bump();
types::type_(p);
m.complete(p, ASSOC_TYPE_ARG);
}
_ => {
types::type_(p);
m.complete(p, TYPE_ARG);
}
}
}

View File

@@ -0,0 +1,127 @@
use super::*;
pub(super) fn type_param_list(p: &mut Parser) {
if !p.at(L_ANGLE) {
return;
}
let m = p.start();
p.bump();
while !p.at(EOF) && !p.at(R_ANGLE) {
match p.current() {
LIFETIME => lifetime_param(p),
IDENT => type_param(p),
_ => p.err_and_bump("expected type parameter"),
}
if !p.at(R_ANGLE) && !p.expect(COMMA) {
break;
}
}
p.expect(R_ANGLE);
m.complete(p, TYPE_PARAM_LIST);
fn lifetime_param(p: &mut Parser) {
assert!(p.at(LIFETIME));
let m = p.start();
p.bump();
if p.at(COLON) {
lifetime_bounds(p);
}
m.complete(p, LIFETIME_PARAM);
}
fn type_param(p: &mut Parser) {
assert!(p.at(IDENT));
let m = p.start();
name(p);
if p.at(COLON) {
bounds(p);
}
// test type_param_default
// struct S<T = i32>;
if p.at(EQ) {
p.bump();
types::type_(p)
}
m.complete(p, TYPE_PARAM);
}
}
// test type_param_bounds
// struct S<T: 'a + ?Sized + (Copy)>;
pub(super) fn bounds(p: &mut Parser) {
assert!(p.at(COLON));
p.bump();
bounds_without_colon(p);
}
fn lifetime_bounds(p: &mut Parser) {
assert!(p.at(COLON));
p.bump();
while p.at(LIFETIME) {
p.bump();
if !p.eat(PLUS) {
break;
}
}
}
pub(super) fn bounds_without_colon(p: &mut Parser) {
loop {
let has_paren = p.eat(L_PAREN);
p.eat(QUESTION);
if p.at(FOR_KW) {
//TODO
}
if p.at(LIFETIME) {
p.bump();
} else if paths::is_path_start(p) {
paths::type_path(p);
} else {
break;
}
if has_paren {
p.expect(R_PAREN);
}
if !p.eat(PLUS) {
break;
}
}
}
// test where_clause
// fn foo()
// where
// 'a: 'b + 'c,
// T: Clone + Copy + 'static,
// Iterator::Item: 'a,
// {}
pub(super) fn where_clause(p: &mut Parser) {
if !p.at(WHERE_KW) {
return;
}
let m = p.start();
p.bump();
loop {
if !(paths::is_path_start(p) || p.current() == LIFETIME) {
break
}
where_predicate(p);
if p.current() != L_CURLY && p.current() != SEMI {
p.expect(COMMA);
}
}
m.complete(p, WHERE_CLAUSE);
}
fn where_predicate(p: &mut Parser) {
let m = p.start();
if p.at(LIFETIME) {
p.eat(LIFETIME);
lifetime_bounds(p)
} else {
types::path_type(p);
bounds(p);
}
m.complete(p, WHERE_PRED);
}

View File

@@ -0,0 +1,212 @@
use super::*;
pub(super) fn type_(p: &mut Parser) {
match p.current() {
L_PAREN => paren_or_tuple_type(p),
EXCL => never_type(p),
STAR => pointer_type(p),
L_BRACK => array_or_slice_type(p),
AMP => reference_type(p),
UNDERSCORE => placeholder_type(p),
FN_KW | UNSAFE_KW | EXTERN_KW => fn_pointer_type(p),
FOR_KW => for_type(p),
IMPL_KW => impl_trait_type(p),
_ if paths::is_path_start(p) => path_type(p),
_ => {
p.error("expected type");
}
}
}
pub(super) fn ascription(p: &mut Parser) {
p.expect(COLON);
type_(p)
}
fn type_no_plus(p: &mut Parser) {
type_(p);
}
fn paren_or_tuple_type(p: &mut Parser) {
assert!(p.at(L_PAREN));
let m = p.start();
p.bump();
let mut n_types: u32 = 0;
let mut trailing_comma: bool = false;
while !p.at(EOF) && !p.at(R_PAREN) {
n_types += 1;
type_(p);
if p.eat(COMMA) {
trailing_comma = true;
} else {
trailing_comma = false;
break;
}
}
p.expect(R_PAREN);
let kind = if n_types == 1 && !trailing_comma {
// test paren_type
// type T = (i32);
PAREN_TYPE
} else {
// test unit_type
// type T = ();
// test singleton_tuple_type
// type T = (i32,);
TUPLE_TYPE
};
m.complete(p, kind);
}
// test never_type
// type Never = !;
fn never_type(p: &mut Parser) {
assert!(p.at(EXCL));
let m = p.start();
p.bump();
m.complete(p, NEVER_TYPE);
}
fn pointer_type(p: &mut Parser) {
assert!(p.at(STAR));
let m = p.start();
p.bump();
match p.current() {
// test pointer_type_mut
// type M = *mut ();
// type C = *mut ();
MUT_KW | CONST_KW => p.bump(),
_ => {
// test pointer_type_no_mutability
// type T = *();
p.error(
"expected mut or const in raw pointer type \
(use `*mut T` or `*const T` as appropriate)",
);
}
};
type_no_plus(p);
m.complete(p, POINTER_TYPE);
}
fn array_or_slice_type(p: &mut Parser) {
assert!(p.at(L_BRACK));
let m = p.start();
p.bump();
type_(p);
let kind = match p.current() {
// test slice_type
// type T = [()];
R_BRACK => {
p.bump();
SLICE_TYPE
}
// test array_type
// type T = [(); 92];
SEMI => {
p.bump();
expressions::expr(p);
p.expect(R_BRACK);
ARRAY_TYPE
}
// test array_type_missing_semi
// type T = [() 92];
_ => {
p.error("expected `;` or `]`");
SLICE_TYPE
}
};
m.complete(p, kind);
}
// test reference_type;
// type A = &();
// type B = &'static ();
// type C = &mut ();
fn reference_type(p: &mut Parser) {
assert!(p.at(AMP));
let m = p.start();
p.bump();
p.eat(LIFETIME);
p.eat(MUT_KW);
type_no_plus(p);
m.complete(p, REFERENCE_TYPE);
}
// test placeholder_type
// type Placeholder = _;
fn placeholder_type(p: &mut Parser) {
assert!(p.at(UNDERSCORE));
let m = p.start();
p.bump();
m.complete(p, PLACEHOLDER_TYPE);
}
// test fn_pointer_type
// type A = fn();
// type B = unsafe fn();
// type C = unsafe extern "C" fn();
fn fn_pointer_type(p: &mut Parser) {
let m = p.start();
p.eat(UNSAFE_KW);
if p.at(EXTERN_KW) {
abi(p);
}
// test fn_pointer_type_missing_fn
// type F = unsafe ();
if !p.eat(FN_KW) {
m.abandon(p);
p.error("expected `fn`");
return;
}
params::param_list_opt_patterns(p);
// test fn_pointer_type_with_ret
// type F = fn() -> ();
fn_ret_type(p);
m.complete(p, FN_POINTER_TYPE);
}
// test for_type
// type A = for<'a> fn() -> ();
fn for_type(p: &mut Parser) {
assert!(p.at(FOR_KW));
let m = p.start();
p.bump();
type_params::type_param_list(p);
type_(p);
m.complete(p, FOR_TYPE);
}
// test impl_trait_type
// type A = impl Iterator<Item=Foo<'a>> + 'a;
fn impl_trait_type(p: &mut Parser) {
assert!(p.at(IMPL_KW));
let m = p.start();
p.bump();
type_params::bounds_without_colon(p);
m.complete(p, IMPL_TRAIT_TYPE);
}
// test path_type
// type A = Foo;
// type B = ::Foo;
// type C = self::Foo;
// type D = super::Foo;
pub(super) fn path_type(p: &mut Parser) {
assert!(paths::is_path_start(p));
let m = p.start();
paths::type_path(p);
// test path_type_with_bounds
// fn foo() -> Box<T + 'f> {}
if p.eat(PLUS) {
type_params::bounds_without_colon(p);
}
m.complete(p, PATH_TYPE);
}