blob: 42d45bb9e861617439e1088e87c7e926033525ac [file] [log] [blame]
// Copyright (c) 2013-2014 Sandstorm Development Group, Inc. and contributors
// Licensed under the MIT License:
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#if _WIN32
#include <kj/win32-api-version.h>
#endif
#include "parser.h"
#include "type-id.h"
#include <capnp/dynamic.h>
#include <kj/debug.h>
#include <kj/encoding.h>
#if !_MSC_VER
#include <unistd.h>
#endif
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#if _WIN32
#include <windows.h>
#include <wincrypt.h>
#undef CONST
#include <kj/windows-sanity.h>
#endif
namespace capnp {
namespace compiler {
uint64_t generateRandomId() {
uint64_t result;
#if _WIN32
HCRYPTPROV handle;
KJ_ASSERT(CryptAcquireContextW(&handle, nullptr, nullptr,
PROV_RSA_FULL, CRYPT_VERIFYCONTEXT | CRYPT_SILENT));
KJ_DEFER(KJ_ASSERT(CryptReleaseContext(handle, 0)) {break;});
KJ_ASSERT(CryptGenRandom(handle, sizeof(result), reinterpret_cast<BYTE*>(&result)));
#else
int fd;
KJ_SYSCALL(fd = open("/dev/urandom", O_RDONLY));
KJ_DEFER(close(fd));
ssize_t n;
KJ_SYSCALL(n = read(fd, &result, sizeof(result)), "/dev/urandom");
KJ_ASSERT(n == sizeof(result), "Incomplete read from /dev/urandom.", n);
#endif
return result | (1ull << 63);
}
void parseFile(List<Statement>::Reader statements, ParsedFile::Builder result,
ErrorReporter& errorReporter) {
CapnpParser parser(Orphanage::getForMessageContaining(result), errorReporter);
kj::Vector<Orphan<Declaration>> decls(statements.size());
kj::Vector<Orphan<Declaration::AnnotationApplication>> annotations;
auto fileDecl = result.getRoot();
fileDecl.setFile(VOID);
for (auto statement: statements) {
KJ_IF_MAYBE(decl, parser.parseStatement(statement, parser.getParsers().fileLevelDecl)) {
Declaration::Builder builder = decl->get();
switch (builder.which()) {
case Declaration::NAKED_ID:
if (fileDecl.getId().isUid()) {
errorReporter.addError(builder.getStartByte(), builder.getEndByte(),
"File can only have one ID.");
} else {
fileDecl.getId().adoptUid(builder.disownNakedId());
if (builder.hasDocComment()) {
fileDecl.adoptDocComment(builder.disownDocComment());
}
}
break;
case Declaration::NAKED_ANNOTATION:
annotations.add(builder.disownNakedAnnotation());
break;
default:
decls.add(kj::mv(*decl));
break;
}
}
}
if (fileDecl.getId().which() != Declaration::Id::UID) {
// We didn't see an ID. Generate one randomly for now.
uint64_t id = generateRandomId();
fileDecl.getId().initUid().setValue(id);
// Don't report missing ID if there was a parse error, because quite often the parse error
// prevents us from parsing the ID even though it is actually there.
if (!errorReporter.hadErrors()) {
errorReporter.addError(0, 0,
kj::str("File does not declare an ID. I've generated one for you. Add this line to "
"your file: @0x", kj::hex(id), ";"));
}
}
auto declsBuilder = fileDecl.initNestedDecls(decls.size());
for (size_t i = 0; i < decls.size(); i++) {
declsBuilder.adoptWithCaveats(i, kj::mv(decls[i]));
}
auto annotationsBuilder = fileDecl.initAnnotations(annotations.size());
for (size_t i = 0; i < annotations.size(); i++) {
annotationsBuilder.adoptWithCaveats(i, kj::mv(annotations[i]));
}
}
namespace p = kj::parse;
namespace {
// =======================================================================================
template <typename T>
struct Located {
T value;
uint32_t startByte;
uint32_t endByte;
template <typename Builder>
void copyLocationTo(Builder builder) {
builder.setStartByte(startByte);
builder.setEndByte(endByte);
}
template <typename Builder>
void copyTo(Builder builder) {
builder.setValue(value);
copyLocationTo(builder);
}
template <typename Result>
Orphan<Result> asProto(Orphanage orphanage) {
auto result = orphanage.newOrphan<Result>();
copyTo(result.get());
return result;
}
template <typename Other>
Located<kj::Decay<Other>> rewrap(Other&& other) {
return Located<Other>(kj::fwd<Other>(other), startByte, endByte);
}
Located(const T& value, uint32_t startByte, uint32_t endByte)
: value(value), startByte(startByte), endByte(endByte) {}
Located(T&& value, uint32_t startByte, uint32_t endByte)
: value(kj::mv(value)), startByte(startByte), endByte(endByte) {}
};
// =======================================================================================
template <typename T, Token::Which type, T (Token::Reader::*get)() const>
struct MatchTokenType {
kj::Maybe<Located<T>> operator()(Token::Reader token) const {
if (token.which() == type) {
return Located<T>((token.*get)(), token.getStartByte(), token.getEndByte());
} else {
return nullptr;
}
}
};
#define TOKEN_TYPE_PARSER(type, discrim, getter) \
p::transformOrReject(p::any, \
MatchTokenType<type, Token::discrim, &Token::Reader::getter>())
constexpr auto identifier = TOKEN_TYPE_PARSER(Text::Reader, IDENTIFIER, getIdentifier);
constexpr auto stringLiteral = TOKEN_TYPE_PARSER(Text::Reader, STRING_LITERAL, getStringLiteral);
constexpr auto binaryLiteral = TOKEN_TYPE_PARSER(Data::Reader, BINARY_LITERAL, getBinaryLiteral);
constexpr auto integerLiteral = TOKEN_TYPE_PARSER(uint64_t, INTEGER_LITERAL, getIntegerLiteral);
constexpr auto floatLiteral = TOKEN_TYPE_PARSER(double, FLOAT_LITERAL, getFloatLiteral);
constexpr auto operatorToken = TOKEN_TYPE_PARSER(Text::Reader, OPERATOR, getOperator);
constexpr auto rawParenthesizedList =
TOKEN_TYPE_PARSER(List<List<Token>>::Reader, PARENTHESIZED_LIST, getParenthesizedList);
constexpr auto rawBracketedList =
TOKEN_TYPE_PARSER(List<List<Token>>::Reader, BRACKETED_LIST, getBracketedList);
// =======================================================================================
class ExactString {
public:
constexpr ExactString(const char* expected): expected(expected) {}
kj::Maybe<kj::Tuple<>> operator()(Located<Text::Reader>&& text) const {
if (text.value == expected) {
return kj::Tuple<>();
} else {
return nullptr;
}
}
private:
const char* expected;
};
constexpr auto keyword(const char* expected)
-> decltype(p::transformOrReject(identifier, ExactString(expected))) {
return p::transformOrReject(identifier, ExactString(expected));
}
constexpr auto op(const char* expected)
-> decltype(p::transformOrReject(operatorToken, ExactString(expected))) {
return p::transformOrReject(operatorToken, ExactString(expected));
}
class LocatedExactString {
public:
constexpr LocatedExactString(const char* expected): expected(expected) {}
kj::Maybe<Located<Text::Reader>> operator()(Located<Text::Reader>&& text) const {
if (text.value == expected) {
return kj::mv(text);
} else {
return nullptr;
}
}
private:
const char* expected;
};
constexpr auto locatedKeyword(const char* expected)
-> decltype(p::transformOrReject(identifier, LocatedExactString(expected))) {
return p::transformOrReject(identifier, LocatedExactString(expected));
}
// =======================================================================================
template <typename ItemParser>
class ParseListItems {
// Transformer that parses all items in the input token sequence list using the given parser.
public:
constexpr ParseListItems(ItemParser&& itemParser, ErrorReporter& errorReporter)
: itemParser(p::sequence(kj::fwd<ItemParser>(itemParser), p::endOfInput)),
errorReporter(errorReporter) {}
Located<kj::Array<kj::Maybe<p::OutputType<ItemParser, CapnpParser::ParserInput>>>> operator()(
Located<List<List<Token>>::Reader>&& items) const {
auto result = kj::heapArray<kj::Maybe<p::OutputType<ItemParser, CapnpParser::ParserInput>>>(
items.value.size());
for (uint i = 0; i < items.value.size(); i++) {
auto item = items.value[i];
CapnpParser::ParserInput input(item.begin(), item.end());
result[i] = itemParser(input);
if (result[i] == nullptr) {
// Parsing failed. Report an error.
auto best = input.getBest();
if (best < item.end()) {
// Report error from the point where parsing failed to the end of the item.
errorReporter.addError(
best->getStartByte(), (item.end() - 1)->getEndByte(), "Parse error.");
} else if (item.size() > 0) {
// The item is non-empty and the parser consumed all of it before failing. Report an
// error for the whole thing.
errorReporter.addError(
item.begin()->getStartByte(), (item.end() - 1)->getEndByte(), "Parse error.");
} else {
// The item has no content.
// TODO(cleanup): We don't actually know the item's location, so we can only report
// an error across the whole list. Fix this.
errorReporter.addError(items.startByte, items.endByte, "Parse error: Empty list item.");
}
}
}
return Located<kj::Array<kj::Maybe<p::OutputType<ItemParser, CapnpParser::ParserInput>>>>(
kj::mv(result), items.startByte, items.endByte);
}
private:
decltype(p::sequence(kj::instance<ItemParser>(), p::endOfInput)) itemParser;
ErrorReporter& errorReporter;
};
template <typename ItemParser>
constexpr auto parenthesizedList(ItemParser&& itemParser, ErrorReporter& errorReporter) -> decltype(
transform(rawParenthesizedList, ParseListItems<ItemParser>(
kj::fwd<ItemParser>(itemParser), errorReporter))) {
return transform(rawParenthesizedList, ParseListItems<ItemParser>(
kj::fwd<ItemParser>(itemParser), errorReporter));
}
template <typename ItemParser>
constexpr auto bracketedList(ItemParser&& itemParser, ErrorReporter& errorReporter) -> decltype(
transform(rawBracketedList, ParseListItems<ItemParser>(
kj::fwd<ItemParser>(itemParser), errorReporter))) {
return transform(rawBracketedList, ParseListItems<ItemParser>(
kj::fwd<ItemParser>(itemParser), errorReporter));
}
// =======================================================================================
template <typename T>
Orphan<List<T>> arrayToList(Orphanage& orphanage, kj::Array<Orphan<T>>&& elements) {
auto result = orphanage.newOrphan<List<T>>(elements.size());
auto builder = result.get();
for (size_t i = 0; i < elements.size(); i++) {
builder.adoptWithCaveats(i, kj::mv(elements[i]));
}
return kj::mv(result);
}
static void initGenericParams(Declaration::Builder builder,
kj::Maybe<Located<kj::Array<kj::Maybe<Located<Text::Reader>>>>>&& genericParameters) {
KJ_IF_MAYBE(p, genericParameters) {
auto params = builder.initParameters(p->value.size());
for (uint i: kj::indices(p->value)) {
KJ_IF_MAYBE(name, p->value[i]) {
auto param = params[i];
param.setName(name->value);
name->copyLocationTo(param);
}
}
}
}
static Declaration::Builder initDecl(
Declaration::Builder builder, Located<Text::Reader>&& name,
kj::Maybe<Orphan<LocatedInteger>>&& id,
kj::Maybe<Located<kj::Array<kj::Maybe<Located<Text::Reader>>>>>&& genericParameters,
kj::Array<Orphan<Declaration::AnnotationApplication>>&& annotations) {
name.copyTo(builder.initName());
KJ_IF_MAYBE(i, id) {
builder.getId().adoptUid(kj::mv(*i));
}
initGenericParams(builder, kj::mv(genericParameters));
auto list = builder.initAnnotations(annotations.size());
for (uint i = 0; i < annotations.size(); i++) {
list.adoptWithCaveats(i, kj::mv(annotations[i]));
}
return builder;
}
static Declaration::Builder initMemberDecl(
Declaration::Builder builder, Located<Text::Reader>&& name,
Orphan<LocatedInteger>&& ordinal,
kj::Array<Orphan<Declaration::AnnotationApplication>>&& annotations) {
name.copyTo(builder.initName());
builder.getId().adoptOrdinal(kj::mv(ordinal));
auto list = builder.initAnnotations(annotations.size());
for (uint i = 0; i < annotations.size(); i++) {
list.adoptWithCaveats(i, kj::mv(annotations[i]));
}
return builder;
}
template <typename BuilderType>
void initLocation(kj::parse::Span<typename List<Token>::Reader::Iterator> location,
BuilderType builder) {
if (location.begin() < location.end()) {
builder.setStartByte(location.begin()->getStartByte());
builder.setEndByte((location.end() - 1)->getEndByte());
}
}
} // namespace
// =======================================================================================
CapnpParser::CapnpParser(Orphanage orphanageParam, ErrorReporter& errorReporterParam)
: orphanage(orphanageParam), errorReporter(errorReporterParam) {
auto& tupleElement = arena.copy(p::transform(
p::sequence(p::optional(p::sequence(identifier, op("="))), parsers.expression),
[this](kj::Maybe<Located<Text::Reader>>&& fieldName, Orphan<Expression>&& fieldValue)
-> Orphan<Expression::Param> {
auto result = orphanage.newOrphan<Expression::Param>();
auto builder = result.get();
KJ_IF_MAYBE(fn, fieldName) {
fn->copyTo(builder.initNamed());
} else {
builder.setUnnamed();
}
builder.adoptValue(kj::mv(fieldValue));
return kj::mv(result);
}));
auto& tuple = arena.copy<Parser<Located<Orphan<List<Expression::Param>>>>>(
arena.copy(p::transform(
parenthesizedList(tupleElement, errorReporter),
[this](Located<kj::Array<kj::Maybe<Orphan<Expression::Param>>>>&& elements)
-> Located<Orphan<List<Expression::Param>>> {
auto result = orphanage.newOrphan<List<Expression::Param>>(elements.value.size());
auto builder = result.get();
for (uint i: kj::indices(elements.value)) {
KJ_IF_MAYBE(e, elements.value[i]) {
builder.adoptWithCaveats(i, kj::mv(*e));
} else {
builder[i].initValue().setUnknown();
}
}
return elements.rewrap(kj::mv(result));
})));
parsers.expression = arena.copy(p::transform(
p::sequence(
// Base expression.
p::oneOf(
p::transform(integerLiteral,
[this](Located<uint64_t>&& value) -> Orphan<Expression> {
auto result = orphanage.newOrphan<Expression>();
auto builder = result.get();
builder.setPositiveInt(value.value);
value.copyLocationTo(builder);
return result;
}),
p::transform(p::sequence(op("-"), integerLiteral),
[this](Located<uint64_t>&& value) -> Orphan<Expression> {
auto result = orphanage.newOrphan<Expression>();
auto builder = result.get();
builder.setNegativeInt(value.value);
value.copyLocationTo(builder);
return result;
}),
p::transform(floatLiteral,
[this](Located<double>&& value) -> Orphan<Expression> {
auto result = orphanage.newOrphan<Expression>();
auto builder = result.get();
builder.setFloat(value.value);
value.copyLocationTo(builder);
return result;
}),
p::transform(p::sequence(op("-"), floatLiteral),
[this](Located<double>&& value) -> Orphan<Expression> {
auto result = orphanage.newOrphan<Expression>();
auto builder = result.get();
builder.setFloat(-value.value);
value.copyLocationTo(builder);
return result;
}),
p::transformWithLocation(p::sequence(op("-"), keyword("inf")),
[this](kj::parse::Span<List<Token>::Reader::Iterator> location)
-> Orphan<Expression> {
auto result = orphanage.newOrphan<Expression>();
auto builder = result.get();
builder.setFloat(-kj::inf());
initLocation(location, builder);
return result;
}),
p::transform(p::oneOrMore(stringLiteral),
[this](kj::Array<Located<Text::Reader>>&& value) -> Orphan<Expression> {
auto result = orphanage.newOrphan<Expression>();
auto builder = result.get();
builder.setString(kj::strArray(
KJ_MAP(part, value) { return part.value; }, ""));
builder.setStartByte(value.front().startByte);
builder.setEndByte(value.back().endByte);
return result;
}),
p::transform(binaryLiteral,
[this](Located<Data::Reader>&& value) -> Orphan<Expression> {
auto result = orphanage.newOrphan<Expression>();
auto builder = result.get();
builder.setBinary(value.value);
value.copyLocationTo(builder);
return result;
}),
p::transform(bracketedList(parsers.expression, errorReporter),
[this](Located<kj::Array<kj::Maybe<Orphan<Expression>>>>&& value)
-> Orphan<Expression> {
auto result = orphanage.newOrphan<Expression>();
auto builder = result.get();
auto listBuilder = builder.initList(value.value.size());
for (uint i = 0; i < value.value.size(); i++) {
KJ_IF_MAYBE(element, value.value[i]) {
listBuilder.adoptWithCaveats(i, kj::mv(*element));
}
}
value.copyLocationTo(builder);
return result;
}),
p::transform(tuple,
[this](Located<Orphan<List<Expression::Param>>>&& value)
-> Orphan<Expression> {
auto elements = value.value.get();
if (elements.size() == 1 && elements[0].isUnnamed()) {
// Single-value tuple is just a value.
return elements[0].disownValue();
} else {
auto result = orphanage.newOrphan<Expression>();
auto builder = result.get();
builder.adoptTuple(kj::mv(value.value));
value.copyLocationTo(builder);
return result;
}
}),
p::transformWithLocation(p::sequence(keyword("import"), stringLiteral),
[this](kj::parse::Span<List<Token>::Reader::Iterator> location,
Located<Text::Reader>&& filename) -> Orphan<Expression> {
auto result = orphanage.newOrphan<Expression>();
auto builder = result.get();
initLocation(location, builder);
filename.copyTo(builder.initImport());
return result;
}),
p::transformWithLocation(p::sequence(keyword("embed"), stringLiteral),
[this](kj::parse::Span<List<Token>::Reader::Iterator> location,
Located<Text::Reader>&& filename) -> Orphan<Expression> {
auto result = orphanage.newOrphan<Expression>();
auto builder = result.get();
initLocation(location, builder);
filename.copyTo(builder.initEmbed());
return result;
}),
p::transformWithLocation(p::sequence(op("."), identifier),
[this](kj::parse::Span<List<Token>::Reader::Iterator> location,
Located<Text::Reader>&& name) -> Orphan<Expression> {
auto result = orphanage.newOrphan<Expression>();
auto builder = result.get();
initLocation(location, builder);
name.copyTo(builder.initAbsoluteName());
return result;
}),
p::transform(identifier,
[this](Located<Text::Reader>&& name) -> Orphan<Expression> {
auto result = orphanage.newOrphan<Expression>();
auto builder = result.get();
name.copyTo(builder.initRelativeName());
name.copyLocationTo(builder);
return result;
})),
// Suffixes, e.g. ".member" or "(param1, param2)".
p::many(p::oneOf(
p::transformWithLocation(p::sequence(op("."), identifier),
[this](kj::parse::Span<List<Token>::Reader::Iterator> location,
Located<Text::Reader>&& name) -> Orphan<Expression> {
auto result = orphanage.newOrphan<Expression>();
auto builder = result.get();
initLocation(location, builder);
name.copyTo(builder.initMember().initName());
return result;
}),
p::transform(tuple,
[this](Located<Orphan<List<Expression::Param>>>&& params) -> Orphan<Expression> {
auto result = orphanage.newOrphan<Expression>();
auto builder = result.get();
params.copyLocationTo(builder);
builder.initApplication().adoptParams(kj::mv(params.value));
return result;
})))),
[](Orphan<Expression>&& base, kj::Array<Orphan<Expression>>&& suffixes)
-> Orphan<Expression> {
// Apply all the suffixes to the base expression.
uint startByte = base.getReader().getStartByte();
for (auto& suffix: suffixes) {
auto builder = suffix.get();
if (builder.isApplication()) {
builder.getApplication().adoptFunction(kj::mv(base));
} else if (builder.isMember()) {
builder.getMember().adoptParent(kj::mv(base));
} else {
KJ_FAIL_ASSERT("Unknown suffix?", (uint)builder.which());
}
builder.setStartByte(startByte);
base = kj::mv(suffix);
}
return kj::mv(base);
}));
parsers.annotation = arena.copy(p::transform(
p::sequence(op("$"), parsers.expression),
[this](Orphan<Expression>&& expression)
-> Orphan<Declaration::AnnotationApplication> {
auto result = orphanage.newOrphan<Declaration::AnnotationApplication>();
auto builder = result.get();
auto exp = expression.get();
if (exp.isApplication()) {
// Oops, this annotation specifies the value, but we parsed it as an application on
// the preceding expression. Pull it back apart.
auto app = exp.getApplication();
builder.adoptName(app.disownFunction());
auto params = app.getParams();
if (params.size() == 1 && params[0].isUnnamed()) {
// Params has a single unnamed element, so reduce it to a simple value rather than
// a tuple.
builder.getValue().adoptExpression(params[0].disownValue());
} else {
// Params is not a single unnamed element, so it's a tuple.
builder.getValue().initExpression().adoptTuple(app.disownParams());
}
} else {
// The annotation has no value.
builder.adoptName(kj::mv(expression));
builder.getValue().setNone();
}
return result;
}));
parsers.uid = arena.copy(p::transform(
p::sequence(op("@"), integerLiteral),
[this](Located<uint64_t>&& value) {
if (value.value < (1ull << 63)) {
errorReporter.addError(value.startByte, value.endByte,
"Invalid ID. Please generate a new one with 'capnpc -i'.");
}
return value.asProto<LocatedInteger>(orphanage);
}));
parsers.ordinal = arena.copy(p::transform(
p::sequence(op("@"), integerLiteral),
[this](Located<uint64_t>&& value) {
if (value.value >= 65536) {
errorReporter.addError(value.startByte, value.endByte,
"Ordinals cannot be greater than 65535.");
}
return value.asProto<LocatedInteger>(orphanage);
}));
// -----------------------------------------------------------------
parsers.usingDecl = arena.copy(p::transform(
p::sequence(keyword("using"), p::optional(p::sequence(identifier, op("="))),
parsers.expression),
[this](kj::Maybe<Located<Text::Reader>>&& name, Orphan<Expression>&& target)
-> DeclParserResult {
auto decl = orphanage.newOrphan<Declaration>();
auto builder = decl.get();
KJ_IF_MAYBE(n, name) {
n->copyTo(builder.initName());
} else {
auto targetReader = target.getReader();
if (targetReader.isMember()) {
builder.setName(targetReader.getMember().getName());
} else {
errorReporter.addErrorOn(targetReader,
"'using' declaration without '=' must specify a named declaration from a "
"different scope.");
}
}
// no id, no annotations for using decl
builder.initUsing().adoptTarget(kj::mv(target));
return DeclParserResult(kj::mv(decl));
}));
parsers.constDecl = arena.copy(p::transform(
p::sequence(keyword("const"), identifier, p::optional(parsers.uid),
op(":"), parsers.expression,
op("="), parsers.expression,
p::many(parsers.annotation)),
[this](Located<Text::Reader>&& name, kj::Maybe<Orphan<LocatedInteger>>&& id,
Orphan<Expression>&& type, Orphan<Expression>&& value,
kj::Array<Orphan<Declaration::AnnotationApplication>>&& annotations)
-> DeclParserResult {
auto decl = orphanage.newOrphan<Declaration>();
auto builder =
initDecl(decl.get(), kj::mv(name), kj::mv(id), nullptr,
kj::mv(annotations)).initConst();
builder.adoptType(kj::mv(type));
builder.adoptValue(kj::mv(value));
return DeclParserResult(kj::mv(decl));
}));
parsers.enumDecl = arena.copy(p::transform(
p::sequence(keyword("enum"), identifier, p::optional(parsers.uid),
p::many(parsers.annotation)),
[this](Located<Text::Reader>&& name, kj::Maybe<Orphan<LocatedInteger>>&& id,
kj::Array<Orphan<Declaration::AnnotationApplication>>&& annotations)
-> DeclParserResult {
auto decl = orphanage.newOrphan<Declaration>();
initDecl(decl.get(), kj::mv(name), kj::mv(id), nullptr, kj::mv(annotations)).setEnum();
return DeclParserResult(kj::mv(decl), parsers.enumLevelDecl);
}));
parsers.enumerantDecl = arena.copy(p::transform(
p::sequence(identifier, parsers.ordinal, p::many(parsers.annotation)),
[this](Located<Text::Reader>&& name, Orphan<LocatedInteger>&& ordinal,
kj::Array<Orphan<Declaration::AnnotationApplication>>&& annotations)
-> DeclParserResult {
auto decl = orphanage.newOrphan<Declaration>();
initMemberDecl(decl.get(), kj::mv(name), kj::mv(ordinal), kj::mv(annotations))
.setEnumerant();
return DeclParserResult(kj::mv(decl));
}));
parsers.structDecl = arena.copy(p::transform(
p::sequence(keyword("struct"), identifier, p::optional(parsers.uid),
p::optional(parenthesizedList(identifier, errorReporter)),
p::many(parsers.annotation)),
[this](Located<Text::Reader>&& name, kj::Maybe<Orphan<LocatedInteger>>&& id,
kj::Maybe<Located<kj::Array<kj::Maybe<Located<Text::Reader>>>>>&& genericParameters,
kj::Array<Orphan<Declaration::AnnotationApplication>>&& annotations)
-> DeclParserResult {
auto decl = orphanage.newOrphan<Declaration>();
initDecl(decl.get(), kj::mv(name), kj::mv(id), kj::mv(genericParameters),
kj::mv(annotations)).setStruct();
return DeclParserResult(kj::mv(decl), parsers.structLevelDecl);
}));
parsers.fieldDecl = arena.copy(p::transform(
p::sequence(identifier, parsers.ordinal, op(":"), parsers.expression,
p::optional(p::sequence(op("="), parsers.expression)),
p::many(parsers.annotation)),
[this](Located<Text::Reader>&& name, Orphan<LocatedInteger>&& ordinal,
Orphan<Expression>&& type, kj::Maybe<Orphan<Expression>>&& defaultValue,
kj::Array<Orphan<Declaration::AnnotationApplication>>&& annotations)
-> DeclParserResult {
auto decl = orphanage.newOrphan<Declaration>();
auto builder =
initMemberDecl(decl.get(), kj::mv(name), kj::mv(ordinal), kj::mv(annotations))
.initField();
builder.adoptType(kj::mv(type));
KJ_IF_MAYBE(val, defaultValue) {
builder.getDefaultValue().adoptValue(kj::mv(*val));
} else {
builder.getDefaultValue().setNone();
}
return DeclParserResult(kj::mv(decl));
}));
// Parse an ordinal followed by an optional colon, or no ordinal but require a colon.
auto& ordinalOrColon = arena.copy(p::oneOf(
p::transform(p::sequence(parsers.ordinal, p::optional(op("!")), p::optional(op(":"))),
[](Orphan<LocatedInteger>&& ordinal,
kj::Maybe<kj::Tuple<>> exclamation,
kj::Maybe<kj::Tuple<>> colon)
-> kj::Tuple<kj::Maybe<Orphan<LocatedInteger>>, bool, bool> {
return kj::tuple(kj::mv(ordinal), exclamation == nullptr, colon == nullptr);
}),
p::transform(op(":"),
[]() -> kj::Tuple<kj::Maybe<Orphan<LocatedInteger>>, bool, bool> {
return kj::tuple(nullptr, false, false);
})));
parsers.unionDecl = arena.copy(p::transform(
// The first branch of this oneOf() matches named unions. The second branch matches unnamed
// unions and generates dummy values for the parse results.
p::oneOf(
p::sequence(
identifier, ordinalOrColon,
keyword("union"), p::many(parsers.annotation)),
p::transformWithLocation(p::sequence(keyword("union"), p::endOfInput),
[](kj::parse::Span<List<Token>::Reader::Iterator> location) {
return kj::tuple(
Located<Text::Reader>("", location.begin()->getStartByte(),
location.begin()->getEndByte()),
kj::Maybe<Orphan<LocatedInteger>>(nullptr),
false, false,
kj::Array<Orphan<Declaration::AnnotationApplication>>(nullptr));
})),
[this](Located<Text::Reader>&& name,
kj::Maybe<Orphan<LocatedInteger>>&& ordinal,
bool missingExclamation, bool missingColon,
kj::Array<Orphan<Declaration::AnnotationApplication>>&& annotations)
-> DeclParserResult {
if (missingExclamation) {
errorReporter.addErrorOn(KJ_ASSERT_NONNULL(ordinal).getReader(),
"As of Cap'n Proto v0.3, it is no longer necessary to assign numbers to "
"unions. However, removing the number will break binary compatibility. "
"If this is an old protocol and you need to retain compatibility, please "
"add an exclamation point after the number to indicate that it is really "
"needed, e.g. `foo @1! :union {`. If this is a new protocol or compatibility "
"doesn't matter, just remove the @n entirely. Sorry for the inconvenience, "
"and thanks for being an early adopter! :)");
}
if (missingColon) {
errorReporter.addErrorOn(KJ_ASSERT_NONNULL(ordinal).getReader(),
"As of Cap'n Proto v0.3, the 'union' keyword should be prefixed with a colon "
"for named unions, e.g. `foo :union {`.");
}
auto decl = orphanage.newOrphan<Declaration>();
auto builder = decl.get();
name.copyTo(builder.initName());
KJ_IF_MAYBE(ord, ordinal) {
builder.getId().adoptOrdinal(kj::mv(*ord));
} else {
builder.getId().setUnspecified();
}
auto list = builder.initAnnotations(annotations.size());
for (uint i = 0; i < annotations.size(); i++) {
list.adoptWithCaveats(i, kj::mv(annotations[i]));
}
builder.setUnion();
return DeclParserResult(kj::mv(decl), parsers.structLevelDecl);
}));
parsers.groupDecl = arena.copy(p::transform(
p::sequence(identifier, op(":"), keyword("group"), p::many(parsers.annotation)),
[this](Located<Text::Reader>&& name,
kj::Array<Orphan<Declaration::AnnotationApplication>>&& annotations)
-> DeclParserResult {
auto decl = orphanage.newOrphan<Declaration>();
auto builder = decl.get();
name.copyTo(builder.getName());
builder.getId().setUnspecified();
auto list = builder.initAnnotations(annotations.size());
for (uint i = 0; i < annotations.size(); i++) {
list.adoptWithCaveats(i, kj::mv(annotations[i]));
}
builder.setGroup();
return DeclParserResult(kj::mv(decl), parsers.structLevelDecl);
}));
parsers.interfaceDecl = arena.copy(p::transform(
p::sequence(keyword("interface"), identifier, p::optional(parsers.uid),
p::optional(parenthesizedList(identifier, errorReporter)),
p::optional(p::sequence(
keyword("extends"), parenthesizedList(parsers.expression, errorReporter))),
p::many(parsers.annotation)),
[this](Located<Text::Reader>&& name, kj::Maybe<Orphan<LocatedInteger>>&& id,
kj::Maybe<Located<kj::Array<kj::Maybe<Located<Text::Reader>>>>>&& genericParameters,
kj::Maybe<Located<kj::Array<kj::Maybe<Orphan<Expression>>>>>&& superclasses,
kj::Array<Orphan<Declaration::AnnotationApplication>>&& annotations)
-> DeclParserResult {
auto decl = orphanage.newOrphan<Declaration>();
auto builder = initDecl(
decl.get(), kj::mv(name), kj::mv(id), kj::mv(genericParameters),
kj::mv(annotations)).initInterface();
KJ_IF_MAYBE(s, superclasses) {
auto superclassesBuilder = builder.initSuperclasses(s->value.size());
for (uint i: kj::indices(s->value)) {
KJ_IF_MAYBE(superclass, s->value[i]) {
superclassesBuilder.adoptWithCaveats(i, kj::mv(*superclass));
}
}
}
return DeclParserResult(kj::mv(decl), parsers.interfaceLevelDecl);
}));
parsers.param = arena.copy(p::transformWithLocation(
p::sequence(identifier, op(":"), parsers.expression,
p::optional(p::sequence(op("="), parsers.expression)),
p::many(parsers.annotation)),
[this](kj::parse::Span<List<Token>::Reader::Iterator> location,
Located<Text::Reader>&& name, Orphan<Expression>&& type,
kj::Maybe<Orphan<Expression>>&& defaultValue,
kj::Array<Orphan<Declaration::AnnotationApplication>>&& annotations)
-> Orphan<Declaration::Param> {
auto result = orphanage.newOrphan<Declaration::Param>();
auto builder = result.get();
initLocation(location, builder);
name.copyTo(builder.initName());
builder.adoptType(kj::mv(type));
builder.adoptAnnotations(arrayToList(orphanage, kj::mv(annotations)));
KJ_IF_MAYBE(val, defaultValue) {
builder.getDefaultValue().adoptValue(kj::mv(*val));
} else {
builder.getDefaultValue().setNone();
}
return kj::mv(result);
}));
auto& paramList = arena.copy(p::oneOf(
p::transform(parenthesizedList(parsers.param, errorReporter),
[this](Located<kj::Array<kj::Maybe<Orphan<Declaration::Param>>>>&& params)
-> Orphan<Declaration::ParamList> {
auto decl = orphanage.newOrphan<Declaration::ParamList>();
auto builder = decl.get();
params.copyLocationTo(builder);
auto listBuilder = builder.initNamedList(params.value.size());
for (uint i: kj::indices(params.value)) {
KJ_IF_MAYBE(param, params.value[i]) {
listBuilder.adoptWithCaveats(i, kj::mv(*param));
}
}
return decl;
}),
p::transform(locatedKeyword("stream"),
[this](Located<Text::Reader>&& kw) -> Orphan<Declaration::ParamList> {
auto decl = orphanage.newOrphan<Declaration::ParamList>();
auto builder = decl.get();
kw.copyLocationTo(builder);
builder.setStream();
return decl;
}),
p::transform(parsers.expression,
[this](Orphan<Expression>&& name) -> Orphan<Declaration::ParamList> {
auto decl = orphanage.newOrphan<Declaration::ParamList>();
auto builder = decl.get();
auto nameReader = name.getReader();
builder.setStartByte(nameReader.getStartByte());
builder.setEndByte(nameReader.getEndByte());
builder.adoptType(kj::mv(name));
return decl;
})));
parsers.methodDecl = arena.copy(p::transform(
p::sequence(identifier, parsers.ordinal,
p::optional(bracketedList(identifier, errorReporter)),
paramList,
p::optional(p::sequence(op("->"), paramList)),
p::many(parsers.annotation)),
[this](Located<Text::Reader>&& name, Orphan<LocatedInteger>&& ordinal,
kj::Maybe<Located<kj::Array<kj::Maybe<Located<Text::Reader>>>>>&& genericParams,
Orphan<Declaration::ParamList>&& params,
kj::Maybe<Orphan<Declaration::ParamList>>&& results,
kj::Array<Orphan<Declaration::AnnotationApplication>>&& annotations)
-> DeclParserResult {
auto decl = orphanage.newOrphan<Declaration>();
auto nodeBuilder = initMemberDecl(
decl.get(), kj::mv(name), kj::mv(ordinal), kj::mv(annotations));
initGenericParams(nodeBuilder, kj::mv(genericParams));
auto builder = nodeBuilder.initMethod();
builder.adoptParams(kj::mv(params));
KJ_IF_MAYBE(r, results) {
builder.getResults().adoptExplicit(kj::mv(*r));
} else {
builder.getResults().setNone();
}
return DeclParserResult(kj::mv(decl));
}));
auto& annotationTarget = arena.copy(p::oneOf(
identifier,
p::transformWithLocation(op("*"),
[](kj::parse::Span<List<Token>::Reader::Iterator> location) {
// Hacky...
return Located<Text::Reader>("*",
location.begin()->getStartByte(),
location.begin()->getEndByte());
})));
parsers.annotationDecl = arena.copy(p::transform(
p::sequence(keyword("annotation"), identifier, p::optional(parsers.uid),
parenthesizedList(annotationTarget, errorReporter),
op(":"), parsers.expression,
p::many(parsers.annotation)),
[this](Located<Text::Reader>&& name, kj::Maybe<Orphan<LocatedInteger>>&& id,
Located<kj::Array<kj::Maybe<Located<Text::Reader>>>>&& targets,
Orphan<Expression>&& type,
kj::Array<Orphan<Declaration::AnnotationApplication>>&& annotations)
-> DeclParserResult {
auto decl = orphanage.newOrphan<Declaration>();
auto builder =
initDecl(decl.get(), kj::mv(name), kj::mv(id), nullptr,
kj::mv(annotations)).initAnnotation();
builder.adoptType(kj::mv(type));
DynamicStruct::Builder dynamicBuilder = builder;
for (auto& maybeTarget: targets.value) {
KJ_IF_MAYBE(target, maybeTarget) {
if (target->value == "*") {
// Set all.
if (targets.value.size() > 1) {
errorReporter.addError(target->startByte, target->endByte,
"Wildcard should not be specified together with other targets.");
}
for (auto field: dynamicBuilder.getSchema().getFields()) {
if (field.getProto().getName().startsWith("targets")) {
dynamicBuilder.set(field, true);
}
}
} else {
if (target->value.size() == 0 || target->value.size() >= 32 ||
target->value[0] < 'a' || target->value[0] > 'z') {
errorReporter.addError(target->startByte, target->endByte,
"Not a valid annotation target.");
} else {
char buffer[64];
strcpy(buffer, "targets");
strcat(buffer, target->value.cStr());
buffer[strlen("targets")] += 'A' - 'a';
KJ_IF_MAYBE(field, dynamicBuilder.getSchema().findFieldByName(buffer)) {
if (dynamicBuilder.get(*field).as<bool>()) {
errorReporter.addError(target->startByte, target->endByte,
"Duplicate target specification.");
}
dynamicBuilder.set(*field, true);
} else {
errorReporter.addError(target->startByte, target->endByte,
"Not a valid annotation target.");
}
}
}
}
}
return DeclParserResult(kj::mv(decl));
}));
// -----------------------------------------------------------------
auto& nakedId = arena.copy(p::transform(parsers.uid,
[this](Orphan<LocatedInteger>&& value) -> DeclParserResult {
auto decl = orphanage.newOrphan<Declaration>();
decl.get().adoptNakedId(kj::mv(value));
return DeclParserResult(kj::mv(decl));
}));
auto& nakedAnnotation = arena.copy(p::transform(parsers.annotation,
[this](Orphan<Declaration::AnnotationApplication>&& value) -> DeclParserResult {
auto decl = orphanage.newOrphan<Declaration>();
decl.get().adoptNakedAnnotation(kj::mv(value));
return DeclParserResult(kj::mv(decl));
}));
// -----------------------------------------------------------------
parsers.genericDecl = arena.copy(p::oneOf(
parsers.usingDecl, parsers.constDecl, parsers.annotationDecl,
parsers.enumDecl, parsers.structDecl, parsers.interfaceDecl));
parsers.fileLevelDecl = arena.copy(p::oneOf(
parsers.genericDecl, nakedId, nakedAnnotation));
parsers.enumLevelDecl = arena.copy(p::oneOf(parsers.enumerantDecl));
parsers.structLevelDecl = arena.copy(p::oneOf(
parsers.unionDecl, parsers.fieldDecl, parsers.groupDecl, parsers.genericDecl));
parsers.interfaceLevelDecl = arena.copy(p::oneOf(
parsers.methodDecl, parsers.genericDecl));
}
CapnpParser::~CapnpParser() noexcept(false) {}
kj::Maybe<Orphan<Declaration>> CapnpParser::parseStatement(
Statement::Reader statement, const DeclParser& parser) {
auto fullParser = p::sequence(parser, p::endOfInput);
auto tokens = statement.getTokens();
ParserInput parserInput(tokens.begin(), tokens.end());
KJ_IF_MAYBE(output, fullParser(parserInput)) {
auto builder = output->decl.get();
if (statement.hasDocComment()) {
builder.setDocComment(statement.getDocComment());
}
builder.setStartByte(statement.getStartByte());
builder.setEndByte(statement.getEndByte());
switch (statement.which()) {
case Statement::LINE:
if (output->memberParser != nullptr) {
errorReporter.addError(statement.getStartByte(), statement.getEndByte(),
"This statement should end with a block, not a semicolon.");
}
break;
case Statement::BLOCK:
KJ_IF_MAYBE(memberParser, output->memberParser) {
auto memberStatements = statement.getBlock();
kj::Vector<Orphan<Declaration>> members(memberStatements.size());
for (auto memberStatement: memberStatements) {
KJ_IF_MAYBE(member, parseStatement(memberStatement, *memberParser)) {
members.add(kj::mv(*member));
}
}
builder.adoptNestedDecls(arrayToList(orphanage, members.releaseAsArray()));
} else {
errorReporter.addError(statement.getStartByte(), statement.getEndByte(),
"This statement should end with a semicolon, not a block.");
}
break;
}
return kj::mv(output->decl);
} else {
// Parse error. Figure out where to report it.
auto best = parserInput.getBest();
uint32_t bestByte;
if (best != tokens.end()) {
bestByte = best->getStartByte();
} else if (tokens.end() != tokens.begin()) {
bestByte = (tokens.end() - 1)->getEndByte();
} else {
bestByte = statement.getStartByte();
}
errorReporter.addError(bestByte, bestByte, "Parse error.");
return nullptr;
}
}
// =======================================================================================
static const char HEXDIGITS[] = "0123456789abcdef";
static kj::StringTree stringLiteralStringTree(kj::StringPtr chars) {
return kj::strTree('"', kj::encodeCEscape(chars), '"');
}
static kj::StringTree binaryLiteralStringTree(Data::Reader data) {
kj::Vector<char> escaped(data.size() * 3);
for (byte b: data) {
escaped.add(HEXDIGITS[b % 16]);
escaped.add(HEXDIGITS[b / 16]);
escaped.add(' ');
}
escaped.removeLast();
return kj::strTree("0x\"", escaped, '"');
}
static kj::StringTree expressionStringTree(Expression::Reader exp);
static kj::StringTree tupleLiteral(List<Expression::Param>::Reader params) {
auto parts = kj::heapArrayBuilder<kj::StringTree>(params.size());
for (auto param: params) {
auto part = expressionStringTree(param.getValue());
if (param.isNamed()) {
part = kj::strTree(param.getNamed().getValue(), " = ", kj::mv(part));
}
parts.add(kj::mv(part));
}
return kj::strTree("( ", kj::StringTree(parts.finish(), ", "), " )");
}
static kj::StringTree expressionStringTree(Expression::Reader exp) {
switch (exp.which()) {
case Expression::UNKNOWN:
return kj::strTree("<parse error>");
case Expression::POSITIVE_INT:
return kj::strTree(exp.getPositiveInt());
case Expression::NEGATIVE_INT:
return kj::strTree('-', exp.getNegativeInt());
case Expression::FLOAT:
return kj::strTree(exp.getFloat());
case Expression::STRING:
return stringLiteralStringTree(exp.getString());
case Expression::BINARY:
return binaryLiteralStringTree(exp.getBinary());
case Expression::RELATIVE_NAME:
return kj::strTree(exp.getRelativeName().getValue());
case Expression::ABSOLUTE_NAME:
return kj::strTree('.', exp.getAbsoluteName().getValue());
case Expression::IMPORT:
return kj::strTree("import ", stringLiteralStringTree(exp.getImport().getValue()));
case Expression::EMBED:
return kj::strTree("embed ", stringLiteralStringTree(exp.getEmbed().getValue()));
case Expression::LIST: {
auto list = exp.getList();
auto parts = kj::heapArrayBuilder<kj::StringTree>(list.size());
for (auto element: list) {
parts.add(expressionStringTree(element));
}
return kj::strTree("[ ", kj::StringTree(parts.finish(), ", "), " ]");
}
case Expression::TUPLE:
return tupleLiteral(exp.getTuple());
case Expression::APPLICATION: {
auto app = exp.getApplication();
return kj::strTree(expressionStringTree(app.getFunction()),
'(', tupleLiteral(app.getParams()), ')');
}
case Expression::MEMBER: {
auto member = exp.getMember();
return kj::strTree(expressionStringTree(member.getParent()), '.',
member.getName().getValue());
}
}
KJ_UNREACHABLE;
}
kj::String expressionString(Expression::Reader name) {
return expressionStringTree(name).flatten();
}
} // namespace compiler
} // namespace capnp