package org.shedlang.compiler.patio; import org.shedlang.compiler.patio.instructions.*; import org.shedlang.compiler.patio.operands.*; import org.shedlang.compiler.patio.types.*; import org.shedlang.compiler.patio.values.*; import org.shedlang.compiler.treetext.Tree; import java.util.List; import java.util.Optional; import static org.shedlang.compiler.treetext.TreeSerializer.serializeTree; import static org.shedlang.compiler.treetext.Trees.*; public class PatioSerializer { private PatioSerializer() {} public static void serializeCompilationUnit( PatioCompilationUnit compilationUnit, StringBuilder builder ) { var tree = compilationUnitToTree(compilationUnit); serializeTree(tree, builder, 0); } public static void serializeCompilationUnitElement( PatioCompilationUnitElement element, StringBuilder builder ) { var tree = compilationUnitElementToTree(element); serializeTree(tree, builder, 0); } public static void serializeDataLayout( PatioDataLayout layout, StringBuilder builder ) { var tree = dataLayoutToTree(layout); serializeTree(tree, builder, 0); } public static void serializeDataPtrValue( PatioDataPtrValue value, StringBuilder builder ) { var tree = dataPtrValueToTree(value); serializeTree(tree, builder, 0); } public static void serializeDataValue( PatioDataValue value, StringBuilder builder ) { var tree = dataValueToTree(value); serializeTree(tree, builder, 0); } public static void serializeInstructions(List instructions, StringBuilder builder) { var tree = instructionsToTree(instructions); serializeTree(tree, builder, 0); } public static void serializeInstruction(PatioInstruction instruction, StringBuilder builder) { var tree = instructionToTree(instruction); serializeTree(tree, builder, 0); } public static void serializeIntValue(PatioIntValue value, StringBuilder builder) { var tree = intValueToTree(value); serializeTree(tree, builder, 0); } public static void serializeLocal(PatioLocal local, StringBuilder builder) { var tree = localToTree(local); serializeTree(tree, builder, 0); } public static void serializeOperand(PatioOperand operand, StringBuilder builder) { var tree = operandToTree(operand); serializeTree(tree, builder, 0); } public static void serializeType(PatioType type, StringBuilder builder) { var tree = typeToTree(type); serializeTree(tree, builder, 0); } private static Tree compilationUnitToTree(PatioCompilationUnit compilationUnit) { return treeListIndented( compilationUnit.elements().stream() .map(element -> compilationUnitElementToTree(element)) .toList() ); } private static Tree compilationUnitElementToTree(PatioCompilationUnitElement element) { return switch (element) { case PatioDataElementImmutableDefinition dataElementImmutableDefinition -> treeList( treeIdentifier("data-immutable"), treeString(dataElementImmutableDefinition.name()), visibilityToTree(dataElementImmutableDefinition.visibility()), dataValueToTree(dataElementImmutableDefinition.value()) ); case PatioDataElementMutableDeclaration dataElementMutableDeclaration -> treeList( treeIdentifier("data-mutable-decl"), treeString(dataElementMutableDeclaration.name()), dataLayoutToTree(dataElementMutableDeclaration.layout()) ); case PatioDataElementMutableDefinition dataElementMutableDefinition -> treeList( treeIdentifier("data-mutable"), treeString(dataElementMutableDefinition.name()), visibilityToTree(dataElementMutableDefinition.visibility()), dataValueToTree(dataElementMutableDefinition.initialValue()) ); case PatioExceptionTypeDefinition exceptionTypeDefinition -> treeList( treeIdentifier("exception-type"), treeString(exceptionTypeDefinition.name()), typeToTree(exceptionTypeDefinition.valueType()) ); case PatioFunctionDeclaration functionDeclaration -> treeListIndented( treeIdentifier("function-decl"), treeString(element.name()), treeNamed("params", treeList( functionDeclaration.params().stream() .map(param -> localToTree(param)) .toList() )), treeNamed("return-types", treeList( functionDeclaration.returnTypes().stream() .map(returnType -> typeToTree(returnType)) .toList() )) ); case PatioFunctionDefinition functionDefinition -> treeListIndented( treeIdentifier("function"), treeString(element.name()), treeNamed("visibility", visibilityToTree(functionDefinition.visibility())), treeNamed("locals", treeList( functionDefinition.locals().stream() .map(local -> localToTree(local)) .toList() )), treeNamed("return-types", treeList( functionDefinition.returnTypes().stream() .map(returnType -> typeToTree(returnType)) .toList() )), treeNamed("body", instructionsToTree(functionDefinition.body())) ); }; } private static Tree instructionsToTree(List instructions) { return treeListIndented( instructions.stream() .map(instruction -> instructionToTree(instruction)) .toList() ); } private static Tree instructionToTree(PatioInstruction instruction) { return switch (instruction) { case PatioBlock block -> blockToTree(block); case PatioBoolToInt boolToInt -> treeList( treeIdentifier("bool-to-int"), operandToTree(boolToInt.destination()), operandToTree(boolToInt.value()) ); case PatioCall call -> treeList( treeIdentifier("call"), operandsToTree(call.destinations()), treeString(call.functionName()), operandsToTree(call.args()) ); case PatioCallPointer callPointer -> treeList( treeIdentifier("call-pointer"), operandsToTree(callPointer.destinations()), operandToTree(callPointer.functionPointer()), operandsToTree(callPointer.args()) ); case PatioDataPtrToInt dataPtrToInt -> treeList( treeIdentifier("data-ptr-to-int"), operandToTree(dataPtrToInt.destination()), operandToTree(dataPtrToInt.value()) ); case PatioIf if_ -> treeListIndented( treeIdentifier("if"), operandsToTree(if_.destinations()), operandToTree(if_.condition()), blockToTree(if_.trueBody()), blockToTree(if_.falseBody()) ); case PatioIntAdd intAdd -> treeList( treeIdentifier("int-add"), operandToTree(intAdd.destination()), operandToTree(intAdd.left()), operandToTree(intAdd.right()) ); case PatioIntEquals intEquals -> treeList( treeIdentifier("int-eq"), operandToTree(intEquals.destination()), operandToTree(intEquals.left()), operandToTree(intEquals.right()) ); case PatioIntNotEqual intNotEqual -> treeList( treeIdentifier("int-ne"), operandToTree(intNotEqual.destination()), operandToTree(intNotEqual.left()), operandToTree(intNotEqual.right()) ); case PatioIntSub intSub -> treeList( treeIdentifier("int-sub"), operandToTree(intSub.destination()), operandToTree(intSub.left()), operandToTree(intSub.right()) ); case PatioIntToBool intToBool -> treeList( treeIdentifier("int-to-bool"), operandToTree(intToBool.destination()), operandToTree(intToBool.value()) ); case PatioIntToDataPtr intToDataPtr -> treeList( treeIdentifier("int-to-data-ptr"), operandToTree(intToDataPtr.destination()), operandToTree(intToDataPtr.value()) ); case PatioLocalStore localStore -> treeList( treeIdentifier("local-store"), operandToTree(localStore.destination()), operandToTree(localStore.value()) ); case PatioLoop loop -> treeListIndented( treeIdentifier("loop"), instructionsToTree(loop.body()) ); case PatioLoopBreak loopBreak -> treeList( treeIdentifier("break") ); case PatioLoopContinue loopContinue -> treeList( treeIdentifier("continue") ); case PatioMemoryLoad memoryLoad -> treeListIndented( treeIdentifier("memory-load"), operandToTree(memoryLoad.destination()), treeNamed("pointer", operandToTree(memoryLoad.pointer())), treeNamed("layout", dataLayoutToTree(memoryLoad.layout())), treeNamed("element-offsets", elementOffsetsToTree(memoryLoad.elementOffsets())) ); case PatioMemoryStore memoryStore -> treeListIndented( treeIdentifier("memory-store"), treeNamed("pointer", operandToTree(memoryStore.pointer())), treeNamed("layout", dataLayoutToTree(memoryStore.layout())), treeNamed("element-offsets", elementOffsetsToTree(memoryStore.elementOffsets())), treeNamed("value", operandToTree(memoryStore.value())) ); case PatioReturn return_ -> treeList( treeIdentifier("return"), operandsToTree(return_.values()) ); case PatioSwitch switch_ -> treeListIndented( treeIdentifier("switch"), operandsToTree(switch_.destinations()), operandToTree(switch_.value()), treeListIndented( switch_.cases().stream() .map(switchCase -> treeListIndented( treeIdentifier("case"), treeInt(switchCase.value()), blockToTree(switchCase.body()) )) .toList() ), blockToTree(switch_.defaultBody()) ); case PatioTry try_ -> treeListIndented( treeIdentifier("try"), operandsToTree(try_.destinations()), treeListIndented( try_.catchClauses().stream() .map(catchClause -> treeListIndented( treeIdentifier("catch"), treeString(catchClause.exceptionTypeName()), operandToTree(catchClause.destination()), blockToTree(catchClause.body()) )) .toList() ), blockToTree(try_.body()) ); case PatioThrow throw_ -> treeList( treeIdentifier("throw"), treeString(throw_.exceptionTypeName()), operandToTree(throw_.value()) ); case PatioUnreachable unreachable -> treeList(treeIdentifier("unreachable")); case PatioVarLoad varLoad -> treeList( treeIdentifier("var-load"), operandToTree(varLoad.destination()), operandToTree(varLoad.var()) ); case PatioVarStore varStore -> treeList( treeIdentifier("var-store"), operandToTree(varStore.var()), operandToTree(varStore.value()) ); }; } private static Tree blockToTree(PatioBlock block) { return treeListIndented( treeIdentifier("block"), instructionsToTree(block.body()), treeNamed( "results", treeList( block.results().stream() .map(result -> operandToTree(result)) .toList() ) ) ); } private static Tree localToTree(PatioLocal local) { var localType = switch (local.localType()) { case PARAM -> "param"; case VAL -> "val"; case VAR -> "var"; }; return treeList( treeIdentifier(localType), treeString(local.name()), typeToTree(local.type()) ); } private static Tree optionalOperandToTree(Optional destination) { if (destination.isEmpty()) { return treeIdentifier("_"); } else { return operandToTree(destination.get()); } } private static Tree operandsToTree(List operands) { return treeList( operands.stream() .map(destination -> operandToTree(destination)) .toList() ); } private static Tree operandToTree(PatioOperand operand) { return switch (operand) { case PatioBoolConst bool -> treeList(treeIdentifier("bool-const"), treeBool(bool.value())); case PatioByteConst byte_ -> treeList(treeIdentifier("byte-const"), treeInt(byte_.value())); case PatioDataPtrConst dataPtr -> treeList(treeIdentifier("data-ptr-const"), dataPtrValueToTree(dataPtr.value())); case PatioFunctionPtrConst functionPtr -> treeList(treeIdentifier("function-ptr-const"), treeString(functionPtr.name())); case PatioIntConst int_ -> treeList(treeIdentifier("int-const"), intValueToTree(int_.value())); case PatioInt32Const int32 -> treeList(treeIdentifier("int32-const"), intValueToTree(int32.value())); case PatioLocalRef localRef -> treeList(treeIdentifier("local"), treeString(localRef.name())); }; } private static Tree typeToTree(PatioType type) { return switch (type) { case PatioBoolType boolType -> treeIdentifier("bool"); case PatioByteType byteType -> treeIdentifier("byte"); case PatioDataPtrType dataPointerType -> treeIdentifier("data-ptr"); case PatioFunctionPtrType functionPointerType -> treeIdentifier("function-ptr"); case PatioIntType intType -> treeIdentifier("int"); case PatioInt32Type int32Type -> treeIdentifier("int32"); }; } private static Tree dataLayoutToTree(PatioDataLayout layout) { return switch (layout) { case PatioDataLayoutArray array -> treeList( treeIdentifier("array"), dataLayoutToTree(array.element()), treeInt(array.size()) ); case PatioDataLayoutBool bool -> treeIdentifier("bool"); case PatioDataLayoutByte byte_ -> treeIdentifier("byte"); case PatioDataLayoutDataPtr dataPointer -> treeIdentifier("data-ptr"); case PatioDataLayoutFunctionPtr functionPointer -> treeIdentifier("function-ptr"); case PatioDataLayoutInt int_ -> treeIdentifier("int"); case PatioDataLayoutInt32 int32 -> treeIdentifier("int32"); case PatioDataLayoutStruct struct -> treeList( treeIdentifier("struct"), treeList( struct.elements().stream() .map(element -> dataLayoutToTree(element)) .toList() ) ); }; } private static Tree dataValueToTree(PatioDataValue value) { return switch (value) { case PatioDataValueArray array -> treeList( treeIdentifier("array"), dataLayoutToTree(array.elementLayout()), treeList( array.elements().stream() .map(element -> dataValueToTree(element)) .toList() ) ); case PatioDataValueBool bool -> treeList( treeIdentifier("bool"), treeIdentifier(bool.value() ? "true" : "false") ); case PatioDataValueByte byte_ -> treeList( treeIdentifier("byte"), treeIdentifier(String.format("0x%02x", byte_.value())) ); case PatioDataValueDataPtr dataPointer -> treeList( treeIdentifier("data-ptr"), dataPtrValueToTree(dataPointer.value()) ); case PatioDataValueInt int_ -> treeList( treeIdentifier("int"), intValueToTree(int_.value()) ); case PatioDataValueInt32 int32 -> treeList( treeIdentifier("int32"), intValueToTree(int32.value()) ); case PatioDataValueStruct struct -> treeList( treeIdentifier("struct"), treeList( struct.elements().stream() .map(element -> dataValueToTree(element)) .toList() ) ); }; } private static Tree dataPtrValueToTree(PatioDataPtrValue dataPtrValue) { return switch (dataPtrValue) { case PatioDataPtrValueDataElement dataElement -> treeString(dataElement.name()); case PatioDataPtrValueNull null_ -> treeIdentifier("null"); }; } private static Tree intValueToTree(PatioIntValue intValue) { return switch (intValue) { case PatioIntValueByteOffsetOf byteOffsetOf -> treeList( treeIdentifier("byte-offset-of"), dataLayoutToTree(byteOffsetOf.layout()), elementOffsetsToTree(byteOffsetOf.elementOffsets()) ); case PatioIntValueByteSizeOf byteSizeOf -> treeList( treeIdentifier("byte-size-of"), dataLayoutToTree(byteSizeOf.layout()) ); case PatioIntValueLiteral literal -> treeInt(literal.value()); }; } private static Tree elementOffsetsToTree(List elementOffsets) { return treeList( elementOffsets.stream() .map(offset -> treeInt(offset)) .toList() ); } private static Tree visibilityToTree(PatioVisibility visibility) { return treeIdentifier(switch (visibility) { case PRIVATE -> "private"; case PUBLIC -> "public"; }); } }