diff options
| author | Laurenz <laurmaedje@gmail.com> | 2022-10-04 13:42:49 +0200 |
|---|---|---|
| committer | Laurenz <laurmaedje@gmail.com> | 2022-10-04 13:45:16 +0200 |
| commit | 5a8534a395b500a25cbc46ee15ec031c8231de59 (patch) | |
| tree | a525c447c3243fe315c7ed91923e158df131809b /src/parse/mod.rs | |
| parent | 7ef6cb31df0fe1ebec99b1077053a586a349f530 (diff) | |
Parse basic math syntax
Diffstat (limited to 'src/parse/mod.rs')
| -rw-r--r-- | src/parse/mod.rs | 152 |
1 files changed, 134 insertions, 18 deletions
diff --git a/src/parse/mod.rs b/src/parse/mod.rs index ed8bc5ce..7eb7343b 100644 --- a/src/parse/mod.rs +++ b/src/parse/mod.rs @@ -11,7 +11,7 @@ pub use tokens::*; use std::collections::HashSet; -use crate::syntax::ast::{Associativity, BinOp, UnOp}; +use crate::syntax::ast::{Assoc, BinOp, UnOp}; use crate::syntax::{NodeKind, SpanPos, SyntaxNode}; use crate::util::EcoString; @@ -22,11 +22,22 @@ pub fn parse(text: &str) -> SyntaxNode { p.finish().into_iter().next().unwrap() } +/// Parse math directly, only used for syntax highlighting. +pub fn parse_math(text: &str) -> SyntaxNode { + let mut p = Parser::new(text, TokenMode::Math); + p.perform(NodeKind::Math, |p| { + while !p.eof() { + math_node(p); + } + }); + p.finish().into_iter().next().unwrap() +} + /// Parse code directly, only used for syntax highlighting. -pub fn parse_code(text: &str) -> Vec<SyntaxNode> { +pub fn parse_code(text: &str) -> SyntaxNode { let mut p = Parser::new(text, TokenMode::Code); - code(&mut p); - p.finish() + p.perform(NodeKind::CodeBlock, code); + p.finish().into_iter().next().unwrap() } /// Reparse a code block. @@ -240,20 +251,20 @@ fn markup_node(p: &mut Parser, at_start: &mut bool) { // Text and markup. NodeKind::Text(_) | NodeKind::Linebreak { .. } - | NodeKind::NonBreakingSpace - | NodeKind::Shy - | NodeKind::EnDash - | NodeKind::EmDash - | NodeKind::Ellipsis + | NodeKind::Tilde + | NodeKind::HyphQuest + | NodeKind::Hyph2 + | NodeKind::Hyph3 + | NodeKind::Dot3 | NodeKind::Quote { .. } | NodeKind::Escape(_) | NodeKind::Link(_) | NodeKind::Raw(_) - | NodeKind::Math(_) | NodeKind::Label(_) - | NodeKind::Ref(_) => { - p.eat(); - } + | NodeKind::Ref(_) => p.eat(), + + // Math. + NodeKind::Dollar => math(p), // Strong, emph, heading. NodeKind::Star => strong(p), @@ -405,6 +416,111 @@ fn markup_expr(p: &mut Parser) { p.end_group(); } +/// Parse math. +fn math(p: &mut Parser) { + p.perform(NodeKind::Math, |p| { + p.start_group(Group::Math); + while !p.eof() { + math_node(p); + } + p.end_group(); + }); +} + +/// Parse a math node. +fn math_node(p: &mut Parser) { + math_node_prec(p, 0, None) +} + +/// Parse a math node with operators having at least the minimum precedence. +fn math_node_prec(p: &mut Parser, min_prec: usize, stop: Option<NodeKind>) { + let marker = p.marker(); + math_primary(p); + + loop { + let (kind, mut prec, assoc, stop) = match p.peek() { + v if v == stop.as_ref() => break, + Some(NodeKind::Underscore) => { + (NodeKind::Script, 2, Assoc::Right, Some(NodeKind::Hat)) + } + Some(NodeKind::Hat) => ( + NodeKind::Script, + 2, + Assoc::Right, + Some(NodeKind::Underscore), + ), + Some(NodeKind::Slash) => (NodeKind::Frac, 1, Assoc::Left, None), + _ => break, + }; + + if prec < min_prec { + break; + } + + match assoc { + Assoc::Left => prec += 1, + Assoc::Right => {} + } + + p.eat(); + math_node_prec(p, prec, stop); + + // Allow up to two different scripts. We do not risk encountering the + // previous script kind again here due to right-associativity. + if p.eat_if(NodeKind::Underscore) || p.eat_if(NodeKind::Hat) { + math_node_prec(p, prec, None); + } + + marker.end(p, kind); + } +} + +/// Parse a primary math node. +fn math_primary(p: &mut Parser) { + let token = match p.peek() { + Some(t) => t, + None => return, + }; + + match token { + // Spaces, atoms and expressions. + NodeKind::Space { .. } + | NodeKind::Linebreak + | NodeKind::Escape(_) + | NodeKind::Atom(_) + | NodeKind::Ident(_) => p.eat(), + + // Groups. + NodeKind::LeftParen => group(p, Group::Paren), + NodeKind::LeftBracket => group(p, Group::Bracket), + NodeKind::LeftBrace => group(p, Group::Brace), + + // Alignment indactor. + NodeKind::Amp => align(p), + + _ => p.unexpected(), + } +} + +/// Parse grouped math. +fn group(p: &mut Parser, group: Group) { + p.perform(NodeKind::Math, |p| { + p.start_group(group); + while !p.eof() { + math_node(p); + } + p.end_group(); + }) +} + +/// Parse an alignment indicator. +fn align(p: &mut Parser) { + p.perform(NodeKind::Align, |p| { + p.assert(NodeKind::Amp); + while p.eat_if(NodeKind::Amp) {} + }) +} + /// Parse an expression. fn expr(p: &mut Parser) -> ParseResult { expr_prec(p, false, 0) @@ -434,7 +550,7 @@ fn expr_prec(p: &mut Parser, atomic: bool, min_prec: usize) -> ParseResult { loop { // Parenthesis or bracket means this is a function call. if let Some(NodeKind::LeftParen | NodeKind::LeftBracket) = p.peek_direct() { - marker.perform(p, NodeKind::FuncCall, |p| args(p))?; + marker.perform(p, NodeKind::FuncCall, args)?; continue; } @@ -446,7 +562,7 @@ fn expr_prec(p: &mut Parser, atomic: bool, min_prec: usize) -> ParseResult { if p.eat_if(NodeKind::Dot) { ident(p)?; if let Some(NodeKind::LeftParen | NodeKind::LeftBracket) = p.peek_direct() { - marker.perform(p, NodeKind::MethodCall, |p| args(p))?; + marker.perform(p, NodeKind::MethodCall, args)?; } else { marker.end(p, NodeKind::FieldAccess); } @@ -474,9 +590,9 @@ fn expr_prec(p: &mut Parser, atomic: bool, min_prec: usize) -> ParseResult { p.eat(); - match op.associativity() { - Associativity::Left => prec += 1, - Associativity::Right => {} + match op.assoc() { + Assoc::Left => prec += 1, + Assoc::Right => {} } marker.perform(p, NodeKind::BinaryExpr, |p| expr_prec(p, atomic, prec))?; |
