mirror of
https://github.com/typst/typst
synced 2025-05-14 04:56:26 +08:00
Express functions as trait objects 🚄
This commit is contained in:
parent
98c3788cf1
commit
a51e7a0758
@ -1,5 +1,6 @@
|
||||
//! Tokenization and parsing of source code into syntax trees.
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::fmt;
|
||||
use std::iter::Peekable;
|
||||
use std::mem::swap;
|
||||
@ -208,7 +209,7 @@ impl<'s> Tokens<'s> {
|
||||
pub struct Parser<'s, T> where T: Iterator<Item=Token<'s>> {
|
||||
tokens: Peekable<T>,
|
||||
state: ParserState,
|
||||
stack: Vec<Function<'s>>,
|
||||
stack: Vec<FuncInvocation>,
|
||||
tree: SyntaxTree<'s>,
|
||||
}
|
||||
|
||||
@ -296,9 +297,13 @@ impl<'s, T> Parser<'s, T> where T: Iterator<Item=Token<'s>> {
|
||||
},
|
||||
|
||||
PS::Function => {
|
||||
let name = match token {
|
||||
Token::Word(word) if word.is_identifier() => word,
|
||||
_ => return self.err("expected identifier"),
|
||||
let name = if let Token::Word(word) = token {
|
||||
match Ident::new(word) {
|
||||
Some(ident) => ident,
|
||||
None => return self.err("invalid identifier"),
|
||||
}
|
||||
} else {
|
||||
return self.err("expected identifier");
|
||||
};
|
||||
|
||||
self.advance();
|
||||
@ -306,9 +311,15 @@ impl<'s, T> Parser<'s, T> where T: Iterator<Item=Token<'s>> {
|
||||
return self.err("expected closing bracket");
|
||||
}
|
||||
|
||||
let mut func = Function {
|
||||
let header = FuncHeader {
|
||||
name,
|
||||
body: None,
|
||||
args: vec![],
|
||||
kwargs: HashMap::new(),
|
||||
};
|
||||
|
||||
let func = FuncInvocation {
|
||||
header,
|
||||
body: unimplemented!(),
|
||||
};
|
||||
|
||||
// This function has a body.
|
||||
|
110
src/syntax.rs
110
src/syntax.rs
@ -1,5 +1,10 @@
|
||||
//! Tokenized and syntax tree representations of source code.
|
||||
|
||||
use std::fmt::Debug;
|
||||
use std::collections::HashMap;
|
||||
use std::ops::Deref;
|
||||
use crate::utility::StrExt;
|
||||
|
||||
|
||||
/// A logical unit of the incoming text stream.
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||
@ -32,7 +37,7 @@ pub enum Token<'s> {
|
||||
}
|
||||
|
||||
/// A tree representation of the source.
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub struct SyntaxTree<'s> {
|
||||
/// The children.
|
||||
pub nodes: Vec<Node<'s>>,
|
||||
@ -47,7 +52,7 @@ impl<'s> SyntaxTree<'s> {
|
||||
}
|
||||
|
||||
/// A node in the abstract syntax tree.
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum Node<'s> {
|
||||
/// Whitespace between other nodes.
|
||||
Space,
|
||||
@ -62,15 +67,98 @@ pub enum Node<'s> {
|
||||
/// A literal word.
|
||||
Word(&'s str),
|
||||
/// A function invocation.
|
||||
Func(Function<'s>),
|
||||
Func(FuncInvocation),
|
||||
}
|
||||
|
||||
/// A node representing a function invocation.
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct Function<'s> {
|
||||
/// The name of the function.
|
||||
pub name: &'s str,
|
||||
/// Some syntax tree if the function had a body (second set of brackets),
|
||||
/// otherwise nothing.
|
||||
pub body: Option<SyntaxTree<'s>>,
|
||||
/// A complete function invocation consisting of header and body.
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub struct FuncInvocation {
|
||||
pub header: FuncHeader,
|
||||
pub body: Box<dyn Function>,
|
||||
}
|
||||
|
||||
/// Contains header information of a function invocation.
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct FuncHeader {
|
||||
pub name: Ident,
|
||||
pub args: Vec<Expression>,
|
||||
pub kwargs: HashMap<Ident, Expression>
|
||||
}
|
||||
|
||||
use std::any::Any;
|
||||
|
||||
/// Types that act as functions.
|
||||
pub trait Function: Debug + FunctionHelper {
|
||||
/// Parse the function.
|
||||
fn parse() -> Self where Self: Sized;
|
||||
|
||||
/// Execute the function and optionally yield a return value.
|
||||
fn typeset(&self, header: &FuncHeader) -> Option<Expression>;
|
||||
}
|
||||
|
||||
trait FunctionHelper {
|
||||
fn help_as_any(&self) -> &dyn Any;
|
||||
fn help_eq(&self, other: &dyn Function) -> bool;
|
||||
}
|
||||
|
||||
impl<T> FunctionHelper for T where T: Clone + PartialEq + 'static {
|
||||
fn help_as_any(&self) -> &dyn Any {
|
||||
self
|
||||
}
|
||||
|
||||
fn help_eq(&self, other: &dyn Function) -> bool {
|
||||
if let Some(other) = other.help_as_any().downcast_ref::<Self>() {
|
||||
self == other
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq<dyn Function> for &dyn Function {
|
||||
fn eq(&self, other: &dyn Function) -> bool {
|
||||
self.help_eq(other)
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq<Box<dyn Function>> for Box<dyn Function> {
|
||||
fn eq(&self, other: &Box<dyn Function>) -> bool {
|
||||
&*self == &*other
|
||||
}
|
||||
}
|
||||
|
||||
/// A potentially unevaluated expression.
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum Expression {}
|
||||
|
||||
/// A valid identifier.
|
||||
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
|
||||
pub struct Ident(String);
|
||||
|
||||
impl Ident {
|
||||
/// Create a new identifier if the string is a valid one.
|
||||
#[inline]
|
||||
pub fn new<S: Into<String>>(ident: S) -> Option<Ident> {
|
||||
let ident = ident.into();
|
||||
if ident.is_identifier() {
|
||||
Some(Ident(ident))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Consume self and return the underlying string.
|
||||
#[inline]
|
||||
pub fn into_inner(self) -> String {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for Ident {
|
||||
type Target = str;
|
||||
|
||||
#[inline]
|
||||
fn deref(&self) -> &str {
|
||||
&*self.0
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user