mirror of
https://github.com/typst/typst
synced 2025-05-14 04:56:26 +08:00
Remove captured expression 🗑️
This commit is contained in:
parent
062d99f70f
commit
e35bbfffcb
@ -3,17 +3,27 @@ use std::rc::Rc;
|
|||||||
use super::*;
|
use super::*;
|
||||||
use crate::syntax::visit::*;
|
use crate::syntax::visit::*;
|
||||||
|
|
||||||
/// A visitor that replaces all captured variables with their values.
|
/// A visitor that captures variable slots.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct CapturesVisitor<'a> {
|
pub struct CapturesVisitor<'a> {
|
||||||
external: &'a Scopes<'a>,
|
external: &'a Scopes<'a>,
|
||||||
internal: Scopes<'a>,
|
internal: Scopes<'a>,
|
||||||
|
captures: Scope,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> CapturesVisitor<'a> {
|
impl<'a> CapturesVisitor<'a> {
|
||||||
/// Create a new visitor for the given external scopes.
|
/// Create a new visitor for the given external scopes.
|
||||||
pub fn new(external: &'a Scopes) -> Self {
|
pub fn new(external: &'a Scopes) -> Self {
|
||||||
Self { external, internal: Scopes::default() }
|
Self {
|
||||||
|
external,
|
||||||
|
internal: Scopes::default(),
|
||||||
|
captures: Scope::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return the scope of capture variables.
|
||||||
|
pub fn finish(self) -> Scope {
|
||||||
|
self.captures
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Define an internal variable.
|
/// Define an internal variable.
|
||||||
@ -23,14 +33,14 @@ impl<'a> CapturesVisitor<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'ast> Visit<'ast> for CapturesVisitor<'_> {
|
impl<'ast> Visit<'ast> for CapturesVisitor<'_> {
|
||||||
fn visit_expr(&mut self, item: &'ast mut Expr) {
|
fn visit_expr(&mut self, item: &'ast Expr) {
|
||||||
match item {
|
match item {
|
||||||
Expr::Ident(ident) => {
|
Expr::Ident(ident) => {
|
||||||
// Find out whether the identifier is not locally defined, but
|
// Find out whether the identifier is not locally defined, but
|
||||||
// captured, and if so, replace it with its value.
|
// captured, and if so, replace it with its value.
|
||||||
if self.internal.get(ident).is_none() {
|
if self.internal.get(ident).is_none() {
|
||||||
if let Some(value) = self.external.get(ident) {
|
if let Some(slot) = self.external.get(ident) {
|
||||||
*item = Expr::Captured(Rc::clone(&value));
|
self.captures.def_slot(ident.as_str(), Rc::clone(slot));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -38,7 +48,7 @@ impl<'ast> Visit<'ast> for CapturesVisitor<'_> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn visit_block(&mut self, item: &'ast mut ExprBlock) {
|
fn visit_block(&mut self, item: &'ast ExprBlock) {
|
||||||
// Blocks create a scope except if directly in a template.
|
// Blocks create a scope except if directly in a template.
|
||||||
if item.scopes {
|
if item.scopes {
|
||||||
self.internal.push();
|
self.internal.push();
|
||||||
@ -49,20 +59,20 @@ impl<'ast> Visit<'ast> for CapturesVisitor<'_> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn visit_template(&mut self, item: &'ast mut ExprTemplate) {
|
fn visit_template(&mut self, item: &'ast ExprTemplate) {
|
||||||
// Templates always create a scope.
|
// Templates always create a scope.
|
||||||
self.internal.push();
|
self.internal.push();
|
||||||
visit_template(self, item);
|
visit_template(self, item);
|
||||||
self.internal.pop();
|
self.internal.pop();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn visit_let(&mut self, item: &'ast mut ExprLet) {
|
fn visit_let(&mut self, item: &'ast ExprLet) {
|
||||||
self.define(&item.pat.v);
|
self.define(&item.pat.v);
|
||||||
visit_let(self, item);
|
visit_let(self, item);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn visit_for(&mut self, item: &'ast mut ExprFor) {
|
fn visit_for(&mut self, item: &'ast ExprFor) {
|
||||||
match &mut item.pat.v {
|
match &item.pat.v {
|
||||||
ForPattern::Value(value) => self.define(value),
|
ForPattern::Value(value) => self.define(value),
|
||||||
ForPattern::KeyValue(key, value) => {
|
ForPattern::KeyValue(key, value) => {
|
||||||
self.define(key);
|
self.define(key);
|
||||||
|
@ -194,7 +194,6 @@ impl Eval for Spanned<&Expr> {
|
|||||||
Expr::Let(v) => v.with_span(self.span).eval(ctx),
|
Expr::Let(v) => v.with_span(self.span).eval(ctx),
|
||||||
Expr::If(v) => v.with_span(self.span).eval(ctx),
|
Expr::If(v) => v.with_span(self.span).eval(ctx),
|
||||||
Expr::For(v) => v.with_span(self.span).eval(ctx),
|
Expr::For(v) => v.with_span(self.span).eval(ctx),
|
||||||
Expr::Captured(v) => v.borrow().clone(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -344,21 +343,17 @@ impl Spanned<&ExprBinary> {
|
|||||||
let rhs = self.v.rhs.eval(ctx);
|
let rhs = self.v.rhs.eval(ctx);
|
||||||
let span = self.v.lhs.span;
|
let span = self.v.lhs.span;
|
||||||
|
|
||||||
let slot = match &self.v.lhs.v {
|
let slot = if let Expr::Ident(id) = &self.v.lhs.v {
|
||||||
Expr::Ident(id) => match ctx.scopes.get(id) {
|
match ctx.scopes.get(id) {
|
||||||
Some(slot) => slot,
|
Some(slot) => slot,
|
||||||
None => {
|
None => {
|
||||||
ctx.diag(error!(span, "unknown variable"));
|
ctx.diag(error!(span, "unknown variable"));
|
||||||
return Value::Error;
|
return Value::Error;
|
||||||
}
|
}
|
||||||
},
|
|
||||||
|
|
||||||
Expr::Captured(slot) => slot,
|
|
||||||
|
|
||||||
_ => {
|
|
||||||
ctx.diag(error!(span, "cannot assign to this expression"));
|
|
||||||
return Value::Error;
|
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
ctx.diag(error!(span, "cannot assign to this expression"));
|
||||||
|
return Value::Error;
|
||||||
};
|
};
|
||||||
|
|
||||||
let (constant, err, value) = if let Ok(mut inner) = slot.try_borrow_mut() {
|
let (constant, err, value) = if let Ok(mut inner) = slot.try_borrow_mut() {
|
||||||
|
@ -86,6 +86,11 @@ impl Scope {
|
|||||||
self.values.insert(var.into(), Rc::new(RefCell::new(value.into())));
|
self.values.insert(var.into(), Rc::new(RefCell::new(value.into())));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Define a variable with a slot.
|
||||||
|
pub fn def_slot(&mut self, var: impl Into<String>, slot: Slot) {
|
||||||
|
self.values.insert(var.into(), slot);
|
||||||
|
}
|
||||||
|
|
||||||
/// Look up the value of a variable.
|
/// Look up the value of a variable.
|
||||||
pub fn get(&self, var: &str) -> Option<&Slot> {
|
pub fn get(&self, var: &str) -> Option<&Slot> {
|
||||||
self.values.get(var)
|
self.values.get(var)
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
use super::*;
|
use super::*;
|
||||||
use crate::color::RgbaColor;
|
use crate::color::RgbaColor;
|
||||||
use crate::eval::Slot;
|
|
||||||
use crate::geom::{AngularUnit, LengthUnit};
|
use crate::geom::{AngularUnit, LengthUnit};
|
||||||
|
|
||||||
/// An expression.
|
/// An expression.
|
||||||
@ -51,11 +50,6 @@ pub enum Expr {
|
|||||||
If(ExprIf),
|
If(ExprIf),
|
||||||
/// A for expression: `#for x #in y { z }`.
|
/// A for expression: `#for x #in y { z }`.
|
||||||
For(ExprFor),
|
For(ExprFor),
|
||||||
/// A captured variable slot.
|
|
||||||
///
|
|
||||||
/// This node is never created by parsing. It only results from an in-place
|
|
||||||
/// transformation of an identifier to a captured variable.
|
|
||||||
Captured(Slot),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Pretty for Expr {
|
impl Pretty for Expr {
|
||||||
@ -92,7 +86,6 @@ impl Pretty for Expr {
|
|||||||
Self::Let(v) => v.pretty(p),
|
Self::Let(v) => v.pretty(p),
|
||||||
Self::If(v) => v.pretty(p),
|
Self::If(v) => v.pretty(p),
|
||||||
Self::For(v) => v.pretty(p),
|
Self::For(v) => v.pretty(p),
|
||||||
Self::Captured(v) => v.borrow().pretty(p),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,17 +3,17 @@
|
|||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
macro_rules! visit {
|
macro_rules! visit {
|
||||||
($(fn $name:ident($v:ident, $item:ident: &mut $ty:ty) $body:block)*) => {
|
($(fn $name:ident($v:ident, $item:ident: &$ty:ty) $body:block)*) => {
|
||||||
/// Traverses the syntax tree.
|
/// Traverses the syntax tree.
|
||||||
pub trait Visit<'ast> {
|
pub trait Visit<'ast> {
|
||||||
$(fn $name(&mut self, $item: &'ast mut $ty) {
|
$(fn $name(&mut self, $item: &'ast $ty) {
|
||||||
$name(self, $item);
|
$name(self, $item);
|
||||||
})*
|
})*
|
||||||
}
|
}
|
||||||
|
|
||||||
$(visit! {
|
$(visit! {
|
||||||
@concat!("Walk a node of type [`", stringify!($ty), "`]."),
|
@concat!("Walk a node of type [`", stringify!($ty), "`]."),
|
||||||
pub fn $name<'ast, V>($v: &mut V, $item: &'ast mut $ty)
|
pub fn $name<'ast, V>($v: &mut V, $item: &'ast $ty)
|
||||||
where
|
where
|
||||||
V: Visit<'ast> + ?Sized
|
V: Visit<'ast> + ?Sized
|
||||||
$body
|
$body
|
||||||
@ -27,13 +27,13 @@ macro_rules! visit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
visit! {
|
visit! {
|
||||||
fn visit_tree(v, item: &mut Tree) {
|
fn visit_tree(v, item: &Tree) {
|
||||||
for node in item {
|
for node in item {
|
||||||
v.visit_node(&mut node.v);
|
v.visit_node(&node.v);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn visit_node(v, item: &mut Node) {
|
fn visit_node(v, item: &Node) {
|
||||||
match item {
|
match item {
|
||||||
Node::Strong => {}
|
Node::Strong => {}
|
||||||
Node::Emph => {}
|
Node::Emph => {}
|
||||||
@ -41,13 +41,13 @@ visit! {
|
|||||||
Node::Linebreak => {}
|
Node::Linebreak => {}
|
||||||
Node::Parbreak => {}
|
Node::Parbreak => {}
|
||||||
Node::Text(_) => {}
|
Node::Text(_) => {}
|
||||||
Node::Heading(n) => v.visit_tree(&mut n.contents),
|
Node::Heading(n) => v.visit_tree(&n.contents),
|
||||||
Node::Raw(_) => {}
|
Node::Raw(_) => {}
|
||||||
Node::Expr(expr) => v.visit_expr(expr),
|
Node::Expr(expr) => v.visit_expr(expr),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn visit_expr(v, item: &mut Expr) {
|
fn visit_expr(v, item: &Expr) {
|
||||||
match item {
|
match item {
|
||||||
Expr::None => {}
|
Expr::None => {}
|
||||||
Expr::Ident(_) => {}
|
Expr::Ident(_) => {}
|
||||||
@ -70,79 +70,78 @@ visit! {
|
|||||||
Expr::Let(e) => v.visit_let(e),
|
Expr::Let(e) => v.visit_let(e),
|
||||||
Expr::If(e) => v.visit_if(e),
|
Expr::If(e) => v.visit_if(e),
|
||||||
Expr::For(e) => v.visit_for(e),
|
Expr::For(e) => v.visit_for(e),
|
||||||
Expr::Captured(_) => {}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn visit_array(v, item: &mut ExprArray) {
|
fn visit_array(v, item: &ExprArray) {
|
||||||
for expr in item {
|
for expr in item {
|
||||||
v.visit_expr(&mut expr.v);
|
v.visit_expr(&expr.v);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn visit_dict(v, item: &mut ExprDict) {
|
fn visit_dict(v, item: &ExprDict) {
|
||||||
for named in item {
|
for named in item {
|
||||||
v.visit_expr(&mut named.expr.v);
|
v.visit_expr(&named.expr.v);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn visit_template(v, item: &mut ExprTemplate) {
|
fn visit_template(v, item: &ExprTemplate) {
|
||||||
v.visit_tree(item);
|
v.visit_tree(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn visit_group(v, item: &mut ExprGroup) {
|
fn visit_group(v, item: &ExprGroup) {
|
||||||
v.visit_expr(&mut item.v);
|
v.visit_expr(&item.v);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn visit_block(v, item: &mut ExprBlock) {
|
fn visit_block(v, item: &ExprBlock) {
|
||||||
for expr in &mut item.exprs {
|
for expr in &item.exprs {
|
||||||
v.visit_expr(&mut expr.v);
|
v.visit_expr(&expr.v);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn visit_binary(v, item: &mut ExprBinary) {
|
fn visit_binary(v, item: &ExprBinary) {
|
||||||
v.visit_expr(&mut item.lhs.v);
|
v.visit_expr(&item.lhs.v);
|
||||||
v.visit_expr(&mut item.rhs.v);
|
v.visit_expr(&item.rhs.v);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn visit_unary(v, item: &mut ExprUnary) {
|
fn visit_unary(v, item: &ExprUnary) {
|
||||||
v.visit_expr(&mut item.expr.v);
|
v.visit_expr(&item.expr.v);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn visit_call(v, item: &mut ExprCall) {
|
fn visit_call(v, item: &ExprCall) {
|
||||||
v.visit_expr(&mut item.callee.v);
|
v.visit_expr(&item.callee.v);
|
||||||
v.visit_args(&mut item.args.v);
|
v.visit_args(&item.args.v);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn visit_args(v, item: &mut ExprArgs) {
|
fn visit_args(v, item: &ExprArgs) {
|
||||||
for arg in item {
|
for arg in item {
|
||||||
v.visit_arg(arg);
|
v.visit_arg(arg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn visit_arg(v, item: &mut Argument) {
|
fn visit_arg(v, item: &Argument) {
|
||||||
match item {
|
match item {
|
||||||
Argument::Pos(expr) => v.visit_expr(&mut expr.v),
|
Argument::Pos(expr) => v.visit_expr(&expr.v),
|
||||||
Argument::Named(named) => v.visit_expr(&mut named.expr.v),
|
Argument::Named(named) => v.visit_expr(&named.expr.v),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn visit_let(v, item: &mut ExprLet) {
|
fn visit_let(v, item: &ExprLet) {
|
||||||
if let Some(init) = &mut item.init {
|
if let Some(init) = &item.init {
|
||||||
v.visit_expr(&mut init.v);
|
v.visit_expr(&init.v);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn visit_if(v, item: &mut ExprIf) {
|
fn visit_if(v, item: &ExprIf) {
|
||||||
v.visit_expr(&mut item.condition.v);
|
v.visit_expr(&item.condition.v);
|
||||||
v.visit_expr(&mut item.if_body.v);
|
v.visit_expr(&item.if_body.v);
|
||||||
if let Some(body) = &mut item.else_body {
|
if let Some(body) = &item.else_body {
|
||||||
v.visit_expr(&mut body.v);
|
v.visit_expr(&body.v);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn visit_for(v, item: &mut ExprFor) {
|
fn visit_for(v, item: &ExprFor) {
|
||||||
v.visit_expr(&mut item.iter.v);
|
v.visit_expr(&item.iter.v);
|
||||||
v.visit_expr(&mut item.body.v);
|
v.visit_expr(&item.body.v);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user