capnpc-capnp.c++ 23.7 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
// Copyright (c) 2013, Kenton Varda <temporal@gmail.com>
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this
//    list of conditions and the following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright notice,
//    this list of conditions and the following disclaimer in the documentation
//    and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

// This program is a code generator plugin for capnpc which writes the schema back to stdout in
// roughly capnpc format.

#include "../schema.capnp.h"
#include "../serialize.h"
Kenton Varda's avatar
Kenton Varda committed
29
#include <kj/debug.h>
30
#include <kj/io.h>
31 32
#include "../schema-loader.h"
#include "../dynamic.h"
33 34 35 36
#include <unistd.h>
#include <unordered_map>
#include <vector>

37
namespace capnp {
38 39 40 41 42 43
namespace {

class TextBlob {
public:
  TextBlob() = default;
  template <typename... Params>
44
  explicit TextBlob(Params&&... params);
45
  TextBlob(kj::Array<TextBlob>&& params);
46

47
  void writeTo(kj::OutputStream& out) const;
48 49

private:
50
  kj::Array<char> text;
51
  struct Branch;
52
  kj::Array<Branch> branches;
53 54 55 56 57 58 59 60 61 62 63 64 65 66

  void allocate(size_t textSize, size_t branchCount);
  template <typename First, typename... Rest>
  void allocate(size_t textSize, size_t branchCount, const First& first, Rest&&... rest);
  template <typename... Rest>
  void allocate(size_t textSize, size_t branchCount, const TextBlob& first, Rest&&... rest);

  void fill(char* textPos, Branch* branchesPos);
  template <typename First, typename... Rest>
  void fill(char* textPos, Branch* branchesPos, First&& first, Rest&&... rest);
  template <typename... Rest>
  void fill(char* textPos, Branch* branchesPos, TextBlob&& first, Rest&&... rest);

  template <typename T>
67 68
  auto toContainer(T&& t) -> decltype(kj::toCharSequence(kj::fwd<T>(t))) {
    return kj::toCharSequence(kj::fwd<T>(t));
69 70
  }
  TextBlob&& toContainer(TextBlob&& t) {
Kenton Varda's avatar
Kenton Varda committed
71
    return kj::mv(t);
72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90
  }
  TextBlob& toContainer(TextBlob& t) {
    return t;
  }
  const TextBlob& toContainer(const TextBlob& t) {
    return t;
  }

