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