mirror of
https://github.com/typst/typst
synced 2025-05-14 17:15:28 +08:00
9. Parse math field access in the lexer
This commit is contained in:
parent
09975d1133
commit
88d86714a1
@ -109,7 +109,10 @@ impl Lexer<'_> {
|
|||||||
Some('`') if self.mode != LexMode::Math => return self.raw(),
|
Some('`') if self.mode != LexMode::Math => return self.raw(),
|
||||||
Some(c) => match self.mode {
|
Some(c) => match self.mode {
|
||||||
LexMode::Markup => self.markup(start, c),
|
LexMode::Markup => self.markup(start, c),
|
||||||
LexMode::Math => self.math(start, c),
|
LexMode::Math => match self.math(start, c) {
|
||||||
|
(kind, None) => kind,
|
||||||
|
(kind, Some(node)) => return (kind, node),
|
||||||
|
},
|
||||||
LexMode::Code => self.code(start, c),
|
LexMode::Code => self.code(start, c),
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -507,8 +510,8 @@ impl Lexer<'_> {
|
|||||||
|
|
||||||
/// Math.
|
/// Math.
|
||||||
impl Lexer<'_> {
|
impl Lexer<'_> {
|
||||||
fn math(&mut self, start: usize, c: char) -> SyntaxKind {
|
fn math(&mut self, start: usize, c: char) -> (SyntaxKind, Option<SyntaxNode>) {
|
||||||
match c {
|
let kind = match c {
|
||||||
'\\' => self.backslash(),
|
'\\' => self.backslash(),
|
||||||
'"' => self.string(),
|
'"' => self.string(),
|
||||||
|
|
||||||
@ -561,11 +564,41 @@ impl Lexer<'_> {
|
|||||||
// Identifiers.
|
// Identifiers.
|
||||||
c if is_math_id_start(c) && self.s.at(is_math_id_continue) => {
|
c if is_math_id_start(c) && self.s.at(is_math_id_continue) => {
|
||||||
self.s.eat_while(is_math_id_continue);
|
self.s.eat_while(is_math_id_continue);
|
||||||
SyntaxKind::MathIdent
|
let (kind, node) = self.math_ident_or_field(start);
|
||||||
|
return (kind, Some(node));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Other math atoms.
|
// Other math atoms.
|
||||||
_ => self.math_text(start, c),
|
_ => self.math_text(start, c),
|
||||||
|
};
|
||||||
|
(kind, None)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Parse a single `MathIdent` or an entire `FieldAccess`.
|
||||||
|
fn math_ident_or_field(&mut self, start: usize) -> (SyntaxKind, SyntaxNode) {
|
||||||
|
let mut kind = SyntaxKind::MathIdent;
|
||||||
|
let mut node = SyntaxNode::leaf(kind, self.s.from(start));
|
||||||
|
while let Some(ident) = self.maybe_dot_ident() {
|
||||||
|
kind = SyntaxKind::FieldAccess;
|
||||||
|
let field_children = vec![
|
||||||
|
node,
|
||||||
|
SyntaxNode::leaf(SyntaxKind::Dot, '.'),
|
||||||
|
SyntaxNode::leaf(SyntaxKind::Ident, ident),
|
||||||
|
];
|
||||||
|
node = SyntaxNode::inner(kind, field_children);
|
||||||
|
}
|
||||||
|
(kind, node)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// If at a dot and a math identifier, eat and return the identifier.
|
||||||
|
fn maybe_dot_ident(&mut self) -> Option<&str> {
|
||||||
|
if self.s.scout(1).is_some_and(is_math_id_start) && self.s.eat_if('.') {
|
||||||
|
let ident_start = self.s.cursor();
|
||||||
|
self.s.eat();
|
||||||
|
self.s.eat_while(is_math_id_continue);
|
||||||
|
Some(self.s.from(ident_start))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,9 +6,7 @@ use ecow::{eco_format, EcoString};
|
|||||||
use unicode_math_class::MathClass;
|
use unicode_math_class::MathClass;
|
||||||
|
|
||||||
use crate::set::{syntax_set, SyntaxSet};
|
use crate::set::{syntax_set, SyntaxSet};
|
||||||
use crate::{
|
use crate::{ast, is_newline, set, LexMode, Lexer, SyntaxError, SyntaxKind, SyntaxNode};
|
||||||
ast, is_ident, is_newline, set, LexMode, Lexer, SyntaxError, SyntaxKind, SyntaxNode,
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Parses a source file as top-level markup.
|
/// Parses a source file as top-level markup.
|
||||||
pub fn parse(text: &str) -> SyntaxNode {
|
pub fn parse(text: &str) -> SyntaxNode {
|
||||||
@ -261,21 +259,11 @@ fn math_expr_prec(p: &mut Parser, min_prec: usize, stop: SyntaxKind) {
|
|||||||
let mut continuable = false;
|
let mut continuable = false;
|
||||||
match p.current() {
|
match p.current() {
|
||||||
SyntaxKind::Hash => embedded_code_expr(p),
|
SyntaxKind::Hash => embedded_code_expr(p),
|
||||||
SyntaxKind::MathIdent => {
|
// The lexer manages creating full FieldAccess nodes if needed.
|
||||||
|
SyntaxKind::MathIdent | SyntaxKind::FieldAccess => {
|
||||||
continuable = true;
|
continuable = true;
|
||||||
p.eat();
|
p.eat();
|
||||||
while p.directly_at(SyntaxKind::Text) && p.current_text() == "." && {
|
// Parse a function call for an identifier or field access.
|
||||||
let mut copy = p.lexer.clone();
|
|
||||||
let start = copy.cursor();
|
|
||||||
let next = copy.next().0;
|
|
||||||
let end = copy.cursor();
|
|
||||||
matches!(next, SyntaxKind::MathIdent | SyntaxKind::Text)
|
|
||||||
&& is_ident(&p.text[start..end])
|
|
||||||
} {
|
|
||||||
p.convert_and_eat(SyntaxKind::Dot);
|
|
||||||
p.convert_and_eat(SyntaxKind::Ident);
|
|
||||||
p.wrap(m, SyntaxKind::FieldAccess);
|
|
||||||
}
|
|
||||||
if min_prec < 3 && p.directly_at(SyntaxKind::Text) && p.current_text() == "("
|
if min_prec < 3 && p.directly_at(SyntaxKind::Text) && p.current_text() == "("
|
||||||
{
|
{
|
||||||
math_args(p);
|
math_args(p);
|
||||||
|
@ -58,6 +58,7 @@ pub const STMT: SyntaxSet = syntax_set!(Let, Set, Show, Import, Include, Return)
|
|||||||
pub const MATH_EXPR: SyntaxSet = syntax_set!(
|
pub const MATH_EXPR: SyntaxSet = syntax_set!(
|
||||||
Hash,
|
Hash,
|
||||||
MathIdent,
|
MathIdent,
|
||||||
|
FieldAccess,
|
||||||
Text,
|
Text,
|
||||||
MathShorthand,
|
MathShorthand,
|
||||||
Linebreak,
|
Linebreak,
|
||||||
|
29
tests/suite/math/symbols.typ
Normal file
29
tests/suite/math/symbols.typ
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
// Test math symbol edge cases.
|
||||||
|
|
||||||
|
--- math-symbol-basic ---
|
||||||
|
#let sym = symbol("s", ("basic", "s"))
|
||||||
|
#test($sym.basic$, $#"s"$)
|
||||||
|
|
||||||
|
--- math-symbol-underscore ---
|
||||||
|
#let sym = symbol("s", ("test_underscore", "s"))
|
||||||
|
// Error: 6-10 unknown symbol modifier
|
||||||
|
$sym.test_underscore$
|
||||||
|
|
||||||
|
--- math-symbol-dash ---
|
||||||
|
#let sym = symbol("s", ("test-dash", "s"))
|
||||||
|
// Error: 6-10 unknown symbol modifier
|
||||||
|
$sym.test-dash$
|
||||||
|
|
||||||
|
--- math-symbol-double ---
|
||||||
|
#let sym = symbol("s", ("test.basic", "s"))
|
||||||
|
#test($sym.test.basic$, $#"s"$)
|
||||||
|
|
||||||
|
--- math-symbol-double-underscore ---
|
||||||
|
#let sym = symbol("s", ("one.test_underscore", "s"))
|
||||||
|
// Error: 10-14 unknown symbol modifier
|
||||||
|
$sym.one.test_underscore$
|
||||||
|
|
||||||
|
--- math-symbol-double-dash ---
|
||||||
|
#let sym = symbol("s", ("one.test-dash", "s"))
|
||||||
|
// Error: 10-14 unknown symbol modifier
|
||||||
|
$sym.one.test-dash$
|
Loading…
x
Reference in New Issue
Block a user