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