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