java-commons/muscript/src/main/java/io/gitlab/jfronny/muscript/ast/dynamic/Call.java

126 lines
4.2 KiB
Java

package io.gitlab.jfronny.muscript.ast.dynamic;
import io.gitlab.jfronny.muscript.ast.DynamicExpr;
import io.gitlab.jfronny.muscript.annotations.CanThrow;
import io.gitlab.jfronny.muscript.annotations.UncheckedDynamic;
import io.gitlab.jfronny.muscript.ast.Expr;
import io.gitlab.jfronny.muscript.compiler.*;
import io.gitlab.jfronny.muscript.data.Scope;
import io.gitlab.jfronny.muscript.data.dynamic.*;
import io.gitlab.jfronny.muscript.data.dynamic.additional.DFinal;
import io.gitlab.jfronny.muscript.error.LocationalException;
import io.gitlab.jfronny.muscript.error.StackFrame;
import java.io.IOException;
import java.util.*;
import java.util.stream.Stream;
@CanThrow
@UncheckedDynamic
public class Call extends DynamicExpr {
public final DynamicExpr left;
public final List<Arg> args;
public Call(CodeLocation location, DynamicExpr left, List<Arg> args) {
super(Order.Call, location);
this.left = left;
this.args = args;
}
@Override
public Dynamic get(Scope dataRoot) {
DCallable dc;
DList arg;
try {
Dynamic lv = left.get(dataRoot);
if (Dynamic.isNull(lv)) throw new LocationalException(location, "Cannot invoke null");
dc = lv.asCallable();
arg = DFinal.of(args.stream().flatMap(e -> e.get(dataRoot)).toArray(Dynamic[]::new));
} catch (DynamicTypeConversionException e) {
throw e.locational(location);
} catch (RuntimeException e) {
throw new LocationalException(location, "Could not perform call successfully", e);
}
try {
return dc.call(arg);
} catch (LocationalException le) {
throw le.appendStack(new StackFrame.Raw(location.file(), dc.getName(), left.location.chStart()));
} catch (DynamicTypeConversionException e) {
throw e.locational(location);
} catch (RuntimeException e) {
throw new LocationalException(location, e.getMessage(), e);
}
}
@Override
public DynamicExpr optimize() {
List<Arg> args = new ArrayList<>();
DynamicExpr left = this.left.optimize();
if (left instanceof Bind bind) {
left = bind.callable;
args.add(new Arg(bind.parameter, false));
}
for (Arg arg : this.args) args.add(arg.optimize());
if (left instanceof Closure closure) {
return new ExprGroup(
closure.location,
closure.stream().toList(),
new ExprGroup.PackedArgs(
args,
closure.boundArgs,
closure.variadic
)
);
}
return new Call(location, left, args);
}
@Override
public Stream<Expr<?>> extractSideEffects() {
return Stream.of(this);
}
@Override
public void decompile(ExprWriter writer) throws IOException {
parenthesize(left, writer, false);
if (args.size() > 3) {
writer.increaseIndent();
writer.append("(\n");
boolean first = true;
for (Arg arg : args) {
if (!first) writer.append(",\n");
first = false;
arg.expr.decompile(writer);
if (arg.variadic) writer.append("...");
}
writer.decreaseIndent();
writer.append("\n)");
} else {
writer.append('(');
boolean first = true;
for (Arg arg : args) {
if (!first) writer.append(", ");
first = false;
arg.expr.decompile(writer);
if (arg.variadic) writer.append("...");
}
writer.append(')');
}
}
@Override
public boolean equals(Object obj) {
return obj instanceof Call call && left.equals(call.left) && args.equals(call.args);
}
public record Arg(DynamicExpr expr, boolean variadic) {
public Stream<? extends Dynamic> get(Scope dataRoot) {
return variadic ? expr.get(dataRoot).asList().getValue().stream() : Stream.of(expr.get(dataRoot));
}
public Arg optimize() {
return new Arg(expr.optimize(), variadic);
}
}
}