serialize-text.c++ (5722B)
1 // Copyright (c) 2015 Philip Quinn. 2 // Licensed under the MIT License: 3 // 4 // Permission is hereby granted, free of charge, to any person obtaining a copy 5 // of this software and associated documentation files (the "Software"), to deal 6 // in the Software without restriction, including without limitation the rights 7 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 // copies of the Software, and to permit persons to whom the Software is 9 // furnished to do so, subject to the following conditions: 10 // 11 // The above copyright notice and this permission notice shall be included in 12 // all copies or substantial portions of the Software. 13 // 14 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 // THE SOFTWARE. 21 22 #include "serialize-text.h" 23 24 #include <kj/debug.h> 25 26 #include "pretty-print.h" 27 #include "compiler/lexer.capnp.h" 28 #include "compiler/lexer.h" 29 #include "compiler/node-translator.h" 30 #include "compiler/parser.h" 31 32 namespace capnp { 33 34 namespace { 35 36 class ThrowingErrorReporter final: public capnp::compiler::ErrorReporter { 37 // Throws all errors as assertion failures. 38 public: 39 ThrowingErrorReporter(kj::StringPtr input): input(input) {} 40 41 void addError(uint32_t startByte, uint32_t endByte, kj::StringPtr message) override { 42 // Note: Line and column numbers are usually 1-based. 43 uint line = 1; 44 uint32_t lineStart = 0; 45 for (auto i: kj::zeroTo(startByte)) { 46 if (input[i] == '\n') { 47 ++line; 48 lineStart = i; // Omit +1 so that column is 1-based. 49 } 50 } 51 52 kj::throwRecoverableException(kj::Exception( 53 kj::Exception::Type::FAILED, "(capnp text input)", line, 54 kj::str(startByte - lineStart, "-", endByte - lineStart, ": ", message) 55 )); 56 } 57 58 bool hadErrors() override { return false; } 59 60 private: 61 kj::StringPtr input; 62 }; 63 64 class ExternalResolver final: public capnp::compiler::ValueTranslator::Resolver { 65 // Throws all external resolution requests as assertion failures. 66 public: 67 kj::Maybe<capnp::DynamicValue::Reader> 68 resolveConstant(capnp::compiler::Expression::Reader name) override { 69 KJ_FAIL_REQUIRE("External constants not allowed."); 70 } 71 72 kj::Maybe<kj::Array<const capnp::byte>> 73 readEmbed(capnp::compiler::LocatedText::Reader filename) override { 74 KJ_FAIL_REQUIRE("External embeds not allowed."); 75 } 76 }; 77 78 template <typename Function> 79 void lexAndParseExpression(kj::StringPtr input, Function f) { 80 // Parses a single expression from the input and calls `f(expression)`. 81 82 ThrowingErrorReporter errorReporter(input); 83 84 capnp::MallocMessageBuilder tokenArena; 85 auto lexedTokens = tokenArena.initRoot<capnp::compiler::LexedTokens>(); 86 capnp::compiler::lex(input, lexedTokens, errorReporter); 87 88 capnp::compiler::CapnpParser parser(tokenArena.getOrphanage(), errorReporter); 89 auto tokens = lexedTokens.asReader().getTokens(); 90 capnp::compiler::CapnpParser::ParserInput parserInput(tokens.begin(), tokens.end()); 91 92 if (parserInput.getPosition() != tokens.end()) { 93 KJ_IF_MAYBE(expression, parser.getParsers().expression(parserInput)) { 94 // The input is expected to contain a *single* message. 95 KJ_REQUIRE(parserInput.getPosition() == tokens.end(), "Extra tokens in input."); 96 97 f(expression->getReader()); 98 } else { 99 auto best = parserInput.getBest(); 100 if (best == tokens.end()) { 101 KJ_FAIL_REQUIRE("Premature end of input."); 102 } else { 103 errorReporter.addErrorOn(*best, "Parse error"); 104 } 105 } 106 } else { 107 KJ_FAIL_REQUIRE("Failed to read input."); 108 } 109 } 110 111 } // namespace 112 113 TextCodec::TextCodec() : prettyPrint(false) {} 114 TextCodec::~TextCodec() noexcept(true) {} 115 116 void TextCodec::setPrettyPrint(bool enabled) { prettyPrint = enabled; } 117 118 kj::String TextCodec::encode(DynamicValue::Reader value) const { 119 if (!prettyPrint) { 120 return kj::str(value); 121 } else { 122 if (value.getType() == DynamicValue::Type::STRUCT) { 123 return capnp::prettyPrint(value.as<DynamicStruct>()).flatten(); 124 } else if (value.getType() == DynamicValue::Type::LIST) { 125 return capnp::prettyPrint(value.as<DynamicList>()).flatten(); 126 } else { 127 return kj::str(value); 128 } 129 } 130 } 131 132 void TextCodec::decode(kj::StringPtr input, DynamicStruct::Builder output) const { 133 lexAndParseExpression(input, [&](compiler::Expression::Reader expression) { 134 KJ_REQUIRE(expression.isTuple(), "Input does not contain a struct.") { return; } 135 136 ThrowingErrorReporter errorReporter(input); 137 ExternalResolver nullResolver; 138 139 Orphanage orphanage = Orphanage::getForMessageContaining(output); 140 compiler::ValueTranslator translator(nullResolver, errorReporter, orphanage); 141 translator.fillStructValue(output, expression.getTuple()); 142 }); 143 } 144 145 Orphan<DynamicValue> TextCodec::decode(kj::StringPtr input, Type type, Orphanage orphanage) const { 146 Orphan<DynamicValue> output; 147 148 lexAndParseExpression(input, [&](compiler::Expression::Reader expression) { 149 ThrowingErrorReporter errorReporter(input); 150 ExternalResolver nullResolver; 151 152 compiler::ValueTranslator translator(nullResolver, errorReporter, orphanage); 153 KJ_IF_MAYBE(value, translator.compileValue(expression, type)) { 154 output = *kj::mv(value); 155 } else { 156 // An error should have already been given to the errorReporter. 157 } 158 }); 159 160 return output; 161 } 162 163 } // namespace capnp