mirror of
https://github.com/typst/typst
synced 2025-05-13 20:46:23 +08:00
Improve documentation comments 📜
This commit is contained in:
parent
b4be25e43b
commit
ccc4639c7d
36
src/lib.rs
36
src/lib.rs
@ -2,21 +2,21 @@
|
|||||||
//!
|
//!
|
||||||
//! # Steps
|
//! # Steps
|
||||||
//! - **Parsing:** The parsing step first transforms a plain string into an
|
//! - **Parsing:** The parsing step first transforms a plain string into an
|
||||||
//! [iterator of tokens](crate::parsing::Tokens). Then parser constructs a
|
//! [iterator of tokens](crate::syntax::Tokens). Then, a parser constructs a
|
||||||
//! syntax tree from the token stream. The structures describing the tree can
|
//! syntax tree from the token stream. The structures describing the tree can
|
||||||
//! be found in the [syntax]. Dynamic functions parse their own bodies
|
//! be found in the [syntax](crate::syntax) module.
|
||||||
//! themselves.
|
|
||||||
//! - **Layouting:** The next step is to transform the syntax tree into a
|
//! - **Layouting:** The next step is to transform the syntax tree into a
|
||||||
//! portable representation of the typesetted document. Types for these can be
|
//! portable representation of the typesetted document. Types for these can be
|
||||||
//! found in the [layout] module.
|
//! found in the [layout] module. A finished layout reading for exporting is a
|
||||||
//! - **Exporting:** The finished document can then be exported into supported
|
//! [multi layout](crate::layout::MultiLayout) consisting of multiple boxes (or
|
||||||
//! formats. Submodules for the supported formats are located in the [export]
|
//! pages).
|
||||||
//! module. Currently the only supported format is _PDF_.
|
//! - **Exporting:** The finished document can finally be exported into a supported
|
||||||
|
//! format. Submodules for these formats are located in the [export](crate::export)
|
||||||
|
//! module. Currently, the only supported output format is _PDF_.
|
||||||
|
|
||||||
pub extern crate toddle;
|
pub extern crate toddle;
|
||||||
|
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
|
|
||||||
use toddle::query::{FontLoader, FontProvider, SharedFontLoader};
|
use toddle::query::{FontLoader, FontProvider, SharedFontLoader};
|
||||||
|
|
||||||
use crate::func::Scope;
|
use crate::func::Scope;
|
||||||
@ -36,15 +36,15 @@ pub mod size;
|
|||||||
pub mod style;
|
pub mod style;
|
||||||
pub mod syntax;
|
pub mod syntax;
|
||||||
|
|
||||||
/// Transforms source code into typesetted documents.
|
/// Transforms source code into typesetted layouts.
|
||||||
///
|
///
|
||||||
/// Can be configured through various methods.
|
/// A typesetter can be configured through various methods.
|
||||||
pub struct Typesetter<'p> {
|
pub struct Typesetter<'p> {
|
||||||
/// The font loader shared by all typesetting processes.
|
/// The font loader shared by all typesetting processes.
|
||||||
loader: SharedFontLoader<'p>,
|
loader: SharedFontLoader<'p>,
|
||||||
/// The default text style.
|
/// The base text style.
|
||||||
text_style: TextStyle,
|
text_style: TextStyle,
|
||||||
/// The default page style.
|
/// The base page style.
|
||||||
page_style: PageStyle,
|
page_style: PageStyle,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -59,13 +59,13 @@ impl<'p> Typesetter<'p> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set the default page style for the document.
|
/// Set the base page style.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn set_page_style(&mut self, style: PageStyle) {
|
pub fn set_page_style(&mut self, style: PageStyle) {
|
||||||
self.page_style = style;
|
self.page_style = style;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set the default text style for the document.
|
/// Set the base text style.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn set_text_style(&mut self, style: TextStyle) {
|
pub fn set_text_style(&mut self, style: TextStyle) {
|
||||||
self.text_style = style;
|
self.text_style = style;
|
||||||
@ -90,7 +90,7 @@ impl<'p> Typesetter<'p> {
|
|||||||
parse(src, ParseContext { scope: &scope })
|
parse(src, ParseContext { scope: &scope })
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Layout a syntax tree and return the layout and the referenced font list.
|
/// Layout a syntax tree and return the produced layout.
|
||||||
pub fn layout(&self, tree: &SyntaxTree) -> LayoutResult<MultiLayout> {
|
pub fn layout(&self, tree: &SyntaxTree) -> LayoutResult<MultiLayout> {
|
||||||
let space = LayoutSpace {
|
let space = LayoutSpace {
|
||||||
dimensions: self.page_style.dimensions,
|
dimensions: self.page_style.dimensions,
|
||||||
@ -113,7 +113,7 @@ impl<'p> Typesetter<'p> {
|
|||||||
Ok(pages)
|
Ok(pages)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Typeset a portable document from source code.
|
/// Process source code directly into a layout.
|
||||||
pub fn typeset(&self, src: &str) -> Result<MultiLayout, TypesetError> {
|
pub fn typeset(&self, src: &str) -> Result<MultiLayout, TypesetError> {
|
||||||
let tree = self.parse(src)?;
|
let tree = self.parse(src)?;
|
||||||
let layout = self.layout(&tree)?;
|
let layout = self.layout(&tree)?;
|
||||||
@ -123,9 +123,9 @@ impl<'p> Typesetter<'p> {
|
|||||||
|
|
||||||
/// The general error type for typesetting.
|
/// The general error type for typesetting.
|
||||||
pub enum TypesetError {
|
pub enum TypesetError {
|
||||||
/// An error that occured while parsing.
|
/// An error that occured in the parsing step.
|
||||||
Parse(ParseError),
|
Parse(ParseError),
|
||||||
/// An error that occured while layouting.
|
/// An error that occured in the layouting step.
|
||||||
Layout(LayoutError),
|
Layout(LayoutError),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
/// Create an error type.
|
//! Auxiliary macros.
|
||||||
|
|
||||||
|
/// Create trait implementations for an error type.
|
||||||
macro_rules! error_type {
|
macro_rules! error_type {
|
||||||
(
|
(
|
||||||
$var:ident: $err:ident,
|
$var:ident: $err:ident,
|
||||||
@ -38,7 +40,7 @@ macro_rules! error_type {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a `Debug` implementation from a display implementation.
|
/// Create a `Debug` implementation from a `Display` implementation.
|
||||||
macro_rules! debug_display {
|
macro_rules! debug_display {
|
||||||
($type:ident) => {
|
($type:ident) => {
|
||||||
impl std::fmt::Debug for $type {
|
impl std::fmt::Debug for $type {
|
||||||
|
24
src/size.rs
24
src/size.rs
@ -6,7 +6,7 @@ use std::iter::Sum;
|
|||||||
use std::ops::*;
|
use std::ops::*;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
/// A general spacing type.
|
/// A general space type.
|
||||||
#[derive(Copy, Clone, PartialEq, Default)]
|
#[derive(Copy, Clone, PartialEq, Default)]
|
||||||
pub struct Size {
|
pub struct Size {
|
||||||
/// The size in typographic points (1/72 inches).
|
/// The size in typographic points (1/72 inches).
|
||||||
@ -98,31 +98,31 @@ impl Size {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Size2D {
|
impl Size2D {
|
||||||
/// Create a new vector from two sizes.
|
/// Create a new 2D-size from two sizes.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn new(x: Size, y: Size) -> Size2D {
|
pub fn new(x: Size, y: Size) -> Size2D {
|
||||||
Size2D { x, y }
|
Size2D { x, y }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a vector with all set to zero.
|
/// Create a 2D-size with both sizes set to zero.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn zero() -> Size2D {
|
pub fn zero() -> Size2D {
|
||||||
Size2D::default()
|
Size2D::default()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a new vector with `y` set to zero and `x` to a value.
|
/// Create a new 2D-size with `x` set to a value and `y` zero.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn with_x(x: Size) -> Size2D {
|
pub fn with_x(x: Size) -> Size2D {
|
||||||
Size2D { x, y: Size::zero() }
|
Size2D { x, y: Size::zero() }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a new vector with `x` set to zero and `y` to a value.
|
/// Create a new 2D-size with `y` set to a value and `x` zero.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn with_y(y: Size) -> Size2D {
|
pub fn with_y(y: Size) -> Size2D {
|
||||||
Size2D { x: Size::zero(), y }
|
Size2D { x: Size::zero(), y }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return a [`Size2D`] padded by the paddings of the given box.
|
/// Return a 2D-size padded by the paddings of the given box.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn padded(&self, padding: SizeBox) -> Size2D {
|
pub fn padded(&self, padding: SizeBox) -> Size2D {
|
||||||
Size2D {
|
Size2D {
|
||||||
@ -131,7 +131,7 @@ impl Size2D {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return a [`Size2D`] reduced by the paddings of the given box.
|
/// Return a 2D-size reduced by the paddings of the given box.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn unpadded(&self, padding: SizeBox) -> Size2D {
|
pub fn unpadded(&self, padding: SizeBox) -> Size2D {
|
||||||
Size2D {
|
Size2D {
|
||||||
@ -140,8 +140,8 @@ impl Size2D {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Whether the given [`Size2D`] fits into this one, that is,
|
/// Whether the given 2D-size fits into this one, that is,
|
||||||
/// both coordinate values are smaller.
|
/// both coordinate values are smaller or equal.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn fits(&self, other: Size2D) -> bool {
|
pub fn fits(&self, other: Size2D) -> bool {
|
||||||
self.x >= other.x && self.y >= other.y
|
self.x >= other.x && self.y >= other.y
|
||||||
@ -160,7 +160,7 @@ impl SizeBox {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a box with all set to zero.
|
/// Create a box with all values set to zero.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn zero() -> SizeBox {
|
pub fn zero() -> SizeBox {
|
||||||
SizeBox::default()
|
SizeBox::default()
|
||||||
@ -319,6 +319,8 @@ impl_num_both!(Mul, mul, MulAssign, mul_assign, i32);
|
|||||||
impl_num_back!(Div, div, DivAssign, div_assign, f32);
|
impl_num_back!(Div, div, DivAssign, div_assign, f32);
|
||||||
impl_num_back!(Div, div, DivAssign, div_assign, i32);
|
impl_num_back!(Div, div, DivAssign, div_assign, i32);
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------------------------//
|
||||||
|
|
||||||
impl Display for Size2D {
|
impl Display for Size2D {
|
||||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||||
write!(f, "[{}, {}]", self.x, self.y)
|
write!(f, "[{}, {}]", self.x, self.y)
|
||||||
@ -412,6 +414,8 @@ impl_num_both2d!(Mul, mul, MulAssign, mul_assign, i32);
|
|||||||
impl_num_back2d!(Div, div, DivAssign, div_assign, f32);
|
impl_num_back2d!(Div, div, DivAssign, div_assign, f32);
|
||||||
impl_num_back2d!(Div, div, DivAssign, div_assign, i32);
|
impl_num_back2d!(Div, div, DivAssign, div_assign, i32);
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------------------------//
|
||||||
|
|
||||||
impl Display for SizeBox {
|
impl Display for SizeBox {
|
||||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||||
write!(
|
write!(
|
||||||
|
47
src/style.rs
47
src/style.rs
@ -1,22 +1,22 @@
|
|||||||
//! Layouting styles.
|
//! Styles for text and pages.
|
||||||
|
|
||||||
use toddle::query::FontClass;
|
use toddle::query::FontClass;
|
||||||
|
use FontClass::*;
|
||||||
use crate::size::{Size, Size2D, SizeBox};
|
use crate::size::{Size, Size2D, SizeBox};
|
||||||
|
|
||||||
/// Default styles for text.
|
/// Defines which fonts to use and how to space text.
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct TextStyle {
|
pub struct TextStyle {
|
||||||
/// The classes the font we want has to be part of.
|
/// The classes the font has to be part of.
|
||||||
pub classes: Vec<FontClass>,
|
pub classes: Vec<FontClass>,
|
||||||
/// A sequence of classes. We need the font to be part of at least one of
|
/// The fallback classes from which the font needs to match the
|
||||||
/// these and preferably the leftmost possible.
|
/// leftmost possible one.
|
||||||
pub fallback: Vec<FontClass>,
|
pub fallback: Vec<FontClass>,
|
||||||
/// The font size.
|
/// The font size.
|
||||||
pub font_size: f32,
|
pub font_size: f32,
|
||||||
/// The line spacing (as a multiple of the font size).
|
/// The line spacing (as a multiple of the font size).
|
||||||
pub line_spacing: f32,
|
pub line_spacing: f32,
|
||||||
/// The paragraphs spacing (as a multiple of the line spacing).
|
/// The paragraphs spacing (as a multiple of the font size).
|
||||||
pub paragraph_spacing: f32,
|
pub paragraph_spacing: f32,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -24,21 +24,35 @@ impl TextStyle {
|
|||||||
/// Toggle a class.
|
/// Toggle a class.
|
||||||
///
|
///
|
||||||
/// If the class was one of _italic_ or _bold_, then:
|
/// If the class was one of _italic_ or _bold_, then:
|
||||||
/// - If it was not present, the _regular_ class will be removed.
|
/// - If it was not present before, the _regular_ class will be removed.
|
||||||
/// - If it was present, the _regular_ class will be added in case the other
|
/// - If it was present before, the _regular_ class will be added in case the other
|
||||||
/// style class is not present.
|
/// style class is not present.
|
||||||
pub fn toggle_class(&mut self, class: FontClass) {
|
pub fn toggle_class(&mut self, class: FontClass) {
|
||||||
if self.classes.contains(&class) {
|
if self.classes.contains(&class) {
|
||||||
self.classes.retain(|x| x != &class);
|
// If we retain a Bold or Italic class, we will not add
|
||||||
if (class == FontClass::Italic && !self.classes.contains(&FontClass::Bold))
|
// the Regular class.
|
||||||
|| (class == FontClass::Bold && !self.classes.contains(&FontClass::Italic))
|
let mut regular = true;
|
||||||
{
|
self.classes.retain(|x| {
|
||||||
self.classes.push(FontClass::Regular);
|
if class == *x {
|
||||||
|
false
|
||||||
|
} else {
|
||||||
|
if class == Bold || class == Italic {
|
||||||
|
regular = false;
|
||||||
|
}
|
||||||
|
true
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if regular {
|
||||||
|
self.classes.push(Regular);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
// If we add an Italic or Bold class, we remove
|
||||||
|
// the Regular class.
|
||||||
if class == FontClass::Italic || class == FontClass::Bold {
|
if class == FontClass::Italic || class == FontClass::Bold {
|
||||||
self.classes.retain(|x| x != &FontClass::Regular);
|
self.classes.retain(|x| x != &FontClass::Regular);
|
||||||
}
|
}
|
||||||
|
|
||||||
self.classes.push(class);
|
self.classes.push(class);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -46,7 +60,6 @@ impl TextStyle {
|
|||||||
|
|
||||||
impl Default for TextStyle {
|
impl Default for TextStyle {
|
||||||
fn default() -> TextStyle {
|
fn default() -> TextStyle {
|
||||||
use FontClass::*;
|
|
||||||
TextStyle {
|
TextStyle {
|
||||||
classes: vec![Regular],
|
classes: vec![Regular],
|
||||||
fallback: vec![Serif],
|
fallback: vec![Serif],
|
||||||
@ -57,10 +70,10 @@ impl Default for TextStyle {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Default styles for pages.
|
/// Defines the size and margins of a page.
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct PageStyle {
|
pub struct PageStyle {
|
||||||
/// Width and height of the page.
|
/// The width and height of the page.
|
||||||
pub dimensions: Size2D,
|
pub dimensions: Size2D,
|
||||||
/// The amount of white space on each side.
|
/// The amount of white space on each side.
|
||||||
pub margins: SizeBox,
|
pub margins: SizeBox,
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
//! Tokenized and syntax tree representations of source code.
|
//! Tokenization and parsing of source code.
|
||||||
|
|
||||||
use std::collections::HashMap;
|
|
||||||
use std::fmt::{self, Display, Formatter};
|
use std::fmt::{self, Display, Formatter};
|
||||||
|
|
||||||
use crate::func::Function;
|
use crate::func::Function;
|
||||||
@ -17,8 +16,7 @@ pub use parsing::{parse, ParseContext, ParseError, ParseResult};
|
|||||||
pub enum Token<'s> {
|
pub enum Token<'s> {
|
||||||
/// One or more whitespace (non-newline) codepoints.
|
/// One or more whitespace (non-newline) codepoints.
|
||||||
Space,
|
Space,
|
||||||
/// A line feed (`\n`, `\r\n` and some more as defined by the Unicode
|
/// A line feed (`\n`, `\r\n` and some more as defined by the Unicode standard).
|
||||||
/// standard).
|
|
||||||
Newline,
|
Newline,
|
||||||
/// A left bracket: `[`.
|
/// A left bracket: `[`.
|
||||||
LeftBracket,
|
LeftBracket,
|
||||||
@ -28,37 +26,36 @@ pub enum Token<'s> {
|
|||||||
/// header only).
|
/// header only).
|
||||||
///
|
///
|
||||||
/// If a colon occurs outside of a function header, it will be tokenized as
|
/// If a colon occurs outside of a function header, it will be tokenized as
|
||||||
/// a [Word](Token::Word).
|
/// [Text](Token::Text), just like the other tokens annotated with
|
||||||
|
/// _Function header only_.
|
||||||
Colon,
|
Colon,
|
||||||
/// An equals (`=`) sign assigning a function argument a value (Function
|
/// An equals (`=`) sign assigning a function argument a value (Function header only).
|
||||||
/// header only).
|
|
||||||
Equals,
|
Equals,
|
||||||
/// A comma (`,`) separating two function arguments (Function header only).
|
/// A comma (`,`) separating two function arguments (Function header only).
|
||||||
Comma,
|
Comma,
|
||||||
/// Quoted text as a string value (Function header only).
|
/// Quoted text as a string value (Function header only).
|
||||||
Quoted(&'s str),
|
Quoted(&'s str),
|
||||||
/// An underscore, indicating text in italics.
|
/// An underscore, indicating text in italics (Body only).
|
||||||
Underscore,
|
Underscore,
|
||||||
/// A star, indicating bold text.
|
/// A star, indicating bold text (Body only).
|
||||||
Star,
|
Star,
|
||||||
/// A backtick, indicating monospace text.
|
/// A backtick, indicating monospace text (Body only).
|
||||||
Backtick,
|
Backtick,
|
||||||
/// A line comment.
|
/// A line comment.
|
||||||
LineComment(&'s str),
|
LineComment(&'s str),
|
||||||
/// A block comment.
|
/// A block comment.
|
||||||
BlockComment(&'s str),
|
BlockComment(&'s str),
|
||||||
/// A star followed by a slash unexpectedly ending a block comment (the
|
/// A star followed by a slash unexpectedly ending a block comment
|
||||||
/// comment was not started before, otherwise a
|
/// (the comment was not started before, otherwise a
|
||||||
/// [BlockComment](Token::BlockComment) would be returned).
|
/// [BlockComment](Token::BlockComment would be returned).
|
||||||
StarSlash,
|
StarSlash,
|
||||||
/// Everything else is just text.
|
/// A unit of Plain text.
|
||||||
Text(&'s str),
|
Text(&'s str),
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A tree representation of the source.
|
/// A tree representation of source code.
|
||||||
#[derive(Debug, PartialEq)]
|
#[derive(Debug, PartialEq)]
|
||||||
pub struct SyntaxTree {
|
pub struct SyntaxTree {
|
||||||
/// The children.
|
|
||||||
pub nodes: Vec<Node>,
|
pub nodes: Vec<Node>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -70,10 +67,10 @@ impl SyntaxTree {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A node in the abstract syntax tree.
|
/// A node in the syntax tree.
|
||||||
#[derive(Debug, PartialEq)]
|
#[derive(Debug, PartialEq)]
|
||||||
pub enum Node {
|
pub enum Node {
|
||||||
/// Whitespace between other nodes.
|
/// Whitespace.
|
||||||
Space,
|
Space,
|
||||||
/// A line feed.
|
/// A line feed.
|
||||||
Newline,
|
Newline,
|
||||||
@ -89,28 +86,22 @@ pub enum Node {
|
|||||||
Func(FuncCall),
|
Func(FuncCall),
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A function invocation consisting of header and body.
|
/// A function invocation, consisting of header and a dynamically parsed body.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct FuncCall {
|
pub struct FuncCall {
|
||||||
pub header: FuncHeader,
|
pub header: FuncHeader,
|
||||||
pub body: Box<dyn Function>,
|
pub body: Box<dyn Function>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PartialEq for FuncCall {
|
|
||||||
fn eq(&self, other: &FuncCall) -> bool {
|
|
||||||
(self.header == other.header) && (&self.body == &other.body)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Contains header information of a function invocation.
|
/// Contains header information of a function invocation.
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
pub struct FuncHeader {
|
pub struct FuncHeader {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub args: Vec<Expression>,
|
pub args: Vec<Expression>,
|
||||||
pub kwargs: HashMap<String, Expression>,
|
pub kwargs: Vec<(String, Expression)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A value expression.
|
/// An argument or return value.
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
pub enum Expression {
|
pub enum Expression {
|
||||||
Ident(String),
|
Ident(String),
|
||||||
@ -120,6 +111,12 @@ pub enum Expression {
|
|||||||
Bool(bool),
|
Bool(bool),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl PartialEq for FuncCall {
|
||||||
|
fn eq(&self, other: &FuncCall) -> bool {
|
||||||
|
(self.header == other.header) && (&self.body == &other.body)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Display for Expression {
|
impl Display for Expression {
|
||||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||||
use Expression::*;
|
use Expression::*;
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
//! Parsing of source code into token streams and syntax trees.
|
//! Parsing of token streams into syntax trees.
|
||||||
|
|
||||||
use std::collections::HashMap;
|
|
||||||
use unicode_xid::UnicodeXID;
|
use unicode_xid::UnicodeXID;
|
||||||
|
|
||||||
use crate::func::{Function, Scope};
|
use crate::func::{Function, Scope};
|
||||||
@ -20,7 +19,7 @@ pub struct ParseContext<'a> {
|
|||||||
pub scope: &'a Scope,
|
pub scope: &'a Scope,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Transforms token streams to syntax trees.
|
/// Transforms token streams into syntax trees.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
struct Parser<'s> {
|
struct Parser<'s> {
|
||||||
src: &'s str,
|
src: &'s str,
|
||||||
@ -35,14 +34,15 @@ struct Parser<'s> {
|
|||||||
enum ParserState {
|
enum ParserState {
|
||||||
/// The base state of the parser.
|
/// The base state of the parser.
|
||||||
Body,
|
Body,
|
||||||
/// We saw one newline already.
|
/// We saw one newline already and are looking for another.
|
||||||
FirstNewline,
|
FirstNewline,
|
||||||
/// We wrote a newline.
|
/// We saw at least two newlines and wrote one, thus not
|
||||||
|
/// writing another one for more newlines.
|
||||||
WroteNewline,
|
WroteNewline,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'s> Parser<'s> {
|
impl<'s> Parser<'s> {
|
||||||
/// Create a new parser from the source and the context.
|
/// Create a new parser from the source code and the context.
|
||||||
fn new(src: &'s str, ctx: ParseContext<'s>) -> Parser<'s> {
|
fn new(src: &'s str, ctx: ParseContext<'s>) -> Parser<'s> {
|
||||||
Parser {
|
Parser {
|
||||||
src,
|
src,
|
||||||
@ -53,9 +53,8 @@ impl<'s> Parser<'s> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parse the source into an abstract syntax tree.
|
/// Parse the source into a syntax tree.
|
||||||
fn parse(mut self) -> ParseResult<SyntaxTree> {
|
fn parse(mut self) -> ParseResult<SyntaxTree> {
|
||||||
// Loop through all the tokens.
|
|
||||||
while self.tokens.peek().is_some() {
|
while self.tokens.peek().is_some() {
|
||||||
self.parse_white()?;
|
self.parse_white()?;
|
||||||
self.parse_body_part()?;
|
self.parse_body_part()?;
|
||||||
@ -66,27 +65,30 @@ impl<'s> Parser<'s> {
|
|||||||
|
|
||||||
/// Parse the next part of the body.
|
/// Parse the next part of the body.
|
||||||
fn parse_body_part(&mut self) -> ParseResult<()> {
|
fn parse_body_part(&mut self) -> ParseResult<()> {
|
||||||
|
use Token::*;
|
||||||
|
|
||||||
if let Some(token) = self.tokens.peek() {
|
if let Some(token) = self.tokens.peek() {
|
||||||
match token {
|
match token {
|
||||||
// Functions
|
// Functions.
|
||||||
Token::LeftBracket => self.parse_func()?,
|
LeftBracket => self.parse_func()?,
|
||||||
Token::RightBracket => return Err(ParseError::new("unexpected closing bracket")),
|
RightBracket => return Err(ParseError::new("unexpected closing bracket")),
|
||||||
|
|
||||||
// Modifiers
|
// Modifiers.
|
||||||
Token::Underscore => self.append_consumed(Node::ToggleItalics),
|
Underscore => self.append_consumed(Node::ToggleItalics),
|
||||||
Token::Star => self.append_consumed(Node::ToggleBold),
|
Star => self.append_consumed(Node::ToggleBold),
|
||||||
Token::Backtick => self.append_consumed(Node::ToggleMonospace),
|
Backtick => self.append_consumed(Node::ToggleMonospace),
|
||||||
|
|
||||||
// Normal text
|
// Normal text.
|
||||||
Token::Text(word) => self.append_consumed(Node::Text(word.to_owned())),
|
Text(word) => self.append_consumed(Node::Text(word.to_owned())),
|
||||||
|
|
||||||
Token::Colon | Token::Equals => panic!("bad token for body: {:?}", token),
|
|
||||||
|
|
||||||
// The rest is handled elsewhere or should not happen, because `Tokens` does not
|
// The rest is handled elsewhere or should not happen, because `Tokens` does not
|
||||||
// yield colons or equals in the body, but their text equivalents instead.
|
// yield these in a body.
|
||||||
_ => panic!("unexpected token: {:?}", token),
|
Space | Newline | LineComment(_) | BlockComment(_) |
|
||||||
|
Colon | Equals | Comma | Quoted(_) | StarSlash
|
||||||
|
=> panic!("parse_body_part: unexpected token: {:?}", token),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -122,7 +124,7 @@ impl<'s> Parser<'s> {
|
|||||||
let mut header = FuncHeader {
|
let mut header = FuncHeader {
|
||||||
name,
|
name,
|
||||||
args: vec![],
|
args: vec![],
|
||||||
kwargs: HashMap::new(),
|
kwargs: vec![],
|
||||||
};
|
};
|
||||||
|
|
||||||
self.skip_white();
|
self.skip_white();
|
||||||
@ -147,9 +149,9 @@ impl<'s> Parser<'s> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Parse the arguments to a function.
|
/// Parse the arguments to a function.
|
||||||
fn parse_func_args(&mut self) -> ParseResult<(Vec<Expression>, HashMap<String, Expression>)> {
|
fn parse_func_args(&mut self) -> ParseResult<(Vec<Expression>, Vec<(String, Expression)>)> {
|
||||||
let mut args = Vec::new();
|
let mut args = Vec::new();
|
||||||
let kwargs = HashMap::new();
|
let kwargs = Vec::new();
|
||||||
|
|
||||||
let mut comma = false;
|
let mut comma = false;
|
||||||
loop {
|
loop {
|
||||||
@ -448,38 +450,22 @@ mod tests {
|
|||||||
#[derive(Debug, PartialEq)]
|
#[derive(Debug, PartialEq)]
|
||||||
pub struct TreeFn(pub SyntaxTree);
|
pub struct TreeFn(pub SyntaxTree);
|
||||||
|
|
||||||
impl Function for TreeFn {
|
function! {
|
||||||
fn parse(_: &FuncHeader, body: Option<&str>, ctx: ParseContext) -> ParseResult<Self>
|
data: TreeFn,
|
||||||
where Self: Sized {
|
|
||||||
if let Some(src) = body {
|
|
||||||
parse(src, ctx).map(|tree| TreeFn(tree))
|
|
||||||
} else {
|
|
||||||
Err(ParseError::new("expected body for tree fn"))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn layout(&self, _: LayoutContext) -> LayoutResult<CommandList> {
|
parse(_args, body, ctx) { Ok(TreeFn(parse!(required: body, ctx))) }
|
||||||
Ok(CommandList::new())
|
layout(_, _) { Ok(commands![]) }
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A testing function without a body.
|
/// A testing function without a body.
|
||||||
#[derive(Debug, PartialEq)]
|
#[derive(Debug, PartialEq)]
|
||||||
pub struct BodylessFn;
|
pub struct BodylessFn;
|
||||||
|
|
||||||
impl Function for BodylessFn {
|
function! {
|
||||||
fn parse(_: &FuncHeader, body: Option<&str>, _: ParseContext) -> ParseResult<Self>
|
data: BodylessFn,
|
||||||
where Self: Sized {
|
|
||||||
if body.is_none() {
|
|
||||||
Ok(BodylessFn)
|
|
||||||
} else {
|
|
||||||
Err(ParseError::new("unexpected body for bodyless fn"))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn layout(&self, _: LayoutContext) -> LayoutResult<CommandList> {
|
parse(_args, body, _ctx) { parse!(forbidden: body); Ok(BodylessFn) }
|
||||||
Ok(CommandList::new())
|
layout(_, _) { Ok(commands![]) }
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -539,7 +525,7 @@ mod tests {
|
|||||||
header: FuncHeader {
|
header: FuncHeader {
|
||||||
name: $name.to_string(),
|
name: $name.to_string(),
|
||||||
args: vec![],
|
args: vec![],
|
||||||
kwargs: HashMap::new(),
|
kwargs: vec![],
|
||||||
},
|
},
|
||||||
body: $body,
|
body: $body,
|
||||||
}
|
}
|
||||||
@ -617,7 +603,7 @@ mod tests {
|
|||||||
header: FuncHeader {
|
header: FuncHeader {
|
||||||
name: name.to_string(),
|
name: name.to_string(),
|
||||||
args,
|
args,
|
||||||
kwargs: HashMap::new(),
|
kwargs: vec![],
|
||||||
},
|
},
|
||||||
body: Box::new(BodylessFn)
|
body: Box::new(BodylessFn)
|
||||||
})
|
})
|
||||||
|
Loading…
x
Reference in New Issue
Block a user