1use std::char;
4use texlang::prelude as txl;
5use texlang::traits::*;
6use texlang::*;
7
8pub const THE_DOC: &str = "Output text describing some inputted tokens";
9
10pub trait TheCompatible: TexlangState {
12 fn get_command_ref_for_font(&self, font: types::Font) -> Option<token::CommandRef> {
13 _ = font;
14 None
15 }
16}
17
18pub fn get_the<S: TheCompatible>() -> command::BuiltIn<S> {
20 command::BuiltIn::new_expansion(the_primitive_fn)
21}
22
23fn the_primitive_fn<S: TheCompatible>(
24 the_token: token::Token,
25 input: &mut vm::ExpansionInput<S>,
26) -> txl::Result<()> {
27 let token = input.next_or_err(EndOfInputError {})?;
28 match &token.value() {
30 token::Value::CommandRef(command_ref) => {
31 match input.commands_map().get_command(command_ref) {
32 Some(command::Command::Variable(cmd)) => {
33 let variable = cmd.clone().resolve(token, input.as_mut())?;
34 let (state, expansions) = input.state_and_expansions_mut();
35 match variable.value(state) {
36 variable::ValueRef::Int(i) => {
37 write(expansions, the_token, *i);
38 }
39 variable::ValueRef::SmallInt(i) => {
40 write(expansions, the_token, *i);
41 }
42 variable::ValueRef::CatCode(i) => {
43 write(expansions, the_token, (*i as u8) as i32);
44 }
45 variable::ValueRef::MathCode(i) => write(expansions, the_token, i.0 as i32),
46 variable::ValueRef::Dimen(d) => {
47 write(expansions, the_token, *d);
48 }
49 variable::ValueRef::Glue(g) => {
50 write(expansions, the_token, *g);
51 }
52 variable::ValueRef::Font(font) => {
53 let font = *font;
54 let command_ref = input.state().get_command_ref_for_font(font).unwrap();
55 let font_token =
56 token::Token::new_command_ref(command_ref, the_token.trace_key());
57 input.back(font_token);
58 }
59 variable::ValueRef::TokenList(t) => {
60 expansions.extend(t.iter().rev());
61 }
62 };
63 }
64 Some(command::Command::Character(c)) => {
65 let i = *c as i32;
66 write(input.expansions_mut(), the_token, i);
67 }
68 Some(command::Command::MathCharacter(c)) => {
69 let i = c.0 as i32;
70 write(input.expansions_mut(), the_token, i);
71 }
72 Some(command::Command::Font(font)) => {
73 font_to_tokens(the_token, input, *font);
78 }
79 Some(command::Command::Execution(_, Some(tag))) => {
80 if input.state().is_current_font_command(*tag) {
81 font_to_tokens(the_token, input, input.vm().current_font());
82 } else {
83 todo!("should return an error")
84 }
85 }
86 None
87 | Some(
88 command::Command::Expansion(..)
89 | command::Command::Macro(..)
90 | command::Command::Execution(..)
91 | command::Command::CharacterTokenAlias(..),
92 ) => {
93 todo!("should return an error")
94 }
95 }
96 }
97 _ => todo!("should return an error"),
98 };
99 Ok(())
100}
101
102fn font_to_tokens<S: TexlangState + TheCompatible>(
103 the_token: token::Token,
104 input: &mut vm::ExpansionInput<S>,
105 font: types::Font,
106) {
107 let command_ref = input.state().get_command_ref_for_font(font).unwrap();
108 let font_token = token::Token::new_command_ref(command_ref, the_token.trace_key());
109 input.back(font_token);
110}
111
112struct TokenWrite<'a>(&'a mut Vec<token::Token>, token::trace::Key);
117
118impl<'a> std::fmt::Write for TokenWrite<'a> {
119 fn write_str(&mut self, s: &str) -> std::fmt::Result {
120 for c in s.chars() {
121 self.write_char(c)?;
122 }
123 Ok(())
124 }
125 fn write_char(&mut self, c: char) -> std::fmt::Result {
126 let token = if c == ' ' {
127 token::Token::new_space(c, self.1)
128 } else if c.is_ascii_alphabetic() {
129 token::Token::new_letter(c, self.1)
130 } else {
131 token::Token::new_other(c, self.1)
132 };
133 self.0.push(token);
134 Ok(())
135 }
136}
137
138fn write<D: std::fmt::Display>(buffer: &mut Vec<token::Token>, the_token: token::Token, value: D) {
139 let start = buffer.len();
140 use std::fmt::Write;
141 let mut t = TokenWrite(buffer, the_token.trace_key());
142 write!(t, "{value}").expect("the token writer cannot error");
143 buffer[start..].reverse();
144}
145
146#[derive(Debug)]
147struct EndOfInputError;
148
149impl error::EndOfInputError for EndOfInputError {
150 fn doing(&self) -> String {
151 r"determining the argument to \the".into()
152 }
153}
154
155#[cfg(test)]
156mod tests {
157 use super::*;
158 impl TheCompatible for texlang_testing::State {}
159}