mirror of
https://github.com/typst/typst
synced 2025-05-14 04:56:26 +08:00
More spans in AST
This commit is contained in:
parent
54b38c4790
commit
4fd031a256
13
src/diag.rs
13
src/diag.rs
@ -9,7 +9,7 @@ use std::string::FromUtf8Error;
|
|||||||
|
|
||||||
use comemo::Tracked;
|
use comemo::Tracked;
|
||||||
|
|
||||||
use crate::syntax::{Span, Spanned};
|
use crate::syntax::{ErrorPos, Span, Spanned};
|
||||||
use crate::util::EcoString;
|
use crate::util::EcoString;
|
||||||
use crate::World;
|
use crate::World;
|
||||||
|
|
||||||
@ -83,17 +83,6 @@ impl SourceError {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Where in a node an error should be annotated,
|
|
||||||
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
|
|
||||||
pub enum ErrorPos {
|
|
||||||
/// Over the full width of the node.
|
|
||||||
Full,
|
|
||||||
/// At the start of the node.
|
|
||||||
Start,
|
|
||||||
/// At the end of the node.
|
|
||||||
End,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A part of an error's [trace](SourceError::trace).
|
/// A part of an error's [trace](SourceError::trace).
|
||||||
#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
|
#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
|
||||||
pub enum Tracepoint {
|
pub enum Tracepoint {
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
use super::{Scope, Scopes, Value};
|
use super::{Scope, Scopes, Value};
|
||||||
use crate::syntax::ast::{ClosureParam, Expr, Ident, Imports, TypedNode};
|
use crate::syntax::ast::TypedNode;
|
||||||
use crate::syntax::SyntaxNode;
|
use crate::syntax::{ast, SyntaxNode};
|
||||||
|
|
||||||
/// A visitor that captures variable slots.
|
/// A visitor that captures variable slots.
|
||||||
pub struct CapturesVisitor<'a> {
|
pub struct CapturesVisitor<'a> {
|
||||||
@ -25,12 +25,12 @@ impl<'a> CapturesVisitor<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Bind a new internal variable.
|
/// Bind a new internal variable.
|
||||||
pub fn bind(&mut self, ident: Ident) {
|
pub fn bind(&mut self, ident: ast::Ident) {
|
||||||
self.internal.top.define(ident.take(), Value::None);
|
self.internal.top.define(ident.take(), Value::None);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Capture a variable if it isn't internal.
|
/// Capture a variable if it isn't internal.
|
||||||
pub fn capture(&mut self, ident: Ident) {
|
pub fn capture(&mut self, ident: ast::Ident) {
|
||||||
if self.internal.get(&ident).is_err() {
|
if self.internal.get(&ident).is_err() {
|
||||||
if let Ok(value) = self.external.get(&ident) {
|
if let Ok(value) = self.external.get(&ident) {
|
||||||
self.captures.define_captured(ident.take(), value.clone());
|
self.captures.define_captured(ident.take(), value.clone());
|
||||||
@ -45,10 +45,10 @@ impl<'a> CapturesVisitor<'a> {
|
|||||||
// Identifiers that shouldn't count as captures because they
|
// Identifiers that shouldn't count as captures because they
|
||||||
// actually bind a new name are handled below (individually through
|
// actually bind a new name are handled below (individually through
|
||||||
// the expressions that contain them).
|
// the expressions that contain them).
|
||||||
Some(Expr::Ident(ident)) => self.capture(ident),
|
Some(ast::Expr::Ident(ident)) => self.capture(ident),
|
||||||
|
|
||||||
// Code and content blocks create a scope.
|
// Code and content blocks create a scope.
|
||||||
Some(Expr::Code(_) | Expr::Content(_)) => {
|
Some(ast::Expr::Code(_) | ast::Expr::Content(_)) => {
|
||||||
self.internal.enter();
|
self.internal.enter();
|
||||||
for child in node.children() {
|
for child in node.children() {
|
||||||
self.visit(child);
|
self.visit(child);
|
||||||
@ -59,18 +59,18 @@ impl<'a> CapturesVisitor<'a> {
|
|||||||
// A closure contains parameter bindings, which are bound before the
|
// A closure contains parameter bindings, which are bound before the
|
||||||
// body is evaluated. Care must be taken so that the default values
|
// body is evaluated. Care must be taken so that the default values
|
||||||
// of named parameters cannot access previous parameter bindings.
|
// of named parameters cannot access previous parameter bindings.
|
||||||
Some(Expr::Closure(expr)) => {
|
Some(ast::Expr::Closure(expr)) => {
|
||||||
for param in expr.params() {
|
for param in expr.params() {
|
||||||
if let ClosureParam::Named(named) = param {
|
if let ast::Param::Named(named) = param {
|
||||||
self.visit(named.expr().as_untyped());
|
self.visit(named.expr().as_untyped());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for param in expr.params() {
|
for param in expr.params() {
|
||||||
match param {
|
match param {
|
||||||
ClosureParam::Pos(ident) => self.bind(ident),
|
ast::Param::Pos(ident) => self.bind(ident),
|
||||||
ClosureParam::Named(named) => self.bind(named.name()),
|
ast::Param::Named(named) => self.bind(named.name()),
|
||||||
ClosureParam::Sink(ident) => self.bind(ident),
|
ast::Param::Sink(ident) => self.bind(ident),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -79,7 +79,7 @@ impl<'a> CapturesVisitor<'a> {
|
|||||||
|
|
||||||
// A let expression contains a binding, but that binding is only
|
// A let expression contains a binding, but that binding is only
|
||||||
// active after the body is evaluated.
|
// active after the body is evaluated.
|
||||||
Some(Expr::Let(expr)) => {
|
Some(ast::Expr::Let(expr)) => {
|
||||||
if let Some(init) = expr.init() {
|
if let Some(init) = expr.init() {
|
||||||
self.visit(init.as_untyped());
|
self.visit(init.as_untyped());
|
||||||
}
|
}
|
||||||
@ -88,7 +88,7 @@ impl<'a> CapturesVisitor<'a> {
|
|||||||
|
|
||||||
// A show rule contains a binding, but that binding is only active
|
// A show rule contains a binding, but that binding is only active
|
||||||
// after the target has been evaluated.
|
// after the target has been evaluated.
|
||||||
Some(Expr::Show(show)) => {
|
Some(ast::Expr::Show(show)) => {
|
||||||
self.visit(show.pattern().as_untyped());
|
self.visit(show.pattern().as_untyped());
|
||||||
if let Some(binding) = show.binding() {
|
if let Some(binding) = show.binding() {
|
||||||
self.bind(binding);
|
self.bind(binding);
|
||||||
@ -99,7 +99,7 @@ impl<'a> CapturesVisitor<'a> {
|
|||||||
// A for loop contains one or two bindings in its pattern. These are
|
// A for loop contains one or two bindings in its pattern. These are
|
||||||
// active after the iterable is evaluated but before the body is
|
// active after the iterable is evaluated but before the body is
|
||||||
// evaluated.
|
// evaluated.
|
||||||
Some(Expr::For(expr)) => {
|
Some(ast::Expr::For(expr)) => {
|
||||||
self.visit(expr.iter().as_untyped());
|
self.visit(expr.iter().as_untyped());
|
||||||
let pattern = expr.pattern();
|
let pattern = expr.pattern();
|
||||||
if let Some(key) = pattern.key() {
|
if let Some(key) = pattern.key() {
|
||||||
@ -111,9 +111,9 @@ impl<'a> CapturesVisitor<'a> {
|
|||||||
|
|
||||||
// An import contains items, but these are active only after the
|
// An import contains items, but these are active only after the
|
||||||
// path is evaluated.
|
// path is evaluated.
|
||||||
Some(Expr::Import(expr)) => {
|
Some(ast::Expr::Import(expr)) => {
|
||||||
self.visit(expr.path().as_untyped());
|
self.visit(expr.path().as_untyped());
|
||||||
if let Imports::Items(items) = expr.imports() {
|
if let ast::Imports::Items(items) = expr.imports() {
|
||||||
for item in items {
|
for item in items {
|
||||||
self.bind(item);
|
self.bind(item);
|
||||||
}
|
}
|
||||||
|
369
src/eval/mod.rs
369
src/eval/mod.rs
@ -43,8 +43,8 @@ use crate::geom::{Angle, Em, Fraction, Length, Ratio};
|
|||||||
use crate::library;
|
use crate::library;
|
||||||
use crate::model::{Content, Pattern, Recipe, StyleEntry, StyleMap};
|
use crate::model::{Content, Pattern, Recipe, StyleEntry, StyleMap};
|
||||||
use crate::source::SourceId;
|
use crate::source::SourceId;
|
||||||
use crate::syntax::ast::*;
|
use crate::syntax::ast::TypedNode;
|
||||||
use crate::syntax::{Span, Spanned};
|
use crate::syntax::{ast, Span, Spanned, Unit};
|
||||||
use crate::util::EcoString;
|
use crate::util::EcoString;
|
||||||
use crate::World;
|
use crate::World;
|
||||||
|
|
||||||
@ -133,25 +133,25 @@ pub trait Eval {
|
|||||||
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output>;
|
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output>;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Eval for MarkupNode {
|
impl Eval for ast::Markup {
|
||||||
type Output = Content;
|
type Output = Content;
|
||||||
|
|
||||||
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
|
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
|
||||||
eval_markup(vm, &mut self.items())
|
eval_markup(vm, &mut self.children())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Evaluate a stream of markup nodes.
|
/// Evaluate a stream of markup nodes.
|
||||||
fn eval_markup(
|
fn eval_markup(
|
||||||
vm: &mut Vm,
|
vm: &mut Vm,
|
||||||
nodes: &mut impl Iterator<Item = MarkupItem>,
|
nodes: &mut impl Iterator<Item = ast::MarkupNode>,
|
||||||
) -> SourceResult<Content> {
|
) -> SourceResult<Content> {
|
||||||
let flow = vm.flow.take();
|
let flow = vm.flow.take();
|
||||||
let mut seq = Vec::with_capacity(nodes.size_hint().1.unwrap_or_default());
|
let mut seq = Vec::with_capacity(nodes.size_hint().1.unwrap_or_default());
|
||||||
|
|
||||||
while let Some(node) = nodes.next() {
|
while let Some(node) = nodes.next() {
|
||||||
seq.push(match node {
|
seq.push(match node {
|
||||||
MarkupItem::Expr(Expr::Set(set)) => {
|
ast::MarkupNode::Expr(ast::Expr::Set(set)) => {
|
||||||
let styles = set.eval(vm)?;
|
let styles = set.eval(vm)?;
|
||||||
if vm.flow.is_some() {
|
if vm.flow.is_some() {
|
||||||
break;
|
break;
|
||||||
@ -159,7 +159,7 @@ fn eval_markup(
|
|||||||
|
|
||||||
eval_markup(vm, nodes)?.styled_with_map(styles)
|
eval_markup(vm, nodes)?.styled_with_map(styles)
|
||||||
}
|
}
|
||||||
MarkupItem::Expr(Expr::Show(show)) => {
|
ast::MarkupNode::Expr(ast::Expr::Show(show)) => {
|
||||||
let recipe = show.eval(vm)?;
|
let recipe = show.eval(vm)?;
|
||||||
if vm.flow.is_some() {
|
if vm.flow.is_some() {
|
||||||
break;
|
break;
|
||||||
@ -168,7 +168,7 @@ fn eval_markup(
|
|||||||
eval_markup(vm, nodes)?
|
eval_markup(vm, nodes)?
|
||||||
.styled_with_entry(StyleEntry::Recipe(recipe).into())
|
.styled_with_entry(StyleEntry::Recipe(recipe).into())
|
||||||
}
|
}
|
||||||
MarkupItem::Expr(Expr::Wrap(wrap)) => {
|
ast::MarkupNode::Expr(ast::Expr::Wrap(wrap)) => {
|
||||||
let tail = eval_markup(vm, nodes)?;
|
let tail = eval_markup(vm, nodes)?;
|
||||||
vm.scopes.top.define(wrap.binding().take(), tail);
|
vm.scopes.top.define(wrap.binding().take(), tail);
|
||||||
wrap.body().eval(vm)?.display()
|
wrap.body().eval(vm)?.display()
|
||||||
@ -189,35 +189,86 @@ fn eval_markup(
|
|||||||
Ok(Content::sequence(seq))
|
Ok(Content::sequence(seq))
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Eval for MarkupItem {
|
impl Eval for ast::MarkupNode {
|
||||||
type Output = Content;
|
type Output = Content;
|
||||||
|
|
||||||
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
|
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
|
||||||
Ok(match self {
|
match self {
|
||||||
Self::Space => Content::Space,
|
Self::Space(v) => v.eval(vm),
|
||||||
Self::Parbreak => Content::Parbreak,
|
Self::Linebreak(v) => v.eval(vm),
|
||||||
&Self::Linebreak => Content::Linebreak { justify: false },
|
Self::Text(v) => v.eval(vm),
|
||||||
Self::Text(text) => Content::Text(text.clone()),
|
Self::Escape(v) => v.eval(vm),
|
||||||
&Self::Quote { double } => Content::Quote { double },
|
Self::Shorthand(v) => v.eval(vm),
|
||||||
Self::Strong(strong) => strong.eval(vm)?,
|
Self::SmartQuote(v) => v.eval(vm),
|
||||||
Self::Emph(emph) => emph.eval(vm)?,
|
Self::Strong(v) => v.eval(vm),
|
||||||
Self::Link(url) => {
|
Self::Emph(v) => v.eval(vm),
|
||||||
Content::show(library::text::LinkNode::from_url(url.clone()))
|
Self::Link(v) => v.eval(vm),
|
||||||
|
Self::Raw(v) => v.eval(vm),
|
||||||
|
Self::Math(v) => v.eval(vm),
|
||||||
|
Self::Heading(v) => v.eval(vm),
|
||||||
|
Self::List(v) => v.eval(vm),
|
||||||
|
Self::Enum(v) => v.eval(vm),
|
||||||
|
Self::Desc(v) => v.eval(vm),
|
||||||
|
Self::Label(v) => v.eval(vm),
|
||||||
|
Self::Ref(v) => v.eval(vm),
|
||||||
|
Self::Expr(v) => v.eval(vm).map(Value::display),
|
||||||
}
|
}
|
||||||
Self::Raw(raw) => raw.eval(vm)?,
|
}
|
||||||
Self::Math(math) => math.eval(vm)?,
|
}
|
||||||
Self::Heading(heading) => heading.eval(vm)?,
|
|
||||||
Self::List(list) => list.eval(vm)?,
|
impl Eval for ast::Space {
|
||||||
Self::Enum(enum_) => enum_.eval(vm)?,
|
type Output = Content;
|
||||||
Self::Desc(desc) => desc.eval(vm)?,
|
|
||||||
Self::Label(_) => Content::Empty,
|
fn eval(&self, _: &mut Vm) -> SourceResult<Self::Output> {
|
||||||
Self::Ref(label) => Content::show(library::structure::RefNode(label.clone())),
|
Ok(if self.newlines() < 2 {
|
||||||
Self::Expr(expr) => expr.eval(vm)?.display(),
|
Content::Space
|
||||||
|
} else {
|
||||||
|
Content::Parbreak
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Eval for StrongNode {
|
impl Eval for ast::Linebreak {
|
||||||
|
type Output = Content;
|
||||||
|
|
||||||
|
fn eval(&self, _: &mut Vm) -> SourceResult<Self::Output> {
|
||||||
|
Ok(Content::Linebreak { justify: false })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Eval for ast::Text {
|
||||||
|
type Output = Content;
|
||||||
|
|
||||||
|
fn eval(&self, _: &mut Vm) -> SourceResult<Self::Output> {
|
||||||
|
Ok(Content::Text(self.get().clone()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Eval for ast::Escape {
|
||||||
|
type Output = Content;
|
||||||
|
|
||||||
|
fn eval(&self, _: &mut Vm) -> SourceResult<Self::Output> {
|
||||||
|
Ok(Content::Text(self.get().into()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Eval for ast::Shorthand {
|
||||||
|
type Output = Content;
|
||||||
|
|
||||||
|
fn eval(&self, _: &mut Vm) -> SourceResult<Self::Output> {
|
||||||
|
Ok(Content::Text(self.get().into()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Eval for ast::SmartQuote {
|
||||||
|
type Output = Content;
|
||||||
|
|
||||||
|
fn eval(&self, _: &mut Vm) -> SourceResult<Self::Output> {
|
||||||
|
Ok(Content::Quote { double: self.double() })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Eval for ast::Strong {
|
||||||
type Output = Content;
|
type Output = Content;
|
||||||
|
|
||||||
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
|
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
|
||||||
@ -227,7 +278,7 @@ impl Eval for StrongNode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Eval for EmphNode {
|
impl Eval for ast::Emph {
|
||||||
type Output = Content;
|
type Output = Content;
|
||||||
|
|
||||||
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
|
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
|
||||||
@ -237,27 +288,39 @@ impl Eval for EmphNode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Eval for RawNode {
|
impl Eval for ast::Link {
|
||||||
|
type Output = Content;
|
||||||
|
|
||||||
|
fn eval(&self, _: &mut Vm) -> SourceResult<Self::Output> {
|
||||||
|
Ok(Content::show(library::text::LinkNode::from_url(
|
||||||
|
self.url().clone(),
|
||||||
|
)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Eval for ast::Raw {
|
||||||
type Output = Content;
|
type Output = Content;
|
||||||
|
|
||||||
fn eval(&self, _: &mut Vm) -> SourceResult<Self::Output> {
|
fn eval(&self, _: &mut Vm) -> SourceResult<Self::Output> {
|
||||||
let content = Content::show(library::text::RawNode {
|
let content = Content::show(library::text::RawNode {
|
||||||
text: self.text.clone(),
|
text: self.text().clone(),
|
||||||
block: self.block,
|
block: self.block(),
|
||||||
});
|
});
|
||||||
Ok(match self.lang {
|
Ok(match self.lang() {
|
||||||
Some(_) => content.styled(library::text::RawNode::LANG, self.lang.clone()),
|
Some(_) => content.styled(library::text::RawNode::LANG, self.lang().cloned()),
|
||||||
None => content,
|
None => content,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Eval for MathNode {
|
impl Eval for ast::Math {
|
||||||
type Output = Content;
|
type Output = Content;
|
||||||
|
|
||||||
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
|
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
|
||||||
let nodes =
|
let nodes = self
|
||||||
self.items().map(|node| node.eval(vm)).collect::<SourceResult<_>>()?;
|
.children()
|
||||||
|
.map(|node| node.eval(vm))
|
||||||
|
.collect::<SourceResult<_>>()?;
|
||||||
Ok(Content::show(library::math::MathNode::Row(
|
Ok(Content::show(library::math::MathNode::Row(
|
||||||
Arc::new(nodes),
|
Arc::new(nodes),
|
||||||
self.span(),
|
self.span(),
|
||||||
@ -265,20 +328,21 @@ impl Eval for MathNode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Eval for MathItem {
|
impl Eval for ast::MathNode {
|
||||||
type Output = library::math::MathNode;
|
type Output = library::math::MathNode;
|
||||||
|
|
||||||
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
|
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
|
||||||
Ok(match self {
|
Ok(match self {
|
||||||
Self::Space => library::math::MathNode::Space,
|
Self::Space(_) => library::math::MathNode::Space,
|
||||||
Self::Linebreak => library::math::MathNode::Linebreak,
|
Self::Linebreak(_) => library::math::MathNode::Linebreak,
|
||||||
Self::Atom(atom) => library::math::MathNode::Atom(atom.clone()),
|
Self::Escape(c) => library::math::MathNode::Atom(c.get().into()),
|
||||||
|
Self::Atom(atom) => library::math::MathNode::Atom(atom.get().clone()),
|
||||||
Self::Script(node) => node.eval(vm)?,
|
Self::Script(node) => node.eval(vm)?,
|
||||||
Self::Frac(node) => node.eval(vm)?,
|
Self::Frac(node) => node.eval(vm)?,
|
||||||
Self::Align(node) => node.eval(vm)?,
|
Self::Align(node) => node.eval(vm)?,
|
||||||
Self::Group(node) => library::math::MathNode::Row(
|
Self::Group(node) => library::math::MathNode::Row(
|
||||||
Arc::new(
|
Arc::new(
|
||||||
node.items()
|
node.children()
|
||||||
.map(|node| node.eval(vm))
|
.map(|node| node.eval(vm))
|
||||||
.collect::<SourceResult<_>>()?,
|
.collect::<SourceResult<_>>()?,
|
||||||
),
|
),
|
||||||
@ -292,7 +356,7 @@ impl Eval for MathItem {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Eval for ScriptNode {
|
impl Eval for ast::Script {
|
||||||
type Output = library::math::MathNode;
|
type Output = library::math::MathNode;
|
||||||
|
|
||||||
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
|
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
|
||||||
@ -314,7 +378,7 @@ impl Eval for ScriptNode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Eval for FracNode {
|
impl Eval for ast::Frac {
|
||||||
type Output = library::math::MathNode;
|
type Output = library::math::MathNode;
|
||||||
|
|
||||||
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
|
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
|
||||||
@ -327,7 +391,7 @@ impl Eval for FracNode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Eval for AlignNode {
|
impl Eval for ast::Align {
|
||||||
type Output = library::math::MathNode;
|
type Output = library::math::MathNode;
|
||||||
|
|
||||||
fn eval(&self, _: &mut Vm) -> SourceResult<Self::Output> {
|
fn eval(&self, _: &mut Vm) -> SourceResult<Self::Output> {
|
||||||
@ -335,7 +399,7 @@ impl Eval for AlignNode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Eval for HeadingNode {
|
impl Eval for ast::Heading {
|
||||||
type Output = Content;
|
type Output = Content;
|
||||||
|
|
||||||
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
|
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
|
||||||
@ -346,7 +410,7 @@ impl Eval for HeadingNode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Eval for ListItem {
|
impl Eval for ast::ListItem {
|
||||||
type Output = Content;
|
type Output = Content;
|
||||||
|
|
||||||
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
|
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
|
||||||
@ -355,7 +419,7 @@ impl Eval for ListItem {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Eval for EnumItem {
|
impl Eval for ast::EnumItem {
|
||||||
type Output = Content;
|
type Output = Content;
|
||||||
|
|
||||||
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
|
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
|
||||||
@ -367,7 +431,7 @@ impl Eval for EnumItem {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Eval for DescItem {
|
impl Eval for ast::DescItem {
|
||||||
type Output = Content;
|
type Output = Content;
|
||||||
|
|
||||||
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
|
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
|
||||||
@ -379,7 +443,25 @@ impl Eval for DescItem {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Eval for Expr {
|
impl Eval for ast::Label {
|
||||||
|
type Output = Content;
|
||||||
|
|
||||||
|
fn eval(&self, _: &mut Vm) -> SourceResult<Self::Output> {
|
||||||
|
Ok(Content::Empty)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Eval for ast::Ref {
|
||||||
|
type Output = Content;
|
||||||
|
|
||||||
|
fn eval(&self, _: &mut Vm) -> SourceResult<Self::Output> {
|
||||||
|
Ok(Content::show(library::structure::RefNode(
|
||||||
|
self.get().clone(),
|
||||||
|
)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Eval for ast::Expr {
|
||||||
type Output = Value;
|
type Output = Value;
|
||||||
|
|
||||||
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
|
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
|
||||||
@ -397,7 +479,7 @@ impl Eval for Expr {
|
|||||||
Self::Content(v) => v.eval(vm).map(Value::Content),
|
Self::Content(v) => v.eval(vm).map(Value::Content),
|
||||||
Self::Array(v) => v.eval(vm).map(Value::Array),
|
Self::Array(v) => v.eval(vm).map(Value::Array),
|
||||||
Self::Dict(v) => v.eval(vm).map(Value::Dict),
|
Self::Dict(v) => v.eval(vm).map(Value::Dict),
|
||||||
Self::Group(v) => v.eval(vm),
|
Self::Parenthesized(v) => v.eval(vm),
|
||||||
Self::FieldAccess(v) => v.eval(vm),
|
Self::FieldAccess(v) => v.eval(vm),
|
||||||
Self::FuncCall(v) => v.eval(vm),
|
Self::FuncCall(v) => v.eval(vm),
|
||||||
Self::MethodCall(v) => v.eval(vm),
|
Self::MethodCall(v) => v.eval(vm),
|
||||||
@ -408,7 +490,7 @@ impl Eval for Expr {
|
|||||||
Self::Set(_) => bail!(forbidden("set")),
|
Self::Set(_) => bail!(forbidden("set")),
|
||||||
Self::Show(_) => bail!(forbidden("show")),
|
Self::Show(_) => bail!(forbidden("show")),
|
||||||
Self::Wrap(_) => bail!(forbidden("wrap")),
|
Self::Wrap(_) => bail!(forbidden("wrap")),
|
||||||
Self::If(v) => v.eval(vm),
|
Self::Conditional(v) => v.eval(vm),
|
||||||
Self::While(v) => v.eval(vm),
|
Self::While(v) => v.eval(vm),
|
||||||
Self::For(v) => v.eval(vm),
|
Self::For(v) => v.eval(vm),
|
||||||
Self::Import(v) => v.eval(vm),
|
Self::Import(v) => v.eval(vm),
|
||||||
@ -420,29 +502,29 @@ impl Eval for Expr {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Eval for Lit {
|
impl Eval for ast::Lit {
|
||||||
type Output = Value;
|
type Output = Value;
|
||||||
|
|
||||||
fn eval(&self, _: &mut Vm) -> SourceResult<Self::Output> {
|
fn eval(&self, _: &mut Vm) -> SourceResult<Self::Output> {
|
||||||
Ok(match self.kind() {
|
Ok(match self.kind() {
|
||||||
LitKind::None => Value::None,
|
ast::LitKind::None => Value::None,
|
||||||
LitKind::Auto => Value::Auto,
|
ast::LitKind::Auto => Value::Auto,
|
||||||
LitKind::Bool(v) => Value::Bool(v),
|
ast::LitKind::Bool(v) => Value::Bool(v),
|
||||||
LitKind::Int(v) => Value::Int(v),
|
ast::LitKind::Int(v) => Value::Int(v),
|
||||||
LitKind::Float(v) => Value::Float(v),
|
ast::LitKind::Float(v) => Value::Float(v),
|
||||||
LitKind::Numeric(v, unit) => match unit {
|
ast::LitKind::Numeric(v, unit) => match unit {
|
||||||
Unit::Length(unit) => Length::with_unit(v, unit).into(),
|
Unit::Length(unit) => Length::with_unit(v, unit).into(),
|
||||||
Unit::Angle(unit) => Angle::with_unit(v, unit).into(),
|
Unit::Angle(unit) => Angle::with_unit(v, unit).into(),
|
||||||
Unit::Em => Em::new(v).into(),
|
Unit::Em => Em::new(v).into(),
|
||||||
Unit::Fr => Fraction::new(v).into(),
|
Unit::Fr => Fraction::new(v).into(),
|
||||||
Unit::Percent => Ratio::new(v / 100.0).into(),
|
Unit::Percent => Ratio::new(v / 100.0).into(),
|
||||||
},
|
},
|
||||||
LitKind::Str(v) => Value::Str(v.into()),
|
ast::LitKind::Str(v) => Value::Str(v.into()),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Eval for Ident {
|
impl Eval for ast::Ident {
|
||||||
type Output = Value;
|
type Output = Value;
|
||||||
|
|
||||||
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
|
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
|
||||||
@ -450,7 +532,7 @@ impl Eval for Ident {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Eval for CodeBlock {
|
impl Eval for ast::CodeBlock {
|
||||||
type Output = Value;
|
type Output = Value;
|
||||||
|
|
||||||
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
|
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
|
||||||
@ -462,14 +544,17 @@ impl Eval for CodeBlock {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Evaluate a stream of expressions.
|
/// Evaluate a stream of expressions.
|
||||||
fn eval_code(vm: &mut Vm, exprs: &mut impl Iterator<Item = Expr>) -> SourceResult<Value> {
|
fn eval_code(
|
||||||
|
vm: &mut Vm,
|
||||||
|
exprs: &mut impl Iterator<Item = ast::Expr>,
|
||||||
|
) -> SourceResult<Value> {
|
||||||
let flow = vm.flow.take();
|
let flow = vm.flow.take();
|
||||||
let mut output = Value::None;
|
let mut output = Value::None;
|
||||||
|
|
||||||
while let Some(expr) = exprs.next() {
|
while let Some(expr) = exprs.next() {
|
||||||
let span = expr.span();
|
let span = expr.span();
|
||||||
let value = match expr {
|
let value = match expr {
|
||||||
Expr::Set(set) => {
|
ast::Expr::Set(set) => {
|
||||||
let styles = set.eval(vm)?;
|
let styles = set.eval(vm)?;
|
||||||
if vm.flow.is_some() {
|
if vm.flow.is_some() {
|
||||||
break;
|
break;
|
||||||
@ -478,7 +563,7 @@ fn eval_code(vm: &mut Vm, exprs: &mut impl Iterator<Item = Expr>) -> SourceResul
|
|||||||
let tail = eval_code(vm, exprs)?.display();
|
let tail = eval_code(vm, exprs)?.display();
|
||||||
Value::Content(tail.styled_with_map(styles))
|
Value::Content(tail.styled_with_map(styles))
|
||||||
}
|
}
|
||||||
Expr::Show(show) => {
|
ast::Expr::Show(show) => {
|
||||||
let recipe = show.eval(vm)?;
|
let recipe = show.eval(vm)?;
|
||||||
let entry = StyleEntry::Recipe(recipe).into();
|
let entry = StyleEntry::Recipe(recipe).into();
|
||||||
if vm.flow.is_some() {
|
if vm.flow.is_some() {
|
||||||
@ -488,7 +573,7 @@ fn eval_code(vm: &mut Vm, exprs: &mut impl Iterator<Item = Expr>) -> SourceResul
|
|||||||
let tail = eval_code(vm, exprs)?.display();
|
let tail = eval_code(vm, exprs)?.display();
|
||||||
Value::Content(tail.styled_with_entry(entry))
|
Value::Content(tail.styled_with_entry(entry))
|
||||||
}
|
}
|
||||||
Expr::Wrap(wrap) => {
|
ast::Expr::Wrap(wrap) => {
|
||||||
let tail = eval_code(vm, exprs)?;
|
let tail = eval_code(vm, exprs)?;
|
||||||
vm.scopes.top.define(wrap.binding().take(), tail);
|
vm.scopes.top.define(wrap.binding().take(), tail);
|
||||||
wrap.body().eval(vm)?
|
wrap.body().eval(vm)?
|
||||||
@ -511,7 +596,7 @@ fn eval_code(vm: &mut Vm, exprs: &mut impl Iterator<Item = Expr>) -> SourceResul
|
|||||||
Ok(output)
|
Ok(output)
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Eval for ContentBlock {
|
impl Eval for ast::ContentBlock {
|
||||||
type Output = Content;
|
type Output = Content;
|
||||||
|
|
||||||
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
|
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
|
||||||
@ -522,7 +607,7 @@ impl Eval for ContentBlock {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Eval for GroupExpr {
|
impl Eval for ast::Parenthesized {
|
||||||
type Output = Value;
|
type Output = Value;
|
||||||
|
|
||||||
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
|
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
|
||||||
@ -530,7 +615,7 @@ impl Eval for GroupExpr {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Eval for ArrayExpr {
|
impl Eval for ast::Array {
|
||||||
type Output = Array;
|
type Output = Array;
|
||||||
|
|
||||||
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
|
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
|
||||||
@ -539,8 +624,8 @@ impl Eval for ArrayExpr {
|
|||||||
let mut vec = Vec::with_capacity(items.size_hint().0);
|
let mut vec = Vec::with_capacity(items.size_hint().0);
|
||||||
for item in items {
|
for item in items {
|
||||||
match item {
|
match item {
|
||||||
ArrayItem::Pos(expr) => vec.push(expr.eval(vm)?),
|
ast::ArrayItem::Pos(expr) => vec.push(expr.eval(vm)?),
|
||||||
ArrayItem::Spread(expr) => match expr.eval(vm)? {
|
ast::ArrayItem::Spread(expr) => match expr.eval(vm)? {
|
||||||
Value::None => {}
|
Value::None => {}
|
||||||
Value::Array(array) => vec.extend(array.into_iter()),
|
Value::Array(array) => vec.extend(array.into_iter()),
|
||||||
v => bail!(expr.span(), "cannot spread {} into array", v.type_name()),
|
v => bail!(expr.span(), "cannot spread {} into array", v.type_name()),
|
||||||
@ -552,7 +637,7 @@ impl Eval for ArrayExpr {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Eval for DictExpr {
|
impl Eval for ast::Dict {
|
||||||
type Output = Dict;
|
type Output = Dict;
|
||||||
|
|
||||||
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
|
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
|
||||||
@ -560,13 +645,13 @@ impl Eval for DictExpr {
|
|||||||
|
|
||||||
for item in self.items() {
|
for item in self.items() {
|
||||||
match item {
|
match item {
|
||||||
DictItem::Named(named) => {
|
ast::DictItem::Named(named) => {
|
||||||
map.insert(named.name().take().into(), named.expr().eval(vm)?);
|
map.insert(named.name().take().into(), named.expr().eval(vm)?);
|
||||||
}
|
}
|
||||||
DictItem::Keyed(keyed) => {
|
ast::DictItem::Keyed(keyed) => {
|
||||||
map.insert(keyed.key().into(), keyed.expr().eval(vm)?);
|
map.insert(keyed.key().into(), keyed.expr().eval(vm)?);
|
||||||
}
|
}
|
||||||
DictItem::Spread(expr) => match expr.eval(vm)? {
|
ast::DictItem::Spread(expr) => match expr.eval(vm)? {
|
||||||
Value::None => {}
|
Value::None => {}
|
||||||
Value::Dict(dict) => map.extend(dict.into_iter()),
|
Value::Dict(dict) => map.extend(dict.into_iter()),
|
||||||
v => bail!(
|
v => bail!(
|
||||||
@ -582,49 +667,49 @@ impl Eval for DictExpr {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Eval for UnaryExpr {
|
impl Eval for ast::Unary {
|
||||||
type Output = Value;
|
type Output = Value;
|
||||||
|
|
||||||
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
|
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
|
||||||
let value = self.expr().eval(vm)?;
|
let value = self.expr().eval(vm)?;
|
||||||
let result = match self.op() {
|
let result = match self.op() {
|
||||||
UnOp::Pos => ops::pos(value),
|
ast::UnOp::Pos => ops::pos(value),
|
||||||
UnOp::Neg => ops::neg(value),
|
ast::UnOp::Neg => ops::neg(value),
|
||||||
UnOp::Not => ops::not(value),
|
ast::UnOp::Not => ops::not(value),
|
||||||
};
|
};
|
||||||
Ok(result.at(self.span())?)
|
Ok(result.at(self.span())?)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Eval for BinaryExpr {
|
impl Eval for ast::Binary {
|
||||||
type Output = Value;
|
type Output = Value;
|
||||||
|
|
||||||
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
|
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
|
||||||
match self.op() {
|
match self.op() {
|
||||||
BinOp::Add => self.apply(vm, ops::add),
|
ast::BinOp::Add => self.apply(vm, ops::add),
|
||||||
BinOp::Sub => self.apply(vm, ops::sub),
|
ast::BinOp::Sub => self.apply(vm, ops::sub),
|
||||||
BinOp::Mul => self.apply(vm, ops::mul),
|
ast::BinOp::Mul => self.apply(vm, ops::mul),
|
||||||
BinOp::Div => self.apply(vm, ops::div),
|
ast::BinOp::Div => self.apply(vm, ops::div),
|
||||||
BinOp::And => self.apply(vm, ops::and),
|
ast::BinOp::And => self.apply(vm, ops::and),
|
||||||
BinOp::Or => self.apply(vm, ops::or),
|
ast::BinOp::Or => self.apply(vm, ops::or),
|
||||||
BinOp::Eq => self.apply(vm, ops::eq),
|
ast::BinOp::Eq => self.apply(vm, ops::eq),
|
||||||
BinOp::Neq => self.apply(vm, ops::neq),
|
ast::BinOp::Neq => self.apply(vm, ops::neq),
|
||||||
BinOp::Lt => self.apply(vm, ops::lt),
|
ast::BinOp::Lt => self.apply(vm, ops::lt),
|
||||||
BinOp::Leq => self.apply(vm, ops::leq),
|
ast::BinOp::Leq => self.apply(vm, ops::leq),
|
||||||
BinOp::Gt => self.apply(vm, ops::gt),
|
ast::BinOp::Gt => self.apply(vm, ops::gt),
|
||||||
BinOp::Geq => self.apply(vm, ops::geq),
|
ast::BinOp::Geq => self.apply(vm, ops::geq),
|
||||||
BinOp::In => self.apply(vm, ops::in_),
|
ast::BinOp::In => self.apply(vm, ops::in_),
|
||||||
BinOp::NotIn => self.apply(vm, ops::not_in),
|
ast::BinOp::NotIn => self.apply(vm, ops::not_in),
|
||||||
BinOp::Assign => self.assign(vm, |_, b| Ok(b)),
|
ast::BinOp::Assign => self.assign(vm, |_, b| Ok(b)),
|
||||||
BinOp::AddAssign => self.assign(vm, ops::add),
|
ast::BinOp::AddAssign => self.assign(vm, ops::add),
|
||||||
BinOp::SubAssign => self.assign(vm, ops::sub),
|
ast::BinOp::SubAssign => self.assign(vm, ops::sub),
|
||||||
BinOp::MulAssign => self.assign(vm, ops::mul),
|
ast::BinOp::MulAssign => self.assign(vm, ops::mul),
|
||||||
BinOp::DivAssign => self.assign(vm, ops::div),
|
ast::BinOp::DivAssign => self.assign(vm, ops::div),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BinaryExpr {
|
impl ast::Binary {
|
||||||
/// Apply a basic binary operation.
|
/// Apply a basic binary operation.
|
||||||
fn apply(
|
fn apply(
|
||||||
&self,
|
&self,
|
||||||
@ -634,8 +719,8 @@ impl BinaryExpr {
|
|||||||
let lhs = self.lhs().eval(vm)?;
|
let lhs = self.lhs().eval(vm)?;
|
||||||
|
|
||||||
// Short-circuit boolean operations.
|
// Short-circuit boolean operations.
|
||||||
if (self.op() == BinOp::And && lhs == Value::Bool(false))
|
if (self.op() == ast::BinOp::And && lhs == Value::Bool(false))
|
||||||
|| (self.op() == BinOp::Or && lhs == Value::Bool(true))
|
|| (self.op() == ast::BinOp::Or && lhs == Value::Bool(true))
|
||||||
{
|
{
|
||||||
return Ok(lhs);
|
return Ok(lhs);
|
||||||
}
|
}
|
||||||
@ -658,11 +743,11 @@ impl BinaryExpr {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Eval for FieldAccess {
|
impl Eval for ast::FieldAccess {
|
||||||
type Output = Value;
|
type Output = Value;
|
||||||
|
|
||||||
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
|
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
|
||||||
let object = self.object().eval(vm)?;
|
let object = self.target().eval(vm)?;
|
||||||
let span = self.field().span();
|
let span = self.field().span();
|
||||||
let field = self.field().take();
|
let field = self.field().take();
|
||||||
|
|
||||||
@ -676,7 +761,7 @@ impl Eval for FieldAccess {
|
|||||||
.clone(),
|
.clone(),
|
||||||
|
|
||||||
v => bail!(
|
v => bail!(
|
||||||
self.object().span(),
|
self.target().span(),
|
||||||
"cannot access field on {}",
|
"cannot access field on {}",
|
||||||
v.type_name()
|
v.type_name()
|
||||||
),
|
),
|
||||||
@ -684,7 +769,7 @@ impl Eval for FieldAccess {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Eval for FuncCall {
|
impl Eval for ast::FuncCall {
|
||||||
type Output = Value;
|
type Output = Value;
|
||||||
|
|
||||||
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
|
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
|
||||||
@ -708,7 +793,7 @@ impl Eval for FuncCall {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Eval for MethodCall {
|
impl Eval for ast::MethodCall {
|
||||||
type Output = Value;
|
type Output = Value;
|
||||||
|
|
||||||
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
|
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
|
||||||
@ -718,19 +803,19 @@ impl Eval for MethodCall {
|
|||||||
|
|
||||||
Ok(if methods::is_mutating(&method) {
|
Ok(if methods::is_mutating(&method) {
|
||||||
let args = self.args().eval(vm)?;
|
let args = self.args().eval(vm)?;
|
||||||
let mut value = self.receiver().access(vm)?;
|
let mut value = self.target().access(vm)?;
|
||||||
methods::call_mut(&mut value, &method, args, span)
|
methods::call_mut(&mut value, &method, args, span)
|
||||||
.trace(vm.world, point, span)?;
|
.trace(vm.world, point, span)?;
|
||||||
Value::None
|
Value::None
|
||||||
} else {
|
} else {
|
||||||
let value = self.receiver().eval(vm)?;
|
let value = self.target().eval(vm)?;
|
||||||
let args = self.args().eval(vm)?;
|
let args = self.args().eval(vm)?;
|
||||||
methods::call(vm, value, &method, args, span).trace(vm.world, point, span)?
|
methods::call(vm, value, &method, args, span).trace(vm.world, point, span)?
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Eval for CallArgs {
|
impl Eval for ast::Args {
|
||||||
type Output = Args;
|
type Output = Args;
|
||||||
|
|
||||||
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
|
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
|
||||||
@ -739,21 +824,21 @@ impl Eval for CallArgs {
|
|||||||
for arg in self.items() {
|
for arg in self.items() {
|
||||||
let span = arg.span();
|
let span = arg.span();
|
||||||
match arg {
|
match arg {
|
||||||
CallArg::Pos(expr) => {
|
ast::Arg::Pos(expr) => {
|
||||||
items.push(Arg {
|
items.push(Arg {
|
||||||
span,
|
span,
|
||||||
name: None,
|
name: None,
|
||||||
value: Spanned::new(expr.eval(vm)?, expr.span()),
|
value: Spanned::new(expr.eval(vm)?, expr.span()),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
CallArg::Named(named) => {
|
ast::Arg::Named(named) => {
|
||||||
items.push(Arg {
|
items.push(Arg {
|
||||||
span,
|
span,
|
||||||
name: Some(named.name().take().into()),
|
name: Some(named.name().take().into()),
|
||||||
value: Spanned::new(named.expr().eval(vm)?, named.expr().span()),
|
value: Spanned::new(named.expr().eval(vm)?, named.expr().span()),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
CallArg::Spread(expr) => match expr.eval(vm)? {
|
ast::Arg::Spread(expr) => match expr.eval(vm)? {
|
||||||
Value::None => {}
|
Value::None => {}
|
||||||
Value::Array(array) => {
|
Value::Array(array) => {
|
||||||
items.extend(array.into_iter().map(|value| Arg {
|
items.extend(array.into_iter().map(|value| Arg {
|
||||||
@ -779,12 +864,12 @@ impl Eval for CallArgs {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Eval for ClosureExpr {
|
impl Eval for ast::Closure {
|
||||||
type Output = Value;
|
type Output = Value;
|
||||||
|
|
||||||
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
|
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
|
||||||
// The closure's name is defined by its let binding if there's one.
|
// The closure's name is defined by its let binding if there's one.
|
||||||
let name = self.name().map(Ident::take);
|
let name = self.name().map(ast::Ident::take);
|
||||||
|
|
||||||
// Collect captured variables.
|
// Collect captured variables.
|
||||||
let captured = {
|
let captured = {
|
||||||
@ -799,13 +884,13 @@ impl Eval for ClosureExpr {
|
|||||||
// Collect parameters and an optional sink parameter.
|
// Collect parameters and an optional sink parameter.
|
||||||
for param in self.params() {
|
for param in self.params() {
|
||||||
match param {
|
match param {
|
||||||
ClosureParam::Pos(name) => {
|
ast::Param::Pos(name) => {
|
||||||
params.push((name.take(), None));
|
params.push((name.take(), None));
|
||||||
}
|
}
|
||||||
ClosureParam::Named(named) => {
|
ast::Param::Named(named) => {
|
||||||
params.push((named.name().take(), Some(named.expr().eval(vm)?)));
|
params.push((named.name().take(), Some(named.expr().eval(vm)?)));
|
||||||
}
|
}
|
||||||
ClosureParam::Sink(name) => {
|
ast::Param::Sink(name) => {
|
||||||
if sink.is_some() {
|
if sink.is_some() {
|
||||||
bail!(name.span(), "only one argument sink is allowed");
|
bail!(name.span(), "only one argument sink is allowed");
|
||||||
}
|
}
|
||||||
@ -826,7 +911,7 @@ impl Eval for ClosureExpr {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Eval for LetExpr {
|
impl Eval for ast::LetBinding {
|
||||||
type Output = Value;
|
type Output = Value;
|
||||||
|
|
||||||
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
|
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
|
||||||
@ -839,7 +924,7 @@ impl Eval for LetExpr {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Eval for SetExpr {
|
impl Eval for ast::SetRule {
|
||||||
type Output = StyleMap;
|
type Output = StyleMap;
|
||||||
|
|
||||||
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
|
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
|
||||||
@ -850,7 +935,7 @@ impl Eval for SetExpr {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Eval for ShowExpr {
|
impl Eval for ast::ShowRule {
|
||||||
type Output = Recipe;
|
type Output = Recipe;
|
||||||
|
|
||||||
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
|
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
|
||||||
@ -887,7 +972,7 @@ impl Eval for ShowExpr {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Eval for IfExpr {
|
impl Eval for ast::Conditional {
|
||||||
type Output = Value;
|
type Output = Value;
|
||||||
|
|
||||||
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
|
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
|
||||||
@ -902,7 +987,7 @@ impl Eval for IfExpr {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Eval for WhileExpr {
|
impl Eval for ast::WhileLoop {
|
||||||
type Output = Value;
|
type Output = Value;
|
||||||
|
|
||||||
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
|
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
|
||||||
@ -934,7 +1019,7 @@ impl Eval for WhileExpr {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Eval for ForExpr {
|
impl Eval for ast::ForLoop {
|
||||||
type Output = Value;
|
type Output = Value;
|
||||||
|
|
||||||
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
|
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
|
||||||
@ -968,7 +1053,7 @@ impl Eval for ForExpr {
|
|||||||
|
|
||||||
let iter = self.iter().eval(vm)?;
|
let iter = self.iter().eval(vm)?;
|
||||||
let pattern = self.pattern();
|
let pattern = self.pattern();
|
||||||
let key = pattern.key().map(Ident::take);
|
let key = pattern.key().map(ast::Ident::take);
|
||||||
let value = pattern.value().take();
|
let value = pattern.value().take();
|
||||||
|
|
||||||
match (key, value, iter) {
|
match (key, value, iter) {
|
||||||
@ -1013,7 +1098,7 @@ impl Eval for ForExpr {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Eval for ImportExpr {
|
impl Eval for ast::ModuleImport {
|
||||||
type Output = Value;
|
type Output = Value;
|
||||||
|
|
||||||
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
|
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
|
||||||
@ -1022,12 +1107,12 @@ impl Eval for ImportExpr {
|
|||||||
let module = import(vm, &path, span)?;
|
let module = import(vm, &path, span)?;
|
||||||
|
|
||||||
match self.imports() {
|
match self.imports() {
|
||||||
Imports::Wildcard => {
|
ast::Imports::Wildcard => {
|
||||||
for (var, value) in module.scope.iter() {
|
for (var, value) in module.scope.iter() {
|
||||||
vm.scopes.top.define(var, value.clone());
|
vm.scopes.top.define(var, value.clone());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Imports::Items(idents) => {
|
ast::Imports::Items(idents) => {
|
||||||
for ident in idents {
|
for ident in idents {
|
||||||
if let Some(value) = module.scope.get(&ident) {
|
if let Some(value) = module.scope.get(&ident) {
|
||||||
vm.scopes.top.define(ident.take(), value.clone());
|
vm.scopes.top.define(ident.take(), value.clone());
|
||||||
@ -1042,7 +1127,7 @@ impl Eval for ImportExpr {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Eval for IncludeExpr {
|
impl Eval for ast::ModuleInclude {
|
||||||
type Output = Content;
|
type Output = Content;
|
||||||
|
|
||||||
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
|
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
|
||||||
@ -1071,7 +1156,7 @@ fn import(vm: &mut Vm, path: &str, span: Span) -> SourceResult<Module> {
|
|||||||
Ok(module)
|
Ok(module)
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Eval for BreakExpr {
|
impl Eval for ast::BreakStmt {
|
||||||
type Output = Value;
|
type Output = Value;
|
||||||
|
|
||||||
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
|
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
|
||||||
@ -1082,7 +1167,7 @@ impl Eval for BreakExpr {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Eval for ContinueExpr {
|
impl Eval for ast::ContinueStmt {
|
||||||
type Output = Value;
|
type Output = Value;
|
||||||
|
|
||||||
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
|
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
|
||||||
@ -1093,7 +1178,7 @@ impl Eval for ContinueExpr {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Eval for ReturnExpr {
|
impl Eval for ast::ReturnStmt {
|
||||||
type Output = Value;
|
type Output = Value;
|
||||||
|
|
||||||
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
|
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
|
||||||
@ -1111,29 +1196,29 @@ pub trait Access {
|
|||||||
fn access<'a>(&self, vm: &'a mut Vm) -> SourceResult<&'a mut Value>;
|
fn access<'a>(&self, vm: &'a mut Vm) -> SourceResult<&'a mut Value>;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Access for Expr {
|
impl Access for ast::Expr {
|
||||||
fn access<'a>(&self, vm: &'a mut Vm) -> SourceResult<&'a mut Value> {
|
fn access<'a>(&self, vm: &'a mut Vm) -> SourceResult<&'a mut Value> {
|
||||||
match self {
|
match self {
|
||||||
Expr::Ident(v) => v.access(vm),
|
Self::Ident(v) => v.access(vm),
|
||||||
Expr::FieldAccess(v) => v.access(vm),
|
Self::FieldAccess(v) => v.access(vm),
|
||||||
Expr::FuncCall(v) => v.access(vm),
|
Self::FuncCall(v) => v.access(vm),
|
||||||
_ => bail!(self.span(), "cannot mutate a temporary value"),
|
_ => bail!(self.span(), "cannot mutate a temporary value"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Access for Ident {
|
impl Access for ast::Ident {
|
||||||
fn access<'a>(&self, vm: &'a mut Vm) -> SourceResult<&'a mut Value> {
|
fn access<'a>(&self, vm: &'a mut Vm) -> SourceResult<&'a mut Value> {
|
||||||
vm.scopes.get_mut(self).at(self.span())
|
vm.scopes.get_mut(self).at(self.span())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Access for FieldAccess {
|
impl Access for ast::FieldAccess {
|
||||||
fn access<'a>(&self, vm: &'a mut Vm) -> SourceResult<&'a mut Value> {
|
fn access<'a>(&self, vm: &'a mut Vm) -> SourceResult<&'a mut Value> {
|
||||||
Ok(match self.object().access(vm)? {
|
Ok(match self.target().access(vm)? {
|
||||||
Value::Dict(dict) => dict.get_mut(self.field().take().into()),
|
Value::Dict(dict) => dict.get_mut(self.field().take().into()),
|
||||||
v => bail!(
|
v => bail!(
|
||||||
self.object().span(),
|
self.target().span(),
|
||||||
"expected dictionary, found {}",
|
"expected dictionary, found {}",
|
||||||
v.type_name(),
|
v.type_name(),
|
||||||
),
|
),
|
||||||
@ -1141,7 +1226,7 @@ impl Access for FieldAccess {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Access for FuncCall {
|
impl Access for ast::FuncCall {
|
||||||
fn access<'a>(&self, vm: &'a mut Vm) -> SourceResult<&'a mut Value> {
|
fn access<'a>(&self, vm: &'a mut Vm) -> SourceResult<&'a mut Value> {
|
||||||
let args = self.args().eval(vm)?;
|
let args = self.args().eval(vm)?;
|
||||||
Ok(match self.callee().access(vm)? {
|
Ok(match self.callee().access(vm)? {
|
||||||
|
@ -389,16 +389,12 @@ fn is_bounded(kind: &NodeKind) -> bool {
|
|||||||
match kind {
|
match kind {
|
||||||
NodeKind::CodeBlock
|
NodeKind::CodeBlock
|
||||||
| NodeKind::ContentBlock
|
| NodeKind::ContentBlock
|
||||||
| NodeKind::Backslash
|
| NodeKind::Linebreak
|
||||||
| NodeKind::Tilde
|
| NodeKind::SmartQuote { .. }
|
||||||
| NodeKind::HyphQuest
|
|
||||||
| NodeKind::Hyph2
|
|
||||||
| NodeKind::Hyph3
|
|
||||||
| NodeKind::Dot3
|
|
||||||
| NodeKind::Quote { .. }
|
|
||||||
| NodeKind::BlockComment
|
| NodeKind::BlockComment
|
||||||
| NodeKind::Space { .. }
|
| NodeKind::Space { .. }
|
||||||
| NodeKind::Escape(_) => true,
|
| NodeKind::Escape(_)
|
||||||
|
| NodeKind::Shorthand(_) => true,
|
||||||
_ => false,
|
_ => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,9 +11,8 @@ pub use tokens::*;
|
|||||||
|
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
|
|
||||||
use crate::diag::ErrorPos;
|
|
||||||
use crate::syntax::ast::{Assoc, BinOp, UnOp};
|
use crate::syntax::ast::{Assoc, BinOp, UnOp};
|
||||||
use crate::syntax::{NodeKind, SyntaxNode};
|
use crate::syntax::{ErrorPos, NodeKind, SyntaxNode};
|
||||||
use crate::util::EcoString;
|
use crate::util::EcoString;
|
||||||
|
|
||||||
/// Parse a source file.
|
/// Parse a source file.
|
||||||
@ -240,14 +239,10 @@ fn markup_node(p: &mut Parser, at_start: &mut bool) {
|
|||||||
|
|
||||||
// Text and markup.
|
// Text and markup.
|
||||||
NodeKind::Text(_)
|
NodeKind::Text(_)
|
||||||
| NodeKind::Backslash
|
| NodeKind::Linebreak
|
||||||
| NodeKind::Tilde
|
| NodeKind::SmartQuote { .. }
|
||||||
| NodeKind::HyphQuest
|
|
||||||
| NodeKind::Hyph2
|
|
||||||
| NodeKind::Hyph3
|
|
||||||
| NodeKind::Dot3
|
|
||||||
| NodeKind::Quote { .. }
|
|
||||||
| NodeKind::Escape(_)
|
| NodeKind::Escape(_)
|
||||||
|
| NodeKind::Shorthand(_)
|
||||||
| NodeKind::Link(_)
|
| NodeKind::Link(_)
|
||||||
| NodeKind::Raw(_)
|
| NodeKind::Raw(_)
|
||||||
| NodeKind::Label(_)
|
| NodeKind::Label(_)
|
||||||
@ -475,15 +470,15 @@ fn math_primary(p: &mut Parser) {
|
|||||||
match token {
|
match token {
|
||||||
// Spaces, atoms and expressions.
|
// Spaces, atoms and expressions.
|
||||||
NodeKind::Space { .. }
|
NodeKind::Space { .. }
|
||||||
| NodeKind::Backslash
|
| NodeKind::Linebreak
|
||||||
| NodeKind::Escape(_)
|
| NodeKind::Escape(_)
|
||||||
| NodeKind::Atom(_)
|
| NodeKind::Atom(_)
|
||||||
| NodeKind::Ident(_) => p.eat(),
|
| NodeKind::Ident(_) => p.eat(),
|
||||||
|
|
||||||
// Groups.
|
// Groups.
|
||||||
NodeKind::LeftParen => group(p, Group::Paren),
|
NodeKind::LeftParen => group(p, Group::Paren, '(', ')'),
|
||||||
NodeKind::LeftBracket => group(p, Group::Bracket),
|
NodeKind::LeftBracket => group(p, Group::Bracket, '[', ']'),
|
||||||
NodeKind::LeftBrace => group(p, Group::Brace),
|
NodeKind::LeftBrace => group(p, Group::Brace, '{', '}'),
|
||||||
|
|
||||||
// Alignment indactor.
|
// Alignment indactor.
|
||||||
NodeKind::Amp => align(p),
|
NodeKind::Amp => align(p),
|
||||||
@ -493,13 +488,17 @@ fn math_primary(p: &mut Parser) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Parse grouped math.
|
/// Parse grouped math.
|
||||||
fn group(p: &mut Parser, group: Group) {
|
fn group(p: &mut Parser, group: Group, l: char, r: char) {
|
||||||
p.perform(NodeKind::Math, |p| {
|
p.perform(NodeKind::Math, |p| {
|
||||||
|
let marker = p.marker();
|
||||||
p.start_group(group);
|
p.start_group(group);
|
||||||
|
marker.convert(p, NodeKind::Atom(l.into()));
|
||||||
while !p.eof() {
|
while !p.eof() {
|
||||||
math_node(p);
|
math_node(p);
|
||||||
}
|
}
|
||||||
|
let marker = p.marker();
|
||||||
p.end_group();
|
p.end_group();
|
||||||
|
marker.convert(p, NodeKind::Atom(r.into()));
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -532,7 +531,7 @@ fn expr_prec(p: &mut Parser, atomic: bool, min_prec: usize) -> ParseResult {
|
|||||||
p.eat();
|
p.eat();
|
||||||
let prec = op.precedence();
|
let prec = op.precedence();
|
||||||
expr_prec(p, atomic, prec)?;
|
expr_prec(p, atomic, prec)?;
|
||||||
marker.end(p, NodeKind::UnaryExpr);
|
marker.end(p, NodeKind::Unary);
|
||||||
}
|
}
|
||||||
_ => primary(p, atomic)?,
|
_ => primary(p, atomic)?,
|
||||||
};
|
};
|
||||||
@ -585,7 +584,7 @@ fn expr_prec(p: &mut Parser, atomic: bool, min_prec: usize) -> ParseResult {
|
|||||||
Assoc::Right => {}
|
Assoc::Right => {}
|
||||||
}
|
}
|
||||||
|
|
||||||
marker.perform(p, NodeKind::BinaryExpr, |p| expr_prec(p, atomic, prec))?;
|
marker.perform(p, NodeKind::Binary, |p| expr_prec(p, atomic, prec))?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -605,9 +604,9 @@ fn primary(p: &mut Parser, atomic: bool) -> ParseResult {
|
|||||||
|
|
||||||
// Arrow means this is a closure's lone parameter.
|
// Arrow means this is a closure's lone parameter.
|
||||||
if !atomic && p.at(NodeKind::Arrow) {
|
if !atomic && p.at(NodeKind::Arrow) {
|
||||||
marker.end(p, NodeKind::ClosureParams);
|
marker.end(p, NodeKind::Params);
|
||||||
p.assert(NodeKind::Arrow);
|
p.assert(NodeKind::Arrow);
|
||||||
marker.perform(p, NodeKind::ClosureExpr, expr)
|
marker.perform(p, NodeKind::Closure, expr)
|
||||||
} else {
|
} else {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -703,12 +702,12 @@ fn parenthesized(p: &mut Parser, atomic: bool) -> ParseResult {
|
|||||||
if !atomic && p.at(NodeKind::Arrow) {
|
if !atomic && p.at(NodeKind::Arrow) {
|
||||||
params(p, marker);
|
params(p, marker);
|
||||||
p.assert(NodeKind::Arrow);
|
p.assert(NodeKind::Arrow);
|
||||||
return marker.perform(p, NodeKind::ClosureExpr, expr);
|
return marker.perform(p, NodeKind::Closure, expr);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Transform into the identified collection.
|
// Transform into the identified collection.
|
||||||
match kind {
|
match kind {
|
||||||
CollectionKind::Group => marker.end(p, NodeKind::GroupExpr),
|
CollectionKind::Group => marker.end(p, NodeKind::Parenthesized),
|
||||||
CollectionKind::Positional => array(p, marker),
|
CollectionKind::Positional => array(p, marker),
|
||||||
CollectionKind::Named => dict(p, marker),
|
CollectionKind::Named => dict(p, marker),
|
||||||
}
|
}
|
||||||
@ -833,7 +832,7 @@ fn array(p: &mut Parser, marker: Marker) {
|
|||||||
NodeKind::Named | NodeKind::Keyed => Err("expected expression"),
|
NodeKind::Named | NodeKind::Keyed => Err("expected expression"),
|
||||||
_ => Ok(()),
|
_ => Ok(()),
|
||||||
});
|
});
|
||||||
marker.end(p, NodeKind::ArrayExpr);
|
marker.end(p, NodeKind::Array);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Convert a collection into a dictionary, producing errors for anything other
|
/// Convert a collection into a dictionary, producing errors for anything other
|
||||||
@ -855,7 +854,7 @@ fn dict(p: &mut Parser, marker: Marker) {
|
|||||||
NodeKind::Spread | NodeKind::Comma | NodeKind::Colon => Ok(()),
|
NodeKind::Spread | NodeKind::Comma | NodeKind::Colon => Ok(()),
|
||||||
_ => Err("expected named or keyed pair"),
|
_ => Err("expected named or keyed pair"),
|
||||||
});
|
});
|
||||||
marker.end(p, NodeKind::DictExpr);
|
marker.end(p, NodeKind::Dict);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Convert a collection into a list of parameters, producing errors for
|
/// Convert a collection into a list of parameters, producing errors for
|
||||||
@ -874,7 +873,7 @@ fn params(p: &mut Parser, marker: Marker) {
|
|||||||
}
|
}
|
||||||
_ => Err("expected identifier, named pair or argument sink"),
|
_ => Err("expected identifier, named pair or argument sink"),
|
||||||
});
|
});
|
||||||
marker.end(p, NodeKind::ClosureParams);
|
marker.end(p, NodeKind::Params);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parse a code block: `{...}`.
|
/// Parse a code block: `{...}`.
|
||||||
@ -920,7 +919,7 @@ fn args(p: &mut Parser) -> ParseResult {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
p.perform(NodeKind::CallArgs, |p| {
|
p.perform(NodeKind::Args, |p| {
|
||||||
if p.at(NodeKind::LeftParen) {
|
if p.at(NodeKind::LeftParen) {
|
||||||
let marker = p.marker();
|
let marker = p.marker();
|
||||||
p.start_group(Group::Paren);
|
p.start_group(Group::Paren);
|
||||||
@ -953,7 +952,7 @@ fn args(p: &mut Parser) -> ParseResult {
|
|||||||
|
|
||||||
/// Parse a let expression.
|
/// Parse a let expression.
|
||||||
fn let_expr(p: &mut Parser) -> ParseResult {
|
fn let_expr(p: &mut Parser) -> ParseResult {
|
||||||
p.perform(NodeKind::LetExpr, |p| {
|
p.perform(NodeKind::LetBinding, |p| {
|
||||||
p.assert(NodeKind::Let);
|
p.assert(NodeKind::Let);
|
||||||
|
|
||||||
let marker = p.marker();
|
let marker = p.marker();
|
||||||
@ -978,7 +977,7 @@ fn let_expr(p: &mut Parser) -> ParseResult {
|
|||||||
|
|
||||||
// Rewrite into a closure expression if it's a function definition.
|
// Rewrite into a closure expression if it's a function definition.
|
||||||
if has_params {
|
if has_params {
|
||||||
marker.end(p, NodeKind::ClosureExpr);
|
marker.end(p, NodeKind::Closure);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -987,7 +986,7 @@ fn let_expr(p: &mut Parser) -> ParseResult {
|
|||||||
|
|
||||||
/// Parse a set expression.
|
/// Parse a set expression.
|
||||||
fn set_expr(p: &mut Parser) -> ParseResult {
|
fn set_expr(p: &mut Parser) -> ParseResult {
|
||||||
p.perform(NodeKind::SetExpr, |p| {
|
p.perform(NodeKind::SetRule, |p| {
|
||||||
p.assert(NodeKind::Set);
|
p.assert(NodeKind::Set);
|
||||||
ident(p)?;
|
ident(p)?;
|
||||||
args(p)
|
args(p)
|
||||||
@ -996,7 +995,7 @@ fn set_expr(p: &mut Parser) -> ParseResult {
|
|||||||
|
|
||||||
/// Parse a show expression.
|
/// Parse a show expression.
|
||||||
fn show_expr(p: &mut Parser) -> ParseResult {
|
fn show_expr(p: &mut Parser) -> ParseResult {
|
||||||
p.perform(NodeKind::ShowExpr, |p| {
|
p.perform(NodeKind::ShowRule, |p| {
|
||||||
p.assert(NodeKind::Show);
|
p.assert(NodeKind::Show);
|
||||||
let marker = p.marker();
|
let marker = p.marker();
|
||||||
expr(p)?;
|
expr(p)?;
|
||||||
@ -1014,7 +1013,7 @@ fn show_expr(p: &mut Parser) -> ParseResult {
|
|||||||
|
|
||||||
/// Parse a wrap expression.
|
/// Parse a wrap expression.
|
||||||
fn wrap_expr(p: &mut Parser) -> ParseResult {
|
fn wrap_expr(p: &mut Parser) -> ParseResult {
|
||||||
p.perform(NodeKind::WrapExpr, |p| {
|
p.perform(NodeKind::WrapRule, |p| {
|
||||||
p.assert(NodeKind::Wrap);
|
p.assert(NodeKind::Wrap);
|
||||||
ident(p)?;
|
ident(p)?;
|
||||||
p.expect(NodeKind::In)?;
|
p.expect(NodeKind::In)?;
|
||||||
@ -1024,7 +1023,7 @@ fn wrap_expr(p: &mut Parser) -> ParseResult {
|
|||||||
|
|
||||||
/// Parse an if-else expresion.
|
/// Parse an if-else expresion.
|
||||||
fn if_expr(p: &mut Parser) -> ParseResult {
|
fn if_expr(p: &mut Parser) -> ParseResult {
|
||||||
p.perform(NodeKind::IfExpr, |p| {
|
p.perform(NodeKind::Conditional, |p| {
|
||||||
p.assert(NodeKind::If);
|
p.assert(NodeKind::If);
|
||||||
|
|
||||||
expr(p)?;
|
expr(p)?;
|
||||||
@ -1044,7 +1043,7 @@ fn if_expr(p: &mut Parser) -> ParseResult {
|
|||||||
|
|
||||||
/// Parse a while expresion.
|
/// Parse a while expresion.
|
||||||
fn while_expr(p: &mut Parser) -> ParseResult {
|
fn while_expr(p: &mut Parser) -> ParseResult {
|
||||||
p.perform(NodeKind::WhileExpr, |p| {
|
p.perform(NodeKind::WhileLoop, |p| {
|
||||||
p.assert(NodeKind::While);
|
p.assert(NodeKind::While);
|
||||||
expr(p)?;
|
expr(p)?;
|
||||||
body(p)
|
body(p)
|
||||||
@ -1053,7 +1052,7 @@ fn while_expr(p: &mut Parser) -> ParseResult {
|
|||||||
|
|
||||||
/// Parse a for-in expression.
|
/// Parse a for-in expression.
|
||||||
fn for_expr(p: &mut Parser) -> ParseResult {
|
fn for_expr(p: &mut Parser) -> ParseResult {
|
||||||
p.perform(NodeKind::ForExpr, |p| {
|
p.perform(NodeKind::ForLoop, |p| {
|
||||||
p.assert(NodeKind::For);
|
p.assert(NodeKind::For);
|
||||||
for_pattern(p)?;
|
for_pattern(p)?;
|
||||||
p.expect(NodeKind::In)?;
|
p.expect(NodeKind::In)?;
|
||||||
@ -1075,7 +1074,7 @@ fn for_pattern(p: &mut Parser) -> ParseResult {
|
|||||||
|
|
||||||
/// Parse an import expression.
|
/// Parse an import expression.
|
||||||
fn import_expr(p: &mut Parser) -> ParseResult {
|
fn import_expr(p: &mut Parser) -> ParseResult {
|
||||||
p.perform(NodeKind::ImportExpr, |p| {
|
p.perform(NodeKind::ModuleImport, |p| {
|
||||||
p.assert(NodeKind::Import);
|
p.assert(NodeKind::Import);
|
||||||
|
|
||||||
if !p.eat_if(NodeKind::Star) {
|
if !p.eat_if(NodeKind::Star) {
|
||||||
@ -1103,7 +1102,7 @@ fn import_expr(p: &mut Parser) -> ParseResult {
|
|||||||
|
|
||||||
/// Parse an include expression.
|
/// Parse an include expression.
|
||||||
fn include_expr(p: &mut Parser) -> ParseResult {
|
fn include_expr(p: &mut Parser) -> ParseResult {
|
||||||
p.perform(NodeKind::IncludeExpr, |p| {
|
p.perform(NodeKind::ModuleInclude, |p| {
|
||||||
p.assert(NodeKind::Include);
|
p.assert(NodeKind::Include);
|
||||||
expr(p)
|
expr(p)
|
||||||
})
|
})
|
||||||
@ -1111,7 +1110,7 @@ fn include_expr(p: &mut Parser) -> ParseResult {
|
|||||||
|
|
||||||
/// Parse a break expression.
|
/// Parse a break expression.
|
||||||
fn break_expr(p: &mut Parser) -> ParseResult {
|
fn break_expr(p: &mut Parser) -> ParseResult {
|
||||||
p.perform(NodeKind::BreakExpr, |p| {
|
p.perform(NodeKind::BreakStmt, |p| {
|
||||||
p.assert(NodeKind::Break);
|
p.assert(NodeKind::Break);
|
||||||
Ok(())
|
Ok(())
|
||||||
})
|
})
|
||||||
@ -1119,7 +1118,7 @@ fn break_expr(p: &mut Parser) -> ParseResult {
|
|||||||
|
|
||||||
/// Parse a continue expression.
|
/// Parse a continue expression.
|
||||||
fn continue_expr(p: &mut Parser) -> ParseResult {
|
fn continue_expr(p: &mut Parser) -> ParseResult {
|
||||||
p.perform(NodeKind::ContinueExpr, |p| {
|
p.perform(NodeKind::ContinueStmt, |p| {
|
||||||
p.assert(NodeKind::Continue);
|
p.assert(NodeKind::Continue);
|
||||||
Ok(())
|
Ok(())
|
||||||
})
|
})
|
||||||
@ -1127,7 +1126,7 @@ fn continue_expr(p: &mut Parser) -> ParseResult {
|
|||||||
|
|
||||||
/// Parse a return expression.
|
/// Parse a return expression.
|
||||||
fn return_expr(p: &mut Parser) -> ParseResult {
|
fn return_expr(p: &mut Parser) -> ParseResult {
|
||||||
p.perform(NodeKind::ReturnExpr, |p| {
|
p.perform(NodeKind::ReturnStmt, |p| {
|
||||||
p.assert(NodeKind::Return);
|
p.assert(NodeKind::Return);
|
||||||
if !p.at(NodeKind::Comma) && !p.eof() {
|
if !p.at(NodeKind::Comma) && !p.eof() {
|
||||||
expr(p)?;
|
expr(p)?;
|
||||||
|
@ -3,8 +3,7 @@ use std::mem;
|
|||||||
use std::ops::Range;
|
use std::ops::Range;
|
||||||
|
|
||||||
use super::{TokenMode, Tokens};
|
use super::{TokenMode, Tokens};
|
||||||
use crate::diag::ErrorPos;
|
use crate::syntax::{ErrorPos, InnerNode, NodeData, NodeKind, SyntaxNode};
|
||||||
use crate::syntax::{InnerNode, NodeData, NodeKind, SyntaxNode};
|
|
||||||
use crate::util::EcoString;
|
use crate::util::EcoString;
|
||||||
|
|
||||||
/// A convenient token-based parser.
|
/// A convenient token-based parser.
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
use unscanny::Scanner;
|
use unscanny::Scanner;
|
||||||
|
|
||||||
use super::{is_ident, is_newline};
|
use super::{is_ident, is_newline};
|
||||||
use crate::syntax::ast::RawNode;
|
use crate::syntax::RawKind;
|
||||||
use crate::util::EcoString;
|
use crate::util::EcoString;
|
||||||
|
|
||||||
/// Resolve all escape sequences in a string.
|
/// Resolve all escape sequences in a string.
|
||||||
@ -46,17 +46,17 @@ pub fn resolve_hex(sequence: &str) -> Option<char> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Resolve the language tag and trim the raw text.
|
/// Resolve the language tag and trim the raw text.
|
||||||
pub fn resolve_raw(column: usize, backticks: usize, text: &str) -> RawNode {
|
pub fn resolve_raw(column: usize, backticks: usize, text: &str) -> RawKind {
|
||||||
if backticks > 1 {
|
if backticks > 1 {
|
||||||
let (tag, inner) = split_at_lang_tag(text);
|
let (tag, inner) = split_at_lang_tag(text);
|
||||||
let (text, block) = trim_and_split_raw(column, inner);
|
let (text, block) = trim_and_split_raw(column, inner);
|
||||||
RawNode {
|
RawKind {
|
||||||
lang: is_ident(tag).then(|| tag.into()),
|
lang: is_ident(tag).then(|| tag.into()),
|
||||||
text: text.into(),
|
text: text.into(),
|
||||||
block,
|
block,
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
RawNode {
|
RawKind {
|
||||||
lang: None,
|
lang: None,
|
||||||
text: split_lines(text).join("\n").into(),
|
text: split_lines(text).join("\n").into(),
|
||||||
block: false,
|
block: false,
|
||||||
|
@ -4,10 +4,8 @@ use unicode_xid::UnicodeXID;
|
|||||||
use unscanny::Scanner;
|
use unscanny::Scanner;
|
||||||
|
|
||||||
use super::resolve::{resolve_hex, resolve_raw, resolve_string};
|
use super::resolve::{resolve_hex, resolve_raw, resolve_string};
|
||||||
use crate::diag::ErrorPos;
|
|
||||||
use crate::geom::{AngleUnit, LengthUnit};
|
use crate::geom::{AngleUnit, LengthUnit};
|
||||||
use crate::syntax::ast::{RawNode, Unit};
|
use crate::syntax::{ErrorPos, NodeKind, RawKind, Unit};
|
||||||
use crate::syntax::NodeKind;
|
|
||||||
use crate::util::EcoString;
|
use crate::util::EcoString;
|
||||||
|
|
||||||
/// An iterator over the tokens of a string of source code.
|
/// An iterator over the tokens of a string of source code.
|
||||||
@ -199,24 +197,9 @@ impl<'s> Tokens<'s> {
|
|||||||
'[' => NodeKind::LeftBracket,
|
'[' => NodeKind::LeftBracket,
|
||||||
']' => NodeKind::RightBracket,
|
']' => NodeKind::RightBracket,
|
||||||
|
|
||||||
// Escape sequences.
|
|
||||||
'\\' => self.backslash(),
|
|
||||||
|
|
||||||
// Single-char things.
|
|
||||||
'~' => NodeKind::Tilde,
|
|
||||||
'.' if self.s.eat_if("..") => NodeKind::Dot3,
|
|
||||||
'\'' => NodeKind::Quote { double: false },
|
|
||||||
'"' => NodeKind::Quote { double: true },
|
|
||||||
'*' if !self.in_word() => NodeKind::Star,
|
|
||||||
'_' if !self.in_word() => NodeKind::Underscore,
|
|
||||||
'$' => NodeKind::Dollar,
|
|
||||||
'=' => NodeKind::Eq,
|
|
||||||
'+' => NodeKind::Plus,
|
|
||||||
'/' => NodeKind::Slash,
|
|
||||||
':' => NodeKind::Colon,
|
|
||||||
|
|
||||||
// Multi-char things.
|
// Multi-char things.
|
||||||
'#' => self.hash(start),
|
'#' => self.hash(start),
|
||||||
|
'.' if self.s.eat_if("..") => NodeKind::Shorthand('\u{2026}'),
|
||||||
'-' => self.hyph(),
|
'-' => self.hyph(),
|
||||||
'h' if self.s.eat_if("ttp://") || self.s.eat_if("ttps://") => {
|
'h' if self.s.eat_if("ttp://") || self.s.eat_if("ttps://") => {
|
||||||
self.link(start)
|
self.link(start)
|
||||||
@ -226,6 +209,21 @@ impl<'s> Tokens<'s> {
|
|||||||
'<' => self.label(),
|
'<' => self.label(),
|
||||||
'@' => self.reference(start),
|
'@' => self.reference(start),
|
||||||
|
|
||||||
|
// Escape sequences.
|
||||||
|
'\\' => self.backslash(),
|
||||||
|
|
||||||
|
// Single-char things.
|
||||||
|
'~' => NodeKind::Shorthand('\u{00A0}'),
|
||||||
|
'\'' => NodeKind::SmartQuote { double: false },
|
||||||
|
'"' => NodeKind::SmartQuote { double: true },
|
||||||
|
'*' if !self.in_word() => NodeKind::Star,
|
||||||
|
'_' if !self.in_word() => NodeKind::Underscore,
|
||||||
|
'$' => NodeKind::Dollar,
|
||||||
|
'=' => NodeKind::Eq,
|
||||||
|
'+' => NodeKind::Plus,
|
||||||
|
'/' => NodeKind::Slash,
|
||||||
|
':' => NodeKind::Colon,
|
||||||
|
|
||||||
// Plain text.
|
// Plain text.
|
||||||
_ => self.text(start),
|
_ => self.text(start),
|
||||||
}
|
}
|
||||||
@ -291,8 +289,8 @@ impl<'s> Tokens<'s> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Linebreaks.
|
// Linebreaks.
|
||||||
Some(c) if c.is_whitespace() => NodeKind::Backslash,
|
Some(c) if c.is_whitespace() => NodeKind::Linebreak,
|
||||||
None => NodeKind::Backslash,
|
None => NodeKind::Linebreak,
|
||||||
|
|
||||||
// Escapes.
|
// Escapes.
|
||||||
Some(c) => {
|
Some(c) => {
|
||||||
@ -317,24 +315,17 @@ impl<'s> Tokens<'s> {
|
|||||||
fn hyph(&mut self) -> NodeKind {
|
fn hyph(&mut self) -> NodeKind {
|
||||||
if self.s.eat_if('-') {
|
if self.s.eat_if('-') {
|
||||||
if self.s.eat_if('-') {
|
if self.s.eat_if('-') {
|
||||||
NodeKind::Hyph3
|
NodeKind::Shorthand('\u{2014}')
|
||||||
} else {
|
} else {
|
||||||
NodeKind::Hyph2
|
NodeKind::Shorthand('\u{2013}')
|
||||||
}
|
}
|
||||||
} else if self.s.eat_if('?') {
|
} else if self.s.eat_if('?') {
|
||||||
NodeKind::HyphQuest
|
NodeKind::Shorthand('\u{00AD}')
|
||||||
} else {
|
} else {
|
||||||
NodeKind::Minus
|
NodeKind::Minus
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn in_word(&self) -> bool {
|
|
||||||
let alphanumeric = |c: Option<char>| c.map_or(false, |c| c.is_alphanumeric());
|
|
||||||
let prev = self.s.scout(-2);
|
|
||||||
let next = self.s.peek();
|
|
||||||
alphanumeric(prev) && alphanumeric(next)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn link(&mut self, start: usize) -> NodeKind {
|
fn link(&mut self, start: usize) -> NodeKind {
|
||||||
#[rustfmt::skip]
|
#[rustfmt::skip]
|
||||||
self.s.eat_while(|c: char| matches!(c,
|
self.s.eat_while(|c: char| matches!(c,
|
||||||
@ -360,7 +351,7 @@ impl<'s> Tokens<'s> {
|
|||||||
|
|
||||||
// Special case for empty inline block.
|
// Special case for empty inline block.
|
||||||
if backticks == 2 {
|
if backticks == 2 {
|
||||||
return NodeKind::Raw(Arc::new(RawNode {
|
return NodeKind::Raw(Arc::new(RawKind {
|
||||||
text: EcoString::new(),
|
text: EcoString::new(),
|
||||||
lang: None,
|
lang: None,
|
||||||
block: false,
|
block: false,
|
||||||
@ -567,23 +558,24 @@ impl<'s> Tokens<'s> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Ok(f) = number.parse::<f64>() {
|
let v = match number.parse::<f64>() {
|
||||||
|
Ok(v) => v,
|
||||||
|
Err(_) => return NodeKind::Error(ErrorPos::Full, "invalid number".into()),
|
||||||
|
};
|
||||||
|
|
||||||
match suffix {
|
match suffix {
|
||||||
"" => NodeKind::Float(f),
|
"" => NodeKind::Float(v),
|
||||||
"pt" => NodeKind::Numeric(f, Unit::Length(LengthUnit::Pt)),
|
"pt" => NodeKind::Numeric(v, Unit::Length(LengthUnit::Pt)),
|
||||||
"mm" => NodeKind::Numeric(f, Unit::Length(LengthUnit::Mm)),
|
"mm" => NodeKind::Numeric(v, Unit::Length(LengthUnit::Mm)),
|
||||||
"cm" => NodeKind::Numeric(f, Unit::Length(LengthUnit::Cm)),
|
"cm" => NodeKind::Numeric(v, Unit::Length(LengthUnit::Cm)),
|
||||||
"in" => NodeKind::Numeric(f, Unit::Length(LengthUnit::In)),
|
"in" => NodeKind::Numeric(v, Unit::Length(LengthUnit::In)),
|
||||||
"deg" => NodeKind::Numeric(f, Unit::Angle(AngleUnit::Deg)),
|
"deg" => NodeKind::Numeric(v, Unit::Angle(AngleUnit::Deg)),
|
||||||
"rad" => NodeKind::Numeric(f, Unit::Angle(AngleUnit::Rad)),
|
"rad" => NodeKind::Numeric(v, Unit::Angle(AngleUnit::Rad)),
|
||||||
"em" => NodeKind::Numeric(f, Unit::Em),
|
"em" => NodeKind::Numeric(v, Unit::Em),
|
||||||
"fr" => NodeKind::Numeric(f, Unit::Fr),
|
"fr" => NodeKind::Numeric(v, Unit::Fr),
|
||||||
"%" => NodeKind::Numeric(f, Unit::Percent),
|
"%" => NodeKind::Numeric(v, Unit::Percent),
|
||||||
_ => NodeKind::Error(ErrorPos::Full, "invalid number suffix".into()),
|
_ => NodeKind::Error(ErrorPos::Full, "invalid number suffix".into()),
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
NodeKind::Error(ErrorPos::Full, "invalid number".into())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn string(&mut self) -> NodeKind {
|
fn string(&mut self) -> NodeKind {
|
||||||
@ -605,6 +597,13 @@ impl<'s> Tokens<'s> {
|
|||||||
NodeKind::Error(ErrorPos::End, "expected quote".into())
|
NodeKind::Error(ErrorPos::End, "expected quote".into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn in_word(&self) -> bool {
|
||||||
|
let alphanumeric = |c: Option<char>| c.map_or(false, |c| c.is_alphanumeric());
|
||||||
|
let prev = self.s.scout(-2);
|
||||||
|
let next = self.s.peek();
|
||||||
|
alphanumeric(prev) && alphanumeric(next)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn keyword(ident: &str) -> Option<NodeKind> {
|
fn keyword(ident: &str) -> Option<NodeKind> {
|
||||||
@ -724,7 +723,7 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn Raw(text: &str, lang: Option<&str>, block: bool) -> NodeKind {
|
fn Raw(text: &str, lang: Option<&str>, block: bool) -> NodeKind {
|
||||||
NodeKind::Raw(Arc::new(RawNode {
|
NodeKind::Raw(Arc::new(RawKind {
|
||||||
text: text.into(),
|
text: text.into(),
|
||||||
lang: lang.map(Into::into),
|
lang: lang.map(Into::into),
|
||||||
block,
|
block,
|
||||||
@ -762,22 +761,15 @@ mod tests {
|
|||||||
/// - '/': symbols
|
/// - '/': symbols
|
||||||
const BLOCKS: &str = " a1/";
|
const BLOCKS: &str = " a1/";
|
||||||
|
|
||||||
macro_rules! t {
|
|
||||||
(Both $($tts:tt)*) => {
|
|
||||||
t!(Markup $($tts)*);
|
|
||||||
t!(Code $($tts)*);
|
|
||||||
};
|
|
||||||
($mode:ident $([$blocks:literal])?: $text:expr => $($token:expr),*) => {{
|
|
||||||
// Test without suffix.
|
|
||||||
t!(@$mode: $text => $($token),*);
|
|
||||||
|
|
||||||
// Suffixes described by four-tuples of:
|
// Suffixes described by four-tuples of:
|
||||||
//
|
//
|
||||||
// - block the suffix is part of
|
// - block the suffix is part of
|
||||||
// - mode in which the suffix is applicable
|
// - mode in which the suffix is applicable
|
||||||
// - the suffix string
|
// - the suffix string
|
||||||
// - the resulting suffix NodeKind
|
// - the resulting suffix NodeKind
|
||||||
let suffixes: &[(char, Option<TokenMode>, &str, NodeKind)] = &[
|
fn suffixes()
|
||||||
|
-> impl Iterator<Item = (char, Option<TokenMode>, &'static str, NodeKind)> {
|
||||||
|
[
|
||||||
// Whitespace suffixes.
|
// Whitespace suffixes.
|
||||||
(' ', None, " ", Space(0)),
|
(' ', None, " ", Space(0)),
|
||||||
(' ', None, "\n", Space(1)),
|
(' ', None, "\n", Space(1)),
|
||||||
@ -802,10 +794,21 @@ mod tests {
|
|||||||
('/', Some(Code), "(", LeftParen),
|
('/', Some(Code), "(", LeftParen),
|
||||||
('/', Some(Code), ":", Colon),
|
('/', Some(Code), ":", Colon),
|
||||||
('/', Some(Code), "+=", PlusEq),
|
('/', Some(Code), "+=", PlusEq),
|
||||||
];
|
]
|
||||||
|
.into_iter()
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! t {
|
||||||
|
(Both $($tts:tt)*) => {
|
||||||
|
t!(Markup $($tts)*);
|
||||||
|
t!(Code $($tts)*);
|
||||||
|
};
|
||||||
|
($mode:ident $([$blocks:literal])?: $text:expr => $($token:expr),*) => {{
|
||||||
|
// Test without suffix.
|
||||||
|
t!(@$mode: $text => $($token),*);
|
||||||
|
|
||||||
// Test with each applicable suffix.
|
// Test with each applicable suffix.
|
||||||
for &(block, mode, suffix, ref token) in suffixes {
|
for (block, mode, suffix, ref token) in suffixes() {
|
||||||
let text = $text;
|
let text = $text;
|
||||||
#[allow(unused_variables)]
|
#[allow(unused_variables)]
|
||||||
let blocks = BLOCKS;
|
let blocks = BLOCKS;
|
||||||
@ -872,14 +875,14 @@ mod tests {
|
|||||||
t!(Markup[" /"]: "reha-world" => Text("reha-world"));
|
t!(Markup[" /"]: "reha-world" => Text("reha-world"));
|
||||||
|
|
||||||
// Test code symbols in text.
|
// Test code symbols in text.
|
||||||
t!(Markup[" /"]: "a():\"b" => Text("a()"), Colon, Quote { double: true }, Text("b"));
|
t!(Markup[" /"]: "a():\"b" => Text("a()"), Colon, SmartQuote { double: true }, Text("b"));
|
||||||
t!(Markup[" /"]: ";,|/+" => Text(";,|/+"));
|
t!(Markup[" /"]: ";,|/+" => Text(";,|/+"));
|
||||||
t!(Markup[" /"]: "=-a" => Eq, Minus, Text("a"));
|
t!(Markup[" /"]: "=-a" => Eq, Minus, Text("a"));
|
||||||
t!(Markup[" "]: "#123" => Text("#123"));
|
t!(Markup[" "]: "#123" => Text("#123"));
|
||||||
|
|
||||||
// Test text ends.
|
// Test text ends.
|
||||||
t!(Markup[""]: "hello " => Text("hello"), Space(0));
|
t!(Markup[""]: "hello " => Text("hello"), Space(0));
|
||||||
t!(Markup[""]: "hello~" => Text("hello"), Tilde);
|
t!(Markup[""]: "hello~" => Text("hello"), Shorthand('\u{00A0}'));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -924,10 +927,10 @@ mod tests {
|
|||||||
t!(Markup: "_" => Underscore);
|
t!(Markup: "_" => Underscore);
|
||||||
t!(Markup[""]: "===" => Eq, Eq, Eq);
|
t!(Markup[""]: "===" => Eq, Eq, Eq);
|
||||||
t!(Markup["a1/"]: "= " => Eq, Space(0));
|
t!(Markup["a1/"]: "= " => Eq, Space(0));
|
||||||
t!(Markup[" "]: r"\" => Backslash);
|
t!(Markup[" "]: r"\" => Linebreak);
|
||||||
t!(Markup: "~" => Tilde);
|
t!(Markup: "~" => Shorthand('\u{00A0}'));
|
||||||
t!(Markup["a1/"]: "-?" => HyphQuest);
|
t!(Markup["a1/"]: "-?" => Shorthand('\u{00AD}'));
|
||||||
t!(Markup["a "]: r"a--" => Text("a"), Hyph2);
|
t!(Markup["a "]: r"a--" => Text("a"), Shorthand('\u{2013}'));
|
||||||
t!(Markup["a1/"]: "- " => Minus, Space(0));
|
t!(Markup["a1/"]: "- " => Minus, Space(0));
|
||||||
t!(Markup[" "]: "+" => Plus);
|
t!(Markup[" "]: "+" => Plus);
|
||||||
t!(Markup[" "]: "1." => EnumNumbering(1));
|
t!(Markup[" "]: "1." => EnumNumbering(1));
|
||||||
|
@ -10,7 +10,7 @@ use unscanny::Scanner;
|
|||||||
|
|
||||||
use crate::diag::SourceResult;
|
use crate::diag::SourceResult;
|
||||||
use crate::parse::{is_newline, parse, reparse};
|
use crate::parse::{is_newline, parse, reparse};
|
||||||
use crate::syntax::ast::MarkupNode;
|
use crate::syntax::ast::Markup;
|
||||||
use crate::syntax::{Span, SyntaxNode};
|
use crate::syntax::{Span, SyntaxNode};
|
||||||
use crate::util::{PathExt, StrExt};
|
use crate::util::{PathExt, StrExt};
|
||||||
|
|
||||||
@ -66,7 +66,7 @@ impl Source {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// The root node of the file's typed abstract syntax tree.
|
/// The root node of the file's typed abstract syntax tree.
|
||||||
pub fn ast(&self) -> SourceResult<MarkupNode> {
|
pub fn ast(&self) -> SourceResult<Markup> {
|
||||||
let errors = self.root.errors();
|
let errors = self.root.errors();
|
||||||
if errors.is_empty() {
|
if errors.is_empty() {
|
||||||
Ok(self.root.cast().expect("root node must be markup"))
|
Ok(self.root.cast().expect("root node must be markup"))
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -138,23 +138,21 @@ pub enum Category {
|
|||||||
/// An escape sequence.
|
/// An escape sequence.
|
||||||
Escape,
|
Escape,
|
||||||
/// An easily typable shortcut to a unicode codepoint.
|
/// An easily typable shortcut to a unicode codepoint.
|
||||||
Shortcut,
|
Shorthand,
|
||||||
/// A smart quote.
|
/// A smart quote.
|
||||||
Quote,
|
SmartQuote,
|
||||||
/// Strong text.
|
/// Strong markup.
|
||||||
Strong,
|
Strong,
|
||||||
/// Emphasized text.
|
/// Emphasized markup.
|
||||||
Emph,
|
Emph,
|
||||||
/// A hyperlink.
|
/// A hyperlink.
|
||||||
Link,
|
Link,
|
||||||
/// Raw text or code.
|
/// Raw text or code.
|
||||||
Raw,
|
Raw,
|
||||||
/// A full math formula.
|
/// A label.
|
||||||
Math,
|
Label,
|
||||||
/// The delimiters of a math formula.
|
/// A reference.
|
||||||
MathDelimiter,
|
Ref,
|
||||||
/// A symbol with special meaning in a math formula.
|
|
||||||
MathSymbol,
|
|
||||||
/// A section heading.
|
/// A section heading.
|
||||||
Heading,
|
Heading,
|
||||||
/// A full item of a list, enumeration or description list.
|
/// A full item of a list, enumeration or description list.
|
||||||
@ -163,10 +161,12 @@ pub enum Category {
|
|||||||
ListMarker,
|
ListMarker,
|
||||||
/// A term in a description list.
|
/// A term in a description list.
|
||||||
ListTerm,
|
ListTerm,
|
||||||
/// A label.
|
/// A full math formula.
|
||||||
Label,
|
Math,
|
||||||
/// A reference.
|
/// The delimiters of a math formula.
|
||||||
Ref,
|
MathDelimiter,
|
||||||
|
/// An operator with special meaning in a math formula.
|
||||||
|
MathOperator,
|
||||||
/// A keyword.
|
/// A keyword.
|
||||||
Keyword,
|
Keyword,
|
||||||
/// A literal defined by a keyword like `none`, `auto` or a boolean.
|
/// A literal defined by a keyword like `none`, `auto` or a boolean.
|
||||||
@ -212,17 +212,10 @@ impl Category {
|
|||||||
_ => Some(Category::Operator),
|
_ => Some(Category::Operator),
|
||||||
},
|
},
|
||||||
NodeKind::Underscore => match parent.kind() {
|
NodeKind::Underscore => match parent.kind() {
|
||||||
NodeKind::Script => Some(Category::MathSymbol),
|
NodeKind::Script => Some(Category::MathOperator),
|
||||||
_ => None,
|
_ => None,
|
||||||
},
|
},
|
||||||
NodeKind::Dollar => Some(Category::MathDelimiter),
|
NodeKind::Dollar => Some(Category::MathDelimiter),
|
||||||
NodeKind::Backslash => Some(Category::Shortcut),
|
|
||||||
NodeKind::Tilde => Some(Category::Shortcut),
|
|
||||||
NodeKind::HyphQuest => Some(Category::Shortcut),
|
|
||||||
NodeKind::Hyph2 => Some(Category::Shortcut),
|
|
||||||
NodeKind::Hyph3 => Some(Category::Shortcut),
|
|
||||||
NodeKind::Dot3 => Some(Category::Shortcut),
|
|
||||||
NodeKind::Quote { .. } => Some(Category::Quote),
|
|
||||||
NodeKind::Plus => Some(match parent.kind() {
|
NodeKind::Plus => Some(match parent.kind() {
|
||||||
NodeKind::EnumItem => Category::ListMarker,
|
NodeKind::EnumItem => Category::ListMarker,
|
||||||
_ => Category::Operator,
|
_ => Category::Operator,
|
||||||
@ -233,11 +226,11 @@ impl Category {
|
|||||||
}),
|
}),
|
||||||
NodeKind::Slash => Some(match parent.kind() {
|
NodeKind::Slash => Some(match parent.kind() {
|
||||||
NodeKind::DescItem => Category::ListMarker,
|
NodeKind::DescItem => Category::ListMarker,
|
||||||
NodeKind::Frac => Category::MathSymbol,
|
NodeKind::Frac => Category::MathOperator,
|
||||||
_ => Category::Operator,
|
_ => Category::Operator,
|
||||||
}),
|
}),
|
||||||
NodeKind::Hat => Some(Category::MathSymbol),
|
NodeKind::Hat => Some(Category::MathOperator),
|
||||||
NodeKind::Amp => Some(Category::MathSymbol),
|
NodeKind::Amp => Some(Category::MathOperator),
|
||||||
NodeKind::Dot => Some(Category::Punctuation),
|
NodeKind::Dot => Some(Category::Punctuation),
|
||||||
NodeKind::Eq => match parent.kind() {
|
NodeKind::Eq => match parent.kind() {
|
||||||
NodeKind::Heading => None,
|
NodeKind::Heading => None,
|
||||||
@ -291,32 +284,35 @@ impl Category {
|
|||||||
_ => None,
|
_ => None,
|
||||||
},
|
},
|
||||||
NodeKind::Text(_) => None,
|
NodeKind::Text(_) => None,
|
||||||
|
NodeKind::Linebreak => Some(Category::Escape),
|
||||||
NodeKind::Escape(_) => Some(Category::Escape),
|
NodeKind::Escape(_) => Some(Category::Escape),
|
||||||
|
NodeKind::Shorthand(_) => Some(Category::Shorthand),
|
||||||
|
NodeKind::SmartQuote { .. } => Some(Category::SmartQuote),
|
||||||
NodeKind::Strong => Some(Category::Strong),
|
NodeKind::Strong => Some(Category::Strong),
|
||||||
NodeKind::Emph => Some(Category::Emph),
|
NodeKind::Emph => Some(Category::Emph),
|
||||||
NodeKind::Link(_) => Some(Category::Link),
|
|
||||||
NodeKind::Raw(_) => Some(Category::Raw),
|
NodeKind::Raw(_) => Some(Category::Raw),
|
||||||
NodeKind::Math => Some(Category::Math),
|
NodeKind::Link(_) => Some(Category::Link),
|
||||||
NodeKind::Atom(_) => None,
|
NodeKind::Label(_) => Some(Category::Label),
|
||||||
NodeKind::Script => None,
|
NodeKind::Ref(_) => Some(Category::Ref),
|
||||||
NodeKind::Frac => None,
|
|
||||||
NodeKind::Align => None,
|
|
||||||
NodeKind::Heading => Some(Category::Heading),
|
NodeKind::Heading => Some(Category::Heading),
|
||||||
NodeKind::ListItem => Some(Category::ListItem),
|
NodeKind::ListItem => Some(Category::ListItem),
|
||||||
NodeKind::EnumItem => Some(Category::ListItem),
|
NodeKind::EnumItem => Some(Category::ListItem),
|
||||||
NodeKind::EnumNumbering(_) => Some(Category::ListMarker),
|
NodeKind::EnumNumbering(_) => Some(Category::ListMarker),
|
||||||
NodeKind::DescItem => Some(Category::ListItem),
|
NodeKind::DescItem => Some(Category::ListItem),
|
||||||
NodeKind::Label(_) => Some(Category::Label),
|
NodeKind::Math => Some(Category::Math),
|
||||||
NodeKind::Ref(_) => Some(Category::Ref),
|
NodeKind::Atom(_) => None,
|
||||||
|
NodeKind::Script => None,
|
||||||
|
NodeKind::Frac => None,
|
||||||
|
NodeKind::Align => None,
|
||||||
|
|
||||||
NodeKind::Ident(_) => match parent.kind() {
|
NodeKind::Ident(_) => match parent.kind() {
|
||||||
NodeKind::Markup { .. } => Some(Category::Interpolated),
|
NodeKind::Markup { .. } => Some(Category::Interpolated),
|
||||||
NodeKind::Math => Some(Category::Interpolated),
|
NodeKind::Math => Some(Category::Interpolated),
|
||||||
NodeKind::FuncCall => Some(Category::Function),
|
NodeKind::FuncCall => Some(Category::Function),
|
||||||
NodeKind::MethodCall if i > 0 => Some(Category::Function),
|
NodeKind::MethodCall if i > 0 => Some(Category::Function),
|
||||||
NodeKind::ClosureExpr if i == 0 => Some(Category::Function),
|
NodeKind::Closure if i == 0 => Some(Category::Function),
|
||||||
NodeKind::SetExpr => Some(Category::Function),
|
NodeKind::SetRule => Some(Category::Function),
|
||||||
NodeKind::ShowExpr
|
NodeKind::ShowRule
|
||||||
if parent
|
if parent
|
||||||
.children()
|
.children()
|
||||||
.rev()
|
.rev()
|
||||||
@ -336,34 +332,34 @@ impl Category {
|
|||||||
NodeKind::Str(_) => Some(Category::String),
|
NodeKind::Str(_) => Some(Category::String),
|
||||||
NodeKind::CodeBlock => None,
|
NodeKind::CodeBlock => None,
|
||||||
NodeKind::ContentBlock => None,
|
NodeKind::ContentBlock => None,
|
||||||
NodeKind::GroupExpr => None,
|
NodeKind::Parenthesized => None,
|
||||||
NodeKind::ArrayExpr => None,
|
NodeKind::Array => None,
|
||||||
NodeKind::DictExpr => None,
|
NodeKind::Dict => None,
|
||||||
NodeKind::Named => None,
|
NodeKind::Named => None,
|
||||||
NodeKind::Keyed => None,
|
NodeKind::Keyed => None,
|
||||||
NodeKind::UnaryExpr => None,
|
NodeKind::Unary => None,
|
||||||
NodeKind::BinaryExpr => None,
|
NodeKind::Binary => None,
|
||||||
NodeKind::FieldAccess => None,
|
NodeKind::FieldAccess => None,
|
||||||
NodeKind::FuncCall => None,
|
NodeKind::FuncCall => None,
|
||||||
NodeKind::MethodCall => None,
|
NodeKind::MethodCall => None,
|
||||||
NodeKind::CallArgs => None,
|
NodeKind::Args => None,
|
||||||
NodeKind::Spread => None,
|
NodeKind::Spread => None,
|
||||||
NodeKind::ClosureExpr => None,
|
NodeKind::Closure => None,
|
||||||
NodeKind::ClosureParams => None,
|
NodeKind::Params => None,
|
||||||
NodeKind::LetExpr => None,
|
NodeKind::LetBinding => None,
|
||||||
NodeKind::SetExpr => None,
|
NodeKind::SetRule => None,
|
||||||
NodeKind::ShowExpr => None,
|
NodeKind::ShowRule => None,
|
||||||
NodeKind::WrapExpr => None,
|
NodeKind::WrapRule => None,
|
||||||
NodeKind::IfExpr => None,
|
NodeKind::Conditional => None,
|
||||||
NodeKind::WhileExpr => None,
|
NodeKind::WhileLoop => None,
|
||||||
NodeKind::ForExpr => None,
|
NodeKind::ForLoop => None,
|
||||||
NodeKind::ForPattern => None,
|
NodeKind::ForPattern => None,
|
||||||
NodeKind::ImportExpr => None,
|
NodeKind::ModuleImport => None,
|
||||||
NodeKind::ImportItems => None,
|
NodeKind::ImportItems => None,
|
||||||
NodeKind::IncludeExpr => None,
|
NodeKind::ModuleInclude => None,
|
||||||
NodeKind::BreakExpr => None,
|
NodeKind::BreakStmt => None,
|
||||||
NodeKind::ContinueExpr => None,
|
NodeKind::ContinueStmt => None,
|
||||||
NodeKind::ReturnExpr => None,
|
NodeKind::ReturnStmt => None,
|
||||||
|
|
||||||
NodeKind::Error(_, _) => Some(Category::Error),
|
NodeKind::Error(_, _) => Some(Category::Error),
|
||||||
}
|
}
|
||||||
@ -376,15 +372,15 @@ impl Category {
|
|||||||
Self::Bracket => "punctuation.definition.bracket.typst",
|
Self::Bracket => "punctuation.definition.bracket.typst",
|
||||||
Self::Punctuation => "punctuation.typst",
|
Self::Punctuation => "punctuation.typst",
|
||||||
Self::Escape => "constant.character.escape.typst",
|
Self::Escape => "constant.character.escape.typst",
|
||||||
Self::Shortcut => "constant.character.shortcut.typst",
|
Self::Shorthand => "constant.character.shorthand.typst",
|
||||||
Self::Quote => "constant.character.quote.typst",
|
Self::SmartQuote => "constant.character.quote.typst",
|
||||||
Self::Strong => "markup.bold.typst",
|
Self::Strong => "markup.bold.typst",
|
||||||
Self::Emph => "markup.italic.typst",
|
Self::Emph => "markup.italic.typst",
|
||||||
Self::Link => "markup.underline.link.typst",
|
Self::Link => "markup.underline.link.typst",
|
||||||
Self::Raw => "markup.raw.typst",
|
Self::Raw => "markup.raw.typst",
|
||||||
Self::Math => "string.other.math.typst",
|
Self::Math => "string.other.math.typst",
|
||||||
Self::MathDelimiter => "punctuation.definition.math.typst",
|
Self::MathDelimiter => "punctuation.definition.math.typst",
|
||||||
Self::MathSymbol => "keyword.operator.math.typst",
|
Self::MathOperator => "keyword.operator.math.typst",
|
||||||
Self::Heading => "markup.heading.typst",
|
Self::Heading => "markup.heading.typst",
|
||||||
Self::ListItem => "markup.list.typst",
|
Self::ListItem => "markup.list.typst",
|
||||||
Self::ListMarker => "punctuation.definition.list.typst",
|
Self::ListMarker => "punctuation.definition.list.typst",
|
||||||
|
@ -1,8 +1,7 @@
|
|||||||
use std::hash::{Hash, Hasher};
|
use std::hash::{Hash, Hasher};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use super::ast::{RawNode, Unit};
|
use crate::geom::{AngleUnit, LengthUnit};
|
||||||
use crate::diag::ErrorPos;
|
|
||||||
use crate::util::EcoString;
|
use crate::util::EcoString;
|
||||||
|
|
||||||
/// All syntactical building blocks that can be part of a Typst document.
|
/// All syntactical building blocks that can be part of a Typst document.
|
||||||
@ -11,13 +10,9 @@ use crate::util::EcoString;
|
|||||||
/// the parser.
|
/// the parser.
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
pub enum NodeKind {
|
pub enum NodeKind {
|
||||||
/// A line comment, two slashes followed by inner contents, terminated with
|
/// A line comment: `// ...`.
|
||||||
/// a newline: `//<str>\n`.
|
|
||||||
LineComment,
|
LineComment,
|
||||||
/// A block comment, a slash and a star followed by inner contents,
|
/// A block comment: `/* ... */`.
|
||||||
/// terminated with a star and a slash: `/*<str>*/`.
|
|
||||||
///
|
|
||||||
/// The comment can contain nested block comments.
|
|
||||||
BlockComment,
|
BlockComment,
|
||||||
/// One or more whitespace characters. Single spaces are collapsed into text
|
/// One or more whitespace characters. Single spaces are collapsed into text
|
||||||
/// nodes if they would otherwise be surrounded by text nodes.
|
/// nodes if they would otherwise be surrounded by text nodes.
|
||||||
@ -43,7 +38,7 @@ pub enum NodeKind {
|
|||||||
Comma,
|
Comma,
|
||||||
/// A semicolon terminating an expression: `;`.
|
/// A semicolon terminating an expression: `;`.
|
||||||
Semicolon,
|
Semicolon,
|
||||||
/// A colon between name / key and value in a dictionary, argument or
|
/// A colon between name/key and value in a dictionary, argument or
|
||||||
/// parameter list, or between the term and body of a description list
|
/// parameter list, or between the term and body of a description list
|
||||||
/// term: `:`.
|
/// term: `:`.
|
||||||
Colon,
|
Colon,
|
||||||
@ -52,22 +47,8 @@ pub enum NodeKind {
|
|||||||
Star,
|
Star,
|
||||||
/// Toggles emphasized text and indicates a subscript in a formula: `_`.
|
/// Toggles emphasized text and indicates a subscript in a formula: `_`.
|
||||||
Underscore,
|
Underscore,
|
||||||
/// Starts and ends a math formula.
|
/// Starts and ends a math formula: `$`.
|
||||||
Dollar,
|
Dollar,
|
||||||
/// A forced line break: `\`.
|
|
||||||
Backslash,
|
|
||||||
/// The non-breaking space: `~`.
|
|
||||||
Tilde,
|
|
||||||
/// The soft hyphen: `-?`.
|
|
||||||
HyphQuest,
|
|
||||||
/// The en-dash: `--`.
|
|
||||||
Hyph2,
|
|
||||||
/// The em-dash: `---`.
|
|
||||||
Hyph3,
|
|
||||||
/// The ellipsis: `...`.
|
|
||||||
Dot3,
|
|
||||||
/// A smart quote: `'` or `"`.
|
|
||||||
Quote { double: bool },
|
|
||||||
/// The unary plus, binary addition operator, and start of enum items: `+`.
|
/// The unary plus, binary addition operator, and start of enum items: `+`.
|
||||||
Plus,
|
Plus,
|
||||||
/// The unary negation, binary subtraction operator, and start of list
|
/// The unary negation, binary subtraction operator, and start of list
|
||||||
@ -160,18 +141,37 @@ pub enum NodeKind {
|
|||||||
Markup { min_indent: usize },
|
Markup { min_indent: usize },
|
||||||
/// Consecutive text without markup.
|
/// Consecutive text without markup.
|
||||||
Text(EcoString),
|
Text(EcoString),
|
||||||
/// A unicode escape sequence, written as a slash and the letter "u"
|
/// A forced line break: `\`.
|
||||||
/// followed by a hexadecimal unicode entity enclosed in curly braces:
|
Linebreak,
|
||||||
/// `\u{1F5FA}`.
|
/// An escape sequence: `\#`, `\u{1F5FA}`.
|
||||||
Escape(char),
|
Escape(char),
|
||||||
/// Strong content: `*Strong*`.
|
/// A shorthand for a unicode codepoint. For example, `~` for non-breaking
|
||||||
|
/// space or `-?` for a soft hyphen.
|
||||||
|
Shorthand(char),
|
||||||
|
/// A smart quote: `'` or `"`.
|
||||||
|
SmartQuote { double: bool },
|
||||||
|
/// Strong markup: `*Strong*`.
|
||||||
Strong,
|
Strong,
|
||||||
/// Emphasized content: `_Emphasized_`.
|
/// Emphasized markup: `_Emphasized_`.
|
||||||
Emph,
|
Emph,
|
||||||
|
/// A raw block with optional syntax highlighting: `` `...` ``.
|
||||||
|
Raw(Arc<RawKind>),
|
||||||
/// A hyperlink: `https://typst.org`.
|
/// A hyperlink: `https://typst.org`.
|
||||||
Link(EcoString),
|
Link(EcoString),
|
||||||
/// A raw block with optional syntax highlighting: `` `...` ``.
|
/// A label: `<label>`.
|
||||||
Raw(Arc<RawNode>),
|
Label(EcoString),
|
||||||
|
/// A reference: `@target`.
|
||||||
|
Ref(EcoString),
|
||||||
|
/// A section heading: `= Introduction`.
|
||||||
|
Heading,
|
||||||
|
/// An item of an unordered list: `- ...`.
|
||||||
|
ListItem,
|
||||||
|
/// An item of an enumeration (ordered list): `+ ...` or `1. ...`.
|
||||||
|
EnumItem,
|
||||||
|
/// An explicit enumeration numbering: `23.`.
|
||||||
|
EnumNumbering(usize),
|
||||||
|
/// An item of a description list: `/ Term: Details.
|
||||||
|
DescItem,
|
||||||
/// A math formula: `$x$`, `$ x^2 $`.
|
/// A math formula: `$x$`, `$ x^2 $`.
|
||||||
Math,
|
Math,
|
||||||
/// An atom in a math formula: `x`, `+`, `12`.
|
/// An atom in a math formula: `x`, `+`, `12`.
|
||||||
@ -182,20 +182,6 @@ pub enum NodeKind {
|
|||||||
Frac,
|
Frac,
|
||||||
/// An alignment indicator in a math formula: `&`, `&&`.
|
/// An alignment indicator in a math formula: `&`, `&&`.
|
||||||
Align,
|
Align,
|
||||||
/// A section heading: `= Introduction`.
|
|
||||||
Heading,
|
|
||||||
/// An item in an unordered list: `- ...`.
|
|
||||||
ListItem,
|
|
||||||
/// An item in an enumeration (ordered list): `+ ...` or `1. ...`.
|
|
||||||
EnumItem,
|
|
||||||
/// An explicit enumeration numbering: `23.`.
|
|
||||||
EnumNumbering(usize),
|
|
||||||
/// An item in a description list: `/ Term: Details.
|
|
||||||
DescItem,
|
|
||||||
/// A label: `<label>`.
|
|
||||||
Label(EcoString),
|
|
||||||
/// A reference: `@label`.
|
|
||||||
Ref(EcoString),
|
|
||||||
|
|
||||||
/// An identifier: `center`.
|
/// An identifier: `center`.
|
||||||
Ident(EcoString),
|
Ident(EcoString),
|
||||||
@ -214,19 +200,19 @@ pub enum NodeKind {
|
|||||||
/// A content block: `[*Hi* there!]`.
|
/// A content block: `[*Hi* there!]`.
|
||||||
ContentBlock,
|
ContentBlock,
|
||||||
/// A grouped expression: `(1 + 2)`.
|
/// A grouped expression: `(1 + 2)`.
|
||||||
GroupExpr,
|
Parenthesized,
|
||||||
/// An array expression: `(1, "hi", 12cm)`.
|
/// An array: `(1, "hi", 12cm)`.
|
||||||
ArrayExpr,
|
Array,
|
||||||
/// A dictionary expression: `(thickness: 3pt, pattern: dashed)`.
|
/// A dictionary: `(thickness: 3pt, pattern: dashed)`.
|
||||||
DictExpr,
|
Dict,
|
||||||
/// A named pair: `thickness: 3pt`.
|
/// A named pair: `thickness: 3pt`.
|
||||||
Named,
|
Named,
|
||||||
/// A keyed pair: `"spacy key": true`.
|
/// A keyed pair: `"spacy key": true`.
|
||||||
Keyed,
|
Keyed,
|
||||||
/// A unary operation: `-x`.
|
/// A unary operation: `-x`.
|
||||||
UnaryExpr,
|
Unary,
|
||||||
/// A binary operation: `a + b`.
|
/// A binary operation: `a + b`.
|
||||||
BinaryExpr,
|
Binary,
|
||||||
/// A field access: `properties.age`.
|
/// A field access: `properties.age`.
|
||||||
FieldAccess,
|
FieldAccess,
|
||||||
/// An invocation of a function: `f(x, y)`.
|
/// An invocation of a function: `f(x, y)`.
|
||||||
@ -234,50 +220,89 @@ pub enum NodeKind {
|
|||||||
/// An invocation of a method: `array.push(v)`.
|
/// An invocation of a method: `array.push(v)`.
|
||||||
MethodCall,
|
MethodCall,
|
||||||
/// A function call's argument list: `(x, y)`.
|
/// A function call's argument list: `(x, y)`.
|
||||||
CallArgs,
|
Args,
|
||||||
/// Spreaded arguments or a argument sink: `..x`.
|
/// Spreaded arguments or a argument sink: `..x`.
|
||||||
Spread,
|
Spread,
|
||||||
/// A closure expression: `(x, y) => z`.
|
/// A closure: `(x, y) => z`.
|
||||||
ClosureExpr,
|
Closure,
|
||||||
/// A closure's parameters: `(x, y)`.
|
/// A closure's parameters: `(x, y)`.
|
||||||
ClosureParams,
|
Params,
|
||||||
/// A let expression: `let x = 1`.
|
/// A let binding: `let x = 1`.
|
||||||
LetExpr,
|
LetBinding,
|
||||||
/// A set expression: `set text(...)`.
|
/// A set rule: `set text(...)`.
|
||||||
SetExpr,
|
SetRule,
|
||||||
/// A show expression: `show node: heading as [*{nody.body}*]`.
|
/// A show rule: `show node: heading as [*{nody.body}*]`.
|
||||||
ShowExpr,
|
ShowRule,
|
||||||
/// A wrap expression: `wrap body in columns(2, body)`.
|
/// A wrap rule: `wrap body in columns(2, body)`.
|
||||||
WrapExpr,
|
WrapRule,
|
||||||
/// An if-else expression: `if x { y } else { z }`.
|
/// An if-else conditional: `if x { y } else { z }`.
|
||||||
IfExpr,
|
Conditional,
|
||||||
/// A while loop expression: `while x { ... }`.
|
/// A while loop: `while x { ... }`.
|
||||||
WhileExpr,
|
WhileLoop,
|
||||||
/// A for loop expression: `for x in y { ... }`.
|
/// A for loop: `for x in y { ... }`.
|
||||||
ForExpr,
|
ForLoop,
|
||||||
/// A for loop's destructuring pattern: `x` or `x, y`.
|
/// A for loop's destructuring pattern: `x` or `x, y`.
|
||||||
ForPattern,
|
ForPattern,
|
||||||
/// An import expression: `import a, b, c from "utils.typ"`.
|
/// A module import: `import a, b, c from "utils.typ"`.
|
||||||
ImportExpr,
|
ModuleImport,
|
||||||
/// Items to import: `a, b, c`.
|
/// Items to import from a module: `a, b, c`.
|
||||||
ImportItems,
|
ImportItems,
|
||||||
/// An include expression: `include "chapter1.typ"`.
|
/// A module include: `include "chapter1.typ"`.
|
||||||
IncludeExpr,
|
ModuleInclude,
|
||||||
/// A break expression: `break`.
|
/// A break statement: `break`.
|
||||||
BreakExpr,
|
BreakStmt,
|
||||||
/// A continue expression: `continue`.
|
/// A continue statement: `continue`.
|
||||||
ContinueExpr,
|
ContinueStmt,
|
||||||
/// A return expression: `return x + 1`.
|
/// A return statement: `return x + 1`.
|
||||||
ReturnExpr,
|
ReturnStmt,
|
||||||
|
|
||||||
/// An invalid sequence of characters.
|
/// An invalid sequence of characters.
|
||||||
Error(ErrorPos, EcoString),
|
Error(ErrorPos, EcoString),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Fields of the node kind `Raw`.
|
||||||
|
#[derive(Debug, Clone, PartialEq, Hash)]
|
||||||
|
pub struct RawKind {
|
||||||
|
/// An optional identifier specifying the language to syntax-highlight in.
|
||||||
|
pub lang: Option<EcoString>,
|
||||||
|
/// The raw text, determined as the raw string between the backticks trimmed
|
||||||
|
/// according to the above rules.
|
||||||
|
pub text: EcoString,
|
||||||
|
/// Whether the element is block-level, that is, it has 3+ backticks
|
||||||
|
/// and contains at least one newline.
|
||||||
|
pub block: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Unit of a numeric value.
|
||||||
|
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
|
||||||
|
pub enum Unit {
|
||||||
|
/// An absolute length unit.
|
||||||
|
Length(LengthUnit),
|
||||||
|
/// An angular unit.
|
||||||
|
Angle(AngleUnit),
|
||||||
|
/// Font-relative: `1em` is the same as the font size.
|
||||||
|
Em,
|
||||||
|
/// Fractions: `fr`.
|
||||||
|
Fr,
|
||||||
|
/// Percentage: `%`.
|
||||||
|
Percent,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Where in a node an error should be annotated,
|
||||||
|
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
|
||||||
|
pub enum ErrorPos {
|
||||||
|
/// Over the full width of the node.
|
||||||
|
Full,
|
||||||
|
/// At the start of the node.
|
||||||
|
Start,
|
||||||
|
/// At the end of the node.
|
||||||
|
End,
|
||||||
|
}
|
||||||
|
|
||||||
impl NodeKind {
|
impl NodeKind {
|
||||||
/// Whether this is a kind of parenthesis.
|
/// Whether this is trivia.
|
||||||
pub fn is_paren(&self) -> bool {
|
pub fn is_trivia(&self) -> bool {
|
||||||
matches!(self, Self::LeftParen | Self::RightParen)
|
self.is_space() || matches!(self, Self::LineComment | Self::BlockComment)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Whether this is a space.
|
/// Whether this is a space.
|
||||||
@ -285,12 +310,12 @@ impl NodeKind {
|
|||||||
matches!(self, Self::Space { .. })
|
matches!(self, Self::Space { .. })
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Whether this is trivia.
|
/// Whether this is a left or right parenthesis.
|
||||||
pub fn is_trivia(&self) -> bool {
|
pub fn is_paren(&self) -> bool {
|
||||||
self.is_space() || matches!(self, Self::LineComment | Self::BlockComment)
|
matches!(self, Self::LeftParen | Self::RightParen)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Whether this is a kind of error.
|
/// Whether this is an error.
|
||||||
pub fn is_error(&self) -> bool {
|
pub fn is_error(&self) -> bool {
|
||||||
matches!(self, NodeKind::Error(_, _))
|
matches!(self, NodeKind::Error(_, _))
|
||||||
}
|
}
|
||||||
@ -313,14 +338,8 @@ impl NodeKind {
|
|||||||
Self::Star => "star",
|
Self::Star => "star",
|
||||||
Self::Underscore => "underscore",
|
Self::Underscore => "underscore",
|
||||||
Self::Dollar => "dollar sign",
|
Self::Dollar => "dollar sign",
|
||||||
Self::Backslash => "linebreak",
|
Self::SmartQuote { double: false } => "single quote",
|
||||||
Self::Tilde => "non-breaking space",
|
Self::SmartQuote { double: true } => "double quote",
|
||||||
Self::HyphQuest => "soft hyphen",
|
|
||||||
Self::Hyph2 => "en dash",
|
|
||||||
Self::Hyph3 => "em dash",
|
|
||||||
Self::Dot3 => "ellipsis",
|
|
||||||
Self::Quote { double: false } => "single quote",
|
|
||||||
Self::Quote { double: true } => "double quote",
|
|
||||||
Self::Plus => "plus",
|
Self::Plus => "plus",
|
||||||
Self::Minus => "minus",
|
Self::Minus => "minus",
|
||||||
Self::Slash => "slash",
|
Self::Slash => "slash",
|
||||||
@ -363,23 +382,25 @@ impl NodeKind {
|
|||||||
Self::As => "keyword `as`",
|
Self::As => "keyword `as`",
|
||||||
Self::Markup { .. } => "markup",
|
Self::Markup { .. } => "markup",
|
||||||
Self::Text(_) => "text",
|
Self::Text(_) => "text",
|
||||||
|
Self::Linebreak => "linebreak",
|
||||||
Self::Escape(_) => "escape sequence",
|
Self::Escape(_) => "escape sequence",
|
||||||
|
Self::Shorthand(_) => "shorthand",
|
||||||
Self::Strong => "strong content",
|
Self::Strong => "strong content",
|
||||||
Self::Emph => "emphasized content",
|
Self::Emph => "emphasized content",
|
||||||
Self::Link(_) => "link",
|
|
||||||
Self::Raw(_) => "raw block",
|
Self::Raw(_) => "raw block",
|
||||||
Self::Math => "math formula",
|
Self::Link(_) => "link",
|
||||||
Self::Atom(_) => "math atom",
|
Self::Label(_) => "label",
|
||||||
Self::Script => "script",
|
Self::Ref(_) => "reference",
|
||||||
Self::Frac => "fraction",
|
|
||||||
Self::Align => "alignment indicator",
|
|
||||||
Self::Heading => "heading",
|
Self::Heading => "heading",
|
||||||
Self::ListItem => "list item",
|
Self::ListItem => "list item",
|
||||||
Self::EnumItem => "enumeration item",
|
Self::EnumItem => "enumeration item",
|
||||||
Self::EnumNumbering(_) => "enumeration item numbering",
|
Self::EnumNumbering(_) => "enumeration item numbering",
|
||||||
Self::DescItem => "description list item",
|
Self::DescItem => "description list item",
|
||||||
Self::Label(_) => "label",
|
Self::Math => "math formula",
|
||||||
Self::Ref(_) => "reference",
|
Self::Atom(_) => "math atom",
|
||||||
|
Self::Script => "script",
|
||||||
|
Self::Frac => "fraction",
|
||||||
|
Self::Align => "alignment indicator",
|
||||||
Self::Ident(_) => "identifier",
|
Self::Ident(_) => "identifier",
|
||||||
Self::Bool(_) => "boolean",
|
Self::Bool(_) => "boolean",
|
||||||
Self::Int(_) => "integer",
|
Self::Int(_) => "integer",
|
||||||
@ -388,34 +409,34 @@ impl NodeKind {
|
|||||||
Self::Str(_) => "string",
|
Self::Str(_) => "string",
|
||||||
Self::CodeBlock => "code block",
|
Self::CodeBlock => "code block",
|
||||||
Self::ContentBlock => "content block",
|
Self::ContentBlock => "content block",
|
||||||
Self::GroupExpr => "group",
|
Self::Parenthesized => "group",
|
||||||
Self::ArrayExpr => "array",
|
Self::Array => "array",
|
||||||
Self::DictExpr => "dictionary",
|
Self::Dict => "dictionary",
|
||||||
Self::Named => "named pair",
|
Self::Named => "named pair",
|
||||||
Self::Keyed => "keyed pair",
|
Self::Keyed => "keyed pair",
|
||||||
Self::UnaryExpr => "unary expression",
|
Self::Unary => "unary expression",
|
||||||
Self::BinaryExpr => "binary expression",
|
Self::Binary => "binary expression",
|
||||||
Self::FieldAccess => "field access",
|
Self::FieldAccess => "field access",
|
||||||
Self::FuncCall => "function call",
|
Self::FuncCall => "function call",
|
||||||
Self::MethodCall => "method call",
|
Self::MethodCall => "method call",
|
||||||
Self::CallArgs => "call arguments",
|
Self::Args => "call arguments",
|
||||||
Self::Spread => "spread",
|
Self::Spread => "spread",
|
||||||
Self::ClosureExpr => "closure",
|
Self::Closure => "closure",
|
||||||
Self::ClosureParams => "closure parameters",
|
Self::Params => "closure parameters",
|
||||||
Self::LetExpr => "`let` expression",
|
Self::LetBinding => "`let` expression",
|
||||||
Self::SetExpr => "`set` expression",
|
Self::SetRule => "`set` expression",
|
||||||
Self::ShowExpr => "`show` expression",
|
Self::ShowRule => "`show` expression",
|
||||||
Self::WrapExpr => "`wrap` expression",
|
Self::WrapRule => "`wrap` expression",
|
||||||
Self::IfExpr => "`if` expression",
|
Self::Conditional => "`if` expression",
|
||||||
Self::WhileExpr => "while-loop expression",
|
Self::WhileLoop => "while-loop expression",
|
||||||
Self::ForExpr => "for-loop expression",
|
Self::ForLoop => "for-loop expression",
|
||||||
Self::ForPattern => "for-loop destructuring pattern",
|
Self::ForPattern => "for-loop destructuring pattern",
|
||||||
Self::ImportExpr => "`import` expression",
|
Self::ModuleImport => "`import` expression",
|
||||||
Self::ImportItems => "import items",
|
Self::ImportItems => "import items",
|
||||||
Self::IncludeExpr => "`include` expression",
|
Self::ModuleInclude => "`include` expression",
|
||||||
Self::BreakExpr => "`break` expression",
|
Self::BreakStmt => "`break` expression",
|
||||||
Self::ContinueExpr => "`continue` expression",
|
Self::ContinueStmt => "`continue` expression",
|
||||||
Self::ReturnExpr => "`return` expression",
|
Self::ReturnStmt => "`return` expression",
|
||||||
Self::Error(_, _) => "syntax error",
|
Self::Error(_, _) => "syntax error",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -440,13 +461,6 @@ impl Hash for NodeKind {
|
|||||||
Self::Star => {}
|
Self::Star => {}
|
||||||
Self::Underscore => {}
|
Self::Underscore => {}
|
||||||
Self::Dollar => {}
|
Self::Dollar => {}
|
||||||
Self::Backslash => {}
|
|
||||||
Self::Tilde => {}
|
|
||||||
Self::HyphQuest => {}
|
|
||||||
Self::Hyph2 => {}
|
|
||||||
Self::Hyph3 => {}
|
|
||||||
Self::Dot3 => {}
|
|
||||||
Self::Quote { double } => double.hash(state),
|
|
||||||
Self::Plus => {}
|
Self::Plus => {}
|
||||||
Self::Minus => {}
|
Self::Minus => {}
|
||||||
Self::Slash => {}
|
Self::Slash => {}
|
||||||
@ -489,23 +503,26 @@ impl Hash for NodeKind {
|
|||||||
Self::As => {}
|
Self::As => {}
|
||||||
Self::Markup { min_indent } => min_indent.hash(state),
|
Self::Markup { min_indent } => min_indent.hash(state),
|
||||||
Self::Text(s) => s.hash(state),
|
Self::Text(s) => s.hash(state),
|
||||||
|
Self::Linebreak => {}
|
||||||
Self::Escape(c) => c.hash(state),
|
Self::Escape(c) => c.hash(state),
|
||||||
|
Self::Shorthand(c) => c.hash(state),
|
||||||
|
Self::SmartQuote { double } => double.hash(state),
|
||||||
Self::Strong => {}
|
Self::Strong => {}
|
||||||
Self::Emph => {}
|
Self::Emph => {}
|
||||||
Self::Link(link) => link.hash(state),
|
|
||||||
Self::Raw(raw) => raw.hash(state),
|
Self::Raw(raw) => raw.hash(state),
|
||||||
Self::Math => {}
|
Self::Link(link) => link.hash(state),
|
||||||
Self::Atom(c) => c.hash(state),
|
Self::Label(c) => c.hash(state),
|
||||||
Self::Script => {}
|
Self::Ref(c) => c.hash(state),
|
||||||
Self::Frac => {}
|
|
||||||
Self::Align => {}
|
|
||||||
Self::Heading => {}
|
Self::Heading => {}
|
||||||
Self::ListItem => {}
|
Self::ListItem => {}
|
||||||
Self::EnumItem => {}
|
Self::EnumItem => {}
|
||||||
Self::EnumNumbering(num) => num.hash(state),
|
Self::EnumNumbering(num) => num.hash(state),
|
||||||
Self::DescItem => {}
|
Self::DescItem => {}
|
||||||
Self::Label(c) => c.hash(state),
|
Self::Math => {}
|
||||||
Self::Ref(c) => c.hash(state),
|
Self::Atom(c) => c.hash(state),
|
||||||
|
Self::Script => {}
|
||||||
|
Self::Frac => {}
|
||||||
|
Self::Align => {}
|
||||||
Self::Ident(v) => v.hash(state),
|
Self::Ident(v) => v.hash(state),
|
||||||
Self::Bool(v) => v.hash(state),
|
Self::Bool(v) => v.hash(state),
|
||||||
Self::Int(v) => v.hash(state),
|
Self::Int(v) => v.hash(state),
|
||||||
@ -514,34 +531,34 @@ impl Hash for NodeKind {
|
|||||||
Self::Str(v) => v.hash(state),
|
Self::Str(v) => v.hash(state),
|
||||||
Self::CodeBlock => {}
|
Self::CodeBlock => {}
|
||||||
Self::ContentBlock => {}
|
Self::ContentBlock => {}
|
||||||
Self::GroupExpr => {}
|
Self::Parenthesized => {}
|
||||||
Self::ArrayExpr => {}
|
Self::Array => {}
|
||||||
Self::DictExpr => {}
|
Self::Dict => {}
|
||||||
Self::Named => {}
|
Self::Named => {}
|
||||||
Self::Keyed => {}
|
Self::Keyed => {}
|
||||||
Self::UnaryExpr => {}
|
Self::Unary => {}
|
||||||
Self::BinaryExpr => {}
|
Self::Binary => {}
|
||||||
Self::FieldAccess => {}
|
Self::FieldAccess => {}
|
||||||
Self::FuncCall => {}
|
Self::FuncCall => {}
|
||||||
Self::MethodCall => {}
|
Self::MethodCall => {}
|
||||||
Self::CallArgs => {}
|
Self::Args => {}
|
||||||
Self::Spread => {}
|
Self::Spread => {}
|
||||||
Self::ClosureExpr => {}
|
Self::Closure => {}
|
||||||
Self::ClosureParams => {}
|
Self::Params => {}
|
||||||
Self::LetExpr => {}
|
Self::LetBinding => {}
|
||||||
Self::SetExpr => {}
|
Self::SetRule => {}
|
||||||
Self::ShowExpr => {}
|
Self::ShowRule => {}
|
||||||
Self::WrapExpr => {}
|
Self::WrapRule => {}
|
||||||
Self::IfExpr => {}
|
Self::Conditional => {}
|
||||||
Self::WhileExpr => {}
|
Self::WhileLoop => {}
|
||||||
Self::ForExpr => {}
|
Self::ForLoop => {}
|
||||||
Self::ForPattern => {}
|
Self::ForPattern => {}
|
||||||
Self::ImportExpr => {}
|
Self::ModuleImport => {}
|
||||||
Self::ImportItems => {}
|
Self::ImportItems => {}
|
||||||
Self::IncludeExpr => {}
|
Self::ModuleInclude => {}
|
||||||
Self::BreakExpr => {}
|
Self::BreakStmt => {}
|
||||||
Self::ContinueExpr => {}
|
Self::ContinueStmt => {}
|
||||||
Self::ReturnExpr => {}
|
Self::ReturnStmt => {}
|
||||||
Self::Error(pos, msg) => (pos, msg).hash(state),
|
Self::Error(pos, msg) => (pos, msg).hash(state),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user