Make AST borrowed
This commit is contained in:
parent
16855121b6
commit
5fb5854ed8
File diff suppressed because it is too large
Load Diff
@ -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()
|
||||
}
|
||||
|
@ -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
@ -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, ¶m);
|
||||
named_param_value_completions(ctx, callee, ¶m);
|
||||
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,
|
||||
});
|
||||
|
Loading…
Reference in New Issue
Block a user