  template <typename... Params>
  void init(Params&&... params);
};

struct TextBlob::Branch {
  char* pos;
  TextBlob content;
};

template <typename... Params>
TextBlob::TextBlob(Params&&... params) {
Kenton Varda's avatar
Kenton Varda committed
91
  init(toContainer(kj::fwd<Params>(params))...);
92 93
}

94
TextBlob::TextBlob(kj::Array<TextBlob>&& params) {
Kenton Varda's avatar
Kenton Varda committed
95
  branches = kj::heapArray<Branch>(params.size());
96 97
  for (size_t i = 0; i < params.size(); i++) {
    branches[i].pos = nullptr;
Kenton Varda's avatar
Kenton Varda committed
98
    branches[i].content = kj::mv(params[i]);
99 100 101
  }
}

102
void TextBlob::writeTo(kj::OutputStream& out) const {
103 104 105 106 107 108 109 110 111 112
  const char* pos = text.begin();
  for (auto& branch: branches) {
    out.write(pos, branch.pos - pos);
    pos = branch.pos;
    branch.content.writeTo(out);
  }
  out.write(pos, text.end() - pos);
}

void TextBlob::allocate(size_t textSize, size_t branchCount) {
Kenton Varda's avatar
Kenton Varda committed
113 114
  text = kj::heapArray<char>(textSize);
  branches = kj::heapArray<Branch>(branchCount);
115 116 117 118
}

template <typename First, typename... Rest>
void TextBlob::allocate(size_t textSize, size_t branchCount, const First& first, Rest&&... rest) {
Kenton Varda's avatar
Kenton Varda committed
119
  allocate(textSize + first.size(), branchCount, kj::fwd<Rest>(rest)...);
120 121 122 123 124
}

template <typename... Rest>
void TextBlob::allocate(size_t textSize, size_t branchCount,
                        const TextBlob& first, Rest&&... rest) {
Kenton Varda's avatar
Kenton Varda committed
125
  allocate(textSize, branchCount + 1, kj::fwd<Rest>(rest)...);
126 127 128
}

void TextBlob::fill(char* textPos, Branch* branchesPos) {
129 130
  KJ_ASSERT(textPos == text.end(), textPos - text.end());
  KJ_ASSERT(branchesPos == branches.end(), branchesPos - branches.end());
131 132 133 134
}

template <typename First, typename... Rest>
void TextBlob::fill(char* textPos, Branch* branchesPos, First&& first, Rest&&... rest) {
135
  textPos = kj::_::fill(textPos, kj::fwd<First>(first));
Kenton Varda's avatar
Kenton Varda committed
136
  fill(textPos, branchesPos, kj::fwd<Rest>(rest)...);
137 138 139 140 141
}

template <typename... Rest>
void TextBlob::fill(char* textPos, Branch* branchesPos, TextBlob&& first, Rest&&... rest) {
  branchesPos->pos = textPos;
Kenton Varda's avatar
Kenton Varda committed
142
  branchesPos->content = kj::mv(first);
143
  ++branchesPos;
Kenton Varda's avatar
Kenton Varda committed
144
  fill(textPos, branchesPos, kj::fwd<Rest>(rest)...);
145 146 147 148 149
}

template <typename... Params>
void TextBlob::init(Params&&... params) {
  allocate(0, 0, params...);
Kenton Varda's avatar
Kenton Varda committed
150
  fill(text.begin(), branches.begin(), kj::fwd<Params>(params)...);
151 152 153 154
}

template <typename... Params>
TextBlob text(Params&&... params) {
Kenton Varda's avatar
Kenton Varda committed
155
  return TextBlob(kj::fwd<Params>(params)...);
156 157 158 159
}

template <typename List, typename Func>
TextBlob forText(List&& list, Func&& func) {
Kenton Varda's avatar
Kenton Varda committed
160
  kj::Array<TextBlob> items = kj::heapArray<TextBlob>(list.size());
161 162 163
  for (size_t i = 0; i < list.size(); i++) {
    items[i] = func(list[i]);
  }
Kenton Varda's avatar
Kenton Varda committed
164
  return TextBlob(kj::mv(items));
165 166 167 168 169
}

template <typename T>
struct ForTextHack {
  T list;
Kenton Varda's avatar
Kenton Varda committed
170
  ForTextHack(T list): list(kj::fwd<T>(list)) {}
171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205
  template <typename Func>
  TextBlob operator*(Func&& func) {
    return forText(list, func);
  }
};

#define FOR_EACH(list, name) ForTextHack<decltype(list)>(list) * \
    [&](decltype((list)[0]) name) -> TextBlob

struct Indent {
  uint amount;
  Indent() = default;
  inline Indent(int amount): amount(amount) {}

  Indent next() {
    return Indent(amount + 2);
  }

  struct Iterator {
    uint i;
    Iterator() = default;
    inline Iterator(uint i): i(i) {}
    inline char operator*() const { return ' '; }
    inline Iterator& operator++() { ++i; return *this; }
    inline Iterator operator++(int) { Iterator result = *this; ++i; return result; }
    inline bool operator==(const Iterator& other) const { return i == other.i; }
    inline bool operator!=(const Iterator& other) const { return i != other.i; }
  };

  inline size_t size() const { return amount; }

