ratex_parser/functions/
environment.rs1use std::collections::HashMap;
2
3use crate::environments::{EnvContext, ENVIRONMENTS};
4use crate::error::{ParseError, ParseResult};
5use crate::functions::{define_function_full, ArgType, FunctionContext, FunctionSpec};
6use crate::parse_node::ParseNode;
7
8pub fn register(map: &mut HashMap<&'static str, FunctionSpec>) {
9 define_function_full(
10 map,
11 &["\\begin", "\\end"],
12 "environment",
13 1,
14 0,
15 Some(vec![ArgType::Text]),
16 true,
17 true,
18 true,
19 false,
20 false,
21 handle_begin_end,
22 );
23
24 define_function_full(
25 map,
26 &["\\hline", "\\hdashline"],
27 "text",
28 0,
29 0,
30 None,
31 false,
32 true,
33 true,
34 false,
35 false,
36 handle_hline,
37 );
38}
39
40fn extract_env_name(name_group: &ParseNode) -> ParseResult<String> {
41 match name_group {
42 ParseNode::OrdGroup { body, .. } => {
43 let mut env_name = String::new();
44 for node in body {
45 match node {
46 ParseNode::TextOrd { text, .. } | ParseNode::MathOrd { text, .. } => {
47 env_name.push_str(text);
48 }
49 ParseNode::Atom { text, .. } => {
50 env_name.push_str(text);
51 }
52 _ => {
53 return Err(ParseError::msg(format!(
54 "Invalid environment name character: {:?}",
55 node.type_name()
56 )));
57 }
58 }
59 }
60 Ok(env_name)
61 }
62 _ => Err(ParseError::msg("Invalid environment name")),
63 }
64}
65
66fn handle_begin_end(
67 ctx: &mut FunctionContext,
68 args: Vec<ParseNode>,
69 _opt_args: Vec<Option<ParseNode>>,
70) -> ParseResult<ParseNode> {
71 let name_group = &args[0];
72 let env_name = extract_env_name(name_group)?;
73
74 if ctx.func_name == "\\begin" {
75 let env = ENVIRONMENTS
78 .get(env_name.as_str())
79 .ok_or_else(|| ParseError::msg(format!("No such environment: {}", env_name)))?;
80
81 let mut env_args = Vec::new();
83 for _ in 0..env.num_args {
84 let arg = ctx.parser.parse_argument_group(false, None)?;
85 match arg {
86 Some(a) => env_args.push(a),
87 None => {
88 return Err(ParseError::msg(format!(
89 "Expected argument to \\begin{{{}}}",
90 env_name
91 )));
92 }
93 }
94 }
95
96 let mut env_ctx = EnvContext {
97 mode: ctx.parser.mode,
98 env_name: env_name.clone(),
99 parser: ctx.parser,
100 };
101
102 let result = (env.handler)(&mut env_ctx, env_args, Vec::new())?;
103
104 ctx.parser.expect("\\end", false)?;
106
107 let end_node = ctx.parser.parse_function(None, None)?;
109 let end_name = match &end_node {
110 Some(ParseNode::Environment { name, .. }) => name.clone(),
111 _ => {
112 return Err(ParseError::msg("Expected \\end after environment body"));
113 }
114 };
115
116 if end_name != env_name {
117 return Err(ParseError::msg(format!(
118 "Mismatch: \\begin{{{}}} matched by \\end{{{}}}",
119 env_name, end_name
120 )));
121 }
122
123 Ok(result)
124 } else {
125 Ok(ParseNode::Environment {
127 mode: ctx.parser.mode,
128 name: env_name,
129 name_group: Box::new(name_group.clone()),
130 loc: None,
131 })
132 }
133}
134
135fn handle_hline(
136 ctx: &mut FunctionContext,
137 _args: Vec<ParseNode>,
138 _opt_args: Vec<Option<ParseNode>>,
139) -> ParseResult<ParseNode> {
140 Err(ParseError::msg(format!(
141 "{} valid only within array environment",
142 ctx.func_name
143 )))
144}