implements PR008.1 clean up
This commit is contained in:
parent
b84d4c29f6
commit
099ed96c82
@ -0,0 +1,300 @@
|
|||||||
|
package p.studio.compiler.pbs.semantics;
|
||||||
|
|
||||||
|
import p.studio.compiler.pbs.ast.PbsAst;
|
||||||
|
import p.studio.compiler.source.diagnostics.DiagnosticSink;
|
||||||
|
import p.studio.utilities.structures.ReadOnlyList;
|
||||||
|
import p.studio.compiler.pbs.semantics.PbsFlowSemanticSupport.ExprUse;
|
||||||
|
import p.studio.compiler.pbs.semantics.PbsFlowSemanticSupport.Model;
|
||||||
|
import p.studio.compiler.pbs.semantics.PbsFlowSemanticSupport.Scope;
|
||||||
|
import p.studio.compiler.pbs.semantics.PbsFlowSemanticSupport.TypeView;
|
||||||
|
|
||||||
|
final class PbsFlowBodyAnalyzer {
|
||||||
|
private final PbsFlowTypeOps typeOps = new PbsFlowTypeOps();
|
||||||
|
private final PbsFlowExpressionAnalyzer expressionAnalyzer = new PbsFlowExpressionAnalyzer(typeOps);
|
||||||
|
|
||||||
|
public void validate(final PbsAst.File ast, final DiagnosticSink diagnostics) {
|
||||||
|
final var model = Model.from(ast);
|
||||||
|
|
||||||
|
for (final var topDecl : ast.topDecls()) {
|
||||||
|
if (topDecl instanceof PbsAst.FunctionDecl functionDecl) {
|
||||||
|
validateCallableBody(
|
||||||
|
functionDecl.parameters(),
|
||||||
|
functionDecl.body(),
|
||||||
|
typeOps.callableReturnType(functionDecl.returnKind(), functionDecl.returnType(), functionDecl.resultErrorType(), model),
|
||||||
|
functionDecl.resultErrorType() == null ? null : functionDecl.resultErrorType().name(),
|
||||||
|
null,
|
||||||
|
model,
|
||||||
|
diagnostics);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (topDecl instanceof PbsAst.StructDecl structDecl) {
|
||||||
|
final var receiverType = TypeView.struct(structDecl.name());
|
||||||
|
for (final var method : structDecl.methods()) {
|
||||||
|
validateCallableBody(
|
||||||
|
method.parameters(),
|
||||||
|
method.body(),
|
||||||
|
typeOps.callableReturnType(method.returnKind(), method.returnType(), method.resultErrorType(), model),
|
||||||
|
method.resultErrorType() == null ? null : method.resultErrorType().name(),
|
||||||
|
receiverType,
|
||||||
|
model,
|
||||||
|
diagnostics);
|
||||||
|
}
|
||||||
|
for (final var ctor : structDecl.ctors()) {
|
||||||
|
validateCallableBody(
|
||||||
|
ctor.parameters(),
|
||||||
|
ctor.body(),
|
||||||
|
TypeView.unit(),
|
||||||
|
null,
|
||||||
|
receiverType,
|
||||||
|
model,
|
||||||
|
diagnostics);
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (topDecl instanceof PbsAst.ServiceDecl serviceDecl) {
|
||||||
|
final var receiverType = TypeView.service(serviceDecl.name());
|
||||||
|
for (final var method : serviceDecl.methods()) {
|
||||||
|
validateCallableBody(
|
||||||
|
method.parameters(),
|
||||||
|
method.body(),
|
||||||
|
typeOps.callableReturnType(method.returnKind(), method.returnType(), method.resultErrorType(), model),
|
||||||
|
method.resultErrorType() == null ? null : method.resultErrorType().name(),
|
||||||
|
receiverType,
|
||||||
|
model,
|
||||||
|
diagnostics);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void validateCallableBody(
|
||||||
|
final ReadOnlyList<PbsAst.Parameter> parameters,
|
||||||
|
final PbsAst.Block body,
|
||||||
|
final TypeView returnType,
|
||||||
|
final String resultErrorName,
|
||||||
|
final TypeView receiverType,
|
||||||
|
final Model model,
|
||||||
|
final DiagnosticSink diagnostics) {
|
||||||
|
final var scope = new Scope();
|
||||||
|
for (final var parameter : parameters) {
|
||||||
|
scope.bind(parameter.name(), typeOps.typeFromTypeRef(parameter.typeRef(), model, receiverType));
|
||||||
|
}
|
||||||
|
analyzeBlock(body, scope, returnType, resultErrorName, receiverType, model, diagnostics, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private TypeView analyzeBlock(
|
||||||
|
final PbsAst.Block block,
|
||||||
|
final Scope outerScope,
|
||||||
|
final TypeView returnType,
|
||||||
|
final String resultErrorName,
|
||||||
|
final TypeView receiverType,
|
||||||
|
final Model model,
|
||||||
|
final DiagnosticSink diagnostics,
|
||||||
|
final boolean valueContext) {
|
||||||
|
final var scope = outerScope.copy();
|
||||||
|
for (final var statement : block.statements()) {
|
||||||
|
analyzeStatement(statement, scope, returnType, resultErrorName, receiverType, model, diagnostics);
|
||||||
|
}
|
||||||
|
if (block.tailExpression() == null) {
|
||||||
|
return TypeView.unit();
|
||||||
|
}
|
||||||
|
return expressionAnalyzer.analyzeExpression(
|
||||||
|
block.tailExpression(),
|
||||||
|
scope,
|
||||||
|
null,
|
||||||
|
returnType,
|
||||||
|
resultErrorName,
|
||||||
|
receiverType,
|
||||||
|
model,
|
||||||
|
diagnostics,
|
||||||
|
ExprUse.VALUE,
|
||||||
|
valueContext,
|
||||||
|
this::analyzeBlock).type();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void analyzeStatement(
|
||||||
|
final PbsAst.Statement statement,
|
||||||
|
final Scope scope,
|
||||||
|
final TypeView returnType,
|
||||||
|
final String resultErrorName,
|
||||||
|
final TypeView receiverType,
|
||||||
|
final Model model,
|
||||||
|
final DiagnosticSink diagnostics) {
|
||||||
|
if (statement instanceof PbsAst.LetStatement letStatement) {
|
||||||
|
final var expected = letStatement.explicitType() == null
|
||||||
|
? null
|
||||||
|
: typeOps.typeFromTypeRef(letStatement.explicitType(), model, receiverType);
|
||||||
|
final var initializer = expressionAnalyzer.analyzeExpression(
|
||||||
|
letStatement.initializer(),
|
||||||
|
scope,
|
||||||
|
expected,
|
||||||
|
returnType,
|
||||||
|
resultErrorName,
|
||||||
|
receiverType,
|
||||||
|
model,
|
||||||
|
diagnostics,
|
||||||
|
ExprUse.VALUE,
|
||||||
|
true,
|
||||||
|
this::analyzeBlock);
|
||||||
|
scope.bind(letStatement.name(), expected == null ? initializer.type() : expected);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (statement instanceof PbsAst.ReturnStatement returnStatement) {
|
||||||
|
if (returnStatement.value() != null) {
|
||||||
|
expressionAnalyzer.analyzeExpression(
|
||||||
|
returnStatement.value(),
|
||||||
|
scope,
|
||||||
|
returnType,
|
||||||
|
returnType,
|
||||||
|
resultErrorName,
|
||||||
|
receiverType,
|
||||||
|
model,
|
||||||
|
diagnostics,
|
||||||
|
ExprUse.VALUE,
|
||||||
|
true,
|
||||||
|
this::analyzeBlock);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (statement instanceof PbsAst.IfStatement ifStatement) {
|
||||||
|
final var condition = expressionAnalyzer.analyzeExpression(
|
||||||
|
ifStatement.condition(),
|
||||||
|
scope,
|
||||||
|
TypeView.bool(),
|
||||||
|
returnType,
|
||||||
|
resultErrorName,
|
||||||
|
receiverType,
|
||||||
|
model,
|
||||||
|
diagnostics,
|
||||||
|
ExprUse.VALUE,
|
||||||
|
true,
|
||||||
|
this::analyzeBlock).type();
|
||||||
|
if (!typeOps.isBool(condition)) {
|
||||||
|
diagnostics.error(
|
||||||
|
PbsSemanticsErrors.E_SEM_IF_NON_BOOL_CONDITION.name(),
|
||||||
|
"If statement condition must have bool type",
|
||||||
|
ifStatement.condition().span());
|
||||||
|
}
|
||||||
|
analyzeBlock(ifStatement.thenBlock(), scope, returnType, resultErrorName, receiverType, model, diagnostics, false);
|
||||||
|
if (ifStatement.elseBlock() != null) {
|
||||||
|
analyzeBlock(ifStatement.elseBlock(), scope, returnType, resultErrorName, receiverType, model, diagnostics, false);
|
||||||
|
}
|
||||||
|
if (ifStatement.elseIf() != null) {
|
||||||
|
analyzeStatement(ifStatement.elseIf(), scope, returnType, resultErrorName, receiverType, model, diagnostics);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (statement instanceof PbsAst.ForStatement(
|
||||||
|
String iteratorName, PbsAst.TypeRef type, PbsAst.Expression fromExpression,
|
||||||
|
PbsAst.Expression untilExpression, PbsAst.Expression stepExpression, PbsAst.Block body,
|
||||||
|
p.studio.compiler.source.Span span
|
||||||
|
)) {
|
||||||
|
final var iteratorType = typeOps.typeFromTypeRef(type, model, receiverType);
|
||||||
|
final var fromType = expressionAnalyzer.analyzeExpression(
|
||||||
|
fromExpression,
|
||||||
|
scope,
|
||||||
|
iteratorType,
|
||||||
|
returnType,
|
||||||
|
resultErrorName,
|
||||||
|
receiverType,
|
||||||
|
model,
|
||||||
|
diagnostics,
|
||||||
|
ExprUse.VALUE,
|
||||||
|
true,
|
||||||
|
this::analyzeBlock).type();
|
||||||
|
final var untilType = expressionAnalyzer.analyzeExpression(
|
||||||
|
untilExpression,
|
||||||
|
scope,
|
||||||
|
iteratorType,
|
||||||
|
returnType,
|
||||||
|
resultErrorName,
|
||||||
|
receiverType,
|
||||||
|
model,
|
||||||
|
diagnostics,
|
||||||
|
ExprUse.VALUE,
|
||||||
|
true,
|
||||||
|
this::analyzeBlock).type();
|
||||||
|
if (!typeOps.compatible(fromType, iteratorType) || !typeOps.compatible(untilType, iteratorType)) {
|
||||||
|
diagnostics.error(
|
||||||
|
PbsSemanticsErrors.E_SEM_FOR_TYPE_MISMATCH.name(),
|
||||||
|
"For-loop bounds must match declared iterator type",
|
||||||
|
span);
|
||||||
|
}
|
||||||
|
if (stepExpression != null) {
|
||||||
|
final var stepType = expressionAnalyzer.analyzeExpression(
|
||||||
|
stepExpression,
|
||||||
|
scope,
|
||||||
|
iteratorType,
|
||||||
|
returnType,
|
||||||
|
resultErrorName,
|
||||||
|
receiverType,
|
||||||
|
model,
|
||||||
|
diagnostics,
|
||||||
|
ExprUse.VALUE,
|
||||||
|
true,
|
||||||
|
this::analyzeBlock).type();
|
||||||
|
if (!typeOps.compatible(stepType, iteratorType)) {
|
||||||
|
diagnostics.error(
|
||||||
|
PbsSemanticsErrors.E_SEM_FOR_TYPE_MISMATCH.name(),
|
||||||
|
"For-loop step must match declared iterator type",
|
||||||
|
stepExpression.span());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
final var bodyScope = scope.copy();
|
||||||
|
bodyScope.bind(iteratorName, iteratorType);
|
||||||
|
analyzeBlock(body, bodyScope, returnType, resultErrorName, receiverType, model, diagnostics, false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (statement instanceof PbsAst.WhileStatement whileStatement) {
|
||||||
|
final var condition = expressionAnalyzer.analyzeExpression(
|
||||||
|
whileStatement.condition(),
|
||||||
|
scope,
|
||||||
|
TypeView.bool(),
|
||||||
|
returnType,
|
||||||
|
resultErrorName,
|
||||||
|
receiverType,
|
||||||
|
model,
|
||||||
|
diagnostics,
|
||||||
|
ExprUse.VALUE,
|
||||||
|
true,
|
||||||
|
this::analyzeBlock).type();
|
||||||
|
if (!typeOps.isBool(condition)) {
|
||||||
|
diagnostics.error(
|
||||||
|
PbsSemanticsErrors.E_SEM_WHILE_NON_BOOL_CONDITION.name(),
|
||||||
|
"While condition must have bool type",
|
||||||
|
whileStatement.condition().span());
|
||||||
|
}
|
||||||
|
analyzeBlock(whileStatement.body(), scope, returnType, resultErrorName, receiverType, model, diagnostics, false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (statement instanceof PbsAst.ExpressionStatement expressionStatement) {
|
||||||
|
expressionAnalyzer.analyzeExpression(
|
||||||
|
expressionStatement.expression(),
|
||||||
|
scope,
|
||||||
|
null,
|
||||||
|
returnType,
|
||||||
|
resultErrorName,
|
||||||
|
receiverType,
|
||||||
|
model,
|
||||||
|
diagnostics,
|
||||||
|
ExprUse.VALUE,
|
||||||
|
false,
|
||||||
|
this::analyzeBlock);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (statement instanceof PbsAst.AssignStatement assignStatement) {
|
||||||
|
expressionAnalyzer.analyzeExpression(
|
||||||
|
assignStatement.value(),
|
||||||
|
scope,
|
||||||
|
null,
|
||||||
|
returnType,
|
||||||
|
resultErrorName,
|
||||||
|
receiverType,
|
||||||
|
model,
|
||||||
|
diagnostics,
|
||||||
|
ExprUse.VALUE,
|
||||||
|
true,
|
||||||
|
this::analyzeBlock);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,367 @@
|
|||||||
|
package p.studio.compiler.pbs.semantics;
|
||||||
|
|
||||||
|
import lombok.experimental.UtilityClass;
|
||||||
|
import p.studio.compiler.pbs.ast.PbsAst;
|
||||||
|
import p.studio.compiler.source.Span;
|
||||||
|
import p.studio.utilities.structures.ReadOnlyList;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
@UtilityClass
|
||||||
|
final class PbsFlowSemanticSupport {
|
||||||
|
record ExprResult(
|
||||||
|
TypeView type,
|
||||||
|
List<CallableSymbol> callables,
|
||||||
|
boolean methodTarget) {
|
||||||
|
static ExprResult type(final TypeView type) {
|
||||||
|
return new ExprResult(type, List.of(), false);
|
||||||
|
}
|
||||||
|
|
||||||
|
static ExprResult callables(final List<CallableSymbol> callables, final boolean methodTarget) {
|
||||||
|
return new ExprResult(TypeView.unknown(), List.copyOf(callables), methodTarget);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum ExprUse {
|
||||||
|
VALUE,
|
||||||
|
CALL_TARGET,
|
||||||
|
APPLY_TARGET
|
||||||
|
}
|
||||||
|
|
||||||
|
enum Kind {
|
||||||
|
UNKNOWN,
|
||||||
|
UNIT,
|
||||||
|
INT,
|
||||||
|
FLOAT,
|
||||||
|
BOOL,
|
||||||
|
STR,
|
||||||
|
STRUCT,
|
||||||
|
SERVICE,
|
||||||
|
CONTRACT,
|
||||||
|
CALLBACK,
|
||||||
|
ENUM,
|
||||||
|
ERROR,
|
||||||
|
OPTIONAL,
|
||||||
|
RESULT,
|
||||||
|
TUPLE,
|
||||||
|
TYPE_REF
|
||||||
|
}
|
||||||
|
|
||||||
|
record TupleField(
|
||||||
|
String label,
|
||||||
|
TypeView type) {
|
||||||
|
}
|
||||||
|
|
||||||
|
record TypeView(
|
||||||
|
Kind kind,
|
||||||
|
String name,
|
||||||
|
List<TupleField> tupleFields,
|
||||||
|
TypeView inner,
|
||||||
|
TypeView errorType,
|
||||||
|
List<TypeView> callbackInputs,
|
||||||
|
TypeView callbackOutput) {
|
||||||
|
static TypeView unknown() {
|
||||||
|
return new TypeView(Kind.UNKNOWN, null, List.of(), null, null, List.of(), null);
|
||||||
|
}
|
||||||
|
|
||||||
|
static TypeView unit() {
|
||||||
|
return new TypeView(Kind.UNIT, "unit", List.of(), null, null, List.of(), null);
|
||||||
|
}
|
||||||
|
|
||||||
|
static TypeView intType() {
|
||||||
|
return new TypeView(Kind.INT, "int", List.of(), null, null, List.of(), null);
|
||||||
|
}
|
||||||
|
|
||||||
|
static TypeView floatType() {
|
||||||
|
return new TypeView(Kind.FLOAT, "float", List.of(), null, null, List.of(), null);
|
||||||
|
}
|
||||||
|
|
||||||
|
static TypeView bool() {
|
||||||
|
return new TypeView(Kind.BOOL, "bool", List.of(), null, null, List.of(), null);
|
||||||
|
}
|
||||||
|
|
||||||
|
static TypeView str() {
|
||||||
|
return new TypeView(Kind.STR, "str", List.of(), null, null, List.of(), null);
|
||||||
|
}
|
||||||
|
|
||||||
|
static TypeView struct(final String name) {
|
||||||
|
return new TypeView(Kind.STRUCT, name, List.of(), null, null, List.of(), null);
|
||||||
|
}
|
||||||
|
|
||||||
|
static TypeView service(final String name) {
|
||||||
|
return new TypeView(Kind.SERVICE, name, List.of(), null, null, List.of(), null);
|
||||||
|
}
|
||||||
|
|
||||||
|
static TypeView contract(final String name) {
|
||||||
|
return new TypeView(Kind.CONTRACT, name, List.of(), null, null, List.of(), null);
|
||||||
|
}
|
||||||
|
|
||||||
|
static TypeView callback(
|
||||||
|
final String name,
|
||||||
|
final List<TypeView> inputTypes,
|
||||||
|
final TypeView outputType) {
|
||||||
|
return new TypeView(Kind.CALLBACK, name, List.of(), null, null, List.copyOf(inputTypes), outputType);
|
||||||
|
}
|
||||||
|
|
||||||
|
static TypeView enumType(final String name) {
|
||||||
|
return new TypeView(Kind.ENUM, name, List.of(), null, null, List.of(), null);
|
||||||
|
}
|
||||||
|
|
||||||
|
static TypeView error(final String name) {
|
||||||
|
return new TypeView(Kind.ERROR, name, List.of(), null, null, List.of(), null);
|
||||||
|
}
|
||||||
|
|
||||||
|
static TypeView optional(final TypeView inner) {
|
||||||
|
return new TypeView(Kind.OPTIONAL, "optional", List.of(), inner, null, List.of(), null);
|
||||||
|
}
|
||||||
|
|
||||||
|
static TypeView result(final TypeView error, final TypeView payload) {
|
||||||
|
return new TypeView(Kind.RESULT, "result", List.of(), payload, error, List.of(), null);
|
||||||
|
}
|
||||||
|
|
||||||
|
static TypeView tuple(final List<TupleField> fields) {
|
||||||
|
return new TypeView(Kind.TUPLE, "tuple", List.copyOf(fields), null, null, List.of(), null);
|
||||||
|
}
|
||||||
|
|
||||||
|
static TypeView typeRef(final String name) {
|
||||||
|
return new TypeView(Kind.TYPE_REF, name, List.of(), null, null, List.of(), null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
record CallableSymbol(
|
||||||
|
String name,
|
||||||
|
List<TypeView> inputTypes,
|
||||||
|
TypeView outputType,
|
||||||
|
Span span) {
|
||||||
|
}
|
||||||
|
|
||||||
|
record CallbackSignature(
|
||||||
|
List<TypeView> inputTypes,
|
||||||
|
TypeView outputType) {
|
||||||
|
}
|
||||||
|
|
||||||
|
record StructInfo(
|
||||||
|
Map<String, TypeView> fields,
|
||||||
|
Map<String, List<CallableSymbol>> methods) {
|
||||||
|
}
|
||||||
|
|
||||||
|
record ServiceInfo(
|
||||||
|
Map<String, List<CallableSymbol>> methods) {
|
||||||
|
}
|
||||||
|
|
||||||
|
record ContractInfo(
|
||||||
|
Map<String, List<CallableSymbol>> methods) {
|
||||||
|
}
|
||||||
|
|
||||||
|
static final class Model {
|
||||||
|
final Map<String, List<CallableSymbol>> topLevelCallables = new HashMap<>();
|
||||||
|
final Map<String, StructInfo> structs = new HashMap<>();
|
||||||
|
final Map<String, ServiceInfo> services = new HashMap<>();
|
||||||
|
final Map<String, ContractInfo> contracts = new HashMap<>();
|
||||||
|
final Map<String, CallbackSignature> callbacks = new HashMap<>();
|
||||||
|
final Map<String, Set<String>> enums = new HashMap<>();
|
||||||
|
final Map<String, Set<String>> errors = new HashMap<>();
|
||||||
|
final Map<String, TypeView> constTypes = new HashMap<>();
|
||||||
|
final Map<String, TypeView> serviceSingletons = new HashMap<>();
|
||||||
|
|
||||||
|
static Model from(final PbsAst.File ast) {
|
||||||
|
final var model = new Model();
|
||||||
|
for (final var topDecl : ast.topDecls()) {
|
||||||
|
if (topDecl instanceof PbsAst.StructDecl structDecl) {
|
||||||
|
final var fields = new HashMap<String, TypeView>();
|
||||||
|
for (final var field : structDecl.fields()) {
|
||||||
|
fields.put(field.name(), model.typeFrom(field.typeRef()));
|
||||||
|
}
|
||||||
|
final var methods = new HashMap<String, List<CallableSymbol>>();
|
||||||
|
for (final var method : structDecl.methods()) {
|
||||||
|
methods.computeIfAbsent(method.name(), ignored -> new ArrayList<>())
|
||||||
|
.add(model.callableFrom(
|
||||||
|
method.name(),
|
||||||
|
method.parameters(),
|
||||||
|
method.returnKind(),
|
||||||
|
method.returnType(),
|
||||||
|
method.resultErrorType(),
|
||||||
|
method.span()));
|
||||||
|
}
|
||||||
|
model.structs.put(structDecl.name(), new StructInfo(fields, methods));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (topDecl instanceof PbsAst.ServiceDecl serviceDecl) {
|
||||||
|
final var methods = new HashMap<String, List<CallableSymbol>>();
|
||||||
|
for (final var method : serviceDecl.methods()) {
|
||||||
|
methods.computeIfAbsent(method.name(), ignored -> new ArrayList<>())
|
||||||
|
.add(model.callableFrom(
|
||||||
|
method.name(),
|
||||||
|
method.parameters(),
|
||||||
|
method.returnKind(),
|
||||||
|
method.returnType(),
|
||||||
|
method.resultErrorType(),
|
||||||
|
method.span()));
|
||||||
|
}
|
||||||
|
model.services.put(serviceDecl.name(), new ServiceInfo(methods));
|
||||||
|
model.serviceSingletons.put(serviceDecl.name(), TypeView.service(serviceDecl.name()));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (topDecl instanceof PbsAst.ContractDecl contractDecl) {
|
||||||
|
final var methods = new HashMap<String, List<CallableSymbol>>();
|
||||||
|
for (final var signature : contractDecl.signatures()) {
|
||||||
|
methods.computeIfAbsent(signature.name(), ignored -> new ArrayList<>())
|
||||||
|
.add(model.callableFrom(
|
||||||
|
signature.name(),
|
||||||
|
signature.parameters(),
|
||||||
|
signature.returnKind(),
|
||||||
|
signature.returnType(),
|
||||||
|
signature.resultErrorType(),
|
||||||
|
signature.span()));
|
||||||
|
}
|
||||||
|
model.contracts.put(contractDecl.name(), new ContractInfo(methods));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (topDecl instanceof PbsAst.FunctionDecl functionDecl) {
|
||||||
|
model.topLevelCallables.computeIfAbsent(functionDecl.name(), ignored -> new ArrayList<>())
|
||||||
|
.add(model.callableFrom(
|
||||||
|
functionDecl.name(),
|
||||||
|
functionDecl.parameters(),
|
||||||
|
functionDecl.returnKind(),
|
||||||
|
functionDecl.returnType(),
|
||||||
|
functionDecl.resultErrorType(),
|
||||||
|
functionDecl.span()));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (topDecl instanceof PbsAst.CallbackDecl(
|
||||||
|
String name, ReadOnlyList<PbsAst.Parameter> parameters, PbsAst.ReturnKind returnKind,
|
||||||
|
PbsAst.TypeRef returnType, PbsAst.TypeRef resultErrorType, Span span
|
||||||
|
)) {
|
||||||
|
final var symbol = model.callableFrom(
|
||||||
|
name,
|
||||||
|
parameters,
|
||||||
|
returnKind,
|
||||||
|
returnType,
|
||||||
|
resultErrorType,
|
||||||
|
span);
|
||||||
|
model.callbacks.put(name, new CallbackSignature(symbol.inputTypes(), symbol.outputType()));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (topDecl instanceof PbsAst.EnumDecl enumDecl) {
|
||||||
|
final var cases = new HashSet<String>();
|
||||||
|
for (final var enumCase : enumDecl.cases()) {
|
||||||
|
cases.add(enumCase.name());
|
||||||
|
}
|
||||||
|
model.enums.put(enumDecl.name(), cases);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (topDecl instanceof PbsAst.ErrorDecl errorDecl) {
|
||||||
|
model.errors.put(errorDecl.name(), new HashSet<>(errorDecl.cases().asList()));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (topDecl instanceof PbsAst.ConstDecl constDecl && constDecl.explicitType() != null) {
|
||||||
|
model.constTypes.put(constDecl.name(), model.typeFrom(constDecl.explicitType()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return model;
|
||||||
|
}
|
||||||
|
|
||||||
|
private CallableSymbol callableFrom(
|
||||||
|
final String name,
|
||||||
|
final ReadOnlyList<PbsAst.Parameter> parameters,
|
||||||
|
final PbsAst.ReturnKind returnKind,
|
||||||
|
final PbsAst.TypeRef returnType,
|
||||||
|
final PbsAst.TypeRef resultErrorType,
|
||||||
|
final Span span) {
|
||||||
|
final var input = new ArrayList<TypeView>(parameters.size());
|
||||||
|
for (final var parameter : parameters) {
|
||||||
|
input.add(typeFrom(parameter.typeRef()));
|
||||||
|
}
|
||||||
|
return new CallableSymbol(name, input, callableReturn(returnKind, returnType, resultErrorType), span);
|
||||||
|
}
|
||||||
|
|
||||||
|
private TypeView callableReturn(
|
||||||
|
final PbsAst.ReturnKind returnKind,
|
||||||
|
final PbsAst.TypeRef returnType,
|
||||||
|
final PbsAst.TypeRef resultErrorType) {
|
||||||
|
return switch (returnKind) {
|
||||||
|
case INFERRED_UNIT, EXPLICIT_UNIT -> TypeView.unit();
|
||||||
|
case PLAIN -> collapse(typeFrom(returnType));
|
||||||
|
case RESULT -> TypeView.result(typeFrom(resultErrorType), collapse(typeFrom(returnType)));
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private TypeView collapse(final TypeView type) {
|
||||||
|
if (type.kind() == Kind.TUPLE && type.tupleFields().size() == 1) {
|
||||||
|
return type.tupleFields().getFirst().type();
|
||||||
|
}
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
private TypeView typeFrom(final PbsAst.TypeRef typeRef) {
|
||||||
|
if (typeRef == null) {
|
||||||
|
return TypeView.unit();
|
||||||
|
}
|
||||||
|
return switch (typeRef.kind()) {
|
||||||
|
case UNIT -> TypeView.unit();
|
||||||
|
case SELF, ERROR -> TypeView.unknown();
|
||||||
|
case SIMPLE -> {
|
||||||
|
if ("int".equals(typeRef.name())) {
|
||||||
|
yield TypeView.intType();
|
||||||
|
}
|
||||||
|
if ("float".equals(typeRef.name())) {
|
||||||
|
yield TypeView.floatType();
|
||||||
|
}
|
||||||
|
if ("bool".equals(typeRef.name())) {
|
||||||
|
yield TypeView.bool();
|
||||||
|
}
|
||||||
|
if ("str".equals(typeRef.name())) {
|
||||||
|
yield TypeView.str();
|
||||||
|
}
|
||||||
|
if (structs.containsKey(typeRef.name())) {
|
||||||
|
yield TypeView.struct(typeRef.name());
|
||||||
|
}
|
||||||
|
if (services.containsKey(typeRef.name())) {
|
||||||
|
yield TypeView.service(typeRef.name());
|
||||||
|
}
|
||||||
|
if (contracts.containsKey(typeRef.name())) {
|
||||||
|
yield TypeView.contract(typeRef.name());
|
||||||
|
}
|
||||||
|
if (enums.containsKey(typeRef.name())) {
|
||||||
|
yield TypeView.enumType(typeRef.name());
|
||||||
|
}
|
||||||
|
if (errors.containsKey(typeRef.name())) {
|
||||||
|
yield TypeView.error(typeRef.name());
|
||||||
|
}
|
||||||
|
final var callbackSignature = callbacks.get(typeRef.name());
|
||||||
|
if (callbackSignature != null) {
|
||||||
|
yield TypeView.callback(typeRef.name(), callbackSignature.inputTypes(), callbackSignature.outputType());
|
||||||
|
}
|
||||||
|
yield TypeView.unknown();
|
||||||
|
}
|
||||||
|
case OPTIONAL -> TypeView.optional(typeFrom(typeRef.inner()));
|
||||||
|
case GROUP -> typeFrom(typeRef.inner());
|
||||||
|
case NAMED_TUPLE -> {
|
||||||
|
final var fields = new ArrayList<TupleField>(typeRef.fields().size());
|
||||||
|
for (final var field : typeRef.fields()) {
|
||||||
|
fields.add(new TupleField(field.label(), typeFrom(field.typeRef())));
|
||||||
|
}
|
||||||
|
yield TypeView.tuple(fields);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static final class Scope {
|
||||||
|
private final Map<String, TypeView> names = new HashMap<>();
|
||||||
|
|
||||||
|
Scope copy() {
|
||||||
|
final var scope = new Scope();
|
||||||
|
scope.names.putAll(names);
|
||||||
|
return scope;
|
||||||
|
}
|
||||||
|
|
||||||
|
void bind(final String name, final TypeView type) {
|
||||||
|
names.put(name, type);
|
||||||
|
}
|
||||||
|
|
||||||
|
TypeView resolve(final String name) {
|
||||||
|
return names.get(name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,197 @@
|
|||||||
|
package p.studio.compiler.pbs.semantics;
|
||||||
|
|
||||||
|
import p.studio.compiler.pbs.ast.PbsAst;
|
||||||
|
import p.studio.compiler.pbs.semantics.PbsFlowSemanticSupport.Kind;
|
||||||
|
import p.studio.compiler.pbs.semantics.PbsFlowSemanticSupport.Model;
|
||||||
|
import p.studio.compiler.pbs.semantics.PbsFlowSemanticSupport.TupleField;
|
||||||
|
import p.studio.compiler.pbs.semantics.PbsFlowSemanticSupport.TypeView;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
final class PbsFlowTypeOps {
|
||||||
|
TypeView inferBinaryResult(final String operator, final TypeView left, final TypeView right) {
|
||||||
|
if ("+".equals(operator) || "-".equals(operator) || "*".equals(operator) || "/".equals(operator) || "%".equals(operator)) {
|
||||||
|
if (isFloat(left) || isFloat(right)) {
|
||||||
|
return TypeView.floatType();
|
||||||
|
}
|
||||||
|
if (isInt(left) && isInt(right)) {
|
||||||
|
return TypeView.intType();
|
||||||
|
}
|
||||||
|
return TypeView.unknown();
|
||||||
|
}
|
||||||
|
if ("==".equals(operator)
|
||||||
|
|| "!=".equals(operator)
|
||||||
|
|| "<".equals(operator)
|
||||||
|
|| "<=".equals(operator)
|
||||||
|
|| ">".equals(operator)
|
||||||
|
|| ">=".equals(operator)
|
||||||
|
|| "and".equals(operator)
|
||||||
|
|| "or".equals(operator)
|
||||||
|
|| "&&".equals(operator)
|
||||||
|
|| "||".equals(operator)) {
|
||||||
|
return TypeView.bool();
|
||||||
|
}
|
||||||
|
return TypeView.unknown();
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean compatible(final TypeView actual, final TypeView expected) {
|
||||||
|
if (actual == null || expected == null) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (actual.kind() == Kind.UNKNOWN || expected.kind() == Kind.UNKNOWN) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (actual.kind() != expected.kind()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return switch (actual.kind()) {
|
||||||
|
case UNIT, INT, FLOAT, BOOL, STR -> true;
|
||||||
|
case STRUCT, SERVICE, CONTRACT, CALLBACK, ENUM, ERROR, TYPE_REF -> actual.name().equals(expected.name());
|
||||||
|
case OPTIONAL -> compatible(actual.inner(), expected.inner());
|
||||||
|
case RESULT -> compatible(actual.errorType(), expected.errorType()) && compatible(actual.inner(), expected.inner());
|
||||||
|
case TUPLE -> tupleCompatible(actual, expected);
|
||||||
|
case UNKNOWN -> true;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean inputCompatible(final List<TypeView> inputTypes, final TypeView argumentType) {
|
||||||
|
if (inputTypes.isEmpty()) {
|
||||||
|
return isUnit(argumentType) || argumentType.kind() == Kind.UNKNOWN;
|
||||||
|
}
|
||||||
|
if (inputTypes.size() == 1) {
|
||||||
|
return compatible(argumentType, inputTypes.getFirst());
|
||||||
|
}
|
||||||
|
if (argumentType.kind() != Kind.TUPLE) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (argumentType.tupleFields().size() != inputTypes.size()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
for (int i = 0; i < inputTypes.size(); i++) {
|
||||||
|
if (!compatible(argumentType.tupleFields().get(i).type(), inputTypes.get(i))) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
TypeView callableReturnType(
|
||||||
|
final PbsAst.ReturnKind returnKind,
|
||||||
|
final PbsAst.TypeRef returnType,
|
||||||
|
final PbsAst.TypeRef resultErrorType,
|
||||||
|
final Model model) {
|
||||||
|
return switch (returnKind) {
|
||||||
|
case INFERRED_UNIT, EXPLICIT_UNIT -> TypeView.unit();
|
||||||
|
case PLAIN -> collapseReturnPayload(typeFromTypeRef(returnType, model, null));
|
||||||
|
case RESULT -> TypeView.result(
|
||||||
|
typeFromTypeRef(resultErrorType, model, null),
|
||||||
|
collapseReturnPayload(typeFromTypeRef(returnType, model, null)));
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
TypeView typeFromTypeRef(
|
||||||
|
final PbsAst.TypeRef typeRef,
|
||||||
|
final Model model,
|
||||||
|
final TypeView receiverType) {
|
||||||
|
if (typeRef == null) {
|
||||||
|
return TypeView.unit();
|
||||||
|
}
|
||||||
|
return switch (typeRef.kind()) {
|
||||||
|
case UNIT -> TypeView.unit();
|
||||||
|
case SELF -> receiverType == null ? TypeView.unknown() : receiverType;
|
||||||
|
case SIMPLE -> simpleType(typeRef.name(), model);
|
||||||
|
case OPTIONAL -> TypeView.optional(typeFromTypeRef(typeRef.inner(), model, receiverType));
|
||||||
|
case GROUP -> typeFromTypeRef(typeRef.inner(), model, receiverType);
|
||||||
|
case NAMED_TUPLE -> {
|
||||||
|
final var fields = new ArrayList<TupleField>(typeRef.fields().size());
|
||||||
|
for (final var field : typeRef.fields()) {
|
||||||
|
fields.add(new TupleField(field.label(), typeFromTypeRef(field.typeRef(), model, receiverType)));
|
||||||
|
}
|
||||||
|
yield TypeView.tuple(fields);
|
||||||
|
}
|
||||||
|
case ERROR -> TypeView.unknown();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean isScalarComparable(final TypeView type) {
|
||||||
|
return isBool(type) || isInt(type) || isFloat(type) || isStr(type);
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean isUnit(final TypeView type) {
|
||||||
|
return type.kind() == Kind.UNIT || type.kind() == Kind.UNKNOWN;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean isBool(final TypeView type) {
|
||||||
|
return type.kind() == Kind.BOOL || type.kind() == Kind.UNKNOWN;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean isInt(final TypeView type) {
|
||||||
|
return type.kind() == Kind.INT || type.kind() == Kind.UNKNOWN;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean isFloat(final TypeView type) {
|
||||||
|
return type.kind() == Kind.FLOAT || type.kind() == Kind.UNKNOWN;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean isStr(final TypeView type) {
|
||||||
|
return type.kind() == Kind.STR || type.kind() == Kind.UNKNOWN;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean tupleCompatible(final TypeView actual, final TypeView expected) {
|
||||||
|
if (actual.tupleFields().size() != expected.tupleFields().size()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
for (int i = 0; i < actual.tupleFields().size(); i++) {
|
||||||
|
if (!compatible(actual.tupleFields().get(i).type(), expected.tupleFields().get(i).type())) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private TypeView collapseReturnPayload(final TypeView type) {
|
||||||
|
if (type == null) {
|
||||||
|
return TypeView.unit();
|
||||||
|
}
|
||||||
|
if (type.kind() == Kind.TUPLE && type.tupleFields().size() == 1) {
|
||||||
|
return type.tupleFields().getFirst().type();
|
||||||
|
}
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
private TypeView simpleType(final String name, final Model model) {
|
||||||
|
if ("int".equals(name)) {
|
||||||
|
return TypeView.intType();
|
||||||
|
}
|
||||||
|
if ("float".equals(name)) {
|
||||||
|
return TypeView.floatType();
|
||||||
|
}
|
||||||
|
if ("bool".equals(name)) {
|
||||||
|
return TypeView.bool();
|
||||||
|
}
|
||||||
|
if ("str".equals(name)) {
|
||||||
|
return TypeView.str();
|
||||||
|
}
|
||||||
|
if (model.structs.containsKey(name)) {
|
||||||
|
return TypeView.struct(name);
|
||||||
|
}
|
||||||
|
if (model.services.containsKey(name)) {
|
||||||
|
return TypeView.service(name);
|
||||||
|
}
|
||||||
|
if (model.contracts.containsKey(name)) {
|
||||||
|
return TypeView.contract(name);
|
||||||
|
}
|
||||||
|
if (model.enums.containsKey(name)) {
|
||||||
|
return TypeView.enumType(name);
|
||||||
|
}
|
||||||
|
if (model.errors.containsKey(name)) {
|
||||||
|
return TypeView.error(name);
|
||||||
|
}
|
||||||
|
final var callbackSignature = model.callbacks.get(name);
|
||||||
|
if (callbackSignature != null) {
|
||||||
|
return TypeView.callback(name, callbackSignature.inputTypes(), callbackSignature.outputType());
|
||||||
|
}
|
||||||
|
return TypeView.unknown();
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user