Commit 5c851221 authored by Kenton Varda's avatar Kenton Varda

Implement 'capnp encode' command which does the opposite of 'capnp decode'.

parent 98e6519e
...@@ -25,6 +25,7 @@ ...@@ -25,6 +25,7 @@
#include "parser.h" #include "parser.h"
#include "compiler.h" #include "compiler.h"
#include "module-loader.h" #include "module-loader.h"
#include "node-translator.h"
#include <capnp/pretty-print.h> #include <capnp/pretty-print.h>
#include <capnp/schema.capnp.h> #include <capnp/schema.capnp.h>
#include <kj/vector.h> #include <kj/vector.h>
...@@ -99,8 +100,9 @@ public: ...@@ -99,8 +100,9 @@ public:
.addSubCommand("id", KJ_BIND_METHOD(*this, getGenIdMain), .addSubCommand("id", KJ_BIND_METHOD(*this, getGenIdMain),
"Generate a new unique ID.") "Generate a new unique ID.")
.addSubCommand("decode", KJ_BIND_METHOD(*this, getDecodeMain), .addSubCommand("decode", KJ_BIND_METHOD(*this, getDecodeMain),
"Decode binary Cap'n Proto message to text."); "Decode binary Cap'n Proto message to text.")
// TODO(someday): "encode" -- requires the ability to parse text format. .addSubCommand("encode", KJ_BIND_METHOD(*this, getEncodeMain),
"Encode text Cap'n Proto message to binary.");
addGlobalOptions(builder); addGlobalOptions(builder);
return builder.build(); return builder.build();
} }
...@@ -150,6 +152,37 @@ public: ...@@ -150,6 +152,37 @@ public:
return builder.build(); return builder.build();
} }
kj::MainFunc getEncodeMain() {
// Only parse the schemas we actually need for decoding.
compileEagerness = Compiler::NODE;
// Drop annotations since we don't need them. This avoids importing files like c++.capnp.
annotationFlag = Compiler::DROP_ANNOTATIONS;
kj::MainBuilder builder(context, VERSION_STRING,
"Encodes one or more textual Cap'n Proto messages to binary. The messages have root "
"type <type> defined in <schema-file>. Messages are read from standard input. Each "
"mesage is a parenthesized struct literal, like the format used to specify constants "
"and default values of struct type in the schema language. For example:\n"
" (foo = 123, bar = \"hello\", baz = [true, false, true])\n"
"The input may contain any number of such values; each will be encoded as a separate "
"message.",
"Note that the current implementation reads the entire input into memory before "
"beginning to encode. A better implementation would read and encode one message at "
"a time.");
addGlobalOptions(builder);
builder.addOption({'f', "flat"}, KJ_BIND_METHOD(*this, codeFlat),
"Expect only one input value, serializing it as a single-segment message "
"with no framing.")
.addOption({'p', "packed"}, KJ_BIND_METHOD(*this, codePacked),
"Pack the output message with standard Cap'n Proto packing, which "
"deflates zero-valued bytes.")
.expectArg("<schema-file>", KJ_BIND_METHOD(*this, addSource))
.expectArg("<type>", KJ_BIND_METHOD(*this, setRootType))
.callAfterParsing(KJ_BIND_METHOD(*this, encode));
return builder.build();
}
void addGlobalOptions(kj::MainBuilder& builder) { void addGlobalOptions(kj::MainBuilder& builder) {
builder.addOptionWithArg({'I', "import-path"}, KJ_BIND_METHOD(*this, addImportPath), "<dir>", builder.addOptionWithArg({'I', "import-path"}, KJ_BIND_METHOD(*this, addImportPath), "<dir>",
"Add <dir> to the list of directories searched for non-relative " "Add <dir> to the list of directories searched for non-relative "
...@@ -208,6 +241,19 @@ public: ...@@ -208,6 +241,19 @@ public:
addStandardImportPaths = false; addStandardImportPaths = false;
} }
KJ_IF_MAYBE(module, loadModule(file)) {
uint64_t id = compiler->add(*module);
compiler->eagerlyCompile(id, compileEagerness);
sourceFiles.add(SourceFile { id, module->getSourceName(), &*module });
} else {
return "no such file";
}
return true;
}
private:
kj::Maybe<const Module&> loadModule(kj::StringPtr file) {
size_t longestPrefix = 0; size_t longestPrefix = 0;
for (auto& prefix: sourcePrefixes) { for (auto& prefix: sourcePrefixes) {
...@@ -217,17 +263,10 @@ public: ...@@ -217,17 +263,10 @@ public:
} }
kj::StringPtr canonicalName = file.slice(longestPrefix); kj::StringPtr canonicalName = file.slice(longestPrefix);
KJ_IF_MAYBE(module, loader.loadModule(file, canonicalName)) { return loader.loadModule(file, canonicalName);
uint64_t id = compiler->add(*module);
compiler->eagerlyCompile(id, compileEagerness);
sourceFiles.add(SourceFile { id, canonicalName, &*module });
} else {
return "no such file";
}
return true;
} }
public:
// ===================================================================================== // =====================================================================================
// "id" command // "id" command
...@@ -512,6 +551,150 @@ private: ...@@ -512,6 +551,150 @@ private:
} }
} }
public:
// =====================================================================================
kj::MainBuilder::Validity encode() {
kj::Vector<char> allText;
{
kj::FdInputStream rawInput(STDIN_FILENO);
kj::BufferedInputStreamWrapper input(rawInput);
for (;;) {
auto buf = input.tryGetReadBuffer();
if (buf.size() == 0) break;
allText.addAll(reinterpret_cast<const char*>(buf.begin()),
reinterpret_cast<const char*>(buf.end()));
input.skip(buf.size());
}
}
EncoderErrorReporter errorReporter(*this, allText);
MallocMessageBuilder arena;
// Lex the input.
auto lexedTokens = arena.initRoot<LexedTokens>();
lex(allText, lexedTokens, errorReporter);
// Set up the parser.
CapnpParser parser(arena.getOrphanage(), errorReporter);
auto tokens = lexedTokens.asReader().getTokens();
CapnpParser::ParserInput parserInput(tokens.begin(), tokens.end());
// Allocate some scratch space.
kj::Array<word> scratch = kj::heapArray<word>(8192);
memset(scratch.begin(), 0, scratch.size() * sizeof(word));
// Set up stuff for the ValueTranslator.
ValueResolverGlue resolver(compiler->getLoader(), errorReporter);
auto type = arena.getOrphanage().newOrphan<schema::Type>();
type.get().setStruct(rootType.getProto().getId());
// Set up output stream.
kj::FdOutputStream rawOutput(STDOUT_FILENO);
kj::BufferedOutputStreamWrapper output(rawOutput);
while (parserInput.getPosition() != tokens.end()) {
KJ_IF_MAYBE(expression, parser.getParsers().parenthesizedValueExpression(parserInput)) {
MallocMessageBuilder item(scratch);
ValueTranslator translator(resolver, errorReporter, item.getOrphanage());
KJ_IF_MAYBE(value, translator.compileValue(expression->getReader(), type.getReader())) {
writeEncoded(value->getReader().as<DynamicStruct>(), output);
} else {
// Errors were reported, so we'll exit with a failure status later.
}
} else {
auto best = parserInput.getBest();
if (best == tokens.end()) {
context.exitError("Premature EOF.");
} else {
errorReporter.addErrorOn(*best, "Parse error.");
context.exit();
}
}
}
return true;
}
private:
void writeEncoded(DynamicStruct::Reader value, kj::BufferedOutputStream& output) {
// Always copy the message to a flat array so that the output is predictable (one segment,
// in canonical order).
size_t size = value.totalSizeInWords() + 1;
kj::Array<word> space = kj::heapArray<word>(size);
memset(space.begin(), 0, size * sizeof(word));
FlatMessageBuilder flatMessage(space);
flatMessage.setRoot(value);
flatMessage.requireFilled();
if (flat) {
output.write(space.begin(), space.size() * sizeof(word));
} else if (packed) {
writePackedMessage(output, flatMessage);
} else {
writeMessage(output, flatMessage);
}
}
class EncoderErrorReporter final: public ErrorReporter {
public:
EncoderErrorReporter(GlobalErrorReporter& globalReporter,
kj::ArrayPtr<const char> content)
: globalReporter(globalReporter), lineBreaks(content) {}
void addError(uint32_t startByte, uint32_t endByte, kj::StringPtr message) const override {
globalReporter.addError("<stdin>", lineBreaks.toSourcePos(startByte),
lineBreaks.toSourcePos(endByte), message);
}
bool hadErrors() const override {
return globalReporter.hadErrors();
}
private:
GlobalErrorReporter& globalReporter;
LineBreakTable lineBreaks;
};
class ValueResolverGlue final: public ValueTranslator::Resolver {
public:
ValueResolverGlue(const SchemaLoader& loader, const ErrorReporter& errorReporter)
: loader(loader), errorReporter(errorReporter) {}
kj::Maybe<Schema> resolveType(uint64_t id) {
// Don't use tryGet() here because we shouldn't even be here if there were compile errors.
return loader.get(id);
}
kj::Maybe<DynamicValue::Reader> resolveConstant(DeclName::Reader name) {
auto base = name.getBase();
switch (base.which()) {
case DeclName::Base::RELATIVE_NAME: {
auto value = base.getRelativeName();
errorReporter.addErrorOn(value, kj::str("Not defined: ", value.getValue()));
return nullptr;
}
case DeclName::Base::ABSOLUTE_NAME: {
auto value = base.getAbsoluteName();
errorReporter.addErrorOn(value, kj::str("Not defined: ", value.getValue()));
return nullptr;
}
case DeclName::Base::IMPORT_NAME: {
auto value = base.getImportName();
errorReporter.addErrorOn(value, "Imports not allowed in encode input.");
return nullptr;
}
}
}
private:
const SchemaLoader& loader;
const ErrorReporter& errorReporter;
};
public: public:
// ===================================================================================== // =====================================================================================
......
...@@ -22,12 +22,49 @@ ...@@ -22,12 +22,49 @@
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "error-reporter.h" #include "error-reporter.h"
#include <unistd.h> #include <kj/debug.h>
namespace capnp { namespace capnp {
namespace compiler { namespace compiler {
ErrorReporter::~ErrorReporter() noexcept(false) {} namespace {
template <typename T>
static size_t findLargestElementBefore(const kj::Vector<T>& vec, const T& key) {
KJ_REQUIRE(vec.size() > 0 && vec[0] <= key);
size_t lower = 0;
size_t upper = vec.size();
while (upper - lower > 1) {
size_t mid = (lower + upper) / 2;
if (vec[mid] > key) {
upper = mid;
} else {
lower = mid;
}
}
return lower;
}
} // namespace
LineBreakTable::LineBreakTable(kj::ArrayPtr<const char> content)
: lineBreaks(content.size() / 40) {
lineBreaks.add(0);
for (const char* pos = content.begin(); pos < content.end(); ++pos) {
if (*pos == '\n') {
lineBreaks.add(pos + 1 - content.begin());
}
}
}
GlobalErrorReporter::SourcePos LineBreakTable::toSourcePos(uint32_t byteOffset) const {
uint line = findLargestElementBefore(lineBreaks, byteOffset);
uint col = byteOffset - lineBreaks[line];
return GlobalErrorReporter::SourcePos { byteOffset, line, col };
}
} // namespace compiler } // namespace compiler
} // namespace capnp } // namespace capnp
...@@ -27,6 +27,7 @@ ...@@ -27,6 +27,7 @@
#include "../common.h" #include "../common.h"
#include <kj/string.h> #include <kj/string.h>
#include <kj/exception.h> #include <kj/exception.h>
#include <kj/vector.h>
namespace capnp { namespace capnp {
namespace compiler { namespace compiler {
...@@ -35,8 +36,6 @@ class ErrorReporter { ...@@ -35,8 +36,6 @@ class ErrorReporter {
// Callback for reporting errors within a particular file. // Callback for reporting errors within a particular file.
public: public:
virtual ~ErrorReporter() noexcept(false);
virtual void addError(uint32_t startByte, uint32_t endByte, kj::StringPtr message) const = 0; virtual void addError(uint32_t startByte, uint32_t endByte, kj::StringPtr message) const = 0;
// Report an error at the given location in the input text. `startByte` and `endByte` indicate // Report an error at the given location in the input text. `startByte` and `endByte` indicate
// the span of text that is erroneous. They may be equal, in which case the parser was only // the span of text that is erroneous. They may be equal, in which case the parser was only
...@@ -78,6 +77,18 @@ public: ...@@ -78,6 +77,18 @@ public:
// of previous errors. // of previous errors.
}; };
class LineBreakTable {
public:
LineBreakTable(kj::ArrayPtr<const char> content);
GlobalErrorReporter::SourcePos toSourcePos(uint32_t byteOffset) const;
private:
kj::Vector<uint> lineBreaks;
// Byte offsets of the first byte in each source line. The first element is always zero.
// Initialized the first time the module is loaded.
};
} // namespace compiler } // namespace compiler
} // namespace capnp } // namespace capnp
......
...@@ -42,25 +42,6 @@ namespace compiler { ...@@ -42,25 +42,6 @@ namespace compiler {
namespace { namespace {
template <typename T>
size_t findLargestElementBefore(const kj::Vector<T>& vec, const T& key) {
KJ_REQUIRE(vec.size() > 0 && vec[0] <= key);
size_t lower = 0;
size_t upper = vec.size();
while (upper - lower > 1) {
size_t mid = (lower + upper) / 2;
if (vec[mid] > key) {
upper = mid;
} else {
lower = mid;
}
}
return lower;
}
class MmapDisposer: public kj::ArrayDisposer { class MmapDisposer: public kj::ArrayDisposer {
protected: protected:
void disposeImpl(void* firstElement, size_t elementSize, size_t elementCount, void disposeImpl(void* firstElement, size_t elementSize, size_t elementCount,
...@@ -228,7 +209,7 @@ private: ...@@ -228,7 +209,7 @@ private:
kj::MutexGuarded<std::map<kj::StringPtr, kj::Own<Module>>> modules; kj::MutexGuarded<std::map<kj::StringPtr, kj::Own<Module>>> modules;
}; };
class ModuleLoader::ModuleImpl: public Module { class ModuleLoader::ModuleImpl final: public Module {
public: public:
ModuleImpl(const ModuleLoader::Impl& loader, kj::String localName, kj::String sourceName) ModuleImpl(const ModuleLoader::Impl& loader, kj::String localName, kj::String sourceName)
: loader(loader), localName(kj::mv(localName)), sourceName(kj::mv(sourceName)) {} : loader(loader), localName(kj::mv(localName)), sourceName(kj::mv(sourceName)) {}
...@@ -244,15 +225,8 @@ public: ...@@ -244,15 +225,8 @@ public:
Orphan<ParsedFile> loadContent(Orphanage orphanage) const override { Orphan<ParsedFile> loadContent(Orphanage orphanage) const override {
kj::Array<const char> content = mmapForRead(localName); kj::Array<const char> content = mmapForRead(localName);
lineBreaks.get([&](kj::SpaceFor<kj::Vector<uint>>& space) { lineBreaks.get([&](kj::SpaceFor<LineBreakTable>& space) {
auto vec = space.construct(content.size() / 40); return space.construct(content);
vec->add(0);
for (const char* pos = content.begin(); pos < content.end(); ++pos) {
if (*pos == '\n') {
vec->add(pos + 1 - content.begin());
}
}
return vec;
}); });
MallocMessageBuilder lexedBuilder; MallocMessageBuilder lexedBuilder;
...@@ -274,22 +248,12 @@ public: ...@@ -274,22 +248,12 @@ public:
void addError(uint32_t startByte, uint32_t endByte, kj::StringPtr message) const override { void addError(uint32_t startByte, uint32_t endByte, kj::StringPtr message) const override {
auto& lines = lineBreaks.get( auto& lines = lineBreaks.get(
[](kj::SpaceFor<kj::Vector<uint>>& space) { [](kj::SpaceFor<LineBreakTable>& space) -> kj::Own<LineBreakTable> {
KJ_FAIL_REQUIRE("Can't report errors until loadContent() is called."); KJ_FAIL_REQUIRE("Can't report errors until loadContent() is called.");
return space.construct();
}); });
// TODO(someday): This counts tabs as single characters. Do we care?
uint startLine = findLargestElementBefore(lines, startByte);
uint startCol = startByte - lines[startLine];
uint endLine = findLargestElementBefore(lines, endByte);
uint endCol = endByte - lines[endLine];
loader.getErrorReporter().addError( loader.getErrorReporter().addError(
localName, localName, lines.toSourcePos(startByte), lines.toSourcePos(endByte), message);
GlobalErrorReporter::SourcePos { startByte, startLine, startCol },
GlobalErrorReporter::SourcePos { endByte, endLine, endCol },
message);
} }
bool hadErrors() const override { bool hadErrors() const override {
...@@ -301,9 +265,7 @@ private: ...@@ -301,9 +265,7 @@ private:
kj::String localName; kj::String localName;
kj::String sourceName; kj::String sourceName;
kj::Lazy<kj::Vector<uint>> lineBreaks; kj::Lazy<LineBreakTable> lineBreaks;
// Byte offsets of the first byte in each source line. The first element is always zero.
// Initialized the first time the module is loaded.
}; };
// ======================================================================================= // =======================================================================================
......
...@@ -1410,8 +1410,31 @@ void NodeTranslator::compileBootstrapValue(ValueExpression::Reader source, ...@@ -1410,8 +1410,31 @@ void NodeTranslator::compileBootstrapValue(ValueExpression::Reader source,
void NodeTranslator::compileValue(ValueExpression::Reader source, schema::Type::Reader type, void NodeTranslator::compileValue(ValueExpression::Reader source, schema::Type::Reader type,
schema::Value::Builder target, bool isBootstrap) { schema::Value::Builder target, bool isBootstrap) {
class ResolverGlue: public ValueTranslator::Resolver {
public:
inline ResolverGlue(NodeTranslator& translator, bool isBootstrap)
: translator(translator), isBootstrap(isBootstrap) {}
kj::Maybe<Schema> resolveType(uint64_t id) override {
// Always use bootstrap schemas when resolving types, because final schemas are unsafe to
// use with the dynamic API and bootstrap schemas have all the info needed anyway.
return translator.resolver.resolveBootstrapSchema(id);
}
kj::Maybe<DynamicValue::Reader> resolveConstant(DeclName::Reader name) override {
return translator.readConstant(name, isBootstrap);
}
private:
NodeTranslator& translator;
bool isBootstrap;
};
ResolverGlue glue(*this, isBootstrap);
ValueTranslator valueTranslator(glue, errorReporter, orphanage);
kj::StringPtr fieldName = KJ_ASSERT_NONNULL(toDynamic(type).which()).getProto().getName(); kj::StringPtr fieldName = KJ_ASSERT_NONNULL(toDynamic(type).which()).getProto().getName();
KJ_IF_MAYBE(value, compileValue(source, type, isBootstrap)) { KJ_IF_MAYBE(value, valueTranslator.compileValue(source, type)) {
if (type.isEnum()) { if (type.isEnum()) {
target.setEnum(value->getReader().as<DynamicEnum>().getRaw()); target.setEnum(value->getReader().as<DynamicEnum>().getRaw());
} else { } else {
...@@ -1420,9 +1443,9 @@ void NodeTranslator::compileValue(ValueExpression::Reader source, schema::Type:: ...@@ -1420,9 +1443,9 @@ void NodeTranslator::compileValue(ValueExpression::Reader source, schema::Type::
} }
} }
kj::Maybe<Orphan<DynamicValue>> NodeTranslator::compileValue( kj::Maybe<Orphan<DynamicValue>> ValueTranslator::compileValue(
ValueExpression::Reader src, schema::Type::Reader type, bool isBootstrap) { ValueExpression::Reader src, schema::Type::Reader type) {
Orphan<DynamicValue> result = compileValueInner(src, type, isBootstrap); Orphan<DynamicValue> result = compileValueInner(src, type);
switch (result.getType()) { switch (result.getType()) {
case DynamicValue::UNKNOWN: case DynamicValue::UNKNOWN:
...@@ -1536,7 +1559,7 @@ kj::Maybe<Orphan<DynamicValue>> NodeTranslator::compileValue( ...@@ -1536,7 +1559,7 @@ kj::Maybe<Orphan<DynamicValue>> NodeTranslator::compileValue(
case DynamicValue::ENUM: case DynamicValue::ENUM:
if (type.isEnum()) { if (type.isEnum()) {
KJ_IF_MAYBE(schema, resolver.resolveBootstrapSchema(type.getEnum())) { KJ_IF_MAYBE(schema, resolver.resolveType(type.getEnum())) {
if (result.getReader().as<DynamicEnum>().getSchema() == *schema) { if (result.getReader().as<DynamicEnum>().getSchema() == *schema) {
return kj::mv(result); return kj::mv(result);
} }
...@@ -1548,7 +1571,7 @@ kj::Maybe<Orphan<DynamicValue>> NodeTranslator::compileValue( ...@@ -1548,7 +1571,7 @@ kj::Maybe<Orphan<DynamicValue>> NodeTranslator::compileValue(
case DynamicValue::STRUCT: case DynamicValue::STRUCT:
if (type.isStruct()) { if (type.isStruct()) {
KJ_IF_MAYBE(schema, resolver.resolveBootstrapSchema(type.getStruct())) { KJ_IF_MAYBE(schema, resolver.resolveType(type.getStruct())) {
if (result.getReader().as<DynamicStruct>().getSchema() == *schema) { if (result.getReader().as<DynamicStruct>().getSchema() == *schema) {
return kj::mv(result); return kj::mv(result);
} }
...@@ -1569,8 +1592,8 @@ kj::Maybe<Orphan<DynamicValue>> NodeTranslator::compileValue( ...@@ -1569,8 +1592,8 @@ kj::Maybe<Orphan<DynamicValue>> NodeTranslator::compileValue(
return nullptr; return nullptr;
} }
Orphan<DynamicValue> NodeTranslator::compileValueInner( Orphan<DynamicValue> ValueTranslator::compileValueInner(
ValueExpression::Reader src, schema::Type::Reader type, bool isBootstrap) { ValueExpression::Reader src, schema::Type::Reader type) {
switch (src.which()) { switch (src.which()) {
case ValueExpression::NAME: { case ValueExpression::NAME: {
auto name = src.getName(); auto name = src.getName();
...@@ -1581,7 +1604,7 @@ Orphan<DynamicValue> NodeTranslator::compileValueInner( ...@@ -1581,7 +1604,7 @@ Orphan<DynamicValue> NodeTranslator::compileValueInner(
kj::StringPtr id = name.getBase().getRelativeName().getValue(); kj::StringPtr id = name.getBase().getRelativeName().getValue();
if (type.isEnum()) { if (type.isEnum()) {
KJ_IF_MAYBE(enumSchema, resolver.resolveBootstrapSchema(type.getEnum())) { KJ_IF_MAYBE(enumSchema, resolver.resolveType(type.getEnum())) {
KJ_IF_MAYBE(enumerant, enumSchema->asEnum().findEnumerantByName(id)) { KJ_IF_MAYBE(enumerant, enumSchema->asEnum().findEnumerantByName(id)) {
return DynamicEnum(*enumerant); return DynamicEnum(*enumerant);
} }
...@@ -1606,7 +1629,7 @@ Orphan<DynamicValue> NodeTranslator::compileValueInner( ...@@ -1606,7 +1629,7 @@ Orphan<DynamicValue> NodeTranslator::compileValueInner(
} }
// Haven't resolved the name yet. Try looking up a constant. // Haven't resolved the name yet. Try looking up a constant.
KJ_IF_MAYBE(constValue, readConstant(src.getName(), isBootstrap, src)) { KJ_IF_MAYBE(constValue, resolver.resolveConstant(src.getName())) {
return orphanage.newOrphanCopy(*constValue); return orphanage.newOrphanCopy(*constValue);
} }
...@@ -1651,7 +1674,7 @@ Orphan<DynamicValue> NodeTranslator::compileValueInner( ...@@ -1651,7 +1674,7 @@ Orphan<DynamicValue> NodeTranslator::compileValueInner(
Orphan<DynamicList> result = orphanage.newOrphan(*listSchema, srcList.size()); Orphan<DynamicList> result = orphanage.newOrphan(*listSchema, srcList.size());
auto dstList = result.get(); auto dstList = result.get();
for (uint i = 0; i < srcList.size(); i++) { for (uint i = 0; i < srcList.size(); i++) {
KJ_IF_MAYBE(value, compileValue(srcList[i], elementType, isBootstrap)) { KJ_IF_MAYBE(value, compileValue(srcList[i], elementType)) {
dstList.adopt(i, kj::mv(*value)); dstList.adopt(i, kj::mv(*value));
} }
} }
...@@ -1666,10 +1689,10 @@ Orphan<DynamicValue> NodeTranslator::compileValueInner( ...@@ -1666,10 +1689,10 @@ Orphan<DynamicValue> NodeTranslator::compileValueInner(
errorReporter.addErrorOn(src, "Type mismatch."); errorReporter.addErrorOn(src, "Type mismatch.");
return nullptr; return nullptr;
} }
KJ_IF_MAYBE(schema, resolver.resolveBootstrapSchema(type.getStruct())) { KJ_IF_MAYBE(schema, resolver.resolveType(type.getStruct())) {
auto structSchema = schema->asStruct(); auto structSchema = schema->asStruct();
Orphan<DynamicStruct> result = orphanage.newOrphan(structSchema); Orphan<DynamicStruct> result = orphanage.newOrphan(structSchema);
fillStructValue(result.get(), src.getStruct(), isBootstrap); fillStructValue(result.get(), src.getStruct());
return kj::mv(result); return kj::mv(result);
} else { } else {
return nullptr; return nullptr;
...@@ -1684,9 +1707,8 @@ Orphan<DynamicValue> NodeTranslator::compileValueInner( ...@@ -1684,9 +1707,8 @@ Orphan<DynamicValue> NodeTranslator::compileValueInner(
KJ_UNREACHABLE; KJ_UNREACHABLE;
} }
void NodeTranslator::fillStructValue(DynamicStruct::Builder builder, void ValueTranslator::fillStructValue(DynamicStruct::Builder builder,
List<ValueExpression::FieldAssignment>::Reader assignments, List<ValueExpression::FieldAssignment>::Reader assignments) {
bool isBootstrap) {
for (auto assignment: assignments) { for (auto assignment: assignments) {
auto fieldName = assignment.getFieldName(); auto fieldName = assignment.getFieldName();
KJ_IF_MAYBE(field, builder.getSchema().findFieldByName(fieldName.getValue())) { KJ_IF_MAYBE(field, builder.getSchema().findFieldByName(fieldName.getValue())) {
...@@ -1695,16 +1717,14 @@ void NodeTranslator::fillStructValue(DynamicStruct::Builder builder, ...@@ -1695,16 +1717,14 @@ void NodeTranslator::fillStructValue(DynamicStruct::Builder builder,
switch (fieldProto.which()) { switch (fieldProto.which()) {
case schema::Field::NON_GROUP: case schema::Field::NON_GROUP:
KJ_IF_MAYBE(compiledValue, KJ_IF_MAYBE(compiledValue, compileValue(value, fieldProto.getNonGroup().getType())) {
compileValue(value, fieldProto.getNonGroup().getType(), isBootstrap)) {
builder.adopt(*field, kj::mv(*compiledValue)); builder.adopt(*field, kj::mv(*compiledValue));
} }
break; break;
case schema::Field::GROUP: case schema::Field::GROUP:
if (value.isStruct()) { if (value.isStruct()) {
fillStructValue(builder.init(*field).as<DynamicStruct>(), value.getStruct(), fillStructValue(builder.init(*field).as<DynamicStruct>(), value.getStruct());
isBootstrap);
} else { } else {
errorReporter.addErrorOn(value, "Type mismatch."); errorReporter.addErrorOn(value, "Type mismatch.");
} }
...@@ -1717,8 +1737,8 @@ void NodeTranslator::fillStructValue(DynamicStruct::Builder builder, ...@@ -1717,8 +1737,8 @@ void NodeTranslator::fillStructValue(DynamicStruct::Builder builder,
} }
} }
kj::String NodeTranslator::makeNodeName(uint64_t id) { kj::String ValueTranslator::makeNodeName(uint64_t id) {
KJ_IF_MAYBE(schema, resolver.resolveBootstrapSchema(id)) { KJ_IF_MAYBE(schema, resolver.resolveType(id)) {
schema::Node::Reader proto = schema->getProto(); schema::Node::Reader proto = schema->getProto();
return kj::str(proto.getDisplayName().slice(proto.getDisplayNamePrefixLength())); return kj::str(proto.getDisplayName().slice(proto.getDisplayNamePrefixLength()));
} else { } else {
...@@ -1726,7 +1746,7 @@ kj::String NodeTranslator::makeNodeName(uint64_t id) { ...@@ -1726,7 +1746,7 @@ kj::String NodeTranslator::makeNodeName(uint64_t id) {
} }
} }
kj::String NodeTranslator::makeTypeName(schema::Type::Reader type) { kj::String ValueTranslator::makeTypeName(schema::Type::Reader type) {
switch (type.which()) { switch (type.which()) {
case schema::Type::VOID: return kj::str("Void"); case schema::Type::VOID: return kj::str("Void");
case schema::Type::BOOL: return kj::str("Bool"); case schema::Type::BOOL: return kj::str("Bool");
...@@ -1752,10 +1772,10 @@ kj::String NodeTranslator::makeTypeName(schema::Type::Reader type) { ...@@ -1752,10 +1772,10 @@ kj::String NodeTranslator::makeTypeName(schema::Type::Reader type) {
} }
kj::Maybe<DynamicValue::Reader> NodeTranslator::readConstant( kj::Maybe<DynamicValue::Reader> NodeTranslator::readConstant(
DeclName::Reader name, bool isBootstrap, ValueExpression::Reader errorLocation) { DeclName::Reader name, bool isBootstrap) {
KJ_IF_MAYBE(resolved, resolver.resolve(name)) { KJ_IF_MAYBE(resolved, resolver.resolve(name)) {
if (resolved->kind != Declaration::CONST) { if (resolved->kind != Declaration::CONST) {
errorReporter.addErrorOn(errorLocation, errorReporter.addErrorOn(name,
kj::str("'", declNameString(name), "' does not refer to a constant.")); kj::str("'", declNameString(name), "' does not refer to a constant."));
return nullptr; return nullptr;
} }
...@@ -1817,7 +1837,7 @@ kj::Maybe<DynamicValue::Reader> NodeTranslator::readConstant( ...@@ -1817,7 +1837,7 @@ kj::Maybe<DynamicValue::Reader> NodeTranslator::readConstant(
} }
kj::StringPtr id = name.getBase().getRelativeName().getValue(); kj::StringPtr id = name.getBase().getRelativeName().getValue();
errorReporter.addErrorOn(errorLocation, kj::str( errorReporter.addErrorOn(name, kj::str(
"Constant names must be qualified to avoid confusion. Please replace '", "Constant names must be qualified to avoid confusion. Please replace '",
declNameString(name), "' with '", parent, ".", id, declNameString(name), "' with '", parent, ".", id,
"', if that's what you intended.")); "', if that's what you intended."));
...@@ -1835,28 +1855,30 @@ kj::Maybe<DynamicValue::Reader> NodeTranslator::readConstant( ...@@ -1835,28 +1855,30 @@ kj::Maybe<DynamicValue::Reader> NodeTranslator::readConstant(
} }
} }
kj::Maybe<ListSchema> NodeTranslator::makeListSchemaOf(schema::Type::Reader elementType) { template <typename ResolveTypeFunc>
static kj::Maybe<ListSchema> makeListSchemaImpl(schema::Type::Reader elementType,
const ResolveTypeFunc& resolveType) {
switch (elementType.which()) { switch (elementType.which()) {
case schema::Type::ENUM: case schema::Type::ENUM:
KJ_IF_MAYBE(enumSchema, resolver.resolveBootstrapSchema(elementType.getEnum())) { KJ_IF_MAYBE(enumSchema, resolveType(elementType.getEnum())) {
return ListSchema::of(enumSchema->asEnum()); return ListSchema::of(enumSchema->asEnum());
} else { } else {
return nullptr; return nullptr;
} }
case schema::Type::STRUCT: case schema::Type::STRUCT:
KJ_IF_MAYBE(structSchema, resolver.resolveBootstrapSchema(elementType.getStruct())) { KJ_IF_MAYBE(structSchema, resolveType(elementType.getStruct())) {
return ListSchema::of(structSchema->asStruct()); return ListSchema::of(structSchema->asStruct());
} else { } else {
return nullptr; return nullptr;
} }
case schema::Type::INTERFACE: case schema::Type::INTERFACE:
KJ_IF_MAYBE(interfaceSchema, resolver.resolveBootstrapSchema(elementType.getInterface())) { KJ_IF_MAYBE(interfaceSchema, resolveType(elementType.getInterface())) {
return ListSchema::of(interfaceSchema->asInterface()); return ListSchema::of(interfaceSchema->asInterface());
} else { } else {
return nullptr; return nullptr;
} }
case schema::Type::LIST: case schema::Type::LIST:
KJ_IF_MAYBE(listSchema, makeListSchemaOf(elementType.getList())) { KJ_IF_MAYBE(listSchema, makeListSchemaImpl(elementType.getList(), resolveType)) {
return ListSchema::of(*listSchema); return ListSchema::of(*listSchema);
} else { } else {
return nullptr; return nullptr;
...@@ -1866,6 +1888,16 @@ kj::Maybe<ListSchema> NodeTranslator::makeListSchemaOf(schema::Type::Reader elem ...@@ -1866,6 +1888,16 @@ kj::Maybe<ListSchema> NodeTranslator::makeListSchemaOf(schema::Type::Reader elem
} }
} }
kj::Maybe<ListSchema> NodeTranslator::makeListSchemaOf(schema::Type::Reader elementType) {
return makeListSchemaImpl(elementType,
[this](uint64_t id) { return resolver.resolveBootstrapSchema(id); });
}
kj::Maybe<ListSchema> ValueTranslator::makeListSchemaOf(schema::Type::Reader elementType) {
return makeListSchemaImpl(elementType,
[this](uint64_t id) { return resolver.resolveType(id); });
}
Orphan<List<schema::Annotation>> NodeTranslator::compileAnnotationApplications( Orphan<List<schema::Annotation>> NodeTranslator::compileAnnotationApplications(
List<Declaration::AnnotationApplication>::Reader annotations, List<Declaration::AnnotationApplication>::Reader annotations,
kj::StringPtr targetsFlagName) { kj::StringPtr targetsFlagName) {
......
...@@ -169,35 +169,49 @@ private: ...@@ -169,35 +169,49 @@ private:
schema::Value::Builder target, bool isBootstrap); schema::Value::Builder target, bool isBootstrap);
// Interprets the value expression and initializes `target` with the result. // Interprets the value expression and initializes `target` with the result.
kj::Maybe<DynamicValue::Reader> readConstant(DeclName::Reader name, bool isBootstrap);
// Get the value of the given constant. May return null if some error occurs, which will already
// have been reported.
kj::Maybe<ListSchema> makeListSchemaOf(schema::Type::Reader elementType);
// Construct a list schema representing a list of elements of the given type. May return null if
// some error occurs, which will already have been reported.
Orphan<List<schema::Annotation>> compileAnnotationApplications(
List<Declaration::AnnotationApplication>::Reader annotations,
kj::StringPtr targetsFlagName);
};
class ValueTranslator {
public:
class Resolver {
public:
virtual kj::Maybe<Schema> resolveType(uint64_t id) = 0;
virtual kj::Maybe<DynamicValue::Reader> resolveConstant(DeclName::Reader name) = 0;
};
ValueTranslator(Resolver& resolver, const ErrorReporter& errorReporter, Orphanage orphanage)
: resolver(resolver), errorReporter(errorReporter), orphanage(orphanage) {}
kj::Maybe<Orphan<DynamicValue>> compileValue( kj::Maybe<Orphan<DynamicValue>> compileValue(
ValueExpression::Reader src, schema::Type::Reader type, bool isBootstrap); ValueExpression::Reader src, schema::Type::Reader type);
// Compile the given value as the given type. Returns null if there was an error, including
// if the value doesn't match the type. private:
Resolver& resolver;
const ErrorReporter& errorReporter;
Orphanage orphanage;
Orphan<DynamicValue> compileValueInner(ValueExpression::Reader src, schema::Type::Reader type, Orphan<DynamicValue> compileValueInner(ValueExpression::Reader src, schema::Type::Reader type);
bool isBootstrap);
// Helper for compileValue(). // Helper for compileValue().
void fillStructValue(DynamicStruct::Builder builder, void fillStructValue(DynamicStruct::Builder builder,
List<ValueExpression::FieldAssignment>::Reader assignments, List<ValueExpression::FieldAssignment>::Reader assignments);
bool isBootstrap);
// Interprets the given assignments and uses them to fill in the given struct builder. // Interprets the given assignments and uses them to fill in the given struct builder.
kj::String makeNodeName(uint64_t id); kj::String makeNodeName(uint64_t id);
kj::String makeTypeName(schema::Type::Reader type); kj::String makeTypeName(schema::Type::Reader type);
kj::Maybe<DynamicValue::Reader> readConstant(DeclName::Reader name, bool isBootstrap,
ValueExpression::Reader errorLocation);
// Get the value of the given constant. May return null if some error occurs, which will already
// have been reported.
kj::Maybe<ListSchema> makeListSchemaOf(schema::Type::Reader elementType); kj::Maybe<ListSchema> makeListSchemaOf(schema::Type::Reader elementType);
// Construct a list schema representing a list of elements of the given type. May return null if
// some error occurs, which will already have been reported.
Orphan<List<schema::Annotation>> compileAnnotationApplications(
List<Declaration::AnnotationApplication>::Reader annotations,
kj::StringPtr targetsFlagName);
}; };
} // namespace compiler } // namespace compiler
......
...@@ -1659,6 +1659,19 @@ DynamicStruct::Builder MessageBuilder::getRoot<DynamicStruct>(StructSchema schem ...@@ -1659,6 +1659,19 @@ DynamicStruct::Builder MessageBuilder::getRoot<DynamicStruct>(StructSchema schem
return DynamicStruct::Builder(schema, getRoot(structSizeFromSchema(schema))); return DynamicStruct::Builder(schema, getRoot(structSizeFromSchema(schema)));
} }
template <>
void MessageBuilder::setRoot<DynamicStruct::Reader>(DynamicStruct::Reader&& value) {
setRootInternal(value.reader);
}
template <>
void MessageBuilder::setRoot<const DynamicStruct::Reader&>(const DynamicStruct::Reader& value) {
setRootInternal(value.reader);
}
template <>
void MessageBuilder::setRoot<DynamicStruct::Reader&>(DynamicStruct::Reader& value) {
setRootInternal(value.reader);
}
namespace _ { // private namespace _ { // private
DynamicStruct::Reader PointerHelpers<DynamicStruct, Kind::UNKNOWN>::getDynamic( DynamicStruct::Reader PointerHelpers<DynamicStruct, Kind::UNKNOWN>::getDynamic(
......
...@@ -216,6 +216,8 @@ public: ...@@ -216,6 +216,8 @@ public:
template <typename T, typename = kj::EnableIf<kind<FromReader<T>>() == Kind::STRUCT>> template <typename T, typename = kj::EnableIf<kind<FromReader<T>>() == Kind::STRUCT>>
inline Reader(T&& value): Reader(toDynamic(value)) {} inline Reader(T&& value): Reader(toDynamic(value)) {}
inline size_t totalSizeInWords() const { return reader.totalSize() / ::capnp::WORDS; }
template <typename T> template <typename T>
typename T::Reader as() const; typename T::Reader as() const;
// Convert the dynamic struct to its compiled-in type. // Convert the dynamic struct to its compiled-in type.
...@@ -279,6 +281,8 @@ public: ...@@ -279,6 +281,8 @@ public:
template <typename T, typename = kj::EnableIf<kind<FromBuilder<T>>() == Kind::STRUCT>> template <typename T, typename = kj::EnableIf<kind<FromBuilder<T>>() == Kind::STRUCT>>
inline Builder(T&& value): Builder(toDynamic(value)) {} inline Builder(T&& value): Builder(toDynamic(value)) {}
inline size_t totalSizeInWords() const { return asReader().totalSizeInWords(); }
template <typename T> template <typename T>
typename T::Builder as(); typename T::Builder as();
// Cast to a particular struct type. // Cast to a particular struct type.
...@@ -926,6 +930,12 @@ template <> ...@@ -926,6 +930,12 @@ template <>
DynamicStruct::Builder MessageBuilder::initRoot<DynamicStruct>(StructSchema schema); DynamicStruct::Builder MessageBuilder::initRoot<DynamicStruct>(StructSchema schema);
template <> template <>
DynamicStruct::Builder MessageBuilder::getRoot<DynamicStruct>(StructSchema schema); DynamicStruct::Builder MessageBuilder::getRoot<DynamicStruct>(StructSchema schema);
template <>
void MessageBuilder::setRoot<DynamicStruct::Reader>(DynamicStruct::Reader&& value);
template <>
void MessageBuilder::setRoot<const DynamicStruct::Reader&>(const DynamicStruct::Reader& value);
template <>
void MessageBuilder::setRoot<DynamicStruct::Reader&>(DynamicStruct::Reader& value);
namespace _ { // private namespace _ { // private
......
...@@ -67,7 +67,7 @@ size_t findLargestElementBefore(const kj::Vector<T>& vec, const T& key) { ...@@ -67,7 +67,7 @@ size_t findLargestElementBefore(const kj::Vector<T>& vec, const T& key) {
// ======================================================================================= // =======================================================================================
class SchemaParser::ModuleImpl: public compiler::Module { class SchemaParser::ModuleImpl final: public compiler::Module {
public: public:
ModuleImpl(const SchemaParser& parser, kj::Own<const SchemaFile>&& file) ModuleImpl(const SchemaParser& parser, kj::Own<const SchemaFile>&& file)
: parser(parser), file(kj::mv(file)) {} : parser(parser), file(kj::mv(file)) {}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment