use crate::SyntaxKind::{
COLON, COMMA, CONST_DECL, EOF, EQ, EXTERN_DECL, FLAGS_DECL, FLAGS_MEMBER, FLAGS_MEMBER_LIST,
FLOW_DECL, FN_DECL, GT, HASH, IDENT, IMPORT_DECL, KW_AS, KW_CONST, KW_EXTERN, KW_FLAGS,
KW_FLOW, KW_FN, KW_MODULE, KW_PUB, KW_REF, KW_STRUCT, KW_VAR, L_BRACE, L_PAREN, MODULE_DECL,
NEWLINE, PARAM, PARAM_LIST, R_BRACE, R_PAREN, STRUCT_DECL, STRUCT_FIELD, TILDE, USE_DECL,
USE_TREE, USE_TREE_LIST, VAR_DECL,
};
use super::Parser;
pub(crate) fn at_flow_decl(p: &Parser<'_, '_>) -> bool {
at_flow_decl_at(p, 0)
}
fn at_flow_decl_at(p: &Parser<'_, '_>, base: usize) -> bool {
p.nth(base) == KW_FLOW
&& p.nth(base + 1) == IDENT
&& match p.nth(base + 2) {
L_PAREN | L_BRACE => true,
HASH => header_tags_precede_a_body(p, base + 2),
_ => false,
}
}
fn header_tags_precede_a_body(p: &Parser<'_, '_>, n: usize) -> bool {
let mut i = n;
let mut last = None;
loop {
match p.nth(i) {
NEWLINE | EOF => return last == Some(L_BRACE),
kind => {
last = Some(kind);
i += 1;
}
}
}
}
pub(crate) fn at_fn_decl(p: &Parser<'_, '_>) -> bool {
at_fn_decl_at(p, 0)
}
fn at_fn_decl_at(p: &Parser<'_, '_>, base: usize) -> bool {
p.nth(base) == KW_FN && p.nth(base + 1) == IDENT && matches!(p.nth(base + 2), L_PAREN | L_BRACE)
}
pub(crate) fn at_binding_decl(p: &Parser<'_, '_>) -> bool {
at_binding_decl_at(p, 0)
}
fn at_binding_decl_at(p: &Parser<'_, '_>, base: usize) -> bool {
p.nth(base + 1) == IDENT && matches!(p.nth(base + 2), EQ | COLON)
}
pub(crate) fn at_flags_decl(p: &Parser<'_, '_>) -> bool {
at_flags_decl_at(p, 0)
}
fn at_flags_decl_at(p: &Parser<'_, '_>, base: usize) -> bool {
p.nth(base) == KW_FLAGS && p.nth(base + 1) == IDENT && p.nth(base + 2) == EQ
}
pub(crate) fn at_struct_decl(p: &Parser<'_, '_>) -> bool {
at_struct_decl_at(p, 0)
}
fn at_struct_decl_at(p: &Parser<'_, '_>, base: usize) -> bool {
p.nth(base) == KW_STRUCT && p.nth(base + 1) == IDENT && p.nth(base + 2) == L_BRACE
}
pub(crate) fn at_extern_decl(p: &Parser<'_, '_>) -> bool {
at_extern_decl_at(p, 0)
}
fn at_extern_decl_at(p: &Parser<'_, '_>, base: usize) -> bool {
p.nth(base) == KW_EXTERN && p.nth(base + 1) == IDENT && p.nth(base + 2) == L_PAREN
}
pub(crate) fn at_pub_flow_decl(p: &Parser<'_, '_>) -> bool {
p.at(KW_PUB) && at_flow_decl_at(p, 1)
}
pub(crate) fn at_pub_fn_decl(p: &Parser<'_, '_>) -> bool {
p.at(KW_PUB) && at_fn_decl_at(p, 1)
}
pub(crate) fn at_pub_var_decl(p: &Parser<'_, '_>) -> bool {
p.at(KW_PUB) && p.nth(1) == KW_VAR && at_binding_decl_at(p, 1)
}
pub(crate) fn at_pub_const_decl(p: &Parser<'_, '_>) -> bool {
p.at(KW_PUB) && p.nth(1) == KW_CONST && at_binding_decl_at(p, 1)
}
pub(crate) fn at_pub_flags_decl(p: &Parser<'_, '_>) -> bool {
p.at(KW_PUB) && at_flags_decl_at(p, 1)
}
pub(crate) fn at_pub_struct_decl(p: &Parser<'_, '_>) -> bool {
p.at(KW_PUB) && at_struct_decl_at(p, 1)
}
pub(crate) fn at_pub_extern_decl(p: &Parser<'_, '_>) -> bool {
p.at(KW_PUB) && at_extern_decl_at(p, 1)
}
pub(crate) fn at_import_decl(p: &Parser<'_, '_>) -> bool {
p.nth(1) == IDENT
}
pub(crate) fn at_use_decl(p: &Parser<'_, '_>) -> bool {
p.nth(1) == IDENT
}
pub(crate) fn at_module_decl(p: &Parser<'_, '_>) -> bool {
p.at(KW_MODULE) && p.nth(1) == IDENT && p.nth(2) == L_BRACE
}
fn eat_pub(p: &mut Parser<'_, '_>) {
p.eat(KW_PUB);
p.skip_ws();
}
pub(crate) fn flow_decl(p: &mut Parser<'_, '_>, doc: Option<rowan::Checkpoint>) {
super::doc_comment::open_with_doc(p, FLOW_DECL, doc);
eat_pub(p);
p.bump(); p.expect(IDENT);
if p.at(L_PAREN) {
param_list(p);
}
return_type_clause(p);
header_tags(p);
decl_body(p, false, "expected a braced body after the flow header");
p.finish_node();
}
fn header_tags(p: &mut Parser<'_, '_>) {
if p.at(HASH) {
super::content::header_tag_tail(p);
}
}
pub(crate) fn fn_decl(p: &mut Parser<'_, '_>, doc: Option<rowan::Checkpoint>) {
super::doc_comment::open_with_doc(p, FN_DECL, doc);
eat_pub(p);
p.bump(); p.expect(IDENT);
if p.at(L_PAREN) {
param_list(p);
}
return_type_clause(p);
decl_body(p, true, "expected a braced body after the fn header");
p.finish_node();
}
fn return_type_clause(p: &mut Parser<'_, '_>) {
if super::types::at_type_annotation(p) {
super::types::type_annotation(p);
}
}
fn decl_body(p: &mut Parser<'_, '_>, code_default: bool, missing_body_msg: &str) {
if p.at(TILDE) && p.nth(1) == L_BRACE {
p.eat(TILDE); super::stmt::stmt_block(p);
} else if p.at(GT) && p.nth(1) == L_BRACE {
p.eat(GT); super::block::block(p);
} else if p.at(L_BRACE) {
if code_default {
super::stmt::stmt_block(p);
} else {
super::block::block(p);
}
} else {
p.error(missing_body_msg.into());
}
}
fn param_list(p: &mut Parser<'_, '_>) {
p.start_node(PARAM_LIST);
p.expect(L_PAREN);
p.skip_ws_and_newlines();
while p.peek_skip_nl() != R_PAREN && !p.at_eof() {
let before = p.pos();
param(p);
if p.pos() == before {
p.error_recover("unexpected token in parameter list");
continue;
}
p.skip_ws_and_newlines();
if !p.eat(COMMA) {
break;
}
p.skip_ws_and_newlines();
}
p.expect(R_PAREN);
p.finish_node();
}
fn param(p: &mut Parser<'_, '_>) {
p.start_node(PARAM);
p.eat(KW_REF);
p.expect(IDENT);
if super::types::at_type_annotation(p) {
super::types::type_annotation(p);
}
p.finish_node();
}
pub(crate) fn var_decl(p: &mut Parser<'_, '_>, doc: Option<rowan::Checkpoint>) {
super::doc_comment::open_with_doc(p, VAR_DECL, doc);
eat_pub(p);
p.bump(); p.expect(IDENT);
binding_annotation(p);
if p.eat(crate::SyntaxKind::EQ) {
super::expr::expression(p);
}
p.finish_node();
}
pub(crate) fn const_decl(p: &mut Parser<'_, '_>, doc: Option<rowan::Checkpoint>) {
super::doc_comment::open_with_doc(p, CONST_DECL, doc);
eat_pub(p);
p.bump(); p.expect(IDENT);
binding_annotation(p);
if p.eat(crate::SyntaxKind::EQ) {
super::expr::expression(p);
}
p.finish_node();
}
pub(super) fn binding_annotation(p: &mut Parser<'_, '_>) {
if super::types::at_type_annotation(p) {
super::types::type_annotation(p);
}
}
pub(crate) fn flags_decl(p: &mut Parser<'_, '_>, doc: Option<rowan::Checkpoint>) {
super::doc_comment::open_with_doc(p, FLAGS_DECL, doc);
eat_pub(p);
p.bump(); p.expect(IDENT);
p.expect(crate::SyntaxKind::EQ);
flags_member_list(p);
p.finish_node();
}
fn flags_member_list(p: &mut Parser<'_, '_>) {
p.start_node(FLAGS_MEMBER_LIST);
if p.at(L_PAREN) && p.nth(1) == R_PAREN {
p.eat(L_PAREN);
p.eat(R_PAREN);
p.finish_node();
return;
}
let mut has_member = false;
loop {
let before = p.pos();
flags_member(p);
if p.pos() == before {
if !has_member {
p.skip_ws();
p.error_recover(
"expected a flags member after `=` (use `()` for an explicit empty set)",
);
}
break;
}
has_member = true;
if !p.eat(COMMA) {
break;
}
}
p.finish_node();
}
fn flags_member(p: &mut Parser<'_, '_>) {
if !p.at(IDENT) && !p.at(L_PAREN) {
return;
}
p.start_node(FLAGS_MEMBER);
if p.eat(L_PAREN) {
p.expect(IDENT);
p.expect(R_PAREN);
} else {
p.expect(IDENT);
}
p.finish_node();
}
pub(crate) fn struct_decl(p: &mut Parser<'_, '_>, doc: Option<rowan::Checkpoint>) {
super::doc_comment::open_with_doc(p, STRUCT_DECL, doc);
eat_pub(p);
p.bump(); p.expect(IDENT);
p.expect(L_BRACE);
p.skip_ws_and_newlines();
while p.peek_skip_nl() != R_BRACE && !p.at_eof() {
let before = p.pos();
struct_field(p);
if p.pos() == before {
p.error_recover("unexpected token in struct body");
p.skip_ws_and_newlines();
continue;
}
p.skip_ws_and_newlines();
p.eat(COMMA);
p.skip_ws_and_newlines();
}
p.expect(R_BRACE);
p.finish_node();
}
fn struct_field(p: &mut Parser<'_, '_>) {
if !p.at(IDENT) {
return;
}
p.start_node(STRUCT_FIELD);
p.expect(IDENT);
super::types::type_annotation(p);
p.finish_node();
}
pub(crate) fn extern_decl(p: &mut Parser<'_, '_>, doc: Option<rowan::Checkpoint>) {
super::doc_comment::open_with_doc(p, EXTERN_DECL, doc);
eat_pub(p);
p.bump(); p.expect(IDENT);
if p.at(L_PAREN) {
param_list(p);
}
p.finish_node();
}
pub(crate) fn import_decl(p: &mut Parser<'_, '_>, doc: Option<rowan::Checkpoint>) {
super::doc_comment::open_with_doc(p, IMPORT_DECL, doc);
p.bump(); super::expr::path(p);
p.finish_node();
}
pub(crate) fn use_decl(p: &mut Parser<'_, '_>, doc: Option<rowan::Checkpoint>) {
super::doc_comment::open_with_doc(p, USE_DECL, doc);
p.bump(); use_tree(p);
p.eat(crate::SyntaxKind::SEMICOLON);
p.finish_node();
}
fn use_tree(p: &mut Parser<'_, '_>) {
p.start_node(USE_TREE);
if p.at(IDENT) {
p.expect(IDENT);
loop {
if p.at(crate::SyntaxKind::COLON_COLON) {
if p.nth(1) == L_BRACE {
p.expect(crate::SyntaxKind::COLON_COLON);
use_tree_list(p);
break;
}
p.expect(crate::SyntaxKind::COLON_COLON);
p.expect(IDENT);
} else {
break;
}
}
if p.eat(KW_AS) {
p.expect(IDENT);
}
} else {
p.error("a `use` needs a module path".into());
}
p.finish_node();
}
fn use_tree_list(p: &mut Parser<'_, '_>) {
p.start_node(USE_TREE_LIST);
p.expect(L_BRACE);
p.skip_ws_and_newlines();
while p.peek_skip_nl() != R_BRACE && !p.at_eof() {
let before = p.pos();
use_tree(p);
if p.pos() == before {
p.error_recover("unexpected token in use-tree list");
p.skip_ws_and_newlines();
continue;
}
p.skip_ws_and_newlines();
if !p.eat(COMMA) {
break;
}
p.skip_ws_and_newlines();
}
p.expect(R_BRACE);
p.finish_node();
}
pub(crate) fn module_decl(p: &mut Parser<'_, '_>, doc: Option<rowan::Checkpoint>) {
super::doc_comment::open_with_doc(p, MODULE_DECL, doc);
p.bump(); p.expect(IDENT);
if p.at(L_BRACE) {
super::block::block(p);
} else {
p.error("expected a braced body after the module header".into());
}
p.finish_node();
}