package org.shedlang.compiler.shid; import org.junit.jupiter.api.Test; import org.shedlang.compiler.shid.instructions.*; import org.shedlang.compiler.types.Effects; import org.shedlang.compiler.types.NamespaceName; import org.shedlang.compiler.types.Types; import java.util.List; import static org.zwobble.precisely.AssertThat.assertThat; import static org.zwobble.precisely.Matchers.equalTo; public class ShidSerializerTests { @Test public void canSerializeBlock() { var instruction = Shid.block( List.of(new ShidVariable("x", ShidVariable.Variant.VAR)), List.of(Shid.pushInt(1)) ); var string = serialize(instruction); assertThat(string, equalTo(""" ( block ( (var "x") ) ( (push-int 1) ) )""")); } @Test public void canSerializeCall() { var instruction = Shid.call(1, List.of("x", "y")); var string = serialize(instruction); assertThat(string, equalTo("(call positional-arg-count=1 named-arg-names=(\"x\" \"y\"))")); } @Test public void canSerializeDeclareNativeFunction() { var instruction = ShidDeclareNativeFunction.builder() .withNamespaceName(NamespaceName.of("Example", "Project")) .withName("f") .addPositionalParam("p1") .addPositionalParam("p2") .addNamedParam("n1") .addNamedParam("n2") .build(); var string = serialize(instruction); assertThat(string, equalTo(""" ( declare-native-function namespace-name="Example/Project" name="f" positional-params=("p1" "p2") named-params=("n1" "n2") )""")); } @Test public void canSerializeDeclareNativeType() { var instruction = Shid.declareNativeType( Types.nativeType(NamespaceName.of("Example", "Project"), "Array") ); var string = serialize(instruction); assertThat(string, equalTo(""" (declare-native-type "Example/Project.Array")""")); } @Test public void canSerializeDefineEffect() { var instruction = Shid.defineEffect( Effects.userDefined(NamespaceName.of("Example", "Project"), "State") ); var string = serialize(instruction); assertThat(string, equalTo(""" (define-effect "Example/Project.State")""" )); } @Test public void canSerializeDefineFunction() { var instruction = ShidDefineFunction.builder() .withName("f") .addPositionalParam("p1") .addPositionalParam("p2") .addNamedParam("n1") .addNamedParam("n2") .addFreeVariable("fv1") .addFreeVariable("fv2") .addBodyInstruction(Shid.pushBool(true)) .addBodyInstruction(Shid.pushBool(false)) .build(); var string = serialize(instruction); assertThat(string, equalTo(""" ( define-function variant="function" name="f" positional-params=("p1" "p2") named-params=("n1" "n2") free-variables=("fv1" "fv2") body=( block ( ) ( (push-bool true) (push-bool false) ) ) )""")); } @Test public void canSerializeDefineStruct() { var instruction = Shid.defineStruct( Types.struct(NamespaceName.of("Example", "Project"), "User"), List.of( Shid.defineStructField("name"), Shid.defineStructField("id") ) ); var string = serialize(instruction); assertThat(string, equalTo(""" ( define-struct type="Example/Project.User" fields=("name" "id") )""")); } @Test public void canSerializeDiscard() { var instruction = Shid.discard(); var string = serialize(instruction); assertThat(string, equalTo("(discard)")); } @Test public void canSerializeDuplicate() { var instruction = Shid.duplicate(); var string = serialize(instruction); assertThat(string, equalTo("(duplicate)")); } @Test public void canSerializeEffectHandlerBreak() { var effect = Effects.userDefined(NamespaceName.of("Example", "Project"), "State"); var instruction = Shid.effectHandlerBreak(effect); var string = serialize(instruction); assertThat(string, equalTo(""" (effect-handler-break "Example/Project.State")""")); } @Test public void canSerializeNamespaceFieldLoad() { var instruction = Shid.fieldLoad( new NamespaceName(List.of("Example", "Lib")), "x" ); var string = serialize(instruction); assertThat(string, equalTo("(field-load (namespace \"Example/Lib\") \"x\")")); } @Test public void canSerializeStructFieldLoad() { var instruction = Shid.fieldLoad( Types.struct(NamespaceName.of("Example", "Project"), "User"), "name" ); var string = serialize(instruction); assertThat(string, equalTo("(field-load (struct \"Example/Project.User\") \"name\")")); } @Test public void canSerializeUserDefinedEffectFieldLoad() { var instruction = Shid.fieldLoad( Effects.userDefined(NamespaceName.of("Example", "Project"), "State"), "read" ); var string = serialize(instruction); assertThat(string, equalTo("(field-load (effect \"Example/Project.State\") \"read\")")); } @Test public void canSerializeIf() { var instruction = Shid.if_( 1, Shid.block(List.of( Shid.pushUnit() )), Shid.block(List.of( Shid.localLoad("x"), Shid.discard(), Shid.pushBool(true) )) ); var string = serialize(instruction); assertThat(string, equalTo(""" ( if result-count=1 ( block ( ) ( (push-unit) ) ) ( block ( ) ( (local-load "x") (discard) (push-bool true) ) ) )""")); } @Test public void canSerializeIntAdd() { var instruction = Shid.intAdd(); var string = serialize(instruction); assertThat(string, equalTo("(int-add)")); } @Test public void canSerializeIntEquals() { var instruction = Shid.intEquals(); var string = serialize(instruction); assertThat(string, equalTo("(int-eq)")); } @Test public void canSerializeIntGreaterThan() { var instruction = Shid.intGreaterThan(); var string = serialize(instruction); assertThat(string, equalTo("(int-gt)")); } @Test public void canSerializeIntGreaterThanOrEqual() { var instruction = Shid.intGreaterThanOrEqual(); var string = serialize(instruction); assertThat(string, equalTo("(int-gte)")); } @Test public void canSerializeIntLessThan() { var instruction = Shid.intLessThan(); var string = serialize(instruction); assertThat(string, equalTo("(int-lt)")); } @Test public void canSerializeIntLessThanOrEqual() { var instruction = Shid.intLessThanOrEqual(); var string = serialize(instruction); assertThat(string, equalTo("(int-lte)")); } @Test public void canSerializeIntMultiply() { var instruction = Shid.intMultiply(); var string = serialize(instruction); assertThat(string, equalTo("(int-mul)")); } @Test public void canSerializeIntNotEqual() { var instruction = Shid.intNotEqual(); var string = serialize(instruction); assertThat(string, equalTo("(int-ne)")); } @Test public void canSerializeIntSub() { var instruction = Shid.intSub(); var string = serialize(instruction); assertThat(string, equalTo("(int-sub)")); } @Test public void canSerializeLocalDrop() { var instruction = Shid.localDrop("x"); var string = serialize(instruction); assertThat(string, equalTo("(local-drop \"x\")")); } @Test public void canSerializeLocalLoad() { var instruction = Shid.localLoad("x"); var string = serialize(instruction); assertThat(string, equalTo("(local-load \"x\")")); } @Test public void canSerializeLocalStore() { var instruction = Shid.localStore("x"); var string = serialize(instruction); assertThat(string, equalTo("(local-store \"x\")")); } @Test public void canSerializeLoop() { var instruction = Shid.loop( Shid.block(List.of( Shid.localLoad("x"), Shid.discard() )) ); var string = serialize(instruction); assertThat(string, equalTo(""" ( loop ( block ( ) ( (local-load "x") (discard) ) ) )""")); } @Test public void canSerializeLoopBreak() { var instruction = Shid.loopBreak(); var string = serialize(instruction); assertThat(string, equalTo("(break)")); } @Test public void canSerializeLoopContinue() { var instruction = Shid.loopContinue(); var string = serialize(instruction); assertThat(string, equalTo("(continue)")); } @Test public void canSerializeMethodLoad() { var instruction = Shid.methodLoad( Types.struct(NamespaceName.of("Example", "Lib"), "User"), "name" ); var string = serialize(instruction); assertThat(string, equalTo("(method-load \"Example/Lib.User\" \"name\")")); } @Test public void canSerializeMethodStore() { var instruction = Shid.methodStore( Types.struct(NamespaceName.of("Example", "Lib"), "User"), "name" ); var string = serialize(instruction); assertThat(string, equalTo("(method-store \"Example/Lib.User\" \"name\")")); } @Test public void canSerializeNamespaceFieldStore() { var instruction = Shid.namespaceFieldStore( new NamespaceName(List.of("Example", "Lib")), "x" ); var string = serialize(instruction); assertThat(string, equalTo("(namespace-field-store \"Example/Lib\" \"x\")")); } @Test public void canSerializeNamespaceImport() { var instruction = Shid.namespaceImport( new NamespaceName(List.of("Example", "Lib")) ); var string = serialize(instruction); assertThat(string, equalTo("(namespace-import \"Example/Lib\")")); } @Test public void canSerializePushBool() { var instruction = Shid.pushBool(true); var string = serialize(instruction); assertThat(string, equalTo("(push-bool true)")); } @Test public void canSerializePushInt() { var instruction = Shid.pushInt(42); var string = serialize(instruction); assertThat(string, equalTo("(push-int 42)")); } @Test public void canSerializePushString() { var instruction = Shid.pushString("hello"); var string = serialize(instruction); assertThat(string, equalTo("(push-string \"hello\")")); } @Test public void specialCharactersInStringAreEscaped() { var instruction = Shid.pushString("\b\t\n\f\r\"\\"); var string = serialize(instruction); assertThat(string, equalTo("(push-string \"\\b\\t\\n\\f\\r\\\"\\\\\")")); } @Test public void canSerializePushUnit() { var instruction = Shid.pushUnit(); var string = serialize(instruction); assertThat(string, equalTo("(push-unit)")); } @Test public void canSerializeResume() { var instruction = Shid.resume(); var string = serialize(instruction); assertThat(string, equalTo("(resume)")); } @Test public void canSerializeReturn() { var instruction = Shid.return_(); var string = serialize(instruction); assertThat(string, equalTo("(return)")); } @Test public void canSerializeStringAdd() { var instruction = Shid.stringAdd(); var string = serialize(instruction); assertThat(string, equalTo("(string-add)")); } @Test public void canSerializeSwitch() { var instruction = Shid.switch_(List.of( Shid.switchCase( Types.struct(NamespaceName.of("Example", "Project"), "A"), Shid.block(List.of( Shid.return_() )) ), Shid.switchCase( Types.struct(NamespaceName.of("Example", "Project"), "B"), Shid.block(List.of( Shid.discard() )) ) )); var string = serialize(instruction); assertThat(string, equalTo(""" ( switch ( ( "Example/Project.A" ( block ( ) ( (return) ) ) ) ( "Example/Project.B" ( block ( ) ( (discard) ) ) ) ) )""" )); } @Test public void canSerializeTry() { var effect = Effects.userDefined(NamespaceName.of("Example", "Project"), "State"); var instruction = Shid.try_( effect, List.of("read", "write"), Shid.block(List.of( Shid.pushUnit() )) ); var string = serialize(instruction); assertThat(string, equalTo(""" ( try "Example/Project.State" ( "read" "write" ) ( block ( ) ( (push-unit) ) ) )""")); } @Test public void canSerializeTupleCreate() { var instruction = Shid.tupleCreate(2); var string = serialize(instruction); assertThat(string, equalTo("(tuple-create 2)")); } @Test public void canSerializeTupleGet() { var instruction = Shid.tupleGet(2, 3); var string = serialize(instruction); assertThat(string, equalTo("(tuple-get 2 3)")); } @Test public void canSerializeVarLoad() { var instruction = Shid.varLoad("x"); var string = serialize(instruction); assertThat(string, equalTo("(var-load \"x\")")); } @Test public void canSerializeVarStore() { var instruction = Shid.varStore("x"); var string = serialize(instruction); assertThat(string, equalTo("(var-store \"x\")")); } private String serialize(ShidInstruction instruction) { var builder = new StringBuilder(); ShidSerializer.serializeInstruction(instruction, builder); return builder.toString(); } }