summaryrefslogtreecommitdiff
path: root/src/syntax/mod.rs
diff options
context:
space:
mode:
authorLaurenz <laurmaedje@gmail.com>2020-01-19 21:50:20 +0100
committerLaurenz <laurmaedje@gmail.com>2020-01-19 21:53:24 +0100
commit95e6b078fecddeaa3d6f2c920b617201b74bf01e (patch)
tree1c03b0b16d614a5a2350dccf71a1eb1e34f9a812 /src/syntax/mod.rs
parent277f2d2176f5e98305870f90b16af3feae1bb3d1 (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.rs382
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,
}