use texform_knowledge::builtin::base;
use texform_knowledge::specs::BuiltinCommandRecord;
use crate::ast::{
Argument, ArgumentKind, ArgumentSlot, ArgumentValue, ContentMode, Delimiter, Node, NodeId,
};
pub fn mandatory_content_slot(node_id: NodeId, mode: ContentMode) -> ArgumentSlot {
let value = match mode {
ContentMode::Math => ArgumentValue::MathContent(node_id),
ContentMode::Text => ArgumentValue::TextContent(node_id),
};
Some(Argument {
kind: ArgumentKind::Mandatory,
value,
})
}
pub fn delimiter_slot(delimiter: Delimiter) -> ArgumentSlot {
Some(Argument {
kind: ArgumentKind::Mandatory,
value: ArgumentValue::Delimiter(delimiter),
})
}
pub fn dimension_slot(value: impl Into<String>) -> ArgumentSlot {
Some(Argument {
kind: ArgumentKind::Mandatory,
value: ArgumentValue::Dimension(value.into()),
})
}
pub fn integer_slot(value: impl Into<String>) -> ArgumentSlot {
Some(Argument {
kind: ArgumentKind::Mandatory,
value: ArgumentValue::Integer(value.into()),
})
}
pub fn infix_prefix_args(left: NodeId, right: NodeId, mode: ContentMode) -> Vec<ArgumentSlot> {
vec![
mandatory_content_slot(left, mode),
mandatory_content_slot(right, mode),
]
}
pub fn star_slot(value: bool) -> ArgumentSlot {
Some(Argument {
kind: ArgumentKind::Star,
value: ArgumentValue::Boolean(value),
})
}
pub fn bare_command_node(name: &str) -> Node {
Node::Command {
name: name.to_string(),
args: Vec::new(),
known: true,
}
}
pub fn prefix_command_node(record: &'static BuiltinCommandRecord, args: Vec<ArgumentSlot>) -> Node {
Node::Command {
name: record.name.to_string(),
args,
known: true,
}
}
pub fn linebreak_command_node() -> Node {
prefix_command_node(&base::cmd::_BACKSLASH, vec![star_slot(false), None])
}
#[derive(Clone, Copy)]
pub enum FenceToken {
Char(char),
Control(&'static str),
}
impl FenceToken {
pub fn node(self) -> Node {
match self {
Self::Char(ch) => Node::Char(ch),
Self::Control(name) => bare_command_node(name),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn constructs_common_argument_slots() {
let mut ast = crate::ast::Ast::new();
let node_id = ast.new_node(Node::Char('x'));
let mandatory = mandatory_content_slot(node_id, ContentMode::Math);
assert!(matches!(
mandatory,
Some(Argument {
kind: ArgumentKind::Mandatory,
value: ArgumentValue::MathContent(id),
}) if id == node_id
));
let star = star_slot(true);
assert!(matches!(
star,
Some(Argument {
kind: ArgumentKind::Star,
value: ArgumentValue::Boolean(true),
})
));
let linebreak = linebreak_command_node();
assert!(matches!(
linebreak,
Node::Command { name, args, known }
if name == base::cmd::_BACKSLASH.name
&& known
&& matches!(
args.as_slice(),
[
Some(Argument {
kind: ArgumentKind::Star,
value: ArgumentValue::Boolean(false),
}),
None,
]
)
));
}
}