texlang_stdlib/
the.rs

1//! The `\the` primitive
2
3use std::char;
4use std::convert::TryInto;
5use texlang::traits::*;
6use texlang::*;
7
8pub const THE_DOC: &str = "Output text describing some inputted tokens";
9
10/// Get the `\the` expansion primitive.
11pub fn get_the<S: TexlangState>() -> command::BuiltIn<S> {
12    command::BuiltIn::new_expansion(the_primitive_fn)
13}
14
15fn the_primitive_fn<S: TexlangState>(
16    the_token: token::Token,
17    input: &mut vm::ExpansionInput<S>,
18) -> command::Result<Vec<token::Token>> {
19    // TODO: double check \the expands the input
20    let token = match input.next()? {
21        None => return Err(error::SimpleEndOfInputError::new(input.vm(), "TODO").into()),
22        Some(token) => token,
23    };
24    Ok(match &token.value() {
25        token::Value::ControlSequence(name) => {
26            if let Some(command::Command::Variable(cmd)) = input.commands_map().get_command(name) {
27                match cmd.clone().value(token, input.as_mut())? {
28                    variable::ValueRef::Int(i) => int_to_tokens(the_token, *i),
29                    variable::ValueRef::CatCode(i) => int_to_tokens(the_token, (*i as u8).into()),
30                }
31            } else {
32                // TODO: push straight onto the expansions stack?
33                vec![token]
34            }
35        }
36        // TODO: push straight onto the expansions stack?
37        _ => vec![token],
38    })
39}
40
41fn int_to_tokens(the_token: token::Token, mut i: i32) -> Vec<token::Token> {
42    if i == 0 {
43        return vec![token::Token::new_other('0', the_token.trace_key())];
44    }
45    let negative = i < 0;
46    // TODO: allocate the capacity precisely?
47    // Even better: can push straight onto the expansions stack?
48    let mut tokens = Vec::new();
49    while i != 0 {
50        let digit = (i % 10).abs();
51        tokens.push(token::Token::new_other(
52            char::from_digit(digit.try_into().unwrap(), 10).unwrap(),
53            the_token.trace_key(),
54        ));
55        i /= 10;
56    }
57    if negative {
58        tokens.push(token::Token::new_other('-', the_token.trace_key()));
59    }
60    tokens.reverse();
61    tokens
62}