implements PR-039
This commit is contained in:
parent
0728637f58
commit
4e2cad60cb
@ -3,6 +3,7 @@ package p.studio.compiler.pbs;
|
|||||||
import p.studio.compiler.models.IRFunction;
|
import p.studio.compiler.models.IRFunction;
|
||||||
import p.studio.compiler.models.SourceKind;
|
import p.studio.compiler.models.SourceKind;
|
||||||
import p.studio.compiler.models.IRBackendFile;
|
import p.studio.compiler.models.IRBackendFile;
|
||||||
|
import p.studio.compiler.models.IRBackendExecutableFunction;
|
||||||
import p.studio.compiler.messages.HostAdmissionContext;
|
import p.studio.compiler.messages.HostAdmissionContext;
|
||||||
import p.studio.compiler.models.IRReservedMetadata;
|
import p.studio.compiler.models.IRReservedMetadata;
|
||||||
import p.studio.compiler.pbs.ast.PbsAst;
|
import p.studio.compiler.pbs.ast.PbsAst;
|
||||||
@ -16,6 +17,9 @@ import p.studio.compiler.source.identifiers.FileId;
|
|||||||
import p.studio.utilities.structures.ReadOnlyList;
|
import p.studio.utilities.structures.ReadOnlyList;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
public final class PbsFrontendCompiler {
|
public final class PbsFrontendCompiler {
|
||||||
private final PbsDeclarationSemanticsValidator declarationSemanticsValidator = new PbsDeclarationSemanticsValidator();
|
private final PbsDeclarationSemanticsValidator declarationSemanticsValidator = new PbsDeclarationSemanticsValidator();
|
||||||
@ -61,7 +65,7 @@ public final class PbsFrontendCompiler {
|
|||||||
final FileId fileId,
|
final FileId fileId,
|
||||||
final PbsAst.File ast,
|
final PbsAst.File ast,
|
||||||
final DiagnosticSink diagnostics) {
|
final DiagnosticSink diagnostics) {
|
||||||
return compileParsedFile(fileId, ast, diagnostics, SourceKind.PROJECT);
|
return compileParsedFile(fileId, ast, diagnostics, SourceKind.PROJECT, "", HostAdmissionContext.permissiveDefault());
|
||||||
}
|
}
|
||||||
|
|
||||||
public IRBackendFile compileParsedFile(
|
public IRBackendFile compileParsedFile(
|
||||||
@ -69,7 +73,7 @@ public final class PbsFrontendCompiler {
|
|||||||
final PbsAst.File ast,
|
final PbsAst.File ast,
|
||||||
final DiagnosticSink diagnostics,
|
final DiagnosticSink diagnostics,
|
||||||
final SourceKind sourceKind) {
|
final SourceKind sourceKind) {
|
||||||
return compileParsedFile(fileId, ast, diagnostics, sourceKind, HostAdmissionContext.permissiveDefault());
|
return compileParsedFile(fileId, ast, diagnostics, sourceKind, "", HostAdmissionContext.permissiveDefault());
|
||||||
}
|
}
|
||||||
|
|
||||||
public IRBackendFile compileParsedFile(
|
public IRBackendFile compileParsedFile(
|
||||||
@ -78,6 +82,16 @@ public final class PbsFrontendCompiler {
|
|||||||
final DiagnosticSink diagnostics,
|
final DiagnosticSink diagnostics,
|
||||||
final SourceKind sourceKind,
|
final SourceKind sourceKind,
|
||||||
final HostAdmissionContext hostAdmissionContext) {
|
final HostAdmissionContext hostAdmissionContext) {
|
||||||
|
return compileParsedFile(fileId, ast, diagnostics, sourceKind, "", hostAdmissionContext);
|
||||||
|
}
|
||||||
|
|
||||||
|
public IRBackendFile compileParsedFile(
|
||||||
|
final FileId fileId,
|
||||||
|
final PbsAst.File ast,
|
||||||
|
final DiagnosticSink diagnostics,
|
||||||
|
final SourceKind sourceKind,
|
||||||
|
final String moduleKey,
|
||||||
|
final HostAdmissionContext hostAdmissionContext) {
|
||||||
final var semanticsErrorBaseline = diagnostics.errorCount();
|
final var semanticsErrorBaseline = diagnostics.errorCount();
|
||||||
declarationSemanticsValidator.validate(ast, sourceKind, diagnostics);
|
declarationSemanticsValidator.validate(ast, sourceKind, diagnostics);
|
||||||
flowSemanticsValidator.validate(ast, diagnostics);
|
flowSemanticsValidator.validate(ast, diagnostics);
|
||||||
@ -103,7 +117,10 @@ public final class PbsFrontendCompiler {
|
|||||||
final ReadOnlyList<IRFunction> functions = sourceKind == SourceKind.SDK_INTERFACE
|
final ReadOnlyList<IRFunction> functions = sourceKind == SourceKind.SDK_INTERFACE
|
||||||
? ReadOnlyList.empty()
|
? ReadOnlyList.empty()
|
||||||
: lowerFunctions(fileId, ast);
|
: lowerFunctions(fileId, ast);
|
||||||
return new IRBackendFile(fileId, functions, reservedMetadata);
|
final ReadOnlyList<IRBackendExecutableFunction> executableFunctions = sourceKind == SourceKind.SDK_INTERFACE
|
||||||
|
? ReadOnlyList.empty()
|
||||||
|
: lowerExecutableFunctions(fileId, ast, moduleKey, reservedMetadata);
|
||||||
|
return new IRBackendFile(fileId, functions, executableFunctions, reservedMetadata);
|
||||||
}
|
}
|
||||||
|
|
||||||
private ReadOnlyList<IRFunction> lowerFunctions(final FileId fileId, final PbsAst.File ast) {
|
private ReadOnlyList<IRFunction> lowerFunctions(final FileId fileId, final PbsAst.File ast) {
|
||||||
@ -118,4 +135,258 @@ public final class PbsFrontendCompiler {
|
|||||||
}
|
}
|
||||||
return ReadOnlyList.wrap(functions);
|
return ReadOnlyList.wrap(functions);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private ReadOnlyList<IRBackendExecutableFunction> lowerExecutableFunctions(
|
||||||
|
final FileId fileId,
|
||||||
|
final PbsAst.File ast,
|
||||||
|
final String moduleKey,
|
||||||
|
final IRReservedMetadata reservedMetadata) {
|
||||||
|
final var hostByMethodName = new HashMap<String, IRReservedMetadata.HostMethodBinding>();
|
||||||
|
for (final var hostBinding : reservedMetadata.hostMethodBindings()) {
|
||||||
|
hostByMethodName.put(hostBinding.sourceMethodName(), hostBinding);
|
||||||
|
}
|
||||||
|
final var intrinsicByMethodName = new HashMap<String, IRReservedMetadata.IntrinsicSurface>();
|
||||||
|
for (final var builtinType : reservedMetadata.builtinTypeSurfaces()) {
|
||||||
|
for (final var intrinsicSurface : builtinType.intrinsics()) {
|
||||||
|
intrinsicByMethodName.put(intrinsicSurface.sourceMethodName(), intrinsicSurface);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final var executableFunctions = new ArrayList<IRBackendExecutableFunction>(ast.functions().size());
|
||||||
|
for (final var fn : ast.functions()) {
|
||||||
|
final var instructions = new ArrayList<IRBackendExecutableFunction.Instruction>();
|
||||||
|
final var callsites = new ArrayList<PbsAst.CallExpr>();
|
||||||
|
collectCallsFromBlock(fn.body(), callsites);
|
||||||
|
for (final var callExpr : callsites) {
|
||||||
|
final var calleeName = extractSimpleCalleeName(callExpr.callee());
|
||||||
|
final var host = hostByMethodName.get(calleeName);
|
||||||
|
if (host != null) {
|
||||||
|
instructions.add(new IRBackendExecutableFunction.Instruction(
|
||||||
|
IRBackendExecutableFunction.InstructionKind.CALL_HOST,
|
||||||
|
"",
|
||||||
|
"",
|
||||||
|
new IRBackendExecutableFunction.HostCallMetadata(
|
||||||
|
host.abiModule(),
|
||||||
|
host.abiMethod(),
|
||||||
|
host.abiVersion(),
|
||||||
|
callExpr.arguments().size(),
|
||||||
|
0),
|
||||||
|
null,
|
||||||
|
callExpr.span()));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
final var intrinsic = intrinsicByMethodName.get(calleeName);
|
||||||
|
if (intrinsic != null) {
|
||||||
|
instructions.add(new IRBackendExecutableFunction.Instruction(
|
||||||
|
IRBackendExecutableFunction.InstructionKind.CALL_INTRINSIC,
|
||||||
|
"",
|
||||||
|
"",
|
||||||
|
null,
|
||||||
|
new IRBackendExecutableFunction.IntrinsicCallMetadata(
|
||||||
|
intrinsic.canonicalName(),
|
||||||
|
intrinsic.canonicalVersion(),
|
||||||
|
intrinsicIdFor(intrinsic.canonicalName(), intrinsic.canonicalVersion())),
|
||||||
|
callExpr.span()));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
instructions.add(new IRBackendExecutableFunction.Instruction(
|
||||||
|
IRBackendExecutableFunction.InstructionKind.CALL_FUNC,
|
||||||
|
moduleKey == null ? "" : moduleKey,
|
||||||
|
calleeName,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
callExpr.span()));
|
||||||
|
}
|
||||||
|
instructions.add(new IRBackendExecutableFunction.Instruction(
|
||||||
|
IRBackendExecutableFunction.InstructionKind.RET,
|
||||||
|
"",
|
||||||
|
"",
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
fn.span()));
|
||||||
|
|
||||||
|
final var returnSlots = switch (fn.returnKind()) {
|
||||||
|
case INFERRED_UNIT, EXPLICIT_UNIT -> 0;
|
||||||
|
case PLAIN, RESULT -> 1;
|
||||||
|
};
|
||||||
|
final var start = safeToInt(fn.span().getStart());
|
||||||
|
final var end = safeToInt(fn.span().getEnd());
|
||||||
|
executableFunctions.add(new IRBackendExecutableFunction(
|
||||||
|
fileId,
|
||||||
|
moduleKey == null ? "" : moduleKey,
|
||||||
|
fn.name(),
|
||||||
|
start,
|
||||||
|
end,
|
||||||
|
fn.parameters().size(),
|
||||||
|
0,
|
||||||
|
returnSlots,
|
||||||
|
Math.max(4, fn.parameters().size() + 2),
|
||||||
|
ReadOnlyList.wrap(instructions),
|
||||||
|
fn.span()));
|
||||||
|
}
|
||||||
|
return ReadOnlyList.wrap(executableFunctions);
|
||||||
|
}
|
||||||
|
|
||||||
|
private int intrinsicIdFor(
|
||||||
|
final String canonicalName,
|
||||||
|
final long canonicalVersion) {
|
||||||
|
return (canonicalName + "#" + canonicalVersion).hashCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
private int safeToInt(final long value) {
|
||||||
|
if (value > Integer.MAX_VALUE) {
|
||||||
|
return Integer.MAX_VALUE;
|
||||||
|
}
|
||||||
|
if (value < Integer.MIN_VALUE) {
|
||||||
|
return Integer.MIN_VALUE;
|
||||||
|
}
|
||||||
|
return (int) value;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void collectCallsFromBlock(
|
||||||
|
final PbsAst.Block block,
|
||||||
|
final List<PbsAst.CallExpr> output) {
|
||||||
|
for (final var statement : block.statements()) {
|
||||||
|
collectCallsFromStatement(statement, output);
|
||||||
|
}
|
||||||
|
if (block.tailExpression() != null) {
|
||||||
|
collectCallsFromExpression(block.tailExpression(), output);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void collectCallsFromStatement(
|
||||||
|
final PbsAst.Statement statement,
|
||||||
|
final List<PbsAst.CallExpr> output) {
|
||||||
|
switch (statement) {
|
||||||
|
case PbsAst.LetStatement letStatement -> collectCallsFromExpression(letStatement.initializer(), output);
|
||||||
|
case PbsAst.AssignStatement assignStatement -> collectCallsFromExpression(assignStatement.value(), output);
|
||||||
|
case PbsAst.ReturnStatement returnStatement -> {
|
||||||
|
if (returnStatement.value() != null) {
|
||||||
|
collectCallsFromExpression(returnStatement.value(), output);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case PbsAst.IfStatement ifStatement -> {
|
||||||
|
collectCallsFromExpression(ifStatement.condition(), output);
|
||||||
|
collectCallsFromBlock(ifStatement.thenBlock(), output);
|
||||||
|
if (ifStatement.elseIf() != null) {
|
||||||
|
collectCallsFromStatement(ifStatement.elseIf(), output);
|
||||||
|
}
|
||||||
|
if (ifStatement.elseBlock() != null) {
|
||||||
|
collectCallsFromBlock(ifStatement.elseBlock(), output);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case PbsAst.ForStatement forStatement -> {
|
||||||
|
collectCallsFromExpression(forStatement.fromExpression(), output);
|
||||||
|
collectCallsFromExpression(forStatement.untilExpression(), output);
|
||||||
|
if (forStatement.stepExpression() != null) {
|
||||||
|
collectCallsFromExpression(forStatement.stepExpression(), output);
|
||||||
|
}
|
||||||
|
collectCallsFromBlock(forStatement.body(), output);
|
||||||
|
}
|
||||||
|
case PbsAst.WhileStatement whileStatement -> {
|
||||||
|
collectCallsFromExpression(whileStatement.condition(), output);
|
||||||
|
collectCallsFromBlock(whileStatement.body(), output);
|
||||||
|
}
|
||||||
|
case PbsAst.ExpressionStatement expressionStatement ->
|
||||||
|
collectCallsFromExpression(expressionStatement.expression(), output);
|
||||||
|
case PbsAst.BreakStatement ignored -> {
|
||||||
|
}
|
||||||
|
case PbsAst.ContinueStatement ignored -> {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void collectCallsFromExpression(
|
||||||
|
final PbsAst.Expression expression,
|
||||||
|
final List<PbsAst.CallExpr> output) {
|
||||||
|
switch (expression) {
|
||||||
|
case PbsAst.CallExpr callExpr -> {
|
||||||
|
output.add(callExpr);
|
||||||
|
collectCallsFromExpression(callExpr.callee(), output);
|
||||||
|
for (final var arg : callExpr.arguments()) {
|
||||||
|
collectCallsFromExpression(arg, output);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case PbsAst.ApplyExpr applyExpr -> {
|
||||||
|
collectCallsFromExpression(applyExpr.callee(), output);
|
||||||
|
collectCallsFromExpression(applyExpr.argument(), output);
|
||||||
|
}
|
||||||
|
case PbsAst.BinaryExpr binaryExpr -> {
|
||||||
|
collectCallsFromExpression(binaryExpr.left(), output);
|
||||||
|
collectCallsFromExpression(binaryExpr.right(), output);
|
||||||
|
}
|
||||||
|
case PbsAst.UnaryExpr unaryExpr -> collectCallsFromExpression(unaryExpr.expression(), output);
|
||||||
|
case PbsAst.ElseExpr elseExpr -> {
|
||||||
|
collectCallsFromExpression(elseExpr.optionalExpression(), output);
|
||||||
|
collectCallsFromExpression(elseExpr.fallbackExpression(), output);
|
||||||
|
}
|
||||||
|
case PbsAst.IfExpr ifExpr -> {
|
||||||
|
collectCallsFromExpression(ifExpr.condition(), output);
|
||||||
|
collectCallsFromBlock(ifExpr.thenBlock(), output);
|
||||||
|
collectCallsFromExpression(ifExpr.elseExpression(), output);
|
||||||
|
}
|
||||||
|
case PbsAst.SwitchExpr switchExpr -> {
|
||||||
|
collectCallsFromExpression(switchExpr.selector(), output);
|
||||||
|
for (final var arm : switchExpr.arms()) {
|
||||||
|
collectCallsFromBlock(arm.block(), output);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case PbsAst.HandleExpr handleExpr -> {
|
||||||
|
collectCallsFromExpression(handleExpr.value(), output);
|
||||||
|
for (final var arm : handleExpr.arms()) {
|
||||||
|
collectCallsFromBlock(arm.block(), output);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case PbsAst.AsExpr asExpr -> collectCallsFromExpression(asExpr.expression(), output);
|
||||||
|
case PbsAst.MemberExpr memberExpr -> collectCallsFromExpression(memberExpr.receiver(), output);
|
||||||
|
case PbsAst.PropagateExpr propagateExpr -> collectCallsFromExpression(propagateExpr.expression(), output);
|
||||||
|
case PbsAst.GroupExpr groupExpr -> collectCallsFromExpression(groupExpr.expression(), output);
|
||||||
|
case PbsAst.NewExpr newExpr -> {
|
||||||
|
for (final var arg : newExpr.arguments()) {
|
||||||
|
collectCallsFromExpression(arg, output);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case PbsAst.BindExpr bindExpr -> collectCallsFromExpression(bindExpr.contextExpression(), output);
|
||||||
|
case PbsAst.SomeExpr someExpr -> collectCallsFromExpression(someExpr.value(), output);
|
||||||
|
case PbsAst.OkExpr okExpr -> collectCallsFromExpression(okExpr.value(), output);
|
||||||
|
case PbsAst.TupleExpr tupleExpr -> {
|
||||||
|
for (final var item : tupleExpr.items()) {
|
||||||
|
collectCallsFromExpression(item.expression(), output);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case PbsAst.BlockExpr blockExpr -> collectCallsFromBlock(blockExpr.block(), output);
|
||||||
|
case PbsAst.IdentifierExpr ignored -> {
|
||||||
|
}
|
||||||
|
case PbsAst.IntLiteralExpr ignored -> {
|
||||||
|
}
|
||||||
|
case PbsAst.FloatLiteralExpr ignored -> {
|
||||||
|
}
|
||||||
|
case PbsAst.BoundedLiteralExpr ignored -> {
|
||||||
|
}
|
||||||
|
case PbsAst.StringLiteralExpr ignored -> {
|
||||||
|
}
|
||||||
|
case PbsAst.BoolLiteralExpr ignored -> {
|
||||||
|
}
|
||||||
|
case PbsAst.ThisExpr ignored -> {
|
||||||
|
}
|
||||||
|
case PbsAst.NoneExpr ignored -> {
|
||||||
|
}
|
||||||
|
case PbsAst.ErrExpr ignored -> {
|
||||||
|
}
|
||||||
|
case PbsAst.UnitExpr ignored -> {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private String extractSimpleCalleeName(final PbsAst.Expression callee) {
|
||||||
|
return switch (callee) {
|
||||||
|
case PbsAst.IdentifierExpr identifierExpr -> identifierExpr.name();
|
||||||
|
case PbsAst.MemberExpr memberExpr -> memberExpr.memberName();
|
||||||
|
case PbsAst.BindExpr bindExpr -> bindExpr.functionName();
|
||||||
|
case PbsAst.GroupExpr groupExpr -> extractSimpleCalleeName(groupExpr.expression());
|
||||||
|
default -> "<unknown>";
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -148,6 +148,7 @@ public class PBSFrontendPhaseService implements FrontendPhaseService {
|
|||||||
parsedSource.ast(),
|
parsedSource.ast(),
|
||||||
diagnostics,
|
diagnostics,
|
||||||
parsedSource.sourceKind(),
|
parsedSource.sourceKind(),
|
||||||
|
parsedSource.moduleKey(),
|
||||||
ctx.hostAdmissionContext());
|
ctx.hostAdmissionContext());
|
||||||
if (diagnostics.errorCount() > compileErrorBaseline) {
|
if (diagnostics.errorCount() > compileErrorBaseline) {
|
||||||
failedModuleKeys.add(parsedSource.moduleKey());
|
failedModuleKeys.add(parsedSource.moduleKey());
|
||||||
|
|||||||
@ -72,6 +72,32 @@ class PbsFrontendCompilerTest {
|
|||||||
assertEquals(2, fileBackend.functions().size());
|
assertEquals(2, fileBackend.functions().size());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldLowerExecutableFunctionsWithCallsiteCategories() {
|
||||||
|
final var source = """
|
||||||
|
fn b(x: int) -> int {
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn a() -> int {
|
||||||
|
return b(1);
|
||||||
|
}
|
||||||
|
""";
|
||||||
|
|
||||||
|
final var diagnostics = DiagnosticSink.empty();
|
||||||
|
final var compiler = new PbsFrontendCompiler();
|
||||||
|
final var fileBackend = compiler.compileFile(new FileId(42), source, diagnostics);
|
||||||
|
|
||||||
|
assertTrue(diagnostics.isEmpty(), "Valid program should not report diagnostics");
|
||||||
|
assertEquals(2, fileBackend.executableFunctions().size());
|
||||||
|
final var executableA = fileBackend.executableFunctions().stream()
|
||||||
|
.filter(fn -> fn.callableName().equals("a"))
|
||||||
|
.findFirst()
|
||||||
|
.orElseThrow();
|
||||||
|
assertTrue(executableA.instructions().stream().anyMatch(i ->
|
||||||
|
i.kind() == p.studio.compiler.models.IRBackendExecutableFunction.InstructionKind.CALL_FUNC));
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void shouldNotLowerWhenSyntaxErrorsExist() {
|
void shouldNotLowerWhenSyntaxErrorsExist() {
|
||||||
final var source = """
|
final var source = """
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user