Make AST borrowed

This commit is contained in:
Laurenz 2023-08-19 19:44:08 +02:00
parent 16855121b6
commit 5fb5854ed8
5 changed files with 702 additions and 671 deletions

File diff suppressed because it is too large Load Diff

View File

@ -105,22 +105,22 @@ impl SyntaxNode {
}
/// Whether the node can be cast to the given AST node.
pub fn is<T: AstNode>(&self) -> bool {
pub fn is<'a, T: AstNode<'a>>(&'a self) -> bool {
self.cast::<T>().is_some()
}
/// Try to convert the node to a typed AST node.
pub fn cast<T: AstNode>(&self) -> Option<T> {
pub fn cast<'a, T: AstNode<'a>>(&'a self) -> Option<T> {
T::from_untyped(self)
}
/// Cast the first child that can cast to the AST type `T`.
pub fn cast_first_match<T: AstNode>(&self) -> Option<T> {
pub fn cast_first_match<'a, T: AstNode<'a>>(&'a self) -> Option<T> {
self.children().find_map(Self::cast)
}
/// Cast the last child that can cast to the AST type `T`.
pub fn cast_last_match<T: AstNode>(&self) -> Option<T> {
pub fn cast_last_match<'a, T: AstNode<'a>>(&'a self) -> Option<T> {
self.children().rev().find_map(Self::cast)
}
@ -273,6 +273,17 @@ impl SyntaxNode {
Repr::Error(node) => node.error.span.number() + 1,
}
}
/// An arbitrary node just for filling a slot in memory.
///
/// In contrast to `default()`, this is a const fn.
pub(super) const fn arbitrary() -> Self {
Self(Repr::Leaf(LeafNode {
kind: SyntaxKind::Eof,
text: EcoString::new(),
span: Span::detached(),
}))
}
}
impl Debug for SyntaxNode {
@ -287,7 +298,7 @@ impl Debug for SyntaxNode {
impl Default for SyntaxNode {
fn default() -> Self {
Self::error("", "")
Self::arbitrary()
}
}
@ -802,6 +813,8 @@ impl<'a> LinkedNode<'a> {
impl Deref for LinkedNode<'_> {
type Target = SyntaxNode;
/// Dereference to a syntax node. Note that this shortens the lifetime, so
/// you may need to use [`get()`](Self::get) instead in some situations.
fn deref(&self) -> &Self::Target {
self.get()
}

View File

@ -12,7 +12,7 @@ use super::{
};
use crate::diag::{bail, SourceResult, StrResult};
use crate::model::{DelayedErrors, ElemFunc, Introspector, Locator, Vt};
use crate::syntax::ast::{self, AstNode, Expr, Ident};
use crate::syntax::ast::{self, AstNode};
use crate::syntax::{FileId, Span, SyntaxNode};
use crate::World;
@ -45,7 +45,7 @@ impl Func {
match &self.repr {
Repr::Native(native) => Some(native.info.name),
Repr::Elem(func) => Some(func.info().name),
Repr::Closure(closure) => closure.name.as_deref(),
Repr::Closure(closure) => closure.name(),
Repr::With(arc) => arc.0.name(),
}
}
@ -295,36 +295,32 @@ pub struct ParamInfo {
/// A user-defined closure.
#[derive(Hash)]
pub(super) struct Closure {
/// The closure's syntax node. Must be castable to `ast::Closure`.
pub node: SyntaxNode,
/// The source file where the closure was defined.
pub location: FileId,
/// The name of the closure.
pub name: Option<Ident>,
/// Default values of named parameters.
pub defaults: Vec<Value>,
/// Captured values from outer scopes.
pub captured: Scope,
/// The list of parameters.
pub params: Vec<Param>,
/// The expression the closure should evaluate to.
pub body: Expr,
}
/// A closure parameter.
#[derive(Hash)]
pub enum Param {
/// A positional parameter: `x`.
Pos(ast::Pattern),
/// A named parameter with a default value: `draw: false`.
Named(Ident, Value),
/// An argument sink: `..args`.
Sink(Option<Ident>),
}
impl Closure {
/// The name of the closure.
pub fn name(&self) -> Option<&str> {
self.node
.cast::<ast::Closure>()
.unwrap()
.name()
.map(|ident| ident.as_str())
}
/// Call the function in the context with the arguments.
#[comemo::memoize]
#[tracing::instrument(skip_all)]
#[allow(clippy::too_many_arguments)]
fn call(
this: &Func,
func: &Func,
world: Tracked<dyn World + '_>,
route: Tracked<Route>,
introspector: Tracked<Introspector>,
@ -334,15 +330,15 @@ impl Closure {
depth: usize,
mut args: Args,
) -> SourceResult<Value> {
let closure = match &this.repr {
Repr::Closure(closure) => closure,
_ => panic!("`this` must be a closure"),
let Repr::Closure(this) = &func.repr else {
panic!("`this` must be a closure");
};
let closure = this.node.cast::<ast::Closure>().unwrap();
// Don't leak the scopes from the call site. Instead, we use the scope
// of captured variables we collected earlier.
let mut scopes = Scopes::new(None);
scopes.top = closure.captured.clone();
scopes.top = this.captured.clone();
// Prepare VT.
let mut locator = Locator::chained(locator);
@ -355,30 +351,34 @@ impl Closure {
};
// Prepare VM.
let mut vm = Vm::new(vt, route, closure.location, scopes);
let mut vm = Vm::new(vt, route, this.location, scopes);
vm.depth = depth;
// Provide the closure itself for recursive calls.
if let Some(name) = &closure.name {
vm.define(name.clone(), Value::Func(this.clone()));
if let Some(name) = closure.name() {
vm.define(name, Value::Func(func.clone()));
}
// Parse the arguments according to the parameter list.
let num_pos_params =
closure.params.iter().filter(|p| matches!(p, Param::Pos(_))).count();
let num_pos_params = closure
.params()
.children()
.filter(|p| matches!(p, ast::Param::Pos(_)))
.count();
let num_pos_args = args.to_pos().len();
let sink_size = num_pos_args.checked_sub(num_pos_params);
let mut sink = None;
let mut sink_pos_values = None;
for p in &closure.params {
let mut defaults = this.defaults.iter();
for p in closure.params().children() {
match p {
Param::Pos(pattern) => match pattern {
ast::Param::Pos(pattern) => match pattern {
ast::Pattern::Normal(ast::Expr::Ident(ident)) => {
vm.define(ident.clone(), args.expect::<Value>(ident)?)
vm.define(ident, args.expect::<Value>(&ident)?)
}
ast::Pattern::Normal(_) => unreachable!(),
_ => {
pattern => {
super::define_pattern(
&mut vm,
pattern,
@ -386,16 +386,18 @@ impl Closure {
)?;
}
},
Param::Sink(ident) => {
sink = ident.clone();
ast::Param::Sink(ident) => {
sink = ident.name();
if let Some(sink_size) = sink_size {
sink_pos_values = Some(args.consume(sink_size)?);
}
}
Param::Named(ident, default) => {
ast::Param::Named(named) => {
let name = named.name();
let default = defaults.next().unwrap();
let value =
args.named::<Value>(ident)?.unwrap_or_else(|| default.clone());
vm.define(ident.clone(), value);
args.named::<Value>(&name)?.unwrap_or_else(|| default.clone());
vm.define(name, value);
}
}
}
@ -412,7 +414,7 @@ impl Closure {
args.finish()?;
// Handle control flow.
let result = closure.body.eval(&mut vm);
let result = closure.body().eval(&mut vm);
match vm.flow {
Some(FlowEvent::Return(_, Some(explicit))) => return Ok(explicit),
Some(FlowEvent::Return(_, None)) => {}
@ -483,7 +485,7 @@ impl<'a> CapturesVisitor<'a> {
Some(ast::Expr::Closure(expr)) => {
for param in expr.params().children() {
if let ast::Param::Named(named) = param {
self.visit(named.expr().as_untyped());
self.visit(named.expr().to_untyped());
}
}
@ -506,7 +508,7 @@ impl<'a> CapturesVisitor<'a> {
}
}
self.visit(expr.body().as_untyped());
self.visit(expr.body().to_untyped());
self.internal.exit();
}
@ -514,7 +516,7 @@ impl<'a> CapturesVisitor<'a> {
// active after the body is evaluated.
Some(ast::Expr::Let(expr)) => {
if let Some(init) = expr.init() {
self.visit(init.as_untyped());
self.visit(init.to_untyped());
}
for ident in expr.kind().idents() {
@ -526,7 +528,7 @@ impl<'a> CapturesVisitor<'a> {
// active after the iterable is evaluated but before the body is
// evaluated.
Some(ast::Expr::For(expr)) => {
self.visit(expr.iter().as_untyped());
self.visit(expr.iter().to_untyped());
self.internal.enter();
let pattern = expr.pattern();
@ -534,16 +536,16 @@ impl<'a> CapturesVisitor<'a> {
self.bind(ident);
}
self.visit(expr.body().as_untyped());
self.visit(expr.body().to_untyped());
self.internal.exit();
}
// An import contains items, but these are active only after the
// path is evaluated.
Some(ast::Expr::Import(expr)) => {
self.visit(expr.source().as_untyped());
self.visit(expr.source().to_untyped());
if let Some(ast::Imports::Items(items)) = expr.imports() {
for item in items {
for item in items.idents() {
self.bind(item);
}
}
@ -560,14 +562,14 @@ impl<'a> CapturesVisitor<'a> {
/// Bind a new internal variable.
fn bind(&mut self, ident: ast::Ident) {
self.internal.top.define(ident.take(), Value::None);
self.internal.top.define(ident.get().clone(), Value::None);
}
/// Capture a variable if it isn't internal.
fn capture(&mut self, ident: ast::Ident) {
if self.internal.get(&ident).is_err() {
if let Ok(value) = self.external.get(&ident) {
self.captures.define_captured(ident.take(), value.clone());
self.captures.define_captured(ident.get().clone(), value.clone());
}
}
}
@ -576,7 +578,7 @@ impl<'a> CapturesVisitor<'a> {
fn capture_in_math(&mut self, ident: ast::MathIdent) {
if self.internal.get(&ident).is_err() {
if let Ok(value) = self.external.get_in_math(&ident) {
self.captures.define_captured(ident.take(), value.clone());
self.captures.define_captured(ident.get().clone(), value.clone());
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -442,13 +442,13 @@ fn complete_imports(ctx: &mut CompletionContext) -> bool {
// "#import "path.typ": a, b, |".
if_chain! {
if let Some(prev) = ctx.leaf.prev_sibling();
if let Some(ast::Expr::Import(import)) = prev.cast();
if let Some(ast::Expr::Import(import)) = prev.get().cast();
if let Some(ast::Imports::Items(items)) = import.imports();
if let Some(source) = prev.children().find(|child| child.is::<ast::Expr>());
if let Some(value) = analyze_expr(ctx.world, &source).into_iter().next();
then {
ctx.from = ctx.cursor;
import_item_completions(ctx, &items, &value);
import_item_completions(ctx, items, &value);
return true;
}
}
@ -460,13 +460,13 @@ fn complete_imports(ctx: &mut CompletionContext) -> bool {
if let Some(parent) = ctx.leaf.parent();
if parent.kind() == SyntaxKind::ImportItems;
if let Some(grand) = parent.parent();
if let Some(ast::Expr::Import(import)) = grand.cast();
if let Some(ast::Expr::Import(import)) = grand.get().cast();
if let Some(ast::Imports::Items(items)) = import.imports();
if let Some(source) = grand.children().find(|child| child.is::<ast::Expr>());
if let Some(value) = analyze_expr(ctx.world, &source).into_iter().next();
then {
ctx.from = ctx.leaf.offset();
import_item_completions(ctx, &items, &value);
import_item_completions(ctx, items, &value);
return true;
}
}
@ -475,9 +475,9 @@ fn complete_imports(ctx: &mut CompletionContext) -> bool {
}
/// Add completions for all exports of a module.
fn import_item_completions(
ctx: &mut CompletionContext,
existing: &[ast::Ident],
fn import_item_completions<'a>(
ctx: &mut CompletionContext<'a>,
existing: ast::ImportItems<'a>,
value: &Value,
) {
let module = match value {
@ -489,12 +489,12 @@ fn import_item_completions(
_ => return,
};
if existing.is_empty() {
if existing.idents().next().is_none() {
ctx.snippet_completion("*", "*", "Import everything.");
}
for (name, value) in module.scope().iter() {
if existing.iter().all(|ident| ident.as_str() != name) {
if existing.idents().all(|ident| ident.as_str() != name) {
ctx.value_completion(Some(name.clone()), value, false, None);
}
}
@ -604,9 +604,9 @@ fn complete_params(ctx: &mut CompletionContext) -> bool {
SyntaxKind::Named => parent.parent(),
_ => Some(parent),
};
if let Some(args) = parent.cast::<ast::Args>();
if let Some(args) = parent.get().cast::<ast::Args>();
if let Some(grand) = parent.parent();
if let Some(expr) = grand.cast::<ast::Expr>();
if let Some(expr) = grand.get().cast::<ast::Expr>();
let set = matches!(expr, ast::Expr::Set(_));
if let Some(callee) = match expr {
ast::Expr::FuncCall(call) => Some(call.callee()),
@ -634,13 +634,13 @@ fn complete_params(ctx: &mut CompletionContext) -> bool {
if_chain! {
if deciding.kind() == SyntaxKind::Colon;
if let Some(prev) = deciding.prev_leaf();
if let Some(param) = prev.cast::<ast::Ident>();
if let Some(param) = prev.get().cast::<ast::Ident>();
then {
if let Some(next) = deciding.next_leaf() {
ctx.from = ctx.cursor.min(next.offset());
}
named_param_value_completions(ctx, &callee, &param);
named_param_value_completions(ctx, callee, &param);
return true;
}
}
@ -655,12 +655,15 @@ fn complete_params(ctx: &mut CompletionContext) -> bool {
}
// Exclude arguments which are already present.
let exclude: Vec<_> = args.items().filter_map(|arg| match arg {
ast::Arg::Named(named) => Some(named.name()),
_ => None,
}).collect();
let exclude: Vec<_> = args
.items()
.filter_map(|arg| match arg {
ast::Arg::Named(named) => Some(named.name()),
_ => None,
})
.collect();
param_completions(ctx, &callee, set, &exclude);
param_completions(ctx, callee, set, &exclude);
return true;
}
}
@ -669,11 +672,11 @@ fn complete_params(ctx: &mut CompletionContext) -> bool {
}
/// Add completions for the parameters of a function.
fn param_completions(
ctx: &mut CompletionContext,
callee: &ast::Expr,
fn param_completions<'a>(
ctx: &mut CompletionContext<'a>,
callee: ast::Expr<'a>,
set: bool,
exclude: &[ast::Ident],
exclude: &[ast::Ident<'a>],
) {
let Some(func) = resolve_global_callee(ctx, callee) else { return };
let Some(info) = func.info() else { return };
@ -707,9 +710,9 @@ fn param_completions(
}
/// Add completions for the values of a named function parameter.
fn named_param_value_completions(
ctx: &mut CompletionContext,
callee: &ast::Expr,
fn named_param_value_completions<'a>(
ctx: &mut CompletionContext<'a>,
callee: ast::Expr<'a>,
name: &str,
) {
let Some(func) = resolve_global_callee(ctx, callee) else { return };
@ -732,10 +735,10 @@ fn named_param_value_completions(
/// Resolve a callee expression to a global function.
fn resolve_global_callee<'a>(
ctx: &CompletionContext<'a>,
callee: &ast::Expr,
callee: ast::Expr<'a>,
) -> Option<&'a Func> {
let value = match callee {
ast::Expr::Ident(ident) => ctx.global.get(ident)?,
ast::Expr::Ident(ident) => ctx.global.get(&ident)?,
ast::Expr::FieldAccess(access) => match access.target() {
ast::Expr::Ident(target) => match ctx.global.get(&target)? {
Value::Module(module) => module.get(&access.field()).ok()?,
@ -1189,20 +1192,20 @@ impl<'a> CompletionContext<'a> {
while let Some(node) = &ancestor {
let mut sibling = Some(node.clone());
while let Some(node) = &sibling {
if let Some(v) = node.cast::<ast::LetBinding>() {
if let Some(v) = node.get().cast::<ast::LetBinding>() {
for ident in v.kind().idents() {
defined.insert(ident.take());
defined.insert(ident.get());
}
}
sibling = node.prev_sibling();
}
if let Some(parent) = node.parent() {
if let Some(v) = parent.cast::<ast::ForLoop>() {
if let Some(v) = parent.get().cast::<ast::ForLoop>() {
if node.prev_sibling_kind() != Some(SyntaxKind::In) {
let pattern = v.pattern();
for ident in pattern.idents() {
defined.insert(ident.take());
defined.insert(ident.get());
}
}
}
@ -1233,7 +1236,7 @@ impl<'a> CompletionContext<'a> {
if !name.is_empty() {
self.completions.push(Completion {
kind: CompletionKind::Constant,
label: name,
label: name.clone(),
apply: None,
detail: None,
});