Merge MarkupNode
and MathNode
into Expr
This commit is contained in:
parent
a6d90c1bf1
commit
7c683db367
@ -180,7 +180,7 @@ fn items() -> LangItems {
|
||||
em: |styles| styles.get(text::TextNode::SIZE),
|
||||
dir: |styles| styles.get(text::TextNode::DIR),
|
||||
space: || text::SpaceNode.pack(),
|
||||
linebreak: |justify| text::LinebreakNode { justify }.pack(),
|
||||
linebreak: || text::LinebreakNode { justify: false }.pack(),
|
||||
text: |text| text::TextNode(text).pack(),
|
||||
text_id: NodeId::of::<text::TextNode>(),
|
||||
text_str: |content| Some(&content.to::<text::TextNode>()?.0),
|
||||
|
@ -17,7 +17,7 @@ use crate::diag::{
|
||||
use crate::geom::{Abs, Angle, Em, Fr, Ratio};
|
||||
use crate::syntax::ast::AstNode;
|
||||
use crate::syntax::{ast, Source, SourceId, Span, Spanned, SyntaxKind, SyntaxNode, Unit};
|
||||
use crate::util::{format_eco, EcoString, PathExt};
|
||||
use crate::util::{EcoString, PathExt};
|
||||
use crate::World;
|
||||
|
||||
const MAX_ITERATIONS: usize = 10_000;
|
||||
@ -98,7 +98,7 @@ impl<'a> Vm<'a> {
|
||||
}
|
||||
|
||||
/// Access the underlying world.
|
||||
pub fn world(&self) -> Tracked<dyn World> {
|
||||
pub fn world(&self) -> Tracked<'a, dyn World> {
|
||||
self.world
|
||||
}
|
||||
|
||||
@ -203,38 +203,38 @@ impl Eval for ast::Markup {
|
||||
type Output = Content;
|
||||
|
||||
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
|
||||
eval_markup(vm, &mut self.children())
|
||||
eval_markup(vm, &mut self.exprs())
|
||||
}
|
||||
}
|
||||
|
||||
/// Evaluate a stream of markup nodes.
|
||||
/// Evaluate a stream of markup.
|
||||
fn eval_markup(
|
||||
vm: &mut Vm,
|
||||
nodes: &mut impl Iterator<Item = ast::MarkupNode>,
|
||||
exprs: &mut impl Iterator<Item = ast::Expr>,
|
||||
) -> SourceResult<Content> {
|
||||
let flow = vm.flow.take();
|
||||
let mut seq = Vec::with_capacity(nodes.size_hint().1.unwrap_or_default());
|
||||
let mut seq = Vec::with_capacity(exprs.size_hint().1.unwrap_or_default());
|
||||
|
||||
while let Some(node) = nodes.next() {
|
||||
match node {
|
||||
ast::MarkupNode::Expr(ast::Expr::Set(set)) => {
|
||||
while let Some(expr) = exprs.next() {
|
||||
match expr {
|
||||
ast::Expr::Set(set) => {
|
||||
let styles = set.eval(vm)?;
|
||||
if vm.flow.is_some() {
|
||||
break;
|
||||
}
|
||||
|
||||
seq.push(eval_markup(vm, nodes)?.styled_with_map(styles))
|
||||
seq.push(eval_markup(vm, exprs)?.styled_with_map(styles))
|
||||
}
|
||||
ast::MarkupNode::Expr(ast::Expr::Show(show)) => {
|
||||
ast::Expr::Show(show) => {
|
||||
let recipe = show.eval(vm)?;
|
||||
if vm.flow.is_some() {
|
||||
break;
|
||||
}
|
||||
|
||||
let tail = eval_markup(vm, nodes)?;
|
||||
let tail = eval_markup(vm, exprs)?;
|
||||
seq.push(tail.styled_with_recipe(vm.world, recipe)?)
|
||||
}
|
||||
ast::MarkupNode::Expr(expr) => match expr.eval(vm)? {
|
||||
expr => match expr.eval(vm)? {
|
||||
Value::Label(label) => {
|
||||
if let Some(node) =
|
||||
seq.iter_mut().rev().find(|node| node.labellable())
|
||||
@ -244,7 +244,6 @@ fn eval_markup(
|
||||
}
|
||||
value => seq.push(value.display().spanned(expr.span())),
|
||||
},
|
||||
_ => seq.push(node.eval(vm)?),
|
||||
}
|
||||
|
||||
if vm.flow.is_some() {
|
||||
@ -259,33 +258,88 @@ fn eval_markup(
|
||||
Ok(Content::sequence(seq))
|
||||
}
|
||||
|
||||
impl Eval for ast::MarkupNode {
|
||||
impl Eval for ast::Expr {
|
||||
type Output = Value;
|
||||
|
||||
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
|
||||
let forbidden = |name| {
|
||||
error!(
|
||||
self.span(),
|
||||
"{} is only allowed directly in code and content blocks", name
|
||||
)
|
||||
};
|
||||
|
||||
match self {
|
||||
Self::Space(v) => v.eval(vm).map(Value::Content),
|
||||
Self::Linebreak(v) => v.eval(vm).map(Value::Content),
|
||||
Self::Text(v) => v.eval(vm).map(Value::Content),
|
||||
Self::Escape(v) => v.eval(vm).map(Value::Content),
|
||||
Self::Shorthand(v) => v.eval(vm).map(Value::Content),
|
||||
Self::Symbol(v) => v.eval(vm).map(Value::Content),
|
||||
Self::SmartQuote(v) => v.eval(vm).map(Value::Content),
|
||||
Self::Strong(v) => v.eval(vm).map(Value::Content),
|
||||
Self::Emph(v) => v.eval(vm).map(Value::Content),
|
||||
Self::Link(v) => v.eval(vm).map(Value::Content),
|
||||
Self::Raw(v) => v.eval(vm).map(Value::Content),
|
||||
Self::Ref(v) => v.eval(vm).map(Value::Content),
|
||||
Self::Heading(v) => v.eval(vm).map(Value::Content),
|
||||
Self::List(v) => v.eval(vm).map(Value::Content),
|
||||
Self::Enum(v) => v.eval(vm).map(Value::Content),
|
||||
Self::Term(v) => v.eval(vm).map(Value::Content),
|
||||
Self::Atom(v) => v.eval(vm).map(Value::Content),
|
||||
Self::Script(v) => v.eval(vm).map(Value::Content),
|
||||
Self::Frac(v) => v.eval(vm).map(Value::Content),
|
||||
Self::AlignPoint(v) => v.eval(vm).map(Value::Content),
|
||||
Self::Lit(v) => v.eval(vm),
|
||||
Self::Ident(v) => v.eval(vm),
|
||||
Self::Code(v) => v.eval(vm),
|
||||
Self::Content(v) => v.eval(vm).map(Value::Content),
|
||||
Self::Math(v) => v.eval(vm).map(Value::Content),
|
||||
Self::Array(v) => v.eval(vm).map(Value::Array),
|
||||
Self::Dict(v) => v.eval(vm).map(Value::Dict),
|
||||
Self::Parenthesized(v) => v.eval(vm),
|
||||
Self::FieldAccess(v) => v.eval(vm),
|
||||
Self::FuncCall(v) => v.eval(vm),
|
||||
Self::MethodCall(v) => v.eval(vm),
|
||||
Self::Closure(v) => v.eval(vm),
|
||||
Self::Unary(v) => v.eval(vm),
|
||||
Self::Binary(v) => v.eval(vm),
|
||||
Self::Let(v) => v.eval(vm),
|
||||
Self::Set(_) => bail!(forbidden("set")),
|
||||
Self::Show(_) => bail!(forbidden("show")),
|
||||
Self::Conditional(v) => v.eval(vm),
|
||||
Self::While(v) => v.eval(vm),
|
||||
Self::For(v) => v.eval(vm),
|
||||
Self::Import(v) => v.eval(vm),
|
||||
Self::Include(v) => v.eval(vm).map(Value::Content),
|
||||
Self::Break(v) => v.eval(vm),
|
||||
Self::Continue(v) => v.eval(vm),
|
||||
Self::Return(v) => v.eval(vm),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ast::Expr {
|
||||
fn eval_in_math(&self, vm: &mut Vm) -> SourceResult<Content> {
|
||||
Ok(match self {
|
||||
Self::Escape(v) => v.eval_in_math(vm)?,
|
||||
Self::Shorthand(v) => v.eval_in_math(vm)?,
|
||||
Self::Symbol(v) => v.eval_in_math(vm)?,
|
||||
Self::Ident(v) => v.eval_in_math(vm)?,
|
||||
_ => self.eval(vm)?.display_in_math(),
|
||||
}
|
||||
.spanned(self.span()))
|
||||
}
|
||||
}
|
||||
|
||||
impl Eval for ast::Space {
|
||||
type Output = Content;
|
||||
|
||||
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
|
||||
Ok(match self {
|
||||
Self::Space(v) => match v.newlines() {
|
||||
0..=1 => (vm.items.space)(),
|
||||
_ => (vm.items.parbreak)(),
|
||||
},
|
||||
Self::Linebreak(v) => v.eval(vm)?,
|
||||
Self::Text(v) => v.eval(vm)?,
|
||||
Self::Escape(v) => (vm.items.text)(v.get().into()),
|
||||
Self::Shorthand(v) => v.eval(vm)?,
|
||||
Self::Symbol(v) => v.eval(vm)?,
|
||||
Self::SmartQuote(v) => v.eval(vm)?,
|
||||
Self::Strong(v) => v.eval(vm)?,
|
||||
Self::Emph(v) => v.eval(vm)?,
|
||||
Self::Link(v) => v.eval(vm)?,
|
||||
Self::Raw(v) => v.eval(vm)?,
|
||||
Self::Heading(v) => v.eval(vm)?,
|
||||
Self::List(v) => v.eval(vm)?,
|
||||
Self::Enum(v) => v.eval(vm)?,
|
||||
Self::Term(v) => v.eval(vm)?,
|
||||
Self::Ref(v) => v.eval(vm)?,
|
||||
Self::Expr(_) => unimplemented!("handled above"),
|
||||
}
|
||||
.spanned(self.span()))
|
||||
Ok(match self.newlines() {
|
||||
0..=1 => (vm.items.space)(),
|
||||
_ => (vm.items.parbreak)(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@ -293,7 +347,7 @@ impl Eval for ast::Linebreak {
|
||||
type Output = Content;
|
||||
|
||||
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
|
||||
Ok((vm.items.linebreak)(false))
|
||||
Ok((vm.items.linebreak)())
|
||||
}
|
||||
}
|
||||
|
||||
@ -305,6 +359,20 @@ impl Eval for ast::Text {
|
||||
}
|
||||
}
|
||||
|
||||
impl Eval for ast::Escape {
|
||||
type Output = Content;
|
||||
|
||||
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
|
||||
Ok((vm.items.text)(self.get().into()))
|
||||
}
|
||||
}
|
||||
|
||||
impl ast::Escape {
|
||||
fn eval_in_math(&self, vm: &mut Vm) -> SourceResult<Content> {
|
||||
Ok((vm.items.math_atom)(self.get().into()))
|
||||
}
|
||||
}
|
||||
|
||||
impl Eval for ast::Shorthand {
|
||||
type Output = Content;
|
||||
|
||||
@ -313,6 +381,12 @@ impl Eval for ast::Shorthand {
|
||||
}
|
||||
}
|
||||
|
||||
impl ast::Shorthand {
|
||||
fn eval_in_math(&self, vm: &mut Vm) -> SourceResult<Content> {
|
||||
Ok((vm.items.math_atom)(self.get().into()))
|
||||
}
|
||||
}
|
||||
|
||||
impl Eval for ast::Symbol {
|
||||
type Output = Content;
|
||||
|
||||
@ -321,6 +395,12 @@ impl Eval for ast::Symbol {
|
||||
}
|
||||
}
|
||||
|
||||
impl ast::Symbol {
|
||||
fn eval_in_math(&self, vm: &mut Vm) -> SourceResult<Content> {
|
||||
Ok((vm.items.symbol)(self.get().clone() + ":op".into()))
|
||||
}
|
||||
}
|
||||
|
||||
impl Eval for ast::SmartQuote {
|
||||
type Output = Content;
|
||||
|
||||
@ -414,48 +494,12 @@ impl Eval for ast::Math {
|
||||
type Output = Content;
|
||||
|
||||
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
|
||||
Ok((vm.items.math)(
|
||||
self.children()
|
||||
.map(|node| node.eval(vm))
|
||||
.collect::<SourceResult<_>>()?,
|
||||
self.block(),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
impl Eval for ast::MathNode {
|
||||
type Output = Content;
|
||||
|
||||
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
|
||||
Ok(match self {
|
||||
Self::Space(_) => (vm.items.space)(),
|
||||
Self::Linebreak(v) => v.eval(vm)?,
|
||||
Self::Escape(v) => (vm.items.math_atom)(v.get().into()),
|
||||
Self::Shorthand(v) => (vm.items.math_atom)(v.get().into()),
|
||||
Self::Atom(v) => v.eval(vm)?,
|
||||
Self::Symbol(v) => (vm.items.symbol)(v.get().clone() + ":op".into()),
|
||||
Self::Script(v) => v.eval(vm)?,
|
||||
Self::Frac(v) => v.eval(vm)?,
|
||||
Self::AlignPoint(v) => v.eval(vm)?,
|
||||
Self::Group(v) => v.eval(vm)?,
|
||||
Self::Expr(v) => {
|
||||
if let ast::Expr::Ident(ident) = v {
|
||||
if self.as_untyped().len() == ident.len()
|
||||
&& matches!(vm.scopes.get(ident), Ok(Value::Func(_)) | Err(_))
|
||||
{
|
||||
let node = (vm.items.symbol)(ident.get().clone() + ":op".into());
|
||||
return Ok(node.spanned(self.span()));
|
||||
}
|
||||
}
|
||||
|
||||
match v.eval(vm)? {
|
||||
Value::Int(v) => (vm.items.math_atom)(format_eco!("{}", v)),
|
||||
Value::Float(v) => (vm.items.math_atom)(format_eco!("{}", v)),
|
||||
v => v.display(),
|
||||
}
|
||||
}
|
||||
}
|
||||
.spanned(self.span()))
|
||||
let seq = self
|
||||
.exprs()
|
||||
.map(|expr| expr.eval_in_math(vm))
|
||||
.collect::<SourceResult<_>>()?;
|
||||
let block = self.block();
|
||||
Ok((vm.items.math)(seq, block))
|
||||
}
|
||||
}
|
||||
|
||||
@ -471,11 +515,10 @@ impl Eval for ast::Script {
|
||||
type Output = Content;
|
||||
|
||||
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
|
||||
Ok((vm.items.math_script)(
|
||||
self.base().eval(vm)?,
|
||||
self.sub().map(|node| node.eval(vm)).transpose()?,
|
||||
self.sup().map(|node| node.eval(vm)).transpose()?,
|
||||
))
|
||||
let base = self.base().eval_in_math(vm)?;
|
||||
let sub = self.sub().map(|expr| expr.eval_in_math(vm)).transpose()?;
|
||||
let sup = self.sup().map(|expr| expr.eval_in_math(vm)).transpose()?;
|
||||
Ok((vm.items.math_script)(base, sub, sup))
|
||||
}
|
||||
}
|
||||
|
||||
@ -483,7 +526,9 @@ impl Eval for ast::Frac {
|
||||
type Output = Content;
|
||||
|
||||
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
|
||||
Ok((vm.items.math_frac)(self.num().eval(vm)?, self.denom().eval(vm)?))
|
||||
let num = self.num().eval_in_math(vm)?;
|
||||
let denom = self.denom().eval_in_math(vm)?;
|
||||
Ok((vm.items.math_frac)(num, denom))
|
||||
}
|
||||
}
|
||||
|
||||
@ -495,47 +540,6 @@ impl Eval for ast::AlignPoint {
|
||||
}
|
||||
}
|
||||
|
||||
impl Eval for ast::Expr {
|
||||
type Output = Value;
|
||||
|
||||
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
|
||||
let forbidden = |name| {
|
||||
error!(
|
||||
self.span(),
|
||||
"{} is only allowed directly in code and content blocks", name
|
||||
)
|
||||
};
|
||||
|
||||
match self {
|
||||
Self::Lit(v) => v.eval(vm),
|
||||
Self::Ident(v) => v.eval(vm),
|
||||
Self::Code(v) => v.eval(vm),
|
||||
Self::Content(v) => v.eval(vm).map(Value::Content),
|
||||
Self::Math(v) => v.eval(vm).map(Value::Content),
|
||||
Self::Array(v) => v.eval(vm).map(Value::Array),
|
||||
Self::Dict(v) => v.eval(vm).map(Value::Dict),
|
||||
Self::Parenthesized(v) => v.eval(vm),
|
||||
Self::FieldAccess(v) => v.eval(vm),
|
||||
Self::FuncCall(v) => v.eval(vm),
|
||||
Self::MethodCall(v) => v.eval(vm),
|
||||
Self::Closure(v) => v.eval(vm),
|
||||
Self::Unary(v) => v.eval(vm),
|
||||
Self::Binary(v) => v.eval(vm),
|
||||
Self::Let(v) => v.eval(vm),
|
||||
Self::Set(_) => bail!(forbidden("set")),
|
||||
Self::Show(_) => bail!(forbidden("show")),
|
||||
Self::Conditional(v) => v.eval(vm),
|
||||
Self::While(v) => v.eval(vm),
|
||||
Self::For(v) => v.eval(vm),
|
||||
Self::Import(v) => v.eval(vm),
|
||||
Self::Include(v) => v.eval(vm).map(Value::Content),
|
||||
Self::Break(v) => v.eval(vm),
|
||||
Self::Continue(v) => v.eval(vm),
|
||||
Self::Return(v) => v.eval(vm),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Eval for ast::Lit {
|
||||
type Output = Value;
|
||||
|
||||
@ -571,6 +575,18 @@ impl Eval for ast::Ident {
|
||||
}
|
||||
}
|
||||
|
||||
impl ast::Ident {
|
||||
fn eval_in_math(&self, vm: &mut Vm) -> SourceResult<Content> {
|
||||
if self.as_untyped().len() == self.len()
|
||||
&& matches!(vm.scopes.get(&self), Ok(Value::Func(_)) | Err(_))
|
||||
{
|
||||
Ok((vm.items.symbol)(self.get().clone() + ":op".into()))
|
||||
} else {
|
||||
Ok(self.eval(vm)?.display_in_math())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Eval for ast::CodeBlock {
|
||||
type Output = Value;
|
||||
|
||||
@ -789,7 +805,11 @@ impl Eval for ast::FieldAccess {
|
||||
.field(&field)
|
||||
.ok_or_else(|| format!("unknown field {field:?}"))
|
||||
.at(span)?,
|
||||
v => bail!(self.target().span(), "cannot access field on {}", v.type_name()),
|
||||
v => bail!(
|
||||
self.target().span(),
|
||||
"expected dictionary or content, found {}",
|
||||
v.type_name()
|
||||
),
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -916,7 +936,7 @@ impl Eval for ast::Closure {
|
||||
}
|
||||
}
|
||||
|
||||
// Define the closure function.
|
||||
// Define the closure.
|
||||
let closure = Closure {
|
||||
location: vm.location,
|
||||
name,
|
||||
@ -966,7 +986,7 @@ impl Eval for ast::ShowRule {
|
||||
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
|
||||
let selector = self
|
||||
.selector()
|
||||
.map(|selector| selector.eval(vm)?.cast::<Selector>().at(selector.span()))
|
||||
.map(|sel| sel.eval(vm)?.cast::<Selector>().at(sel.span()))
|
||||
.transpose()?;
|
||||
|
||||
let transform = self.transform();
|
||||
@ -1094,7 +1114,6 @@ impl Eval for ast::ForLoop {
|
||||
}
|
||||
|
||||
let iter = self.iter().eval(vm)?;
|
||||
|
||||
let pattern = self.pattern();
|
||||
let key = pattern.key().map(ast::Ident::take);
|
||||
let value = pattern.value().take();
|
||||
@ -1266,28 +1285,35 @@ impl Access for ast::Parenthesized {
|
||||
|
||||
impl Access for ast::FieldAccess {
|
||||
fn access<'a>(&self, vm: &'a mut Vm) -> SourceResult<&'a mut Value> {
|
||||
Ok(match self.target().access(vm)? {
|
||||
Value::Dict(dict) => dict.at_mut(self.field().take().into()),
|
||||
v => bail!(
|
||||
let value = self.target().access(vm)?;
|
||||
let Value::Dict(dict) = value else {
|
||||
bail!(
|
||||
self.target().span(),
|
||||
"expected dictionary, found {}",
|
||||
v.type_name(),
|
||||
),
|
||||
})
|
||||
value.type_name(),
|
||||
);
|
||||
};
|
||||
|
||||
Ok(dict.at_mut(self.field().take().into()))
|
||||
}
|
||||
}
|
||||
|
||||
impl Access for ast::MethodCall {
|
||||
fn access<'a>(&self, vm: &'a mut Vm) -> SourceResult<&'a mut Value> {
|
||||
let span = self.span();
|
||||
let method = self.method();
|
||||
let args = self.args().eval(vm)?;
|
||||
if methods::is_accessor(&method) {
|
||||
let value = self.target().access(vm)?;
|
||||
methods::call_access(value, &method, args, span)
|
||||
} else {
|
||||
let method = self.method().take();
|
||||
let world = vm.world();
|
||||
|
||||
if !methods::is_accessor(&method) {
|
||||
let _ = self.eval(vm)?;
|
||||
bail!(span, "cannot mutate a temporary value");
|
||||
}
|
||||
|
||||
let args = self.args().eval(vm)?;
|
||||
let value = self.target().access(vm)?;
|
||||
let result = methods::call_access(value, &method, args, span);
|
||||
|
||||
let point = || Tracepoint::Call(Some(method.clone()));
|
||||
result.trace(world, point, span)
|
||||
}
|
||||
}
|
||||
|
@ -34,7 +34,7 @@ pub struct LangItems {
|
||||
/// Whitespace.
|
||||
pub space: fn() -> Content,
|
||||
/// A forced line break: `\`.
|
||||
pub linebreak: fn(justify: bool) -> Content,
|
||||
pub linebreak: fn() -> Content,
|
||||
/// Plain text without markup.
|
||||
pub text: fn(text: EcoString) -> Content,
|
||||
/// The id of the text node.
|
||||
|
@ -112,6 +112,15 @@ impl Value {
|
||||
_ => item!(raw)(self.repr().into(), Some("typc".into()), false),
|
||||
}
|
||||
}
|
||||
|
||||
/// Return the display representation of the value in math mode.
|
||||
pub fn display_in_math(self) -> Content {
|
||||
match self {
|
||||
Self::Int(v) => item!(math_atom)(format_eco!("{}", v)),
|
||||
Self::Float(v) => item!(math_atom)(format_eco!("{}", v)),
|
||||
_ => self.display(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Value {
|
||||
|
@ -1,6 +1,6 @@
|
||||
//! A typed layer over the untyped syntax tree.
|
||||
//!
|
||||
//! The AST is rooted in the [`MarkupNode`].
|
||||
//! The AST is rooted in the [`Markup`] node.
|
||||
|
||||
use std::num::NonZeroUsize;
|
||||
use std::ops::Deref;
|
||||
@ -54,26 +54,26 @@ node! {
|
||||
}
|
||||
|
||||
impl Markup {
|
||||
/// The children.
|
||||
pub fn children(&self) -> impl DoubleEndedIterator<Item = MarkupNode> + '_ {
|
||||
/// The expressions.
|
||||
pub fn exprs(&self) -> impl DoubleEndedIterator<Item = Expr> + '_ {
|
||||
let mut was_stmt = false;
|
||||
self.0
|
||||
.children()
|
||||
.filter(move |node| {
|
||||
// Ignore linebreak directly after statements without semicolons.
|
||||
// Ignore newline directly after statements without semicolons.
|
||||
let kind = node.kind();
|
||||
let keep =
|
||||
!was_stmt || !matches!(kind, SyntaxKind::Space { newlines: 1 });
|
||||
was_stmt = kind.is_stmt();
|
||||
keep
|
||||
})
|
||||
.filter_map(SyntaxNode::cast)
|
||||
.filter_map(Expr::cast_with_space)
|
||||
}
|
||||
}
|
||||
|
||||
/// A single piece of markup.
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum MarkupNode {
|
||||
/// An expression in markup, math or code.
|
||||
#[derive(Debug, Clone, PartialEq, Hash)]
|
||||
pub enum Expr {
|
||||
/// Whitespace.
|
||||
Space(Space),
|
||||
/// A forced line break: `\`.
|
||||
@ -107,14 +107,78 @@ pub enum MarkupNode {
|
||||
Enum(EnumItem),
|
||||
/// An item in a term list: `/ Term: Details`.
|
||||
Term(TermItem),
|
||||
/// An expression.
|
||||
Expr(Expr),
|
||||
/// A math formula: `$x$`, `$ x^2 $`.
|
||||
Math(Math),
|
||||
/// An atom in a math formula: `x`, `+`, `12`.
|
||||
Atom(Atom),
|
||||
/// A base with optional sub- and superscripts in a math formula: `a_1^2`.
|
||||
Script(Script),
|
||||
/// A fraction in a math formula: `x/2`.
|
||||
Frac(Frac),
|
||||
/// An alignment point in a math formula: `&`, `&&`.
|
||||
AlignPoint(AlignPoint),
|
||||
/// A literal: `1`, `true`, ...
|
||||
Lit(Lit),
|
||||
/// An identifier: `left`.
|
||||
Ident(Ident),
|
||||
/// A code block: `{ let x = 1; x + 2 }`.
|
||||
Code(CodeBlock),
|
||||
/// A content block: `[*Hi* there!]`.
|
||||
Content(ContentBlock),
|
||||
/// A grouped expression: `(1 + 2)`.
|
||||
Parenthesized(Parenthesized),
|
||||
/// An array: `(1, "hi", 12cm)`.
|
||||
Array(Array),
|
||||
/// A dictionary: `(thickness: 3pt, pattern: dashed)`.
|
||||
Dict(Dict),
|
||||
/// A unary operation: `-x`.
|
||||
Unary(Unary),
|
||||
/// A binary operation: `a + b`.
|
||||
Binary(Binary),
|
||||
/// A field access: `properties.age`.
|
||||
FieldAccess(FieldAccess),
|
||||
/// An invocation of a function: `f(x, y)`.
|
||||
FuncCall(FuncCall),
|
||||
/// An invocation of a method: `array.push(v)`.
|
||||
MethodCall(MethodCall),
|
||||
/// A closure: `(x, y) => z`.
|
||||
Closure(Closure),
|
||||
/// A let binding: `let x = 1`.
|
||||
Let(LetBinding),
|
||||
/// A set rule: `set text(...)`.
|
||||
Set(SetRule),
|
||||
/// A show rule: `show heading: it => [*{it.body}*]`.
|
||||
Show(ShowRule),
|
||||
/// An if-else conditional: `if x { y } else { z }`.
|
||||
Conditional(Conditional),
|
||||
/// A while loop: `while x { y }`.
|
||||
While(WhileLoop),
|
||||
/// A for loop: `for x in y { z }`.
|
||||
For(ForLoop),
|
||||
/// A module import: `import a, b, c from "utils.typ"`.
|
||||
Import(ModuleImport),
|
||||
/// A module include: `include "chapter1.typ"`.
|
||||
Include(ModuleInclude),
|
||||
/// A break from a loop: `break`.
|
||||
Break(LoopBreak),
|
||||
/// A continue in a loop: `continue`.
|
||||
Continue(LoopContinue),
|
||||
/// A return from a function: `return`, `return x + 1`.
|
||||
Return(FuncReturn),
|
||||
}
|
||||
|
||||
impl AstNode for MarkupNode {
|
||||
fn from_untyped(node: &SyntaxNode) -> Option<Self> {
|
||||
impl Expr {
|
||||
fn cast_with_space(node: &SyntaxNode) -> Option<Self> {
|
||||
match node.kind() {
|
||||
SyntaxKind::Space { .. } => node.cast().map(Self::Space),
|
||||
_ => Self::from_untyped(node),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AstNode for Expr {
|
||||
fn from_untyped(node: &SyntaxNode) -> Option<Self> {
|
||||
match node.kind() {
|
||||
SyntaxKind::Linebreak => node.cast().map(Self::Linebreak),
|
||||
SyntaxKind::Text(_) => node.cast().map(Self::Text),
|
||||
SyntaxKind::Escape(_) => node.cast().map(Self::Escape),
|
||||
@ -130,7 +194,35 @@ impl AstNode for MarkupNode {
|
||||
SyntaxKind::ListItem => node.cast().map(Self::List),
|
||||
SyntaxKind::EnumItem => node.cast().map(Self::Enum),
|
||||
SyntaxKind::TermItem => node.cast().map(Self::Term),
|
||||
_ => node.cast().map(Self::Expr),
|
||||
SyntaxKind::Math => node.cast().map(Self::Math),
|
||||
SyntaxKind::Atom(_) => node.cast().map(Self::Atom),
|
||||
SyntaxKind::Script => node.cast().map(Self::Script),
|
||||
SyntaxKind::Frac => node.cast().map(Self::Frac),
|
||||
SyntaxKind::AlignPoint => node.cast().map(Self::AlignPoint),
|
||||
SyntaxKind::Ident(_) => node.cast().map(Self::Ident),
|
||||
SyntaxKind::CodeBlock => node.cast().map(Self::Code),
|
||||
SyntaxKind::ContentBlock => node.cast().map(Self::Content),
|
||||
SyntaxKind::Parenthesized => node.cast().map(Self::Parenthesized),
|
||||
SyntaxKind::Array => node.cast().map(Self::Array),
|
||||
SyntaxKind::Dict => node.cast().map(Self::Dict),
|
||||
SyntaxKind::Unary => node.cast().map(Self::Unary),
|
||||
SyntaxKind::Binary => node.cast().map(Self::Binary),
|
||||
SyntaxKind::FieldAccess => node.cast().map(Self::FieldAccess),
|
||||
SyntaxKind::FuncCall => node.cast().map(Self::FuncCall),
|
||||
SyntaxKind::MethodCall => node.cast().map(Self::MethodCall),
|
||||
SyntaxKind::Closure => node.cast().map(Self::Closure),
|
||||
SyntaxKind::LetBinding => node.cast().map(Self::Let),
|
||||
SyntaxKind::SetRule => node.cast().map(Self::Set),
|
||||
SyntaxKind::ShowRule => node.cast().map(Self::Show),
|
||||
SyntaxKind::Conditional => node.cast().map(Self::Conditional),
|
||||
SyntaxKind::WhileLoop => node.cast().map(Self::While),
|
||||
SyntaxKind::ForLoop => node.cast().map(Self::For),
|
||||
SyntaxKind::ModuleImport => node.cast().map(Self::Import),
|
||||
SyntaxKind::ModuleInclude => node.cast().map(Self::Include),
|
||||
SyntaxKind::LoopBreak => node.cast().map(Self::Break),
|
||||
SyntaxKind::LoopContinue => node.cast().map(Self::Continue),
|
||||
SyntaxKind::FuncReturn => node.cast().map(Self::Return),
|
||||
_ => node.cast().map(Self::Lit),
|
||||
}
|
||||
}
|
||||
|
||||
@ -152,11 +244,58 @@ impl AstNode for MarkupNode {
|
||||
Self::List(v) => v.as_untyped(),
|
||||
Self::Enum(v) => v.as_untyped(),
|
||||
Self::Term(v) => v.as_untyped(),
|
||||
Self::Expr(v) => v.as_untyped(),
|
||||
Self::Math(v) => v.as_untyped(),
|
||||
Self::Atom(v) => v.as_untyped(),
|
||||
Self::Script(v) => v.as_untyped(),
|
||||
Self::Frac(v) => v.as_untyped(),
|
||||
Self::AlignPoint(v) => v.as_untyped(),
|
||||
Self::Lit(v) => v.as_untyped(),
|
||||
Self::Code(v) => v.as_untyped(),
|
||||
Self::Content(v) => v.as_untyped(),
|
||||
Self::Ident(v) => v.as_untyped(),
|
||||
Self::Array(v) => v.as_untyped(),
|
||||
Self::Dict(v) => v.as_untyped(),
|
||||
Self::Parenthesized(v) => v.as_untyped(),
|
||||
Self::Unary(v) => v.as_untyped(),
|
||||
Self::Binary(v) => v.as_untyped(),
|
||||
Self::FieldAccess(v) => v.as_untyped(),
|
||||
Self::FuncCall(v) => v.as_untyped(),
|
||||
Self::MethodCall(v) => v.as_untyped(),
|
||||
Self::Closure(v) => v.as_untyped(),
|
||||
Self::Let(v) => v.as_untyped(),
|
||||
Self::Set(v) => v.as_untyped(),
|
||||
Self::Show(v) => v.as_untyped(),
|
||||
Self::Conditional(v) => v.as_untyped(),
|
||||
Self::While(v) => v.as_untyped(),
|
||||
Self::For(v) => v.as_untyped(),
|
||||
Self::Import(v) => v.as_untyped(),
|
||||
Self::Include(v) => v.as_untyped(),
|
||||
Self::Break(v) => v.as_untyped(),
|
||||
Self::Continue(v) => v.as_untyped(),
|
||||
Self::Return(v) => v.as_untyped(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Expr {
|
||||
/// Whether the expression can be shortened in markup with a hashtag.
|
||||
pub fn has_short_form(&self) -> bool {
|
||||
matches!(
|
||||
self,
|
||||
Self::Ident(_)
|
||||
| Self::FuncCall(_)
|
||||
| Self::Let(_)
|
||||
| Self::Set(_)
|
||||
| Self::Show(_)
|
||||
| Self::Conditional(_)
|
||||
| Self::While(_)
|
||||
| Self::For(_)
|
||||
| Self::Import(_)
|
||||
| Self::Include(_)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
node! {
|
||||
/// Whitespace.
|
||||
Space
|
||||
@ -418,78 +557,15 @@ node! {
|
||||
}
|
||||
|
||||
impl Math {
|
||||
/// The children.
|
||||
pub fn children(&self) -> impl DoubleEndedIterator<Item = MathNode> + '_ {
|
||||
self.0.children().filter_map(SyntaxNode::cast)
|
||||
/// The expressions the formula consists of.
|
||||
pub fn exprs(&self) -> impl DoubleEndedIterator<Item = Expr> + '_ {
|
||||
self.0.children().filter_map(Expr::cast_with_space)
|
||||
}
|
||||
|
||||
/// Whether the formula should be displayed as a separate block.
|
||||
pub fn block(&self) -> bool {
|
||||
matches!(self.children().next(), Some(MathNode::Space(_)))
|
||||
&& matches!(self.children().last(), Some(MathNode::Space(_)))
|
||||
}
|
||||
}
|
||||
|
||||
/// A single piece of a math formula.
|
||||
#[derive(Debug, Clone, PartialEq, Hash)]
|
||||
pub enum MathNode {
|
||||
/// Whitespace.
|
||||
Space(Space),
|
||||
/// A forced line break: `\`.
|
||||
Linebreak(Linebreak),
|
||||
/// An escape sequence: `\#`, `\u{1F5FA}`.
|
||||
Escape(Escape),
|
||||
/// A shorthand for a unicode codepoint. For example, `->` for a right
|
||||
/// arrow.
|
||||
Shorthand(Shorthand),
|
||||
/// An atom: `x`, `+`, `12`.
|
||||
Atom(Atom),
|
||||
/// Symbol notation: `:arrow:l:` or `arrow:l`. Notations without any colons
|
||||
/// are parsed as identifier expression and handled during evaluation.
|
||||
Symbol(Symbol),
|
||||
/// A base with optional sub- and superscripts: `a_1^2`.
|
||||
Script(Script),
|
||||
/// A fraction: `x/2`.
|
||||
Frac(Frac),
|
||||
/// An alignment point: `&`, `&&`.
|
||||
AlignPoint(AlignPoint),
|
||||
/// Grouped mathematical material.
|
||||
Group(Math),
|
||||
/// An expression.
|
||||
Expr(Expr),
|
||||
}
|
||||
|
||||
impl AstNode for MathNode {
|
||||
fn from_untyped(node: &SyntaxNode) -> Option<Self> {
|
||||
match node.kind() {
|
||||
SyntaxKind::Space { .. } => node.cast().map(Self::Space),
|
||||
SyntaxKind::Linebreak => node.cast().map(Self::Linebreak),
|
||||
SyntaxKind::Escape(_) => node.cast().map(Self::Escape),
|
||||
SyntaxKind::Shorthand(_) => node.cast().map(Self::Shorthand),
|
||||
SyntaxKind::Atom(_) => node.cast().map(Self::Atom),
|
||||
SyntaxKind::Symbol(_) => node.cast().map(Self::Symbol),
|
||||
SyntaxKind::Script => node.cast().map(Self::Script),
|
||||
SyntaxKind::Frac => node.cast().map(Self::Frac),
|
||||
SyntaxKind::AlignPoint => node.cast().map(Self::AlignPoint),
|
||||
SyntaxKind::Math => node.cast().map(Self::Group),
|
||||
_ => node.cast().map(Self::Expr),
|
||||
}
|
||||
}
|
||||
|
||||
fn as_untyped(&self) -> &SyntaxNode {
|
||||
match self {
|
||||
Self::Space(v) => v.as_untyped(),
|
||||
Self::Linebreak(v) => v.as_untyped(),
|
||||
Self::Escape(v) => v.as_untyped(),
|
||||
Self::Shorthand(v) => v.as_untyped(),
|
||||
Self::Atom(v) => v.as_untyped(),
|
||||
Self::Symbol(v) => v.as_untyped(),
|
||||
Self::Script(v) => v.as_untyped(),
|
||||
Self::Frac(v) => v.as_untyped(),
|
||||
Self::AlignPoint(v) => v.as_untyped(),
|
||||
Self::Group(v) => v.as_untyped(),
|
||||
Self::Expr(v) => v.as_untyped(),
|
||||
}
|
||||
matches!(self.exprs().next(), Some(Expr::Space(_)))
|
||||
&& matches!(self.exprs().last(), Some(Expr::Space(_)))
|
||||
}
|
||||
}
|
||||
|
||||
@ -515,12 +591,12 @@ node! {
|
||||
|
||||
impl Script {
|
||||
/// The base of the script.
|
||||
pub fn base(&self) -> MathNode {
|
||||
pub fn base(&self) -> Expr {
|
||||
self.0.cast_first_child().expect("script node is missing base")
|
||||
}
|
||||
|
||||
/// The subscript.
|
||||
pub fn sub(&self) -> Option<MathNode> {
|
||||
pub fn sub(&self) -> Option<Expr> {
|
||||
self.0
|
||||
.children()
|
||||
.skip_while(|node| !matches!(node.kind(), SyntaxKind::Underscore))
|
||||
@ -529,7 +605,7 @@ impl Script {
|
||||
}
|
||||
|
||||
/// The superscript.
|
||||
pub fn sup(&self) -> Option<MathNode> {
|
||||
pub fn sup(&self) -> Option<Expr> {
|
||||
self.0
|
||||
.children()
|
||||
.skip_while(|node| !matches!(node.kind(), SyntaxKind::Hat))
|
||||
@ -545,12 +621,12 @@ node! {
|
||||
|
||||
impl Frac {
|
||||
/// The numerator.
|
||||
pub fn num(&self) -> MathNode {
|
||||
pub fn num(&self) -> Expr {
|
||||
self.0.cast_first_child().expect("fraction is missing numerator")
|
||||
}
|
||||
|
||||
/// The denominator.
|
||||
pub fn denom(&self) -> MathNode {
|
||||
pub fn denom(&self) -> Expr {
|
||||
self.0.cast_last_child().expect("fraction is missing denominator")
|
||||
}
|
||||
}
|
||||
@ -572,142 +648,6 @@ impl AlignPoint {
|
||||
}
|
||||
}
|
||||
|
||||
/// An expression.
|
||||
#[derive(Debug, Clone, PartialEq, Hash)]
|
||||
pub enum Expr {
|
||||
/// A literal: `1`, `true`, ...
|
||||
Lit(Lit),
|
||||
/// An identifier: `left`.
|
||||
Ident(Ident),
|
||||
/// A code block: `{ let x = 1; x + 2 }`.
|
||||
Code(CodeBlock),
|
||||
/// A content block: `[*Hi* there!]`.
|
||||
Content(ContentBlock),
|
||||
/// A math formula: `$x$`, `$ x^2 $`.
|
||||
Math(Math),
|
||||
/// A grouped expression: `(1 + 2)`.
|
||||
Parenthesized(Parenthesized),
|
||||
/// An array: `(1, "hi", 12cm)`.
|
||||
Array(Array),
|
||||
/// A dictionary: `(thickness: 3pt, pattern: dashed)`.
|
||||
Dict(Dict),
|
||||
/// A unary operation: `-x`.
|
||||
Unary(Unary),
|
||||
/// A binary operation: `a + b`.
|
||||
Binary(Binary),
|
||||
/// A field access: `properties.age`.
|
||||
FieldAccess(FieldAccess),
|
||||
/// An invocation of a function: `f(x, y)`.
|
||||
FuncCall(FuncCall),
|
||||
/// An invocation of a method: `array.push(v)`.
|
||||
MethodCall(MethodCall),
|
||||
/// A closure: `(x, y) => z`.
|
||||
Closure(Closure),
|
||||
/// A let binding: `let x = 1`.
|
||||
Let(LetBinding),
|
||||
/// A set rule: `set text(...)`.
|
||||
Set(SetRule),
|
||||
/// A show rule: `show heading: it => [*{it.body}*]`.
|
||||
Show(ShowRule),
|
||||
/// An if-else conditional: `if x { y } else { z }`.
|
||||
Conditional(Conditional),
|
||||
/// A while loop: `while x { y }`.
|
||||
While(WhileLoop),
|
||||
/// A for loop: `for x in y { z }`.
|
||||
For(ForLoop),
|
||||
/// A module import: `import a, b, c from "utils.typ"`.
|
||||
Import(ModuleImport),
|
||||
/// A module include: `include "chapter1.typ"`.
|
||||
Include(ModuleInclude),
|
||||
/// A break from a loop: `break`.
|
||||
Break(LoopBreak),
|
||||
/// A continue in a loop: `continue`.
|
||||
Continue(LoopContinue),
|
||||
/// A return from a function: `return`, `return x + 1`.
|
||||
Return(FuncReturn),
|
||||
}
|
||||
|
||||
impl AstNode for Expr {
|
||||
fn from_untyped(node: &SyntaxNode) -> Option<Self> {
|
||||
match node.kind() {
|
||||
SyntaxKind::Ident(_) => node.cast().map(Self::Ident),
|
||||
SyntaxKind::CodeBlock => node.cast().map(Self::Code),
|
||||
SyntaxKind::ContentBlock => node.cast().map(Self::Content),
|
||||
SyntaxKind::Math => node.cast().map(Self::Math),
|
||||
SyntaxKind::Parenthesized => node.cast().map(Self::Parenthesized),
|
||||
SyntaxKind::Array => node.cast().map(Self::Array),
|
||||
SyntaxKind::Dict => node.cast().map(Self::Dict),
|
||||
SyntaxKind::Unary => node.cast().map(Self::Unary),
|
||||
SyntaxKind::Binary => node.cast().map(Self::Binary),
|
||||
SyntaxKind::FieldAccess => node.cast().map(Self::FieldAccess),
|
||||
SyntaxKind::FuncCall => node.cast().map(Self::FuncCall),
|
||||
SyntaxKind::MethodCall => node.cast().map(Self::MethodCall),
|
||||
SyntaxKind::Closure => node.cast().map(Self::Closure),
|
||||
SyntaxKind::LetBinding => node.cast().map(Self::Let),
|
||||
SyntaxKind::SetRule => node.cast().map(Self::Set),
|
||||
SyntaxKind::ShowRule => node.cast().map(Self::Show),
|
||||
SyntaxKind::Conditional => node.cast().map(Self::Conditional),
|
||||
SyntaxKind::WhileLoop => node.cast().map(Self::While),
|
||||
SyntaxKind::ForLoop => node.cast().map(Self::For),
|
||||
SyntaxKind::ModuleImport => node.cast().map(Self::Import),
|
||||
SyntaxKind::ModuleInclude => node.cast().map(Self::Include),
|
||||
SyntaxKind::LoopBreak => node.cast().map(Self::Break),
|
||||
SyntaxKind::LoopContinue => node.cast().map(Self::Continue),
|
||||
SyntaxKind::FuncReturn => node.cast().map(Self::Return),
|
||||
_ => node.cast().map(Self::Lit),
|
||||
}
|
||||
}
|
||||
|
||||
fn as_untyped(&self) -> &SyntaxNode {
|
||||
match self {
|
||||
Self::Lit(v) => v.as_untyped(),
|
||||
Self::Code(v) => v.as_untyped(),
|
||||
Self::Content(v) => v.as_untyped(),
|
||||
Self::Math(v) => v.as_untyped(),
|
||||
Self::Ident(v) => v.as_untyped(),
|
||||
Self::Array(v) => v.as_untyped(),
|
||||
Self::Dict(v) => v.as_untyped(),
|
||||
Self::Parenthesized(v) => v.as_untyped(),
|
||||
Self::Unary(v) => v.as_untyped(),
|
||||
Self::Binary(v) => v.as_untyped(),
|
||||
Self::FieldAccess(v) => v.as_untyped(),
|
||||
Self::FuncCall(v) => v.as_untyped(),
|
||||
Self::MethodCall(v) => v.as_untyped(),
|
||||
Self::Closure(v) => v.as_untyped(),
|
||||
Self::Let(v) => v.as_untyped(),
|
||||
Self::Set(v) => v.as_untyped(),
|
||||
Self::Show(v) => v.as_untyped(),
|
||||
Self::Conditional(v) => v.as_untyped(),
|
||||
Self::While(v) => v.as_untyped(),
|
||||
Self::For(v) => v.as_untyped(),
|
||||
Self::Import(v) => v.as_untyped(),
|
||||
Self::Include(v) => v.as_untyped(),
|
||||
Self::Break(v) => v.as_untyped(),
|
||||
Self::Continue(v) => v.as_untyped(),
|
||||
Self::Return(v) => v.as_untyped(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Expr {
|
||||
/// Whether the expression can be shortened in markup with a hashtag.
|
||||
pub fn has_short_form(&self) -> bool {
|
||||
matches!(
|
||||
self,
|
||||
Self::Ident(_)
|
||||
| Self::FuncCall(_)
|
||||
| Self::Let(_)
|
||||
| Self::Set(_)
|
||||
| Self::Show(_)
|
||||
| Self::Conditional(_)
|
||||
| Self::While(_)
|
||||
| Self::For(_)
|
||||
| Self::Import(_)
|
||||
| Self::Include(_)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
node! {
|
||||
/// A literal: `1`, `true`, ...
|
||||
Lit: SyntaxKind::None
|
||||
|
@ -586,11 +586,23 @@ fn expr_prec(p: &mut Parser, atomic: bool, min_prec: usize) -> ParseResult {
|
||||
}
|
||||
|
||||
fn primary(p: &mut Parser, atomic: bool) -> ParseResult {
|
||||
if literal(p) {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
match p.peek() {
|
||||
// Literals and few other things.
|
||||
Some(
|
||||
SyntaxKind::None
|
||||
| SyntaxKind::Auto
|
||||
| SyntaxKind::Int(_)
|
||||
| SyntaxKind::Float(_)
|
||||
| SyntaxKind::Bool(_)
|
||||
| SyntaxKind::Numeric(_, _)
|
||||
| SyntaxKind::Str(_)
|
||||
| SyntaxKind::Label(_)
|
||||
| SyntaxKind::Raw(_),
|
||||
) => {
|
||||
p.eat();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Things that start with an identifier.
|
||||
Some(SyntaxKind::Ident(_)) => {
|
||||
let marker = p.marker();
|
||||
@ -638,25 +650,6 @@ fn primary(p: &mut Parser, atomic: bool) -> ParseResult {
|
||||
}
|
||||
}
|
||||
|
||||
fn literal(p: &mut Parser) -> bool {
|
||||
match p.peek() {
|
||||
Some(
|
||||
SyntaxKind::None
|
||||
| SyntaxKind::Auto
|
||||
| SyntaxKind::Int(_)
|
||||
| SyntaxKind::Float(_)
|
||||
| SyntaxKind::Bool(_)
|
||||
| SyntaxKind::Numeric(_, _)
|
||||
| SyntaxKind::Str(_)
|
||||
| SyntaxKind::Label(_),
|
||||
) => {
|
||||
p.eat();
|
||||
true
|
||||
}
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
fn ident(p: &mut Parser) -> ParseResult {
|
||||
match p.peek() {
|
||||
Some(SyntaxKind::Ident(_)) => {
|
||||
|
@ -523,8 +523,9 @@ impl Tokens<'_> {
|
||||
// Math.
|
||||
'$' => SyntaxKind::Dollar,
|
||||
|
||||
// Labels.
|
||||
// Labels and raw.
|
||||
'<' if self.s.at(is_id_continue) => self.label(),
|
||||
'`' => self.raw(),
|
||||
|
||||
// Two-char operators.
|
||||
'=' if self.s.eat_if('=') => SyntaxKind::EqEq,
|
||||
|
@ -27,7 +27,7 @@
|
||||
{(:).invalid}
|
||||
|
||||
---
|
||||
// Error: 2-7 cannot access field on boolean
|
||||
// Error: 2-7 expected dictionary or content, found boolean
|
||||
{false.ok}
|
||||
|
||||
---
|
||||
|
Loading…
x
Reference in New Issue
Block a user