use super::{combinator::*, expression::*, identifier::*, types::*};
use crate::ast::*;
pub fn stmt(input: &str) -> ParseResult<Statement> {
alt((
alias_stmt,
assignment_stmt,
case_stmt,
compound_stmt,
escape_stmt,
if_stmt,
null_stmt,
procedure_call_stmt,
repeat_stmt,
return_stmt,
skip_stmt,
))
.parse(input)
}
pub fn alias_stmt(input: &str) -> ParseResult<Statement> {
tuple((
tag("ALIAS"),
variable_id,
tag("FOR"),
general_ref,
many0(qualifier),
char(';'),
many1(stmt),
tag("END_ALIAS"),
char(';'),
))
.map(
|(_alias, name, _for, dest, qualifiers, _semicolon1, statements, _end, _semicolon2)| {
Statement::Alias {
name,
dest,
qualifiers,
statements,
}
},
)
.parse(input)
}
pub fn assignment_stmt(input: &str) -> ParseResult<Statement> {
tuple((
general_ref,
many0(qualifier),
tag(":="),
expression,
char(';'),
))
.map(
|(name, qualifiers, _def, expr, _semicolon)| Statement::Assignment {
name,
qualifiers,
expr,
},
)
.parse(input)
}
pub fn case_stmt(input: &str) -> ParseResult<Statement> {
tuple((
tag("CASE"),
selector,
tag("OF"),
many0(case_action),
opt(tuple((tag("OTHERWISE"), char(':'), stmt))
.map(|(_otherwise, _colon, stmt)| Box::new(stmt))),
tag("END_CASE"),
char(';'),
))
.map(
|(_case, selector, _of, actions, otherwise, _end, _semicolon)| Statement::Case {
selector,
actions,
otherwise,
},
)
.parse(input)
}
pub fn selector(input: &str) -> ParseResult<Expression> {
expression(input)
}
pub fn case_action(input: &str) -> ParseResult<(Vec<Expression>, Statement)> {
tuple((comma_separated(case_label), char(':'), stmt))
.map(|(labels, _colon, statement)| (labels, statement))
.parse(input)
}
pub fn case_label(input: &str) -> ParseResult<Expression> {
expression(input)
}
pub fn compound_stmt(input: &str) -> ParseResult<Statement> {
tuple((tag("BEGIN"), many1(stmt), tag("END"), char(';')))
.map(|(_begin, statements, _end, _semicolon)| Statement::Compound { statements })
.parse(input)
}
pub fn escape_stmt(input: &str) -> ParseResult<Statement> {
tuple((tag("ESCAPE"), char(';')))
.map(|(_skip, _semicolon)| Statement::Escape)
.parse(input)
}
pub fn if_stmt(input: &str) -> ParseResult<Statement> {
tuple((
tag("IF"),
logical_expression,
tag("THEN"),
many1(stmt),
opt(tuple((tag("ELSE"), many1(stmt))).map(|(_else, stmts)| stmts)),
tag("END_IF"),
char(';'),
))
.map(
|(_if, condition, _then, then_branch, else_branch, _endif, _semicolon)| Statement::If {
condition,
then_branch,
else_branch,
},
)
.parse(input)
}
pub fn null_stmt(input: &str) -> ParseResult<Statement> {
char(';').map(|_c| Statement::Null).parse(input)
}
pub fn procedure_call_stmt(input: &str) -> ParseResult<Statement> {
tuple((
alt((
built_in_procedure,
procedure_ref.map(ProcedureCallName::Reference),
)),
opt(actual_parameter_list),
char(';'),
))
.map(
|(procedure, parameters, _semicolon)| Statement::ProcedureCall {
procedure,
parameters,
},
)
.parse(input)
}
pub fn built_in_procedure(input: &str) -> ParseResult<ProcedureCallName> {
alt((
tag("INSERT").map(|_| ProcedureCallName::Insert),
tag("REMOVE").map(|_| ProcedureCallName::Remove),
))
.parse(input)
}
pub fn repeat_stmt(input: &str) -> ParseResult<Statement> {
tuple((
tag("REPEAT"),
repeat_control,
char(';'),
many1(stmt),
tag("END_REPEAT"),
char(';'),
))
.map(
|(_repeat, control, _semicolon1, statements, _end, _semicolon2)| Statement::Repeat {
control,
statements,
},
)
.parse(input)
}
pub fn repeat_control(input: &str) -> ParseResult<RepeatControl> {
tuple((
opt(increment_control),
opt(while_control),
opt(until_control),
))
.map(|(increment, while_, until)| RepeatControl {
increment,
while_,
until,
})
.parse(input)
}
pub fn increment_control(input: &str) -> ParseResult<RepeatIncrement> {
tuple((
variable_id,
tag(":="),
bound_1,
tag("TO"),
bound_2,
opt(tuple((tag("BY"), increment)).map(|(_by, inc)| inc)),
))
.map(
|(variable, _def, begin, _to, end, increment)| RepeatIncrement {
variable,
begin,
end,
increment,
},
)
.parse(input)
}
pub fn increment(input: &str) -> ParseResult<Expression> {
numeric_expression(input)
}
pub fn while_control(input: &str) -> ParseResult<Expression> {
tuple((tag("WHILE"), logical_expression))
.map(|(_where, expr)| expr)
.parse(input)
}
pub fn until_control(input: &str) -> ParseResult<Expression> {
tuple((tag("UNTIL"), logical_expression))
.map(|(_where, expr)| expr)
.parse(input)
}
pub fn return_stmt(input: &str) -> ParseResult<Statement> {
tuple((
tag("RETURN"),
opt(tuple((char('('), expression, char(')'))).map(|(_open, expr, _close)| expr)),
char(';'),
))
.map(|(_return, value, _semicolon)| Statement::Return { value })
.parse(input)
}
pub fn skip_stmt(input: &str) -> ParseResult<Statement> {
tuple((tag("SKIP"), char(';')))
.map(|(_skip, _semicolon)| Statement::Skip)
.parse(input)
}
#[cfg(test)]
mod tests {
use nom::Finish;
#[test]
fn alias() {
let exp_str = r#"
ALIAS s FOR the_line.start_point;
ALIAS e FOR the_line.end_point;
RETURN (SQRT((s.x - e.x)**2 + (s.y - e.y)**2 + (s.z - e.z)**2));
END_ALIAS;
END_ALIAS;
"#
.trim();
let (residual, (result, _remark)) = super::alias_stmt(exp_str).finish().unwrap();
dbg!(&result);
assert_eq!(residual, "");
}
#[test]
fn if_then() {
let exp_str = r#"
IF SIZEOF (the_bag) > 0 THEN
REPEAT i := 1 TO HIINDEX (the_bag);
the_set := the_set + the_bag [i];
END_REPEAT;
END_IF;
"#
.trim();
let (residual, (result, _remark)) = super::if_stmt(exp_str).finish().unwrap();
dbg!(&result);
assert_eq!(residual, "");
}
#[test]
fn if_then_else() {
let exp_str = r#"
IF a < 10 THEN
c := c + 1;
ELSE
c := c - 1;
END_IF;
"#
.trim();
let (residual, (result, _remark)) = super::if_stmt(exp_str).finish().unwrap();
dbg!(&result);
assert_eq!(residual, "");
}
#[test]
fn procedure_call() {
let exp_str = r#"
INSERT (point_list, this_point, here );
"#
.trim();
let (residual, (result, _remark)) = super::procedure_call_stmt(exp_str).finish().unwrap();
dbg!(&result);
assert_eq!(residual, "");
}
#[test]
fn return_expr1() {
let exp_str = r#"
RETURN (SQRT((s.x - e.x)**2 + (s.y - e.y)**2 + (s.z - e.z)**2));
"#
.trim();
let (residual, (result, _remark)) = super::return_stmt(exp_str).finish().unwrap();
dbg!(&result);
assert_eq!(residual, "");
}
#[test]
fn return_expr2() {
let exp_str = r#"
RETURN ((type1 IN USEDIN(sample, '')) AND (type2 IN USEDIN(sample, '')));
"#
.trim();
let (residual, (result, _remark)) = super::return_stmt(exp_str).finish().unwrap();
dbg!(&result);
assert_eq!(residual, "");
}
#[test]
fn return_none() {
let exp_str = r#"
RETURN;
"#
.trim();
let (residual, (result, _remark)) = super::return_stmt(exp_str).finish().unwrap();
dbg!(&result);
assert_eq!(residual, "");
}
#[test]
fn skip() {
let exp_str = r#"
SKIP;
"#
.trim();
let (residual, (result, _remark)) = super::skip_stmt(exp_str).finish().unwrap();
dbg!(&result);
assert_eq!(residual, "");
}
#[test]
fn repeat_until() {
let exp_str = r#"
REPEAT UNTIL (a=1);
IF (a < 0) THEN
SKIP;
END_IF;
END_REPEAT;
"#
.trim();
let (residual, (result, _remark)) = super::repeat_stmt(exp_str).finish().unwrap();
dbg!(&result);
assert_eq!(residual, "");
}
#[test]
fn repeat_increment() {
let exp_str = r#"
REPEAT i := 1 TO HIINDEX (the_bag);
the_set := the_set + the_bag [i];
END_REPEAT;
"#
.trim();
let (residual, (result, _remark)) = super::repeat_stmt(exp_str).finish().unwrap();
dbg!(&result);
assert_eq!(residual, "");
}
}