use crate::ast::{
ActionBodyDecl, ActionDef, ActionDefBody, ActionDefBodyElement, ActionUsage, ActionUsageBody,
ActionUsageBodyElement, AssignStmt, FirstMergeBody, FirstStmt, Flow, ForLoop, InOut, InOutDecl,
MergeStmt, Node, ParseErrorNode, ThenAction,
};
use crate::parser::body::{advance_to_closing_brace, parse_structured_brace_members};
use crate::parser::build_recovery_error_node_from_span;
use crate::parser::definition_prefix::{parse_definition_prefix, DefinitionPrefixOptions};
use crate::parser::expr::path_expression;
use crate::parser::interface::connect_body;
use crate::parser::lex::{
name, qualified_name, starts_with_any_keyword, take_until_terminator, ws1, ws_and_comments,
};
use crate::parser::metadata_annotation::annotation;
use crate::parser::node_from_to;
use crate::parser::part::bind_;
use crate::parser::usage::usage_header;
use crate::parser::with_span;
use crate::parser::Input;
use nom::branch::alt;
use nom::bytes::complete::tag;
use nom::combinator::{map, opt};
use nom::sequence::{delimited, preceded};
use nom::IResult;
use nom::Parser;
const ACTION_BODY_STARTERS: &[&[u8]] = &[
b"in",
b"out",
b"ref",
b"perform",
b"bind",
b"flow",
b"first",
b"merge",
b"state",
b"assign",
b"then",
b"for",
b"action",
b"attribute",
b"calc",
b"event",
b"accept",
b"decision",
b"fork",
b"join",
b"send",
b"terminate",
b"while",
b"if",
b"@",
b"#",
];
const CONTROL_NODE_KEYWORDS: &[&[u8]] = &[
b"accept",
b"decision",
b"fork",
b"join",
b"send",
b"terminate",
b"while",
b"if",
];
const UNTIL_SEMI_OR_BRACE: &[u8] = b";{";
fn doc_comment_stmt(input: Input<'_>) -> IResult<Input<'_>, Node<crate::ast::DocComment>> {
let (input, doc) = crate::parser::requirement::doc_comment(input)?;
let (input, _) = opt(preceded(ws_and_comments, tag(&b";"[..]))).parse(input)?;
Ok((input, doc))
}
fn optional_multiplicity_brackets(input: Input<'_>) -> IResult<Input<'_>, ()> {
let (input, _) = opt(preceded(
ws_and_comments,
delimited(
tag(&b"["[..]),
nom::bytes::complete::take_until(&b"]"[..]),
tag(&b"]"[..]),
),
))
.parse(input)?;
Ok((input, ()))
}
fn action_ref_decl(input: Input<'_>) -> IResult<Input<'_>, Node<crate::ast::RefDecl>> {
use crate::parser::expr::expression;
let start = input;
let (input, _) = ws_and_comments(input)?;
let (input, _) = opt(alt((
preceded(tag(&b"public"[..]), ws1),
preceded(tag(&b"private"[..]), ws1),
preceded(tag(&b"protected"[..]), ws1),
)))
.parse(input)?;
let (input, _) = tag(&b"ref"[..]).parse(input)?;
let (input, _) = ws1(input)?;
let (input, _) = opt(preceded(tag(&b"action"[..]), ws1)).parse(input)?;
let (input, parsed_name) = opt(with_span(name)).parse(input)?;
let (input, _) = optional_multiplicity_brackets(input)?;
let (name_span, name_str) = parsed_name.unwrap_or((crate::ast::Span::dummy(), String::new()));
let (input, uses_shift) = preceded(
ws_and_comments,
alt((
map(tag(&b":>>"[..]), |_| true),
map(tag(&b":>"[..]), |_| false),
map(tag(&b":"[..]), |_| false),
)),
)
.parse(input)?;
let (input, (type_ref_span, type_name)) = if uses_shift {
(input, (crate::ast::Span::dummy(), String::new()))
} else {
preceded(ws_and_comments, with_span(qualified_name)).parse(input)?
};
let (input, _) = ws_and_comments(input)?;
let (mut input, value) = opt(preceded(
preceded(ws_and_comments, tag(&b"="[..])),
preceded(ws_and_comments, expression),
))
.parse(input)?;
if !input.fragment().is_empty()
&& !input.fragment().starts_with(b";")
&& !input.fragment().starts_with(b"{")
{
let (next, _) = take_until_terminator(input, UNTIL_SEMI_OR_BRACE)?;
input = next;
}
let (input, body) = preceded(
ws_and_comments,
alt((
map(tag(&b";"[..]), |_| crate::ast::RefBody::Semicolon),
map(
delimited(
tag(&b"{"[..]),
advance_to_closing_brace,
preceded(ws_and_comments, tag(&b"}"[..])),
),
|_| crate::ast::RefBody::Brace,
),
)),
)
.parse(input)?;
Ok((
input,
node_from_to(
start,
input,
crate::ast::RefDecl {
name: name_str,
type_name,
value,
body,
name_span: Some(name_span),
type_ref_span: Some(type_ref_span),
},
),
))
}
fn first_merge_body(input: Input<'_>) -> IResult<Input<'_>, FirstMergeBody> {
let (input, _) = ws_and_comments(input)?;
alt((
map(tag(&b";"[..]), |_| FirstMergeBody::Semicolon),
map(
nom::sequence::delimited(
tag(&b"{"[..]),
advance_to_closing_brace,
preceded(ws_and_comments, tag(&b"}"[..])),
),
|_| FirstMergeBody::Brace,
),
))
.parse(input)
}
pub(crate) fn in_out_decl(input: Input<'_>) -> IResult<Input<'_>, Node<InOutDecl>> {
let start = input;
let (input, _) = ws_and_comments(input)?;
let (input, direction) = alt((
map(preceded(tag(&b"in"[..]), ws1), |_| InOut::In),
map(preceded(tag(&b"out"[..]), ws1), |_| InOut::Out),
map(preceded(tag(&b"inout"[..]), ws1), |_| InOut::InOut),
))
.parse(input)?;
let (input, _) = nom::combinator::opt(preceded(tag(&b"attribute"[..]), ws1)).parse(input)?;
let parsed = (|| {
let (input, action_typed_name) = opt(preceded(tag(&b"action"[..]), ws1)).parse(input)?;
let (input, param_name) = name(input)?;
let (input, type_name) = nom::combinator::opt(map(
(
preceded(ws_and_comments, tag(&b":"[..])),
preceded(ws_and_comments, qualified_name),
),
|(_, tn)| tn,
))
.parse(input)?;
let mut type_name = type_name.unwrap_or_default();
if action_typed_name.is_some() && type_name.is_empty() {
type_name = "action".to_string();
}
let (input, _) = opt((
preceded(ws_and_comments, tag(&b"default"[..])),
ws1,
delimited(
tag(&b"{"[..]),
advance_to_closing_brace,
preceded(ws_and_comments, tag(&b"}"[..])),
),
))
.parse(input)?;
let (input, _) = preceded(
ws_and_comments,
alt((
map(tag(&b";"[..]), |_| ()),
map(
delimited(
tag(&b"{"[..]),
advance_to_closing_brace,
preceded(ws_and_comments, tag(&b"}"[..])),
),
|_| (),
),
)),
)
.parse(input)?;
Ok::<_, nom::Err<nom::error::Error<Input<'_>>>>((input, (param_name, type_name)))
})();
let (input, (param_name, type_name)) = match parsed {
Ok(v) => v,
Err(_) => {
let (input, raw_text) = take_until_terminator(input, UNTIL_SEMI_OR_BRACE)?;
let raw_text = raw_text.trim().to_string();
let name_guess = raw_text
.split(|c: char| c.is_whitespace() || c == ':' || c == '[' || c == ',' || c == ';')
.find(|s| !s.is_empty() && *s != ":>>")
.unwrap_or("param")
.to_string();
let (input, _) = preceded(
ws_and_comments,
alt((
map(tag(&b";"[..]), |_| ()),
map(
delimited(
tag(&b"{"[..]),
advance_to_closing_brace,
preceded(ws_and_comments, tag(&b"}"[..])),
),
|_| (),
),
)),
)
.parse(input)?;
(input, (name_guess, raw_text))
}
};
Ok((
input,
node_from_to(
start,
input,
InOutDecl {
direction,
name: param_name,
type_name,
},
),
))
}
fn action_def_body(input: Input<'_>) -> IResult<Input<'_>, ActionDefBody> {
let (input, _) = ws_and_comments(input)?;
alt((
map(tag(&b";"[..]), |_| ActionDefBody::Semicolon),
action_def_body_brace,
))
.parse(input)
}
pub(crate) fn action_def_body_brace(input: Input<'_>) -> IResult<Input<'_>, ActionDefBody> {
let (input, elements) = parse_structured_brace_members(
input,
ACTION_BODY_STARTERS,
"action body",
"recovered_action_body_element",
action_def_body_element,
|start, end| {
let recovery = build_recovery_error_node_from_span(
start,
end,
ACTION_BODY_STARTERS,
"action body",
"recovered_action_body_element",
);
let node: Node<ParseErrorNode> = node_from_to(start, end, recovery);
node_from_to(start, end, ActionDefBodyElement::Error(node))
},
)?;
Ok((input, ActionDefBody::Brace { elements }))
}
fn slice_text(start: Input<'_>, end: Input<'_>) -> String {
let delta = end
.location_offset()
.saturating_sub(start.location_offset());
let bytes = start.fragment();
let take = delta.min(bytes.len());
String::from_utf8_lossy(&bytes[..take]).trim().to_string()
}
pub(crate) fn assign_stmt(input: Input<'_>) -> IResult<Input<'_>, Node<AssignStmt>> {
let start = input;
let (input, _) = ws_and_comments(input)?;
let (input, is_then) = opt(map(preceded(tag(&b"then"[..]), ws1), |_| true)).parse(input)?;
let is_then = is_then.unwrap_or(false);
let (input, _) = tag(&b"assign"[..]).parse(input)?;
let (mut input, _) = ws1(input)?;
let frag = input.fragment();
let mut pos = 0usize;
while pos + 1 < frag.len() {
if frag[pos] == b':' && frag[pos + 1] == b'=' {
break;
}
pos += 1;
}
if pos + 1 >= frag.len() {
return Err(nom::Err::Error(nom::error::Error::new(
input,
nom::error::ErrorKind::Tag,
)));
}
let (after_lhs, _) = nom::bytes::complete::take(pos).parse(input)?;
let lhs = slice_text(input, after_lhs);
let (after_colon_eq, _) = tag(&b":="[..]).parse(after_lhs)?;
input = after_colon_eq;
let (after_rhs, rhs) = take_until_terminator(input, b";")?;
let (after_semi, _) = preceded(ws_and_comments, tag(&b";"[..])).parse(after_rhs)?;
Ok((
after_semi,
node_from_to(start, after_semi, AssignStmt { is_then, lhs, rhs }),
))
}
pub(crate) fn for_loop(input: Input<'_>) -> IResult<Input<'_>, Node<ForLoop>> {
let start = input;
let (input, _) = ws_and_comments(input)?;
let (input, _) = tag(&b"for"[..]).parse(input)?;
let (input, _) = ws1(input)?;
let (input, var) = name(input)?;
let (input, _) = preceded(ws_and_comments, tag(&b"in"[..])).parse(input)?;
let (input, _) = ws1(input)?;
let (input, range) = take_until_terminator(input, b"{")?;
let (input, body) = action_def_body_brace(input)?;
Ok((
input,
node_from_to(start, input, ForLoop { var, range, body }),
))
}
pub(crate) fn then_action(input: Input<'_>) -> IResult<Input<'_>, Node<ThenAction>> {
let start = input;
let (input, _) = ws_and_comments(input)?;
let (input, _) = tag(&b"then"[..]).parse(input)?;
let (input, _) = ws1(input)?;
let (input, _) = opt(alt((
preceded(tag(&b"public"[..]), ws1),
preceded(tag(&b"private"[..]), ws1),
preceded(tag(&b"protected"[..]), ws1),
)))
.parse(input)?;
let (input, action) = action_usage(input)?;
Ok((input, node_from_to(start, input, ThenAction { action })))
}
fn control_node_action_usage(input: Input<'_>) -> IResult<Input<'_>, Node<ActionUsage>> {
let (peek, _) = ws_and_comments(input)?;
if starts_with_any_keyword(peek.fragment(), CONTROL_NODE_KEYWORDS) {
return visibility_action_usage(input);
}
Err(nom::Err::Error(nom::error::Error::new(
input,
nom::error::ErrorKind::Alt,
)))
}
fn action_def_body_element(
input: Input<'_>,
) -> IResult<Input<'_>, Node<crate::ast::ActionDefBodyElement>> {
use crate::ast::ActionDefBodyElement;
use crate::parser::part::perform_action_decl;
use crate::parser::state::state_usage;
let (input, _) = ws_and_comments(input)?;
let start = input;
let (input, elem) = nom::branch::alt((
map(assign_stmt, ActionDefBodyElement::Assign),
map(for_loop, ActionDefBodyElement::ForLoop),
map(then_action, ActionDefBodyElement::ThenAction),
map(action_body_decl, ActionDefBodyElement::Decl),
map(in_out_decl, ActionDefBodyElement::InOutDecl),
map(doc_comment_stmt, ActionDefBodyElement::Doc),
map(annotation, ActionDefBodyElement::Annotation),
map(action_ref_decl, ActionDefBodyElement::RefDecl),
map(perform_action_decl, ActionDefBodyElement::Perform),
map(bind_, ActionDefBodyElement::Bind),
map(flow_, ActionDefBodyElement::Flow),
map(first_stmt, ActionDefBodyElement::FirstStmt),
map(merge_stmt, ActionDefBodyElement::MergeStmt),
map(state_usage, ActionDefBodyElement::StateUsage),
map(control_node_action_usage, |a| {
ActionDefBodyElement::ActionUsage(Box::new(a))
}),
map(visibility_action_usage, |a| {
ActionDefBodyElement::ActionUsage(Box::new(a))
}),
))
.parse(input)?;
Ok((input, node_from_to(start, input, elem)))
}
pub(crate) fn action_def(input: Input<'_>) -> IResult<Input<'_>, Node<ActionDef>> {
let start = input;
let (input, prefix) = parse_definition_prefix(
input,
DefinitionPrefixOptions::new(b"action").def_required(),
)?;
let (input, body) = action_def_body(input)?;
Ok((
input,
node_from_to(
start,
input,
ActionDef {
identification: prefix.identification,
specializes: prefix.specializes,
specializes_span: prefix.specializes_span,
body,
},
),
))
}
fn flow_(input: Input<'_>) -> IResult<Input<'_>, Node<Flow>> {
let start = input;
let (input, _) = ws_and_comments(input)?;
let (input, _) = tag(&b"flow"[..]).parse(input)?;
let (input, _) = ws1(input)?;
let (input, from_expr) = path_expression(input)?;
let (input, _) = preceded(ws_and_comments, tag(&b"to"[..])).parse(input)?;
let (input, to_expr) = preceded(ws_and_comments, path_expression).parse(input)?;
let (input, body) = connect_body(input)?;
Ok((
input,
node_from_to(
start,
input,
Flow {
from: from_expr,
to: to_expr,
body,
},
),
))
}
fn first_stmt(input: Input<'_>) -> IResult<Input<'_>, Node<FirstStmt>> {
let start = input;
let (input, _) = ws_and_comments(input)?;
let (input, _) = tag(&b"first"[..]).parse(input)?;
let (input, _) = ws1(input)?;
let (input, first_expr) = path_expression(input)?;
let (input, _) = preceded(ws_and_comments, tag(&b"then"[..])).parse(input)?;
let (input, then_expr) = preceded(ws_and_comments, path_expression).parse(input)?;
let (input, body) = first_merge_body(input)?;
Ok((
input,
node_from_to(
start,
input,
FirstStmt {
first: first_expr,
then: then_expr,
body,
},
),
))
}
fn merge_stmt(input: Input<'_>) -> IResult<Input<'_>, Node<MergeStmt>> {
let start = input;
let (input, _) = ws_and_comments(input)?;
let (input, _) = tag(&b"merge"[..]).parse(input)?;
let (input, _) = ws1(input)?;
let (input, merge_expr) = path_expression(input)?;
let (input, body) = first_merge_body(input)?;
Ok((
input,
node_from_to(
start,
input,
MergeStmt {
merge: merge_expr,
body,
},
),
))
}
fn action_usage_body(input: Input<'_>) -> IResult<Input<'_>, ActionUsageBody> {
let (input, _) = ws_and_comments(input)?;
alt((
map(tag(&b";"[..]), |_| ActionUsageBody::Semicolon),
action_usage_body_brace,
))
.parse(input)
}
fn action_usage_body_brace(input: Input<'_>) -> IResult<Input<'_>, ActionUsageBody> {
let (input, elements) = parse_structured_brace_members(
input,
ACTION_BODY_STARTERS,
"action body",
"recovered_action_body_element",
action_usage_body_element,
|start, end| {
let recovery = build_recovery_error_node_from_span(
start,
end,
ACTION_BODY_STARTERS,
"action body",
"recovered_action_body_element",
);
let node: Node<ParseErrorNode> = node_from_to(start, end, recovery);
node_from_to(start, end, ActionUsageBodyElement::Error(node))
},
)?;
Ok((input, ActionUsageBody::Brace { elements }))
}
fn action_usage_body_element(input: Input<'_>) -> IResult<Input<'_>, Node<ActionUsageBodyElement>> {
use crate::parser::state::state_usage;
let (input, _) = ws_and_comments(input)?;
let start = input;
let (input, elem) = alt((
map(assign_stmt, ActionUsageBodyElement::Assign),
map(for_loop, ActionUsageBodyElement::ForLoop),
map(then_action, ActionUsageBodyElement::ThenAction),
map(action_body_decl, ActionUsageBodyElement::Decl),
map(in_out_decl, ActionUsageBodyElement::InOutDecl),
map(doc_comment_stmt, ActionUsageBodyElement::Doc),
map(annotation, ActionUsageBodyElement::Annotation),
map(action_ref_decl, ActionUsageBodyElement::RefDecl),
map(bind_, ActionUsageBodyElement::Bind),
map(flow_, ActionUsageBodyElement::Flow),
map(first_stmt, ActionUsageBodyElement::FirstStmt),
map(merge_stmt, ActionUsageBodyElement::MergeStmt),
map(state_usage, ActionUsageBodyElement::StateUsage),
map(visibility_action_usage, |a| {
ActionUsageBodyElement::ActionUsage(Box::new(a))
}),
))
.parse(input)?;
Ok((input, node_from_to(start, input, elem)))
}
fn visibility_action_usage(input: Input<'_>) -> IResult<Input<'_>, Node<ActionUsage>> {
let (input, _) = ws_and_comments(input)?;
let (input, _) = opt(alt((
preceded(tag(&b"public"[..]), ws1),
preceded(tag(&b"private"[..]), ws1),
preceded(tag(&b"protected"[..]), ws1),
)))
.parse(input)?;
action_usage(input)
}
fn action_body_decl(input: Input<'_>) -> IResult<Input<'_>, Node<ActionBodyDecl>> {
let start = input;
let (input, _) = ws_and_comments(input)?;
let (input, _) = opt(alt((
preceded(tag(&b"public"[..]), ws1),
preceded(tag(&b"private"[..]), ws1),
preceded(tag(&b"protected"[..]), ws1),
)))
.parse(input)?;
let (input, _) = opt(preceded(tag(&b"abstract"[..]), ws1)).parse(input)?;
let (input, keyword) = alt((
map(tag(&b"attribute"[..]), |_| "attribute".to_string()),
map(tag(&b"calc"[..]), |_| "calc".to_string()),
map(tag(&b"event"[..]), |_| "event".to_string()),
))
.parse(input)?;
let (input, text) = take_until_terminator(input, UNTIL_SEMI_OR_BRACE)?;
let (input, _) = ws_and_comments(input)?;
let (input, _) = alt((
map(tag(&b";"[..]), |_| ()),
map(
delimited(
tag(&b"{"[..]),
advance_to_closing_brace,
preceded(ws_and_comments, tag(&b"}"[..])),
),
|_| (),
),
))
.parse(input)?;
Ok((
input,
node_from_to(start, input, ActionBodyDecl { keyword, text }),
))
}
pub(crate) fn action_usage(input: Input<'_>) -> IResult<Input<'_>, Node<ActionUsage>> {
let start = input;
let (input, _) = ws_and_comments(input)?;
let (input, _) = nom::combinator::opt(preceded(tag(&b"abstract"[..]), ws1)).parse(input)?;
let (input, _) = tag(&b"action"[..]).parse(input)?;
let (input, _) = ws1(input)?;
let (input, (name_span, name_str)) = with_span(name).parse(input)?;
let (input, header) = usage_header(input)?;
let (input, accept) = nom::combinator::opt(preceded(
preceded(ws_and_comments, tag(&b"accept"[..])),
preceded(
ws1,
(
name,
preceded(ws_and_comments, tag(&b":"[..])),
preceded(ws_and_comments, qualified_name),
),
),
))
.parse(input)?;
let (input, _) = ws_and_comments(input)?;
let (input, _) = take_until_terminator(input, UNTIL_SEMI_OR_BRACE)?;
let type_name = header.type_name.unwrap_or_default();
let type_ref_span = None;
let accept = accept.map(|(param_name, _, param_type)| (param_name, param_type));
let (input, body) = action_usage_body(input)?;
let (input, _) =
nom::combinator::opt(preceded(ws_and_comments, tag(&b";"[..]))).parse(input)?;
Ok((
input,
node_from_to(
start,
input,
ActionUsage {
name: name_str,
type_name,
accept,
body,
name_span: Some(name_span),
type_ref_span,
},
),
))
}