use sql_dialect_fmt_syntax::SyntaxKind;
use sql_dialect_fmt_syntax::SyntaxKind::*;
use crate::parser::{ContextualKeyword, Parser};
use super::{expr, name_ref, stmt};
pub(super) fn at_block_start(p: &Parser) -> bool {
p.at(DECLARE_KW) || (p.at(BEGIN_KW) && !at_begin_transaction(p))
}
pub(super) fn at_begin_transaction(p: &Parser) -> bool {
p.at(BEGIN_KW)
&& (p.nth_at(1, SEMICOLON)
|| p.nth_contextual(1, ContextualKeyword::Transaction)
|| p.nth_contextual(1, ContextualKeyword::Work))
}
pub(super) fn block_stmt(p: &mut Parser) {
let m = p.start();
if p.at(DECLARE_KW) {
declare_section(p);
}
p.expect(BEGIN_KW);
if p.eat(NOT_KW) {
if p.nth_contextual(0, ContextualKeyword::Atomic) {
p.bump_as(CONTEXTUAL_KEYWORD);
} else {
p.error("expected ATOMIC after NOT");
}
} else if p.nth_contextual(0, ContextualKeyword::Atomic) {
p.bump_as(CONTEXTUAL_KEYWORD);
}
stmt_list(p, |p| p.at(END_KW) || p.at(EXCEPTION_KW));
if p.at(EXCEPTION_KW) {
exception_section(p);
}
p.expect(END_KW);
if p.at_name() {
name_ref(p); }
m.complete(p, BLOCK_STMT);
}
fn declare_section(p: &mut Parser) {
let m = p.start();
p.bump(DECLARE_KW);
while !p.at(BEGIN_KW) && !p.at_eof() {
if p.eat(SEMICOLON) {
continue;
}
declare_item(p);
p.eat(SEMICOLON);
}
m.complete(p, DECLARE_SECTION);
}
fn declare_item(p: &mut Parser) {
let m = p.start();
let mut first = true;
while !p.at(SEMICOLON) && !p.at(BEGIN_KW) && !p.at_eof() {
if !first && p.nth_contextual(0, ContextualKeyword::Default) {
p.bump_as(CONTEXTUAL_KEYWORD);
} else {
p.bump_any();
}
first = false;
}
m.complete(p, DECLARE_ITEM);
}
fn stmt_list(p: &mut Parser, is_end: impl Fn(&Parser) -> bool) {
let m = p.start();
while !is_end(p) && !p.at_eof() {
if p.eat(SEMICOLON) {
continue; }
block_statement(p);
p.eat(SEMICOLON);
}
m.complete(p, STMT_LIST);
}
fn block_statement(p: &mut Parser) {
if p.at(IF_KW) {
if_stmt(p);
} else if p.at(FOR_KW) || p.at(WHILE_KW) || p.at(LOOP_KW) || p.at(REPEAT_KW) {
loop_stmt(p);
} else if p.at(DECLARE_KW) {
simple_script_stmt(p, DECLARE_ITEM);
} else if at_block_start(p) {
block_stmt(p); } else if p.at(CASE_KW) {
case_stmt(p);
} else if p.at(LET_KW) {
simple_script_stmt(p, LET_STMT);
} else if p.at(RETURN_KW) {
simple_script_stmt(p, RETURN_STMT);
} else if stmt::at_sql_stmt_start(p) {
stmt::statement(p);
} else if p.at_name() && p.nth_at(1, ASSIGN) {
simple_script_stmt(p, ASSIGN_STMT);
} else {
simple_script_stmt(p, SCRIPT_STMT);
}
}
fn simple_script_stmt(p: &mut Parser, node: SyntaxKind) {
let m = p.start();
let mut first = true;
while !p.at(SEMICOLON) && !p.at_eof() {
let up = if first {
p.nth_contextual(0, ContextualKeyword::Break)
|| p.nth_contextual(0, ContextualKeyword::Continue)
} else {
p.nth_contextual(0, ContextualKeyword::Default) && !p.nth_at(1, ASSIGN)
};
if up {
p.bump_as(CONTEXTUAL_KEYWORD);
} else {
p.bump_any();
}
first = false;
}
m.complete(p, node);
}
fn if_stmt(p: &mut Parser) {
let m = p.start();
p.bump(IF_KW);
expr(p); p.expect(THEN_KW);
stmt_list(p, |p| p.at(ELSEIF_KW) || p.at(ELSE_KW) || p.at(END_KW));
while p.at(ELSEIF_KW) {
p.bump(ELSEIF_KW);
expr(p);
p.expect(THEN_KW);
stmt_list(p, |p| p.at(ELSEIF_KW) || p.at(ELSE_KW) || p.at(END_KW));
}
if p.eat(ELSE_KW) {
stmt_list(p, |p| p.at(END_KW));
}
p.expect(END_KW);
p.expect(IF_KW);
m.complete(p, IF_STMT);
}
fn case_stmt(p: &mut Parser) {
let m = p.start();
p.bump(CASE_KW);
if !p.at(WHEN_KW) {
expr(p); }
while p.at(WHEN_KW) {
let arm = p.start();
p.bump(WHEN_KW);
expr(p);
p.expect(THEN_KW);
stmt_list(p, |p| p.at(WHEN_KW) || p.at(ELSE_KW) || p.at(END_KW));
arm.complete(p, CASE_STMT_WHEN);
}
if p.eat(ELSE_KW) {
stmt_list(p, |p| p.at(END_KW));
}
p.expect(END_KW);
p.eat(CASE_KW); m.complete(p, CASE_STMT);
}
fn loop_stmt(p: &mut Parser) {
let m = p.start();
if p.at(FOR_KW) || p.at(WHILE_KW) {
p.bump_any(); while !p.at(DO_KW) && !p.at(SEMICOLON) && !p.at(END_KW) && !p.at_eof() {
if p.nth_contextual(0, ContextualKeyword::Reverse)
|| p.nth_contextual(0, ContextualKeyword::To)
{
p.bump_as(CONTEXTUAL_KEYWORD);
} else {
p.bump_any(); }
}
p.expect(DO_KW);
stmt_list(p, |p| p.at(END_KW));
} else if p.at(LOOP_KW) {
p.bump(LOOP_KW);
stmt_list(p, |p| p.at(END_KW));
} else {
p.bump(REPEAT_KW);
stmt_list(p, |p| p.at(UNTIL_KW) || p.at(END_KW));
if p.eat(UNTIL_KW) {
expr(p); while !p.at(END_KW) && !p.at(SEMICOLON) && !p.at_eof() {
p.bump_any();
}
}
}
p.expect(END_KW);
if p.at(FOR_KW) || p.at(WHILE_KW) || p.at(LOOP_KW) || p.at(REPEAT_KW) {
p.bump_any();
}
m.complete(p, LOOP_STMT);
}
fn exception_section(p: &mut Parser) {
let m = p.start();
p.bump(EXCEPTION_KW);
while p.at(WHEN_KW) {
exception_when(p);
}
m.complete(p, EXCEPTION_SECTION);
}
fn exception_when(p: &mut Parser) {
let m = p.start();
p.bump(WHEN_KW);
while !p.at(THEN_KW) && !p.at(WHEN_KW) && !p.at(END_KW) && !p.at_eof() {
p.bump_any(); }
p.expect(THEN_KW);
stmt_list(p, |p| p.at(WHEN_KW) || p.at(END_KW));
m.complete(p, EXCEPTION_WHEN);
}