package org.shedlang.compiler.shid; import org.junit.jupiter.api.Test; import org.shedlang.compiler.ast.typed.Typed; import org.shedlang.compiler.shid.instructions.ShidDefineFunction; import org.shedlang.compiler.shid.instructions.ShidFieldLoad; import org.shedlang.compiler.shid.instructions.ShidMatchers; import org.shedlang.compiler.types.*; import java.util.List; import static org.shedlang.compiler.shid.instructions.ShidMatchers.*; import static org.zwobble.precisely.AssertThat.assertThat; import static org.zwobble.precisely.Matchers.*; import static org.zwobble.precisely.Matchers.has; public class ShidCodeGenMemberAccessTests { @Test public void namespaceFieldAccessIsCompiledToFieldLoad() { var namespaceType = Types.namespace(NamespaceName.of("Example", "Project")); var node = Typed.fieldAccess( Typed.reference("Project", namespaceType), "name", Types.STRING ); var context = ShidCodeGen.Context.EMPTY .addShedVal("Project"); var result = ShidCodeGen.compileExpression(node, context); assertThat(result.instructions(), isSequence( isShidLocalLoad("Project__0"), ShidMatchers.isShidFieldLoad( new ShidFieldLoad.Receiver.Namespace(namespaceType.namespaceName()), "name" ) )); } @Test public void structFieldAccessIsCompiledToFieldLoad() { var structType = Types.struct(NamespaceName.of("Example", "Project"), "User"); var node = Typed.fieldAccess( Typed.reference("user", structType), "name", Types.STRING ); var context = ShidCodeGen.Context.EMPTY .addShedVal("user"); var result = ShidCodeGen.compileExpression(node, context); assertThat(result.instructions(), isSequence( isShidLocalLoad("user__0"), isShidFieldLoad(structType, "name") )); } @Test public void structMethodAccessZeroParamsIsCompiledToDefineFunctionInstruction() { var namespaceName = NamespaceName.of("Example", "Project"); var structType = Types.struct(namespaceName, "User"); var node = Typed.methodAccess( Typed.reference("user", structType), "name", FunctionType.builder() .withReturnType(Types.STRING) .build() ); var context = ShidCodeGen.Context.EMPTY .addShedVal("user"); var result = ShidCodeGen.compileExpression(node, context); assertThat(result.instructions(), isSequence( isShidLocalLoad("user__0"), isShidLocalStore("receiver__0"), instanceOf( ShidDefineFunction.class, has("name", x -> x.name(), equalTo("name")), has("positionalParams", x -> x.positionalParams(), isSequence()), has("namedParams", x -> x.namedParams(), isSequence()), has("freeVariables", x -> x.freeVariables(), containsExactly( has("user", x -> x.name(), equalTo("receiver__0")) )), has("body", x -> x.body(), isShidBlock(isSequence( isShidMethodLoad(structType, "name"), isShidLocalLoad("receiver__0"), isShidCall(1, List.of()), isShidReturn() ))) ) )); } @Test public void structMethodAccessMultipleParamsIsCompiledToDefineFunctionInstruction() { var namespaceName = NamespaceName.of("Example", "Project"); var structType = Types.struct(namespaceName, "User"); var node = Typed.methodAccess( Typed.reference("user", structType), "name", FunctionType.builder() .addPositionalParam(Types.BOOL) .addNamedParam("b", Types.INT) .withReturnType(Types.STRING) .build() ); var context = ShidCodeGen.Context.EMPTY .addShedVal("user"); var result = ShidCodeGen.compileExpression(node, context); assertThat(result.instructions(), isSequence( isShidLocalLoad("user__0"), isShidLocalStore("receiver__0"), instanceOf( ShidDefineFunction.class, has("name", x -> x.name(), equalTo("name")), has("positionalParams", x -> x.positionalParams(), isSequence( isShidDefineFunctionParam("arg0__0") )), has("namedParams", x -> x.namedParams(), isSequence( isShidDefineFunctionParam("b__0") )), has("freeVariables", x -> x.freeVariables(), containsExactly( has("user", x -> x.name(), equalTo("receiver__0")) )), has("body", x -> x.body(), isShidBlock(isSequence( isShidMethodLoad(structType, "name"), isShidLocalLoad("receiver__0"), isShidLocalLoad("arg0__0"), isShidLocalLoad("b__0"), isShidCall(2, List.of("b")), isShidReturn() ))) ) )); } @Test public void nativeMethodAccessZeroParamsIsCompiledToDefineFunctionInstruction() { var namespaceName = NamespaceName.of("Example", "Project"); var nativeType = Types.nativeType(namespaceName, "User"); var node = Typed.methodAccess( Typed.reference("user", nativeType), "name", FunctionType.builder() .withReturnType(Types.STRING) .build() ); var context = ShidCodeGen.Context.EMPTY .addShedVal("user"); var result = ShidCodeGen.compileExpression(node, context); assertThat(result.instructions(), isSequence( isShidLocalLoad("user__0"), isShidLocalStore("receiver__0"), instanceOf( ShidDefineFunction.class, has("name", x -> x.name(), equalTo("name")), has("positionalParams", x -> x.positionalParams(), isSequence()), has("namedParams", x -> x.namedParams(), isSequence()), has("freeVariables", x -> x.freeVariables(), containsExactly( has("user", x -> x.name(), equalTo("receiver__0")) )), has("body", x -> x.body(), isShidBlock(isSequence( isShidMethodLoad(nativeType, "name"), isShidLocalLoad("receiver__0"), isShidCall(1, List.of()), isShidReturn() ))) ) )); } @Test public void constructedTypeFieldAccessIsCompiledToFieldLoad() { var structType = Types.struct(NamespaceName.of("Example", "Project"), "Box"); var typeConstructor = Types.typeConstructor( List.of(TypeParam.forStructType(structType, "T")), structType ); var constructedType = Types.constructedStructType( typeConstructor, List.of(Types.STRING) ); var node = Typed.fieldAccess( Typed.reference("box", constructedType), "value", Types.STRING ); var context = ShidCodeGen.Context.EMPTY .addShedVal("box"); var result = ShidCodeGen.compileExpression(node, context); assertThat(result.instructions(), isSequence( isShidLocalLoad("box__0"), isShidFieldLoad(structType, "value") )); } @Test public void userDefinedEffectFieldAccessIsCompiledToFieldLoad() { var effect = Effects.userDefined(NamespaceName.of("Example", "Project"), "State"); var node = Typed.fieldAccess( Typed.reference("State", Types.effectType(effect)), "read", Types.STRING ); var context = ShidCodeGen.Context.EMPTY .addShedVal("State"); var result = ShidCodeGen.compileExpression(node, context); assertThat(result.instructions(), isSequence( isShidLocalLoad("State__0"), ShidMatchers.isShidFieldLoad( new ShidFieldLoad.Receiver.Effect(effect), "read" ) )); } }