cicada 1.2.2

A simple Bash-like Unix shell.
Documentation
WHITESPACE = _{ " " | "\t" }

KW_IF = _{ "if " }
KW_FI = _{ "fi" ~ (NEWLINE | EOI) }
KW_FOR = _{ "for " }
KW_ELSE = { "else" ~ NEWLINE }
KW_ELSEIF = _{ "else if " }
KW_WHILE = _{ "while " }
KW_DONE = _{ "done" ~ (NEWLINE | EOI) }
KW_LIST = _{ KW_IF | KW_FOR | KW_ELSEIF | KW_ELSE | KW_FI | KW_WHILE | KW_DONE }

DUMMY_DO = _{ ";" ~ "do" ~ NEWLINE }
DUMMY_THEN = _{ ";" ~ "then" ~ NEWLINE }

TEST = {(!(NEWLINE|DUMMY_THEN|DUMMY_DO) ~ ANY)+}

CMD_END = _{ !KW_LIST ~ (!NEWLINE ~ ANY)+ ~ EOI}
CMD_NORMAL = _{ !KW_LIST ~ (!NEWLINE ~ ANY)* ~ NEWLINE}
CMD = { CMD_NORMAL | CMD_END }

IF_HEAD = { KW_IF ~ TEST ~ (DUMMY_THEN|NEWLINE) }
EXP_BODY = { (CMD | EXP_IF | EXP_WHILE | EXP_FOR)+ }
IF_ELSEIF_HEAD = { KW_ELSEIF ~ TEST ~ (DUMMY_THEN|NEWLINE) }
IF_IF_BR = { IF_HEAD ~ EXP_BODY }
IF_ELSEIF_BR = { IF_ELSEIF_HEAD ~ EXP_BODY }
IF_ELSE_BR = { KW_ELSE ~ EXP_BODY }

EXP_IF = {
    (SOI)? ~
    IF_IF_BR ~
    IF_ELSEIF_BR* ~
    IF_ELSE_BR? ~
    KW_FI
}

FOR_VAR = @{ (ASCII_ALPHA | "_") ~ (ASCII_ALPHANUMERIC | "_")* }
FOR_INIT = { FOR_VAR ~ "in" ~ TEST ~ (DUMMY_DO|NEWLINE) }
FOR_HEAD = { KW_FOR ~ FOR_INIT }

EXP_FOR = {
    (SOI)? ~
    FOR_HEAD ~
        EXP_BODY ~
    KW_DONE
}

WHILE_HEAD = { KW_WHILE ~ TEST ~ (DUMMY_DO|NEWLINE) }

EXP_WHILE = {
    (SOI)? ~
    WHILE_HEAD ~
        EXP_BODY ~
    KW_DONE
}

EXP = { (EXP_IF | EXP_FOR | EXP_WHILE | CMD)* }