use std::rc::Rc;
use crate::Engine;
use crate::build::PackSpec;
use crate::cmd::Cmd;
use crate::eqtb::{Loc, Val};
use crate::input::TokKind;
use crate::maincontrol::{IGNORE_DEPTH, Mode};
use crate::nodes::{
DEFAULT_CODE, FenceData, FractionData, NodeData, NodeRef, accent_sub, fence_sub, id, noad_opt,
noad_sub, radical_sub,
};
use crate::primitives_gen::{assign_dimen as dp, assign_int as ip, assign_toks as tp};
use crate::save::GroupCode;
use crate::token::Token;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Field {
Nucleus = 0,
Sup = 1,
Sub = 2,
Degree = 3,
}
impl Field {
fn from_i64(v: i64) -> Field {
match v & 3 {
0 => Field::Nucleus,
1 => Field::Sup,
2 => Field::Sub,
_ => Field::Degree,
}
}
}
fn field_ref(noad: NodeRef, f: Field) -> i64 {
(i64::from(noad.0) << 2) | f as i64
}
fn field_from_ref(v: i64) -> (NodeRef, Field) {
(NodeRef((v >> 2) as u32), Field::from_i64(v))
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct MathChar {
pub class: u32,
pub fam: u32,
pub chr: u32,
}
impl MathChar {
pub fn from_given(g: u32) -> MathChar {
if g & crate::codes::MATH_GIVEN_PACKED != 0 {
MathChar::from_packed(i64::from(g & !crate::codes::MATH_GIVEN_PACKED))
} else {
MathChar::from_classic(g as i32)
}
}
pub fn from_classic(c: i32) -> MathChar {
if c >= 0x8000 {
return MathChar {
class: 8,
fam: 0,
chr: 0,
};
}
let c = c.max(0) as u32;
MathChar {
class: c >> 12,
fam: (c >> 8) & 0xF,
chr: c & 0xFF,
}
}
pub fn from_packed(v: i64) -> MathChar {
if v == crate::codes::MATH_ACTIVE {
return MathChar {
class: 8,
fam: 0,
chr: 0,
};
}
MathChar {
class: crate::codes::math_class(v),
fam: crate::codes::math_fam(v),
chr: crate::codes::math_char(v),
}
}
}
pub fn delcode_parts(v: i64) -> Option<(u32, u32, u32, u32)> {
if v < 0 {
return None;
}
if v & (1 << 62) != 0 {
Some((
((v >> 53) & 0xF) as u32,
((v >> 32) & 0xFF) as u32,
((v >> 21) & 0xF) as u32,
(v & 0xFF) as u32,
))
} else {
Some((((v >> 21) & 0xFF) as u32, (v & 0x1F_FFFF) as u32, 0, 0))
}
}
fn class_subtype(class: u32) -> u16 {
match class {
1 => noad_sub::OP_DISPLAY_LIMITS,
2 => noad_sub::BIN,
3 => noad_sub::REL,
4 => noad_sub::OPEN,
5 => noad_sub::CLOSE,
6 => noad_sub::PUNCT,
_ => noad_sub::ORD,
}
}
pub const fn cramped_style(s: i32) -> i32 {
2 * (s / 2) + 1
}
pub const fn sub_style(s: i32) -> i32 {
2 * (s / 4) + 4 + 1
}
pub const fn sup_style(s: i32) -> i32 {
2 * (s / 4) + 4 + (s % 2)
}
pub const fn num_style(s: i32) -> i32 {
s + 2 - 2 * (s / 6)
}
pub const fn denom_style(s: i32) -> i32 {
2 * (s / 2) + 1 + 2 - 2 * (s / 6)
}
impl Engine {
pub fn new_noad(&mut self, sub: u16) -> NodeRef {
self.new_node(id::NOAD, sub, NodeData::Noad(Box::default()))
}
pub fn new_math_char(&mut self, fam: u32, chr: u32) -> NodeRef {
self.new_node(id::MATH_CHAR, 0, NodeData::MathChar { fam, chr })
}
pub fn new_sub_list(&mut self, mlist: bool, head: NodeRef) -> NodeRef {
self.new_node(
if mlist { id::SUB_MLIST } else { id::SUB_BOX },
0,
NodeData::SubList { head },
)
}
pub fn new_delim(&mut self, parts: (u32, u32, u32, u32)) -> NodeRef {
self.new_node(
id::DELIM,
0,
NodeData::Delim {
small_fam: parts.0,
small_char: parts.1,
large_fam: parts.2,
large_char: parts.3,
},
)
}
pub fn new_style(&mut self, s: u8) -> NodeRef {
self.new_node(id::STYLE, u16::from(s), NodeData::Style { style: s })
}
pub fn noad_field(&self, p: NodeRef, f: Field) -> NodeRef {
match self.nodes.noad(p) {
Some(n) => match f {
Field::Nucleus => n.nucleus,
Field::Sup => n.sup,
Field::Sub => n.sub,
Field::Degree => n.degree,
},
None => NodeRef::NULL,
}
}
pub fn set_noad_field(&mut self, p: NodeRef, f: Field, k: NodeRef) {
if let Some(n) = self.nodes.noad_mut(p) {
match f {
Field::Nucleus => n.nucleus = k,
Field::Sup => n.sup = k,
Field::Sub => n.sub = k,
Field::Degree => n.degree = k,
}
}
}
fn push_math(&mut self, c: GroupCode, style: i32) {
self.push_nest(Mode::Math);
self.cur_list_mut().incompleat_noad = NodeRef::NULL;
self.cur_list_mut().math_style = style;
self.new_save_level(c);
if c == GroupCode::MathShift {
self.begin_math_dirs();
}
}
pub fn init_math(&mut self) {
let shift_cs = self.cur_cmd == Cmd::MathShiftCs;
let display = if shift_cs {
self.cur_chr == 0 && self.mode == Mode::Horizontal
} else {
self.get_token();
if self.cur_cmd == Cmd::MathShift && self.mode == Mode::Horizontal {
true
} else {
self.back_input();
false
}
};
if display {
self.go_into_display_math();
} else {
self.go_into_ordinary_math();
}
}
fn go_into_ordinary_math(&mut self) {
self.push_math(GroupCode::MathShift, 2);
self.eq_define(Loc::IntPar(ip::FAM), Val::Int(-1));
let t = self.eqtb.toks_param(tp::EVERYMATH).clone();
if !t.is_empty() {
self.begin_token_list(t, TokKind::EveryMathText);
}
}
fn go_into_display_math(&mut self) {
let max_dimen = 0o7777777777;
let mut w = -max_dimen;
let (head, tail) = (self.cur_list().head, self.cur_list().tail);
let only_local_par = self.nodes.next(head) == tail
&& matches!(self.nodes.data(tail), NodeData::LocalPar { .. });
if head == tail || only_local_par {
if only_local_par {
self.nodes.flush_list(tail);
self.nodes.set_next(head, NodeRef::NULL);
}
self.pop_nest();
} else {
let wp = self.eqtb.int(ip::DISPLAYWIDOWPENALTY);
self.line_break(wp);
w = self.pre_display_width();
}
let prev_graf = self.cur_list().prev_graf;
let (l, s) = self.display_line_size(prev_graf);
self.push_math(GroupCode::MathShift, 0);
self.mode = Mode::DisplayMath;
self.cur_list_mut().mode = Mode::DisplayMath;
self.eq_define(Loc::IntPar(ip::FAM), Val::Int(-1));
self.eq_define(Loc::DimenPar(dp::PREDISPLAYSIZE), Val::Int(i64::from(w)));
self.eq_define(Loc::DimenPar(dp::DISPLAYWIDTH), Val::Int(i64::from(l)));
self.eq_define(Loc::DimenPar(dp::DISPLAYINDENT), Val::Int(i64::from(s)));
let t = self.eqtb.toks_param(tp::EVERYDISPLAY).clone();
if !t.is_empty() {
self.begin_token_list(t, TokKind::EveryDisplayText);
}
if self.nest.len() == 2 {
self.build_page_from("before_display");
}
}
fn pre_display_width(&mut self) -> i32 {
let max_dimen = 0o7777777777;
let jb = self.ts.just_box;
if jb.is_null() {
return -max_dimen;
}
let Some(b) = self.nodes.box_data(jb).cloned() else {
return -max_dimen;
};
let f = self.eqtb.cur_font.val;
let gap = self.eqtb.int(ip::PREDISPLAYGAPFACTOR);
let q = self.quad(f);
let extra = (q / 1000).wrapping_mul(gap);
let mut v = b.shift + extra;
let mut w = -max_dimen;
let mut p = b.head;
while !p.is_null() {
let (d, visible) = match self.nodes.data(p) {
NodeData::Glyph(_) => (self.glyph_dimensions(p).0, true),
NodeData::Box(bx) => (bx.width, true),
NodeData::Rule { width, .. } => (*width, true),
NodeData::Kern { kern, .. } => (*kern, false),
NodeData::MarginKern { width, .. } => (*width, false),
NodeData::Math { .. } => (self.nodes.width(p), false),
NodeData::Glue { spec, .. } => {
let spec = *spec;
if b.glue_sign == 1 {
if b.glue_order == spec.stretch_order && spec.stretch != 0 {
v = max_dimen;
}
} else if b.glue_sign == 2
&& b.glue_order == spec.shrink_order
&& spec.shrink != 0
{
v = max_dimen;
}
(
spec.width,
self.nodes.subtype(p) >= crate::nodes::glue_sub::A_LEADERS,
)
}
_ => (0, false),
};
if visible {
if v < max_dimen {
v += d;
w = v;
} else {
w = max_dimen;
break;
}
} else if v < max_dimen {
v += d;
}
p = self.nodes.next(p);
}
w
}
fn display_line_size(&self, prev_graf: i32) -> (i32, i32) {
let hsize = self.eqtb.dimen(dp::HSIZE);
match &self.eqtb.par_shape.val {
None => {
let hi = self.eqtb.dimen(dp::HANGINDENT);
let ha = self.eqtb.int(ip::HANGAFTER);
if hi != 0 && ((ha >= 0 && prev_graf + 2 > ha) || (prev_graf + 1 < -ha)) {
(hsize - hi.abs(), if hi > 0 { hi } else { 0 })
} else {
(hsize, 0)
}
}
Some(shape) => {
let n = (shape.len() / 2) as i32;
let i = if prev_graf + 2 >= n { n } else { prev_graf + 2 };
let k = 2 * (i.max(1) as usize - 1);
let indent = shape.get(k).copied().unwrap_or(0);
let len = shape.get(k + 1).copied().unwrap_or(hsize);
(len, indent)
}
}
}
pub fn math_command(&mut self) {
let cmd = self.cur_cmd;
match cmd {
Cmd::Letter | Cmd::OtherChar | Cmd::CharGiven => {
let code = self.eqtb.math.get(self.cur_chr);
self.set_math_char(MathChar::from_packed(code));
}
Cmd::CharNum => {
self.scan_char_num();
self.cur_chr = self.cur_val as u32;
let code = self.eqtb.math.get(self.cur_chr);
self.set_math_char(MathChar::from_packed(code));
}
Cmd::MathCharNum => {
let mc = self.scan_math_char_num();
self.set_math_char(mc);
}
Cmd::MathGiven => {
let mc = MathChar::from_given(self.cur_chr);
self.set_math_char(mc);
}
Cmd::XmathGiven => {
let mc = MathChar::from_packed(i64::from(self.cur_chr));
self.set_math_char(mc);
}
Cmd::DelimNum => {
let mc = self.scan_delim_num_char();
self.set_math_char(mc);
}
Cmd::LeftBrace => {
let n = self.new_noad(noad_sub::ORD);
self.tail_append(n);
self.back_input();
let st = self.cur_list().math_style;
self.scan_math(n, Field::Nucleus, st);
}
Cmd::MathComp => {
let sub = self.cur_chr as u16;
let n = self.new_noad(sub);
self.tail_append(n);
let st = self.cur_list().math_style;
let st = if sub == noad_sub::OVER || sub == noad_sub::UNDER {
if sub == noad_sub::OVER {
cramped_style(st)
} else {
st
}
} else {
st
};
self.scan_math(n, Field::Nucleus, st);
}
Cmd::LimitSwitch => self.math_limit_switch(),
Cmd::SupMark | Cmd::SubMark => {
let sup = cmd == Cmd::SupMark;
self.sub_sup(sup, false);
}
Cmd::SuperSubScript => {
let sup = self.cur_chr == Cmd::SupMark as u32;
self.sub_sup(sup, false);
}
Cmd::NoSuperSubScript => {
let sup = self.cur_chr == Cmd::SupMark as u32;
self.sub_sup(sup, true);
}
Cmd::Radical => self.math_radical(),
Cmd::Accent | Cmd::MathAccent => self.math_ac(),
Cmd::Vcenter => {
self.scan_spec(GroupCode::Vcenter, None);
self.normal_paragraph();
self.push_nest(Mode::InternalVertical);
self.cur_list_mut().prev_depth = IGNORE_DEPTH;
let t = self.eqtb.toks_param(tp::EVERYVBOX).clone();
if !t.is_empty() {
self.begin_token_list(t, TokKind::EveryVboxText);
}
}
Cmd::MathStyle => {
let s = self.cur_chr.min(7) as u8;
let n = self.new_style(s);
self.tail_append(n);
self.cur_list_mut().math_style = i32::from(s);
}
Cmd::NonScript => {
let g = self.new_glue_node(
crate::glue::Glue::ZERO,
crate::nodes::glue_sub::COND_MATH_GLUE,
);
self.tail_append(g);
}
Cmd::MathChoice => {
if self.cur_chr == 0 {
self.append_choices();
} else {
let n = self.new_noad(noad_sub::ORD);
self.tail_append(n);
let st = num_style(self.cur_list().math_style);
self.scan_left_brace();
let sub = self.new_sub_list(true, NodeRef::NULL);
self.set_noad_field(n, Field::Nucleus, sub);
self.push_saved(field_ref(n, Field::Nucleus));
self.push_math(GroupCode::Math, st);
}
}
Cmd::Above => self.math_fraction(),
Cmd::MathShift => {
if self.save.cur_group == GroupCode::MathShift {
self.after_math();
} else {
self.off_save();
}
}
Cmd::MathShiftCs => {
let c = self.cur_chr;
let ok = (c == 3 && self.mode == Mode::Math)
|| (c == 1 && self.mode == Mode::DisplayMath);
if (c == 1 || c == 3) && self.save.cur_group == GroupCode::MathShift && ok {
self.after_math();
} else if c == 1 || c == 3 {
if self.save.cur_group == GroupCode::MathShift {
self.print_err("Invalid math code");
self.help(&["I'm going to ignore this math shift command."]);
self.error();
} else {
self.off_save();
}
} else {
self.report_illegal_case();
}
}
Cmd::EqNo => {
if self.privileged() {
if self.save.cur_group == GroupCode::MathShift {
self.start_eq_no();
} else {
self.off_save();
}
}
}
Cmd::LeftRight => self.math_left_right(),
Cmd::Endv
| Cmd::ParEnd
| Cmd::Stop
| Cmd::Vskip
| Cmd::UnVbox
| Cmd::Valign
| Cmd::Hrule => self.insert_dollar_sign(),
Cmd::Halign => {
if self.privileged() {
if self.save.cur_group == GroupCode::MathShift {
self.init_align();
} else {
self.off_save();
}
}
}
_ => {
let (c, r) = (self.cur_cmd, self.cur_chr);
let s = self.print_to_string(|e| e.print_cmd_chr(c, r));
self.not_implemented(&String::from_utf8_lossy(&s));
}
}
}
fn scan_math_char_num(&mut self) -> MathChar {
match self.cur_chr {
0 => {
let g = self.scan_math_given();
MathChar::from_given(g)
}
1 => {
let v = self.scan_umath_triple();
MathChar::from_packed(v)
}
_ => {
self.scan_int();
MathChar::from_packed(i64::from(self.cur_val))
}
}
}
fn scan_delim_num_char(&mut self) -> MathChar {
if self.cur_chr == 0 {
self.scan_twenty_seven_bit_int();
MathChar::from_classic(self.cur_val / 0o10000)
} else {
let v = self.scan_umath_triple();
MathChar::from_packed(v)
}
}
fn fam_in_range(&self) -> Option<u32> {
let f = self.eqtb.int(ip::FAM);
if (0..crate::eqtb::NUM_FAMILIES as i32).contains(&f) {
Some(f as u32)
} else {
None
}
}
fn effective_fam(&self, mc: MathChar) -> u32 {
if mc.class == 7
&& let Some(f) = self.fam_in_range()
{
return f;
}
let vf = self.eqtb.int(ip::VARIABLEFAM);
if vf >= 0
&& mc.fam == vf as u32
&& let Some(f) = self.fam_in_range()
{
return f;
}
mc.fam
}
fn treat_as_active(&mut self) {
let cs = self.hash.active(self.cur_chr);
self.cur_cs = cs;
let m = self.eqtb.meaning(cs).clone();
self.cur_cmd = m.cmd;
self.cur_chr = m.chr;
self.x_token();
self.back_input();
}
fn set_math_char(&mut self, mc: MathChar) {
if mc.class >= 8 {
self.treat_as_active();
return;
}
let fam = self.effective_fam(mc);
let k = self.new_math_char(fam, mc.chr);
let sub = class_subtype(mc.class);
let p = self.new_noad(sub);
self.set_noad_field(p, Field::Nucleus, k);
self.tail_append(p);
}
fn scan_math(&mut self, p: NodeRef, f: Field, style: i32) -> bool {
self.scan_math_flag(p, f, style, 0)
}
fn scan_math_flag(&mut self, p: NodeRef, f: Field, style: i32, flag: i64) -> bool {
self.get_nonblank_nonrelax_noncall();
let mc = loop {
match self.cur_cmd {
Cmd::Letter | Cmd::OtherChar | Cmd::CharGiven => {
let code = self.eqtb.math.get(self.cur_chr);
let mc = MathChar::from_packed(code);
if mc.class == 8 {
self.treat_as_active();
self.get_nonblank_nonrelax_noncall();
continue;
}
break mc;
}
Cmd::CharNum => {
self.scan_char_num();
self.cur_chr = self.cur_val as u32;
self.cur_cmd = Cmd::CharGiven;
}
Cmd::MathCharNum => break self.scan_math_char_num(),
Cmd::MathGiven => break MathChar::from_given(self.cur_chr),
Cmd::XmathGiven => break MathChar::from_packed(i64::from(self.cur_chr)),
Cmd::DelimNum => break self.scan_delim_num_char(),
_ => {
self.back_input();
self.scan_left_brace();
let sub = self.new_sub_list(true, NodeRef::NULL);
let old = self.noad_field(p, f);
self.nodes.flush_list(old);
self.set_noad_field(p, f, sub);
self.push_saved(field_ref(p, f) | flag);
self.push_math(GroupCode::Math, style);
return true;
}
}
};
self.finish_scan_math(p, f, mc);
false
}
fn finish_scan_math(&mut self, p: NodeRef, f: Field, mc: MathChar) {
let fam = if mc.class == 8 {
0
} else {
self.effective_fam(mc)
};
let k = self.new_math_char(fam, mc.chr);
let old = self.noad_field(p, f);
self.nodes.flush_list(old);
self.set_noad_field(p, f, k);
}
fn math_limit_switch(&mut self) {
let (head, tail) = (self.cur_list().head, self.cur_list().tail);
if head != tail
&& self.nodes.id(tail) == id::NOAD
&& matches!(
self.nodes.subtype(tail),
noad_sub::OP_DISPLAY_LIMITS | noad_sub::OP_LIMITS | noad_sub::OP_NO_LIMITS
)
{
self.nodes.set_subtype(tail, self.cur_chr as u16);
return;
}
self.print_err("Limit controls must follow a math operator");
self.help(&["I'm ignoring this misplaced \\limits or \\nolimits command."]);
self.error();
}
fn scan_delimiter(&mut self, radical: bool, fam_char: bool) -> NodeRef {
let parts: Option<(u32, u32, u32, u32)> = if fam_char {
self.scan_math_family();
let fam = self.cur_val as u32;
self.scan_char_num();
Some((fam, self.cur_val as u32, 0, 0))
} else if radical {
self.scan_twenty_seven_bit_int();
classic_delimiter(self.cur_val)
} else {
self.get_nonblank_nonrelax_noncall();
match self.cur_cmd {
Cmd::Letter | Cmd::OtherChar => delcode_parts(self.eqtb.del.get(self.cur_chr)),
Cmd::DelimNum => {
if self.cur_chr == 0 {
self.scan_twenty_seven_bit_int();
classic_delimiter(self.cur_val)
} else {
let v = self.scan_umath_triple();
let mc = MathChar::from_packed(v);
Some((mc.fam, mc.chr, 0, 0))
}
}
_ => None,
}
};
let parts = match parts {
Some(p) => p,
None => {
self.print_err("Missing delimiter (. inserted)");
self.help(&[
"I was expecting to see something like `(' or `\\{' or",
"`\\}' here. If you typed, e.g., `{' instead of `\\{', you",
"should probably delete the `{' by typing `1' now, so that",
"braces don't get unbalanced. Otherwise just proceed",
"Acceptable delimiters are characters whose \\delcode is",
"nonnegative, or you can use `\\delimiter <delimiter code>'.",
]);
self.back_error();
(0, 0, 0, 0)
}
};
self.new_delim(parts)
}
fn math_radical(&mut self) {
let c = self.cur_chr as u16;
let p = self.new_node(id::RADICAL, c, NodeData::Noad(Box::default()));
self.tail_append(p);
let mut opts = 0u32;
let mut width = 0i32;
if c == radical_sub::UHEXTENSIBLE || c >= radical_sub::UUNDERDELIMITER {
loop {
if self.scan_keyword("width") {
self.scan_normal_dimen();
width = self.cur_val;
} else if self.scan_keyword("left") {
opts |= noad_opt::LEFT;
} else if self.scan_keyword("middle") {
opts |= noad_opt::MIDDLE;
} else if self.scan_keyword("right") {
opts |= noad_opt::RIGHT;
} else {
break;
}
}
}
let d = self.scan_delimiter(c == radical_sub::RADICAL, c != radical_sub::RADICAL);
if let Some(n) = self.nodes.noad_mut(p) {
n.left = d;
n.options = opts;
n.width = width;
}
let st = self.cur_list().math_style;
match c {
radical_sub::UHEXTENSIBLE => {}
radical_sub::UROOT => {
if !self.scan_math_flag(p, Field::Degree, 6, 1 << 62) {
self.scan_math(p, Field::Nucleus, cramped_style(st));
}
}
radical_sub::UUNDERDELIMITER => {
self.scan_math(p, Field::Nucleus, sub_style(st));
}
radical_sub::UOVERDELIMITER => {
self.scan_math(p, Field::Nucleus, sup_style(st));
}
radical_sub::UDELIMITERUNDER => {
self.scan_math(p, Field::Nucleus, st);
}
_ => {
self.scan_math(p, Field::Nucleus, cramped_style(st));
}
}
}
fn scan_accent_char(&mut self, umath: bool) -> (NodeRef, bool) {
let fixed = umath && self.scan_keyword("fixed");
let mc = if umath {
let v = self.scan_umath_triple();
MathChar::from_packed(v)
} else {
let g = self.scan_math_given();
MathChar::from_given(g)
};
let fam = if mc.class == 8 {
0
} else {
self.effective_fam(mc)
};
(self.new_math_char(fam, mc.chr), fixed)
}
fn math_ac(&mut self) {
if self.cur_cmd == Cmd::Accent {
self.print_err("Please use ");
self.print_esc_str("mathaccent");
self.printer.print_str(" for accents in math mode");
self.help(&[
"I'm changing \\accent to \\mathaccent here; wish me luck.",
"(Accents are not the same in formulas as they are in text.)",
]);
self.error();
}
let umath = self.cur_cmd == Cmd::MathAccent && self.cur_chr == 1;
let p = self.new_node(
id::ACCENT,
accent_sub::BOTH_FLEXIBLE,
NodeData::Noad(Box::default()),
);
self.tail_append(p);
let mut sub = accent_sub::BOTH_FLEXIBLE;
let (mut top, mut bot) = (NodeRef::NULL, NodeRef::NULL);
let fraction = 0;
let mut overlay = false;
if umath {
let (want_top, want_bot) = if self.scan_keyword("bottom") {
(false, true)
} else if self.scan_keyword("both") {
(true, true)
} else if self.scan_keyword("overlay") {
overlay = true;
(true, false)
} else {
let _ = self.scan_keyword("top");
(true, false)
};
if want_top {
let (k, fixed) = self.scan_accent_char(true);
top = k;
if fixed {
sub |= accent_sub::FIXED_TOP;
}
}
if want_bot {
let (k, fixed) = self.scan_accent_char(true);
bot = k;
if fixed {
sub |= accent_sub::FIXED_BOTTOM;
}
}
} else {
top = self.scan_accent_char(false).0;
}
self.nodes.set_subtype(p, sub);
if let Some(n) = self.nodes.noad_mut(p) {
n.accent = top;
n.bot_accent = bot;
n.fraction = fraction;
if overlay {
n.options |= noad_opt::OVERLAY;
}
}
let st = cramped_style(self.cur_list().math_style);
self.scan_math(p, Field::Nucleus, st);
}
fn sub_sup(&mut self, sup: bool, no_style: bool) {
let field = if sup { Field::Sup } else { Field::Sub };
let (head, tail) = (self.cur_list().head, self.cur_list().tail);
let mut target = NodeRef::NULL;
let mut t_nonempty = false;
if tail != head && self.nodes.scripts_allowed(tail) {
target = tail;
t_nonempty = !self.noad_field(tail, field).is_null();
}
if target.is_null() || t_nonempty {
let n = self.new_noad(noad_sub::ORD);
let k = self.new_sub_list(true, NodeRef::NULL);
self.set_noad_field(n, Field::Nucleus, k);
self.tail_append(n);
target = n;
if t_nonempty {
if sup {
self.print_err("Double superscript");
self.help(&["I treat `x^1^2' essentially like `x^1{}^2'."]);
} else {
self.print_err("Double subscript");
self.help(&["I treat `x_1_2' essentially like `x_1{}_2'."]);
}
self.error();
}
}
let st = self.cur_list().math_style;
let style = if no_style {
if let Some(n) = self.nodes.noad_mut(target) {
n.options |= if sup {
noad_opt::NO_SUPER_SCRIPT
} else {
noad_opt::NO_SUB_SCRIPT
};
}
st
} else if sup {
sup_style(st)
} else {
sub_style(st)
};
self.scan_math(target, field, style);
}
fn math_fraction(&mut self) {
let c = self.cur_chr;
let delimited = c >= 4;
let kind = c % 4;
if !self.cur_list().incompleat_noad.is_null() {
if delimited {
let a = self.scan_delimiter(false, false);
let b = self.scan_delimiter(false, false);
self.nodes.flush_list(a);
self.nodes.flush_list(b);
}
if kind == 3 {
let m = self.scan_delimiter(false, false);
self.nodes.flush_list(m);
self.scan_fraction_options();
} else if kind == 0 {
let _ = self.scan_keyword("exact");
self.scan_normal_dimen();
}
self.print_err("Ambiguous; you need another { and }");
self.help(&[
"I'm ignoring this fraction specification, since I don't",
"know whether a construction like `x \\over y \\over z'",
"means `{x \\over y} \\over z' or `x \\over {y \\over z}'.",
]);
self.error();
return;
}
let head = self.cur_list().head;
let first = self.nodes.next(head);
let num = self.new_sub_list(true, first);
let mut fr = FractionData {
thickness: DEFAULT_CODE,
num,
denom: NodeRef::NULL,
left: NodeRef::NULL,
right: NodeRef::NULL,
middle: NodeRef::NULL,
options: 0,
new_hlist: NodeRef::NULL,
};
self.nodes.set_next(head, NodeRef::NULL);
self.cur_list_mut().tail = head;
if delimited {
fr.left = self.scan_delimiter(false, false);
fr.right = self.scan_delimiter(false, false);
}
match kind {
0 => {
if self.scan_keyword("exact") {
fr.options |= noad_opt::EXACT;
}
self.scan_normal_dimen();
fr.thickness = self.cur_val;
}
1 => fr.thickness = DEFAULT_CODE,
2 => fr.thickness = 0,
_ => {
fr.middle = self.scan_delimiter(false, false);
fr.options |= self.scan_fraction_options();
fr.thickness = 0;
}
}
let n = self.new_node(id::FRACTION, 0, NodeData::Fraction(Box::new(fr)));
self.cur_list_mut().incompleat_noad = n;
let st = cramped_style(self.cur_list().math_style);
self.cur_list_mut().math_style = st;
}
fn scan_fraction_options(&mut self) -> u32 {
let mut o = 0;
loop {
if self.scan_keyword("noaxis") {
o |= noad_opt::NO_AXIS;
} else if self.scan_keyword("exact") {
o |= noad_opt::EXACT;
} else {
return o;
}
}
}
fn fin_mlist(&mut self, p: NodeRef) -> NodeRef {
let inc = self.cur_list().incompleat_noad;
let head = self.cur_list().head;
let q;
if !inc.is_null() {
let first = self.nodes.next(head);
let denom = self.new_sub_list(true, first);
if let NodeData::Fraction(f) = self.nodes.data_mut(inc) {
f.denom = denom;
}
if p.is_null() {
q = inc;
} else {
let num = match self.nodes.data(inc) {
NodeData::Fraction(f) => f.num,
_ => NodeRef::NULL,
};
let num_list = self.nodes.list_ptr_kernel(num);
q = num_list;
let dp = self.cur_list().delim_ptr;
if num_list.is_null() || self.nodes.id(num_list) != id::FENCE || dp.is_null() {
self.confusion("right");
}
let after = self.nodes.next(dp);
self.nodes.set_kernel_list(num, after);
if !after.is_null() {
self.nodes.set_prev(after, NodeRef::NULL);
}
self.nodes.link(dp, inc);
self.nodes.link(inc, p);
}
} else {
let tail = self.cur_list().tail;
self.nodes.link(tail, p);
q = self.nodes.next(head);
}
self.nodes.set_next(head, NodeRef::NULL);
if !q.is_null() {
self.nodes.set_prev(q, NodeRef::NULL);
}
self.pop_nest();
q
}
pub fn math_right_brace(&mut self) {
match self.save.cur_group {
GroupCode::Math => {
self.unsave();
let r = self.pop_saved();
let root_follows = r & (1 << 62) != 0;
let (noad, f) = field_from_ref(r & !(1 << 62));
let p = self.fin_mlist(NodeRef::NULL);
let k = self.noad_field(noad, f);
self.nodes.set_kernel_list(k, p);
if !p.is_null() && self.nodes.next(p).is_null() {
let pid = self.nodes.id(p);
let flatten = self.eqtb.int(ip::MATHFLATTENMODE);
let bit = match self.nodes.subtype(p) {
noad_sub::ORD => 1,
noad_sub::BIN => 2,
noad_sub::REL => 4,
noad_sub::PUNCT => 8,
noad_sub::INNER => 16,
_ => 0,
};
if pid == id::NOAD && flatten & bit != 0 {
let (sub, sup) = (
self.noad_field(p, Field::Sub),
self.noad_field(p, Field::Sup),
);
if sub.is_null() && sup.is_null() {
let nuc = self.noad_field(p, Field::Nucleus);
self.set_noad_field(p, Field::Nucleus, NodeRef::NULL);
self.nodes.flush_list(p);
self.nodes.set_kernel_list(k, NodeRef::NULL);
self.nodes.flush_list(k);
self.set_noad_field(noad, f, nuc);
}
} else if pid == id::ACCENT
&& f == Field::Nucleus
&& noad == self.cur_list().tail
&& self.nodes.id(noad) == id::NOAD
&& self.nodes.subtype(noad) == noad_sub::ORD
{
let head = self.cur_list().head;
let mut q = head;
while self.nodes.next(q) != noad {
q = self.nodes.next(q);
}
self.nodes.set_kernel_list(k, NodeRef::NULL);
self.nodes.link(q, p);
self.nodes.set_next(noad, NodeRef::NULL);
self.nodes.flush_list(noad);
self.cur_list_mut().tail = p;
}
}
if root_follows {
let st = cramped_style(self.cur_list().math_style);
self.scan_math(noad, Field::Nucleus, st);
}
}
GroupCode::MathChoice => self.build_choices(),
GroupCode::Vcenter => {
if self.par_token_before_group_end(1) {
return;
}
self.end_graf();
self.unsave();
let (spec, _dir) = self.pop_spec_pub();
let head = self.cur_list().head;
let first = self.nodes.next(head);
self.nodes.set_next(head, NodeRef::NULL);
let first = self.filter_list("vpack_filter", first, "vcenter");
let d = self.eqtb.dimen(dp::BOXMAXDEPTH);
let b = self.vpackage(first, spec, d);
self.pop_nest();
let n = self.new_noad(noad_sub::VCENTER);
let k = self.new_sub_list(false, b);
self.set_noad_field(n, Field::Nucleus, k);
self.tail_append(n);
}
_ => self.confusion("rightbrace"),
}
}
fn append_choices(&mut self) {
let c = self.new_node(
id::CHOICE,
0,
NodeData::Choice(Box::new([NodeRef::NULL; 4])),
);
self.tail_append(c);
self.push_saved(0);
self.push_math(GroupCode::MathChoice, 0);
self.scan_left_brace();
}
fn build_choices(&mut self) {
self.unsave();
let p = self.fin_mlist(NodeRef::NULL);
let k = self.pop_saved();
let tail = self.cur_list().tail;
if let NodeData::Choice(c) = self.nodes.data_mut(tail) {
c[(k as usize).min(3)] = p;
}
if k >= 3 {
return;
}
self.push_saved(k + 1);
self.push_math(GroupCode::MathChoice, 2 * (k as i32 + 1));
self.scan_left_brace();
}
fn math_left_right(&mut self) {
let c = self.cur_chr;
if c == 14 {
self.math_vextensible();
return;
}
let t = match c % 10 {
1 => fence_sub::LEFT,
2 => fence_sub::MIDDLE,
_ => fence_sub::RIGHT,
};
if t != fence_sub::LEFT && self.save.cur_group != GroupCode::MathLeft {
if self.save.cur_group == GroupCode::MathShift {
self.get_nonblank_nonrelax_noncall();
if self.cur_cmd == Cmd::DelimNum {
if self.cur_chr == 0 {
self.scan_twenty_seven_bit_int();
} else {
self.scan_umath_triple();
}
}
self.print_err("Extra ");
if t == fence_sub::MIDDLE {
self.print_esc_str("middle");
self.help(&["I'm ignoring a \\middle that had no matching \\left."]);
} else {
self.print_esc_str("right");
self.help(&["I'm ignoring a \\right that had no matching \\left."]);
}
self.error();
} else {
self.off_save();
}
return;
}
let mut fe = FenceData {
class: -1,
..FenceData::default()
};
if c >= 10 {
loop {
if self.scan_keyword("height") {
self.scan_normal_dimen();
fe.height = self.cur_val;
} else if self.scan_keyword("depth") {
self.scan_normal_dimen();
fe.depth = self.cur_val;
} else if self.scan_keyword("axis") {
fe.options |= noad_opt::AXIS;
} else if self.scan_keyword("noaxis") {
fe.options |= noad_opt::NO_AXIS;
} else if self.scan_keyword("exact") {
fe.options |= noad_opt::EXACT;
} else if self.scan_keyword("class") {
self.scan_int();
fe.class = self.cur_val;
} else {
break;
}
}
}
fe.delim = self.scan_delimiter(false, false);
let p = self.new_node(id::FENCE, t, NodeData::Fence(Box::new(fe)));
let q = if t == fence_sub::LEFT {
p
} else {
let q = self.fin_mlist(p);
self.unsave();
q
};
if t != fence_sub::RIGHT {
let st = self.cur_list().math_style;
self.push_math(GroupCode::MathLeft, st);
let head = self.cur_list().head;
self.nodes.link(head, q);
self.cur_list_mut().tail = p;
self.cur_list_mut().delim_ptr = p;
} else {
let n = self.new_noad(noad_sub::INNER);
let k = self.new_sub_list(true, q);
self.set_noad_field(n, Field::Nucleus, k);
self.tail_append(n);
}
}
fn math_vextensible(&mut self) {
let mut fe = FenceData {
class: -1,
..FenceData::default()
};
loop {
if self.scan_keyword("height") {
self.scan_normal_dimen();
fe.height = self.cur_val;
} else if self.scan_keyword("depth") {
self.scan_normal_dimen();
fe.depth = self.cur_val;
} else if self.scan_keyword("axis") {
fe.options |= noad_opt::AXIS;
} else if self.scan_keyword("noaxis") {
fe.options |= noad_opt::NO_AXIS;
} else if self.scan_keyword("exact") {
fe.options |= noad_opt::EXACT;
} else {
break;
}
}
fe.delim = self.scan_delimiter(false, false);
let p = self.new_node(id::FENCE, fence_sub::NO, NodeData::Fence(Box::new(fe)));
let n = self.new_noad(noad_sub::INNER);
let k = self.new_sub_list(true, p);
self.set_noad_field(n, Field::Nucleus, k);
self.tail_append(n);
}
fn start_eq_no(&mut self) {
self.push_saved(i64::from(self.cur_chr));
self.go_into_ordinary_math();
}
pub fn math_box_end(&mut self, b: NodeRef) {
let n = self.new_noad(noad_sub::ORD);
let k = self.new_sub_list(false, b);
self.set_noad_field(n, Field::Nucleus, k);
self.tail_append(n);
}
pub fn math_char_in_text(&mut self) {
let mc = match self.cur_cmd {
Cmd::MathCharNum => self.scan_math_char_num(),
Cmd::MathGiven => MathChar::from_given(self.cur_chr),
_ => MathChar::from_packed(i64::from(self.cur_chr)),
};
if mc.class >= 8 {
self.treat_as_active();
return;
}
let f = self.fam_fnt(mc.fam, 0);
let g = self.new_char_node(f, mc.chr);
self.tail_append(g);
self.cur_list_mut().space_factor = 1000;
}
pub fn fam_fnt(&self, fam: u32, size: u32) -> u32 {
self.eqtb
.math_font
.get((size * crate::eqtb::NUM_FAMILIES as u32 + fam) as usize)
.map_or(0, |s| s.val)
}
fn after_math(&mut self) {
let mut m = self.mode;
let mut l = false;
let mut p = self.fin_mlist(NodeRef::NULL);
let a;
if self.mode == Mode::DisplayMath && m == Mode::Math {
self.check_dollar_follows();
let hl = self.formula_to_hlist(p, 2, false);
a = self.hpack(hl, PackSpec::NATURAL);
self.take_outer_math_dirs();
self.unsave();
if self.pop_saved() == 1 {
l = true;
}
m = self.mode;
p = self.fin_mlist(NodeRef::NULL);
} else {
a = NodeRef::NULL;
}
if m == Mode::Math {
self.finish_math_in_text(p);
} else {
if a.is_null() {
self.check_dollar_follows();
}
self.finish_displayed_math(p, a, l);
}
}
fn take_outer_math_dirs(&mut self) -> Option<(i32, i32)> {
let level = self.save.cur_level;
while let Some(&(l, m, t)) = self.outer_math_dirs.last() {
if l < level {
break;
}
self.outer_math_dirs.pop();
if l == level {
return Some((m, t));
}
}
None
}
fn check_dollar_follows(&mut self) {
self.get_x_token();
if self.cur_cmd != Cmd::MathShift
&& !(self.cur_cmd == Cmd::MathShiftCs && self.cur_chr == 1)
{
self.print_err("Display math should end with $$");
self.help(&[
"The `$' that I just saw supposedly matches a previous `$$'.",
"So I shall assume that you typed `$$' both times.",
]);
self.back_error();
}
}
fn finish_math_in_text(&mut self, p: NodeRef) {
let ms = self.eqtb.dimen(dp::MATHSURROUND);
let mg = self
.eqtb
.glue(crate::primitives_gen::assign_glue::MATHSURROUNDSKIP);
let pen = self.mode == Mode::Horizontal;
let (math_dir, text_dir) = self.take_outer_math_dirs().unwrap_or((
self.dir_param(crate::primitives_gen::assign_dir::MATHDIR),
self.dir_param(crate::primitives_gen::assign_dir::TEXTDIR),
));
let wrap = (math_dir != text_dir).then(|| math_dir.rem_euclid(4) as i8);
let b = self.new_math_node(ms, mg, 0);
self.tail_append(b);
if let Some(d) = wrap {
let p = self.new_dir_node(d, false);
self.tail_append(p);
}
let hl = self.formula_to_hlist(p, 2, pen);
let tail = self.cur_list().tail;
self.nodes.link(tail, hl);
let mut t = tail;
while !self.nodes.next(t).is_null() {
t = self.nodes.next(t);
}
self.cur_list_mut().tail = t;
if let Some(d) = wrap {
let p = self.new_dir_node(d, true);
self.tail_append(p);
}
let e = self.new_math_node(ms, mg, 1);
self.tail_append(e);
self.cur_list_mut().space_factor = 1000;
self.unsave();
}
fn new_math_node(&mut self, surround: i32, skip: crate::glue::Glue, sub: u16) -> NodeRef {
let p = self.new_node(
id::MATH,
sub,
NodeData::Math {
surround,
glue: crate::glue::Glue::ZERO,
},
);
if (skip.width != 0 || skip.stretch != 0 || skip.shrink != 0)
&& let NodeData::Math { glue, .. } = self.nodes.data_mut(p)
{
*glue = skip;
}
p
}
fn finish_displayed_math(&mut self, p: NodeRef, a: NodeRef, l: bool) {
use crate::nodes::glue_sub as gs;
self.take_outer_math_dirs();
let hl = self.formula_to_hlist(p, 0, false);
let adj_head = self.nodes.new_temp();
self.ts.adjust_tail = Some(adj_head);
let mut b = self.hpack(hl, PackSpec::NATURAL);
let p = self.nodes.list_ptr(b);
let t = self.ts.adjust_tail.unwrap_or(adj_head);
self.ts.adjust_tail = None;
let mut w = self.nodes.box_data(b).map_or(0, |x| x.width);
let z = self.eqtb.dimen(dp::DISPLAYWIDTH);
let s = self.eqtb.dimen(dp::DISPLAYINDENT);
let (mut e, q) = if a.is_null() {
(0, 0)
} else {
let e = self.nodes.box_data(a).map_or(0, |x| x.width);
let f2 = self.fam_fnt(2, 0);
let mq = self.math_quad_text(f2);
let step = self.eqtb.int(ip::MATHEQNOGAPSTEP);
let gap = if step == 1000 {
mq
} else {
retex_arith::xn_over_d(mq, step, 1000).map_or(0, |r| r.quotient)
};
(e, e + gap)
};
if w + q > z {
let tot = self.ts.last_totals;
if e != 0
&& (w - tot.1[0] + q <= z
|| tot.1[1] != 0
|| tot.1[2] != 0
|| tot.1[3] != 0
|| tot.1[4] != 0)
{
self.nodes.set_list_ptr(b, NodeRef::NULL);
self.nodes.free(b);
b = self.hpack(p, PackSpec::Exactly(z - q));
} else {
e = 0;
if w > z {
self.nodes.set_list_ptr(b, NodeRef::NULL);
self.nodes.free(b);
b = self.hpack(p, PackSpec::Exactly(z));
}
}
w = self.nodes.box_data(b).map_or(0, |x| x.width);
}
self.nodes.set_subtype(b, 6); let mut d = half(z - w);
if e > 0 && d < 2 * e {
d = half(z - w - e);
let p = self.nodes.list_ptr(b);
if !p.is_null() && self.nodes.id(p) == id::GLUE {
d = 0;
}
}
let pen = self.new_penalty_node(self.eqtb.int(ip::PREDISPLAYPENALTY));
self.tail_append(pen);
let pds = self.eqtb.dimen(dp::PREDISPLAYSIZE);
let (g1, mut g2) = if d + s <= pds || l {
(gs::ABOVE_DISPLAY, gs::BELOW_DISPLAY)
} else {
(gs::ABOVE_DISPLAY_SHORT, gs::BELOW_DISPLAY_SHORT)
};
let mode = self.eqtb.int(ip::MATHDISPLAYSKIPMODE);
if l && e == 0 {
if let Some(bd) = self.nodes.box_data_mut(a) {
bd.shift = s;
}
self.nodes.set_subtype(a, 7);
self.append_to_vlist(a);
let pen = self.new_penalty_node(10000);
self.tail_append(pen);
} else {
self.append_display_skip(g1, mode, true);
}
if e != 0 {
let r = self.new_kern_node(z - w - e - d, 0);
self.nodes.set_subtype(a, 7);
let k = self.new_kern_node(if l { z - w - d } else { d }, 0);
if l {
self.nodes.link(a, r);
self.nodes.link(r, b);
self.nodes.link(b, k);
b = a;
} else {
self.nodes.link(k, b);
self.nodes.link(b, r);
self.nodes.link(r, a);
b = k;
}
b = self.hpack(b, PackSpec::NATURAL);
d = 0;
}
if let Some(bd) = self.nodes.box_data_mut(b) {
bd.shift = s + d;
}
self.append_to_vlist(b);
if !a.is_null() && e == 0 && !l {
let pen = self.new_penalty_node(10000);
self.tail_append(pen);
let aw = self.nodes.box_data(a).map_or(0, |x| x.width);
if let Some(bd) = self.nodes.box_data_mut(a) {
bd.shift = s + z - aw;
}
self.nodes.set_subtype(a, 7);
self.append_to_vlist(a);
g2 = 0;
}
if t != adj_head {
let first = self.nodes.next(adj_head);
let tail = self.cur_list().tail;
self.nodes.link(tail, first);
self.cur_list_mut().tail = t;
}
self.nodes.free(adj_head);
let pen = self.new_penalty_node(self.eqtb.int(ip::POSTDISPLAYPENALTY));
self.tail_append(pen);
if g2 > 0 {
self.append_display_skip(g2, mode, false);
}
self.resume_after_display();
}
fn append_display_skip(&mut self, sub: u16, mode: i32, _above: bool) {
let g = self.eqtb.glue(crate::build::glue_param_of_subtype(sub));
let zero = g.width == 0 && g.stretch == 0 && g.shrink == 0;
let skip = match mode {
2 => zero,
3 => true,
_ => false,
};
if !skip {
let n = self.new_param_glue(sub);
self.tail_append(n);
}
}
fn math_quad_text(&self, f2: u32) -> i32 {
let v = self.math_param_value(0, 2);
if v != crate::codes::UNDEFINED_MATH_PARAM {
v
} else {
self.quad(f2)
}
}
pub fn math_param_value(&self, p: u32, s: u32) -> i32 {
self.eqtb
.math_param
.get(&(p * 8 + s))
.map_or(crate::codes::UNDEFINED_MATH_PARAM, |x| x.val)
}
fn resume_after_display(&mut self) {
if self.save.cur_group != GroupCode::MathShift {
self.confusion("display");
}
self.unsave();
let pg = self.cur_list().prev_graf + 3;
self.cur_list_mut().prev_graf = pg;
self.push_nest(Mode::Horizontal);
self.cur_list_mut().space_factor = 1000;
let l = self.cur_lang() as i32;
self.cur_list_mut().clang = l;
let lp = self.new_local_par();
self.tail_append(lp);
self.get_x_token();
if self.cur_cmd != Cmd::Spacer {
self.back_input();
}
if self.nest.len() == 2 {
self.build_page_from("after_display");
}
}
pub fn finish_display_alignment(&mut self, p: NodeRef, q: NodeRef, aux_save: i32) {
self.do_assignments();
if self.cur_cmd != Cmd::MathShift {
self.print_err("Missing $$ inserted");
self.help(&[
"Displays can use special alignments (like \\eqalignno)",
"only if nothing but the alignment itself is between $$'s.",
]);
self.back_error();
} else {
self.check_dollar_follows();
}
self.pop_nest();
let pen = self.new_penalty_node(self.eqtb.int(ip::PREDISPLAYPENALTY));
self.tail_append(pen);
let g = self.new_param_glue(crate::nodes::glue_sub::ABOVE_DISPLAY);
self.tail_append(g);
let tail = self.cur_list().tail;
self.nodes.link(tail, p);
if !p.is_null() {
self.cur_list_mut().tail = q;
}
let pen = self.new_penalty_node(self.eqtb.int(ip::POSTDISPLAYPENALTY));
self.tail_append(pen);
let g = self.new_param_glue(crate::nodes::glue_sub::BELOW_DISPLAY);
self.tail_append(g);
self.cur_list_mut().prev_depth = aux_save;
self.resume_after_display();
}
pub fn flush_math(&mut self) {
let head = self.cur_list().head;
let first = self.nodes.next(head);
self.nodes.flush_list(first);
let inc = self.cur_list().incompleat_noad;
self.nodes.flush_list(inc);
self.nodes.set_next(head, NodeRef::NULL);
self.cur_list_mut().tail = head;
self.cur_list_mut().incompleat_noad = NodeRef::NULL;
}
pub fn ins_tokens(&mut self, toks: Vec<Token>) {
self.ins_list(Rc::from(toks));
}
}
pub use retex_arith::half;
fn classic_delimiter(v: i32) -> Option<(u32, u32, u32, u32)> {
if v < 0 {
return None;
}
let v = v as u32;
Some((
(v / 0o4000000) % 16,
(v / 0o10000) % 256,
(v / 256) % 16,
v % 256,
))
}