use std::path::PathBuf;
use super::TexlangState;
use crate::token::trace;
use crate::token::Token;
use crate::*;
pub trait TokenStream {
type S;
fn next(&mut self) -> Result<Option<Token>, Box<error::Error>>;
fn peek(&mut self) -> Result<Option<&Token>, Box<error::Error>>;
fn consume(&mut self) -> Result<(), Box<error::Error>> {
self.next().map(|_| ())
}
fn vm(&self) -> &vm::VM<Self::S>;
#[inline]
fn commands_map(&self) -> &command::Map<Self::S> {
&self.vm().commands_map
}
#[inline]
fn state(&self) -> &Self::S {
&self.vm().state
}
fn trace(&self, token: Token) -> trace::SourceCodeTrace {
self.vm().trace(token)
}
fn trace_end_of_input(&self) -> trace::SourceCodeTrace {
self.vm().internal.tracer.trace_end_of_input()
}
}
#[repr(transparent)]
pub struct ExpandedStream<S>(UnexpandedStream<S>);
impl<S> std::convert::AsMut<ExpandedStream<S>> for ExpandedStream<S> {
fn as_mut(&mut self) -> &mut ExpandedStream<S> {
self
}
}
impl<S: TexlangState> ExpandedStream<S> {
pub fn unexpanded(&mut self) -> &mut UnexpandedStream<S> {
&mut self.0
}
pub fn expand_once(&mut self) -> Result<bool, Box<error::Error>> {
stream::expand_once(&mut self.unexpanded().0)
}
}
impl<S: TexlangState> TokenStream for ExpandedStream<S> {
type S = S;
#[inline]
fn next(&mut self) -> Result<Option<Token>, Box<error::Error>> {
stream::next_expanded(&mut self.unexpanded().0)
}
#[inline]
fn peek(&mut self) -> Result<Option<&Token>, Box<error::Error>> {
stream::peek_expanded(&mut self.unexpanded().0)
}
#[inline]
fn vm(&self) -> &vm::VM<Self::S> {
&self.0 .0
}
}
#[repr(transparent)]
pub struct UnexpandedStream<S>(vm::VM<S>);
impl<S: TexlangState> TokenStream for UnexpandedStream<S> {
type S = S;
#[inline]
fn next(&mut self) -> Result<Option<Token>, Box<error::Error>> {
stream::next_unexpanded(&mut self.0)
}
#[inline]
fn peek(&mut self) -> Result<Option<&Token>, Box<error::Error>> {
stream::peek_unexpanded(&mut self.0)
}
#[inline]
fn vm(&self) -> &vm::VM<S> {
&self.0
}
}
#[repr(transparent)]
pub struct ExpansionInput<S>(ExpandedStream<S>);
impl<S> std::convert::AsMut<ExpandedStream<S>> for ExpansionInput<S> {
fn as_mut(&mut self) -> &mut ExpandedStream<S> {
&mut self.0
}
}
impl<S: TexlangState> TokenStream for ExpansionInput<S> {
type S = S;
fn next(&mut self) -> Result<Option<Token>, Box<error::Error>> {
self.0.next()
}
fn peek(&mut self) -> Result<Option<&Token>, Box<error::Error>> {
self.0.peek()
}
fn vm(&self) -> &vm::VM<Self::S> {
self.0.vm()
}
}
impl<S> ExpansionInput<S> {
#[inline]
pub fn new(vm: &mut vm::VM<S>) -> &mut ExpansionInput<S> {
unsafe { &mut *(vm as *mut vm::VM<S> as *mut ExpansionInput<S>) }
}
}
impl<S: TexlangState> ExpansionInput<S> {
#[inline]
pub fn push_source(
&mut self,
token: Token,
file_name: PathBuf,
source_code: String,
) -> Result<(), Box<error::Error>> {
self.0
.0
.0
.internal
.push_source(Some(token), file_name, source_code)
}
pub fn push_string_tokens(&mut self, token: Token, s: &str) {
let trace_key = token.trace_key();
for c in s.chars().rev() {
let token = match c {
' ' => token::Token::new_space(' ', trace_key),
_ => token::Token::new_letter(c, trace_key),
};
self.expansions_mut().push(token);
}
}
}
impl<S> ExpansionInput<S> {
#[inline]
pub fn unexpanded(&mut self) -> &mut UnexpandedStream<S> {
&mut self.0 .0
}
#[inline]
pub fn expanded(&mut self) -> &mut ExpandedStream<S> {
&mut self.0
}
#[inline]
pub fn push_expansion(&mut self, expansion: &[Token]) {
self.0 .0 .0.internal.push_expansion(expansion)
}
#[inline]
pub fn expansions(&self) -> &Vec<Token> {
self.0 .0 .0.internal.expansions()
}
#[inline]
pub fn expansions_mut(&mut self) -> &mut Vec<Token> {
self.0 .0 .0.internal.expansions_mut()
}
pub fn checkout_token_buffer(&mut self) -> Vec<Token> {
self.0
.0
.0
.internal
.token_buffers
.pop()
.unwrap_or_default()
.0
}
pub fn return_token_buffer(&mut self, mut token_buffer: Vec<Token>) {
token_buffer.clear();
self.0
.0
.0
.internal
.token_buffers
.push(super::TokenBuffer(token_buffer))
}
}
#[repr(transparent)]
pub struct ExecutionInput<S>(ExpandedStream<S>);
impl<S> std::convert::AsMut<ExpandedStream<S>> for ExecutionInput<S> {
fn as_mut(&mut self) -> &mut ExpandedStream<S> {
&mut self.0
}
}
impl<S: TexlangState> TokenStream for ExecutionInput<S> {
type S = S;
fn next(&mut self) -> Result<Option<Token>, Box<error::Error>> {
self.0.next()
}
fn peek(&mut self) -> Result<Option<&Token>, Box<error::Error>> {
self.0.peek()
}
fn vm(&self) -> &vm::VM<Self::S> {
self.0.vm()
}
}
impl<S> ExecutionInput<S> {
#[inline]
pub fn new(state: &mut vm::VM<S>) -> &mut ExecutionInput<S> {
unsafe { &mut *(state as *mut vm::VM<S> as *mut ExecutionInput<S>) }
}
#[inline]
pub fn unexpanded(&mut self) -> &mut UnexpandedStream<S> {
&mut self.0 .0
}
#[inline]
pub fn commands_map_mut(&mut self) -> &mut command::Map<S> {
&mut self.0 .0 .0.commands_map
}
#[inline]
pub fn state_mut(&mut self) -> &mut S {
&mut self.0 .0 .0.state
}
pub fn state_mut_and_cs_name_interner(&mut self) -> (&mut S, &token::CsNameInterner) {
(
&mut self.0 .0 .0.state,
&self.0 .0 .0.internal.cs_name_interner,
)
}
pub fn begin_group(&mut self) {
self.0 .0 .0.begin_group()
}
pub fn end_group(&mut self, token: Token) -> Result<(), Box<error::Error>> {
self.0 .0 .0.end_group(token)
}
pub(crate) fn groups(&mut self) -> &mut [variable::SaveStackElement<S>] {
&mut self.0 .0 .0.internal.groups
}
pub(crate) fn current_group_mut(&mut self) -> Option<(&mut variable::SaveStackElement<S>, &S)> {
match self.0 .0 .0.internal.groups.last_mut() {
None => None,
Some(g) => Some((g, &self.0 .0 .0.state)),
}
}
}
#[inline]
unsafe fn launder<'a>(token: &Token) -> &'a Token {
&*(token as *const Token)
}
mod stream {
use super::*;
use crate::token::lexer;
use crate::token::CatCode;
use crate::token::{lexer::CatCodeFn, Value::ControlSequence};
impl<T: TexlangState> CatCodeFn for T {
#[inline]
fn cat_code(&self, c: char) -> crate::token::CatCode {
self.cat_code(c)
}
}
#[inline]
pub fn next_unexpanded<S: TexlangState>(
vm: &mut vm::VM<S>,
) -> Result<Option<Token>, Box<error::Error>> {
if let Some(token) = vm.internal.current_source.expansions.pop() {
return Ok(Some(token));
}
match vm
.internal
.current_source
.root
.next(&vm.state, &mut vm.internal.cs_name_interner)
{
Ok(None) => {}
Ok(Some(token)) => {
return Ok(Some(token));
}
Err(err) => return Err(LexerError::new(vm, err).into()),
}
next_unexpanded_recurse(vm)
}
fn next_unexpanded_recurse<S: TexlangState>(
vm: &mut vm::VM<S>,
) -> Result<Option<Token>, Box<error::Error>> {
if vm.internal.pop_source() {
next_unexpanded(vm)
} else {
Ok(None)
}
}
#[inline]
pub fn peek_unexpanded<S: TexlangState>(
vm: &mut vm::VM<S>,
) -> Result<Option<&Token>, Box<error::Error>> {
if let Some(token) = vm.internal.current_source.expansions.last() {
return Ok(Some(unsafe { launder(token) }));
}
match vm
.internal
.current_source
.root
.next(&vm.state, &mut vm.internal.cs_name_interner)
{
Ok(None) => {}
Ok(Some(token)) => {
vm.internal.current_source.expansions.push(token);
return Ok(vm.internal.current_source.expansions.last());
}
Err(err) => return Err(LexerError::new(vm, err).into()),
}
peek_unexpanded_recurse(vm)
}
fn peek_unexpanded_recurse<S: TexlangState>(
vm: &mut vm::VM<S>,
) -> Result<Option<&Token>, Box<error::Error>> {
if vm.internal.pop_source() {
peek_unexpanded(vm)
} else {
Ok(None)
}
}
pub fn next_expanded<S: TexlangState>(
vm: &mut vm::VM<S>,
) -> Result<Option<Token>, Box<error::Error>> {
let (token, command) = match next_unexpanded(vm)? {
None => return Ok(None),
Some(token) => match token.value() {
ControlSequence(name) => (token, vm.commands_map.get_command(&name)),
_ => return Ok(Some(token)),
},
};
match command {
Some(command::Command::Expansion(command, tag)) => {
let command = *command;
let tag = *tag;
match S::expansion_override_hook(token, ExpansionInput::new(vm), tag) {
Ok(None) => (),
Ok(Some(override_expansion)) => {
return Ok(Some(override_expansion));
}
Err(err) => return Err(convert_command_error(vm, token, err)),
};
let output = match command(token, ExpansionInput::new(vm)) {
Ok(output) => output,
Err(err) => return Err(convert_command_error(vm, token, err)),
};
vm.internal.push_expansion(&output);
next_expanded(vm)
}
Some(command::Command::Macro(command)) => {
let command = command.clone();
if let Err(err) = command.call(token, ExpansionInput::new(vm)) {
return Err(convert_command_error(vm, token, err));
}
next_expanded(vm)
}
_ => Ok(Some(token)),
}
}
pub fn peek_expanded<S: TexlangState>(
vm: &mut vm::VM<S>,
) -> Result<Option<&Token>, Box<error::Error>> {
let (token, command) = match peek_unexpanded(vm)? {
None => return Ok(None),
Some(token) => match token.value() {
ControlSequence(name) => (
unsafe { launder(token) },
vm.commands_map.get_command(&name),
),
_ => return Ok(Some(unsafe { launder(token) })),
},
};
match command {
Some(command::Command::Expansion(command, tag)) => {
let command = *command;
let token = *token;
let tag = *tag;
consume_peek(vm);
match S::expansion_override_hook(token, ExpansionInput::new(vm), tag) {
Ok(None) => (),
Ok(Some(override_expansion)) => {
vm.internal.expansions_mut().push(override_expansion);
return Ok(vm.internal.expansions().last());
}
Err(err) => return Err(convert_command_error(vm, token, err)),
};
let output = match command(token, ExpansionInput::new(vm)) {
Ok(output) => output,
Err(err) => return Err(convert_command_error(vm, token, err)),
};
vm.internal.push_expansion(&output);
peek_expanded(vm)
}
Some(command::Command::Macro(command)) => {
let command = command.clone();
let token = *token;
consume_peek(vm);
if let Err(err) = command.call(token, ExpansionInput::new(vm)) {
return Err(convert_command_error(vm, token, err));
}
peek_expanded(vm)
}
_ => Ok(Some(unsafe { launder(token) })),
}
}
pub fn expand_once<S: TexlangState>(vm: &mut vm::VM<S>) -> Result<bool, Box<error::Error>> {
let (token, command) = match peek_unexpanded(vm)? {
None => return Ok(false),
Some(token) => match token.value() {
ControlSequence(name) => (
unsafe { launder(token) },
vm.commands_map.get_command(&name),
),
_ => return Ok(false),
},
};
match command {
Some(command::Command::Expansion(command, tag)) => {
let command = *command;
let token = *token;
let tag = *tag;
consume_peek(vm);
match S::expansion_override_hook(token, ExpansionInput::new(vm), tag) {
Ok(None) => (),
Ok(Some(override_expansion)) => {
vm.internal.expansions_mut().push(override_expansion);
return Ok(true);
}
Err(err) => return Err(convert_command_error(vm, token, err)),
};
let output = match command(token, ExpansionInput::new(vm)) {
Ok(output) => output,
Err(err) => return Err(convert_command_error(vm, token, err)),
};
vm.internal.push_expansion(&output);
Ok(true)
}
Some(command::Command::Macro(command)) => {
let command = command.clone();
let token = *token;
consume_peek(vm);
if let Err(err) = command.call(token, ExpansionInput::new(vm)) {
return Err(convert_command_error(vm, token, err));
}
Ok(true)
}
_ => Ok(false),
}
}
#[inline]
pub fn consume_peek<S>(vm: &mut vm::VM<S>) {
vm.internal.current_source.expansions.pop();
}
use crate::error::Error;
fn convert_command_error<S: TexlangState>(
vm: &mut vm::VM<S>,
token: Token,
err: Box<error::Error>,
) -> Box<Error> {
Error::new_propagated(vm, error::PropagationContext::Expansion, token, err)
}
#[derive(Debug)]
enum LexerError {
InvalidCharacter(char, trace::SourceCodeTrace),
EmptyControlSequence(trace::SourceCodeTrace),
}
impl LexerError {
fn new<S>(vm: &vm::VM<S>, err: lexer::Error) -> LexerError {
match err {
lexer::Error::InvalidCharacter(c, key) => {
LexerError::InvalidCharacter(c, vm.trace(Token::new_other(c, key)))
}
lexer::Error::EmptyControlSequence(key) => {
LexerError::EmptyControlSequence(vm.trace(Token::new_other(' ', key)))
}
}
}
}
impl error::TexError for LexerError {
fn kind(&self) -> error::Kind {
match self {
LexerError::InvalidCharacter(_, key) => error::Kind::Token(key),
LexerError::EmptyControlSequence(key) => error::Kind::EndOfInput(key),
}
}
fn title(&self) -> String {
match self {
LexerError::InvalidCharacter(c, _) => {
format!["input contains a character {} (Unicode code point {}) with category code {}", *c, *c as u32, CatCode::Invalid]
}
LexerError::EmptyControlSequence(_) => {
format![
"unexpected end of file after a token with category code {}",
CatCode::Escape
]
}
}
}
fn source_annotation(&self) -> String {
match self {
LexerError::InvalidCharacter(_, _) => "invalid character",
LexerError::EmptyControlSequence(_) => "file ended after this token",
}
.into()
}
fn notes(&self) -> Vec<error::display::Note> {
match self {
LexerError::InvalidCharacter(_, _) => vec![
format!["characters with category code {} cannot appear in the input", CatCode::Invalid].into()
],
LexerError::EmptyControlSequence(_) => vec![
"escape tokens start a control sequence and must be followed by at least one character".into(),
],
}
}
}
}