mirror of
https://github.com/typst/typst
synced 2025-06-28 08:12:53 +08:00
Split up math library
This commit is contained in:
parent
e38839c287
commit
e460da1ce7
112
library/src/math/accent.rs
Normal file
112
library/src/math/accent.rs
Normal file
@ -0,0 +1,112 @@
|
|||||||
|
use super::*;
|
||||||
|
|
||||||
|
/// # Accent
|
||||||
|
/// An accented node.
|
||||||
|
///
|
||||||
|
/// ## Example
|
||||||
|
/// ```
|
||||||
|
/// $acc(a, ->) != acc(a, ~)$ \
|
||||||
|
/// $acc(a, `) = acc(a, grave)$
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// ## Parameters
|
||||||
|
/// - base: Content (positional, required)
|
||||||
|
/// The base to which the accent is applied.
|
||||||
|
/// May consist of multiple letters.
|
||||||
|
///
|
||||||
|
/// ### Example
|
||||||
|
/// ```
|
||||||
|
/// $acc(A B C, ->)$
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// - accent: Content (positional, required)
|
||||||
|
/// The accent to apply to the base.
|
||||||
|
///
|
||||||
|
/// Supported accents include:
|
||||||
|
/// - Grave: `` ` ``
|
||||||
|
/// - Acute: `´`
|
||||||
|
/// - Circumflex: `^`
|
||||||
|
/// - Tilde: `~`
|
||||||
|
/// - Macron: `¯`
|
||||||
|
/// - Overline: `‾`
|
||||||
|
/// - Breve: `˘`
|
||||||
|
/// - Dot: `.`
|
||||||
|
/// - Diaeresis: `¨`
|
||||||
|
/// - Caron: `ˇ`
|
||||||
|
/// - Arrow: `→`
|
||||||
|
///
|
||||||
|
/// ## Category
|
||||||
|
/// math
|
||||||
|
#[func]
|
||||||
|
#[capable(Texify)]
|
||||||
|
#[derive(Debug, Hash)]
|
||||||
|
pub struct AccNode {
|
||||||
|
/// The accent base.
|
||||||
|
pub base: Content,
|
||||||
|
/// The Unicode accent character.
|
||||||
|
pub accent: char,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[node]
|
||||||
|
impl AccNode {
|
||||||
|
fn construct(_: &Vm, args: &mut Args) -> SourceResult<Content> {
|
||||||
|
let base = args.expect("base")?;
|
||||||
|
let Spanned { v, span } = args.expect::<Spanned<Content>>("accent")?;
|
||||||
|
let accent = match extract(&v) {
|
||||||
|
Some(Ok(c)) => c,
|
||||||
|
Some(Err(msg)) => bail!(span, "{}", msg),
|
||||||
|
None => bail!(span, "not an accent"),
|
||||||
|
};
|
||||||
|
Ok(Self { base, accent }.pack())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[rustfmt::skip]
|
||||||
|
fn extract(content: &Content) -> Option<Result<char, &'static str>> {
|
||||||
|
let MathNode { children, .. } = content.to::<MathNode>()?;
|
||||||
|
let [child] = children.as_slice() else { return None };
|
||||||
|
let c = if let Some(atom) = child.to::<AtomNode>() {
|
||||||
|
let mut chars = atom.0.chars();
|
||||||
|
chars.next().filter(|_| chars.next().is_none())?
|
||||||
|
} else if let Some(symbol) = child.to::<SymbolNode>() {
|
||||||
|
match symmie::get(&symbol.0) {
|
||||||
|
Some(c) => c,
|
||||||
|
None => return Some(Err("unknown symbol")),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return None;
|
||||||
|
};
|
||||||
|
|
||||||
|
Some(Ok(match c {
|
||||||
|
'`' | '\u{300}' => '\u{300}', // Grave
|
||||||
|
'´' | '\u{301}' => '\u{301}', // Acute
|
||||||
|
'^' | '\u{302}' => '\u{302}', // Circumflex
|
||||||
|
'~' | '\u{223C}' | '\u{303}' => '\u{303}', // Tilde
|
||||||
|
'¯' | '\u{304}' => '\u{304}', // Macron
|
||||||
|
'‾' | '\u{305}' => '\u{305}', // Overline
|
||||||
|
'˘' | '\u{306}' => '\u{306}', // Breve
|
||||||
|
'.' | '\u{22C5}' | '\u{307}' => '\u{307}', // Dot
|
||||||
|
'¨' | '\u{308}' => '\u{308}', // Diaeresis
|
||||||
|
'ˇ' | '\u{30C}' => '\u{30C}', // Caron
|
||||||
|
'→' | '\u{20D7}' => '\u{20D7}', // Arrow
|
||||||
|
_ => return None,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Texify for AccNode {
|
||||||
|
fn texify(&self, t: &mut Texifier) -> SourceResult<()> {
|
||||||
|
if let Some(sym) = unicode_math::SYMBOLS.iter().find(|sym| {
|
||||||
|
sym.codepoint == self.accent
|
||||||
|
&& sym.atom_type == unicode_math::AtomType::Accent
|
||||||
|
}) {
|
||||||
|
t.push_str("\\");
|
||||||
|
t.push_str(sym.name);
|
||||||
|
t.push_str("{");
|
||||||
|
self.base.texify(t)?;
|
||||||
|
t.push_str("}");
|
||||||
|
} else {
|
||||||
|
self.base.texify(t)?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
48
library/src/math/atom.rs
Normal file
48
library/src/math/atom.rs
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
use super::*;
|
||||||
|
|
||||||
|
/// # Atom
|
||||||
|
/// An atom in a math formula: `x`, `+`, `12`.
|
||||||
|
///
|
||||||
|
/// ## Parameters
|
||||||
|
/// - text: EcoString (positional, required)
|
||||||
|
/// The atom's text.
|
||||||
|
///
|
||||||
|
/// ## Category
|
||||||
|
/// math
|
||||||
|
#[func]
|
||||||
|
#[capable(Texify)]
|
||||||
|
#[derive(Debug, Hash)]
|
||||||
|
pub struct AtomNode(pub EcoString);
|
||||||
|
|
||||||
|
#[node]
|
||||||
|
impl AtomNode {
|
||||||
|
fn construct(_: &Vm, args: &mut Args) -> SourceResult<Content> {
|
||||||
|
Ok(Self(args.expect("text")?).pack())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Texify for AtomNode {
|
||||||
|
fn texify(&self, t: &mut Texifier) -> SourceResult<()> {
|
||||||
|
let multi = self.0.graphemes(true).count() > 1;
|
||||||
|
if multi {
|
||||||
|
t.push_str("\\mathrm{");
|
||||||
|
}
|
||||||
|
|
||||||
|
for c in self.0.chars() {
|
||||||
|
let supportive = c == '|';
|
||||||
|
if supportive {
|
||||||
|
t.support();
|
||||||
|
}
|
||||||
|
t.push_escaped(c);
|
||||||
|
if supportive {
|
||||||
|
t.support();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if multi {
|
||||||
|
t.push_str("}");
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
102
library/src/math/frac.rs
Normal file
102
library/src/math/frac.rs
Normal file
@ -0,0 +1,102 @@
|
|||||||
|
use super::*;
|
||||||
|
|
||||||
|
/// # Fraction
|
||||||
|
/// A mathematical fraction.
|
||||||
|
///
|
||||||
|
/// ## Syntax
|
||||||
|
/// This function also has dedicated syntax: Use a slash to turn neighbouring
|
||||||
|
/// expressions into a fraction. Multiple atoms can be grouped into a single
|
||||||
|
/// expression using round grouping parenthesis. Such parentheses are removed
|
||||||
|
/// from the output, but you can nest multiple to force them.
|
||||||
|
///
|
||||||
|
/// ## Example
|
||||||
|
/// ```
|
||||||
|
/// $ 1/2 < (x+1)/2 $
|
||||||
|
/// $ ((x+1)) / 2 = frac(a, b) $
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// ## Parameters
|
||||||
|
/// - num: Content (positional, required)
|
||||||
|
/// The fraction's numerator.
|
||||||
|
///
|
||||||
|
/// - denom: Content (positional, required)
|
||||||
|
/// The fraction's denominator.
|
||||||
|
///
|
||||||
|
/// ## Category
|
||||||
|
/// math
|
||||||
|
#[func]
|
||||||
|
#[capable(Texify)]
|
||||||
|
#[derive(Debug, Hash)]
|
||||||
|
pub struct FracNode {
|
||||||
|
/// The numerator.
|
||||||
|
pub num: Content,
|
||||||
|
/// The denominator.
|
||||||
|
pub denom: Content,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[node]
|
||||||
|
impl FracNode {
|
||||||
|
fn construct(_: &Vm, args: &mut Args) -> SourceResult<Content> {
|
||||||
|
let num = args.expect("numerator")?;
|
||||||
|
let denom = args.expect("denominator")?;
|
||||||
|
Ok(Self { num, denom }.pack())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Texify for FracNode {
|
||||||
|
fn texify(&self, t: &mut Texifier) -> SourceResult<()> {
|
||||||
|
t.push_str("\\frac{");
|
||||||
|
self.num.texify_unparen(t)?;
|
||||||
|
t.push_str("}{");
|
||||||
|
self.denom.texify_unparen(t)?;
|
||||||
|
t.push_str("}");
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// # Binomial
|
||||||
|
/// A binomial expression.
|
||||||
|
///
|
||||||
|
/// ## Example
|
||||||
|
/// ```
|
||||||
|
/// $ binom(n, k) $
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// ## Parameters
|
||||||
|
/// - upper: Content (positional, required)
|
||||||
|
/// The binomial's upper index.
|
||||||
|
///
|
||||||
|
/// - lower: Content (positional, required)
|
||||||
|
/// The binomial's lower index.
|
||||||
|
///
|
||||||
|
/// ## Category
|
||||||
|
/// math
|
||||||
|
#[func]
|
||||||
|
#[capable(Texify)]
|
||||||
|
#[derive(Debug, Hash)]
|
||||||
|
pub struct BinomNode {
|
||||||
|
/// The upper index.
|
||||||
|
pub upper: Content,
|
||||||
|
/// The lower index.
|
||||||
|
pub lower: Content,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[node]
|
||||||
|
impl BinomNode {
|
||||||
|
fn construct(_: &Vm, args: &mut Args) -> SourceResult<Content> {
|
||||||
|
let upper = args.expect("upper index")?;
|
||||||
|
let lower = args.expect("lower index")?;
|
||||||
|
Ok(Self { upper, lower }.pack())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Texify for BinomNode {
|
||||||
|
fn texify(&self, t: &mut Texifier) -> SourceResult<()> {
|
||||||
|
t.push_str("\\binom{");
|
||||||
|
self.upper.texify(t)?;
|
||||||
|
t.push_str("}{");
|
||||||
|
self.lower.texify(t)?;
|
||||||
|
t.push_str("}");
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
71
library/src/math/group.rs
Normal file
71
library/src/math/group.rs
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
use super::*;
|
||||||
|
|
||||||
|
/// # Floor
|
||||||
|
/// A floored expression.
|
||||||
|
///
|
||||||
|
/// ## Example
|
||||||
|
/// ```
|
||||||
|
/// $ floor(x/2) $
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// ## Parameters
|
||||||
|
/// - body: Content (positional, required)
|
||||||
|
/// The expression to floor.
|
||||||
|
///
|
||||||
|
/// ## Category
|
||||||
|
/// math
|
||||||
|
#[func]
|
||||||
|
#[capable(Texify)]
|
||||||
|
#[derive(Debug, Hash)]
|
||||||
|
pub struct FloorNode(pub Content);
|
||||||
|
|
||||||
|
#[node]
|
||||||
|
impl FloorNode {
|
||||||
|
fn construct(_: &Vm, args: &mut Args) -> SourceResult<Content> {
|
||||||
|
Ok(Self(args.expect("body")?).pack())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Texify for FloorNode {
|
||||||
|
fn texify(&self, t: &mut Texifier) -> SourceResult<()> {
|
||||||
|
t.push_str("\\left\\lfloor ");
|
||||||
|
self.0.texify(t)?;
|
||||||
|
t.push_str("\\right\\rfloor ");
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// # Ceil
|
||||||
|
/// A ceiled expression.
|
||||||
|
///
|
||||||
|
/// ## Example
|
||||||
|
/// ```
|
||||||
|
/// $ ceil(x/2) $
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// ## Parameters
|
||||||
|
/// - body: Content (positional, required)
|
||||||
|
/// The expression to ceil.
|
||||||
|
///
|
||||||
|
/// ## Category
|
||||||
|
/// math
|
||||||
|
#[func]
|
||||||
|
#[capable(Texify)]
|
||||||
|
#[derive(Debug, Hash)]
|
||||||
|
pub struct CeilNode(pub Content);
|
||||||
|
|
||||||
|
#[node]
|
||||||
|
impl CeilNode {
|
||||||
|
fn construct(_: &Vm, args: &mut Args) -> SourceResult<Content> {
|
||||||
|
Ok(Self(args.expect("body")?).pack())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Texify for CeilNode {
|
||||||
|
fn texify(&self, t: &mut Texifier) -> SourceResult<()> {
|
||||||
|
t.push_str("\\left\\lceil ");
|
||||||
|
self.0.texify(t)?;
|
||||||
|
t.push_str("\\right\\rceil ");
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
@ -1,10 +1,22 @@
|
|||||||
//! Mathematical formulas.
|
//! Mathematical formulas.
|
||||||
|
|
||||||
|
mod accent;
|
||||||
|
mod atom;
|
||||||
|
mod frac;
|
||||||
|
mod group;
|
||||||
mod matrix;
|
mod matrix;
|
||||||
|
mod root;
|
||||||
|
mod script;
|
||||||
mod style;
|
mod style;
|
||||||
mod tex;
|
mod tex;
|
||||||
|
|
||||||
|
pub use self::accent::*;
|
||||||
|
pub use self::atom::*;
|
||||||
|
pub use self::frac::*;
|
||||||
|
pub use self::group::*;
|
||||||
pub use self::matrix::*;
|
pub use self::matrix::*;
|
||||||
|
pub use self::root::*;
|
||||||
|
pub use self::script::*;
|
||||||
pub use self::style::*;
|
pub use self::style::*;
|
||||||
|
|
||||||
use typst::model::{Guard, SequenceNode};
|
use typst::model::{Guard, SequenceNode};
|
||||||
@ -297,334 +309,6 @@ impl Texify for Content {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// # Atom
|
|
||||||
/// An atom in a math formula: `x`, `+`, `12`.
|
|
||||||
///
|
|
||||||
/// ## Parameters
|
|
||||||
/// - text: EcoString (positional, required)
|
|
||||||
/// The atom's text.
|
|
||||||
///
|
|
||||||
/// ## Category
|
|
||||||
/// math
|
|
||||||
#[func]
|
|
||||||
#[capable(Texify)]
|
|
||||||
#[derive(Debug, Hash)]
|
|
||||||
pub struct AtomNode(pub EcoString);
|
|
||||||
|
|
||||||
#[node]
|
|
||||||
impl AtomNode {
|
|
||||||
fn construct(_: &Vm, args: &mut Args) -> SourceResult<Content> {
|
|
||||||
Ok(Self(args.expect("text")?).pack())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Texify for AtomNode {
|
|
||||||
fn texify(&self, t: &mut Texifier) -> SourceResult<()> {
|
|
||||||
let multi = self.0.graphemes(true).count() > 1;
|
|
||||||
if multi {
|
|
||||||
t.push_str("\\mathrm{");
|
|
||||||
}
|
|
||||||
|
|
||||||
for c in self.0.chars() {
|
|
||||||
let supportive = c == '|';
|
|
||||||
if supportive {
|
|
||||||
t.support();
|
|
||||||
}
|
|
||||||
t.push_escaped(c);
|
|
||||||
if supportive {
|
|
||||||
t.support();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if multi {
|
|
||||||
t.push_str("}");
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// # Accent
|
|
||||||
/// An accented node.
|
|
||||||
///
|
|
||||||
/// ## Example
|
|
||||||
/// ```
|
|
||||||
/// $acc(a, ->) != acc(a, ~)$ \
|
|
||||||
/// $acc(a, `) = acc(a, grave)$
|
|
||||||
/// ```
|
|
||||||
///
|
|
||||||
/// ## Parameters
|
|
||||||
/// - base: Content (positional, required)
|
|
||||||
/// The base to which the accent is applied.
|
|
||||||
/// May consist of multiple letters.
|
|
||||||
///
|
|
||||||
/// ### Example
|
|
||||||
/// ```
|
|
||||||
/// $acc(A B C, ->)$
|
|
||||||
/// ```
|
|
||||||
///
|
|
||||||
/// - accent: Content (positional, required)
|
|
||||||
/// The accent to apply to the base.
|
|
||||||
///
|
|
||||||
/// Supported accents include:
|
|
||||||
/// - Grave: `` ` ``
|
|
||||||
/// - Acute: `´`
|
|
||||||
/// - Circumflex: `^`
|
|
||||||
/// - Tilde: `~`
|
|
||||||
/// - Macron: `¯`
|
|
||||||
/// - Overline: `‾`
|
|
||||||
/// - Breve: `˘`
|
|
||||||
/// - Dot: `.`
|
|
||||||
/// - Diaeresis: `¨`
|
|
||||||
/// - Caron: `ˇ`
|
|
||||||
/// - Arrow: `→`
|
|
||||||
///
|
|
||||||
/// ## Category
|
|
||||||
/// math
|
|
||||||
#[func]
|
|
||||||
#[capable(Texify)]
|
|
||||||
#[derive(Debug, Hash)]
|
|
||||||
pub struct AccNode {
|
|
||||||
/// The accent base.
|
|
||||||
pub base: Content,
|
|
||||||
/// The Unicode accent character.
|
|
||||||
pub accent: char,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[node]
|
|
||||||
impl AccNode {
|
|
||||||
fn construct(_: &Vm, args: &mut Args) -> SourceResult<Content> {
|
|
||||||
let base = args.expect("base")?;
|
|
||||||
let Spanned { v, span } = args.expect::<Spanned<Content>>("accent")?;
|
|
||||||
let accent = match extract(&v) {
|
|
||||||
Some(Ok(c)) => c,
|
|
||||||
Some(Err(msg)) => bail!(span, "{}", msg),
|
|
||||||
None => bail!(span, "not an accent"),
|
|
||||||
};
|
|
||||||
Ok(Self { base, accent }.pack())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[rustfmt::skip]
|
|
||||||
fn extract(content: &Content) -> Option<Result<char, &'static str>> {
|
|
||||||
let MathNode { children, .. } = content.to::<MathNode>()?;
|
|
||||||
let [child] = children.as_slice() else { return None };
|
|
||||||
let c = if let Some(atom) = child.to::<AtomNode>() {
|
|
||||||
let mut chars = atom.0.chars();
|
|
||||||
chars.next().filter(|_| chars.next().is_none())?
|
|
||||||
} else if let Some(symbol) = child.to::<SymbolNode>() {
|
|
||||||
match symmie::get(&symbol.0) {
|
|
||||||
Some(c) => c,
|
|
||||||
None => return Some(Err("unknown symbol")),
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return None;
|
|
||||||
};
|
|
||||||
|
|
||||||
Some(Ok(match c {
|
|
||||||
'`' | '\u{300}' => '\u{300}', // Grave
|
|
||||||
'´' | '\u{301}' => '\u{301}', // Acute
|
|
||||||
'^' | '\u{302}' => '\u{302}', // Circumflex
|
|
||||||
'~' | '\u{223C}' | '\u{303}' => '\u{303}', // Tilde
|
|
||||||
'¯' | '\u{304}' => '\u{304}', // Macron
|
|
||||||
'‾' | '\u{305}' => '\u{305}', // Overline
|
|
||||||
'˘' | '\u{306}' => '\u{306}', // Breve
|
|
||||||
'.' | '\u{22C5}' | '\u{307}' => '\u{307}', // Dot
|
|
||||||
'¨' | '\u{308}' => '\u{308}', // Diaeresis
|
|
||||||
'ˇ' | '\u{30C}' => '\u{30C}', // Caron
|
|
||||||
'→' | '\u{20D7}' => '\u{20D7}', // Arrow
|
|
||||||
_ => return None,
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Texify for AccNode {
|
|
||||||
fn texify(&self, t: &mut Texifier) -> SourceResult<()> {
|
|
||||||
if let Some(sym) = unicode_math::SYMBOLS.iter().find(|sym| {
|
|
||||||
sym.codepoint == self.accent
|
|
||||||
&& sym.atom_type == unicode_math::AtomType::Accent
|
|
||||||
}) {
|
|
||||||
t.push_str("\\");
|
|
||||||
t.push_str(sym.name);
|
|
||||||
t.push_str("{");
|
|
||||||
self.base.texify(t)?;
|
|
||||||
t.push_str("}");
|
|
||||||
} else {
|
|
||||||
self.base.texify(t)?;
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// # Fraction
|
|
||||||
/// A mathematical fraction.
|
|
||||||
///
|
|
||||||
/// ## Syntax
|
|
||||||
/// This function also has dedicated syntax: Use a slash to turn neighbouring
|
|
||||||
/// expressions into a fraction. Multiple atoms can be grouped into a single
|
|
||||||
/// expression using round grouping parenthesis. Such parentheses are removed
|
|
||||||
/// from the output, but you can nest multiple to force them.
|
|
||||||
///
|
|
||||||
/// ## Example
|
|
||||||
/// ```
|
|
||||||
/// $ 1/2 < (x+1)/2 $
|
|
||||||
/// $ ((x+1)) / 2 = frac(a, b) $
|
|
||||||
/// ```
|
|
||||||
///
|
|
||||||
/// ## Parameters
|
|
||||||
/// - num: Content (positional, required)
|
|
||||||
/// The fraction's numerator.
|
|
||||||
///
|
|
||||||
/// - denom: Content (positional, required)
|
|
||||||
/// The fraction's denominator.
|
|
||||||
///
|
|
||||||
/// ## Category
|
|
||||||
/// math
|
|
||||||
#[func]
|
|
||||||
#[capable(Texify)]
|
|
||||||
#[derive(Debug, Hash)]
|
|
||||||
pub struct FracNode {
|
|
||||||
/// The numerator.
|
|
||||||
pub num: Content,
|
|
||||||
/// The denominator.
|
|
||||||
pub denom: Content,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[node]
|
|
||||||
impl FracNode {
|
|
||||||
fn construct(_: &Vm, args: &mut Args) -> SourceResult<Content> {
|
|
||||||
let num = args.expect("numerator")?;
|
|
||||||
let denom = args.expect("denominator")?;
|
|
||||||
Ok(Self { num, denom }.pack())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Texify for FracNode {
|
|
||||||
fn texify(&self, t: &mut Texifier) -> SourceResult<()> {
|
|
||||||
t.push_str("\\frac{");
|
|
||||||
self.num.texify_unparen(t)?;
|
|
||||||
t.push_str("}{");
|
|
||||||
self.denom.texify_unparen(t)?;
|
|
||||||
t.push_str("}");
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// # Binomial
|
|
||||||
/// A binomial expression.
|
|
||||||
///
|
|
||||||
/// ## Example
|
|
||||||
/// ```
|
|
||||||
/// $ binom(n, k) $
|
|
||||||
/// ```
|
|
||||||
///
|
|
||||||
/// ## Parameters
|
|
||||||
/// - upper: Content (positional, required)
|
|
||||||
/// The binomial's upper index.
|
|
||||||
///
|
|
||||||
/// - lower: Content (positional, required)
|
|
||||||
/// The binomial's lower index.
|
|
||||||
///
|
|
||||||
/// ## Category
|
|
||||||
/// math
|
|
||||||
#[func]
|
|
||||||
#[capable(Texify)]
|
|
||||||
#[derive(Debug, Hash)]
|
|
||||||
pub struct BinomNode {
|
|
||||||
/// The upper index.
|
|
||||||
pub upper: Content,
|
|
||||||
/// The lower index.
|
|
||||||
pub lower: Content,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[node]
|
|
||||||
impl BinomNode {
|
|
||||||
fn construct(_: &Vm, args: &mut Args) -> SourceResult<Content> {
|
|
||||||
let upper = args.expect("upper index")?;
|
|
||||||
let lower = args.expect("lower index")?;
|
|
||||||
Ok(Self { upper, lower }.pack())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Texify for BinomNode {
|
|
||||||
fn texify(&self, t: &mut Texifier) -> SourceResult<()> {
|
|
||||||
t.push_str("\\binom{");
|
|
||||||
self.upper.texify(t)?;
|
|
||||||
t.push_str("}{");
|
|
||||||
self.lower.texify(t)?;
|
|
||||||
t.push_str("}");
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// # Script
|
|
||||||
/// A mathematical sub- and/or superscript.
|
|
||||||
///
|
|
||||||
/// _Note:_ In the future, this might be unified with the [sub](@sub) and
|
|
||||||
/// [super](@super) functions that handle sub- and superscripts in text.
|
|
||||||
///
|
|
||||||
/// ## Syntax
|
|
||||||
/// This function also has dedicated syntax: Use the underscore (`_`) to
|
|
||||||
/// indicate a subscript and the circumflex (`^`) to indicate a superscript.
|
|
||||||
///
|
|
||||||
/// ## Example
|
|
||||||
/// ```
|
|
||||||
/// $ a_i = 2^(1+i) $
|
|
||||||
/// ```
|
|
||||||
///
|
|
||||||
/// ## Parameters
|
|
||||||
/// - base: Content (positional, required)
|
|
||||||
/// The base to which the applies the sub- and/or superscript.
|
|
||||||
///
|
|
||||||
/// - sub: Content (named)
|
|
||||||
/// The subscript.
|
|
||||||
///
|
|
||||||
/// - sup: Content (named)
|
|
||||||
/// The superscript.
|
|
||||||
///
|
|
||||||
/// ## Category
|
|
||||||
/// math
|
|
||||||
#[func]
|
|
||||||
#[capable(Texify)]
|
|
||||||
#[derive(Debug, Hash)]
|
|
||||||
pub struct ScriptNode {
|
|
||||||
/// The base.
|
|
||||||
pub base: Content,
|
|
||||||
/// The subscript.
|
|
||||||
pub sub: Option<Content>,
|
|
||||||
/// The superscript.
|
|
||||||
pub sup: Option<Content>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[node]
|
|
||||||
impl ScriptNode {
|
|
||||||
fn construct(_: &Vm, args: &mut Args) -> SourceResult<Content> {
|
|
||||||
let base = args.expect("base")?;
|
|
||||||
let sub = args.named("sub")?;
|
|
||||||
let sup = args.named("sup")?;
|
|
||||||
Ok(Self { base, sub, sup }.pack())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Texify for ScriptNode {
|
|
||||||
fn texify(&self, t: &mut Texifier) -> SourceResult<()> {
|
|
||||||
self.base.texify(t)?;
|
|
||||||
|
|
||||||
if let Some(sub) = &self.sub {
|
|
||||||
t.push_str("_{");
|
|
||||||
sub.texify_unparen(t)?;
|
|
||||||
t.push_str("}");
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(sup) = &self.sup {
|
|
||||||
t.push_str("^{");
|
|
||||||
sup.texify_unparen(t)?;
|
|
||||||
t.push_str("}");
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// # Alignment Point
|
/// # Alignment Point
|
||||||
/// A math alignment point: `&`, `&&`.
|
/// A math alignment point: `&`, `&&`.
|
||||||
///
|
///
|
||||||
@ -651,110 +335,3 @@ impl Texify for AlignPointNode {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// # Square Root
|
|
||||||
/// A square root.
|
|
||||||
///
|
|
||||||
/// _Note:_ Non-square roots are not yet supported.
|
|
||||||
///
|
|
||||||
/// ## Example
|
|
||||||
/// ```
|
|
||||||
/// $ sqrt(x^2) = x = sqrt(x)^2 $
|
|
||||||
/// ```
|
|
||||||
///
|
|
||||||
/// ## Parameters
|
|
||||||
/// - body: Content (positional, required)
|
|
||||||
/// The expression to take the square root of.
|
|
||||||
///
|
|
||||||
/// ## Category
|
|
||||||
/// math
|
|
||||||
#[func]
|
|
||||||
#[capable(Texify)]
|
|
||||||
#[derive(Debug, Hash)]
|
|
||||||
pub struct SqrtNode(pub Content);
|
|
||||||
|
|
||||||
#[node]
|
|
||||||
impl SqrtNode {
|
|
||||||
fn construct(_: &Vm, args: &mut Args) -> SourceResult<Content> {
|
|
||||||
Ok(Self(args.expect("body")?).pack())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Texify for SqrtNode {
|
|
||||||
fn texify(&self, t: &mut Texifier) -> SourceResult<()> {
|
|
||||||
t.push_str("\\sqrt{");
|
|
||||||
self.0.texify(t)?;
|
|
||||||
t.push_str("}");
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// # Floor
|
|
||||||
/// A floored expression.
|
|
||||||
///
|
|
||||||
/// ## Example
|
|
||||||
/// ```
|
|
||||||
/// $ floor(x/2) $
|
|
||||||
/// ```
|
|
||||||
///
|
|
||||||
/// ## Parameters
|
|
||||||
/// - body: Content (positional, required)
|
|
||||||
/// The expression to floor.
|
|
||||||
///
|
|
||||||
/// ## Category
|
|
||||||
/// math
|
|
||||||
#[func]
|
|
||||||
#[capable(Texify)]
|
|
||||||
#[derive(Debug, Hash)]
|
|
||||||
pub struct FloorNode(pub Content);
|
|
||||||
|
|
||||||
#[node]
|
|
||||||
impl FloorNode {
|
|
||||||
fn construct(_: &Vm, args: &mut Args) -> SourceResult<Content> {
|
|
||||||
Ok(Self(args.expect("body")?).pack())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Texify for FloorNode {
|
|
||||||
fn texify(&self, t: &mut Texifier) -> SourceResult<()> {
|
|
||||||
t.push_str("\\left\\lfloor ");
|
|
||||||
self.0.texify(t)?;
|
|
||||||
t.push_str("\\right\\rfloor ");
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// # Ceil
|
|
||||||
/// A ceiled expression.
|
|
||||||
///
|
|
||||||
/// ## Example
|
|
||||||
/// ```
|
|
||||||
/// $ ceil(x/2) $
|
|
||||||
/// ```
|
|
||||||
///
|
|
||||||
/// ## Parameters
|
|
||||||
/// - body: Content (positional, required)
|
|
||||||
/// The expression to ceil.
|
|
||||||
///
|
|
||||||
/// ## Category
|
|
||||||
/// math
|
|
||||||
#[func]
|
|
||||||
#[capable(Texify)]
|
|
||||||
#[derive(Debug, Hash)]
|
|
||||||
pub struct CeilNode(pub Content);
|
|
||||||
|
|
||||||
#[node]
|
|
||||||
impl CeilNode {
|
|
||||||
fn construct(_: &Vm, args: &mut Args) -> SourceResult<Content> {
|
|
||||||
Ok(Self(args.expect("body")?).pack())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Texify for CeilNode {
|
|
||||||
fn texify(&self, t: &mut Texifier) -> SourceResult<()> {
|
|
||||||
t.push_str("\\left\\lceil ");
|
|
||||||
self.0.texify(t)?;
|
|
||||||
t.push_str("\\right\\rceil ");
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
38
library/src/math/root.rs
Normal file
38
library/src/math/root.rs
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
use super::*;
|
||||||
|
|
||||||
|
/// # Square Root
|
||||||
|
/// A square root.
|
||||||
|
///
|
||||||
|
/// _Note:_ Non-square roots are not yet supported.
|
||||||
|
///
|
||||||
|
/// ## Example
|
||||||
|
/// ```
|
||||||
|
/// $ sqrt(x^2) = x = sqrt(x)^2 $
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// ## Parameters
|
||||||
|
/// - body: Content (positional, required)
|
||||||
|
/// The expression to take the square root of.
|
||||||
|
///
|
||||||
|
/// ## Category
|
||||||
|
/// math
|
||||||
|
#[func]
|
||||||
|
#[capable(Texify)]
|
||||||
|
#[derive(Debug, Hash)]
|
||||||
|
pub struct SqrtNode(pub Content);
|
||||||
|
|
||||||
|
#[node]
|
||||||
|
impl SqrtNode {
|
||||||
|
fn construct(_: &Vm, args: &mut Args) -> SourceResult<Content> {
|
||||||
|
Ok(Self(args.expect("body")?).pack())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Texify for SqrtNode {
|
||||||
|
fn texify(&self, t: &mut Texifier) -> SourceResult<()> {
|
||||||
|
t.push_str("\\sqrt{");
|
||||||
|
self.0.texify(t)?;
|
||||||
|
t.push_str("}");
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
70
library/src/math/script.rs
Normal file
70
library/src/math/script.rs
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
use super::*;
|
||||||
|
|
||||||
|
/// # Script
|
||||||
|
/// A mathematical sub- and/or superscript.
|
||||||
|
///
|
||||||
|
/// _Note:_ In the future, this might be unified with the [sub](@sub) and
|
||||||
|
/// [super](@super) functions that handle sub- and superscripts in text.
|
||||||
|
///
|
||||||
|
/// ## Syntax
|
||||||
|
/// This function also has dedicated syntax: Use the underscore (`_`) to
|
||||||
|
/// indicate a subscript and the circumflex (`^`) to indicate a superscript.
|
||||||
|
///
|
||||||
|
/// ## Example
|
||||||
|
/// ```
|
||||||
|
/// $ a_i = 2^(1+i) $
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// ## Parameters
|
||||||
|
/// - base: Content (positional, required)
|
||||||
|
/// The base to which the applies the sub- and/or superscript.
|
||||||
|
///
|
||||||
|
/// - sub: Content (named)
|
||||||
|
/// The subscript.
|
||||||
|
///
|
||||||
|
/// - sup: Content (named)
|
||||||
|
/// The superscript.
|
||||||
|
///
|
||||||
|
/// ## Category
|
||||||
|
/// math
|
||||||
|
#[func]
|
||||||
|
#[capable(Texify)]
|
||||||
|
#[derive(Debug, Hash)]
|
||||||
|
pub struct ScriptNode {
|
||||||
|
/// The base.
|
||||||
|
pub base: Content,
|
||||||
|
/// The subscript.
|
||||||
|
pub sub: Option<Content>,
|
||||||
|
/// The superscript.
|
||||||
|
pub sup: Option<Content>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[node]
|
||||||
|
impl ScriptNode {
|
||||||
|
fn construct(_: &Vm, args: &mut Args) -> SourceResult<Content> {
|
||||||
|
let base = args.expect("base")?;
|
||||||
|
let sub = args.named("sub")?;
|
||||||
|
let sup = args.named("sup")?;
|
||||||
|
Ok(Self { base, sub, sup }.pack())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Texify for ScriptNode {
|
||||||
|
fn texify(&self, t: &mut Texifier) -> SourceResult<()> {
|
||||||
|
self.base.texify(t)?;
|
||||||
|
|
||||||
|
if let Some(sub) = &self.sub {
|
||||||
|
t.push_str("_{");
|
||||||
|
sub.texify_unparen(t)?;
|
||||||
|
t.push_str("}");
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(sup) = &self.sup {
|
||||||
|
t.push_str("^{");
|
||||||
|
sup.texify_unparen(t)?;
|
||||||
|
t.push_str("}");
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user