blob: ee948f655b439bb030f5f394da9ca1eae589a234 [file] [log] [blame]
// Copyright (c) 2013, Kenton Varda <[email protected]>
// 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 "parser.h"
#include <capnp/dynamic.h>
#include <kj/debug.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <limits>
namespace capnp {
namespace compiler {
namespace {
uint64_t randomId() {
uint64_t result;
int fd;
KJ_SYSCALL(fd = open("/dev/urandom", O_RDONLY));
ssize_t n;
KJ_SYSCALL(n = read(fd, &result, sizeof(result)), "/dev/urandom");
KJ_ASSERT(n == sizeof(result), "Incomplete read from /dev/urandom.", n);
return result | (1ull << 63);
}
} // namespace
void parseFile(List<Statement>::Reader statements, ParsedFile::Builder result,
const 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.getBody().initFileDecl();
for (auto statement: statements) {
KJ_IF_MAYBE(decl, parser.parseStatement(statement, parser.getParsers().fileLevelDecl)) {
Declaration::Builder builder = decl->get();
auto body = builder.getBody();
switch (body.which()) {
case Declaration::Body::NAKED_ID:
if (fileDecl.getId().which() == Declaration::Id::UID) {
errorReporter.addError(builder.getStartByte(), builder.getEndByte(),
"File can only have one ID.");
} else {
fileDecl.getId().adoptUid(body.disownNakedId());
if (builder.hasDocComment()) {
fileDecl.adoptDocComment(builder.disownDocComment());
}
}
break;
case Declaration::Body::NAKED_ANNOTATION:
annotations.add(body.disownNakedAnnotation());
break;
default:
decls.add(kj::mv(*decl));
break;
}
}
}
if (fileDecl.getId().which() != Declaration::Id::UID) {
uint64_t id = randomId();
fileDecl.getId().initUid().setValue(id);
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;
}
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::Body::Which type, T (Token::Body::Reader::*get)() const>
struct MatchTokenType {
kj::Maybe<Located<T>> operator()(Token::Reader token) const {
auto body = token.getBody();
if (body.which() == type) {
return Located<T>((body.*get)(), token.getStartByte(), token.getEndByte());
} else {
return nullptr;
}
}
};
#define TOKEN_TYPE_PARSER(type, discrim, getter) \
p::transformOrReject(p::any, \
MatchTokenType<type, Token::Body::discrim, &Token::Body::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 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));
}
// =======================================================================================
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, const 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;
const ErrorReporter& errorReporter;
};
template <typename ItemParser>
constexpr auto parenthesizedList(ItemParser&& itemParser,
const 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,
const 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);
}
inline Declaration::Builder initDecl(
Declaration::Builder builder, Located<Text::Reader>&& name,
kj::Maybe<Orphan<LocatedInteger>>&& id,
kj::Array<Orphan<Declaration::AnnotationApplication>>&& annotations) {
name.copyTo(builder.initName());
KJ_IF_MAYBE(i, id) {
builder.getId().adoptUid(kj::mv(*i));
}
auto list = builder.initAnnotations(annotations.size());
for (uint i = 0; i < annotations.size(); i++) {
list.adoptWithCaveats(i, kj::mv(annotations[i]));
}
return builder;
}
inline 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<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, const ErrorReporter& errorReporterParam)
: orphanage(orphanageParam), errorReporter(errorReporterParam) {
parsers.declName = arena.copy(p::transformWithLocation(
p::sequence(
p::oneOf(
p::transform(p::sequence(keyword("import"), stringLiteral),
[this](Located<Text::Reader>&& filename) -> Orphan<DeclName> {
auto result = orphanage.newOrphan<DeclName>();
filename.copyTo(result.get().getBase().initImportName());
return result;
}),
p::transform(p::sequence(op("."), identifier),
[this](Located<Text::Reader>&& filename) -> Orphan<DeclName> {
auto result = orphanage.newOrphan<DeclName>();
filename.copyTo(result.get().getBase().initAbsoluteName());
return result;
}),
p::transform(identifier,
[this](Located<Text::Reader>&& filename) -> Orphan<DeclName> {
auto result = orphanage.newOrphan<DeclName>();
filename.copyTo(result.get().getBase().initRelativeName());
return result;
})),
p::many(p::sequence(op("."), identifier))),
[this](kj::parse::Span<List<Token>::Reader::Iterator> location,
Orphan<DeclName>&& result, kj::Array<Located<Text::Reader>>&& memberPath)
-> Orphan<DeclName> {
auto builder = result.get();
auto pathBuilder = builder.initMemberPath(memberPath.size());
for (size_t i = 0; i < memberPath.size(); i++) {
memberPath[i].copyTo(pathBuilder[i]);
}
initLocation(location, builder);
return kj::mv(result);
}));
parsers.typeExpression = arena.copy(p::transformWithLocation(
p::sequence(parsers.declName, p::optional(
parenthesizedList(parsers.typeExpression, errorReporter))),
[this](kj::parse::Span<List<Token>::Reader::Iterator> location,
Orphan<DeclName>&& name,
kj::Maybe<Located<kj::Array<kj::Maybe<Orphan<TypeExpression>>>>>&& params)
-> Orphan<TypeExpression> {
auto result = orphanage.newOrphan<TypeExpression>();
auto builder = result.get();
builder.adoptName(kj::mv(name));
KJ_IF_MAYBE(p, params) {
auto paramsBuilder = builder.initParams(p->value.size());
for (uint i = 0; i < p->value.size(); i++) {
KJ_IF_MAYBE(param, p->value[i]) {
paramsBuilder.adoptWithCaveats(i, kj::mv(*param));
} else {
// param failed to parse
paramsBuilder[i].initName().getBase().initAbsoluteName().setValue("Void");
}
}
}
initLocation(location, builder);
return result;
}));
auto& fieldAssignment = arena.copy(p::transform(
p::sequence(p::optional(p::sequence(identifier, op("="))), parsers.valueExpression),
[this](kj::Maybe<Located<Text::Reader>>&& fieldName, Orphan<ValueExpression>&& value)
-> Orphan<ValueExpression::FieldAssignment> {
auto result = orphanage.newOrphan<ValueExpression::FieldAssignment>();
auto builder = result.get();
// The field name is optional for now because this makes it easier for us to parse unions
// later. We'll produce an error later if the name is missing when required.
KJ_IF_MAYBE(fn, fieldName) {
fn->copyTo(builder.initFieldName());
}
builder.adoptValue(kj::mv(value));
return result;
}));
parsers.parenthesizedValueExpression = arena.copy(p::transform(
parenthesizedList(fieldAssignment, errorReporter),
[this](Located<kj::Array<kj::Maybe<Orphan<ValueExpression::FieldAssignment>>>>&& value)
-> Orphan<ValueExpression> {
if (value.value.size() == 1) {
KJ_IF_MAYBE(firstVal, value.value[0]) {
if (!firstVal->get().hasFieldName()) {
// There is only one value and it isn't an assignment, therefore the value is
// not a struct.
return firstVal->get().disownValue();
}
} else {
// There is only one value and it failed to parse.
auto result = orphanage.newOrphan<ValueExpression>();
auto builder = result.get();
builder.getBody().setUnknown();
value.copyLocationTo(builder);
return result;
}
}
// If we get here, the parentheses appear to contain a list of field assignments, meaning
// the value is a struct.
auto result = orphanage.newOrphan<ValueExpression>();
auto builder = result.get();
value.copyLocationTo(builder);
auto structBuilder = builder.getBody().initStructValue(value.value.size());
for (uint i = 0; i < value.value.size(); i++) {
KJ_IF_MAYBE(field, value.value[i]) {
if (field->get().hasFieldName()) {
structBuilder.adoptWithCaveats(i, kj::mv(*field));
} else {
auto fieldValue = field->get().getValue();
errorReporter.addError(fieldValue.getStartByte(), fieldValue.getEndByte(),
"Missing field name.");
}
}
}
return result;
}));
parsers.valueExpression = arena.copy(p::oneOf(
p::transform(integerLiteral,
[this](Located<uint64_t>&& value) -> Orphan<ValueExpression> {
auto result = orphanage.newOrphan<ValueExpression>();
auto builder = result.get();
builder.getBody().setPositiveInt(value.value);
value.copyLocationTo(builder);
return result;
}),
p::transform(p::sequence(op("-"), integerLiteral),
[this](Located<uint64_t>&& value) -> Orphan<ValueExpression> {
auto result = orphanage.newOrphan<ValueExpression>();
auto builder = result.get();
builder.getBody().setNegativeInt(value.value);
value.copyLocationTo(builder);
return result;
}),
p::transform(floatLiteral,
[this](Located<double>&& value) -> Orphan<ValueExpression> {
auto result = orphanage.newOrphan<ValueExpression>();
auto builder = result.get();
builder.getBody().setFloat(value.value);
value.copyLocationTo(builder);
return result;
}),
p::transform(p::sequence(op("-"), floatLiteral),
[this](Located<double>&& value) -> Orphan<ValueExpression> {
auto result = orphanage.newOrphan<ValueExpression>();
auto builder = result.get();
builder.getBody().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<ValueExpression> {
auto result = orphanage.newOrphan<ValueExpression>();
auto builder = result.get();
builder.getBody().setFloat(-std::numeric_limits<double>::infinity());
initLocation(location, builder);
return result;
}),
p::transform(stringLiteral,
[this](Located<Text::Reader>&& value) -> Orphan<ValueExpression> {
auto result = orphanage.newOrphan<ValueExpression>();
auto builder = result.get();
builder.getBody().setString(value.value);
value.copyLocationTo(builder);
return result;
}),
p::transform(p::sequence(identifier, parsers.parenthesizedValueExpression),
[this](Located<Text::Reader>&& fieldName, Orphan<ValueExpression>&& value)
-> Orphan<ValueExpression> {
auto result = orphanage.newOrphan<ValueExpression>();
auto builder = result.get();
builder.setStartByte(fieldName.startByte);
builder.setEndByte(value.get().getEndByte());
auto unionBuilder = builder.getBody().initUnionValue();
fieldName.copyTo(unionBuilder.initFieldName());
unionBuilder.adoptValue(kj::mv(value));
return result;
}),
p::transformWithLocation(parsers.declName,
[this](kj::parse::Span<List<Token>::Reader::Iterator> location,
Orphan<DeclName>&& value) -> Orphan<ValueExpression> {
auto result = orphanage.newOrphan<ValueExpression>();
auto builder = result.get();
builder.getBody().adoptName(kj::mv(value));
initLocation(location, builder);
return result;
}),
p::transform(bracketedList(parsers.valueExpression, errorReporter),
[this](Located<kj::Array<kj::Maybe<Orphan<ValueExpression>>>>&& value)
-> Orphan<ValueExpression> {
auto result = orphanage.newOrphan<ValueExpression>();
auto builder = result.get();
auto listBuilder = builder.getBody().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(parenthesizedList(fieldAssignment, errorReporter),
[this](Located<kj::Array<kj::Maybe<Orphan<ValueExpression::FieldAssignment>>>>&& value)
-> Orphan<ValueExpression> {
auto result = orphanage.newOrphan<ValueExpression>();
auto builder = result.get();
auto structBuilder = builder.getBody().initStructValue(value.value.size());
for (uint i = 0; i < value.value.size(); i++) {
KJ_IF_MAYBE(field, value.value[i]) {
if (field->get().hasFieldName()) {
structBuilder.adoptWithCaveats(i, kj::mv(*field));
} else {
auto fieldValue = field->get().getValue();
errorReporter.addError(fieldValue.getStartByte(), fieldValue.getEndByte(),
"Missing field name.");
}
}
}
value.copyLocationTo(builder);
return result;
})
));
parsers.annotation = arena.copy(p::transform(
p::sequence(op("$"), parsers.declName, p::optional(parsers.parenthesizedValueExpression)),
[this](Orphan<DeclName>&& name, kj::Maybe<Orphan<ValueExpression>>&& value)
-> Orphan<Declaration::AnnotationApplication> {
auto result = orphanage.newOrphan<Declaration::AnnotationApplication>();
auto builder = result.get();
builder.adoptName(kj::mv(name));
KJ_IF_MAYBE(v, value) {
builder.getValue().adoptExpression(kj::mv(*v));
} else {
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"), identifier, op("="), parsers.declName),
[this](Located<Text::Reader>&& name, Orphan<DeclName>&& target) -> DeclParserResult {
auto decl = orphanage.newOrphan<Declaration>();
auto builder = decl.get();
name.copyTo(builder.initName());
// no id, no annotations for using decl
builder.getBody().initUsingDecl().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.typeExpression,
op("="), parsers.valueExpression,
p::many(parsers.annotation)),
[this](Located<Text::Reader>&& name, kj::Maybe<Orphan<LocatedInteger>>&& id,
Orphan<TypeExpression>&& type, Orphan<ValueExpression>&& value,
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(annotations))
.getBody().initConstDecl();
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), kj::mv(annotations))
.getBody().initEnumDecl();
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))
.getBody().initEnumerantDecl();
return DeclParserResult(kj::mv(decl));
}));
parsers.structDecl = arena.copy(p::transform(
p::sequence(keyword("struct"), 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), kj::mv(annotations))
.getBody().initStructDecl();
return DeclParserResult(kj::mv(decl), parsers.structLevelDecl);
}));
parsers.fieldDecl = arena.copy(p::transform(
p::sequence(identifier, parsers.ordinal, op(":"), parsers.typeExpression,
p::optional(p::sequence(op("="), parsers.valueExpression)),
p::many(parsers.annotation)),
[this](Located<Text::Reader>&& name, Orphan<LocatedInteger>&& ordinal,
Orphan<TypeExpression>&& type, kj::Maybe<Orphan<ValueExpression>>&& 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))
.getBody().initFieldDecl();
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));
}));
parsers.unionDecl = arena.copy(p::transform(
p::sequence(p::optional(identifier), p::optional(parsers.ordinal), keyword("union"),
p::many(parsers.annotation)),
[this](kj::Maybe<Located<Text::Reader>>&& name,
kj::Maybe<Orphan<LocatedInteger>>&& ordinal,
kj::Array<Orphan<Declaration::AnnotationApplication>>&& annotations)
-> DeclParserResult {
auto decl = orphanage.newOrphan<Declaration>();
auto builder = decl.get();
KJ_IF_MAYBE(n, name) {
n->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.getBody().initGroupDecl();
return DeclParserResult(kj::mv(decl), parsers.structLevelDecl);
}));
parsers.groupDecl = arena.copy(p::transform(
p::sequence(keyword("group"), p::many(parsers.annotation)),
[this](kj::Array<Orphan<Declaration::AnnotationApplication>>&& annotations)
-> DeclParserResult {
auto decl = orphanage.newOrphan<Declaration>();
auto builder = decl.get();
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.getBody().initGroupDecl();
return DeclParserResult(kj::mv(decl), parsers.structLevelDecl);
}));
parsers.interfaceDecl = arena.copy(p::transform(
p::sequence(keyword("interface"), 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), kj::mv(annotations))
.getBody().initInterfaceDecl();
return DeclParserResult(kj::mv(decl), parsers.interfaceLevelDecl);
}));
parsers.param = arena.copy(p::transform(
p::sequence(identifier, op(":"), parsers.typeExpression,
p::optional(p::sequence(op("="), parsers.valueExpression)),
p::many(parsers.annotation)),
[this](Located<Text::Reader>&& name, Orphan<TypeExpression>&& type,
kj::Maybe<Orphan<ValueExpression>>&& defaultValue,
kj::Array<Orphan<Declaration::AnnotationApplication>>&& annotations)
-> Orphan<Declaration::Method::Param> {
auto result = orphanage.newOrphan<Declaration::Method::Param>();
auto builder = result.get();
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);
}));
parsers.methodDecl = arena.copy(p::transform(
p::sequence(identifier, parsers.ordinal,
parenthesizedList(parsers.param, errorReporter),
p::optional(p::sequence(op(":"), parsers.typeExpression)),
p::many(parsers.annotation)),
[this](Located<Text::Reader>&& name, Orphan<LocatedInteger>&& ordinal,
Located<kj::Array<kj::Maybe<Orphan<Declaration::Method::Param>>>>&& params,
kj::Maybe<Orphan<TypeExpression>>&& returnType,
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))
.getBody().initMethodDecl();
auto paramsBuilder = builder.initParams(params.value.size());
for (uint i = 0; i < params.value.size(); i++) {
KJ_IF_MAYBE(param, params.value[i]) {
paramsBuilder.adoptWithCaveats(i, kj::mv(*param));
}
}
KJ_IF_MAYBE(t, returnType) {
builder.getReturnType().adoptExpression(kj::mv(*t));
} else {
builder.getReturnType().setNone();
}
return DeclParserResult(kj::mv(decl));
}));
auto& annotationTarget = arena.copy(p::oneOf(
identifier,
p::transformWithLocation(op("*"),
[this](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.typeExpression,
p::many(parsers.annotation)),
[this](Located<Text::Reader>&& name, kj::Maybe<Orphan<LocatedInteger>>&& id,
Located<kj::Array<kj::Maybe<Located<Text::Reader>>>>&& targets,
Orphan<TypeExpression>&& type,
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(annotations))
.getBody().initAnnotationDecl();
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 member: dynamicBuilder.getSchema().getMembers()) {
if (member.getProto().getName().startsWith("targets")) {
dynamicBuilder.set(member, 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(member, dynamicBuilder.getSchema().findMemberByName(buffer)) {
if (dynamicBuilder.get(*member).as<bool>()) {
errorReporter.addError(target->startByte, target->endByte,
"Duplicate target specification.");
}
dynamicBuilder.set(*member, 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().getBody().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().getBody().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.fieldDecl, parsers.unionDecl, parsers.groupDecl, parsers.genericDecl));
parsers.interfaceLevelDecl = arena.copy(p::oneOf(
parsers.methodDecl, parsers.genericDecl));
}
CapnpParser::~CapnpParser() {}
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.getBlock().which()) {
case Statement::Block::NONE:
if (output->memberParser != nullptr) {
errorReporter.addError(statement.getStartByte(), statement.getEndByte(),
"This statement should end with a semicolon, not a block.");
}
break;
case Statement::Block::STATEMENTS:
KJ_IF_MAYBE(memberParser, output->memberParser) {
auto memberStatements = statement.getBlock().getStatements();
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 block, not a semicolon.");
}
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 = 0;
}
errorReporter.addError(bestByte, bestByte, "Parse error.");
return nullptr;
}
}
} // namespace compiler
} // namespace capnp