Introduce SyntaxSet
(#3450)
This commit is contained in:
parent
394864fd4a
commit
0fb2a67484
4
NOTICE
4
NOTICE
@ -159,6 +159,10 @@ The MIT License applies to:
|
||||
closely modelled after the `DownloadTracker` from rustup
|
||||
(https://github.com/rust-lang/rustup/blob/master/src/cli/download_tracker.rs)
|
||||
|
||||
* The `SyntaxSet` defined in `crates/typst-syntax/src/set.rs` which is
|
||||
based on the `TokenSet` from rust-analyzer
|
||||
(https://github.com/rust-lang/rust-analyzer/blob/master/crates/parser/src/token_set.rs)
|
||||
|
||||
The MIT License (MIT)
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
|
@ -1,5 +1,5 @@
|
||||
// Acknowledgement:
|
||||
// Closely modelled after rustup's [`DownloadTracker`].
|
||||
// Closely modelled after rustup's `DownloadTracker`.
|
||||
// https://github.com/rust-lang/rustup/blob/master/src/cli/download_tracker.rs
|
||||
|
||||
use std::collections::VecDeque;
|
||||
|
@ -4,7 +4,7 @@
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
|
||||
#[repr(u8)]
|
||||
pub enum SyntaxKind {
|
||||
/// Markup.
|
||||
/// The contents of a file or content block.
|
||||
Markup,
|
||||
/// Plain text without markup.
|
||||
Text,
|
||||
@ -182,7 +182,7 @@ pub enum SyntaxKind {
|
||||
/// The `as` keyword.
|
||||
As,
|
||||
|
||||
/// Code.
|
||||
/// The contents of a code block.
|
||||
Code,
|
||||
/// An identifier: `it`.
|
||||
Ident,
|
||||
|
@ -9,6 +9,7 @@ mod lexer;
|
||||
mod node;
|
||||
mod parser;
|
||||
mod reparser;
|
||||
mod set;
|
||||
mod source;
|
||||
mod span;
|
||||
|
||||
|
@ -4,16 +4,17 @@ use std::ops::Range;
|
||||
use ecow::{eco_format, EcoString};
|
||||
use unicode_math_class::MathClass;
|
||||
|
||||
use crate::{ast, is_ident, is_newline, LexMode, Lexer, SyntaxKind, SyntaxNode};
|
||||
use crate::set::SyntaxSet;
|
||||
use crate::{ast, is_ident, is_newline, set, LexMode, Lexer, SyntaxKind, SyntaxNode};
|
||||
|
||||
/// Parse a source file.
|
||||
/// Parses a source file.
|
||||
pub fn parse(text: &str) -> SyntaxNode {
|
||||
let mut p = Parser::new(text, 0, LexMode::Markup);
|
||||
markup(&mut p, true, 0, |_| false);
|
||||
p.finish().into_iter().next().unwrap()
|
||||
}
|
||||
|
||||
/// Parse top-level code.
|
||||
/// Parses top-level code.
|
||||
pub fn parse_code(text: &str) -> SyntaxNode {
|
||||
let mut p = Parser::new(text, 0, LexMode::Code);
|
||||
let m = p.marker();
|
||||
@ -23,13 +24,14 @@ pub fn parse_code(text: &str) -> SyntaxNode {
|
||||
p.finish().into_iter().next().unwrap()
|
||||
}
|
||||
|
||||
/// Parse top-level math.
|
||||
/// Parses top-level math.
|
||||
pub fn parse_math(text: &str) -> SyntaxNode {
|
||||
let mut p = Parser::new(text, 0, LexMode::Math);
|
||||
math(&mut p, |_| false);
|
||||
p.finish().into_iter().next().unwrap()
|
||||
}
|
||||
|
||||
/// Parses the contents of a file or content block.
|
||||
fn markup(
|
||||
p: &mut Parser,
|
||||
mut at_start: bool,
|
||||
@ -55,15 +57,16 @@ fn markup(
|
||||
continue;
|
||||
}
|
||||
|
||||
let prev = p.prev_end();
|
||||
markup_expr(p, &mut at_start);
|
||||
if !p.progress(prev) {
|
||||
if p.at_set(set::MARKUP_EXPR) {
|
||||
markup_expr(p, &mut at_start);
|
||||
} else {
|
||||
p.unexpected();
|
||||
}
|
||||
}
|
||||
p.wrap(m, SyntaxKind::Markup);
|
||||
}
|
||||
|
||||
/// Reparses a subsection of markup incrementally.
|
||||
pub(super) fn reparse_markup(
|
||||
text: &str,
|
||||
range: Range<usize>,
|
||||
@ -86,15 +89,17 @@ pub(super) fn reparse_markup(
|
||||
continue;
|
||||
}
|
||||
|
||||
let prev = p.prev_end();
|
||||
markup_expr(&mut p, at_start);
|
||||
if !p.progress(prev) {
|
||||
if p.at_set(set::MARKUP_EXPR) {
|
||||
markup_expr(&mut p, at_start);
|
||||
} else {
|
||||
p.unexpected();
|
||||
}
|
||||
}
|
||||
(p.balanced && p.current_start() == range.end).then(|| p.finish())
|
||||
}
|
||||
|
||||
/// Parses a single markup expression: This includes markup elements like
|
||||
/// spaces, text, and headings, and embedded code expressions.
|
||||
fn markup_expr(p: &mut Parser, at_start: &mut bool) {
|
||||
match p.current() {
|
||||
SyntaxKind::Space
|
||||
@ -138,42 +143,52 @@ fn markup_expr(p: &mut Parser, at_start: &mut bool) {
|
||||
*at_start = false;
|
||||
}
|
||||
|
||||
/// Parses strong content: `*Strong*`.
|
||||
fn strong(p: &mut Parser) {
|
||||
const END: SyntaxSet = SyntaxSet::new(&[
|
||||
SyntaxKind::Star,
|
||||
SyntaxKind::Parbreak,
|
||||
SyntaxKind::RightBracket,
|
||||
]);
|
||||
|
||||
let m = p.marker();
|
||||
p.assert(SyntaxKind::Star);
|
||||
markup(p, false, 0, |p| {
|
||||
p.at(SyntaxKind::Star)
|
||||
|| p.at(SyntaxKind::Parbreak)
|
||||
|| p.at(SyntaxKind::RightBracket)
|
||||
});
|
||||
markup(p, false, 0, |p| p.at_set(END));
|
||||
p.expect_closing_delimiter(m, SyntaxKind::Star);
|
||||
p.wrap(m, SyntaxKind::Strong);
|
||||
}
|
||||
|
||||
/// Parses emphasized content: `_Emphasized_`.
|
||||
fn emph(p: &mut Parser) {
|
||||
const END: SyntaxSet = SyntaxSet::new(&[
|
||||
SyntaxKind::Underscore,
|
||||
SyntaxKind::Parbreak,
|
||||
SyntaxKind::RightBracket,
|
||||
]);
|
||||
|
||||
let m = p.marker();
|
||||
p.assert(SyntaxKind::Underscore);
|
||||
markup(p, false, 0, |p| {
|
||||
p.at(SyntaxKind::Underscore)
|
||||
|| p.at(SyntaxKind::Parbreak)
|
||||
|| p.at(SyntaxKind::RightBracket)
|
||||
});
|
||||
markup(p, false, 0, |p| p.at_set(END));
|
||||
p.expect_closing_delimiter(m, SyntaxKind::Underscore);
|
||||
p.wrap(m, SyntaxKind::Emph);
|
||||
}
|
||||
|
||||
/// Parses a section heading: `= Introduction`.
|
||||
fn heading(p: &mut Parser) {
|
||||
const END: SyntaxSet =
|
||||
SyntaxSet::new(&[SyntaxKind::Label, SyntaxKind::RightBracket, SyntaxKind::Space]);
|
||||
|
||||
let m = p.marker();
|
||||
p.assert(SyntaxKind::HeadingMarker);
|
||||
whitespace_line(p);
|
||||
markup(p, false, usize::MAX, |p| {
|
||||
p.at(SyntaxKind::Label)
|
||||
|| p.at(SyntaxKind::RightBracket)
|
||||
|| (p.at(SyntaxKind::Space) && p.lexer.clone().next() == SyntaxKind::Label)
|
||||
p.at_set(END)
|
||||
&& (!p.at(SyntaxKind::Space) || p.lexer.clone().next() == SyntaxKind::Label)
|
||||
});
|
||||
p.wrap(m, SyntaxKind::Heading);
|
||||
}
|
||||
|
||||
/// Parses an item in a bullet list: `- ...`.
|
||||
fn list_item(p: &mut Parser) {
|
||||
let m = p.marker();
|
||||
let min_indent = p.column(p.current_start()) + 1;
|
||||
@ -183,6 +198,7 @@ fn list_item(p: &mut Parser) {
|
||||
p.wrap(m, SyntaxKind::ListItem);
|
||||
}
|
||||
|
||||
/// Parses an item in an enumeration (numbered list): `+ ...` or `1. ...`.
|
||||
fn enum_item(p: &mut Parser) {
|
||||
let m = p.marker();
|
||||
let min_indent = p.column(p.current_start()) + 1;
|
||||
@ -192,20 +208,23 @@ fn enum_item(p: &mut Parser) {
|
||||
p.wrap(m, SyntaxKind::EnumItem);
|
||||
}
|
||||
|
||||
/// Parses an item in a term list: `/ Term: Details`.
|
||||
fn term_item(p: &mut Parser) {
|
||||
const TERM_END: SyntaxSet =
|
||||
SyntaxSet::new(&[SyntaxKind::Colon, SyntaxKind::RightBracket]);
|
||||
|
||||
let m = p.marker();
|
||||
p.assert(SyntaxKind::TermMarker);
|
||||
let min_indent = p.column(p.prev_end());
|
||||
whitespace_line(p);
|
||||
markup(p, false, usize::MAX, |p| {
|
||||
p.at(SyntaxKind::Colon) || p.at(SyntaxKind::RightBracket)
|
||||
});
|
||||
markup(p, false, usize::MAX, |p| p.at_set(TERM_END));
|
||||
p.expect(SyntaxKind::Colon);
|
||||
whitespace_line(p);
|
||||
markup(p, false, min_indent, |p| p.at(SyntaxKind::RightBracket));
|
||||
p.wrap(m, SyntaxKind::TermItem);
|
||||
}
|
||||
|
||||
/// Parses a reference: `@target`, `@target[..]`.
|
||||
fn reference(p: &mut Parser) {
|
||||
let m = p.marker();
|
||||
p.assert(SyntaxKind::RefMarker);
|
||||
@ -215,12 +234,14 @@ fn reference(p: &mut Parser) {
|
||||
p.wrap(m, SyntaxKind::Ref);
|
||||
}
|
||||
|
||||
/// Consumes whitespace that does not contain a newline.
|
||||
fn whitespace_line(p: &mut Parser) {
|
||||
while !p.newline() && p.current().is_trivia() {
|
||||
p.eat();
|
||||
}
|
||||
}
|
||||
|
||||
/// Parses a mathematical equation: `$x$`, `$ x^2 $`.
|
||||
fn equation(p: &mut Parser) {
|
||||
let m = p.marker();
|
||||
p.enter(LexMode::Math);
|
||||
@ -231,22 +252,26 @@ fn equation(p: &mut Parser) {
|
||||
p.wrap(m, SyntaxKind::Equation);
|
||||
}
|
||||
|
||||
/// Parses the contents of a mathematical equation: `x^2 + 1`.
|
||||
fn math(p: &mut Parser, mut stop: impl FnMut(&Parser) -> bool) {
|
||||
let m = p.marker();
|
||||
while !p.eof() && !stop(p) {
|
||||
let prev = p.prev_end();
|
||||
math_expr(p);
|
||||
if !p.progress(prev) {
|
||||
if p.at_set(set::MATH_EXPR) {
|
||||
math_expr(p);
|
||||
} else {
|
||||
p.unexpected();
|
||||
}
|
||||
}
|
||||
p.wrap(m, SyntaxKind::Math);
|
||||
}
|
||||
|
||||
/// Parses a single math expression: This includes math elements like
|
||||
/// attachment, fractions, and roots, and embedded code expressions.
|
||||
fn math_expr(p: &mut Parser) {
|
||||
math_expr_prec(p, 0, SyntaxKind::Eof)
|
||||
}
|
||||
|
||||
/// Parses a math expression with at least the given precedence.
|
||||
fn math_expr_prec(p: &mut Parser, min_prec: usize, stop: SyntaxKind) {
|
||||
let m = p.marker();
|
||||
let mut continuable = false;
|
||||
@ -415,9 +440,9 @@ fn math_delimited(p: &mut Parser) {
|
||||
return;
|
||||
}
|
||||
|
||||
let prev = p.prev_end();
|
||||
math_expr(p);
|
||||
if !p.progress(prev) {
|
||||
if p.at_set(set::MATH_EXPR) {
|
||||
math_expr(p);
|
||||
} else {
|
||||
p.unexpected();
|
||||
}
|
||||
}
|
||||
@ -520,9 +545,9 @@ fn math_args(p: &mut Parser) {
|
||||
_ => {}
|
||||
}
|
||||
|
||||
let prev = p.prev_end();
|
||||
math_expr(p);
|
||||
if !p.progress(prev) {
|
||||
if p.at_set(set::MATH_EXPR) {
|
||||
math_expr(p);
|
||||
} else {
|
||||
p.unexpected();
|
||||
}
|
||||
|
||||
@ -561,27 +586,34 @@ fn maybe_wrap_in_math(p: &mut Parser, arg: Marker, named: Option<Marker>) {
|
||||
}
|
||||
}
|
||||
|
||||
/// Parses the contents of a code block.
|
||||
fn code(p: &mut Parser, stop: impl FnMut(&Parser) -> bool) {
|
||||
let m = p.marker();
|
||||
code_exprs(p, stop);
|
||||
p.wrap(m, SyntaxKind::Code);
|
||||
}
|
||||
|
||||
/// Parses a sequence of code expressions.
|
||||
fn code_exprs(p: &mut Parser, mut stop: impl FnMut(&Parser) -> bool) {
|
||||
while !p.eof() && !stop(p) {
|
||||
p.enter_newline_mode(NewlineMode::Contextual);
|
||||
let prev = p.prev_end();
|
||||
code_expr(p);
|
||||
if p.progress(prev) && !p.eof() && !stop(p) && !p.eat_if(SyntaxKind::Semicolon) {
|
||||
p.expected("semicolon or line break");
|
||||
|
||||
let at_expr = p.at_set(set::CODE_EXPR);
|
||||
if at_expr {
|
||||
code_expr(p);
|
||||
if !p.eof() && !stop(p) && !p.eat_if(SyntaxKind::Semicolon) {
|
||||
p.expected("semicolon or line break");
|
||||
}
|
||||
}
|
||||
|
||||
p.exit_newline_mode();
|
||||
if !p.progress(prev) && !p.eof() {
|
||||
if !at_expr && !p.eof() {
|
||||
p.unexpected();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Parses a single code expression.
|
||||
fn code_expr(p: &mut Parser) {
|
||||
code_expr_prec(p, false, 0, false)
|
||||
}
|
||||
@ -590,27 +622,19 @@ fn code_expr_or_pattern(p: &mut Parser) {
|
||||
code_expr_prec(p, false, 0, true)
|
||||
}
|
||||
|
||||
/// Parses a code expression embedded in markup or math.
|
||||
fn embedded_code_expr(p: &mut Parser) {
|
||||
p.enter_newline_mode(NewlineMode::Stop);
|
||||
p.enter(LexMode::Code);
|
||||
p.assert(SyntaxKind::Hash);
|
||||
p.unskip();
|
||||
|
||||
let stmt = matches!(
|
||||
p.current(),
|
||||
SyntaxKind::Let
|
||||
| SyntaxKind::Set
|
||||
| SyntaxKind::Show
|
||||
| SyntaxKind::Import
|
||||
| SyntaxKind::Include
|
||||
| SyntaxKind::Return
|
||||
);
|
||||
|
||||
let prev = p.prev_end();
|
||||
let stmt = p.at_set(set::STMT);
|
||||
let at = p.at_set(set::ATOMIC_CODE_EXPR);
|
||||
code_expr_prec(p, true, 0, false);
|
||||
|
||||
// Consume error for things like `#12p` or `#"abc\"`.#
|
||||
if !p.progress(prev) && !p.current().is_trivia() && !p.eof() {
|
||||
if !at && !p.current().is_trivia() && !p.eof() {
|
||||
p.unexpected();
|
||||
}
|
||||
|
||||
@ -625,6 +649,7 @@ fn embedded_code_expr(p: &mut Parser) {
|
||||
p.exit_newline_mode();
|
||||
}
|
||||
|
||||
/// Parses a code expression with at least the given precedence.
|
||||
fn code_expr_prec(
|
||||
p: &mut Parser,
|
||||
atomic: bool,
|
||||
@ -632,7 +657,8 @@ fn code_expr_prec(
|
||||
allow_destructuring: bool,
|
||||
) {
|
||||
let m = p.marker();
|
||||
if let (false, Some(op)) = (atomic, ast::UnOp::from_kind(p.current())) {
|
||||
if !atomic && p.at_set(set::UNARY_OP) {
|
||||
let op = ast::UnOp::from_kind(p.current()).unwrap();
|
||||
p.eat();
|
||||
code_expr_prec(p, atomic, op.precedence(), false);
|
||||
p.wrap(m, SyntaxKind::Unary);
|
||||
@ -661,17 +687,19 @@ fn code_expr_prec(
|
||||
continue;
|
||||
}
|
||||
|
||||
let binop =
|
||||
if ast::BinOp::NotIn.precedence() >= min_prec && p.eat_if(SyntaxKind::Not) {
|
||||
if p.at(SyntaxKind::In) {
|
||||
Some(ast::BinOp::NotIn)
|
||||
} else {
|
||||
p.expected("keyword `in`");
|
||||
break;
|
||||
}
|
||||
let binop = if p.at_set(set::BINARY_OP) {
|
||||
ast::BinOp::from_kind(p.current())
|
||||
} else if min_prec <= ast::BinOp::NotIn.precedence() && p.eat_if(SyntaxKind::Not)
|
||||
{
|
||||
if p.at(SyntaxKind::In) {
|
||||
Some(ast::BinOp::NotIn)
|
||||
} else {
|
||||
ast::BinOp::from_kind(p.current())
|
||||
};
|
||||
p.expected("keyword `in`");
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
if let Some(op) = binop {
|
||||
let mut prec = op.precedence();
|
||||
@ -694,6 +722,9 @@ fn code_expr_prec(
|
||||
}
|
||||
}
|
||||
|
||||
/// Parses an primary in a code expression. These are the atoms that unary and
|
||||
/// binary operations, functions calls, and field accesses start with / are
|
||||
/// composed of.
|
||||
fn code_primary(p: &mut Parser, atomic: bool, allow_destructuring: bool) {
|
||||
let m = p.marker();
|
||||
match p.current() {
|
||||
@ -748,6 +779,7 @@ fn code_primary(p: &mut Parser, atomic: bool, allow_destructuring: bool) {
|
||||
}
|
||||
}
|
||||
|
||||
/// Parses a content or code block.
|
||||
fn block(p: &mut Parser) {
|
||||
match p.current() {
|
||||
SyntaxKind::LeftBracket => content_block(p),
|
||||
@ -756,6 +788,7 @@ fn block(p: &mut Parser) {
|
||||
}
|
||||
}
|
||||
|
||||
/// Reparses a full content or code block.
|
||||
pub(super) fn reparse_block(text: &str, range: Range<usize>) -> Option<SyntaxNode> {
|
||||
let mut p = Parser::new(text, range.start, LexMode::Code);
|
||||
assert!(p.at(SyntaxKind::LeftBracket) || p.at(SyntaxKind::LeftBrace));
|
||||
@ -764,22 +797,26 @@ pub(super) fn reparse_block(text: &str, range: Range<usize>) -> Option<SyntaxNod
|
||||
.then(|| p.finish().into_iter().next().unwrap())
|
||||
}
|
||||
|
||||
/// Parses a code block: `{ let x = 1; x + 2 }`.
|
||||
fn code_block(p: &mut Parser) {
|
||||
const END: SyntaxSet = SyntaxSet::new(&[
|
||||
SyntaxKind::RightBrace,
|
||||
SyntaxKind::RightBracket,
|
||||
SyntaxKind::RightParen,
|
||||
]);
|
||||
|
||||
let m = p.marker();
|
||||
p.enter(LexMode::Code);
|
||||
p.enter_newline_mode(NewlineMode::Continue);
|
||||
p.assert(SyntaxKind::LeftBrace);
|
||||
code(p, |p| {
|
||||
p.at(SyntaxKind::RightBrace)
|
||||
|| p.at(SyntaxKind::RightBracket)
|
||||
|| p.at(SyntaxKind::RightParen)
|
||||
});
|
||||
code(p, |p| p.at_set(END));
|
||||
p.expect_closing_delimiter(m, SyntaxKind::RightBrace);
|
||||
p.exit();
|
||||
p.exit_newline_mode();
|
||||
p.wrap(m, SyntaxKind::CodeBlock);
|
||||
}
|
||||
|
||||
/// Parses a content block: `[*Hi* there!]`.
|
||||
fn content_block(p: &mut Parser) {
|
||||
let m = p.marker();
|
||||
p.enter(LexMode::Markup);
|
||||
@ -964,6 +1001,7 @@ fn item(p: &mut Parser, keyed: bool) -> SyntaxKind {
|
||||
kind
|
||||
}
|
||||
|
||||
/// Parses a function call's argument list: `(12pt, y)`.
|
||||
fn args(p: &mut Parser) {
|
||||
if !p.at(SyntaxKind::LeftParen) && !p.at(SyntaxKind::LeftBracket) {
|
||||
p.expected("argument list");
|
||||
@ -987,6 +1025,7 @@ enum PatternKind {
|
||||
Other,
|
||||
}
|
||||
|
||||
/// Parses a pattern that can be assigned to.
|
||||
fn pattern(p: &mut Parser) -> PatternKind {
|
||||
let m = p.marker();
|
||||
if p.at(SyntaxKind::LeftParen) {
|
||||
@ -1005,6 +1044,7 @@ fn pattern(p: &mut Parser) -> PatternKind {
|
||||
}
|
||||
}
|
||||
|
||||
/// Parses a let binding: `let x = 1`.
|
||||
fn let_binding(p: &mut Parser) {
|
||||
let m = p.marker();
|
||||
p.assert(SyntaxKind::Let);
|
||||
@ -1037,6 +1077,7 @@ fn let_binding(p: &mut Parser) {
|
||||
p.wrap(m, SyntaxKind::LetBinding);
|
||||
}
|
||||
|
||||
/// Parses a set rule: `set text(...)`.
|
||||
fn set_rule(p: &mut Parser) {
|
||||
let m = p.marker();
|
||||
p.assert(SyntaxKind::Set);
|
||||
@ -1055,6 +1096,7 @@ fn set_rule(p: &mut Parser) {
|
||||
p.wrap(m, SyntaxKind::SetRule);
|
||||
}
|
||||
|
||||
/// Parses a show rule: `show heading: it => emph(it.body)`.
|
||||
fn show_rule(p: &mut Parser) {
|
||||
let m = p.marker();
|
||||
p.assert(SyntaxKind::Show);
|
||||
@ -1073,6 +1115,7 @@ fn show_rule(p: &mut Parser) {
|
||||
p.wrap(m, SyntaxKind::ShowRule);
|
||||
}
|
||||
|
||||
/// Parses an if-else conditional: `if x { y } else { z }`.
|
||||
fn conditional(p: &mut Parser) {
|
||||
let m = p.marker();
|
||||
p.assert(SyntaxKind::If);
|
||||
@ -1088,6 +1131,7 @@ fn conditional(p: &mut Parser) {
|
||||
p.wrap(m, SyntaxKind::Conditional);
|
||||
}
|
||||
|
||||
/// Parses a while loop: `while x { y }`.
|
||||
fn while_loop(p: &mut Parser) {
|
||||
let m = p.marker();
|
||||
p.assert(SyntaxKind::While);
|
||||
@ -1096,6 +1140,7 @@ fn while_loop(p: &mut Parser) {
|
||||
p.wrap(m, SyntaxKind::WhileLoop);
|
||||
}
|
||||
|
||||
/// Parses a for loop: `for x in y { z }`.
|
||||
fn for_loop(p: &mut Parser) {
|
||||
let m = p.marker();
|
||||
p.assert(SyntaxKind::For);
|
||||
@ -1115,6 +1160,7 @@ fn for_loop(p: &mut Parser) {
|
||||
p.wrap(m, SyntaxKind::ForLoop);
|
||||
}
|
||||
|
||||
/// Parses a module import: `import "utils.typ": a, b, c`.
|
||||
fn module_import(p: &mut Parser) {
|
||||
let m = p.marker();
|
||||
p.assert(SyntaxKind::Import);
|
||||
@ -1131,6 +1177,7 @@ fn module_import(p: &mut Parser) {
|
||||
p.wrap(m, SyntaxKind::ModuleImport);
|
||||
}
|
||||
|
||||
/// Parses items to import from a module: `a, b, c`.
|
||||
fn import_items(p: &mut Parser) {
|
||||
let m = p.marker();
|
||||
while !p.eof() && !p.at(SyntaxKind::Semicolon) {
|
||||
@ -1153,6 +1200,7 @@ fn import_items(p: &mut Parser) {
|
||||
p.wrap(m, SyntaxKind::ImportItems);
|
||||
}
|
||||
|
||||
/// Parses a module include: `include "chapter1.typ"`.
|
||||
fn module_include(p: &mut Parser) {
|
||||
let m = p.marker();
|
||||
p.assert(SyntaxKind::Include);
|
||||
@ -1160,22 +1208,25 @@ fn module_include(p: &mut Parser) {
|
||||
p.wrap(m, SyntaxKind::ModuleInclude);
|
||||
}
|
||||
|
||||
/// Parses a break from a loop: `break`.
|
||||
fn break_stmt(p: &mut Parser) {
|
||||
let m = p.marker();
|
||||
p.assert(SyntaxKind::Break);
|
||||
p.wrap(m, SyntaxKind::LoopBreak);
|
||||
}
|
||||
|
||||
/// Parses a continue in a loop: `continue`.
|
||||
fn continue_stmt(p: &mut Parser) {
|
||||
let m = p.marker();
|
||||
p.assert(SyntaxKind::Continue);
|
||||
p.wrap(m, SyntaxKind::LoopContinue);
|
||||
}
|
||||
|
||||
/// Parses a return from a function: `return`, `return x + 1`.
|
||||
fn return_stmt(p: &mut Parser) {
|
||||
let m = p.marker();
|
||||
p.assert(SyntaxKind::Return);
|
||||
if !p.current().is_terminator() && !p.at(SyntaxKind::Comma) {
|
||||
if p.at_set(set::CODE_EXPR) {
|
||||
code_expr(p);
|
||||
}
|
||||
p.wrap(m, SyntaxKind::FuncReturn);
|
||||
@ -1498,6 +1549,10 @@ impl<'s> Parser<'s> {
|
||||
self.current == kind
|
||||
}
|
||||
|
||||
fn at_set(&self, set: SyntaxSet) -> bool {
|
||||
set.contains(self.current)
|
||||
}
|
||||
|
||||
#[track_caller]
|
||||
fn assert(&mut self, kind: SyntaxKind) {
|
||||
assert_eq!(self.current, kind);
|
||||
|
171
crates/typst-syntax/src/set.rs
Normal file
171
crates/typst-syntax/src/set.rs
Normal file
@ -0,0 +1,171 @@
|
||||
// Acknowledgement:
|
||||
// Based on rust-analyzer's `TokenSet`.
|
||||
// https://github.com/rust-lang/rust-analyzer/blob/master/crates/parser/src/token_set.rs
|
||||
|
||||
use crate::SyntaxKind;
|
||||
|
||||
/// A set of syntax kinds.
|
||||
#[derive(Default, Copy, Clone)]
|
||||
pub struct SyntaxSet(u128);
|
||||
|
||||
impl SyntaxSet {
|
||||
/// Create a new set from a slice of kinds.
|
||||
pub const fn new(slice: &[SyntaxKind]) -> Self {
|
||||
let mut bits = 0;
|
||||
let mut i = 0;
|
||||
while i < slice.len() {
|
||||
bits |= bit(slice[i]);
|
||||
i += 1;
|
||||
}
|
||||
Self(bits)
|
||||
}
|
||||
|
||||
/// Insert a syntax kind into the set.
|
||||
pub const fn union(self, other: Self) -> Self {
|
||||
Self(self.0 | other.0)
|
||||
}
|
||||
|
||||
/// Whether the set contains the given syntax kind.
|
||||
pub const fn contains(&self, kind: SyntaxKind) -> bool {
|
||||
(self.0 & bit(kind)) != 0
|
||||
}
|
||||
}
|
||||
|
||||
const fn bit(kind: SyntaxKind) -> u128 {
|
||||
1 << (kind as usize)
|
||||
}
|
||||
|
||||
/// Syntax kinds that can start a statement.
|
||||
pub const STMT: SyntaxSet = SyntaxSet::new(&[
|
||||
SyntaxKind::Let,
|
||||
SyntaxKind::Set,
|
||||
SyntaxKind::Show,
|
||||
SyntaxKind::Import,
|
||||
SyntaxKind::Include,
|
||||
SyntaxKind::Return,
|
||||
]);
|
||||
|
||||
/// Syntax kinds that can start a markup expression.
|
||||
pub const MARKUP_EXPR: SyntaxSet = SyntaxSet::new(&[
|
||||
SyntaxKind::Space,
|
||||
SyntaxKind::Parbreak,
|
||||
SyntaxKind::LineComment,
|
||||
SyntaxKind::BlockComment,
|
||||
SyntaxKind::Text,
|
||||
SyntaxKind::Linebreak,
|
||||
SyntaxKind::Escape,
|
||||
SyntaxKind::Shorthand,
|
||||
SyntaxKind::SmartQuote,
|
||||
SyntaxKind::Raw,
|
||||
SyntaxKind::Link,
|
||||
SyntaxKind::Label,
|
||||
SyntaxKind::Hash,
|
||||
SyntaxKind::Star,
|
||||
SyntaxKind::Underscore,
|
||||
SyntaxKind::HeadingMarker,
|
||||
SyntaxKind::ListMarker,
|
||||
SyntaxKind::EnumMarker,
|
||||
SyntaxKind::TermMarker,
|
||||
SyntaxKind::RefMarker,
|
||||
SyntaxKind::Dollar,
|
||||
SyntaxKind::LeftBracket,
|
||||
SyntaxKind::RightBracket,
|
||||
SyntaxKind::Colon,
|
||||
]);
|
||||
|
||||
/// Syntax kinds that can start a math expression.
|
||||
pub const MATH_EXPR: SyntaxSet = SyntaxSet::new(&[
|
||||
SyntaxKind::Hash,
|
||||
SyntaxKind::MathIdent,
|
||||
SyntaxKind::Text,
|
||||
SyntaxKind::Shorthand,
|
||||
SyntaxKind::Linebreak,
|
||||
SyntaxKind::MathAlignPoint,
|
||||
SyntaxKind::Escape,
|
||||
SyntaxKind::Str,
|
||||
SyntaxKind::Root,
|
||||
SyntaxKind::Prime,
|
||||
]);
|
||||
|
||||
/// Syntax kinds that can start a code expression.
|
||||
pub const CODE_EXPR: SyntaxSet = CODE_PRIMARY.union(UNARY_OP);
|
||||
|
||||
/// Syntax kinds that can start an atomic code expression.
|
||||
pub const ATOMIC_CODE_EXPR: SyntaxSet = ATOMIC_CODE_PRIMARY;
|
||||
|
||||
/// Syntax kinds that can start a code primary.
|
||||
pub const CODE_PRIMARY: SyntaxSet =
|
||||
ATOMIC_CODE_PRIMARY.union(SyntaxSet::new(&[SyntaxKind::Underscore]));
|
||||
|
||||
/// Syntax kinds that can start an atomic code primary.
|
||||
pub const ATOMIC_CODE_PRIMARY: SyntaxSet = SyntaxSet::new(&[
|
||||
SyntaxKind::Ident,
|
||||
SyntaxKind::LeftBrace,
|
||||
SyntaxKind::LeftBracket,
|
||||
SyntaxKind::LeftParen,
|
||||
SyntaxKind::Dollar,
|
||||
SyntaxKind::Let,
|
||||
SyntaxKind::Set,
|
||||
SyntaxKind::Show,
|
||||
SyntaxKind::If,
|
||||
SyntaxKind::While,
|
||||
SyntaxKind::For,
|
||||
SyntaxKind::Import,
|
||||
SyntaxKind::Include,
|
||||
SyntaxKind::Break,
|
||||
SyntaxKind::Continue,
|
||||
SyntaxKind::Return,
|
||||
SyntaxKind::None,
|
||||
SyntaxKind::Auto,
|
||||
SyntaxKind::Int,
|
||||
SyntaxKind::Float,
|
||||
SyntaxKind::Bool,
|
||||
SyntaxKind::Numeric,
|
||||
SyntaxKind::Str,
|
||||
SyntaxKind::Label,
|
||||
SyntaxKind::Raw,
|
||||
]);
|
||||
|
||||
/// Syntax kinds that are unary operators.
|
||||
pub const UNARY_OP: SyntaxSet =
|
||||
SyntaxSet::new(&[SyntaxKind::Plus, SyntaxKind::Minus, SyntaxKind::Not]);
|
||||
|
||||
/// Syntax kinds that are binary operators.
|
||||
pub const BINARY_OP: SyntaxSet = SyntaxSet::new(&[
|
||||
SyntaxKind::Plus,
|
||||
SyntaxKind::Minus,
|
||||
SyntaxKind::Star,
|
||||
SyntaxKind::Slash,
|
||||
SyntaxKind::And,
|
||||
SyntaxKind::Or,
|
||||
SyntaxKind::EqEq,
|
||||
SyntaxKind::ExclEq,
|
||||
SyntaxKind::Lt,
|
||||
SyntaxKind::LtEq,
|
||||
SyntaxKind::Gt,
|
||||
SyntaxKind::GtEq,
|
||||
SyntaxKind::Eq,
|
||||
SyntaxKind::In,
|
||||
SyntaxKind::PlusEq,
|
||||
SyntaxKind::HyphEq,
|
||||
SyntaxKind::StarEq,
|
||||
SyntaxKind::SlashEq,
|
||||
]);
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_size() {
|
||||
assert!((SyntaxKind::Eof as usize) < 128);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_set() {
|
||||
let set = SyntaxSet::new(&[SyntaxKind::And, SyntaxKind::Or]);
|
||||
assert!(set.contains(SyntaxKind::And));
|
||||
assert!(set.contains(SyntaxKind::Or));
|
||||
assert!(!set.contains(SyntaxKind::Not));
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user