use crate::compile::CompiledExpr;
use crate::primitives::PrimDef;
use crate::quoted::{BText, IText, MathElem};
use rustyfi_backend::{
AnnotAction, Color, Context, DecoId, DocExtras, FrameDecoration, HorzBox, HyphenLang, ImageId,
ImageResource, Length, MathCharClass, MathKind, Page, PageGeometry, VertBox,
};
use std::cell::RefCell;
use std::collections::{BTreeMap, HashMap};
use std::rc::Rc;
#[allow(private_interfaces)]
#[derive(Clone, Debug)]
pub enum Value {
Unit,
Bool(bool),
Int(i64),
Float(f64),
Length(Length),
Str(String),
List(Vec<Value>),
Tuple(Vec<Value>),
Ctor(String, Option<Box<Value>>),
Record(BTreeMap<String, Value>),
Context(Box<Context>),
InlineText {
elems: Rc<Vec<IText>>,
env: Env,
},
BlockText {
elems: Rc<Vec<BText>>,
env: Env,
},
MathText {
elems: Rc<Vec<MathElem>>,
env: Env,
},
Math(Rc<Vec<Math>>),
MathBoxes(Rc<Vec<Math>>),
Ref(Rc<RefCell<Value>>),
InlineBoxes(Vec<HorzBox>),
BlockBoxes(Vec<VertBox>),
Image(ImageId),
Document(Rc<DocumentValue>),
CompiledClosure {
opt_labels: Vec<String>,
body: CompiledExpr,
env: Env,
},
Code {
body: CompiledExpr,
env: Env,
},
Prim {
def: &'static PrimDef,
applied: Vec<Value>,
},
PrePath(rustyfi_backend::PrePath),
Path(rustyfi_backend::Path),
Graphics(rustyfi_backend::GraphicsElem),
Font(rustyfi_backend::FontKey),
TextInfo(TextInfo),
Hyphenation(HyphenLang),
}
impl Value {
pub fn type_name(&self) -> &'static str {
match self {
Value::Unit => "unit",
Value::Bool(_) => "bool",
Value::Int(_) => "int",
Value::Float(_) => "float",
Value::Length(_) => "length",
Value::Str(_) => "string",
Value::List(_) => "list",
Value::Tuple(_) => "tuple",
Value::Ctor(_, _) => "variant",
Value::Record(_) => "record",
Value::Context(_) => "context",
Value::InlineText { .. } => "inline-text",
Value::BlockText { .. } => "block-text",
Value::MathText { .. } => "math",
Value::Math(_) => "math",
Value::MathBoxes(_) => "math-boxes",
Value::Ref(_) => "mutable",
Value::InlineBoxes(_) => "inline-boxes",
Value::BlockBoxes(_) => "block-boxes",
Value::Image(_) => "image",
Value::Document(_) => "document",
Value::CompiledClosure { .. } => "function",
Value::Code { .. } => "code",
Value::Prim { .. } => "function",
Value::PrePath(_) => "pre-path",
Value::Path(_) => "path",
Value::Graphics(_) => "graphics",
Value::Font(_) => "font",
Value::TextInfo(_) => "text-info",
Value::Hyphenation(_) => "hyphenation",
}
}
}
#[derive(Clone, Debug)]
pub enum Math {
Pure(MathElement),
Group(MathKind, MathKind, Vec<Math>),
Sup(Vec<Math>, Vec<Math>),
Sub(Vec<Math>, Vec<Math>),
ChangeColor(Color, Vec<Math>),
ChangeCharClass(MathCharClass, Vec<Math>),
Fraction(Vec<Math>, Vec<Math>),
Radical(Option<Vec<Math>>, Vec<Math>),
Paren(Box<Value>, Box<Value>, Vec<Math>),
ParenWithMiddle(Box<Value>, Box<Value>, Box<Value>, Vec<Vec<Math>>),
UpperLimit(Vec<Math>, Vec<Math>),
LowerLimit(Vec<Math>, Vec<Math>),
PullInScripts(MathKind, MathKind, Box<Value>),
WithContext(Box<Context>, Vec<Math>),
}
#[derive(Clone, Debug)]
pub enum MathElement {
Char {
class: MathKind,
big: bool,
chars: String,
},
CharWithKern {
class: MathKind,
big: bool,
chars: String,
kern_l: Box<Value>,
kern_r: Box<Value>,
},
EmbeddedText { class: MathKind, body: Box<Value> },
VariantChar {
class: MathKind,
big: bool,
style: Box<MathVariantStyle>,
},
VariantCharPending(String),
EmbeddedBoxes {
class: MathKind,
boxes: Vec<HorzBox>,
},
}
#[derive(Clone, Debug)]
pub struct MathVariantStyle {
pub italic: String,
pub bold_italic: String,
pub roman: String,
pub bold_roman: String,
pub script: String,
pub bold_script: String,
pub fraktur: String,
pub bold_fraktur: String,
pub double_struck: String,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct TextInfo {
pub indent: i64,
}
#[derive(Clone, Debug)]
pub struct DocumentValue {
pub geometry: PageGeometry,
pub pages: Vec<Page>,
pub images: Vec<ImageResource>,
pub extras: DocExtras,
pub reflow_source: Option<Vec<VertBox>>,
pub reflow_links: Vec<(DecoId, AnnotAction)>,
pub reflow_dests: Vec<(DecoId, String)>,
pub reflow_frame_decos: Vec<(DecoId, FrameDecoration)>,
}
#[derive(Default)]
struct FxHasher {
hash: usize,
}
#[cfg(target_pointer_width = "64")]
const FX_SEED: usize = 0x51_7c_c1_b7_27_22_0a_95;
#[cfg(not(target_pointer_width = "64"))]
const FX_SEED: usize = 0x9e_37_79_b9;
impl FxHasher {
#[inline]
fn add(&mut self, i: usize) {
self.hash = (self.hash.rotate_left(5) ^ i).wrapping_mul(FX_SEED);
}
}
impl std::hash::Hasher for FxHasher {
#[inline]
fn write(&mut self, mut bytes: &[u8]) {
const WIDE: usize = std::mem::size_of::<usize>();
while bytes.len() >= WIDE {
let (head, rest) = bytes.split_at(WIDE);
self.add(usize::from_le_bytes(head.try_into().unwrap()));
bytes = rest;
}
if WIDE > 4 && bytes.len() >= 4 {
self.add(u32::from_le_bytes(bytes[..4].try_into().unwrap()) as usize);
bytes = &bytes[4..];
}
if bytes.len() >= 2 {
self.add(u16::from_le_bytes(bytes[..2].try_into().unwrap()) as usize);
bytes = &bytes[2..];
}
if let Some(&b) = bytes.first() {
self.add(b as usize);
}
}
#[inline]
fn write_u8(&mut self, i: u8) {
self.add(i as usize);
}
#[inline]
fn write_usize(&mut self, i: usize) {
self.add(i);
}
#[inline]
fn finish(&self) -> u64 {
self.hash as u64
}
}
#[derive(Default, Clone)]
struct FxBuild;
impl std::hash::BuildHasher for FxBuild {
type Hasher = FxHasher;
#[inline]
fn build_hasher(&self) -> FxHasher {
FxHasher::default()
}
}
type FxMap = HashMap<Rc<str>, Value, FxBuild>;
#[derive(Clone, Debug, Default)]
pub struct BaseEnv {
vars: FxMap,
}
impl BaseEnv {
pub fn new() -> BaseEnv {
BaseEnv::default()
}
pub fn child(&self) -> BaseEnv {
self.clone()
}
pub fn define(&mut self, name: impl Into<Rc<str>>, value: Value) {
self.vars.insert(name.into(), value);
}
pub fn lookup(&self, name: &str) -> Option<Value> {
self.vars.get(name).cloned()
}
pub fn names(&self) -> Vec<String> {
self.vars.keys().map(|k| k.to_string()).collect()
}
}
#[derive(Clone, Debug)]
pub struct Env(Rc<Frame>);
#[derive(Debug)]
struct Frame {
slots: RefCell<Vec<Value>>,
parent: Option<Env>,
}
impl Env {
pub fn root() -> Env {
Env(Rc::new(Frame {
slots: RefCell::new(Vec::new()),
parent: None,
}))
}
pub fn child(&self, slots: Vec<Value>) -> Env {
Env(Rc::new(Frame {
slots: RefCell::new(slots),
parent: Some(self.clone()),
}))
}
#[inline]
fn frame_at(&self, depth: u16) -> &Frame {
let mut f = self;
for _ in 0..depth {
f =
f.0.parent
.as_ref()
.expect("compiled slot depth exceeds the runtime frame chain");
}
&f.0
}
#[inline]
pub fn slot(&self, depth: u16, index: u16) -> Value {
self.frame_at(depth).slots.borrow()[index as usize].clone()
}
#[inline]
pub fn set_slot(&self, depth: u16, index: u16, value: Value) {
self.frame_at(depth).slots.borrow_mut()[index as usize] = value;
}
}