Commit 34e70d5a authored by Kenton Varda's avatar Kenton Varda

More compiler work. Almost to the point of basic functionality... almost...

parent a75f59bf
......@@ -183,7 +183,7 @@ private:
class Compiler::CompiledModule {
public:
CompiledModule(const Compiler::Impl& compiler, const Module<ParsedFile::Reader>& parserModule);
CompiledModule(const Compiler::Impl& compiler, const Module& parserModule);
const Compiler::Impl& getCompiler() const { return compiler; }
......@@ -196,7 +196,7 @@ public:
private:
const Compiler::Impl& compiler;
const Module<ParsedFile::Reader>& parserModule;
const Module& parserModule;
MallocMessageBuilder contentArena;
ParsedFile::Reader content;
Node rootNode;
......@@ -206,7 +206,7 @@ class Compiler::Impl: public SchemaLoader::LazyLoadCallback {
public:
Impl();
const CompiledModule& add(const Module<ParsedFile::Reader>& parsedModule) const;
const CompiledModule& add(const Module& parsedModule) const;
struct Workspace {
// Scratch space where stuff can be allocated while working. The Workspace is available
......@@ -264,7 +264,7 @@ private:
uint workspaceRefcount = 0;
// Count of threads that have entered the compiler.
typedef std::unordered_map<Module<ParsedFile::Reader>*, kj::Own<CompiledModule>> ModuleMap;
typedef std::unordered_map<Module*, kj::Own<CompiledModule>> ModuleMap;
kj::MutexGuarded<ModuleMap> modules;
// Map of parser modules to compiler modules.
......@@ -627,7 +627,7 @@ Schema Compiler::Node::resolveFinalSchema(uint64_t id) const {
// =======================================================================================
Compiler::CompiledModule::CompiledModule(
const Compiler::Impl& compiler, const Module<ParsedFile::Reader>& parserModule)
const Compiler::Impl& compiler, const Module& parserModule)
: compiler(compiler), parserModule(parserModule),
content(parserModule.loadContent(contentArena.getOrphanage())),
rootNode(*this) {}
......@@ -635,7 +635,7 @@ Compiler::CompiledModule::CompiledModule(
kj::Maybe<const Compiler::CompiledModule&> Compiler::CompiledModule::importRelative(
kj::StringPtr importPath) const {
return parserModule.importRelative(importPath).map(
[this](const Module<ParsedFile::Reader>& module) -> const Compiler::CompiledModule& {
[this](const Module& module) -> const Compiler::CompiledModule& {
return compiler.add(module);
});
}
......
......@@ -32,7 +32,6 @@
namespace capnp {
namespace compiler {
template <typename ContentType>
class Module: public ErrorReporter {
public:
virtual kj::StringPtr getLocalName() const = 0;
......@@ -43,7 +42,7 @@ public:
// The name of the module file relative to the source tree. Used to decide where to output
// generated code and to form the `displayName` in the schema.
virtual ContentType loadContent(Orphanage orphanage) const = 0;
virtual Orphan<ParsedFile> loadContent(Orphanage orphanage) const = 0;
// Loads the module content, using the given orphanage to allocate objects if necessary.
virtual kj::Maybe<const Module&> importRelative(kj::StringPtr importPath) const = 0;
......@@ -77,7 +76,7 @@ public:
// use EAGER mode.
};
Schema add(Module<ParsedFile::Reader>& module, Mode mode) const;
Schema add(Module& module, Mode mode) const;
// Add a module to the Compiler, returning its root Schema object.
const SchemaLoader& getLoader() const;
......
......@@ -69,6 +69,9 @@ struct DeclName {
memberPath @4 :List(LocatedText);
# List of `.member` suffixes.
startByte @5 :UInt32;
endByte @6 :UInt32;
}
struct TypeExpression {
......
......@@ -31,7 +31,7 @@ namespace compiler {
namespace p = kj::parse;
bool lex(kj::ArrayPtr<const char> input, LexedStatements::Builder result,
ErrorReporter& errorReporter) {
const ErrorReporter& errorReporter) {
Lexer lexer(Orphanage::getForMessageContaining(result), errorReporter);
auto parser = p::sequence(lexer.getParsers().statementSequence, p::endOfInput);
......@@ -53,7 +53,7 @@ bool lex(kj::ArrayPtr<const char> input, LexedStatements::Builder result,
}
bool lex(kj::ArrayPtr<const char> input, LexedTokens::Builder result,
ErrorReporter& errorReporter) {
const ErrorReporter& errorReporter) {
Lexer lexer(Orphanage::getForMessageContaining(result), errorReporter);
auto parser = p::sequence(lexer.getParsers().tokenSequence, p::endOfInput);
......@@ -138,7 +138,7 @@ constexpr auto docComment = p::optional(p::sequence(
} // namespace
Lexer::Lexer(Orphanage orphanageParam, ErrorReporter& errorReporterParam)
Lexer::Lexer(Orphanage orphanageParam, const ErrorReporter& errorReporterParam)
: orphanage(orphanageParam), errorReporter(errorReporterParam) {
// Note that because passing an lvalue to a parser constructor uses it by-referencee, it's safe
......
......@@ -33,9 +33,9 @@ namespace capnp {
namespace compiler {
bool lex(kj::ArrayPtr<const char> input, LexedStatements::Builder result,
ErrorReporter& errorReporter);
const ErrorReporter& errorReporter);
bool lex(kj::ArrayPtr<const char> input, LexedTokens::Builder result,
ErrorReporter& errorReporter);
const ErrorReporter& errorReporter);
// Lex the given source code, placing the results in `result`. Returns true if there
// were no errors, false if there were. Even when errors are present, the file may have partial
// content which can be fed into later stages of parsing in order to find more errors.
......@@ -49,7 +49,7 @@ class Lexer {
// into your own parsers.
public:
Lexer(Orphanage orphanage, ErrorReporter& errorReporter);
Lexer(Orphanage orphanage, const ErrorReporter& errorReporter);
// `orphanage` is used to allocate Cap'n Proto message objects in the result. `inputStart` is
// a pointer to the beginning of the input, used to compute byte offsets.
......@@ -91,7 +91,7 @@ public:
private:
Orphanage orphanage;
ErrorReporter& errorReporter;
const ErrorReporter& errorReporter;
kj::Arena arena;
Parsers parsers;
};
......
// Copyright (c) 2013, Kenton Varda <temporal@gmail.com>
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this
// list of conditions and the following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "module-loader.h"
#include "lexer.h"
#include "parser.h"
#include <kj/vector.h>
#include <kj/mutex.h>
#include <kj/debug.h>
#include <kj/io.h>
#include <capnp/message.h>
#include <map>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
namespace capnp {
namespace compiler {
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 {
protected:
void disposeImpl(void* firstElement, size_t elementSize, size_t elementCount,
size_t capacity, void (*destroyElement)(void*)) const {
munmap(firstElement, elementSize * elementCount);
}
};
constexpr MmapDisposer mmapDisposer = MmapDisposer();
kj::Array<const char> mmapForRead(kj::StringPtr filename) {
int fd;
// We already established that the file exists, so this should not fail.
KJ_SYSCALL(fd = open(filename.cStr(), O_RDONLY), filename);
kj::AutoCloseFd closer(fd);
struct stat stats;
KJ_SYSCALL(fstat(fd, &stats));
const void* mapping = mmap(NULL, stats.st_size, PROT_READ, MAP_SHARED, fd, 0);
if (mapping == MAP_FAILED) {
KJ_FAIL_SYSCALL("mmap", errno, filename);
}
return kj::Array<const char>(
reinterpret_cast<const char*>(mapping), stats.st_size, mmapDisposer);
}
static char* canonicalizePath(char* path) {
// Taken from some old C code of mine.
// Preconditions:
// - path has already been determined to be relative, perhaps because the pointer actually points
// into the middle of some larger path string, in which case it must point to the character
// immediately after a '/'.
// Invariants:
// - src points to the beginning of a path component.
// - dst points to the location where the path component should end up, if it is not special.
// - src == path or src[-1] == '/'.
// - dst == path or dst[-1] == '/'.
char* src = path;
char* dst = path;
char* locked = dst; // dst cannot backtrack past this
char* partEnd;
bool hasMore;
for (;;) {
while (*src == '/') {
// Skip duplicate slash.
++src;
}
partEnd = strchr(src, '/');
hasMore = partEnd != NULL;
if (hasMore) {
*partEnd = '\0';
} else {
partEnd = src + strlen(src);
}
if (strcmp(src, ".") == 0) {
// Skip it.
} else if (strcmp(src, "..") == 0) {
if (dst > locked) {
// Backtrack over last path component.
--dst;
while (dst > locked && dst[-1] != '/') --dst;
} else {
locked += 3;
goto copy;
}
} else {
// Copy if needed.
copy:
if (dst < src) {
memmove(dst, src, partEnd - src);
dst += partEnd - src;
} else {
dst = partEnd;
}
*dst++ = '/';
}
if (hasMore) {
src = partEnd + 1;
} else {
// Oops, we have to remove the trailing '/'.
if (dst == path) {
// Oops, there is no trailing '/'. We have to return ".".
strcpy(path, ".");
return path + 1;
} else {
// Remove the trailing '/'. Note that this means that opening the file will work even
// if it is not a directory, where normally it should fail on non-directories when a
// trailing '/' is present. If this is a problem, we need to add some sort of special
// handling for this case where we stat() it separately to check if it is a directory,
// because Ekam findInput will not accept a trailing '/'.
--dst;
*dst = '\0';
return dst;
}
}
}
}
kj::String canonicalizePath(kj::StringPtr path) {
KJ_STACK_ARRAY(char, result, path.size() + 1, 128, 512);
strcpy(result.begin(), path.begin());
char* end = canonicalizePath(result.begin());
return kj::heapString(result.slice(0, end - result.begin()));
}
kj::String catPath(kj::StringPtr base, kj::StringPtr add) {
if (add.size() > 0 && add[0] == '/') {
return kj::heapString(add);
}
const char* pos = base.end();
while (pos > base.begin() && pos[-1] != '/') {
--pos;
}
return kj::str(base.slice(0, pos - base.begin()), add);
}
} // namespace
class ModuleLoader::Impl {
public:
Impl(int errorFd): errorFd(errorFd) {}
void addImportPath(kj::String path) {
searchPath.add(kj::heapString(kj::mv(path)));
}
kj::Maybe<const Module&> loadModule(kj::StringPtr localName, kj::StringPtr sourceName) const;
kj::Maybe<const Module&> loadModuleFromSearchPath(kj::StringPtr sourceName) const;
void writeError(kj::StringPtr content) const;
private:
int errorFd;
kj::Vector<kj::String> searchPath;
kj::MutexGuarded<std::map<kj::StringPtr, kj::Own<Module>>> modules;
};
class ModuleLoader::ModuleImpl: public Module {
public:
ModuleImpl(const ModuleLoader::Impl& loader, kj::String localName, kj::String sourceName)
: loader(loader), localName(kj::mv(localName)), sourceName(kj::mv(sourceName)) {}
kj::StringPtr getLocalName() const override {
return localName;
}
kj::StringPtr getSourceName() const override {
return sourceName;
}
Orphan<ParsedFile> loadContent(Orphanage orphanage) const override {
kj::Array<const char> content = mmapForRead(localName);
lineBreaks.get([&](kj::SpaceFor<kj::Vector<uint>>& space) {
auto vec = space.construct(content.size() / 40);
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;
auto statements = lexedBuilder.initRoot<LexedStatements>();
lex(content, statements, *this);
auto parsed = orphanage.newOrphan<ParsedFile>();
parseFile(statements.getStatements(), parsed.get(), *this);
return parsed;
}
kj::Maybe<const Module&> importRelative(kj::StringPtr importPath) const override {
if (importPath.size() > 0 && importPath[0] == '/') {
return loader.loadModuleFromSearchPath(importPath.slice(1));
} else {
return loader.loadModule(catPath(localName, importPath), catPath(sourceName, importPath));
}
}
void addError(uint32_t startByte, uint32_t endByte, kj::StringPtr message) const override {
auto& lines = lineBreaks.get(
[](kj::SpaceFor<kj::Vector<uint>>& space) {
KJ_FAIL_REQUIRE("Can't report errors until loadContent() is called.");
return space.construct();
});
uint startLine = findLargestElementBefore(lines, startByte);
uint startCol = startByte - lines[startLine];
loader.writeError(
kj::str(localName, ":", startLine, ":", startCol, ": error: ", message, "\n"));
}
private:
const ModuleLoader::Impl& loader;
kj::String localName;
kj::String sourceName;
kj::Lazy<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.
};
// =======================================================================================
kj::Maybe<const Module&> ModuleLoader::Impl::loadModule(
kj::StringPtr localName, kj::StringPtr sourceName) const {
kj::String canonicalLocalName = canonicalizePath(localName);
kj::String canonicalSourceName = canonicalizePath(sourceName);
auto locked = modules.lockExclusive();
auto iter = locked->find(canonicalLocalName);
if (iter != locked->end()) {
// Return existing file.
return *iter->second;
}
if (access(canonicalLocalName.cStr(), R_OK) < 0) {
// No such file.
return nullptr;
}
auto module = kj::heap<ModuleImpl>(
*this, kj::mv(canonicalLocalName), kj::mv(canonicalSourceName));
auto& result = *module;
locked->insert(std::make_pair(result.getLocalName(), kj::mv(module)));
return result;
}
kj::Maybe<const Module&> ModuleLoader::Impl::loadModuleFromSearchPath(
kj::StringPtr sourceName) const {
for (auto& search: searchPath) {
kj::String candidate = kj::str(search, "/", sourceName);
char* end = canonicalizePath(candidate.begin() + (candidate[0] == '/'));
KJ_IF_MAYBE(module, loadModule(
kj::heapString(candidate.slice(0, end - candidate.begin())), sourceName)) {
return *module;
}
}
return nullptr;
}
void ModuleLoader::Impl::writeError(kj::StringPtr content) const {
const char* pos = content.begin();
while (pos < content.end()) {
ssize_t n;
KJ_SYSCALL(n = write(errorFd, pos, content.end() - pos));
pos += n;
}
}
// =======================================================================================
ModuleLoader::ModuleLoader(int errorFd): impl(kj::heap<Impl>(errorFd)) {}
ModuleLoader::~ModuleLoader() {}
void ModuleLoader::addImportPath(kj::String path) { impl->addImportPath(kj::mv(path)); }
kj::Maybe<const Module&> ModuleLoader::loadModule(
kj::StringPtr localName, kj::StringPtr sourceName) const {
return impl->loadModule(localName, sourceName);
}
} // namespace compiler
} // namespace capnp
// Copyright (c) 2013, Kenton Varda <temporal@gmail.com>
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this
// list of conditions and the following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef CAPNP_COMPILER_MODULE_LOADER_H_
#define CAPNP_COMPILER_MODULE_LOADER_H_
#include "compiler.h"
#include <kj/memory.h>
#include <kj/array.h>
#include <kj/string.h>
namespace capnp {
namespace compiler {
class ModuleLoader {
public:
explicit ModuleLoader(int errorFd);
// Create a ModuleLoader that writes error messages to the given file descriptor.
KJ_DISALLOW_COPY(ModuleLoader);
~ModuleLoader();
void addImportPath(kj::String path);
// Add a directory to the list of paths that is searched for imports that start with a '/'.
kj::Maybe<const Module&> loadModule(kj::StringPtr localName, kj::StringPtr sourceName) const;
// Tries to load the module with the given filename. `localName` is the path to the file on
// disk (as you'd pass to open(2)), and `sourceName` is the canonical name it should be given
// in the schema (this is used e.g. to decide output file locations). Often, these are the same.
private:
class Impl;
kj::Own<Impl> impl;
class ModuleImpl;
};
} // namespace compiler
} // namespace capnp
#endif // CAPNP_COMPILER_MODULE_LOADER_H_
......@@ -570,7 +570,7 @@ schema::Node::Reader NodeTranslator::finish() {
// `unfinishedValues`, invalidating iterators in the process.
for (size_t i = 0; i < unfinishedValues.size(); i++) {
auto& value = unfinishedValues[i];
compileFinalValue(value.source, value.type, value.target);
compileValue(value.source, value.type, value.target, false);
}
return wipNode.getReader();
......@@ -1217,7 +1217,15 @@ bool NodeTranslator::compileType(TypeExpression::Reader source, schema::Type::Bu
return false;
}
if (!compileType(params[0], target.getBody().initListType())) {
auto elementType = target.getBody().initListType();
if (!compileType(params[0], elementType)) {
return false;
}
if (elementType.getBody().which() == schema::Type::Body::OBJECT_TYPE) {
errorReporter.addErrorOn(source, "'List(Object)' is not supported.");
// Seeing List(Object) later can mess things up, so change the type to Void.
elementType.getBody().setVoidType();
return false;
}
......@@ -1291,266 +1299,316 @@ void NodeTranslator::compileDefaultDefaultValue(
}
}
class NodeTranslator::DynamicSlot {
public:
DynamicSlot(DynamicStruct::Builder structBuilder, StructSchema::Member member)
: type(FIELD), structBuilder(structBuilder), member(member) {}
DynamicSlot(DynamicList::Builder listBuilder, uint index)
: type(ELEMENT), listBuilder(listBuilder), index(index) {}
DynamicSlot(DynamicStruct::Builder structBuilder, StructSchema::Member member,
StructSchema structMemberSchema)
: type(STRUCT_OBJECT_FIELD), structBuilder(structBuilder), member(member),
structMemberSchema(structMemberSchema) {}
DynamicSlot(DynamicStruct::Builder structBuilder, StructSchema::Member member,
ListSchema listMemberSchema)
: type(LIST_OBJECT_FIELD), structBuilder(structBuilder), member(member),
listMemberSchema(listMemberSchema) {}
DynamicSlot(DynamicUnion::Builder unionBuilder, StructSchema::Member unionMember)
: type(UNION_MEMBER), unionBuilder(unionBuilder), unionMember(unionMember) {}
bool wasSet = false;
DynamicStruct::Builder initStruct() {
wasSet = true;
switch (type) {
case FIELD: return structBuilder.init(member).as<DynamicStruct>();
case ELEMENT: return listBuilder[index].as<DynamicStruct>();
case STRUCT_OBJECT_FIELD: return structBuilder.initObject(member, structMemberSchema);
case LIST_OBJECT_FIELD: KJ_FAIL_REQUIRE("Value type mismatch.");
case UNION_MEMBER: return unionBuilder.init(unionMember).as<DynamicStruct>();
}
KJ_FAIL_ASSERT("can't get here");
}
DynamicList::Builder initList(uint size) {
wasSet = true;
switch (type) {
case FIELD: return structBuilder.init(member, size).as<DynamicList>();
case ELEMENT: return listBuilder.init(index, size).as<DynamicList>();
case STRUCT_OBJECT_FIELD: KJ_FAIL_REQUIRE("Value type mismatch.");
case LIST_OBJECT_FIELD: return structBuilder.initObject(member, listMemberSchema, size);
case UNION_MEMBER: return unionBuilder.init(unionMember, size).as<DynamicList>();
}
KJ_FAIL_ASSERT("can't get here");
}
DynamicUnion::Builder getUnion() {
wasSet = true;
switch (type) {
case FIELD: return structBuilder.get(member).as<DynamicUnion>();
case ELEMENT: KJ_FAIL_REQUIRE("Value type mismatch.");
case STRUCT_OBJECT_FIELD: KJ_FAIL_REQUIRE("Value type mismatch.");
case LIST_OBJECT_FIELD: KJ_FAIL_REQUIRE("Value type mismatch.");
case UNION_MEMBER: return unionBuilder.init(unionMember).as<DynamicUnion>();
}
KJ_FAIL_ASSERT("can't get here");
}
void set(DynamicValue::Reader value) {
wasSet = true;
switch (type) {
case FIELD: return structBuilder.set(member, value);
case ELEMENT: return listBuilder.set(index, value);
case STRUCT_OBJECT_FIELD: return structBuilder.set(member, value);
case LIST_OBJECT_FIELD: return structBuilder.set(member, value);
case UNION_MEMBER: return unionBuilder.set(unionMember, value);
}
KJ_FAIL_ASSERT("can't get here");
}
kj::Maybe<uint64_t> getEnumType() {
// If the member is an enum, get its type ID. Otherwise return nullptr.
//
// This is really ugly.
switch (type) {
case FIELD: return enumIdForMember(member);
case ELEMENT: {
if (listBuilder.getSchema().whichElementType() == schema::Type::Body::ENUM_TYPE) {
return listBuilder.getSchema().getEnumElementType().getProto().getId();
}
return nullptr;
}
case STRUCT_OBJECT_FIELD: return nullptr;
case LIST_OBJECT_FIELD: return nullptr;
case UNION_MEMBER: return enumIdForMember(unionMember);
}
KJ_FAIL_ASSERT("can't get here");
}
private:
enum Type {
FIELD, ELEMENT, STRUCT_OBJECT_FIELD, LIST_OBJECT_FIELD, UNION_MEMBER
};
Type type;
union {
struct {
DynamicStruct::Builder structBuilder;
StructSchema::Member member;
union {
StructSchema structMemberSchema;
ListSchema listMemberSchema;
};
};
struct {
DynamicList::Builder listBuilder;
uint index;
};
struct {
DynamicUnion::Builder unionBuilder;
StructSchema::Member unionMember;
};
};
static kj::Maybe<uint64_t> enumIdForMember(StructSchema::Member member) {
auto body = member.getProto().getBody();
if (body.which() == schema::StructNode::Member::Body::FIELD_MEMBER) {
auto typeBody = body.getFieldMember().getType().getBody();
if (typeBody.which() == schema::Type::Body::ENUM_TYPE) {
return typeBody.getEnumType();
}
}
return nullptr;
}
};
static kj::StringPtr getValueUnionMemberNameFor(schema::Type::Body::Which type) {
switch (type) {
case schema::Type::Body::VOID_TYPE: return "voidValue";
case schema::Type::Body::BOOL_TYPE: return "boolValue";
case schema::Type::Body::INT8_TYPE: return "int8Value";
case schema::Type::Body::INT16_TYPE: return "int16Value";
case schema::Type::Body::INT32_TYPE: return "int32Value";
case schema::Type::Body::INT64_TYPE: return "int64Value";
case schema::Type::Body::UINT8_TYPE: return "uint8Value";
case schema::Type::Body::UINT16_TYPE: return "uint16Value";
case schema::Type::Body::UINT32_TYPE: return "uint32Value";
case schema::Type::Body::UINT64_TYPE: return "uint64Value";
case schema::Type::Body::FLOAT32_TYPE: return "float32Value";
case schema::Type::Body::FLOAT64_TYPE: return "float64Value";
case schema::Type::Body::TEXT_TYPE: return "textValue";
case schema::Type::Body::DATA_TYPE: return "dataValue";
case schema::Type::Body::LIST_TYPE: return "listValue";
case schema::Type::Body::ENUM_TYPE: return "enumValue";
case schema::Type::Body::STRUCT_TYPE: return "structValue";
case schema::Type::Body::INTERFACE_TYPE: return "interfaceValue";
case schema::Type::Body::OBJECT_TYPE: return "objectValue";
}
KJ_FAIL_ASSERT("Unknown type.");
}
void NodeTranslator::compileBootstrapValue(ValueExpression::Reader source,
schema::Type::Reader type,
schema::Value::Builder target) {
switch (type.getBody().which()) {
case schema::Type::Body::LIST_TYPE:
case schema::Type::Body::OBJECT_TYPE:
case schema::Type::Body::STRUCT_TYPE:
case schema::Type::Body::INTERFACE_TYPE:
// Handle later.
case schema::Type::Body::OBJECT_TYPE:
unfinishedValues.add(UnfinishedValue { source, type, target });
return;
break;
default:
// Primitive value.
compileValue(source, type, target, true);
break;
}
}
void NodeTranslator::compileValue(ValueExpression::Reader source, schema::Type::Reader type,
schema::Value::Builder target, bool isBootstrap) {
auto valueUnion = toDynamic(target).get("body").as<DynamicUnion>();
auto member = valueUnion.getSchema().getMemberByName(
getValueUnionMemberNameFor(type.getBody().which()));
DynamicSlot slot(valueUnion, member);
compileValue(source, slot, isBootstrap);
if (!slot.wasSet) {
// An error should have been reported already. Initialize to a reasonable default.
compileDefaultDefaultValue(type, target);
}
}
void NodeTranslator::compileValue(ValueExpression::Reader src, DynamicSlot& dst, bool isBootstrap) {
// We rely on the dynamic API to detect type errors and throw exceptions.
//
// TODO(cleanup): We should perhaps ensure that all exceptions that this might throw are
// recoverable, so that this doesn't crash if -fno-exceptions is enabled. Or create a better
// way to test for type compatibility without throwing.
KJ_IF_MAYBE(exception, kj::runCatchingExceptions(
[&]() { compileValueInner(src, dst, isBootstrap); })) {
errorReporter.addErrorOn(src, exception->getDescription());
dst.wasSet = false;
}
}
switch (source.getBody().which()) {
void NodeTranslator::compileValueInner(
ValueExpression::Reader src, DynamicSlot& dst, bool isBootstrap) {
switch (src.getBody().which()) {
case ValueExpression::Body::NAME: {
auto name = source.getBody().getName();
auto name = src.getBody().getName();
bool isBare = name.getBase().which() == DeclName::Base::RELATIVE_NAME &&
name.getMemberPath().size() == 0;
if (isBare) {
// The name is just a bare identifier. It may be a literal value or an enumerant.
kj::StringPtr id = name.getBase().getRelativeName().getValue();
switch (type.getBody().which()) {
case schema::Type::Body::VOID_TYPE:
if (id == "void") {
target.getBody().setVoidValue();
return;
KJ_IF_MAYBE(enumId, dst.getEnumType()) {
auto enumSchema = resolver.resolveMaybeBootstrapSchema(*enumId).asEnum();
KJ_IF_MAYBE(enumerant, enumSchema.findEnumerantByName(id)) {
dst.set(DynamicEnum(*enumerant));
}
break;
case schema::Type::Body::BOOL_TYPE:
if (id == "true") {
target.getBody().setBoolValue(true);
return;
} else {
// Interpret known constant values.
if (id == "void") {
dst.set(Void::VOID);
} else if (id == "true") {
dst.set(true);
} else if (id == "false") {
target.getBody().setBoolValue(false);
return;
}
break;
case schema::Type::Body::FLOAT32_TYPE:
if (id == "nan") {
target.getBody().setFloat32Value(std::numeric_limits<float>::quiet_NaN());
return;
} else if (id == "inf") {
target.getBody().setFloat32Value(std::numeric_limits<float>::infinity());
return;
}
break;
case schema::Type::Body::FLOAT64_TYPE:
if (id == "nan") {
target.getBody().setFloat64Value(std::numeric_limits<double>::quiet_NaN());
return;
dst.set(false);
} else if (id == "nan") {
dst.set(std::numeric_limits<double>::quiet_NaN());
} else if (id == "inf") {
target.getBody().setFloat64Value(std::numeric_limits<double>::infinity());
return;
}
break;
case schema::Type::Body::ENUM_TYPE:
KJ_IF_MAYBE(enumerant, resolver.resolveMaybeBootstrapSchema(
type.getBody().getEnumType()).asEnum().findEnumerantByName(id)) {
target.getBody().setEnumValue(enumerant->getOrdinal());
return;
}
break;
default:
break;
}
dst.set(std::numeric_limits<double>::infinity());
}
// Haven't resolved the name yet. Try looking it up.
KJ_IF_MAYBE(resolved, resolver.resolve(source.getBody().getName())) {
if (resolved->kind != Declaration::Body::CONST_DECL) {
errorReporter.addErrorOn(source,
kj::str("'", declNameString(name), "' does not refer to a constant."));
compileDefaultDefaultValue(type, target);
break;
}
// We can get the bootstrap version of the constant here because if it has a
// non-bootstrap-time value then it's an error anyway.
Schema constSchema = resolver.resolveMaybeBootstrapSchema(resolved->id);
auto constReader = constSchema.getProto().getBody().getConstNode();
copyValue(constReader.getValue(), constReader.getType(), target, type, source);
if (isBare) {
// A fully unqualified identifier looks like it might refer to a constant visible in the
// current scope, but if that's really what the user wanted, we want them to use a
// qualified name to make it more obvious. Report an error.
Schema scope = resolver.resolveMaybeBootstrapSchema(constSchema.getProto().getScopeId());
auto scopeReader = scope.getProto();
kj::StringPtr parent;
if (scopeReader.getBody().which() == schema::Node::Body::FILE_NODE) {
parent = "";
} else {
parent = scopeReader.getDisplayName().slice(scopeReader.getDisplayNamePrefixLength());
}
kj::StringPtr id = name.getBase().getRelativeName().getValue();
errorReporter.addErrorOn(source, kj::str(
"Constant names must be qualified to avoid confusion. Please replace '",
declNameString(name), "' with '", parent, ".", id,
"', if that's what you intended."));
if (!dst.wasSet) {
// Haven't resolved the name yet. Try looking up a constant.
KJ_IF_MAYBE(constValue, readConstant(src.getBody().getName(), isBootstrap, src)) {
dst.set(*constValue);
}
}
break;
}
case ValueExpression::Body::POSITIVE_INT: {
uint64_t value = source.getBody().getPositiveInt();
uint64_t limit = std::numeric_limits<uint64_t>::max();
switch (type.getBody().which()) {
#define HANDLE_TYPE(discrim, name, type) \
case schema::Type::Body::discrim##_TYPE: \
limit = std::numeric_limits<type>::max(); \
target.getBody().set##name##Value(value); \
break;
HANDLE_TYPE(INT8 , Int8 , int8_t );
HANDLE_TYPE(INT16, Int16, int16_t);
HANDLE_TYPE(INT32, Int32, int32_t);
HANDLE_TYPE(INT64, Int64, int64_t);
HANDLE_TYPE(UINT8 , Uint8 , uint8_t );
HANDLE_TYPE(UINT16, Uint16, uint16_t);
HANDLE_TYPE(UINT32, Uint32, uint32_t);
HANDLE_TYPE(UINT64, Uint64, uint64_t);
#undef HANDLE_TYPE
case schema::Type::Body::FLOAT32_TYPE:
target.getBody().setFloat32Value(value);
case ValueExpression::Body::POSITIVE_INT:
dst.set(src.getBody().getPositiveInt());
break;
case schema::Type::Body::FLOAT64_TYPE:
target.getBody().setFloat64Value(value);
break;
default:
errorReporter.addErrorOn(source, "Type/value mismatch.");
compileDefaultDefaultValue(type, target);
break;
}
if (value > limit) {
errorReporter.addErrorOn(source, "Value out-of-range for type.");
case ValueExpression::Body::NEGATIVE_INT: {
uint64_t nValue = src.getBody().getNegativeInt();
if (nValue > (std::numeric_limits<uint64_t>::max() >> 1) + 1) {
errorReporter.addErrorOn(src, "Integer is too big to be negative.");
} else {
dst.set(kj::implicitCast<int64_t>(-nValue));
}
break;
}
case ValueExpression::Body::NEGATIVE_INT: {
uint64_t value = source.getBody().getNegativeInt();
uint64_t limit = std::numeric_limits<uint64_t>::max();
switch (type.getBody().which()) {
#define HANDLE_TYPE(discrim, name, type) \
case schema::Type::Body::discrim##_TYPE: \
limit = kj::implicitCast<uint64_t>(std::numeric_limits<type>::max()) + 1; \
target.getBody().set##name##Value(-value); \
case ValueExpression::Body::FLOAT:
dst.set(src.getBody().getFloat());
break;
HANDLE_TYPE(INT8 , Int8 , int8_t );
HANDLE_TYPE(INT16, Int16, int16_t);
HANDLE_TYPE(INT32, Int32, int32_t);
HANDLE_TYPE(INT64, Int64, int64_t);
#undef HANDLE_TYPE
case schema::Type::Body::FLOAT32_TYPE:
target.getBody().setFloat32Value(-kj::implicitCast<float>(value));
break;
case schema::Type::Body::FLOAT64_TYPE:
target.getBody().setFloat64Value(-kj::implicitCast<double>(value));
break;
default:
errorReporter.addErrorOn(source, "Type/value mismatch.");
compileDefaultDefaultValue(type, target);
case ValueExpression::Body::STRING:
dst.set(src.getBody().getString());
break;
}
if (value > limit) {
errorReporter.addErrorOn(source, "Value out-of-range for type.");
case ValueExpression::Body::LIST: {
auto srcList = src.getBody().getList();
auto dstList = dst.initList(srcList.size());
for (uint i = 0; i < srcList.size(); i++) {
DynamicSlot slot(dstList, i);
compileValue(srcList[i], slot, isBootstrap);
}
break;
}
case ValueExpression::Body::FLOAT: {
switch (type.getBody().which()) {
case schema::Type::Body::FLOAT32_TYPE:
target.getBody().setFloat32Value(source.getBody().getFloat());
break;
case schema::Type::Body::FLOAT64_TYPE:
target.getBody().setFloat64Value(source.getBody().getFloat());
break;
default:
errorReporter.addErrorOn(source, "Type/value mismatch.");
compileDefaultDefaultValue(type, target);
break;
case ValueExpression::Body::STRUCT_VALUE: {
auto srcStruct = src.getBody().getStructValue();
auto dstStruct = dst.initStruct();
auto dstSchema = dstStruct.getSchema();
for (auto assignment: srcStruct) {
auto fieldName = assignment.getFieldName();
KJ_IF_MAYBE(member, dstSchema.findMemberByName(fieldName.getValue())) {
DynamicSlot slot(dstStruct, *member);
compileValue(assignment.getValue(), slot, isBootstrap);
} else {
errorReporter.addErrorOn(fieldName, kj::str(
"Value has no field named '", fieldName.getValue(), "'."));
}
}
break;
}
case ValueExpression::Body::UNION_VALUE: {
auto srcUnion = src.getBody().getUnionValue();
auto dstUnion = dst.getUnion();
case ValueExpression::Body::STRING: {
switch (type.getBody().which()) {
case schema::Type::Body::TEXT_TYPE:
target.getBody().setTextValue(source.getBody().getString());
break;
case schema::Type::Body::DATA_TYPE: {
auto str = source.getBody().getString();
target.getBody().setDataValue(
kj::arrayPtr(reinterpret_cast<const byte*>(str.begin()), str.size()));
break;
auto fieldName = srcUnion.getFieldName();
KJ_IF_MAYBE(member, dstUnion.getSchema().findMemberByName(fieldName.getValue())) {
DynamicSlot slot(dstUnion, *member);
compileValue(srcUnion.getValue(), slot, isBootstrap);
} else {
errorReporter.addErrorOn(fieldName, kj::str(
"Union has no field named '", fieldName.getValue(), "'."));
}
default:
errorReporter.addErrorOn(source, "Type/value mismatch.");
compileDefaultDefaultValue(type, target);
break;
}
}
case ValueExpression::Body::LIST:
case ValueExpression::Body::STRUCT_VALUE:
case ValueExpression::Body::UNION_VALUE:
// If the type matched, these cases should have been handled earlier.
errorReporter.addErrorOn(source, "Type/value mismatch.");
compileDefaultDefaultValue(type, target);
break;
case ValueExpression::Body::UNKNOWN:
// Ignore earlier error.
compileDefaultDefaultValue(type, target);
break;
}
}
void NodeTranslator::compileFinalValue(ValueExpression::Reader source,
schema::Type::Reader type, schema::Value::Builder target) {
}
void NodeTranslator::copyValue(schema::Value::Reader src, schema::Type::Reader srcType,
schema::Value::Builder dst, schema::Type::Reader dstType,
ValueExpression::Reader errorLocation) {
DynamicUnion::Reader srcBody = DynamicStruct::Reader(src).get("body").as<DynamicUnion>();
DynamicUnion::Builder dstBody = DynamicStruct::Builder(dst).get("body").as<DynamicUnion>();
kj::StringPtr dstFieldName;
switch (dstType.getBody().which()) {
case schema::Type::Body::VOID_TYPE: dstFieldName = "voidValue"; break;
case schema::Type::Body::BOOL_TYPE: dstFieldName = "boolValue"; break;
case schema::Type::Body::INT8_TYPE: dstFieldName = "int8Value"; break;
case schema::Type::Body::INT16_TYPE: dstFieldName = "int16Value"; break;
case schema::Type::Body::INT32_TYPE: dstFieldName = "int32Value"; break;
case schema::Type::Body::INT64_TYPE: dstFieldName = "int64Value"; break;
case schema::Type::Body::UINT8_TYPE: dstFieldName = "uint8Value"; break;
case schema::Type::Body::UINT16_TYPE: dstFieldName = "uint16Value"; break;
case schema::Type::Body::UINT32_TYPE: dstFieldName = "uint32Value"; break;
case schema::Type::Body::UINT64_TYPE: dstFieldName = "uint64Value"; break;
case schema::Type::Body::FLOAT32_TYPE: dstFieldName = "float32Value"; break;
case schema::Type::Body::FLOAT64_TYPE: dstFieldName = "float64Value"; break;
case schema::Type::Body::TEXT_TYPE: dstFieldName = "textValue"; break;
case schema::Type::Body::DATA_TYPE: dstFieldName = "dataValue"; break;
case schema::Type::Body::LIST_TYPE: dstFieldName = "listValue"; break;
case schema::Type::Body::ENUM_TYPE: dstFieldName = "enumValue"; break;
case schema::Type::Body::STRUCT_TYPE: dstFieldName = "structValue"; break;
case schema::Type::Body::INTERFACE_TYPE: dstFieldName = "interfaceValue"; break;
case schema::Type::Body::OBJECT_TYPE: dstFieldName = "objectValue"; break;
}
kj::StringPtr dstFieldName = getValueUnionMemberNameFor(dstType.getBody().which());
KJ_IF_MAYBE(which, srcBody.which()) {
// Setting a value via the dynamic API implements the implicit conversions that we want, with
......@@ -1567,10 +1625,147 @@ void NodeTranslator::copyValue(schema::Value::Reader src, schema::Type::Reader s
}
}
kj::Maybe<DynamicValue::Reader> NodeTranslator::readConstant(
DeclName::Reader name, bool isBootstrap, ValueExpression::Reader errorLocation) {
KJ_IF_MAYBE(resolved, resolver.resolve(name)) {
if (resolved->kind != Declaration::Body::CONST_DECL) {
errorReporter.addErrorOn(errorLocation,
kj::str("'", declNameString(name), "' does not refer to a constant."));
return nullptr;
}
// If we're bootstrapping, then we know we're expecting a primitive value, so if the
// constant turns out to be non-primitive, we'll error out anyway. If we're not
// bootstrapping, we may be compiling a non-primitive value and so we need the final
// version of the constant to make sure its value is filled in.
//
// We need to be very careful not to query this Schema's dependencies because if it is
// a final schema then this query could trigger a lazy load which would deadlock.
Schema constSchema = isBootstrap ?
resolver.resolveMaybeBootstrapSchema(resolved->id) :
resolver.resolveFinalSchema(resolved->id);
auto constReader = constSchema.getProto().getBody().getConstNode();
auto constValue = toDynamic(constReader.getValue()).get("body").as<DynamicUnion>().get();
if (constValue.getType() == DynamicValue::OBJECT) {
// We need to assign an appropriate schema to this object.
DynamicObject objValue = constValue.as<DynamicObject>();
auto constType = constReader.getType();
switch (constType.getBody().which()) {
case schema::Type::Body::STRUCT_TYPE:
constValue = objValue.as(resolver.resolveMaybeBootstrapSchema(
constType.getBody().getStructType()).asStruct());
break;
case schema::Type::Body::LIST_TYPE:
constValue = objValue.as(makeListSchemaOf(constType.getBody().getListType()));
break;
case schema::Type::Body::OBJECT_TYPE:
// Fine as-is.
break;
default:
KJ_FAIL_ASSERT("Unrecognized Object-typed member of schema::Value::body.");
break;
}
}
if (name.getBase().which() == DeclName::Base::RELATIVE_NAME &&
name.getMemberPath().size() == 0) {
// A fully unqualified identifier looks like it might refer to a constant visible in the
// current scope, but if that's really what the user wanted, we want them to use a
// qualified name to make it more obvious. Report an error.
Schema scope = resolver.resolveMaybeBootstrapSchema(constSchema.getProto().getScopeId());
auto scopeReader = scope.getProto();
kj::StringPtr parent;
if (scopeReader.getBody().which() == schema::Node::Body::FILE_NODE) {
parent = "";
} else {
parent = scopeReader.getDisplayName().slice(scopeReader.getDisplayNamePrefixLength());
}
kj::StringPtr id = name.getBase().getRelativeName().getValue();
errorReporter.addErrorOn(errorLocation, kj::str(
"Constant names must be qualified to avoid confusion. Please replace '",
declNameString(name), "' with '", parent, ".", id,
"', if that's what you intended."));
}
return constValue;
} else {
return nullptr;
}
}
ListSchema NodeTranslator::makeListSchemaOf(schema::Type::Reader elementType) {
auto body = elementType.getBody();
switch (body.which()) {
case schema::Type::Body::ENUM_TYPE:
return ListSchema::of(resolver.resolveMaybeBootstrapSchema(body.getEnumType()).asEnum());
case schema::Type::Body::STRUCT_TYPE:
return ListSchema::of(resolver.resolveMaybeBootstrapSchema(body.getStructType()).asStruct());
case schema::Type::Body::INTERFACE_TYPE:
return ListSchema::of(resolver.resolveMaybeBootstrapSchema(body.getInterfaceType())
.asInterface());
case schema::Type::Body::LIST_TYPE:
return ListSchema::of(makeListSchemaOf(body.getListType()));
default:
return ListSchema::of(body.which());
}
}
Orphan<List<schema::Annotation>> NodeTranslator::compileAnnotationApplications(
List<Declaration::AnnotationApplication>::Reader annotations,
kj::StringPtr targetsFlagName) {
if (annotations.size() == 0) {
// Return null.
return Orphan<List<schema::Annotation>>();
}
Orphanage orphanage = Orphanage::getForMessageContaining(wipNode.get());
auto result = orphanage.newOrphan<List<schema::Annotation>>(annotations.size());
auto builder = result.get();
for (uint i = 0; i < annotations.size(); i++) {
Declaration::AnnotationApplication::Reader annotation = annotations[i];
schema::Annotation::Builder annotationBuilder = builder[i];
auto name = annotation.getName();
KJ_IF_MAYBE(decl, resolver.resolve(name)) {
if (decl->kind != Declaration::Body::ANNOTATION_DECL) {
errorReporter.addErrorOn(name, kj::str(
"'", declNameString(name), "' is not an annotation."));
} else {
annotationBuilder.setId(decl->id);
auto node = resolver.resolveMaybeBootstrapSchema(decl->id).getProto()
.getBody().getAnnotationNode();
if (!toDynamic(node).get(targetsFlagName).as<bool>()) {
errorReporter.addErrorOn(name, kj::str(
"'", declNameString(name), "' cannot be applied to this kind of declaration."));
}
// Interpret the value.
auto value = annotation.getValue();
switch (value.which()) {
case Declaration::AnnotationApplication::Value::NONE:
// No value, i.e. void.
if (node.getType().getBody().which() == schema::Type::Body::VOID_TYPE) {
annotationBuilder.initValue().getBody().setVoidValue();
} else {
errorReporter.addErrorOn(name, kj::str(
"'", declNameString(name), "' requires a value."));
compileDefaultDefaultValue(node.getType(), annotationBuilder.initValue());
}
break;
case Declaration::AnnotationApplication::Value::EXPRESSION:
compileBootstrapValue(value.getExpression(), node.getType(),
annotationBuilder.initValue());
break;
}
}
}
}
return result;
}
} // namespace compiler
......
......@@ -138,13 +138,21 @@ private:
void compileBootstrapValue(ValueExpression::Reader source, schema::Type::Reader type,
schema::Value::Builder target);
// Interprets the value expression and initializes `target` with the result. If some parts of
// the value cannot be built at bootstrap time, they'll be added to `unfinishedValues`
// automatically for later processing.
// Calls compileValue() if this value should be interpreted at bootstrap time. Otheriwse,
// adds the value to `unfinishedValues` for later evaluation.
void compileFinalValue(ValueExpression::Reader source,
schema::Type::Reader type, schema::Value::Builder target);
// Compile a previously-unfinished value. See `unfinishedValues`.
void compileValue(ValueExpression::Reader source, schema::Type::Reader type,
schema::Value::Builder target, bool isBootstrap);
// Interprets the value expression and initializes `target` with the result.
class DynamicSlot;
void compileValue(ValueExpression::Reader src, DynamicSlot& dst, bool isBootstrap);
// Fill in `dst` (which effectively points to a struct field or list element) with the given
// value.
void compileValueInner(ValueExpression::Reader src, DynamicSlot& dst, bool isBootstrap);
// Helper for compileValue().
void copyValue(schema::Value::Reader src, schema::Type::Reader srcType,
schema::Value::Builder dst, schema::Type::Reader dstType,
......@@ -152,6 +160,13 @@ private:
// Copy a value from one schema to another, possibly coercing the type if compatible, or
// reporting an error otherwise.
kj::Maybe<DynamicValue::Reader> readConstant(DeclName::Reader name, bool isBootstrap,
ValueExpression::Reader errorLocation);
// Get the value of the given constant.
ListSchema makeListSchemaOf(schema::Type::Reader elementType);
// Construct a list schema representing a list of elements of the given type.
Orphan<List<schema::Annotation>> compileAnnotationApplications(
List<Declaration::AnnotationApplication>::Reader annotations,
kj::StringPtr targetsFlagName);
......
......@@ -51,7 +51,7 @@ uint64_t randomId() {
} // namespace
void parseFile(List<Statement>::Reader statements, ParsedFile::Builder result,
ErrorReporter& errorReporter) {
const ErrorReporter& errorReporter) {
CapnpParser parser(Orphanage::getForMessageContaining(result), errorReporter);
kj::Vector<Orphan<Declaration>> decls(statements.size());
......@@ -203,7 +203,7 @@ class ParseListItems {
// Transformer that parses all items in the input token sequence list using the given parser.
public:
constexpr ParseListItems(ItemParser&& itemParser, ErrorReporter& errorReporter)
constexpr ParseListItems(ItemParser&& itemParser, const ErrorReporter& errorReporter)
: itemParser(p::sequence(kj::fwd<ItemParser>(itemParser), p::endOfInput)),
errorReporter(errorReporter) {}
......@@ -241,11 +241,12 @@ public:
private:
decltype(p::sequence(kj::instance<ItemParser>(), p::endOfInput)) itemParser;
ErrorReporter& errorReporter;
const ErrorReporter& errorReporter;
};
template <typename ItemParser>
constexpr auto parenthesizedList(ItemParser&& itemParser, ErrorReporter& errorReporter) -> decltype(
constexpr auto parenthesizedList(ItemParser&& itemParser,
const ErrorReporter& errorReporter) -> decltype(
transform(rawParenthesizedList, ParseListItems<ItemParser>(
kj::fwd<ItemParser>(itemParser), errorReporter))) {
return transform(rawParenthesizedList, ParseListItems<ItemParser>(
......@@ -253,7 +254,8 @@ constexpr auto parenthesizedList(ItemParser&& itemParser, ErrorReporter& errorRe
}
template <typename ItemParser>
constexpr auto bracketedList(ItemParser&& itemParser, ErrorReporter& errorReporter) -> decltype(
constexpr auto bracketedList(ItemParser&& itemParser,
const ErrorReporter& errorReporter) -> decltype(
transform(rawBracketedList, ParseListItems<ItemParser>(
kj::fwd<ItemParser>(itemParser), errorReporter))) {
return transform(rawBracketedList, ParseListItems<ItemParser>(
......@@ -313,9 +315,9 @@ void initLocation(kj::parse::Span<List<Token>::Reader::Iterator> location,
// =======================================================================================
CapnpParser::CapnpParser(Orphanage orphanageParam, ErrorReporter& errorReporterParam)
CapnpParser::CapnpParser(Orphanage orphanageParam, const ErrorReporter& errorReporterParam)
: orphanage(orphanageParam), errorReporter(errorReporterParam) {
parsers.declName = arena.copy(p::transform(
parsers.declName = arena.copy(p::transformWithLocation(
p::sequence(
p::oneOf(
p::transform(p::sequence(keyword("import"), stringLiteral),
......@@ -337,12 +339,15 @@ CapnpParser::CapnpParser(Orphanage orphanageParam, ErrorReporter& errorReporterP
return result;
})),
p::many(p::sequence(op("."), identifier))),
[this](Orphan<DeclName>&& result, kj::Array<Located<Text::Reader>>&& memberPath)
[this](kj::parse::Span<List<Token>::Reader::Iterator> location,
Orphan<DeclName>&& result, kj::Array<Located<Text::Reader>>&& memberPath)
-> Orphan<DeclName> {
auto builder = result.get().initMemberPath(memberPath.size());
auto builder = result.get();
auto pathBuilder = builder.initMemberPath(memberPath.size());
for (size_t i = 0; i < memberPath.size(); i++) {
memberPath[i].copyTo(builder[i]);
memberPath[i].copyTo(pathBuilder[i]);
}
initLocation(location, builder);
return kj::mv(result);
}));
......
......@@ -34,7 +34,7 @@ namespace capnp {
namespace compiler {
void parseFile(List<Statement>::Reader statements, ParsedFile::Builder result,
ErrorReporter& errorReporter);
const ErrorReporter& errorReporter);
// Parse a list of statements to build a ParsedFile.
//
// If any errors are reported, then the output is not usable. However, it may be passed on through
......@@ -45,7 +45,7 @@ class CapnpParser {
// them into your own parsers.
public:
CapnpParser(Orphanage orphanage, ErrorReporter& errorReporter);
CapnpParser(Orphanage orphanage, const ErrorReporter& errorReporter);
// `orphanage` is used to allocate Cap'n Proto message objects in the result. `inputStart` is
// a pointer to the beginning of the input, used to compute byte offsets.
......@@ -127,7 +127,7 @@ public:
private:
Orphanage orphanage;
ErrorReporter& errorReporter;
const ErrorReporter& errorReporter;
kj::Arena arena;
Parsers parsers;
};
......
......@@ -109,6 +109,8 @@ DynamicTypeFor<TypeIfEnum<T>> toDynamic(T&& value);
class DynamicEnum {
public:
DynamicEnum() = default;
inline DynamicEnum(EnumSchema::Enumerant enumerant)
: schema(enumerant.getContainingEnum()), value(enumerant.getOrdinal()) {}
template <typename T, typename = kj::EnableIf<kind<T>() == Kind::ENUM>>
inline DynamicEnum(T&& value): DynamicEnum(toDynamic(value)) {}
......
......@@ -138,6 +138,11 @@ public:
inline bool startsWith(const StringPtr& other) const { return StringPtr(*this).startsWith(other);}
inline bool endsWith(const StringPtr& other) const { return StringPtr(*this).endsWith(other); }
inline StringPtr slice(size_t start) const { return StringPtr(*this).slice(start); }
inline ArrayPtr<const char> slice(size_t start, size_t end) const {
return StringPtr(*this).slice(start, end);
}
private:
Array<char> content;
};
......
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