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
32namespace {
33
34class ThrowingErrorReporter final: public capnp::compiler::ErrorReporter {
35 // Throws all errors as assertion failures.
36public:
37 void addError(uint32_t startByte, uint32_t endByte, kj::StringPtr message) override {
38 KJ_FAIL_REQUIRE(kj::str(message, " (", startByte, ":", endByte, ")."));
39 }
40
41 bool hadErrors() override { return false; }
42};
43
44class ExternalResolver final: public capnp::compiler::ValueTranslator::Resolver {
45 // Throws all external resolution requests as assertion failures.
46public:
47 kj::Maybe<capnp::DynamicValue::Reader>
48 resolveConstant(capnp::compiler::Expression::Reader name) override {
49 KJ_FAIL_REQUIRE("External constants not allowed.");
50 }
51
52 kj::Maybe<kj::Array<const capnp::byte>>
53 readEmbed(capnp::compiler::LocatedText::Reader filename) override {
54 KJ_FAIL_REQUIRE("External embeds not allowed.");
55 }
56};
57
58template <typename Function>
59void lexAndParseExpression(kj::StringPtr input, Function f) {
60 // Parses a single expression from the input and calls `f(expression)`.
61
62 ThrowingErrorReporter errorReporter;
63
64 capnp::MallocMessageBuilder tokenArena;
65 auto lexedTokens = tokenArena.initRoot<capnp::compiler::LexedTokens>();
66 capnp::compiler::lex(input, lexedTokens, errorReporter);
67
68 capnp::compiler::CapnpParser parser(tokenArena.getOrphanage(), errorReporter);
69 auto tokens = lexedTokens.asReader().getTokens();
70 capnp::compiler::CapnpParser::ParserInput parserInput(tokens.begin(), tokens.end());
71
72 if (parserInput.getPosition() != tokens.end()) {
73 KJ_IF_MAYBE(expression, parser.getParsers().expression(parserInput)) {
74 // The input is expected to contain a *single* message.
75 KJ_REQUIRE(parserInput.getPosition() == tokens.end(), "Extra tokens in input.");
76
77 f(expression->getReader());
78 } else {
79 auto best = parserInput.getBest();
80 if (best == tokens.end()) {
81 KJ_FAIL_REQUIRE("Premature end of input.");
82 } else {
83 errorReporter.addErrorOn(*best, "Parse error");
84 }
85 }
86 } else {
87 KJ_FAIL_REQUIRE("Failed to read input.");
88 }
89}
90
91} // namespace
92
93namespace capnp {
94
95TextCodec::TextCodec() : prettyPrint(false) {}
96TextCodec::~TextCodec() noexcept(true) {}
97
98void TextCodec::setPrettyPrint(bool enabled) { prettyPrint = enabled; }
99
100kj::String TextCodec::encode(DynamicValue::Reader value) const {
101 if (!prettyPrint) {
102 return kj::str(value);
103 } else {
104 if (value.getType() == DynamicValue::Type::STRUCT) {
105 return capnp::prettyPrint(value.as<DynamicStruct>()).flatten();
106 } else if (value.getType() == DynamicValue::Type::LIST) {
107 return capnp::prettyPrint(value.as<DynamicList>()).flatten();
108 } else {
109 return kj::str(value);
110 }
111 }
112}
113
114void TextCodec::decode(kj::StringPtr input, DynamicStruct::Builder output) const {
115 lexAndParseExpression(input, [&output](compiler::Expression::Reader expression) {
116 KJ_REQUIRE(expression.isTuple(), "Input does not contain a struct.");
117
118 ThrowingErrorReporter errorReporter;
119 ExternalResolver nullResolver;
120
121 Orphanage orphanage = Orphanage::getForMessageContaining(output);
122 compiler::ValueTranslator translator(nullResolver, errorReporter, orphanage);
123 translator.fillStructValue(output, expression.getTuple());
124 });
125}
126
127Orphan<DynamicValue> TextCodec::decode(kj::StringPtr input, Type type, Orphanage orphanage) const {
128 Orphan<DynamicValue> output;
129
130 lexAndParseExpression(input, [&type, &orphanage, &output](compiler::Expression::Reader expression) {
131 ThrowingErrorReporter errorReporter;
132 ExternalResolver nullResolver;
133
134 compiler::ValueTranslator translator(nullResolver, errorReporter, orphanage);
135 KJ_IF_MAYBE(value, translator.compileValue(expression, type)) {
136 output = *kj::mv(value);
137 } else {
138 // An error should have already been given to the errorReporter.
139 }
140 });
141
142 return output;
143}
144
145} // namespace capnp
146