/* * Souffle - A Datalog Compiler * Copyright (c) 2021, The Souffle Developers. All rights reserved * Licensed under the Universal Permissive License v 1.0 as shown at: * - https://opensource.org/licenses/UPL * - /licenses/SOUFFLE-UPL.txt */ /************************************************************************ * * @file WriteStream.h * ***********************************************************************/ #pragma once #include "souffle/RamTypes.h" #include "souffle/RecordTable.h" #include "souffle/SymbolTable.h" #include "souffle/io/SerialisationStream.h" #include "souffle/utility/MiscUtil.h" #include "souffle/utility/json11.h" #include #include #include #include #include #include #include namespace souffle { using json11::Json; class WriteStream : public SerialisationStream { public: WriteStream(const std::map& rwOperation, const SymbolTable& symbolTable, const RecordTable& recordTable) : SerialisationStream(symbolTable, recordTable, rwOperation), summary(rwOperation.at("IO") == "stdoutprintsize") {} template void writeAll(const T& relation) { if (summary) { return writeSize(relation.size()); } if (arity == 0) { if (relation.begin() != relation.end()) { writeNullary(); } return; } for (const auto& current : relation) { writeNext(current); } } template void writeSize(const T& relation) { writeSize(relation.size()); } protected: const bool summary; virtual void writeNullary() = 0; virtual void writeNextTuple(const RamDomain* tuple) = 0; virtual void writeSize(std::size_t) { fatal("attempting to print size of a write operation"); } template void writeNext(const Tuple tuple) { using tcb::make_span; writeNextTuple(make_span(tuple).data()); } virtual void outputSymbol(std::ostream& destination, const std::string& value) { destination << value; } void outputRecord(std::ostream& destination, const RamDomain value, const std::string& name) { auto&& recordInfo = types["records"][name]; // Check if record type information are present assert(!recordInfo.is_null() && "Missing record type information"); // Check for nil if (value == 0) { destination << "nil"; return; } auto&& recordTypes = recordInfo["types"]; const std::size_t recordArity = recordInfo["arity"].long_value(); const RamDomain* tuplePtr = recordTable.unpack(value, recordArity); destination << "["; // print record's elements for (std::size_t i = 0; i < recordArity; ++i) { if (i > 0) { destination << ", "; } const std::string& recordType = recordTypes[i].string_value(); const RamDomain recordValue = tuplePtr[i]; switch (recordType[0]) { case 'i': destination << recordValue; break; case 'f': destination << ramBitCast(recordValue); break; case 'u': destination << ramBitCast(recordValue); break; case 's': outputSymbol(destination, symbolTable.decode(recordValue)); break; case 'r': outputRecord(destination, recordValue, recordType); break; case '+': outputADT(destination, recordValue, recordType); break; default: fatal("Unsupported type attribute: `%c`", recordType[0]); } } destination << "]"; } void outputADT(std::ostream& destination, const RamDomain value, const std::string& name) { auto&& adtInfo = types["ADTs"][name]; assert(!adtInfo.is_null() && "Missing adt type information"); assert(adtInfo["arity"].long_value() > 0); // adt is encoded in one of three possible ways: // [branchID, [branch_args]] when |branch_args| != 1 // [branchID, arg] when a branch takes a single argument. // branchID when ADT is an enumeration. bool isEnum = adtInfo["enum"].bool_value(); RamDomain branchId = value; const RamDomain* branchArgs = nullptr; json11::Json branchInfo; json11::Json::array branchTypes; if (!isEnum) { const RamDomain* tuplePtr = recordTable.unpack(value, 2); branchId = tuplePtr[0]; branchInfo = adtInfo["branches"][branchId]; branchTypes = branchInfo["types"].array_items(); // Prepare branch's arguments for output. branchArgs = [&]() -> const RamDomain* { if (branchTypes.size() > 1) { return recordTable.unpack(tuplePtr[1], branchTypes.size()); } else { return &tuplePtr[1]; } }(); } else { branchInfo = adtInfo["branches"][branchId]; branchTypes = branchInfo["types"].array_items(); } destination << "$" << branchInfo["name"].string_value(); if (branchTypes.size() > 0) { destination << "("; } // Print arguments for (std::size_t i = 0; i < branchTypes.size(); ++i) { if (i > 0) { destination << ", "; } auto argType = branchTypes[i].string_value(); switch (argType[0]) { case 'i': destination << branchArgs[i]; break; case 'f': destination << ramBitCast(branchArgs[i]); break; case 'u': destination << ramBitCast(branchArgs[i]); break; case 's': outputSymbol(destination, symbolTable.decode(branchArgs[i])); break; case 'r': outputRecord(destination, branchArgs[i], argType); break; case '+': outputADT(destination, branchArgs[i], argType); break; default: fatal("Unsupported type attribute: `%c`", argType[0]); } } if (branchTypes.size() > 0) { destination << ")"; } } }; class WriteStreamFactory { public: virtual Own getWriter(const std::map& rwOperation, const SymbolTable& symbolTable, const RecordTable& recordTable) = 0; virtual const std::string& getName() const = 0; virtual ~WriteStreamFactory() = default; }; template <> inline void WriteStream::writeNext(const RamDomain* tuple) { writeNextTuple(tuple); } } /* namespace souffle */