capnproto

FORK: Cap'n Proto serialization/RPC system - core tools and C++ library
git clone https://git.neptards.moe/neptards/capnproto.git
Log | Files | Refs | README | LICENSE

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