Remove method call syntax kind

This commit is contained in:
Laurenz 2023-01-28 18:32:58 +01:00
parent 2d56e3c5e2
commit 406de22ee5
12 changed files with 223 additions and 199 deletions

View File

@ -7,9 +7,7 @@ use crate::World;
/// Try to determine a set of possible values for an expression.
pub fn analyze(world: &(dyn World + 'static), node: &LinkedNode) -> Vec<Value> {
match node.cast::<ast::Expr>() {
Some(
ast::Expr::Ident(_) | ast::Expr::MathIdent(_) | ast::Expr::MethodCall(_),
) => {
Some(ast::Expr::Ident(_) | ast::Expr::MathIdent(_) | ast::Expr::FuncCall(_)) => {
if let Some(parent) = node.parent() {
if parent.kind() == SyntaxKind::FieldAccess && node.index() > 0 {
return analyze(world, parent);

View File

@ -4,7 +4,7 @@ use if_chain::if_chain;
use super::{analyze, plain_docs_sentence, summarize_font_family};
use crate::model::{methods_on, CastInfo, Scope, Value};
use crate::syntax::{ast, LinkedNode, Source, SyntaxKind, SyntaxNode};
use crate::syntax::{ast, LinkedNode, Source, SyntaxKind};
use crate::util::{format_eco, EcoString};
use crate::World;
@ -936,9 +936,7 @@ impl<'a> CompletionContext<'a> {
if let Some(parent) = node.parent() {
if let Some(v) = parent.cast::<ast::ForLoop>() {
if node.prev_sibling().as_deref().map(SyntaxNode::kind)
!= Some(SyntaxKind::In)
{
if node.prev_sibling_kind() != Some(SyntaxKind::In) {
let pattern = v.pattern();
if let Some(key) = pattern.key() {
defined.insert(key.take());

View File

@ -85,8 +85,7 @@ pub fn highlight(node: &LinkedNode) -> Option<Category> {
match node.kind() {
SyntaxKind::Markup
if node.parent_kind() == Some(SyntaxKind::TermItem)
&& node.next_sibling().as_ref().map(|v| v.kind())
== Some(SyntaxKind::Colon) =>
&& node.next_sibling_kind() == Some(SyntaxKind::Colon) =>
{
Some(Category::ListTerm)
}
@ -116,17 +115,12 @@ pub fn highlight(node: &LinkedNode) -> Option<Category> {
SyntaxKind::Math => None,
SyntaxKind::MathIdent => highlight_ident(node),
SyntaxKind::MathAlignPoint => Some(Category::MathOperator),
SyntaxKind::MathDelimited => None,
SyntaxKind::MathAttach => None,
SyntaxKind::MathFrac => None,
SyntaxKind::MathAlignPoint => Some(Category::MathOperator),
SyntaxKind::Hashtag => node
.next_sibling()
.filter(|node| node.cast::<ast::Expr>().map_or(false, |e| e.hashtag()))
.and_then(|node| node.leftmost_leaf())
.and_then(|node| highlight(&node)),
SyntaxKind::Hashtag => highlight_hashtag(node),
SyntaxKind::LeftBrace => Some(Category::Punctuation),
SyntaxKind::RightBrace => Some(Category::Punctuation),
SyntaxKind::LeftBracket => Some(Category::Punctuation),
@ -206,18 +200,8 @@ pub fn highlight(node: &LinkedNode) -> Option<Category> {
SyntaxKind::Keyed => None,
SyntaxKind::Unary => None,
SyntaxKind::Binary => None,
SyntaxKind::FieldAccess => match node.parent_kind() {
Some(
SyntaxKind::Markup
| SyntaxKind::Math
| SyntaxKind::MathFrac
| SyntaxKind::MathAttach,
) => Some(Category::Interpolated),
Some(SyntaxKind::FieldAccess) => node.parent().and_then(highlight),
_ => None,
},
SyntaxKind::FieldAccess => None,
SyntaxKind::FuncCall => None,
SyntaxKind::MethodCall => None,
SyntaxKind::Args => None,
SyntaxKind::Spread => None,
SyntaxKind::Closure => None,
@ -245,49 +229,60 @@ pub fn highlight(node: &LinkedNode) -> Option<Category> {
/// Highlight an identifier based on context.
fn highlight_ident(node: &LinkedNode) -> Option<Category> {
match node.parent_kind() {
Some(SyntaxKind::FuncCall) => Some(Category::Function),
Some(SyntaxKind::FieldAccess)
if node.parent().and_then(|p| p.parent_kind())
== Some(SyntaxKind::SetRule)
&& node.next_sibling().is_none() =>
{
Some(Category::Function)
}
Some(SyntaxKind::FieldAccess)
if node
.parent()
.and_then(|p| p.parent())
.filter(|gp| gp.kind() == SyntaxKind::Parenthesized)
.and_then(|gp| gp.parent())
.map_or(false, |ggp| ggp.kind() == SyntaxKind::FuncCall)
&& node.next_sibling().is_none() =>
{
Some(Category::Function)
}
Some(SyntaxKind::FieldAccess) => node.parent().and_then(highlight),
Some(SyntaxKind::MethodCall) if node.prev_sibling().is_some() => {
Some(Category::Function)
}
Some(SyntaxKind::Closure) if node.prev_sibling().is_none() => {
Some(Category::Function)
}
Some(SyntaxKind::SetRule) => Some(Category::Function),
Some(SyntaxKind::ShowRule)
if node.prev_sibling().as_ref().map(|v| v.kind())
== Some(SyntaxKind::Show) =>
{
Some(Category::Function)
}
Some(
SyntaxKind::Markup
| SyntaxKind::Math
| SyntaxKind::MathFrac
| SyntaxKind::MathAttach,
) => Some(Category::Interpolated),
_ if node.kind() == SyntaxKind::MathIdent => Some(Category::Interpolated),
_ => None,
// Are we directly before an argument list?
let next_leaf_kind = node.next_leaf().map(|leaf| leaf.kind());
if matches!(next_leaf_kind, Some(SyntaxKind::LeftParen | SyntaxKind::LeftBracket)) {
return Some(Category::Function);
}
// Are we in math?
if node.kind() == SyntaxKind::MathIdent {
return Some(Category::Interpolated);
}
// Find the first non-field access ancestor.
let mut ancestor = node;
while ancestor.parent_kind() == Some(SyntaxKind::FieldAccess) {
ancestor = ancestor.parent()?;
}
// Are we directly before a show rule colon?
if next_leaf_kind == Some(SyntaxKind::Colon)
&& ancestor.parent_kind() == Some(SyntaxKind::ShowRule)
{
return Some(Category::Function);
}
// Are we (or an ancestor field access) directly after a hashtag.
if ancestor.prev_leaf().map(|leaf| leaf.kind()) == Some(SyntaxKind::Hashtag) {
return Some(Category::Interpolated);
}
// Are we behind a dot, that is behind another identifier?
let prev = node.prev_leaf()?;
if prev.kind() == SyntaxKind::Dot {
let prev_prev = prev.prev_leaf()?;
if is_ident(&prev_prev) {
return highlight_ident(&prev_prev);
}
}
None
}
/// Highlight a hashtag based on context.
fn highlight_hashtag(node: &LinkedNode) -> Option<Category> {
let next = node.next_sibling()?;
let expr = next.cast::<ast::Expr>()?;
if !expr.hashtag() {
return None;
}
highlight(&next.leftmost_leaf()?)
}
/// Whether the node is one of the two identifier nodes.
fn is_ident(node: &LinkedNode) -> bool {
matches!(node.kind(), SyntaxKind::Ident | SyntaxKind::MathIdent)
}
#[cfg(test)]

View File

@ -364,7 +364,6 @@ impl Eval for ast::Expr {
Self::Parenthesized(v) => v.eval(vm),
Self::FieldAccess(v) => v.eval(vm),
Self::FuncCall(v) => v.eval(vm),
Self::MethodCall(v) => v.eval(vm),
Self::Closure(v) => v.eval(vm),
Self::Unary(v) => v.eval(vm),
Self::Binary(v) => v.eval(vm),
@ -918,12 +917,51 @@ impl Eval for ast::FuncCall {
type Output = Value;
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
let callee_expr = self.callee();
let callee_span = callee_expr.span();
let callee = callee_expr.eval(vm)?;
let mut args = self.args().eval(vm)?;
let span = self.span();
let callee = self.callee();
let in_math = in_math(&callee);
let callee_span = callee.span();
let args = self.args();
if in_math(&callee_expr) && !matches!(callee, Value::Func(_)) {
// Try to evaluate as a method call. This is possible if the callee is a
// field access and does not evaluate to a module.
let (callee, mut args) = if let ast::Expr::FieldAccess(access) = callee {
let target = access.target();
let method = access.field();
let method_span = method.span();
let method = method.take();
let point = || Tracepoint::Call(Some(method.clone()));
if methods::is_mutating(&method) {
let args = args.eval(vm)?;
let value = target.access(vm)?;
let value = if let Value::Module(module) = &value {
module.get(&method).cloned().at(method_span)?
} else {
return methods::call_mut(value, &method, args, span)
.trace(vm.world, point, span);
};
(value, args)
} else {
let target = target.eval(vm)?;
let args = args.eval(vm)?;
let value = if let Value::Module(module) = &target {
module.get(&method).cloned().at(method_span)?
} else {
return methods::call(vm, target, &method, args, span)
.trace(vm.world, point, span);
};
(value, args)
}
} else {
(callee.eval(vm)?, args.eval(vm)?)
};
// Handle math special cases for non-functions:
// Combining accent symbols apply themselves while everything else
// simply displays the arguments verbatim.
if in_math && !matches!(callee, Value::Func(_)) {
if let Value::Symbol(sym) = &callee {
let c = sym.get();
if let Some(accent) = combining_accent(c) {
@ -932,7 +970,6 @@ impl Eval for ast::FuncCall {
return Ok(Value::Content((vm.items.math_accent)(base, accent)));
}
}
let mut body = (vm.items.text)('('.into());
for (i, arg) in args.all::<Content>()?.into_iter().enumerate() {
if i > 0 {
@ -944,8 +981,14 @@ impl Eval for ast::FuncCall {
return Ok(Value::Content(callee.display() + body));
}
// Finally, just a normal function call!
if vm.depth >= MAX_CALL_DEPTH {
bail!(span, "maximum function call depth exceeded");
}
let callee = callee.cast::<Func>().at(callee_span)?;
complete_call(vm, &callee, args, self.span())
let point = || Tracepoint::Call(callee.name().map(Into::into));
callee.call(vm, args).trace(vm.world, point, span)
}
}
@ -957,59 +1000,6 @@ fn in_math(expr: &ast::Expr) -> bool {
}
}
fn complete_call(
vm: &mut Vm,
callee: &Func,
args: Args,
span: Span,
) -> SourceResult<Value> {
if vm.depth >= MAX_CALL_DEPTH {
bail!(span, "maximum function call depth exceeded");
}
let point = || Tracepoint::Call(callee.name().map(Into::into));
callee.call(vm, args).trace(vm.world, point, span)
}
impl Eval for ast::MethodCall {
type Output = Value;
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
let span = self.span();
let method = self.method();
let result = if methods::is_mutating(&method) {
let args = self.args().eval(vm)?;
let value = self.target().access(vm)?;
if let Value::Module(module) = &value {
if let Value::Func(callee) =
module.get(&method).cloned().at(method.span())?
{
return complete_call(vm, &callee, args, self.span());
}
}
methods::call_mut(value, &method, args, span)
} else {
let value = self.target().eval(vm)?;
let args = self.args().eval(vm)?;
if let Value::Module(module) = &value {
if let Value::Func(callee) = module.get(&method).at(method.span())? {
return complete_call(vm, callee, args, self.span());
}
}
methods::call(vm, value, &method, args, span)
};
let method = method.take();
let point = || Tracepoint::Call(Some(method.clone()));
result.trace(vm.world, point, span)
}
}
impl Eval for ast::Args {
type Output = Args;
@ -1223,8 +1213,12 @@ impl Eval for ast::WhileLoop {
fn is_invariant(expr: &SyntaxNode) -> bool {
match expr.cast() {
Some(ast::Expr::Ident(_)) => false,
Some(ast::Expr::MethodCall(call)) => {
is_invariant(call.target().as_untyped())
Some(ast::Expr::MathIdent(_)) => false,
Some(ast::Expr::FieldAccess(access)) => {
is_invariant(access.target().as_untyped())
}
Some(ast::Expr::FuncCall(call)) => {
is_invariant(call.callee().as_untyped())
&& is_invariant(call.args().as_untyped())
}
_ => expr.children().all(is_invariant),
@ -1434,7 +1428,7 @@ impl Access for ast::Expr {
Self::Ident(v) => v.access(vm),
Self::Parenthesized(v) => v.access(vm),
Self::FieldAccess(v) => v.access(vm),
Self::MethodCall(v) => v.access(vm),
Self::FuncCall(v) => v.access(vm),
_ => {
let _ = self.eval(vm)?;
bail!(self.span(), "cannot mutate a temporary value");
@ -1479,22 +1473,22 @@ impl ast::FieldAccess {
}
}
impl Access for ast::MethodCall {
impl Access for ast::FuncCall {
fn access<'a>(&self, vm: &'a mut Vm) -> SourceResult<&'a mut Value> {
let span = self.span();
let method = self.method().take();
let world = vm.world();
if !methods::is_accessor(&method) {
let _ = self.eval(vm)?;
bail!(span, "cannot mutate a temporary value");
if let ast::Expr::FieldAccess(access) = self.callee() {
let method = access.field().take();
if methods::is_accessor(&method) {
let span = self.span();
let world = vm.world();
let args = self.args().eval(vm)?;
let value = access.target().access(vm)?;
let result = methods::call_access(value, &method, args, span);
let point = || Tracepoint::Call(Some(method.clone()));
return result.trace(world, point, span);
}
}
let args = self.args().eval(vm)?;
let value = self.target().access(vm)?;
let result = methods::call_access(value, &method, args, span);
let point = || Tracepoint::Call(Some(method.clone()));
result.trace(world, point, span)
let _ = self.eval(vm)?;
bail!(self.span(), "cannot mutate a temporary value");
}
}

View File

@ -157,10 +157,8 @@ pub enum Expr {
Binary(Binary),
/// A field access: `properties.age`.
FieldAccess(FieldAccess),
/// An invocation of a function: `f(x, y)`.
/// An invocation of a function or method: `f(x, y)`.
FuncCall(FuncCall),
/// An invocation of a method: `array.push(v)`.
MethodCall(MethodCall),
/// A closure: `(x, y) => z`.
Closure(Closure),
/// A let binding: `let x = 1`.
@ -239,7 +237,6 @@ impl AstNode for Expr {
SyntaxKind::Binary => node.cast().map(Self::Binary),
SyntaxKind::FieldAccess => node.cast().map(Self::FieldAccess),
SyntaxKind::FuncCall => node.cast().map(Self::FuncCall),
SyntaxKind::MethodCall => node.cast().map(Self::MethodCall),
SyntaxKind::Closure => node.cast().map(Self::Closure),
SyntaxKind::LetBinding => node.cast().map(Self::Let),
SyntaxKind::SetRule => node.cast().map(Self::Set),
@ -299,7 +296,6 @@ impl AstNode for Expr {
Self::Binary(v) => v.as_untyped(),
Self::FieldAccess(v) => v.as_untyped(),
Self::FuncCall(v) => v.as_untyped(),
Self::MethodCall(v) => v.as_untyped(),
Self::Closure(v) => v.as_untyped(),
Self::Let(v) => v.as_untyped(),
Self::Set(v) => v.as_untyped(),
@ -335,7 +331,6 @@ impl Expr {
Self::Parenthesized(_) => true,
Self::FieldAccess(_) => true,
Self::FuncCall(_) => true,
Self::MethodCall(_) => true,
Self::Let(_) => true,
Self::Set(_) => true,
Self::Show(_) => true,
@ -1403,7 +1398,7 @@ impl FieldAccess {
}
node! {
/// An invocation of a function: `f(x, y)`.
/// An invocation of a function or method: `f(x, y)`.
FuncCall
}
@ -1419,28 +1414,6 @@ impl FuncCall {
}
}
node! {
/// An invocation of a method: `array.push(v)`.
MethodCall
}
impl MethodCall {
/// The expression to call the method on.
pub fn target(&self) -> Expr {
self.0.cast_first_match().unwrap_or_default()
}
/// The name of the method.
pub fn method(&self) -> Ident {
self.0.cast_last_match().unwrap_or_default()
}
/// The arguments to the method.
pub fn args(&self) -> Args {
self.0.cast_last_match().unwrap_or_default()
}
}
node! {
/// A function call's argument list: `(12pt, y)`.
Args

View File

@ -210,10 +210,8 @@ pub enum SyntaxKind {
Binary,
/// A field access: `properties.age`.
FieldAccess,
/// An invocation of a function: `f(x, y)`.
/// An invocation of a function or method: `f(x, y)`.
FuncCall,
/// An invocation of a method: `array.push(v)`.
MethodCall,
/// A function call's argument list: `(12pt, y)`.
Args,
/// Spreaded arguments or an argument sink: `..x`.
@ -416,7 +414,6 @@ impl SyntaxKind {
Self::Binary => "binary expression",
Self::FieldAccess => "field access",
Self::FuncCall => "function call",
Self::MethodCall => "method call",
Self::Args => "call arguments",
Self::Spread => "spread",
Self::Closure => "closure",

View File

@ -681,11 +681,6 @@ impl<'a> LinkedNode<'a> {
self.parent.as_deref()
}
/// Get the kind of this node's parent.
pub fn parent_kind(&self) -> Option<SyntaxKind> {
Some(self.parent()?.node.kind())
}
/// Get the first previous non-trivia sibling node.
pub fn prev_sibling(&self) -> Option<Self> {
let parent = self.parent()?;
@ -713,6 +708,21 @@ impl<'a> LinkedNode<'a> {
Some(next)
}
}
/// Get the kind of this node's parent.
pub fn parent_kind(&self) -> Option<SyntaxKind> {
Some(self.parent()?.node.kind())
}
/// Get the kind of this node's first previous non-trivia sibling.
pub fn prev_sibling_kind(&self) -> Option<SyntaxKind> {
Some(self.prev_sibling()?.node.kind())
}
/// Get the kind of this node's next non-trivia sibling.
pub fn next_sibling_kind(&self) -> Option<SyntaxKind> {
Some(self.next_sibling()?.node.kind())
}
}
/// Access to leafs.

View File

@ -374,12 +374,12 @@ fn math_op(kind: SyntaxKind) -> Option<(SyntaxKind, SyntaxKind, ast::Assoc, usiz
}
fn math_args(p: &mut Parser) {
p.assert(SyntaxKind::Text);
let m = p.marker();
let mut arg = p.marker();
p.convert(SyntaxKind::LeftParen);
let mut namable = true;
let mut named = None;
let mut arg = p.marker();
while !p.eof() && !p.at(SyntaxKind::Dollar) {
if namable
@ -418,11 +418,14 @@ fn math_args(p: &mut Parser) {
maybe_wrap_in_math(p, arg, named);
}
p.wrap(m, SyntaxKind::Args);
if !p.eat_if(SyntaxKind::Text) {
if p.at(SyntaxKind::Text) && p.current_text() == ")" {
p.convert(SyntaxKind::RightParen);
} else {
p.expected("closing paren");
p.balanced = false;
}
p.wrap(m, SyntaxKind::Args);
}
fn maybe_wrap_in_math(p: &mut Parser, arg: Marker, named: Option<Marker>) {
@ -512,14 +515,7 @@ fn code_expr_prec(p: &mut Parser, atomic: bool, min_prec: usize) {
if p.eat_if(SyntaxKind::Dot) {
p.expect(SyntaxKind::Ident);
if p.directly_at(SyntaxKind::LeftParen)
|| p.directly_at(SyntaxKind::LeftBracket)
{
args(p);
p.wrap(m, SyntaxKind::MethodCall);
} else {
p.wrap(m, SyntaxKind::FieldAccess)
}
p.wrap(m, SyntaxKind::FieldAccess);
continue;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 134 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 53 KiB

After

Width:  |  Height:  |  Size: 53 KiB

View File

@ -0,0 +1,63 @@
#set page(width: auto)
```typ
#set hello()
#set hello()
#set hello.world()
#set hello.my.world()
#show heading: func
#show module.func: func
#show module.func: it => {}
#foo(ident: ident)
#hello
#hello()
#hello.world
#hello.world()
#hello().world()
#hello.my.world
#hello.my.world()
#hello.my().world
#hello.my().world()
$ hello $
$ hello() $
$ hello.world $
$ hello.world() $
$ hello().world() $
$ hello.my.world $
$ hello.my.world() $
$ hello.my().world $
$ hello.my().world() $
$ emph(hello) $
$ emph(hello()) $
$ emph(hello.world) $
$ emph(hello.world()) $
$ emph(hello().world()) $
$ emph(hello.my.world) $
$ emph(hello.my.world()) $
$ emph(hello.my().world) $
$ emph(hello.my().world()) $
$ #hello $
$ #hello() $
$ #hello.world $
$ #hello.world() $
$ #hello().world() $
$ #hello.my.world $
$ #hello.my.world() $
$ #hello.my().world $
$ #hello.my().world() $
#{ hello }
#{ hello() }
#{ hello.world }
#{ hello.world() }
#{ hello().world() }
#{ hello.my.world }
#{ hello.my.world() }
#{ hello.my().world }
#{ hello.my().world() }
```

View File

@ -23,5 +23,5 @@ $ f(x, y) := cases(
#set math.vec(delim: "%")
---
// Error: 9-12 missing argument: lower index
// Error: 8-13 missing argument: lower index
$ binom(x^2) $