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:
ThrowingErrorReporter(kj::StringPtr input)39   ThrowingErrorReporter(kj::StringPtr input): input(input) {}
40 
addError(uint32_t startByte,uint32_t endByte,kj::StringPtr message)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 
hadErrors()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>
resolveConstant(capnp::compiler::Expression::Reader name)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>>
readEmbed(capnp::compiler::LocatedText::Reader filename)73   readEmbed(capnp::compiler::LocatedText::Reader filename) override {
74     KJ_FAIL_REQUIRE("External embeds not allowed.");
75   }
76 };
77 
78 template <typename Function>
lexAndParseExpression(kj::StringPtr input,Function f)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 
TextCodec()113 TextCodec::TextCodec() : prettyPrint(false) {}
~TextCodec()114 TextCodec::~TextCodec() noexcept(true) {}
115 
setPrettyPrint(bool enabled)116 void TextCodec::setPrettyPrint(bool enabled) { prettyPrint = enabled; }
117 
encode(DynamicValue::Reader value) const118 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 
decode(kj::StringPtr input,DynamicStruct::Builder output) const132 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 
decode(kj::StringPtr input,Type type,Orphanage orphanage) const145 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
164