use super::*;
pub(crate) fn parse_call_scene(pair: Pair<Rule>) -> Result<CallScene, ParseError> {
let span = Span::from(&pair.as_span());
let mut target = None;
let mut args = None;
for inner in pair.into_inner() {
match inner.as_rule() {
Rule::id => {
target = Some(CallTarget::Static(inner.as_str().to_string()));
}
Rule::call_target_expr => {
if let Some(expr) = parse_expr_from_parts(inner) {
target = Some(CallTarget::Dynamic(expr));
}
}
Rule::args => {
args = Some(parse_args(inner)?);
}
_ => {}
}
}
Ok(CallScene {
target: target.unwrap_or(CallTarget::Static(String::new())),
args,
span,
})
}
pub(crate) fn parse_action_line(pair: Pair<Rule>) -> Result<ActionLine, ParseError> {
let span = Span::from(&pair.as_span());
let mut actor = String::new();
let mut actions = Vec::new();
for inner in pair.into_inner() {
match inner.as_rule() {
Rule::id => {
actor = inner.as_str().to_string();
}
Rule::actions => {
actions = parse_actions(inner)?;
}
_ => {}
}
}
Ok(ActionLine {
actor,
actions,
span,
})
}
pub(crate) fn parse_continue_action_line(pair: Pair<Rule>) -> Result<ContinueAction, ParseError> {
let span = Span::from(&pair.as_span());
let mut actions = Vec::new();
for inner in pair.into_inner() {
if inner.as_rule() == Rule::actions {
actions = parse_actions(inner)?;
}
}
Ok(ContinueAction { actions, span })
}
pub(crate) fn parse_actions(pair: Pair<Rule>) -> Result<Vec<Action>, ParseError> {
let mut actions = Vec::new();
for inner in pair.into_inner() {
let action_span = Span::from(&inner.as_span());
match inner.as_rule() {
Rule::talk => {
actions.push(Action::Talk {
text: inner.as_str().to_string(),
span: action_span,
});
}
Rule::word_ref => {
for id_inner in inner.into_inner() {
if id_inner.as_rule() == Rule::id {
actions.push(Action::WordRef {
name: id_inner.as_str().to_string(),
span: action_span,
});
}
}
}
Rule::var_ref_local => {
if let Some((name, scope)) = parse_var_ref_local_inner(inner) {
actions.push(Action::VarRef {
name,
scope,
span: action_span,
});
}
}
Rule::var_ref_global => {
for id_inner in inner.into_inner() {
if id_inner.as_rule() == Rule::id {
actions.push(Action::VarRef {
name: id_inner.as_str().to_string(),
scope: VarScope::Global,
span: action_span,
});
}
}
}
Rule::var_ref_property => {
for id_inner in inner.into_inner() {
if id_inner.as_rule() == Rule::property_id {
actions.push(Action::VarRef {
name: id_inner.as_str().to_string(),
scope: VarScope::Property,
span: action_span,
});
}
}
}
Rule::fn_call_local => {
let (name, args) = parse_fn_call_inner(inner)?;
actions.push(Action::FnCall {
name,
args,
scope: FnScope::Local,
span: action_span,
});
}
Rule::fn_call_global => {
let (name, args) = parse_fn_call_inner(inner)?;
actions.push(Action::FnCall {
name,
args,
scope: FnScope::Global,
span: action_span,
});
}
Rule::sakura_script => {
actions.push(Action::SakuraScript {
script: inner.as_str().to_string(),
span: action_span,
});
}
Rule::at_escape | Rule::dollar_escape | Rule::sakura_escape => {
actions.push(Action::Escape {
sequence: inner.as_str().to_string(),
span: action_span,
});
}
_ => {}
}
}
Ok(actions)
}
pub(crate) fn parse_fn_call_inner(pair: Pair<Rule>) -> Result<(String, Args), ParseError> {
let mut name = String::new();
let mut args = Args::empty();
for inner in pair.into_inner() {
match inner.as_rule() {
Rule::id => {
name = inner.as_str().to_string();
}
Rule::args => {
args = parse_args(inner)?;
}
_ => {}
}
}
Ok((name, args))
}
pub(crate) fn parse_args(pair: Pair<Rule>) -> Result<Args, ParseError> {
let span = Span::from(&pair.as_span());
let mut items = Vec::new();
for inner in pair.into_inner() {
match inner.as_rule() {
Rule::positional_arg => {
if let Some(expr) = parse_expr_from_parts(inner) {
items.push(Arg::Positional(expr));
}
}
Rule::key_arg => {
let (key, value) = parse_key_arg(inner)?;
items.push(Arg::Keyword { key, value });
}
_ => {}
}
}
Ok(Args { items, span })
}
pub(crate) fn bin_op_from_rule(rule: Rule) -> Option<BinOp> {
match rule {
Rule::add_op => Some(BinOp::Add),
Rule::sub_op => Some(BinOp::Sub),
Rule::mul_op => Some(BinOp::Mul),
Rule::div_op => Some(BinOp::Div),
Rule::modulo_op => Some(BinOp::Mod),
_ => None,
}
}
pub(crate) fn parse_expr_from_parts(pair: Pair<Rule>) -> Option<Expr> {
let mut terms: Vec<Expr> = Vec::new();
let mut operators: Vec<BinOp> = Vec::new();
for expr_inner in pair.into_inner() {
if let Some(op) = bin_op_from_rule(expr_inner.as_rule()) {
operators.push(op);
} else if let Some(term) = try_parse_expr(expr_inner) {
terms.push(term);
}
}
if terms.is_empty() {
return None;
}
Some(build_left_assoc_expr(terms, operators))
}
pub(crate) fn parse_key_arg(pair: Pair<Rule>) -> Result<(String, Expr), ParseError> {
let mut key = String::new();
let mut terms: Vec<Expr> = Vec::new();
let mut operators: Vec<BinOp> = Vec::new();
for inner in pair.into_inner() {
if inner.as_rule() == Rule::key_expr {
for kv_inner in inner.into_inner() {
match kv_inner.as_rule() {
Rule::id => {
if key.is_empty() {
key = kv_inner.as_str().to_string();
}
}
rule => {
if let Some(op) = bin_op_from_rule(rule) {
operators.push(op);
} else if let Some(expr) = try_parse_expr(kv_inner) {
terms.push(expr);
}
}
}
}
}
}
let value = if terms.is_empty() {
Expr::BlankString
} else {
build_left_assoc_expr(terms, operators)
};
Ok((key, value))
}
pub(crate) fn build_left_assoc_expr(terms: Vec<Expr>, operators: Vec<BinOp>) -> Expr {
let mut terms = terms.into_iter();
let mut result = terms
.next()
.expect("build_left_assoc_expr requires at least one term");
for (op, rhs) in operators.into_iter().zip(terms) {
result = Expr::Binary {
op,
lhs: Box::new(result),
rhs: Box::new(rhs),
};
}
result
}
pub(crate) fn try_parse_expr(pair: Pair<Rule>) -> Option<Expr> {
match pair.as_rule() {
Rule::number_literal => {
let normalized = normalize_number_str(pair.as_str());
if normalized.contains('.') {
Some(Expr::Float(normalized.parse().ok()?))
} else {
Some(Expr::Integer(normalized.parse().ok()?))
}
}
Rule::string_contents => Some(Expr::String(pair.as_str().to_string())),
Rule::string_blank => Some(Expr::BlankString),
Rule::var_ref_local => {
parse_var_ref_local_inner(pair).map(|(name, scope)| Expr::VarRef { name, scope })
}
Rule::var_ref_global => {
for inner in pair.into_inner() {
if inner.as_rule() == Rule::id {
return Some(Expr::VarRef {
name: inner.as_str().to_string(),
scope: VarScope::Global,
});
}
}
None
}
Rule::var_ref_property => {
for inner in pair.into_inner() {
if inner.as_rule() == Rule::property_id {
return Some(Expr::VarRef {
name: inner.as_str().to_string(),
scope: VarScope::Property,
});
}
}
None
}
Rule::fn_call_local => {
let (name, args) = parse_fn_call_inner(pair).ok()?;
Some(Expr::FnCall {
name,
args,
scope: FnScope::Local,
})
}
Rule::fn_call_global => {
let (name, args) = parse_fn_call_inner(pair).ok()?;
Some(Expr::FnCall {
name,
args,
scope: FnScope::Global,
})
}
Rule::paren_expr => {
for inner in pair.into_inner() {
if let Some(expr) = try_parse_expr(inner) {
return Some(Expr::Paren(Box::new(expr)));
}
}
None
}
Rule::add_op | Rule::sub_op | Rule::mul_op | Rule::div_op | Rule::modulo_op => {
None
}
_ => {
for inner in pair.into_inner() {
if let Some(expr) = try_parse_expr(inner) {
return Some(expr);
}
}
None
}
}
}
fn parse_var_ref_local_inner(pair: Pair<Rule>) -> Option<(String, VarScope)> {
for var_id_pair in pair.into_inner() {
if var_id_pair.as_rule() == Rule::var_id {
for id_inner in var_id_pair.into_inner() {
match id_inner.as_rule() {
Rule::id => {
return Some((id_inner.as_str().to_string(), VarScope::Local));
}
Rule::digit_id => {
let index = normalize_number_str(id_inner.as_str())
.parse::<u8>()
.unwrap_or(0);
return Some((id_inner.as_str().to_string(), VarScope::Args(index)));
}
_ => {}
}
}
}
}
None
}