diff src/capnproto-0.6.0/c++/src/capnp/compiler/parser.c++ @ 62:0994c39f1e94

Cap'n Proto v0.6 + build for OSX
author Chris Cannam <cannam@all-day-breakfast.com>
date Mon, 22 May 2017 10:01:37 +0100
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/capnproto-0.6.0/c++/src/capnp/compiler/parser.c++	Mon May 22 10:01:37 2017 +0100
@@ -0,0 +1,1134 @@
+// 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.
+
+#include "parser.h"
+#include "md5.h"
+#include <capnp/dynamic.h>
+#include <kj/debug.h>
+#if !_MSC_VER
+#include <unistd.h>
+#endif
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#if _WIN32
+#include <windows.h>
+#undef VOID
+#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));
+
+  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);
+}
+
+uint64_t generateChildId(uint64_t parentId, kj::StringPtr childName) {
+  // Compute ID by MD5 hashing the concatenation of the parent ID and the declaration name, and
+  // then taking the first 8 bytes.
+
+  kj::byte parentIdBytes[sizeof(uint64_t)];
+  for (uint i = 0; i < sizeof(uint64_t); i++) {
+    parentIdBytes[i] = (parentId >> (i * 8)) & 0xff;
+  }
+
+  Md5 md5;
+  md5.update(kj::arrayPtr(parentIdBytes, kj::size(parentIdBytes)));
+  md5.update(childName);
+
+  kj::ArrayPtr<const kj::byte> resultBytes = md5.finish();
+
+  uint64_t result = 0;
+  for (uint i = 0; i < sizeof(uint64_t); i++) {
+    result = (result << 8) | resultBytes[i];
+  }
+
+  return result | (1ull << 63);
+}
+
+uint64_t generateGroupId(uint64_t parentId, uint16_t groupIndex) {
+  // Compute ID by MD5 hashing the concatenation of the parent ID and the group index, and
+  // then taking the first 8 bytes.
+
+  kj::byte bytes[sizeof(uint64_t) + sizeof(uint16_t)];
+  for (uint i = 0; i < sizeof(uint64_t); i++) {
+    bytes[i] = (parentId >> (i * 8)) & 0xff;
+  }
+  for (uint i = 0; i < sizeof(uint16_t); i++) {
+    bytes[sizeof(uint64_t) + i] = (groupIndex >> (i * 8)) & 0xff;
+  }
+
+  Md5 md5;
+  md5.update(bytes);
+
+  kj::ArrayPtr<const kj::byte> resultBytes = md5.finish();
+
+  uint64_t result = 0;
+  for (uint i = 0; i < sizeof(uint64_t); i++) {
+    result = (result << 8) | resultBytes[i];
+  }
+
+  return result | (1ull << 63);
+}
+
+uint64_t generateMethodParamsId(uint64_t parentId, uint16_t methodOrdinal, bool isResults) {
+  // Compute ID by MD5 hashing the concatenation of the parent ID, the method ordinal, and a
+  // boolean indicating whether this is the params or the results, and then taking the first 8
+  // bytes.
+
+  kj::byte bytes[sizeof(uint64_t) + sizeof(uint16_t) + 1];
+  for (uint i = 0; i < sizeof(uint64_t); i++) {
+    bytes[i] = (parentId >> (i * 8)) & 0xff;
+  }
+  for (uint i = 0; i < sizeof(uint16_t); i++) {
+    bytes[sizeof(uint64_t) + i] = (methodOrdinal >> (i * 8)) & 0xff;
+  }
+  bytes[sizeof(bytes) - 1] = isResults;
+
+  Md5 md5;
+  md5.update(bytes);
+
+  kj::ArrayPtr<const kj::byte> resultBytes = md5.finish();
+
+  uint64_t result = 0;
+  for (uint i = 0; i < sizeof(uint64_t); i++) {
+    result = (result << 8) | resultBytes[i];
+  }
+
+  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));
+}
+
+// =======================================================================================
+
+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(stringLiteral,
+                  [this](Located<Text::Reader>&& value) -> Orphan<Expression> {
+                    auto result = orphanage.newOrphan<Expression>();
+                    auto builder = result.get();
+                    builder.setString(value.value);
+                    value.copyLocationTo(builder);
+                    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(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;
+  }
+}
+
+}  // namespace compiler
+}  // namespace capnp