diff options
| author | Laurenz <laurmaedje@gmail.com> | 2020-01-19 21:50:20 +0100 |
|---|---|---|
| committer | Laurenz <laurmaedje@gmail.com> | 2020-01-19 21:53:24 +0100 |
| commit | 95e6b078fecddeaa3d6f2c920b617201b74bf01e (patch) | |
| tree | 1c03b0b16d614a5a2350dccf71a1eb1e34f9a812 /src/syntax/mod.rs | |
| parent | 277f2d2176f5e98305870f90b16af3feae1bb3d1 (diff) | |
Move to non-fatal errors 🪂 [WIP]
- Dynamic models instead of SyntaxTrees
- No more ParseResult/LayoutResult
- Errors and Decorations which are propagated to parent contexts
- Models are finally clonable
Diffstat (limited to 'src/syntax/mod.rs')
| -rw-r--r-- | src/syntax/mod.rs | 382 |
1 files changed, 111 insertions, 271 deletions
diff --git a/src/syntax/mod.rs b/src/syntax/mod.rs index f644f051..75407f82 100644 --- a/src/syntax/mod.rs +++ b/src/syntax/mod.rs @@ -1,321 +1,161 @@ //! Tokenization and parsing of source code. -use std::fmt::{self, Display, Formatter}; -use unicode_xid::UnicodeXID; +use std::any::Any; +use std::fmt::{self, Debug, Display, Formatter}; +use std::future::Future; +use std::pin::Pin; use serde::Serialize; -use crate::func::LayoutFunc; -use crate::size::{Size, ScaleSize}; - - -pub type ParseResult<T> = crate::TypesetResult<T>; +use crate::error::Error; +use crate::func::{Commands, Command}; +use crate::layout::{Layouted, LayoutContext}; +use crate::size::Size; pub_use_mod!(expr); +pub_use_mod!(func); pub_use_mod!(tokens); pub_use_mod!(parsing); pub_use_mod!(span); - -/// A minimal semantic entity of source code. -#[derive(Debug, Copy, Clone, PartialEq)] -pub enum Token<'s> { - /// One or more whitespace characters. The contained `usize` denotes the - /// number of newlines that were contained in the whitespace. - Whitespace(usize), - - /// A line comment with inner string contents `//<&'s str>\n`. - LineComment(&'s str), - /// A block comment with inner string contents `/*<&'s str>*/`. The comment - /// can contain nested block comments. - BlockComment(&'s str), - /// An erroneous `*/` without an opening block comment. - StarSlash, - - /// A left bracket: `[`. - LeftBracket, - /// A right bracket: `]`. - RightBracket, - - /// A left parenthesis in a function header: `(`. - LeftParen, - /// A right parenthesis in a function header: `)`. - RightParen, - /// A left brace in a function header: `{`. - LeftBrace, - /// A right brace in a function header: `}`. - RightBrace, - - /// A colon in a function header: `:`. - Colon, - /// A comma in a function header: `:`. - Comma, - /// An equals sign in a function header: `=`. - Equals, - - /// An identifier in a function header: `center`. - ExprIdent(&'s str), - /// A quoted string in a function header: `"..."`. - ExprStr(&'s str), - /// A number in a function header: `3.14`. - ExprNumber(f64), - /// A size in a function header: `12pt`. - ExprSize(Size), - /// A boolean in a function header: `true | false`. - ExprBool(bool), - - /// A star in body-text. - Star, - /// An underscore in body-text. - Underscore, - /// A backtick in body-text. - Backtick, - - /// Any other consecutive string. - Text(&'s str), -} - -/// A tree representation of source code. -#[derive(Debug, PartialEq)] -pub struct SyntaxTree { - pub nodes: Vec<Spanned<Node>>, +/// Common syntax types. +pub mod prelude { + pub use super::*; } -impl SyntaxTree { - /// Create an empty syntax tree. - pub fn new() -> SyntaxTree { - SyntaxTree { nodes: vec![] } - } - /// Add a node to the tree. - pub fn add(&mut self, node: Spanned<Node>) { - self.nodes.push(node); - } +pub struct Parsed<T> { + pub output: T, + pub errors: SpanVec<Error>, + pub decorations: SpanVec<Decoration>, } -/// A node in the syntax tree. -#[derive(PartialEq)] -pub enum Node { - /// A number of whitespace characters containing less than two newlines. - Space, - /// Whitespace characters with more than two newlines. - Newline, - /// Plain text. - Text(String), - /// Italics enabled / disabled. - ToggleItalic, - /// Bolder enabled / disabled. - ToggleBolder, - /// Monospace enabled / disabled. - ToggleMonospace, - /// A function invocation. - Func(FuncCall), -} - -impl Display for Node { - fn fmt(&self, f: &mut Formatter) -> fmt::Result { - match self { - Node::Space => write!(f, "Space"), - Node::Newline => write!(f, "Newline"), - Node::Text(text) => write!(f, "{:?}", text), - Node::ToggleItalic => write!(f, "ToggleItalic"), - Node::ToggleBolder => write!(f, "ToggleBold"), - Node::ToggleMonospace => write!(f, "ToggleMonospace"), - Node::Func(func) => { - if f.alternate() { - write!(f, "{:#?}", func.0) - } else { - write!(f, "{:?}", func.0) - } - } +impl<T> Parsed<T> { + pub fn map<F, U>(self, f: F) -> Parsed<U> where F: FnOnce(T) -> U { + Parsed { + output: f(self.output), + errors: self.errors, + decorations: self.decorations, } } } -debug_display!(Node); +#[async_trait::async_trait(?Send)] +pub trait Model: Debug + ModelBounds { + async fn layout<'a>( + &'a self, + ctx: LayoutContext<'_, '_> + ) -> Layouted<Commands<'a>>; +} -/// An invocation of a function. -#[derive(Debug)] -pub struct FuncCall(pub Box<dyn LayoutFunc>); +pub type DynFuture<'a, T> = Pin<Box<dyn Future<Output=T> + 'a>>; -impl PartialEq for FuncCall { - fn eq(&self, other: &FuncCall) -> bool { - &self.0 == &other.0 +impl dyn Model { + pub fn downcast<T>(&self) -> Option<&T> where T: Model + 'static { + self.as_any().downcast_ref::<T>() } } -#[derive(Debug, Clone, PartialEq)] -pub struct FuncHeader { - pub name: Spanned<Ident>, - pub args: FuncArgs, +impl PartialEq for dyn Model { + fn eq(&self, other: &dyn Model) -> bool { + self.bound_eq(other) + } } -#[derive(Debug, Clone, PartialEq)] -pub struct FuncArgs { - pub positional: Tuple, - pub keyword: Object, +impl Clone for Box<dyn Model> { + fn clone(&self) -> Self { + self.bound_clone() + } } -#[derive(Debug, Clone, PartialEq)] -pub enum Arg { - Pos(Spanned<Expression>), - Key(Pair), +pub trait ModelBounds { + fn as_any(&self) -> &dyn Any; + fn bound_eq(&self, other: &dyn Model) -> bool; + fn bound_clone(&self) -> Box<dyn Model>; } -impl Arg { - /// The span or the value or combined span of key and value. - pub fn span(&self) -> Span { - match self { - Arg::Pos(spanned) => spanned.span, - Arg::Key(Pair { key, value }) => Span::merge(key.span, value.span), - } +impl<T> ModelBounds for T where T: Model + PartialEq + Clone + 'static { + fn as_any(&self) -> &dyn Any { + self } -} -impl FuncArgs { - pub fn new() -> FuncArgs { - FuncArgs { - positional: Tuple::new(), - keyword: Object::new(), + fn bound_eq(&self, other: &dyn Model) -> bool { + match other.as_any().downcast_ref::<Self>() { + Some(other) => self == other, + None => false, } } - /// Add a positional argument. - pub fn add_pos(&mut self, item: Spanned<Expression>) { - self.positional.add(item); - } - - /// Force-extract the first positional argument. - pub fn get_pos<E: ExpressionKind>(&mut self) -> ParseResult<E> { - expect(self.get_pos_opt()) - } - - /// Extract the first positional argument. - pub fn get_pos_opt<E: ExpressionKind>(&mut self) -> ParseResult<Option<E>> { - Ok(if !self.positional.items.is_empty() { - let spanned = self.positional.items.remove(0); - Some(E::from_expr(spanned)?) - } else { - None - }) - } - - /// Add a keyword argument. - pub fn add_key(&mut self, key: Spanned<Ident>, value: Spanned<Expression>) { - self.keyword.add(key, value); - } - - /// Add a keyword argument from an existing pair. - pub fn add_key_pair(&mut self, pair: Pair) { - self.keyword.add_pair(pair); - } - - /// Force-extract a keyword argument. - pub fn get_key<E: ExpressionKind>(&mut self, name: &str) -> ParseResult<E> { - expect(self.get_key_opt(name)) - } - - /// Extract a keyword argument. - pub fn get_key_opt<E: ExpressionKind>(&mut self, name: &str) -> ParseResult<Option<E>> { - self.keyword.pairs.iter() - .position(|p| p.key.v.0 == name) - .map(|index| { - let value = self.keyword.pairs.swap_remove(index).value; - E::from_expr(value) - }) - .transpose() - } - - /// Iterator over positional arguments. - pub fn iter_pos(&mut self) -> std::vec::IntoIter<Spanned<Expression>> { - let tuple = std::mem::replace(&mut self.positional, Tuple::new()); - tuple.items.into_iter() - } - - /// Iterator over all keyword arguments. - pub fn iter_keys(&mut self) -> std::vec::IntoIter<Pair> { - let object = std::mem::replace(&mut self.keyword, Object::new()); - object.pairs.into_iter() - } - - /// Clear the argument lists. - pub fn clear(&mut self) { - self.positional.items.clear(); - self.keyword.pairs.clear(); - } - - /// Whether both the positional and keyword argument lists are empty. - pub fn is_empty(&self) -> bool { - self.positional.items.is_empty() && self.keyword.pairs.is_empty() - } -} - -/// Extract the option expression kind from the option or return an error. -fn expect<E: ExpressionKind>(opt: ParseResult<Option<E>>) -> ParseResult<E> { - match opt { - Ok(Some(spanned)) => Ok(spanned), - Ok(None) => error!("expected {}", E::NAME), - Err(e) => Err(e), + fn bound_clone(&self) -> Box<dyn Model> { + Box::new(self.clone()) } } -#[derive(Debug, Clone, Eq, PartialEq, Serialize)] -pub struct Colorization { - pub tokens: Vec<Spanned<ColorToken>>, +/// A tree representation of source code. +#[derive(Debug, Clone, PartialEq)] +pub struct SyntaxModel { + pub nodes: SpanVec<Node>, } -impl Colorization { - pub fn new() -> Colorization { - Colorization { tokens: vec![] } - } - - pub fn add(&mut self, token: ColorToken, span: Span) { - self.tokens.push(Spanned { v: token, span }); +impl SyntaxModel { + /// Create an empty syntax model. + pub fn new() -> SyntaxModel { + SyntaxModel { nodes: vec![] } } - pub fn replace_last(&mut self, token: ColorToken) { - self.tokens.last_mut().expect("replace_last: no token").v = token; + /// Add a node to the model. + pub fn add(&mut self, node: Spanned<Node>) { + self.nodes.push(node); } } -/// Entities which can be colored by syntax highlighting. -#[derive(Debug, Copy, Clone, Eq, PartialEq, Serialize)] -#[serde(rename_all = "camelCase")] -pub enum ColorToken { - Comment, - Bracket, - FuncName, - Colon, - Key, - Equals, - Comma, - Paren, - Brace, - ExprIdent, - ExprStr, - ExprNumber, - ExprSize, - ExprBool, - Bold, - Italic, - Monospace, - Invalid, -} - -#[derive(Debug, Clone, Eq, PartialEq, Serialize)] -pub struct ErrorMap { - pub errors: Vec<Spanned<String>>, -} - -impl ErrorMap { - pub fn new() -> ErrorMap { - ErrorMap { errors: vec![] } +#[async_trait::async_trait(?Send)] +impl Model for SyntaxModel { + async fn layout<'a>(&'a self, ctx: LayoutContext<'_, '_>) -> Layouted<Commands<'a>> { + Layouted { + output: vec![Command::LayoutSyntaxModel(self)], + errors: vec![], + } } +} - pub fn add(&mut self, message: impl Into<String>, span: Span) { - self.errors.push(Spanned { v: message.into(), span }); +/// A node in the syntax tree. +#[derive(Debug, Clone)] +pub enum Node { + /// A number of whitespace characters containing less than two newlines. + Space, + /// Whitespace characters with more than two newlines. + Newline, + /// Plain text. + Text(String), + /// Italics enabled / disabled. + ToggleItalic, + /// Bolder enabled / disabled. + ToggleBolder, + /// Monospace enabled / disabled. + ToggleMonospace, + /// A submodel. + Model(Box<dyn Model>), +} + +impl PartialEq for Node { + fn eq(&self, other: &Node) -> bool { + use Node::*; + match (self, other) { + (Space, Space) => true, + (Newline, Newline) => true, + (Text(a), Text(b)) => a == b, + (ToggleItalic, ToggleItalic) => true, + (ToggleBolder, ToggleBolder) => true, + (ToggleMonospace, ToggleMonospace) => true, + (Model(a), Model(b)) => a == b, + _ => false, + } } +} - pub fn add_at(&mut self, message: impl Into<String>, pos: Position) { - self.errors.push(Spanned { v: message.into(), span: Span::at(pos) }) - } +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +pub enum Decoration { + ValidFuncName, + InvalidFuncName, + ArgumentKey, } |
