From d334b5a1db9ec6a57f54077d422a3f4b3c8c1178 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Thu, 21 Feb 2019 13:27:45 +0300 Subject: move parser to a separate crate --- crates/ra_parser/src/grammar/type_params.rs | 175 ++++++++++++++++++++++++++++ 1 file changed, 175 insertions(+) create mode 100644 crates/ra_parser/src/grammar/type_params.rs (limited to 'crates/ra_parser/src/grammar/type_params.rs') diff --git a/crates/ra_parser/src/grammar/type_params.rs b/crates/ra_parser/src/grammar/type_params.rs new file mode 100644 index 000000000..40f998682 --- /dev/null +++ b/crates/ra_parser/src/grammar/type_params.rs @@ -0,0 +1,175 @@ +use super::*; + +pub(super) fn opt_type_param_list(p: &mut Parser) { + if !p.at(L_ANGLE) { + return; + } + type_param_list(p); +} + +fn type_param_list(p: &mut Parser) { + assert!(p.at(L_ANGLE)); + let m = p.start(); + p.bump(); + + while !p.at(EOF) && !p.at(R_ANGLE) { + let m = p.start(); + + // test generic_lifetime_type_attribute + // fn foo<#[derive(Lifetime)] 'a, #[derive(Type)] T>(_: &'a T) { + // } + attributes::outer_attributes(p); + + match p.current() { + LIFETIME => lifetime_param(p, m), + IDENT => type_param(p, m), + _ => { + m.abandon(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, m: Marker) { + assert!(p.at(LIFETIME)); + p.bump(); + if p.at(COLON) { + lifetime_bounds(p); + } + m.complete(p, LIFETIME_PARAM); +} + +fn type_param(p: &mut Parser, m: Marker) { + assert!(p.at(IDENT)); + name(p); + if p.at(COLON) { + bounds(p); + } + // test type_param_default + // struct S; + if p.at(EQ) { + p.bump(); + types::type_(p) + } + m.complete(p, TYPE_PARAM); +} + +// test type_param_bounds +// struct S; +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); + match p.current() { + LIFETIME => p.bump(), + FOR_KW => types::for_type(p), + _ if paths::is_path_start(p) => types::path_type(p), + _ => 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, +// ::Item: 'a +// {} +pub(super) fn opt_where_clause(p: &mut Parser) { + if !p.at(WHERE_KW) { + return; + } + let m = p.start(); + p.bump(); + + while is_where_predicate(p) { + where_predicate(p); + + let comma = p.eat(COMMA); + + if is_where_clause_end(p) { + break; + } + + if !comma { + p.error("expected comma"); + } + } + + m.complete(p, WHERE_CLAUSE); +} + +fn is_where_predicate(p: &mut Parser) -> bool { + match p.current() { + LIFETIME => true, + IMPL_KW => false, + token => types::TYPE_FIRST.contains(token), + } +} + +fn is_where_clause_end(p: &mut Parser) -> bool { + p.current() == L_CURLY || p.current() == SEMI || p.current() == EQ +} + +fn where_predicate(p: &mut Parser) { + let m = p.start(); + match p.current() { + LIFETIME => { + p.bump(); + if p.at(COLON) { + lifetime_bounds(p); + } else { + p.error("expected colon"); + } + } + IMPL_KW => { + p.error("expected lifetime or type"); + } + _ => { + // test where_pred_for + // fn test() + // where + // for<'a> F: Fn(&'a str) + // { } + types::type_(p); + + if p.at(COLON) { + bounds(p); + } else { + p.error("expected colon"); + } + } + } + m.complete(p, WHERE_PRED); +} -- cgit v1.2.3