  inline Iterator begin() const { return Iterator(0); }
  inline Iterator end() const { return Iterator(amount); }
};

206 207 208 209
inline Indent KJ_STRINGIFY(const Indent& indent) {
  return indent;
}

210 211
// =======================================================================================

212
SchemaLoader schemaLoader;
213

214 215 216 217 218
Text::Reader getUnqualifiedName(Schema schema) {
  auto proto = schema.getProto();
  auto parent = schemaLoader.get(proto.getScopeId());
  for (auto nested: parent.getProto().getNestedNodes()) {
    if (nested.getId() == proto.getId()) {
219 220 221
      return nested.getName();
    }
  }
222
  KJ_FAIL_REQUIRE("A schema Node's supposed scope did not contain the node as a NestedNode.");
223 224 225
  return "(?)";
}

226 227 228
TextBlob nodeName(Schema target, Schema scope) {
  std::vector<Schema> targetParents;
  std::vector<Schema> scopeParts;
229 230

  {
231 232 233
    Schema parent = target;
    while (parent.getProto().getScopeId() != 0) {
      parent = schemaLoader.get(parent.getProto().getScopeId());
234 235 236 237 238
      targetParents.push_back(parent);
    }
  }

  {
239 240 241 242
    Schema parent = scope;
    scopeParts.push_back(parent);
    while (parent.getProto().getScopeId() != 0) {
      parent = schemaLoader.get(parent.getProto().getScopeId());
243 244 245 246 247 248
      scopeParts.push_back(parent);
    }
  }

  // Remove common scope.
  while (!scopeParts.empty() && !targetParents.empty() &&
249
         scopeParts.back() == targetParents.back()) {
250 251 252 253
    scopeParts.pop_back();
    targetParents.pop_back();
  }

254 255
  // TODO(someday):  This is broken in that we aren't checking for shadowing.

256 257 258
  TextBlob path = text();
  while (!targetParents.empty()) {
    auto part = targetParents.back();
259 260
    auto proto = part.getProto();
    if (proto.getScopeId() == 0) {
Kenton Varda's avatar
Kenton Varda committed
261
      path = text(kj::mv(path), "import \"", proto.getDisplayName(), "\".");
262
    } else {
Kenton Varda's avatar
Kenton Varda committed
263
      path = text(kj::mv(path), getUnqualifiedName(part), ".");
264 265 266 267
    }
    targetParents.pop_back();
  }

Kenton Varda's avatar
Kenton Varda committed
268
  return text(kj::mv(path), getUnqualifiedName(target));
269 270
}

271
TextBlob genType(schema::Type::Reader type, Schema scope) {
272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289
  auto body = type.getBody();
  switch (body.which()) {
    case schema::Type::Body::VOID_TYPE: return text("Void");
    case schema::Type::Body::BOOL_TYPE: return text("Bool");
    case schema::Type::Body::INT8_TYPE: return text("Int8");
    case schema::Type::Body::INT16_TYPE: return text("Int16");
    case schema::Type::Body::INT32_TYPE: return text("Int32");
    case schema::Type::Body::INT64_TYPE: return text("Int64");
    case schema::Type::Body::UINT8_TYPE: return text("UInt8");
    case schema::Type::Body::UINT16_TYPE: return text("UInt16");
    case schema::Type::Body::UINT32_TYPE: return text("UInt32");
    case schema::Type::Body::UINT64_TYPE: return text("UInt64");
    case schema::Type::Body::FLOAT32_TYPE: return text("Float32");
    case schema::Type::Body::FLOAT64_TYPE: return text("Float64");
    case schema::Type::Body::TEXT_TYPE: return text("Text");
    case schema::Type::Body::DATA_TYPE: return text("Data");
    case schema::Type::Body::LIST_TYPE:
      return text("List(", genType(body.getListType(), scope), ")");
290 291 292 293
    case schema::Type::Body::ENUM_TYPE:
      return nodeName(scope.getDependency(body.getEnumType()), scope);
    case schema::Type::Body::STRUCT_TYPE:
      return nodeName(scope.getDependency(body.getStructType()), scope);
294
    case schema::Type::Body::INTERFACE_TYPE:
295
      return nodeName(scope.getDependency(body.getInterfaceType()), scope);
296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340
    case schema::Type::Body::OBJECT_TYPE: return text("Object");
  }
  return text();
}

int typeSizeBits(schema::Type::Reader type) {
  switch (type.getBody().which()) {
    case schema::Type::Body::VOID_TYPE: return 0;
    case schema::Type::Body::BOOL_TYPE: return 1;
    case schema::Type::Body::INT8_TYPE: return 8;
    case schema::Type::Body::INT16_TYPE: return 16;
    case schema::Type::Body::INT32_TYPE: return 32;
    case schema::Type::Body::INT64_TYPE: return 64;
    case schema::Type::Body::UINT8_TYPE: return 8;
    case schema::Type::Body::UINT16_TYPE: return 16;
    case schema::Type::Body::UINT32_TYPE: return 32;
    case schema::Type::Body::UINT64_TYPE: return 64;
    case schema::Type::Body::FLOAT32_TYPE: return 32;
    case schema::Type::Body::FLOAT64_TYPE: return 64;
    case schema::Type::Body::TEXT_TYPE: return -1;
    case schema::Type::Body::DATA_TYPE: return -1;
    case schema::Type::Body::LIST_TYPE: return -1;
    case schema::Type::Body::ENUM_TYPE: return 16;
    case schema::Type::Body::STRUCT_TYPE: return -1;
    case schema::Type::Body::INTERFACE_TYPE: return -1;
    case schema::Type::Body::OBJECT_TYPE: return -1;
  }
  return 0;
}

bool isEmptyValue(schema::Value::Reader value) {
  auto body = value.getBody();
  switch (body.which()) {
    case schema::Value::Body::VOID_VALUE: return true;
    case schema::Value::Body::BOOL_VALUE: return body.getBoolValue() == false;
    case schema::Value::Body::INT8_VALUE: return body.getInt8Value() == 0;
    case schema::Value::Body::INT16_VALUE: return body.getInt16Value() == 0;
    case schema::Value::Body::INT32_VALUE: return body.getInt32Value() == 0;
    case schema::Value::Body::INT64_VALUE: return body.getInt64Value() == 0;
    case schema::Value::Body::UINT8_VALUE: return body.getUint8Value() == 0;
    case schema::Value::Body::UINT16_VALUE: return body.getUint16Value() == 0;
    case schema::Value::Body::UINT32_VALUE: return body.getUint32Value() == 0;
    case schema::Value::Body::UINT64_VALUE: return body.getUint64Value() == 0;
    case schema::Value::Body::FLOAT32_VALUE: return body.getFloat32Value() == 0;
    case schema::Value::Body::FLOAT64_VALUE: return body.getFloat64Value() == 0;
341 342 343
    case schema::Value::Body::TEXT_VALUE: return !body.hasTextValue();
    case schema::Value::Body::DATA_VALUE: return !body.hasDataValue();
    case schema::Value::Body::LIST_VALUE: return !body.hasListValue();
344
    case schema::Value::Body::ENUM_VALUE: return body.getEnumValue() == 0;
345
    case schema::Value::Body::STRUCT_VALUE: return !body.hasStructValue();
346 347 348 349 350 351
    case schema::Value::Body::INTERFACE_VALUE: return true;
    case schema::Value::Body::OBJECT_VALUE: return true;
  }
  return true;
}

352
TextBlob genValue(schema::Type::Reader type, schema::Value::Reader value, Schema scope) {
353 354 355 356 357 358 359 360 361 362 363 364 365 366
  auto body = value.getBody();
  switch (body.which()) {
    case schema::Value::Body::VOID_VALUE: return text("void");
    case schema::Value::Body::BOOL_VALUE: return text(body.getBoolValue() ? "true" : "false");
    case schema::Value::Body::INT8_VALUE: return text((int)body.getInt8Value());
    case schema::Value::Body::INT16_VALUE: return text(body.getInt16Value());
    case schema::Value::Body::INT32_VALUE: return text(body.getInt32Value());
    case schema::Value::Body::INT64_VALUE: return text(body.getInt64Value());
    case schema::Value::Body::UINT8_VALUE: return text((uint)body.getUint8Value());
    case schema::Value::Body::UINT16_VALUE: return text(body.getUint16Value());
    case schema::Value::Body::UINT32_VALUE: return text(body.getUint32Value());
    case schema::Value::Body::UINT64_VALUE: return text(body.getUint64Value());
    case schema::Value::Body::FLOAT32_VALUE: return text(body.getFloat32Value());
    case schema::Value::Body::FLOAT64_VALUE: return text(body.getFloat64Value());
Kenton Varda's avatar
Kenton Varda committed
367 368
    case schema::Value::Body::TEXT_VALUE: return text(DynamicValue::Reader(body.getTextValue()));
    case schema::Value::Body::DATA_VALUE: return text(DynamicValue::Reader(body.getDataValue()));
369
    case schema::Value::Body::LIST_VALUE: {
370
      KJ_REQUIRE(type.getBody().which() == schema::Type::Body::LIST_TYPE, "type/value mismatch");
371 372
      auto value = body.getListValue<DynamicList>(
          ListSchema::of(type.getBody().getListType(), scope));
Kenton Varda's avatar
Kenton Varda committed
373
      return text(value);
374 375
    }
    case schema::Value::Body::ENUM_VALUE: {
376
      KJ_REQUIRE(type.getBody().which() == schema::Type::Body::ENUM_TYPE, "type/value mismatch");
377
      auto enumNode = scope.getDependency(type.getBody().getEnumType()).asEnum().getProto();
378 379
      auto enumType = enumNode.getBody().getEnumNode();
      auto enumerants = enumType.getEnumerants();
380
      KJ_REQUIRE(body.getEnumValue() < enumerants.size(),
381 382 383 384
              "Enum value out-of-range.", body.getEnumValue(), enumNode.getDisplayName());
      return text(enumerants[body.getEnumValue()].getName());
    }
    case schema::Value::Body::STRUCT_VALUE: {
385
      KJ_REQUIRE(type.getBody().which() == schema::Type::Body::STRUCT_TYPE, "type/value mismatch");
386 387
      auto value = body.getStructValue<DynamicStruct>(
          scope.getDependency(type.getBody().getStructType()).asStruct());
Kenton Varda's avatar
Kenton Varda committed
388
      return text(value);
389 390 391 392 393 394 395 396
    }
    case schema::Value::Body::INTERFACE_VALUE: {
      return text("");
    }
    case schema::Value::Body::OBJECT_VALUE: {
      return text("");
    }
  }
397
  return text("");
398 399 400
}

TextBlob genAnnotation(schema::Annotation::Reader annotation,
401
                       Schema scope,
402
                       const char* prefix = " ", const char* suffix = "") {
403 404
  auto decl = schemaLoader.get(annotation.getId());
  auto body = decl.getProto().getBody();
405
  KJ_REQUIRE(body.which() == schema::Node::Body::ANNOTATION_NODE);
406 407 408
  auto annDecl = body.getAnnotationNode();

  return text(prefix, "$", nodeName(decl, scope), "(",
409
              genValue(annDecl.getType(), annotation.getValue(), scope), ")", suffix);
410 411
}

412
TextBlob genAnnotations(List<schema::Annotation>::Reader list, Schema scope) {
413 414
  return FOR_EACH(list, ann) { return genAnnotation(ann, scope); };
}
415 416 417
TextBlob genAnnotations(Schema schema) {
  auto proto = schema.getProto();
  return genAnnotations(proto.getAnnotations(), schemaLoader.get(proto.getScopeId()));
418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434
}

const char* elementSizeName(schema::ElementSize size) {
  switch (size) {
    case schema::ElementSize::EMPTY: return "void";
    case schema::ElementSize::BIT: return "1-bit";
    case schema::ElementSize::BYTE: return "8-bit";
    case schema::ElementSize::TWO_BYTES: return "16-bit";
    case schema::ElementSize::FOUR_BYTES: return "32-bit";
    case schema::ElementSize::EIGHT_BYTES: return "64-bit";
    case schema::ElementSize::POINTER: return "pointer";
    case schema::ElementSize::INLINE_COMPOSITE: return "inline composite";
  }
  return "";
}

TextBlob genStructMember(schema::StructNode::Member::Reader member,
435
                         Schema scope, Indent indent, int unionTag = -1) {
436 437 438 439 440 441 442
  switch (member.getBody().which()) {
    case schema::StructNode::Member::Body::FIELD_MEMBER: {
      auto field = member.getBody().getFieldMember();
      int size = typeSizeBits(field.getType());
      return text(indent, member.getName(), " @", member.getOrdinal(),
                  " :", genType(field.getType(), scope),
                  isEmptyValue(field.getDefaultValue()) ? text("") :
443
                      text(" = ", genValue(field.getType(), field.getDefaultValue(), scope)),
444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466
                  genAnnotations(member.getAnnotations(), scope),
                  ";  # ", size == -1 ? text("ptr[", field.getOffset(), "]")
                                      : text("bits[", field.getOffset() * size, ", ",
                                                     (field.getOffset() + 1) * size, ")"),
                  unionTag != -1 ? text(", union tag = ", unionTag) : text(),
                  "\n");
    }
    case schema::StructNode::Member::Body::UNION_MEMBER: {
      auto un = member.getBody().getUnionMember();
      int i = 0;
      return text(indent, member.getName(), " @", member.getOrdinal(),
                  " union", genAnnotations(member.getAnnotations(), scope),
                  " {  # tag bits[", un.getDiscriminantOffset(), ", ",
                  un.getDiscriminantOffset() + 16, ")\n",
                  FOR_EACH(un.getMembers(), member) {
                    return genStructMember(member, scope, indent.next(), i++);
                  },
                  indent, "}\n");
    }
  }
  return text();
}

467
TextBlob genNestedDecls(Schema schema, Indent indent);
468

469 470 471
TextBlob genDecl(Schema schema, Text::Reader name, uint64_t scopeId, Indent indent) {
  auto proto = schema.getProto();
  if (proto.getScopeId() != scopeId) {
472
    // This appears to be an alias for something declared elsewhere.
473
    KJ_FAIL_REQUIRE("Aliases not implemented.");
474 475
  }

476
  switch (proto.getBody().which()) {
477
    case schema::Node::Body::FILE_NODE:
478
      KJ_FAIL_REQUIRE("Encountered nested file node.");
479 480
      break;
    case schema::Node::Body::STRUCT_NODE: {
481
      auto body = proto.getBody().getStructNode();
482
      return text(
483
          indent, "struct ", name, " @0x", kj::hex(proto.getId()), genAnnotations(schema), " {  # ",
484 485 486 487 488 489 490
          body.getDataSectionWordSize() * 8, " bytes, ",
          body.getPointerSectionSize(), " ptrs",
          body.getPreferredListEncoding() == schema::ElementSize::INLINE_COMPOSITE
              ? text()
              : text(", packed as ", elementSizeName(body.getPreferredListEncoding())),
          "\n",
          FOR_EACH(body.getMembers(), member) {
491
            return genStructMember(member, schema, indent.next());
492
          },
493
          genNestedDecls(schema, indent.next()),
494 495 496
          indent, "}\n");
    }
    case schema::Node::Body::ENUM_NODE: {
497
      auto body = proto.getBody().getEnumNode();
498 499
      uint i = 0;
      return text(
500
          indent, "enum ", name, " @0x", kj::hex(proto.getId()), genAnnotations(schema), " {\n",
501 502
          FOR_EACH(body.getEnumerants(), enumerant) {
            return text(indent.next(), enumerant.getName(), " @", i++,
503
                        genAnnotations(enumerant.getAnnotations(), schema), ";\n");
504
          },
505
          genNestedDecls(schema, indent.next()),
506 507 508
          indent, "}\n");
    }
    case schema::Node::Body::INTERFACE_NODE: {
509
      auto body = proto.getBody().getInterfaceNode();
510 511
      uint i = 0;
      return text(
512 513
          indent, "interface ", name, " @0x", kj::hex(proto.getId()),
          genAnnotations(schema), " {\n",
514 515 516 517 518 519 520 521 522
          FOR_EACH(body.getMethods(), method) {
            int j = 0;
            return text(
                indent.next(), method.getName(), " @", i++, "(",
                FOR_EACH(method.getParams(), param) {
                  bool hasDefault = j >= method.getRequiredParamCount() ||
                      !isEmptyValue(param.getDefaultValue());
                  return text(
                      j++ > 0 ? ", " : "",
523
                      param.getName(), ": ", genType(param.getType(), schema),
524
                      hasDefault
525
                          ? text(" = ", genValue(param.getType(), param.getDefaultValue(), schema))
526
                          : text(),
527
                      genAnnotations(param.getAnnotations(), schema));
528
                },
529 530
                ") :", genType(method.getReturnType(), schema),
                genAnnotations(method.getAnnotations(), schema), ";\n");
531
          },
532
          genNestedDecls(schema, indent.next()),
533 534 535
          indent, "}\n");
    }
    case schema::Node::Body::CONST_NODE: {
536
      auto body = proto.getBody().getConstNode();
537
      return text(
538 539 540
          indent, "const ", name, " @0x", kj::hex(proto.getId()), " :",
          genType(body.getType(), schema), " = ",
          genValue(body.getType(), body.getValue(), schema), ";\n");
541 542
    }
    case schema::Node::Body::ANNOTATION_NODE: {
543
      auto body = proto.getBody().getAnnotationNode();
544
      kj::CappedArray<const char*, 11> targets;
545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563
      uint i = 0;
      if (body.getTargetsFile()) targets[i++] = "file";
      if (body.getTargetsConst()) targets[i++] = "const";
      if (body.getTargetsEnum()) targets[i++] = "enum";
      if (body.getTargetsEnumerant()) targets[i++] = "enumerant";
      if (body.getTargetsStruct()) targets[i++] = "struct";
      if (body.getTargetsField()) targets[i++] = "field";
      if (body.getTargetsUnion()) targets[i++] = "union";
      if (body.getTargetsInterface()) targets[i++] = "interface";
      if (body.getTargetsMethod()) targets[i++] = "method";
      if (body.getTargetsParam()) targets[i++] = "param";
      if (body.getTargetsAnnotation()) targets[i++] = "annotation";
      if (i == targets.size()) {
        targets[0] = "*";
        targets.setSize(1);
      } else {
        targets.setSize(i);
      }
      return text(
564
          indent, "annotation ", name, " @0x", kj::hex(proto.getId()),
565
          " (", strArray(targets, ", "), ") :",
566
          genType(body.getType(), schema), genAnnotations(schema), ";\n");
567 568 569 570 571 572
    }
  }

  return text();
}

573 574 575 576
TextBlob genNestedDecls(Schema schema, Indent indent) {
  uint64_t id = schema.getProto().getId();
  return FOR_EACH(schema.getProto().getNestedNodes(), nested) {
    return genDecl(schemaLoader.get(nested.getId()), nested.getName(), id, indent);
577 578 579
  };
}

580 581 582
TextBlob genFile(Schema file) {
  auto proto = file.getProto();
  auto body = proto.getBody();
583
  KJ_REQUIRE(body.which() == schema::Node::Body::FILE_NODE, "Expected a file node.",
Kenton Varda's avatar
Kenton Varda committed
584
          (uint)body.which());
585 586

  return text(
587
    "# ", proto.getDisplayName(), "\n",
588
    "@0x", kj::hex(proto.getId()), ";\n",
589
    FOR_EACH(proto.getAnnotations(), ann) { return genAnnotation(ann, file, "", ";\n"); },
590 591 592 593 594 595 596 597 598 599
    genNestedDecls(file, Indent(0)));
}

int main(int argc, char* argv[]) {
  ReaderOptions options;
  options.traversalLimitInWords = 1 << 30;  // Don't limit.
  StreamFdMessageReader reader(STDIN_FILENO, options);
  auto request = reader.getRoot<schema::CodeGeneratorRequest>();

  for (auto node: request.getNodes()) {
600
    schemaLoader.load(node);
601 602
  }

603 604
  kj::FdOutputStream rawOut(STDOUT_FILENO);
  kj::BufferedOutputStreamWrapper out(rawOut);
605 606

  for (auto fileId: request.getRequestedFiles()) {
607
    genFile(schemaLoader.get(fileId)).writeTo(out);
608 609 610 611 612 613
  }

  return 0;
}

}  // namespace
614
}  // namespace capnp
615 616

int main(int argc, char* argv[]) {
617
  return capnp::main(argc, argv);
618
}