summaryrefslogtreecommitdiff
path: root/src/syntax/parsing.rs
diff options
context:
space:
mode:
authorLaurenz <laurmaedje@gmail.com>2020-01-13 11:26:42 +0100
committerLaurenz <laurmaedje@gmail.com>2020-01-13 11:26:42 +0100
commita8f711d49ad65ee08c96fae2a8b52873667bdf5c (patch)
tree7f54ec3421e0add1bbb143a1a2244dc9ba958480 /src/syntax/parsing.rs
parentbd702c2029561a741f48095549a2b6ea97b3a09b (diff)
Checkpoint 🏁
Diffstat (limited to 'src/syntax/parsing.rs')
-rw-r--r--src/syntax/parsing.rs389
1 files changed, 385 insertions, 4 deletions
diff --git a/src/syntax/parsing.rs b/src/syntax/parsing.rs
index 4a50ef96..112c2f65 100644
--- a/src/syntax/parsing.rs
+++ b/src/syntax/parsing.rs
@@ -1,13 +1,55 @@
+use std::iter::Peekable;
+
use crate::func::Scope;
use super::*;
+use Token::*;
+
+
+/// A tree representation of source code.
+#[derive(Debug, PartialEq)]
+pub struct SyntaxTree {
+ pub nodes: Vec<Spanned<Node>>,
+}
+
+impl SyntaxTree {
+ /// Create an empty syntax tree.
+ pub fn new() -> SyntaxTree {
+ SyntaxTree { nodes: vec![] }
+ }
+}
+
+/// A node in the syntax tree.
+#[derive(Debug, 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),
+}
+/// An invocation of a function.
+#[derive(Debug)]
+pub struct FuncCall(pub Box<dyn LayoutFunc>);
-/// The result type for parsing.
-pub type ParseResult<T> = crate::TypesetResult<T>;
+impl PartialEq for FuncCall {
+ fn eq(&self, other: &FuncCall) -> bool {
+ &self.0 == &other.0
+ }
+}
/// Parses source code into a syntax tree given a context.
-pub fn parse(src: &str, ctx: ParseContext) -> ParseResult<SyntaxTree> {
- unimplemented!()
+pub fn parse(src: &str, ctx: ParseContext) -> SyntaxTree {
+ Parser::new(src, ctx).parse()
}
/// The context for parsing.
@@ -16,3 +58,342 @@ pub struct ParseContext<'a> {
/// The scope containing function definitions.
pub scope: &'a Scope,
}
+
+struct Parser<'s> {
+ src: &'s str,
+ ctx: ParseContext<'s>,
+ tokens: Peekable<Tokens<'s>>,
+ errors: Vec<Spanned<String>>,
+ colored: Vec<Spanned<ColorToken>>,
+ span: Span,
+}
+
+macro_rules! defer {
+ ($($tts:tt)*) => (
+ unimplemented!()
+ );
+}
+
+impl<'s> Parser<'s> {
+ fn new(src: &'s str, ctx: ParseContext<'s>) -> Parser<'s> {
+ Parser {
+ src,
+ ctx,
+ tokens: Tokens::new(src).peekable(),
+ errors: vec![],
+ colored: vec![],
+ span: Span::ZERO,
+ }
+ }
+
+ fn parse(mut self) -> SyntaxTree {
+ let mut tree = SyntaxTree::new();
+
+ loop {
+ self.skip_whitespace();
+
+ let start = self.position();
+
+ let node = match self.next() {
+ Some(LeftBracket) => self.parse_func().map(|f| Node::Func(f)),
+ Some(Star) => Some(Node::ToggleBolder),
+ Some(Underscore) => Some(Node::ToggleItalic),
+ Some(Backtick) => Some(Node::ToggleMonospace),
+ Some(Text(text)) => Some(Node::Text(text.to_owned())),
+ Some(other) => { self.unexpected(other); None },
+ None => break,
+ };
+
+ if let Some(node) = node {
+ let end = self.position();
+ let span = Span { start, end };
+
+ tree.nodes.push(Spanned { v: node, span });
+ }
+ }
+
+ tree
+ }
+
+ fn parse_func(&mut self) -> Option<FuncCall> {
+ let (name, args) = self.parse_func_header()?;
+ self.parse_func_call(name, args)
+ }
+
+ fn parse_func_header(&mut self) -> Option<(Spanned<Ident>, FuncArgs)> {
+ defer! { self.eat_until(|t| t == RightBracket, true); }
+
+ self.skip_whitespace();
+
+ let name = self.parse_func_name()?;
+
+ self.skip_whitespace();
+
+ let args = match self.next() {
+ Some(Colon) => self.parse_func_args(),
+ Some(RightBracket) => FuncArgs::new(),
+ other => {
+ self.expected("colon or closing bracket", other);
+ FuncArgs::new()
+ }
+ };
+
+ Some((name, args))
+ }
+
+ fn parse_func_call(
+ &mut self,
+ name: Spanned<Ident>,
+ args: FuncArgs,
+ ) -> Option<FuncCall> {
+ unimplemented!()
+ }
+
+ fn parse_func_name(&mut self) -> Option<Spanned<Ident>> {
+ match self.next() {
+ Some(ExprIdent(ident)) => {
+ self.color_span(ColorToken::FuncName, self.span(), true);
+ Some(Spanned { v: Ident(ident.to_string()), span: self.span() })
+ }
+ other => {
+ self.expected("identifier", other);
+ None
+ }
+ }
+ }
+
+ fn parse_func_args(&mut self) -> FuncArgs {
+ enum State {
+ Start,
+ Identifier(Spanned<Ident>),
+ Assignment(Spanned<Ident>),
+ Value,
+ }
+
+ impl State {
+ fn expected(&self) -> &'static str {
+ match self {
+ State::Start => "value or key",
+ State::Identifier(_) => "comma or assignment",
+ State::Assignment(_) => "value",
+ State::Value => "comma",
+ }
+ }
+ }
+
+ let mut args = FuncArgs::new();
+ let mut state = State::Start;
+
+ loop {
+ self.skip_whitespace();
+
+ /*
+ let token = self.next();
+ match token {
+ Some(ExprIdent(ident)) => match state {
+ State::Start => {
+ state = State::Identifier(Spanned {
+ v: Ident(ident.to_string()),
+ span: self.span(),
+ });
+ }
+ State::Identifier(prev) => {
+ self.expected(state.expected(), token);
+ args.add_pos(prev.map(|id| Expression::Ident(id)));
+ state = State::Identifier(Spanned {
+ v: Ident(ident.to_string()),
+ span: self.span(),
+ });
+ }
+ State::Assignment(key) => {
+ let span = Span::merge(key.span, self.span());
+ args.add_key(Spanned::new(KeyArg {
+ key,
+ value: Spanned {
+ v: Expression::Ident(Ident(ident.to_string())),
+ span: self.span(),
+ },
+ }, span));
+ state = State::Value;
+ }
+ State::Value => {
+ self.expected(state.expected(), token);
+ state = State::Identifier(Spanned {
+ v: Ident(ident.to_string()),
+ span: self.span(),
+ });
+ }
+ }
+
+ // Handle expressions.
+ Some(Expr(_)) | Some(LeftParen) | Some(LeftBrace) => {
+ let expr = match token.unwrap() {
+ Expr(e) => e,
+ LeftParen => self.parse_tuple(),
+ LeftBrace => self.parse_object(),
+ _ => unreachable!(),
+ }
+ }
+
+ // Handle commas after values.
+ Some(Comma) => match state {
+ State::Identifier(ident) => {
+ args.add_pos(ident.map(|id| Expression::Ident(id)));
+ state = State::Start;
+ }
+ State::Value => state = State::Start,
+ _ => self.expected(state.expected(), token),
+ }
+
+ // Handle the end of the function header.
+ Some(RightBracket) => {
+ match state {
+ State::Identifier(ident) => {
+ args.add_pos(ident.map(|id| Expression::Ident(id)));
+ }
+ State::Assignment(_) => {
+ self.expected(state.expected(), token);
+ }
+ _ => {}
+ }
+
+ break;
+ }
+ }
+ */
+ }
+
+ args
+ }
+
+ fn handle_expr(&mut self, expr: Spanned<Expression>) {
+
+ }
+
+ fn parse_tuple(&mut self) -> Spanned<Tuple> {
+ unimplemented!()
+ }
+
+ fn parse_object(&mut self) -> Spanned<Object> {
+ unimplemented!()
+ }
+
+ fn skip_whitespace(&mut self) {
+ self.eat_until(|t| match t {
+ Whitespace(_) | LineComment(_) | BlockComment(_) => false,
+ _ => true,
+ }, false)
+ }
+
+ fn eat_until<F>(&mut self, mut f: F, eat_match: bool)
+ where F: FnMut(Token<'s>) -> bool {
+ while let Some(token) = self.tokens.peek() {
+ if f(token.v) {
+ if eat_match {
+ self.next();
+ }
+ break;
+ }
+
+ self.next();
+ }
+ }
+
+ fn next(&mut self) -> Option<Token<'s>> {
+ self.tokens.next().map(|spanned| {
+ self.color_token(&spanned.v, spanned.span);
+ self.span = spanned.span;
+ spanned.v
+ })
+ }
+
+ fn span(&self) -> Span {
+ self.span
+ }
+
+ fn position(&self) -> Position {
+ self.span.end
+ }
+
+ fn unexpected(&mut self, found: Token) {
+ self.errors.push(Spanned {
+ v: format!("unexpected {}", name(found)),
+ span: self.span(),
+ });
+ }
+
+ fn expected(&mut self, thing: &str, found: Option<Token>) {
+ let message = if let Some(found) = found {
+ format!("expected {}, found {}", thing, name(found))
+ } else {
+ format!("expected {}", thing)
+ };
+
+ self.errors.push(Spanned {
+ v: message,
+ span: self.span(),
+ });
+ }
+
+ fn color_token(&mut self, token: &Token<'s>, span: Span) {
+ let colored = match token {
+ LineComment(_) | BlockComment(_) => Some(ColorToken::Comment),
+ StarSlash => Some(ColorToken::Invalid),
+ LeftBracket | RightBracket => Some(ColorToken::Bracket),
+ LeftParen | RightParen => Some(ColorToken::Paren),
+ LeftBrace | RightBrace => Some(ColorToken::Brace),
+ Colon => Some(ColorToken::Colon),
+ Comma => Some(ColorToken::Comma),
+ Equals => Some(ColorToken::Equals),
+ ExprIdent(_) => Some(ColorToken::ExprIdent),
+ ExprString(_) => Some(ColorToken::ExprString),
+ ExprNumber(_) => Some(ColorToken::ExprNumber),
+ ExprSize(_) => Some(ColorToken::ExprSize),
+ ExprBool(_) => Some(ColorToken::ExprBool),
+ _ => None,
+ };
+
+ if let Some(color) = colored {
+ self.colored.push(Spanned { v: color, span });
+ }
+ }
+
+ fn color_span(&mut self, color: ColorToken, span: Span, replace_last: bool) {
+ let token = Spanned { v: color, span };
+
+ if replace_last {
+ if let Some(last) = self.colored.last_mut() {
+ *last = token;
+ return;
+ }
+ }
+
+ self.colored.push(token);
+ }
+}
+
+fn name(token: Token) -> &'static str {
+ match token {
+ Whitespace(_) => "whitespace",
+ LineComment(_) | BlockComment(_) => "comment",
+ StarSlash => "end of block comment",
+ LeftBracket => "opening bracket",
+ RightBracket => "closing bracket",
+ LeftParen => "opening paren",
+ RightParen => "closing paren",
+ LeftBrace => "opening brace",
+ RightBrace => "closing brace",
+ Colon => "colon",
+ Comma => "comma",
+ Equals => "equals sign",
+ ExprIdent(_) => "identifier",
+ ExprString(_) => "string",
+ ExprNumber(_) => "number",
+ ExprSize(_) => "size",
+ ExprBool(_) => "bool",
+ Star => "star",
+ Underscore => "underscore",
+ Backtick => "backtick",
+ Text(_) => "text",
+ }
+}