texform_transform/rewrite/
helpers.rs1use texform_knowledge::builtin::base;
8use texform_knowledge::specs::BuiltinCommandRecord;
9
10use crate::ast::{
11 Argument, ArgumentKind, ArgumentSlot, ArgumentValue, ContentMode, Delimiter, Node, NodeId,
12};
13
14pub fn mandatory_content_slot(node_id: NodeId, mode: ContentMode) -> ArgumentSlot {
16 let value = match mode {
17 ContentMode::Math => ArgumentValue::MathContent(node_id),
18 ContentMode::Text => ArgumentValue::TextContent(node_id),
19 };
20 Some(Argument {
21 kind: ArgumentKind::Mandatory,
22 value,
23 })
24}
25
26pub fn delimiter_slot(delimiter: Delimiter) -> ArgumentSlot {
28 Some(Argument {
29 kind: ArgumentKind::Mandatory,
30 value: ArgumentValue::Delimiter(delimiter),
31 })
32}
33
34pub fn dimension_slot(value: impl Into<String>) -> ArgumentSlot {
36 Some(Argument {
37 kind: ArgumentKind::Mandatory,
38 value: ArgumentValue::Dimension(value.into()),
39 })
40}
41
42pub fn integer_slot(value: impl Into<String>) -> ArgumentSlot {
44 Some(Argument {
45 kind: ArgumentKind::Mandatory,
46 value: ArgumentValue::Integer(value.into()),
47 })
48}
49
50pub fn infix_prefix_args(left: NodeId, right: NodeId, mode: ContentMode) -> Vec<ArgumentSlot> {
52 vec![
53 mandatory_content_slot(left, mode),
54 mandatory_content_slot(right, mode),
55 ]
56}
57
58pub fn star_slot(value: bool) -> ArgumentSlot {
60 Some(Argument {
61 kind: ArgumentKind::Star,
62 value: ArgumentValue::Boolean(value),
63 })
64}
65
66pub fn bare_command_node(name: &str) -> Node {
68 Node::Command {
69 name: name.to_string(),
70 args: Vec::new(),
71 known: true,
72 }
73}
74
75pub fn prefix_command_node(record: &'static BuiltinCommandRecord, args: Vec<ArgumentSlot>) -> Node {
77 Node::Command {
78 name: record.name.to_string(),
79 args,
80 known: true,
81 }
82}
83
84pub fn linebreak_command_node() -> Node {
86 prefix_command_node(&base::cmd::_BACKSLASH, vec![star_slot(false), None])
87}
88
89#[derive(Clone, Copy)]
90pub enum FenceToken {
91 Char(char),
92 Control(&'static str),
93}
94
95impl FenceToken {
96 pub fn node(self) -> Node {
97 match self {
98 Self::Char(ch) => Node::Char(ch),
99 Self::Control(name) => bare_command_node(name),
100 }
101 }
102}
103
104#[cfg(test)]
105mod tests {
106 use super::*;
107
108 #[test]
109 fn constructs_common_argument_slots() {
110 let mut ast = crate::ast::Ast::new();
111 let node_id = ast.new_node(Node::Char('x'));
112
113 let mandatory = mandatory_content_slot(node_id, ContentMode::Math);
114 assert!(matches!(
115 mandatory,
116 Some(Argument {
117 kind: ArgumentKind::Mandatory,
118 value: ArgumentValue::MathContent(id),
119 }) if id == node_id
120 ));
121
122 let star = star_slot(true);
123 assert!(matches!(
124 star,
125 Some(Argument {
126 kind: ArgumentKind::Star,
127 value: ArgumentValue::Boolean(true),
128 })
129 ));
130
131 let linebreak = linebreak_command_node();
132 assert!(matches!(
133 linebreak,
134 Node::Command { name, args, known }
135 if name == base::cmd::_BACKSLASH.name
136 && known
137 && matches!(
138 args.as_slice(),
139 [
140 Some(Argument {
141 kind: ArgumentKind::Star,
142 value: ArgumentValue::Boolean(false),
143 }),
144 None,
145 ]
146 )
147 ));
148 }
149}