use crate::{
bibs::BibData,
buffer::{BufTy, GlobalBuffer},
char_info::{LexClass, CHAR_WIDTH},
cite::CiteInfo,
entries::{EntryData, ENT_STR_SIZE},
global::{GlobalData, GLOB_STR_SIZE},
hash,
hash::{BstBuiltin, BstFn, HashData},
log::{
brace_lvl_one_letters_complaint, braces_unbalanced_complaint,
bst_1print_string_size_exceeded, bst_2print_string_size_exceeded,
bst_cant_mess_with_entries_print, output_bbl_line, print_a_pool_str, print_confusion,
print_fn_class,
},
other::WizOp,
pool::{Checkpoint, StrNumber, StringPool, MAX_PRINT_LINE, MIN_PRINT_LINE},
scan::{
check_brace_level, decr_brace_level, enough_text_chars, name_scan_for_and,
von_name_ends_and_last_name_starts_stuff, von_token_found,
},
ASCIICode, Bibtex, BibtexError, BufPointer, GlobalItems, HashPointer,
};
use std::ops::{Deref, DerefMut, Index};
#[derive(Copy, Clone, Debug, PartialEq)]
pub(crate) enum ControlSeq {
LowerI,
LowerJ,
LowerAA,
UpperAA,
LowerAE,
UpperAE,
LowerOE,
UpperOE,
LowerO,
UpperO,
LowerL,
UpperL,
LowerSS,
}
#[derive(Copy, Clone, PartialEq, Eq)]
pub(crate) enum StkType {
Integer,
String,
Function,
Missing,
Illegal,
}
#[derive(Copy, Clone, Debug, PartialEq)]
pub(crate) enum ExecVal {
Integer(i64),
String(StrNumber),
Function(HashPointer<hash::BstFn>),
Missing(StrNumber),
Illegal,
}
impl ExecVal {
pub fn ty(&self) -> StkType {
match self {
ExecVal::Integer(_) => StkType::Integer,
ExecVal::String(_) => StkType::String,
ExecVal::Function(_) => StkType::Function,
ExecVal::Missing(_) => StkType::Missing,
ExecVal::Illegal => StkType::Illegal,
}
}
}
pub(crate) struct ExecCtx<'a, 'bib, 'cbs> {
glbl_ctx: &'a mut Bibtex<'bib, 'cbs>,
pub default: HashPointer<hash::BstFn>,
pub(crate) lit_stack: Vec<ExecVal>,
pub mess_with_entries: bool,
pub checkpoint: Checkpoint,
}
impl<'a, 'bib, 'cbs> ExecCtx<'a, 'bib, 'cbs> {
pub(crate) fn new(glbl_ctx: &'a mut Bibtex<'bib, 'cbs>) -> ExecCtx<'a, 'bib, 'cbs> {
ExecCtx {
glbl_ctx,
default: HashPointer::default(),
lit_stack: Vec::new(),
mess_with_entries: false,
checkpoint: Checkpoint::default(),
}
}
pub(crate) fn push_stack(&mut self, val: ExecVal) {
self.lit_stack.push(val);
}
pub(crate) fn pop_stack(
&mut self,
pool: &mut StringPool,
cites: &CiteInfo,
) -> Result<ExecVal, BibtexError> {
if let Some(pop) = self.lit_stack.pop() {
if let ExecVal::String(str) = pop {
if self.checkpoint.is_before(str) && !pool.remove_last_str(str) {
self.write_logs("Nontop top of string stack");
print_confusion(self);
return Err(BibtexError::Fatal);
}
}
Ok(pop)
} else {
self.write_logs("You can't pop an empty literal stack");
bst_ex_warn_print(self, pool, cites)?;
Ok(ExecVal::Illegal)
}
}
}
impl<'bib, 'cbs> Deref for ExecCtx<'_, 'bib, 'cbs> {
type Target = Bibtex<'bib, 'cbs>;
fn deref(&self) -> &Self::Target {
self.glbl_ctx
}
}
impl DerefMut for ExecCtx<'_, '_, '_> {
fn deref_mut(&mut self) -> &mut Self::Target {
self.glbl_ctx
}
}
pub(crate) fn print_lit(
ctx: &mut Bibtex<'_, '_>,
pool: &StringPool,
hash: &HashData,
val: ExecVal,
) -> Result<(), BibtexError> {
match val {
ExecVal::Integer(val) => {
ctx.write_logs(&format!("{val}\n"));
}
ExecVal::String(str) => {
print_a_pool_str(ctx, str, pool)?;
ctx.write_logs("\n");
}
ExecVal::Function(f) => {
print_a_pool_str(ctx, hash.get(f).text(), pool)?;
ctx.write_logs("\n");
}
ExecVal::Missing(s) => {
print_a_pool_str(ctx, s, pool)?;
ctx.write_logs("\n");
}
ExecVal::Illegal => {
illegal_literal_confusion(ctx);
return Err(BibtexError::Fatal);
}
}
Ok(())
}
pub(crate) fn print_stk_lit(
ctx: &mut Bibtex<'_, '_>,
pool: &StringPool,
hash: &HashData,
val: ExecVal,
) -> Result<(), BibtexError> {
match val {
ExecVal::Integer(val) => ctx.write_logs(&format!("{val} is an integer literal")),
ExecVal::String(str) => {
ctx.write_logs("\"");
print_a_pool_str(ctx, str, pool)?;
ctx.write_logs("\" is a string literal");
}
ExecVal::Function(f) => {
ctx.write_logs("`");
print_a_pool_str(ctx, hash.get(f).text(), pool)?;
ctx.write_logs("` is a function literal");
}
ExecVal::Missing(s) => {
ctx.write_logs("`");
print_a_pool_str(ctx, s, pool)?;
ctx.write_logs("` is a missing field");
}
ExecVal::Illegal => {
illegal_literal_confusion(ctx);
return Err(BibtexError::Fatal);
}
}
Ok(())
}
pub(crate) fn print_wrong_stk_lit(
ctx: &mut ExecCtx<'_, '_, '_>,
pool: &StringPool,
hash: &HashData,
cites: &CiteInfo,
val: ExecVal,
typ2: StkType,
) -> Result<(), BibtexError> {
match val {
ExecVal::Illegal => Ok(()),
_ => {
print_stk_lit(ctx, pool, hash, val)?;
match typ2 {
StkType::Integer => ctx.write_logs(", not an integer,"),
StkType::String => ctx.write_logs(", not a string,"),
StkType::Function => ctx.write_logs(", not a function,"),
StkType::Missing | StkType::Illegal => {
illegal_literal_confusion(ctx);
return Err(BibtexError::Fatal);
}
};
bst_ex_warn_print(ctx, pool, cites)
}
}
}
pub(crate) fn bst_ex_warn_print(
ctx: &mut ExecCtx<'_, '_, '_>,
pool: &StringPool,
cites: &CiteInfo,
) -> Result<(), BibtexError> {
if ctx.mess_with_entries {
ctx.write_logs(" for entry ");
print_a_pool_str(ctx, cites.get_cite(cites.ptr()), pool)?;
}
ctx.write_logs("\nwhile executing-");
bst_ln_num_print(ctx, pool)?;
ctx.mark_error();
Ok(())
}
pub(crate) fn bst_ln_num_print(
ctx: &mut Bibtex<'_, '_>,
pool: &StringPool,
) -> Result<(), BibtexError> {
ctx.write_logs(&format!(
"--line {} of file ",
ctx.bst.as_ref().unwrap().line
));
print_bst_name(ctx, pool, ctx.bst.as_ref().unwrap().name)
}
pub(crate) fn print_bst_name(
ctx: &mut Bibtex<'_, '_>,
pool: &StringPool,
name: StrNumber,
) -> Result<(), BibtexError> {
print_a_pool_str(ctx, name, pool)?;
ctx.write_logs(".bst\n");
Ok(())
}
pub fn illegal_literal_confusion(ctx: &mut Bibtex<'_, '_>) {
ctx.write_logs("Illegal literal type");
print_confusion(ctx);
}
fn pop_top_and_print(
ctx: &mut ExecCtx<'_, '_, '_>,
pool: &mut StringPool,
hash: &HashData,
cites: &CiteInfo,
) -> Result<(), BibtexError> {
ctx.pop_stack(pool, cites).and_then(|val| {
if let ExecVal::Illegal = val {
ctx.write_logs("Empty literal\n");
Ok(())
} else {
print_lit(ctx, pool, hash, val)
}
})
}
fn pop_whole_stack(
ctx: &mut ExecCtx<'_, '_, '_>,
pool: &mut StringPool,
hash: &HashData,
cites: &CiteInfo,
) -> Result<(), BibtexError> {
while !ctx.lit_stack.is_empty() {
pop_top_and_print(ctx, pool, hash, cites)?;
}
Ok(())
}
pub fn skip_brace_level_greater_than_one(str: &[ASCIICode], brace_level: &mut i32) -> usize {
let mut pos = 0;
while *brace_level > 1 && pos < str.len() {
if str[pos] == b'}' {
*brace_level -= 1;
} else if str[pos] == b'{' {
*brace_level += 1;
}
pos += 1;
}
pos
}
#[allow(clippy::too_many_arguments)]
pub(crate) fn figure_out_the_formatted_name(
ctx: &mut ExecCtx<'_, '_, '_>,
buffers: &mut GlobalBuffer,
pool: &StringPool,
cites: &CiteInfo,
s1: StrNumber,
first_start: BufPointer,
first_end: BufPointer,
last_end: BufPointer,
von_start: BufPointer,
von_end: BufPointer,
name_bf_ptr: &mut BufPointer,
name_bf_xptr: &mut BufPointer,
jr_end: BufPointer,
brace_level: &mut i32,
) -> Result<(), BibtexError> {
let mut old_idx;
let mut inner_brace_level = 0;
let str = pool.get_str(s1);
let mut idx = 0;
buffers.set_offset(BufTy::Ex, 1, 0);
while idx < str.len() {
if str[idx] == b'{' {
inner_brace_level += 1;
idx += 1;
old_idx = idx;
let mut alpha_found = false;
let mut double_letter = false;
let mut end_of_group = false;
let mut to_be_written = true;
let mut cur_token = 0;
let mut last_token = 0;
while !end_of_group && idx < str.len() {
if LexClass::of(str[idx]) == LexClass::Alpha {
idx += 1;
if alpha_found {
brace_lvl_one_letters_complaint(ctx, pool, cites, s1)?;
to_be_written = false;
} else {
match str[idx - 1] {
b'f' | b'F' => {
cur_token = first_start;
last_token = first_end;
if cur_token == last_token {
to_be_written = false;
}
if str[idx] == b'f' || str[idx] == b'F' {
double_letter = true;
}
}
b'v' | b'V' => {
cur_token = von_start;
last_token = von_end;
if cur_token == last_token {
to_be_written = false;
}
if str[idx] == b'v' || str[idx] == b'V' {
double_letter = true;
}
}
b'l' | b'L' => {
cur_token = von_end;
last_token = last_end;
if cur_token == last_token {
to_be_written = false;
}
if str[idx] == b'l' || str[idx] == b'L' {
double_letter = true;
}
}
b'j' | b'J' => {
cur_token = last_end;
last_token = jr_end;
if cur_token == last_token {
to_be_written = false;
}
if str[idx] == b'j' || str[idx] == b'J' {
double_letter = true;
}
}
_ => {
brace_lvl_one_letters_complaint(ctx, pool, cites, s1)?;
to_be_written = false;
break;
}
}
if double_letter {
idx += 1;
}
}
alpha_found = true;
} else if str[idx] == b'}' {
inner_brace_level -= 1;
idx += 1;
end_of_group = true;
} else if str[idx] == b'{' {
inner_brace_level += 1;
idx =
skip_brace_level_greater_than_one(&str[idx + 1..], &mut inner_brace_level)
+ idx;
idx += 1;
} else {
idx += 1;
}
}
if end_of_group && to_be_written {
let buf_ptr = buffers.offset(BufTy::Ex, 1);
idx = old_idx;
inner_brace_level = 1;
while inner_brace_level > 0 {
if LexClass::of(str[idx]) == LexClass::Alpha && inner_brace_level == 1 {
idx += 1;
if double_letter {
idx += 1;
}
let mut use_default = true;
let mut sp_xptr2 = idx;
if str[idx] == b'{' {
use_default = false;
inner_brace_level += 1;
idx += 1;
old_idx = idx;
idx = skip_brace_level_greater_than_one(
&str[idx..],
&mut inner_brace_level,
) + idx;
sp_xptr2 = idx - 1;
}
while cur_token < last_token {
*name_bf_ptr = buffers.name_tok(cur_token);
*name_bf_xptr = buffers.name_tok(cur_token + 1);
if double_letter {
if buffers.init(BufTy::Ex) + (*name_bf_xptr - *name_bf_ptr)
> buffers.len()
{
buffers.grow_all();
}
let ptr = buffers.offset(BufTy::Ex, 1);
let len = *name_bf_xptr - *name_bf_ptr;
buffers.copy_within(BufTy::Sv, BufTy::Ex, *name_bf_ptr, ptr, len);
buffers.set_offset(BufTy::Ex, 1, ptr + len);
*name_bf_ptr += len;
} else {
while *name_bf_ptr < *name_bf_xptr {
if LexClass::of(buffers.at(BufTy::Sv, *name_bf_ptr))
== LexClass::Alpha
{
if buffers.offset(BufTy::Ex, 1) == buffers.len() {
buffers.grow_all();
}
buffers.set_at(
BufTy::Ex,
buffers.offset(BufTy::Ex, 1),
buffers.at(BufTy::Sv, *name_bf_ptr),
);
buffers.set_offset(
BufTy::Ex,
1,
buffers.offset(BufTy::Ex, 1) + 1,
);
break;
} else if *name_bf_ptr + 1 < *name_bf_xptr
&& buffers.at(BufTy::Sv, *name_bf_ptr) == b'{'
&& buffers.at(BufTy::Sv, *name_bf_ptr + 1) == b'\\'
{
if buffers.offset(BufTy::Ex, 1) + 2 > buffers.len() {
buffers.grow_all();
}
let offset = buffers.offset(BufTy::Ex, 1);
buffers.set_at(BufTy::Ex, offset, b'{');
buffers.set_at(BufTy::Ex, offset + 1, b'\\');
buffers.set_offset(BufTy::Ex, 1, offset + 2);
*name_bf_ptr += 2;
let mut nm_brace_level = 1;
while *name_bf_ptr < *name_bf_xptr && nm_brace_level > 0 {
if buffers.at(BufTy::Sv, *name_bf_ptr) == b'}' {
nm_brace_level -= 1;
} else if buffers.at(BufTy::Sv, *name_bf_ptr) == b'{' {
nm_brace_level += 1;
}
if buffers.offset(BufTy::Ex, 1) == buffers.len() {
buffers.grow_all();
}
buffers.set_at(
BufTy::Ex,
buffers.offset(BufTy::Ex, 1),
buffers.at(BufTy::Sv, *name_bf_ptr),
);
buffers.set_offset(
BufTy::Ex,
1,
buffers.offset(BufTy::Ex, 1) + 1,
);
*name_bf_ptr += 1;
}
break;
}
*name_bf_ptr += 1;
}
}
cur_token += 1;
if cur_token < last_token {
if use_default {
if !double_letter {
if buffers.offset(BufTy::Ex, 1) == buffers.len() {
buffers.grow_all();
}
buffers.set_at(
BufTy::Ex,
buffers.offset(BufTy::Ex, 1),
b'.',
);
buffers.set_offset(
BufTy::Ex,
1,
buffers.offset(BufTy::Ex, 1) + 1,
);
}
if buffers.offset(BufTy::Ex, 1) == buffers.len() {
buffers.grow_all();
}
let c = if LexClass::of(buffers.at(BufTy::NameSep, cur_token))
== LexClass::Sep
{
buffers.at(BufTy::NameSep, cur_token)
} else if cur_token == last_token - 1
|| (!enough_text_chars(buffers, 3, buf_ptr, brace_level))
{
b'~'
} else {
b' '
};
buffers.set_at(BufTy::Ex, buffers.offset(BufTy::Ex, 1), c);
buffers.set_offset(
BufTy::Ex,
1,
buffers.offset(BufTy::Ex, 1) + 1,
);
} else {
if buffers.offset(BufTy::Ex, 1) + (sp_xptr2 - old_idx)
> buffers.len()
{
buffers.grow_all();
}
let ptr = buffers.offset(BufTy::Ex, 1);
let tmp_str = &str[old_idx..sp_xptr2];
buffers.copy_from(BufTy::Ex, ptr, tmp_str);
buffers.set_offset(BufTy::Ex, 1, ptr + tmp_str.len());
idx = sp_xptr2;
}
}
}
if !use_default {
idx = sp_xptr2 + 1;
}
} else if str[idx] == b'}' {
inner_brace_level -= 1;
idx += 1;
if inner_brace_level > 0 {
if buffers.offset(BufTy::Ex, 1) == buffers.len() {
buffers.grow_all();
}
buffers.set_at(BufTy::Ex, buffers.offset(BufTy::Ex, 1), b'}');
buffers.set_offset(BufTy::Ex, 1, buffers.offset(BufTy::Ex, 1) + 1);
}
} else if str[idx] == b'{' {
inner_brace_level += 1;
idx += 1;
if buffers.offset(BufTy::Ex, 1) == buffers.len() {
buffers.grow_all();
}
buffers.set_at(BufTy::Ex, buffers.offset(BufTy::Ex, 1), b'{');
buffers.set_offset(BufTy::Ex, 1, buffers.offset(BufTy::Ex, 1) + 1);
} else {
if buffers.offset(BufTy::Ex, 1) == buffers.len() {
buffers.grow_all();
}
buffers.set_at(BufTy::Ex, buffers.offset(BufTy::Ex, 1), str[idx]);
buffers.set_offset(BufTy::Ex, 1, buffers.offset(BufTy::Ex, 1) + 1);
idx += 1;
}
}
if buffers.offset(BufTy::Ex, 1) > 0
&& buffers.at(BufTy::Ex, buffers.offset(BufTy::Ex, 1) - 1) == b'~'
{
buffers.set_offset(BufTy::Ex, 1, buffers.offset(BufTy::Ex, 1) - 1);
if buffers.at(BufTy::Ex, buffers.offset(BufTy::Ex, 1) - 1) == b'~' {
} else if !enough_text_chars(buffers, 3, buf_ptr, brace_level) {
buffers.set_offset(BufTy::Ex, 1, buffers.offset(BufTy::Ex, 1) + 1);
} else {
buffers.set_at(BufTy::Ex, buffers.offset(BufTy::Ex, 1), b' ');
buffers.set_offset(BufTy::Ex, 1, buffers.offset(BufTy::Ex, 1) + 1);
}
}
}
} else if str[idx] == b'}' {
braces_unbalanced_complaint(ctx, pool, cites, s1)?;
idx += 1;
} else {
if buffers.offset(BufTy::Ex, 1) == buffers.len() {
buffers.grow_all();
}
buffers.set_at(BufTy::Ex, buffers.offset(BufTy::Ex, 1), str[idx]);
buffers.set_offset(BufTy::Ex, 1, buffers.offset(BufTy::Ex, 1) + 1);
idx += 1;
}
}
if inner_brace_level > 0 {
braces_unbalanced_complaint(ctx, pool, cites, s1)?;
}
buffers.set_init(BufTy::Ex, buffers.offset(BufTy::Ex, 1));
Ok(())
}
pub(crate) fn add_buf_pool(pool: &StringPool, buffers: &mut GlobalBuffer, str: StrNumber) {
let str = pool.get_str(str);
if buffers.init(BufTy::Ex) + str.len() > buffers.len() {
buffers.grow_all();
}
let start = buffers.init(BufTy::Ex);
buffers.copy_from(BufTy::Ex, start, str);
buffers.set_offset(BufTy::Ex, 1, start + str.len());
buffers.set_init(BufTy::Ex, start + str.len());
}
pub(crate) fn add_out_pool(
ctx: &mut Bibtex<'_, '_>,
buffers: &mut GlobalBuffer,
pool: &StringPool,
str: StrNumber,
) {
let str = pool.get_str(str);
while buffers.init(BufTy::Out) + str.len() > buffers.len() {
buffers.grow_all();
}
let out_offset = buffers.init(BufTy::Out);
buffers.copy_from(BufTy::Out, out_offset, str);
buffers.set_init(BufTy::Out, out_offset + str.len());
let mut unbreakable_tail = false;
while buffers.init(BufTy::Out) > MAX_PRINT_LINE && !unbreakable_tail {
let end_ptr = buffers.init(BufTy::Out);
let mut out_offset = MAX_PRINT_LINE;
let mut break_pt_found = false;
while LexClass::of(buffers.at(BufTy::Out, out_offset)) != LexClass::Whitespace
&& out_offset >= MIN_PRINT_LINE
{
out_offset -= 1;
}
if out_offset == MIN_PRINT_LINE - 1 {
out_offset = MAX_PRINT_LINE + 1;
while out_offset < end_ptr {
if LexClass::of(buffers.at(BufTy::Out, out_offset)) != LexClass::Whitespace {
out_offset += 1;
} else {
break;
}
}
if out_offset == end_ptr {
unbreakable_tail = true;
} else {
break_pt_found = true;
while out_offset + 1 < end_ptr {
if LexClass::of(buffers.at(BufTy::Out, out_offset + 1)) == LexClass::Whitespace
{
out_offset += 1;
} else {
break;
}
}
}
} else {
break_pt_found = true;
}
if break_pt_found {
buffers.set_init(BufTy::Out, out_offset);
let break_ptr = buffers.init(BufTy::Out) + 1;
output_bbl_line(ctx, buffers);
buffers.set_at(BufTy::Out, 0, b' ');
buffers.set_at(BufTy::Out, 1, b' ');
let len = end_ptr - break_ptr;
buffers.copy_within(BufTy::Out, BufTy::Out, break_ptr, 2, len);
buffers.set_init(BufTy::Out, len + 2);
}
}
}
pub(crate) fn check_command_execution(
ctx: &mut ExecCtx<'_, '_, '_>,
pool: &mut StringPool,
hash: &HashData,
cites: &CiteInfo,
) -> Result<(), BibtexError> {
if !ctx.lit_stack.is_empty() {
let msg = format!("ptr={}, stack=\n", ctx.lit_stack.len());
ctx.write_logs(&msg);
pop_whole_stack(ctx, pool, hash, cites)?;
ctx.write_logs("---the literal stack isn't empty");
bst_ex_warn_print(ctx, pool, cites)?;
}
if !pool.is_at(ctx.checkpoint) {
ctx.write_logs("Nonempty empty string stack");
print_confusion(ctx);
return Err(BibtexError::Fatal);
}
Ok(())
}
fn add_pool_buf_and_push(
ctx: &mut ExecCtx<'_, '_, '_>,
buffers: &mut GlobalBuffer,
pool: &mut StringPool,
) -> Result<(), BibtexError> {
buffers.set_offset(BufTy::Ex, 1, buffers.init(BufTy::Ex));
let str = &buffers.buffer(BufTy::Ex)[0..buffers.init(BufTy::Ex)];
let val = ExecVal::String(pool.add_string(str));
ctx.push_stack(val);
Ok(())
}
fn interp_eq(
ctx: &mut ExecCtx<'_, '_, '_>,
pool: &mut StringPool,
hash: &HashData,
cites: &CiteInfo,
) -> Result<(), BibtexError> {
let pop1 = ctx.pop_stack(pool, cites)?;
let pop2 = ctx.pop_stack(pool, cites)?;
match (pop1, pop2) {
(ExecVal::Integer(i1), ExecVal::Integer(i2)) => {
ctx.push_stack(ExecVal::Integer((i1 == i2) as i64));
}
(ExecVal::String(s1), ExecVal::String(s2)) => {
ctx.push_stack(ExecVal::Integer(
(pool.get_str(s1) == pool.get_str(s2)) as i64,
));
}
_ if pop1.ty() != pop2.ty() => {
if pop1.ty() != StkType::Illegal && pop2.ty() != StkType::Illegal {
print_stk_lit(ctx, pool, hash, pop1)?;
ctx.write_logs(", ");
print_stk_lit(ctx, pool, hash, pop2)?;
ctx.write_logs("\n---they aren't the same literal types");
bst_ex_warn_print(ctx, pool, cites)?;
}
ctx.push_stack(ExecVal::Integer(0));
}
_ => {
if pop1.ty() != StkType::Illegal {
print_stk_lit(ctx, pool, hash, pop1)?;
ctx.write_logs(", not an integer or a string,");
bst_ex_warn_print(ctx, pool, cites)?;
}
ctx.push_stack(ExecVal::Integer(0))
}
}
Ok(())
}
fn interp_gt(
ctx: &mut ExecCtx<'_, '_, '_>,
pool: &mut StringPool,
hash: &HashData,
cites: &CiteInfo,
) -> Result<(), BibtexError> {
let pop1 = ctx.pop_stack(pool, cites)?;
let pop2 = ctx.pop_stack(pool, cites)?;
match (pop1, pop2) {
(ExecVal::Integer(i1), ExecVal::Integer(i2)) => {
ctx.push_stack(ExecVal::Integer((i2 > i1) as i64));
}
(ExecVal::Integer(_), _) => {
print_wrong_stk_lit(ctx, pool, hash, cites, pop2, StkType::Integer)?;
ctx.push_stack(ExecVal::Integer(0));
}
(_, _) => {
print_wrong_stk_lit(ctx, pool, hash, cites, pop1, StkType::Integer)?;
ctx.push_stack(ExecVal::Integer(0));
}
}
Ok(())
}
fn interp_lt(
ctx: &mut ExecCtx<'_, '_, '_>,
pool: &mut StringPool,
hash: &HashData,
cites: &CiteInfo,
) -> Result<(), BibtexError> {
let pop1 = ctx.pop_stack(pool, cites)?;
let pop2 = ctx.pop_stack(pool, cites)?;
match (pop1, pop2) {
(ExecVal::Integer(i1), ExecVal::Integer(i2)) => {
ctx.push_stack(ExecVal::Integer((i2 < i1) as i64));
}
(ExecVal::Integer(_), _) => {
print_wrong_stk_lit(ctx, pool, hash, cites, pop2, StkType::Integer)?;
ctx.push_stack(ExecVal::Integer(0));
}
(_, _) => {
print_wrong_stk_lit(ctx, pool, hash, cites, pop1, StkType::Integer)?;
ctx.push_stack(ExecVal::Integer(0));
}
}
Ok(())
}
fn interp_plus(
ctx: &mut ExecCtx<'_, '_, '_>,
pool: &mut StringPool,
hash: &HashData,
cites: &CiteInfo,
) -> Result<(), BibtexError> {
let pop1 = ctx.pop_stack(pool, cites)?;
let pop2 = ctx.pop_stack(pool, cites)?;
match (pop1, pop2) {
(ExecVal::Integer(i1), ExecVal::Integer(i2)) => {
ctx.push_stack(ExecVal::Integer(i2 + i1));
}
(ExecVal::Integer(_), _) => {
print_wrong_stk_lit(ctx, pool, hash, cites, pop2, StkType::Integer)?;
ctx.push_stack(ExecVal::Integer(0));
}
(_, _) => {
print_wrong_stk_lit(ctx, pool, hash, cites, pop1, StkType::Integer)?;
ctx.push_stack(ExecVal::Integer(0));
}
}
Ok(())
}
fn interp_minus(
ctx: &mut ExecCtx<'_, '_, '_>,
pool: &mut StringPool,
hash: &HashData,
cites: &CiteInfo,
) -> Result<(), BibtexError> {
let pop1 = ctx.pop_stack(pool, cites)?;
let pop2 = ctx.pop_stack(pool, cites)?;
match (pop1, pop2) {
(ExecVal::Integer(i1), ExecVal::Integer(i2)) => {
ctx.push_stack(ExecVal::Integer(i2 - i1));
}
(ExecVal::Integer(_), _) => {
print_wrong_stk_lit(ctx, pool, hash, cites, pop2, StkType::Integer)?;
ctx.push_stack(ExecVal::Integer(0));
}
(_, _) => {
print_wrong_stk_lit(ctx, pool, hash, cites, pop1, StkType::Integer)?;
ctx.push_stack(ExecVal::Integer(0));
}
}
Ok(())
}
fn interp_concat(
ctx: &mut ExecCtx<'_, '_, '_>,
pool: &mut StringPool,
hash: &HashData,
cites: &CiteInfo,
) -> Result<(), BibtexError> {
let pop1 = ctx.pop_stack(pool, cites)?;
let pop2 = ctx.pop_stack(pool, cites)?;
let (s1, s2) = match (pop1, pop2) {
(ExecVal::String(s1), ExecVal::String(s2)) => (s1, s2),
(ExecVal::String(_), _) => {
print_wrong_stk_lit(ctx, pool, hash, cites, pop2, StkType::String)?;
ctx.push_stack(ExecVal::String(ctx.s_null));
return Ok(());
}
(_, _) => {
print_wrong_stk_lit(ctx, pool, hash, cites, pop1, StkType::String)?;
ctx.push_stack(ExecVal::String(ctx.s_null));
return Ok(());
}
};
if ctx.checkpoint.is_before(s2) && ctx.checkpoint.is_before(s1) {
let new_len = pool.get_str(s1).len() + pool.get_str(s2).len();
let new = pool.write_str(|cursor| cursor.extend(new_len));
ctx.push_stack(ExecVal::String(new));
} else if ctx.checkpoint.is_before(s2) {
if pool.get_str(s2).is_empty() {
ctx.push_stack(pop1);
} else {
let s2_len = pool.get_str(s2).len();
let new = pool.write_str(|cursor| {
cursor.extend(s2_len);
cursor.append_str(s1);
});
ctx.push_stack(ExecVal::String(new));
}
} else if ctx.checkpoint.is_before(s1) {
let str1 = pool.get_str(s1);
let str2 = pool.get_str(s2);
if str2.is_empty() {
let s1_len = str1.len();
let new = pool.write_str(|cursor| cursor.extend(s1_len));
ctx.push_stack(ExecVal::String(new));
} else if str1.is_empty() {
ctx.push_stack(pop2);
} else {
let s1_len = str1.len();
let s2_len = str2.len();
let new = pool.write_str(|cursor| {
cursor.extend(s1_len + s2_len);
let raw = cursor.bytes();
raw.copy_within(0..s1_len, s2_len);
cursor.insert_str(s2, 0);
});
let val = ExecVal::String(new);
ctx.push_stack(val);
}
} else {
let str1 = pool.get_str(s1);
let str2 = pool.get_str(s2);
if str1.is_empty() {
ctx.push_stack(pop2);
} else if str2.is_empty() {
ctx.push_stack(pop1);
} else {
let new = pool.write_str(|cursor| {
cursor.append_str(s2);
cursor.append_str(s1);
});
let val = ExecVal::String(new);
ctx.push_stack(val);
}
}
Ok(())
}
fn interp_gets(
ctx: &mut ExecCtx<'_, '_, '_>,
pool: &mut StringPool,
hash: &mut HashData,
entries: &mut EntryData,
globals: &mut GlobalData,
cites: &CiteInfo,
) -> Result<(), BibtexError> {
let pop1 = ctx.pop_stack(pool, cites)?;
let pop2 = ctx.pop_stack(pool, cites)?;
let f1 = match pop1 {
ExecVal::Function(f1) => f1,
_ => {
print_wrong_stk_lit(ctx, pool, hash, cites, pop1, StkType::Function)?;
return Ok(());
}
};
match hash.get(f1).extra() {
BstFn::IntEntry(_) | BstFn::StrEntry(_) if !ctx.mess_with_entries => {
bst_cant_mess_with_entries_print(ctx, pool, cites)?;
}
BstFn::IntEntry(entry) => {
if let ExecVal::Integer(i2) = pop2 {
entries.set_int(cites.ptr() * entries.num_ent_ints() + *entry, i2)
} else {
print_wrong_stk_lit(ctx, pool, hash, cites, pop2, StkType::Integer)?;
}
}
BstFn::StrEntry(entry) => {
if let ExecVal::String(s2) = pop2 {
let mut s = pool.get_str(s2);
if s.len() > ENT_STR_SIZE {
bst_1print_string_size_exceeded(ctx);
ctx.write_logs(&format!("{ENT_STR_SIZE}, the entry"));
bst_2print_string_size_exceeded(ctx, pool, cites)?;
s = &s[..ENT_STR_SIZE];
}
entries.set_str(cites.ptr() * entries.num_ent_strs() + *entry, s);
} else {
print_wrong_stk_lit(ctx, pool, hash, cites, pop2, StkType::Integer)?;
}
}
BstFn::IntGlbl(_) => {
if let ExecVal::Integer(i2) = pop2 {
hash.set_extra(f1, BstFn::IntGlbl(i2));
} else {
print_wrong_stk_lit(ctx, pool, hash, cites, pop2, StkType::Integer)?;
}
}
BstFn::StrGlbl(str_ptr) => {
if let ExecVal::String(s2) = pop2 {
if !ctx.checkpoint.is_before(s2) {
globals.set_str_ptr(*str_ptr, s2);
} else {
globals.set_str_ptr(*str_ptr, StrNumber::invalid());
let mut s = pool.get_str(s2);
if s.len() > GLOB_STR_SIZE {
bst_1print_string_size_exceeded(ctx);
ctx.write_logs(&format!("{GLOB_STR_SIZE}, the global"));
bst_2print_string_size_exceeded(ctx, pool, cites)?;
s = &s[..GLOB_STR_SIZE];
}
globals.set_str(*str_ptr, s);
}
} else {
print_wrong_stk_lit(ctx, pool, hash, cites, pop2, StkType::String)?;
}
}
_ => {
ctx.write_logs("You can't assign to type ");
print_fn_class(ctx, hash, f1);
ctx.write_logs(", a nonvariable function class");
bst_ex_warn_print(ctx, pool, cites)?;
}
}
Ok(())
}
fn interp_add_period(
ctx: &mut ExecCtx<'_, '_, '_>,
pool: &mut StringPool,
hash: &HashData,
cites: &CiteInfo,
) -> Result<(), BibtexError> {
let pop1 = ctx.pop_stack(pool, cites)?;
let s1 = match pop1 {
ExecVal::String(s1) => s1,
_ => {
print_wrong_stk_lit(ctx, pool, hash, cites, pop1, StkType::String)?;
ctx.push_stack(ExecVal::String(ctx.s_null));
return Ok(());
}
};
let str = pool.get_str(s1);
if str.is_empty() {
ctx.push_stack(ExecVal::String(ctx.s_null));
return Ok(());
}
let pos = str.iter().copied().rposition(|c| c != b'}').unwrap_or(0);
match str[pos] {
b'.' | b'?' | b'!' => {
if ctx.checkpoint.is_before(s1) {
let s1_len = pool.get_str(s1).len();
let new = pool.write_str(|cursor| cursor.extend(s1_len));
ctx.push_stack(ExecVal::String(new));
} else {
ctx.push_stack(pop1);
}
}
_ => {
let is_bst_str = ctx.checkpoint.is_before(s1);
let s1_len = pool.get_str(s1).len();
let new = pool.write_str(|cursor| {
if is_bst_str {
cursor.extend(s1_len);
} else {
cursor.append_str(s1);
}
cursor.append(b'.');
});
let val = ExecVal::String(new);
ctx.push_stack(val);
}
}
Ok(())
}
fn interp_change_case(
ctx: &mut ExecCtx<'_, '_, '_>,
pool: &mut StringPool,
cites: &CiteInfo,
hash: &HashData,
) -> Result<(), BibtexError> {
#[derive(PartialEq)]
enum ConvTy {
TitleLower,
AllLower,
AllUpper,
Bad,
}
let pop1 = ctx.pop_stack(pool, cites)?;
let pop2 = ctx.pop_stack(pool, cites)?;
match (pop1, pop2) {
(ExecVal::String(s1), ExecVal::String(s2)) => {
let mut prev_colon = false;
let str1 = pool.get_str(s1);
let conv_ty = if str1.len() == 1 {
match str1[0] {
b't' | b'T' => ConvTy::TitleLower,
b'l' | b'L' => ConvTy::AllLower,
b'u' | b'U' => ConvTy::AllUpper,
_ => ConvTy::Bad,
}
} else {
ConvTy::Bad
};
if conv_ty == ConvTy::Bad {
print_a_pool_str(ctx, s1, pool)?;
ctx.write_logs(" is an illegal case-conversion string");
bst_ex_warn_print(ctx, pool, cites)?;
}
let mut scratch = Vec::from(pool.get_str(s2));
let mut brace_level = 0;
let mut idx = 0;
while idx < scratch.len() {
if scratch[idx] == b'{' {
brace_level += 1;
if !(brace_level != 1
|| idx + 4 > scratch.len()
|| scratch[idx + 1] != b'\\'
|| (conv_ty == ConvTy::TitleLower
&& (idx == 0
|| (prev_colon
&& LexClass::of(scratch[idx - 1]) == LexClass::Whitespace))))
{
idx += 1;
while idx < scratch.len() && brace_level > 0 {
idx += 1;
let old_idx = idx;
while idx < scratch.len()
&& LexClass::of(scratch[idx]) == LexClass::Alpha
{
idx += 1;
}
let res = hash.lookup_str::<ControlSeq>(pool, &scratch[old_idx..idx]);
if let Some(loc) = res {
let seq = *hash.get(loc).extra();
match conv_ty {
ConvTy::TitleLower | ConvTy::AllLower => match seq {
ControlSeq::UpperOE
| ControlSeq::UpperAE
| ControlSeq::UpperAA
| ControlSeq::UpperO
| ControlSeq::UpperL => {
scratch[old_idx..idx].make_ascii_lowercase()
}
_ => (),
},
ConvTy::AllUpper => match seq {
ControlSeq::LowerOE
| ControlSeq::LowerAE
| ControlSeq::LowerAA
| ControlSeq::LowerO
| ControlSeq::LowerL => {
scratch[old_idx..idx].make_ascii_uppercase()
}
ControlSeq::LowerI
| ControlSeq::LowerJ
| ControlSeq::LowerSS => {
scratch[old_idx..idx].make_ascii_uppercase();
scratch.copy_within(old_idx..idx, old_idx - 1);
let old_idx = idx - 1;
while idx < scratch.len()
&& LexClass::of(scratch[idx])
== LexClass::Whitespace
{
idx += 1;
}
scratch.copy_within(idx.., old_idx);
scratch.truncate(scratch.len() - idx + old_idx);
idx = old_idx;
}
_ => (),
},
ConvTy::Bad => (),
}
}
let old_idx = idx;
while idx < scratch.len() && brace_level > 0 && scratch[idx] != b'\\' {
match scratch[idx] {
b'{' => brace_level += 1,
b'}' => brace_level -= 1,
_ => (),
}
idx += 1;
}
match conv_ty {
ConvTy::TitleLower | ConvTy::AllLower => {
scratch[old_idx..idx].make_ascii_lowercase()
}
ConvTy::AllUpper => scratch[old_idx..idx].make_ascii_uppercase(),
ConvTy::Bad => (),
}
}
idx -= 1;
}
prev_colon = false;
} else if scratch[idx] == b'}' {
decr_brace_level(ctx, pool, cites, s2, &mut brace_level)?;
prev_colon = false;
} else if brace_level == 0 {
match conv_ty {
ConvTy::TitleLower => {
if idx != 0
&& !(prev_colon
&& LexClass::of(scratch[idx - 1]) == LexClass::Whitespace)
{
scratch[idx].make_ascii_lowercase()
}
if scratch[idx] == b':' {
prev_colon = true;
} else if LexClass::of(scratch[idx]) != LexClass::Whitespace {
prev_colon = false;
}
}
ConvTy::AllLower => scratch[idx].make_ascii_lowercase(),
ConvTy::AllUpper => scratch[idx].make_ascii_uppercase(),
ConvTy::Bad => (),
}
}
idx += 1;
}
check_brace_level(ctx, pool, cites, s2, brace_level)?;
let val = ExecVal::String(pool.add_string(&scratch));
ctx.push_stack(val);
}
(ExecVal::String(_), _) => {
print_wrong_stk_lit(ctx, pool, hash, cites, pop2, StkType::String)?;
ctx.push_stack(ExecVal::String(ctx.s_null));
}
(_, _) => {
print_wrong_stk_lit(ctx, pool, hash, cites, pop1, StkType::String)?;
ctx.push_stack(ExecVal::String(ctx.s_null));
}
}
Ok(())
}
fn interp_chr_to_int(
ctx: &mut ExecCtx<'_, '_, '_>,
pool: &mut StringPool,
hash: &HashData,
cites: &CiteInfo,
) -> Result<(), BibtexError> {
let pop1 = ctx.pop_stack(pool, cites)?;
match pop1 {
ExecVal::String(s1) => {
let str = pool.get_str(s1);
if str.len() != 1 {
ctx.write_logs("\"");
print_a_pool_str(ctx, s1, pool)?;
ctx.write_logs("\" isn't a single character");
bst_ex_warn_print(ctx, pool, cites)?;
ctx.push_stack(ExecVal::Integer(0));
} else {
ctx.push_stack(ExecVal::Integer(str[0] as i64))
}
}
_ => {
print_wrong_stk_lit(ctx, pool, hash, cites, pop1, StkType::String)?;
ctx.push_stack(ExecVal::Integer(0));
}
}
Ok(())
}
fn interp_cite(
ctx: &mut ExecCtx<'_, '_, '_>,
pool: &StringPool,
cites: &CiteInfo,
) -> Result<(), BibtexError> {
if !ctx.mess_with_entries {
bst_cant_mess_with_entries_print(ctx, pool, cites)?;
} else {
ctx.push_stack(ExecVal::String(cites.get_cite(cites.ptr())))
}
Ok(())
}
fn interp_dup(
ctx: &mut ExecCtx<'_, '_, '_>,
pool: &mut StringPool,
cites: &CiteInfo,
) -> Result<(), BibtexError> {
let pop1 = ctx.pop_stack(pool, cites)?;
match pop1 {
ExecVal::String(s1) => {
ctx.push_stack(pop1);
if !ctx.checkpoint.is_before(s1) {
ctx.push_stack(pop1);
} else {
let str_len = pool.get_str(s1).len();
let _ = pool.write_str(|cursor| {
cursor.extend(str_len);
});
let new = pool.write_str(|cursor| {
cursor.append_str(s1);
});
let val = ExecVal::String(new);
ctx.push_stack(val);
}
}
_ => {
ctx.push_stack(pop1);
ctx.push_stack(pop1);
}
}
Ok(())
}
fn interp_empty(
ctx: &mut ExecCtx<'_, '_, '_>,
pool: &mut StringPool,
hash: &HashData,
cites: &CiteInfo,
) -> Result<(), BibtexError> {
let pop1 = ctx.pop_stack(pool, cites)?;
match pop1 {
ExecVal::String(s1) => {
let str = pool.get_str(s1);
let res = str.iter().all(|c| LexClass::of(*c) == LexClass::Whitespace);
ctx.push_stack(ExecVal::Integer(res as i64));
}
ExecVal::Missing(_) => {
ctx.push_stack(ExecVal::Integer(1));
}
ExecVal::Illegal => {
ctx.push_stack(ExecVal::Integer(0));
}
_ => {
print_stk_lit(ctx, pool, hash, pop1)?;
ctx.write_logs(", not a string or missing field,");
bst_ex_warn_print(ctx, pool, cites)?;
ctx.push_stack(ExecVal::Integer(0));
}
}
Ok(())
}
fn interp_format_name(
ctx: &mut ExecCtx<'_, '_, '_>,
pool: &mut StringPool,
buffers: &mut GlobalBuffer,
cites: &CiteInfo,
hash: &HashData,
) -> Result<(), BibtexError> {
let pop1 = ctx.pop_stack(pool, cites)?;
let pop2 = ctx.pop_stack(pool, cites)?;
let pop3 = ctx.pop_stack(pool, cites)?;
let (s1, i2, s3) = match (pop1, pop2, pop3) {
(ExecVal::String(s1), ExecVal::Integer(i2), ExecVal::String(s3)) => (s1, i2, s3),
(ExecVal::String(_), ExecVal::Integer(_), _) => {
print_wrong_stk_lit(ctx, pool, hash, cites, pop3, StkType::String)?;
ctx.push_stack(ExecVal::String(ctx.s_null));
return Ok(());
}
(ExecVal::String(_), _, _) => {
print_wrong_stk_lit(ctx, pool, hash, cites, pop2, StkType::Integer)?;
ctx.push_stack(ExecVal::String(ctx.s_null));
return Ok(());
}
(_, _, _) => {
print_wrong_stk_lit(ctx, pool, hash, cites, pop1, StkType::String)?;
ctx.push_stack(ExecVal::String(ctx.s_null));
return Ok(());
}
};
let mut brace_level = 0;
let mut xptr = 0;
buffers.set_init(BufTy::Ex, 0);
add_buf_pool(pool, buffers, s3);
buffers.set_offset(BufTy::Ex, 1, 0);
let mut num_names = 0;
while num_names < i2 && buffers.offset(BufTy::Ex, 1) < buffers.init(BufTy::Ex) {
num_names += 1;
xptr = buffers.offset(BufTy::Ex, 1);
name_scan_for_and(ctx, pool, buffers, cites, s3, &mut brace_level)?;
}
if buffers.offset(BufTy::Ex, 1) < buffers.init(BufTy::Ex) {
buffers.set_offset(BufTy::Ex, 1, buffers.offset(BufTy::Ex, 1) - 4);
}
if num_names < i2 {
if i2 == 1 {
ctx.write_logs("There is no name in \"");
} else {
ctx.write_logs(&format!("There aren't {i2} names in \""));
}
print_a_pool_str(ctx, s3, pool)?;
ctx.write_logs("\"");
bst_ex_warn_print(ctx, pool, cites)?;
}
while buffers.offset(BufTy::Ex, 1) > xptr {
match LexClass::of(buffers.at(BufTy::Ex, buffers.offset(BufTy::Ex, 1) - 1)) {
LexClass::Whitespace | LexClass::Sep => {
buffers.set_offset(BufTy::Ex, 1, buffers.offset(BufTy::Ex, 1) - 1);
}
_ => {
if buffers.at(BufTy::Ex, buffers.offset(BufTy::Ex, 1) - 1) == b',' {
ctx.write_logs(&format!("Name {i2} in \""));
print_a_pool_str(ctx, s3, pool)?;
ctx.write_logs("\" has a comma at the end");
bst_ex_warn_print(ctx, pool, cites)?;
buffers.set_offset(BufTy::Ex, 1, buffers.offset(BufTy::Ex, 1) - 1);
} else {
break;
}
}
}
}
enum Commas {
None,
One(BufPointer),
Two(BufPointer, BufPointer),
}
let mut num_tokens = 0;
let mut commas = Commas::None;
let mut name_ptr = 0;
let mut token_starting = true;
while xptr < buffers.offset(BufTy::Ex, 1) {
match buffers.at(BufTy::Ex, xptr) {
b',' => {
match commas {
Commas::None => {
commas = Commas::One(num_tokens);
buffers.set_at(BufTy::NameSep, num_tokens, b',');
}
Commas::One(first) => {
commas = Commas::Two(first, num_tokens);
buffers.set_at(BufTy::NameSep, num_tokens, b',');
}
Commas::Two(_, _) => {
ctx.write_logs(&format!("Too many commas in name {i2} of \""));
print_a_pool_str(ctx, s3, pool)?;
ctx.write_logs("\"");
bst_ex_warn_print(ctx, pool, cites)?;
}
}
xptr += 1;
token_starting = true;
}
b'{' => {
brace_level += 1;
if token_starting {
buffers.set_name_tok(num_tokens, name_ptr);
num_tokens += 1;
}
buffers.set_at(BufTy::Sv, name_ptr, buffers.at(BufTy::Ex, xptr));
name_ptr += 1;
xptr += 1;
while brace_level > 0 && xptr < buffers.offset(BufTy::Ex, 1) {
match buffers.at(BufTy::Ex, xptr) {
b'{' => brace_level += 1,
b'}' => brace_level -= 1,
_ => (),
}
buffers.set_at(BufTy::Sv, name_ptr, buffers.at(BufTy::Ex, xptr));
name_ptr += 1;
xptr += 1;
}
token_starting = false;
}
b'}' => {
if token_starting {
buffers.set_name_tok(num_tokens, name_ptr);
num_tokens += 1;
}
ctx.write_logs(&format!("Name {i2} of \""));
print_a_pool_str(ctx, s3, pool)?;
ctx.write_logs("\" isn't brace balanced");
bst_ex_warn_print(ctx, pool, cites)?;
xptr += 1;
token_starting = false;
}
_ => match LexClass::of(buffers.at(BufTy::Ex, xptr)) {
LexClass::Whitespace => {
if !token_starting {
buffers.set_at(BufTy::NameSep, num_tokens, b' ');
}
xptr += 1;
token_starting = true;
}
LexClass::Sep => {
if !token_starting {
buffers.set_at(BufTy::NameSep, num_tokens, buffers.at(BufTy::Ex, xptr));
}
xptr += 1;
token_starting = true;
}
_ => {
if token_starting {
buffers.set_name_tok(num_tokens, name_ptr);
num_tokens += 1;
}
buffers.set_at(BufTy::Sv, name_ptr, buffers.at(BufTy::Ex, xptr));
name_ptr += 1;
xptr += 1;
token_starting = false;
}
},
}
}
buffers.set_name_tok(num_tokens, name_ptr);
let mut first_start = 0;
let first_end;
let last_end;
let mut von_start = 0;
let mut von_end = 0;
let jr_end;
let mut name_ptr2 = 0;
match commas {
Commas::None => {
last_end = num_tokens;
jr_end = last_end;
let mut second_loop = true;
while von_start < last_end - 1 {
name_ptr = buffers.name_tok(von_start);
name_ptr2 = buffers.name_tok(von_start + 1);
if von_token_found(buffers, hash, pool, &mut name_ptr, name_ptr2) {
von_name_ends_and_last_name_starts_stuff(
buffers,
hash,
pool,
last_end,
von_start,
&mut von_end,
&mut name_ptr,
&mut name_ptr2,
);
second_loop = false;
break;
}
von_start += 1;
}
if second_loop {
while von_start > 0 {
if LexClass::of(buffers.at(BufTy::NameSep, von_start)) != LexClass::Sep
|| buffers.at(BufTy::NameSep, von_start) == b'~'
{
break;
}
von_start -= 1;
}
von_end = von_start;
}
first_end = von_start;
}
Commas::One(comma) => {
last_end = comma;
jr_end = last_end;
first_start = jr_end;
first_end = num_tokens;
von_name_ends_and_last_name_starts_stuff(
buffers,
hash,
pool,
last_end,
von_start,
&mut von_end,
&mut name_ptr,
&mut name_ptr2,
);
}
Commas::Two(comma1, comma2) => {
last_end = comma1;
jr_end = comma2;
first_start = jr_end;
first_end = num_tokens;
von_name_ends_and_last_name_starts_stuff(
buffers,
hash,
pool,
last_end,
von_start,
&mut von_end,
&mut name_ptr,
&mut name_ptr2,
);
}
}
buffers.set_init(BufTy::Ex, 0);
add_buf_pool(pool, buffers, s1);
figure_out_the_formatted_name(
ctx,
buffers,
pool,
cites,
s1,
first_start,
first_end,
last_end,
von_start,
von_end,
&mut name_ptr,
&mut name_ptr2,
jr_end,
&mut brace_level,
)?;
add_pool_buf_and_push(ctx, buffers, pool)?;
Ok(())
}
fn interp_int_to_chr(
ctx: &mut ExecCtx<'_, '_, '_>,
pool: &mut StringPool,
hash: &HashData,
cites: &CiteInfo,
) -> Result<(), BibtexError> {
let pop1 = ctx.pop_stack(pool, cites)?;
let i1 = match pop1 {
ExecVal::Integer(i1) => i1,
_ => {
print_wrong_stk_lit(ctx, pool, hash, cites, pop1, StkType::Integer)?;
ctx.push_stack(ExecVal::String(ctx.s_null));
return Ok(());
}
};
if !(0..=127).contains(&i1) {
ctx.write_logs(&format!("{i1} isn't valid ASCII"));
bst_ex_warn_print(ctx, pool, cites)?;
ctx.push_stack(ExecVal::String(ctx.s_null));
} else {
let val = ExecVal::String(pool.add_string(&[i1 as u8]));
ctx.push_stack(val);
}
Ok(())
}
fn interp_int_to_str(
ctx: &mut ExecCtx<'_, '_, '_>,
pool: &mut StringPool,
hash: &HashData,
cites: &CiteInfo,
) -> Result<(), BibtexError> {
let pop1 = ctx.pop_stack(pool, cites)?;
let i1 = match pop1 {
ExecVal::Integer(i1) => i1,
_ => {
print_wrong_stk_lit(ctx, pool, hash, cites, pop1, StkType::Integer)?;
ctx.push_stack(ExecVal::String(ctx.s_null));
return Ok(());
}
};
let scratch = i1.to_string();
let val = ExecVal::String(pool.add_string(scratch.as_bytes()));
ctx.push_stack(val);
Ok(())
}
fn interp_missing(
ctx: &mut ExecCtx<'_, '_, '_>,
pool: &mut StringPool,
hash: &HashData,
cites: &CiteInfo,
) -> Result<(), BibtexError> {
let pop1 = ctx.pop_stack(pool, cites)?;
if !ctx.mess_with_entries {
bst_cant_mess_with_entries_print(ctx, pool, cites)?;
return Ok(());
}
match pop1 {
ExecVal::String(_) => {
ctx.push_stack(ExecVal::Integer(0));
}
ExecVal::Missing(_) => {
ctx.push_stack(ExecVal::Integer(1));
}
ExecVal::Illegal => {
ctx.push_stack(ExecVal::Integer(0));
}
_ => {
print_stk_lit(ctx, pool, hash, pop1)?;
ctx.write_logs(", not a string or missing field,");
bst_ex_warn_print(ctx, pool, cites)?;
ctx.push_stack(ExecVal::Integer(0));
}
}
Ok(())
}
fn interp_num_names(
ctx: &mut ExecCtx<'_, '_, '_>,
pool: &mut StringPool,
buffers: &mut GlobalBuffer,
hash: &HashData,
cites: &CiteInfo,
) -> Result<(), BibtexError> {
let pop1 = ctx.pop_stack(pool, cites)?;
match pop1 {
ExecVal::String(s1) => {
buffers.set_init(BufTy::Ex, 0);
add_buf_pool(pool, buffers, s1);
buffers.set_offset(BufTy::Ex, 1, 0);
let mut num_names = 0;
while buffers.offset(BufTy::Ex, 1) < buffers.init(BufTy::Ex) {
let mut brace_level = 0;
name_scan_for_and(ctx, pool, buffers, cites, s1, &mut brace_level)?;
num_names += 1;
}
ctx.push_stack(ExecVal::Integer(num_names))
}
_ => {
print_wrong_stk_lit(ctx, pool, hash, cites, pop1, StkType::String)?;
ctx.push_stack(ExecVal::Integer(0));
}
}
Ok(())
}
fn interp_preamble(
ctx: &mut ExecCtx<'_, '_, '_>,
pool: &mut StringPool,
bibs: &mut BibData,
) -> Result<(), BibtexError> {
let mut out = Vec::with_capacity(bibs.preamble_len() * 32);
for s in bibs.preamble() {
out.extend(pool.get_str(*s));
}
let s = pool.add_string(&out);
ctx.push_stack(ExecVal::String(s));
Ok(())
}
fn interp_purify(
ctx: &mut ExecCtx<'_, '_, '_>,
pool: &mut StringPool,
hash: &HashData,
cites: &CiteInfo,
) -> Result<(), BibtexError> {
let pop1 = ctx.pop_stack(pool, cites)?;
let s1 = match pop1 {
ExecVal::String(s1) => s1,
_ => {
print_wrong_stk_lit(ctx, pool, hash, cites, pop1, StkType::String)?;
ctx.push_stack(ExecVal::String(ctx.s_null));
return Ok(());
}
};
let mut scratch = Vec::from(pool.get_str(s1));
let mut idx = 0;
let mut brace_level: i32 = 0;
let mut write_idx = 0;
while idx < scratch.len() {
match LexClass::of(scratch[idx]) {
LexClass::Whitespace | LexClass::Sep => {
scratch[write_idx] = b' ';
write_idx += 1;
}
LexClass::Alpha | LexClass::Numeric => {
scratch[write_idx] = scratch[idx];
write_idx += 1;
}
_ => match scratch[idx] {
b'{' => {
brace_level += 1;
if brace_level == 1 && idx + 1 < scratch.len() && scratch[idx + 1] == b'\\' {
idx += 1;
while idx < scratch.len() && brace_level > 0 {
idx += 1;
let old_idx = idx;
while idx < scratch.len()
&& LexClass::of(scratch[idx]) == LexClass::Alpha
{
idx += 1;
}
let res = hash.lookup_str::<ControlSeq>(pool, &scratch[old_idx..idx]);
if let Some(loc) = res {
let seq = *hash.get(loc).extra();
scratch[write_idx] = scratch[old_idx];
write_idx += 1;
match seq {
ControlSeq::LowerOE
| ControlSeq::UpperOE
| ControlSeq::LowerAE
| ControlSeq::UpperAE
| ControlSeq::LowerSS => {
scratch[write_idx] = scratch[old_idx + 1];
write_idx += 1;
}
_ => (),
}
}
while idx < scratch.len() && brace_level > 0 && scratch[idx] != b'\\' {
match LexClass::of(scratch[idx]) {
LexClass::Alpha | LexClass::Numeric => {
scratch[write_idx] = scratch[idx];
write_idx += 1;
}
_ => match scratch[idx] {
b'{' => brace_level += 1,
b'}' => brace_level -= 1,
_ => (),
},
}
idx += 1;
}
}
idx -= 1;
}
}
b'}' => {
brace_level = brace_level.saturating_sub(1);
}
_ => (),
},
}
idx += 1;
}
scratch.truncate(write_idx);
let out = pool.add_string(&scratch);
ctx.push_stack(ExecVal::String(out));
Ok(())
}
fn interp_quote(ctx: &mut ExecCtx<'_, '_, '_>, pool: &mut StringPool) -> Result<(), BibtexError> {
let s = pool.add_string(b"\"");
ctx.push_stack(ExecVal::String(s));
Ok(())
}
#[derive(Copy, Clone)]
struct SLRange {
start: isize,
len: usize,
}
impl<T> Index<SLRange> for [T] {
type Output = [T];
fn index(&self, index: SLRange) -> &Self::Output {
let len = usize::min(self.len() + 1 - index.start.unsigned_abs(), index.len);
match index.start {
..=-1 => {
let start = index.start.unsigned_abs() - 1;
&self[self.len() - start - len..self.len() - start]
}
1.. => {
let start = index.start as usize - 1;
&self[start..start + len]
}
_ => &[],
}
}
}
fn interp_substr(
ctx: &mut ExecCtx<'_, '_, '_>,
pool: &mut StringPool,
hash: &HashData,
cites: &CiteInfo,
) -> Result<(), BibtexError> {
let pop1 = ctx.pop_stack(pool, cites)?;
let pop2 = ctx.pop_stack(pool, cites)?;
let pop3 = ctx.pop_stack(pool, cites)?;
let (len, start, s3) = match (pop1, pop2, pop3) {
(ExecVal::Integer(i1), ExecVal::Integer(i2), ExecVal::String(s3)) => (i1, i2, s3),
(ExecVal::Integer(_), ExecVal::Integer(_), _) => {
print_wrong_stk_lit(ctx, pool, hash, cites, pop3, StkType::String)?;
ctx.push_stack(ExecVal::String(ctx.s_null));
return Ok(());
}
(ExecVal::Integer(_), _, _) => {
print_wrong_stk_lit(ctx, pool, hash, cites, pop2, StkType::Integer)?;
ctx.push_stack(ExecVal::String(ctx.s_null));
return Ok(());
}
(_, _, _) => {
print_wrong_stk_lit(ctx, pool, hash, cites, pop1, StkType::Integer)?;
ctx.push_stack(ExecVal::String(ctx.s_null));
return Ok(());
}
};
let str = pool.get_str(s3);
if len <= 0 || start == 0 || start.unsigned_abs() as usize > str.len() {
ctx.push_stack(ExecVal::String(ctx.s_null));
return Ok(());
}
let len = len as usize;
let start = start as isize;
if len >= str.len() && (start == 1 || start == -1) {
if ctx.checkpoint.is_before(s3) {
let str_len = pool.get_str(s3).len();
let _ = pool.write_str(|cursor| cursor.extend(str_len));
}
ctx.push_stack(pop3);
return Ok(());
}
if start == 1 && ctx.checkpoint.is_before(s3) {
let new = pool.write_str(|cursor| {
cursor.extend(len);
});
ctx.push_stack(ExecVal::String(new));
return Ok(());
}
let new_str = Vec::from(&str[SLRange { start, len }]);
let out = pool.add_string(&new_str);
ctx.push_stack(ExecVal::String(out));
Ok(())
}
fn interp_swap(
ctx: &mut ExecCtx<'_, '_, '_>,
pool: &mut StringPool,
cites: &CiteInfo,
) -> Result<(), BibtexError> {
let pop1 = ctx.pop_stack(pool, cites)?;
let pop2 = ctx.pop_stack(pool, cites)?;
match (pop1, pop2) {
(ExecVal::String(s1), ExecVal::String(s2))
if ctx.checkpoint.is_before(s1) && ctx.checkpoint.is_before(s2) =>
{
let tmp = Vec::from(pool.get_str(s2));
let new = pool.write_str(|cursor| {
cursor.append_str(s1);
});
let val = ExecVal::String(new);
ctx.push_stack(val);
let val = ExecVal::String(pool.add_string(&tmp));
ctx.push_stack(val);
return Ok(());
}
(ExecVal::String(s), _) | (_, ExecVal::String(s)) if ctx.checkpoint.is_before(s) => {
let str_len = pool.get_str(s).len();
let _ = pool.write_str(|cursor| {
cursor.extend(str_len);
});
}
(_, _) => (),
}
ctx.push_stack(pop1);
ctx.push_stack(pop2);
Ok(())
}
fn interp_text_len(
ctx: &mut ExecCtx<'_, '_, '_>,
pool: &mut StringPool,
hash: &HashData,
cites: &CiteInfo,
) -> Result<(), BibtexError> {
let pop1 = ctx.pop_stack(pool, cites)?;
let s1 = match pop1 {
ExecVal::String(s1) => s1,
_ => {
print_wrong_stk_lit(ctx, pool, hash, cites, pop1, StkType::String)?;
ctx.push_stack(ExecVal::String(ctx.s_null));
return Ok(());
}
};
let str = pool.get_str(s1);
let mut idx = 0;
let mut brace_level: i32 = 0;
let mut num_chars = 0;
while idx < str.len() {
idx += 1;
match str[idx - 1] {
b'{' => {
brace_level += 1;
if brace_level == 1 && idx < str.len() && str[idx] == b'\\' {
idx += 1;
while idx < str.len() && brace_level > 0 {
match str[idx] {
b'{' => brace_level += 1,
b'}' => brace_level -= 1,
_ => (),
}
idx += 1;
num_chars += 1;
}
}
}
b'}' => {
brace_level = brace_level.saturating_sub(1);
}
_ => num_chars += 1,
}
}
ctx.push_stack(ExecVal::Integer(num_chars));
Ok(())
}
fn interp_text_prefix(
ctx: &mut ExecCtx<'_, '_, '_>,
pool: &mut StringPool,
hash: &HashData,
cites: &CiteInfo,
) -> Result<(), BibtexError> {
let pop1 = ctx.pop_stack(pool, cites)?;
let pop2 = ctx.pop_stack(pool, cites)?;
let (i1, s2) = match (pop1, pop2) {
(ExecVal::Integer(i1), ExecVal::String(s2)) => (i1, s2),
(ExecVal::Integer(_), _) => {
print_wrong_stk_lit(ctx, pool, hash, cites, pop2, StkType::String)?;
ctx.push_stack(ExecVal::String(ctx.s_null));
return Ok(());
}
(_, _) => {
print_wrong_stk_lit(ctx, pool, hash, cites, pop1, StkType::Integer)?;
ctx.push_stack(ExecVal::String(ctx.s_null));
return Ok(());
}
};
if i1 <= 0 {
ctx.push_stack(ExecVal::String(ctx.s_null));
return Ok(());
}
let mut brace_level: usize = 0;
let str = pool.get_str(s2);
let mut num_chars = 0;
let mut idx = 0;
while idx < str.len() && num_chars < i1 {
idx += 1;
match str[idx - 1] {
b'{' => {
brace_level += 1;
if brace_level == 1 && idx < str.len() && str[idx] == b'\\' {
idx += 1;
while idx < str.len() && brace_level > 0 {
match str[idx] {
b'{' => brace_level += 1,
b'}' => brace_level -= 1,
_ => (),
}
num_chars += 1;
}
}
}
b'}' => {
brace_level = brace_level.saturating_sub(1);
}
_ => num_chars += 1,
}
}
let is_before = ctx.checkpoint.is_before(s2);
let new = pool.write_str(|cursor| {
if is_before {
cursor.extend(idx)
} else {
cursor.append_substr(s2, 0..idx)
}
for _ in 0..brace_level {
cursor.append(b'}');
}
});
let val = ExecVal::String(new);
ctx.push_stack(val);
Ok(())
}
fn interp_ty(
ctx: &mut ExecCtx<'_, '_, '_>,
pool: &StringPool,
hash: &HashData,
cites: &CiteInfo,
) -> Result<(), BibtexError> {
if !ctx.mess_with_entries {
bst_cant_mess_with_entries_print(ctx, pool, cites)?;
return Ok(());
}
let ty = cites.get_type(cites.ptr());
let s = if ty.is_null() || ty == HashData::undefined() {
ctx.s_null
} else {
hash.get(ty).text()
};
ctx.push_stack(ExecVal::String(s));
Ok(())
}
fn interp_warning(
ctx: &mut ExecCtx<'_, '_, '_>,
pool: &mut StringPool,
hash: &HashData,
cites: &CiteInfo,
) -> Result<(), BibtexError> {
let pop1 = ctx.pop_stack(pool, cites)?;
match pop1 {
ExecVal::String(_) => {
ctx.write_logs("Warning--");
print_lit(ctx, pool, hash, pop1)?;
ctx.mark_warning();
}
_ => print_wrong_stk_lit(ctx, pool, hash, cites, pop1, StkType::String)?,
}
Ok(())
}
fn interp_width(
ctx: &mut ExecCtx<'_, '_, '_>,
pool: &mut StringPool,
hash: &HashData,
cites: &CiteInfo,
) -> Result<(), BibtexError> {
let pop1 = ctx.pop_stack(pool, cites)?;
let s1 = match pop1 {
ExecVal::String(s1) => s1,
_ => {
print_wrong_stk_lit(ctx, pool, hash, cites, pop1, StkType::String)?;
ctx.push_stack(ExecVal::Integer(0));
return Ok(());
}
};
let str = pool.get_str(s1);
let mut string_width = 0;
let mut brace_level = 0;
let mut idx = 0;
while idx < str.len() {
match str[idx] {
b'{' => {
brace_level += 1;
if brace_level == 1 && idx + 1 < str.len() && str[idx + 1] == b'\\' {
while idx < str.len() && brace_level > 0 {
idx += 1;
let old_idx = idx;
while idx < str.len() && LexClass::of(str[idx]) == LexClass::Alpha {
idx += 1;
}
if idx < str.len() && idx == old_idx {
idx += 1;
} else {
let res = hash.lookup_str::<ControlSeq>(pool, &str[old_idx..idx]);
if let Some(loc) = res {
let seq = *hash.get(loc).extra();
match seq {
ControlSeq::LowerSS => string_width += 500,
ControlSeq::LowerAE => string_width += 722,
ControlSeq::LowerOE => string_width += 778,
ControlSeq::UpperAE => string_width += 903,
ControlSeq::UpperOE => string_width += 1014,
_ => string_width += CHAR_WIDTH[str[old_idx] as usize],
}
}
}
while idx < str.len() && LexClass::of(str[idx]) == LexClass::Whitespace {
idx += 1;
}
while idx < str.len() && brace_level > 0 && str[idx] != b'\\' {
match str[idx] {
b'{' => brace_level += 1,
b'}' => brace_level -= 1,
c => string_width += CHAR_WIDTH[c as usize],
}
idx += 1;
}
}
idx -= 1;
} else {
string_width += CHAR_WIDTH[b'{' as usize];
}
}
b'}' => {
decr_brace_level(ctx, pool, cites, s1, &mut brace_level)?;
string_width += CHAR_WIDTH[b'}' as usize];
}
_ => string_width += CHAR_WIDTH[str[idx] as usize],
}
idx += 1;
}
check_brace_level(ctx, pool, cites, s1, brace_level)?;
ctx.push_stack(ExecVal::Integer(string_width));
Ok(())
}
fn interp_write(
ctx: &mut ExecCtx<'_, '_, '_>,
pool: &mut StringPool,
hash: &HashData,
buffers: &mut GlobalBuffer,
cites: &CiteInfo,
) -> Result<(), BibtexError> {
let pop1 = ctx.pop_stack(pool, cites)?;
match pop1 {
ExecVal::String(s1) => {
add_out_pool(ctx, buffers, pool, s1);
}
_ => {
print_wrong_stk_lit(ctx, pool, hash, cites, pop1, StkType::String)?;
}
}
Ok(())
}
pub(crate) fn execute_fn(
ctx: &mut ExecCtx<'_, '_, '_>,
globals: &mut GlobalItems<'_>,
ex_fn_loc: HashPointer<BstFn>,
) -> Result<(), BibtexError> {
match globals.hash.get(ex_fn_loc).extra() {
BstFn::Builtin(builtin) => match builtin {
BstBuiltin::Eq => interp_eq(ctx, globals.pool, globals.hash, globals.cites),
BstBuiltin::Gt => interp_gt(ctx, globals.pool, globals.hash, globals.cites),
BstBuiltin::Lt => interp_lt(ctx, globals.pool, globals.hash, globals.cites),
BstBuiltin::Plus => interp_plus(ctx, globals.pool, globals.hash, globals.cites),
BstBuiltin::Minus => interp_minus(ctx, globals.pool, globals.hash, globals.cites),
BstBuiltin::Concat => interp_concat(ctx, globals.pool, globals.hash, globals.cites),
BstBuiltin::Set => interp_gets(
ctx,
globals.pool,
globals.hash,
globals.entries,
globals.globals,
globals.cites,
),
BstBuiltin::AddPeriod => {
interp_add_period(ctx, globals.pool, globals.hash, globals.cites)
}
BstBuiltin::CallType => {
let default = globals.cites.get_type(globals.cites.ptr());
if !ctx.mess_with_entries {
bst_cant_mess_with_entries_print(ctx, globals.pool, globals.cites)?;
Ok(())
} else if default == HashData::undefined() {
execute_fn(ctx, globals, ctx.default)
} else if !default.is_null() {
execute_fn(ctx, globals, default)
} else {
Ok(())
}
}
BstBuiltin::ChangeCase => {
interp_change_case(ctx, globals.pool, globals.cites, globals.hash)
}
BstBuiltin::ChrToInt => {
interp_chr_to_int(ctx, globals.pool, globals.hash, globals.cites)
}
BstBuiltin::Cite => interp_cite(ctx, globals.pool, globals.cites),
BstBuiltin::Duplicate => interp_dup(ctx, globals.pool, globals.cites),
BstBuiltin::Empty => interp_empty(ctx, globals.pool, globals.hash, globals.cites),
BstBuiltin::FormatName => interp_format_name(
ctx,
globals.pool,
globals.buffers,
globals.cites,
globals.hash,
),
BstBuiltin::If => {
let pop1 = ctx.pop_stack(globals.pool, globals.cites)?;
let pop2 = ctx.pop_stack(globals.pool, globals.cites)?;
let pop3 = ctx.pop_stack(globals.pool, globals.cites)?;
match (pop1, pop2, pop3) {
(ExecVal::Function(f1), ExecVal::Function(f2), ExecVal::Integer(i3)) => {
if i3 > 0 {
execute_fn(ctx, globals, f2)
} else {
execute_fn(ctx, globals, f1)
}
}
(ExecVal::Function(_), ExecVal::Function(_), _) => print_wrong_stk_lit(
ctx,
globals.pool,
globals.hash,
globals.cites,
pop3,
StkType::Integer,
),
(ExecVal::Function(_), _, _) => print_wrong_stk_lit(
ctx,
globals.pool,
globals.hash,
globals.cites,
pop2,
StkType::Function,
),
(_, _, _) => print_wrong_stk_lit(
ctx,
globals.pool,
globals.hash,
globals.cites,
pop1,
StkType::Function,
),
}
}
BstBuiltin::IntToChr => {
interp_int_to_chr(ctx, globals.pool, globals.hash, globals.cites)
}
BstBuiltin::IntToStr => {
interp_int_to_str(ctx, globals.pool, globals.hash, globals.cites)
}
BstBuiltin::Missing => interp_missing(ctx, globals.pool, globals.hash, globals.cites),
BstBuiltin::Newline => {
output_bbl_line(ctx, globals.buffers);
Ok(())
}
BstBuiltin::NumNames => interp_num_names(
ctx,
globals.pool,
globals.buffers,
globals.hash,
globals.cites,
),
BstBuiltin::Pop => ctx.pop_stack(globals.pool, globals.cites).map(|_| ()),
BstBuiltin::Preamble => interp_preamble(ctx, globals.pool, globals.bibs),
BstBuiltin::Purify => interp_purify(ctx, globals.pool, globals.hash, globals.cites),
BstBuiltin::Quote => interp_quote(ctx, globals.pool),
BstBuiltin::Skip => Ok(()),
BstBuiltin::Stack => pop_whole_stack(ctx, globals.pool, globals.hash, globals.cites),
BstBuiltin::Substring => interp_substr(ctx, globals.pool, globals.hash, globals.cites),
BstBuiltin::Swap => interp_swap(ctx, globals.pool, globals.cites),
BstBuiltin::TextLength => {
interp_text_len(ctx, globals.pool, globals.hash, globals.cites)
}
BstBuiltin::TextPrefix => {
interp_text_prefix(ctx, globals.pool, globals.hash, globals.cites)
}
BstBuiltin::Top => pop_top_and_print(ctx, globals.pool, globals.hash, globals.cites),
BstBuiltin::Type => interp_ty(ctx, globals.pool, globals.hash, globals.cites),
BstBuiltin::Warning => interp_warning(ctx, globals.pool, globals.hash, globals.cites),
BstBuiltin::While => {
let pop1 = ctx.pop_stack(globals.pool, globals.cites)?;
let pop2 = ctx.pop_stack(globals.pool, globals.cites)?;
match (pop1, pop2) {
(ExecVal::Function(f1), ExecVal::Function(f2)) => {
loop {
execute_fn(ctx, globals, f2)?;
let res = ctx.pop_stack(globals.pool, globals.cites)?;
if let ExecVal::Integer(i1) = res {
if i1 > 0 {
execute_fn(ctx, globals, f1)?;
} else {
break;
}
} else {
print_wrong_stk_lit(
ctx,
globals.pool,
globals.hash,
globals.cites,
res,
StkType::Integer,
)?;
break;
}
}
Ok(())
}
(ExecVal::Function(_), _) => print_wrong_stk_lit(
ctx,
globals.pool,
globals.hash,
globals.cites,
pop2,
StkType::Function,
),
(_, _) => print_wrong_stk_lit(
ctx,
globals.pool,
globals.hash,
globals.cites,
pop1,
StkType::Function,
),
}
}
BstBuiltin::Width => interp_width(ctx, globals.pool, globals.hash, globals.cites),
BstBuiltin::Write => interp_write(
ctx,
globals.pool,
globals.hash,
globals.buffers,
globals.cites,
),
},
BstFn::Wizard(mut wiz_ptr) => {
loop {
match globals.other.wiz_function(wiz_ptr) {
WizOp::EndOfDef => break,
WizOp::Quote(ptr) => {
ctx.push_stack(ExecVal::Function(ptr));
}
WizOp::Exec(ptr) => {
execute_fn(ctx, globals, ptr)?;
}
WizOp::Text(text) => {
ctx.push_stack(ExecVal::String(globals.hash.get(text).text()));
}
WizOp::Int(int) => {
ctx.push_stack(ExecVal::Integer(*globals.hash.get(int).extra()));
}
}
wiz_ptr += 1;
}
Ok(())
}
BstFn::Field(field) => {
if !ctx.mess_with_entries {
bst_cant_mess_with_entries_print(ctx, globals.pool, globals.cites)
} else {
let field_ptr = globals.cites.ptr() * globals.other.num_fields() + *field;
if field_ptr >= globals.other.max_fields() {
ctx.write_logs("field_info index is out of range");
print_confusion(ctx);
return Err(BibtexError::Fatal);
}
let field = globals.other.field(field_ptr);
if field.is_invalid() {
ctx.push_stack(ExecVal::Missing(globals.hash.get(ex_fn_loc).text()));
} else {
ctx.push_stack(ExecVal::String(field));
}
Ok(())
}
}
BstFn::IntEntry(entry) => {
if !ctx.mess_with_entries {
bst_cant_mess_with_entries_print(ctx, globals.pool, globals.cites)
} else {
ctx.push_stack(ExecVal::Integer(
globals
.entries
.ints(globals.cites.ptr() * globals.entries.num_ent_ints() + *entry),
));
Ok(())
}
}
BstFn::StrEntry(entry) => {
if !ctx.mess_with_entries {
bst_cant_mess_with_entries_print(ctx, globals.pool, globals.cites)
} else {
let str_ent_ptr = globals.cites.ptr() * globals.entries.num_ent_strs() + *entry;
let str = globals.entries.strs(str_ent_ptr);
let val = ExecVal::String(globals.pool.add_string(str));
ctx.push_stack(val);
Ok(())
}
}
BstFn::IntGlbl(value) => {
ctx.push_stack(ExecVal::Integer(*value));
Ok(())
}
BstFn::StrGlbl(glb_ptr) => {
let str_ptr = globals.globals.str_ptr(*glb_ptr);
if !str_ptr.is_invalid() {
ctx.push_stack(ExecVal::String(str_ptr));
} else {
let str = globals.globals.str(*glb_ptr);
let val = ExecVal::String(globals.pool.add_string(str));
ctx.push_stack(val);
}
Ok(())
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_empty_sl_range() {
let slice = b"0123456789";
let r1 = SLRange { start: 0, len: 0 };
assert_eq!(&slice[r1], &[]);
let r2 = SLRange { start: 5, len: 0 };
assert_eq!(&slice[r2], &[]);
let r3 = SLRange { start: -5, len: 0 };
assert_eq!(&slice[r3], &[]);
}
#[test]
fn test_sl_range() {
let slice = b"0123456789";
let r1 = SLRange { start: 1, len: 5 };
assert_eq!(&slice[r1], b"01234");
let r2 = SLRange { start: 3, len: 2 };
assert_eq!(&slice[r2], b"23");
let r3 = SLRange { start: -1, len: 2 };
assert_eq!(&slice[r3], b"89");
}
#[test]
fn test_sl_range_long() {
let slice = b"0123456789";
let r1 = SLRange { start: 1, len: 100 };
assert_eq!(&slice[r1], b"0123456789");
let r1 = SLRange {
start: -1,
len: 100,
};
assert_eq!(&slice[r1], b"0123456789");
}
}