Commit 359ea45a authored by Kenton Varda's avatar Kenton Varda

Continuing work on capnp C++ parser.

parent cc8d8fc7
...@@ -22,33 +22,54 @@ ...@@ -22,33 +22,54 @@
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "lexer.h" #include "lexer.h"
#include "parser.h"
#include <kj/vector.h> #include <kj/vector.h>
#include <kj/io.h> #include <kj/io.h>
#include <unistd.h> #include <unistd.h>
#include <kj/debug.h> #include <kj/debug.h>
#include "../message.h" #include "../message.h"
#include <iostream>
class CoutErrorReporter: public capnp::compiler::ErrorReporter {
public:
void addError(uint32_t startByte, uint32_t endByte, kj::String message) override {
std::cout << "input:" << startByte << "-" << endByte << ": " << message.cStr() << std::endl;
}
};
int main(int argc, char* argv[]) { int main(int argc, char* argv[]) {
// Eventually this will be capnpc. For now it's just a dummy program that tests parsing. // Eventually this will be capnpc. For now it's just a dummy program that tests parsing.
kj::Vector<char> input; // kj::Vector<char> input;
char buffer[4096]; // char buffer[4096];
for (;;) { // for (;;) {
ssize_t n; // ssize_t n;
KJ_SYSCALL(n = read(STDIN_FILENO, buffer, sizeof(buffer))); // KJ_SYSCALL(n = read(STDIN_FILENO, buffer, sizeof(buffer)));
if (n == 0) { // if (n == 0) {
break; // break;
} // }
input.addAll(buffer, buffer + n); // input.addAll(buffer, buffer + n);
} // }
//
// KJ_DBG(input);
// This input triggers a data corruption bug. Fix it before doing anything else!
kj::StringPtr input = "@0xfa974d18d718428e; const x :Int32 = 1;";
CoutErrorReporter errorReporter;
KJ_DBG(input); capnp::MallocMessageBuilder lexerArena;
auto lexedFile = lexerArena.initRoot<capnp::compiler::LexedStatements>();
capnp::compiler::lex(input, lexedFile, errorReporter);
KJ_DBG(lexedFile);
capnp::MallocMessageBuilder message; capnp::MallocMessageBuilder parserArena;
auto file = message.initRoot<capnp::compiler::LexedStatements>(); auto parsedFile = parserArena.initRoot<capnp::compiler::ParsedFile>();
capnp::compiler::lex(input, file); capnp::compiler::parseFile(lexedFile.getStatements(), parsedFile, errorReporter);
KJ_DBG(file); capnp::MallocMessageBuilder parserArena2;
parserArena2.setRoot(parsedFile.asReader());
//KJ_DBG(parsedFile);
return 0; return 0;
} }
// Copyright (c) 2013, Kenton Varda <temporal@gmail.com>
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this
// list of conditions and the following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "error-reporter.h"
#include <unistd.h>
namespace capnp {
namespace compiler {
ErrorReporter::~ErrorReporter() noexcept(false) {}
} // namespace compiler
} // namespace capnp
// Copyright (c) 2013, Kenton Varda <temporal@gmail.com>
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this
// list of conditions and the following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef ERROR_REPORTER_H_
#define ERROR_REPORTER_H_
#include "../common.h"
#include <kj/string.h>
namespace capnp {
namespace compiler {
class ErrorReporter {
public:
virtual ~ErrorReporter() noexcept(false);
virtual void addError(uint32_t startByte, uint32_t endByte, kj::String message) = 0;
// Report an error at the given location in the input text. `startByte` and `endByte` indicate
// the span of text that is erroneous. They may be equal, in which case the parser was only
// able to identify where the error begins, not where it ends.
};
} // namespace compiler
} // namespace capnp
#endif // ERROR_REPORTER_H_
...@@ -27,6 +27,8 @@ using Cxx = import "/capnp/c++.capnp"; ...@@ -27,6 +27,8 @@ using Cxx = import "/capnp/c++.capnp";
$Cxx.namespace("capnp::compiler"); $Cxx.namespace("capnp::compiler");
# TODO(someday): Here's a case where parameterized types might be nice, but note that it would
# need to support primitive parameters...
struct LocatedText { struct LocatedText {
value @0 :Text; value @0 :Text;
startByte @1 :UInt32; startByte @1 :UInt32;
...@@ -169,6 +171,8 @@ struct Declaration { ...@@ -169,6 +171,8 @@ struct Declaration {
struct Union {} struct Union {}
struct Group {}
struct Interface {} struct Interface {}
struct Method { struct Method {
......
...@@ -29,6 +29,13 @@ namespace capnp { ...@@ -29,6 +29,13 @@ namespace capnp {
namespace compiler { namespace compiler {
namespace { namespace {
class TestFailingErrorReporter: public ErrorReporter {
public:
void addError(uint32_t startByte, uint32_t endByte, kj::String message) override {
ADD_FAILURE() << "Parse failed: (" << startByte << "-" << endByte << ") " << message.cStr();
}
};
template <typename LexResult> template <typename LexResult>
kj::String doLex(kj::StringPtr constText) { kj::String doLex(kj::StringPtr constText) {
// Parse the given string into the given Cap'n Proto struct type using lex(), then stringify the // Parse the given string into the given Cap'n Proto struct type using lex(), then stringify the
...@@ -47,7 +54,8 @@ kj::String doLex(kj::StringPtr constText) { ...@@ -47,7 +54,8 @@ kj::String doLex(kj::StringPtr constText) {
} }
MallocMessageBuilder message; MallocMessageBuilder message;
auto file = message.initRoot<LexResult>(); auto file = message.initRoot<LexResult>();
EXPECT_TRUE(lex(text, file)); TestFailingErrorReporter errorReporter;
EXPECT_TRUE(lex(text, file, errorReporter));
kj::String result = kj::str(file); kj::String result = kj::str(file);
for (char& c: result) { for (char& c: result) {
// Make it easier to write golden strings below. // Make it easier to write golden strings below.
......
...@@ -28,16 +28,16 @@ ...@@ -28,16 +28,16 @@
namespace capnp { namespace capnp {
namespace compiler { namespace compiler {
bool lex(kj::ArrayPtr<const char> input, LexedStatements::Builder result) { namespace p = kj::parse;
Lexer lexer(Orphanage::getForMessageContaining(result));
Lexer::ParserInput parserInput(input.begin(), input.end()); bool lex(kj::ArrayPtr<const char> input, LexedStatements::Builder result,
kj::Maybe<kj::Array<Orphan<Statement>>> parseOutput = ErrorReporter& errorReporter) {
lexer.getParsers().statementSequence(parserInput); Lexer lexer(Orphanage::getForMessageContaining(result), errorReporter);
if (!parserInput.atEnd()) { auto parser = p::sequence(lexer.getParsers().statementSequence, p::endOfInput);
return false;
} Lexer::ParserInput parserInput(input.begin(), input.end());
kj::Maybe<kj::Array<Orphan<Statement>>> parseOutput = parser(parserInput);
KJ_IF_MAYBE(output, parseOutput) { KJ_IF_MAYBE(output, parseOutput) {
auto l = result.initStatements(output->size()); auto l = result.initStatements(output->size());
...@@ -46,20 +46,20 @@ bool lex(kj::ArrayPtr<const char> input, LexedStatements::Builder result) { ...@@ -46,20 +46,20 @@ bool lex(kj::ArrayPtr<const char> input, LexedStatements::Builder result) {
} }
return true; return true;
} else { } else {
uint32_t best = parserInput.getBest();
errorReporter.addError(best, best, kj::str("Parse error."));
return false; return false;
} }
} }
bool lex(kj::ArrayPtr<const char> input, LexedTokens::Builder result) { bool lex(kj::ArrayPtr<const char> input, LexedTokens::Builder result,
Lexer lexer(Orphanage::getForMessageContaining(result)); ErrorReporter& errorReporter) {
Lexer lexer(Orphanage::getForMessageContaining(result), errorReporter);
Lexer::ParserInput parserInput(input.begin(), input.end()); auto parser = p::sequence(lexer.getParsers().tokenSequence, p::endOfInput);
kj::Maybe<kj::Array<Orphan<Token>>> parseOutput =
lexer.getParsers().tokenSequence(parserInput);
if (!parserInput.atEnd()) { Lexer::ParserInput parserInput(input.begin(), input.end());
return false; kj::Maybe<kj::Array<Orphan<Token>>> parseOutput = parser(parserInput);
}
KJ_IF_MAYBE(output, parseOutput) { KJ_IF_MAYBE(output, parseOutput) {
auto l = result.initTokens(output->size()); auto l = result.initTokens(output->size());
...@@ -68,12 +68,12 @@ bool lex(kj::ArrayPtr<const char> input, LexedTokens::Builder result) { ...@@ -68,12 +68,12 @@ bool lex(kj::ArrayPtr<const char> input, LexedTokens::Builder result) {
} }
return true; return true;
} else { } else {
uint32_t best = parserInput.getBest();
errorReporter.addError(best, best, kj::str("Parse error."));
return false; return false;
} }
} }
namespace p = kj::parse;
namespace { namespace {
typedef p::Span<uint32_t> Location; typedef p::Span<uint32_t> Location;
...@@ -138,7 +138,8 @@ constexpr auto docComment = p::optional(p::sequence( ...@@ -138,7 +138,8 @@ constexpr auto docComment = p::optional(p::sequence(
} // namespace } // namespace
Lexer::Lexer(Orphanage orphanageParam): orphanage(orphanageParam) { Lexer::Lexer(Orphanage orphanageParam, ErrorReporter& errorReporterParam)
: orphanage(orphanageParam), errorReporter(errorReporterParam) {
// Note that because passing an lvalue to a parser constructor uses it by-referencee, it's safe // Note that because passing an lvalue to a parser constructor uses it by-referencee, it's safe
// for us to use parsers.tokenSequence even though we haven't yet constructed it. // for us to use parsers.tokenSequence even though we haven't yet constructed it.
......
...@@ -24,15 +24,18 @@ ...@@ -24,15 +24,18 @@
#ifndef CAPNP_COMPILER_LEXER_H_ #ifndef CAPNP_COMPILER_LEXER_H_
#define CAPNP_COMPILER_LEXER_H_ #define CAPNP_COMPILER_LEXER_H_
#include "lexer.capnp.h" #include <capnp/compiler/lexer.capnp.h>
#include <kj/parse/common.h> #include <kj/parse/common.h>
#include <kj/arena.h> #include <kj/arena.h>
#include "error-reporter.h"
namespace capnp { namespace capnp {
namespace compiler { namespace compiler {
bool lex(kj::ArrayPtr<const char> input, LexedStatements::Builder result); bool lex(kj::ArrayPtr<const char> input, LexedStatements::Builder result,
bool lex(kj::ArrayPtr<const char> input, LexedTokens::Builder result); ErrorReporter& errorReporter);
bool lex(kj::ArrayPtr<const char> input, LexedTokens::Builder result,
ErrorReporter& errorReporter);
// Lex the given source code, placing the results in `result`. Returns true if there // Lex the given source code, placing the results in `result`. Returns true if there
// were no errors, false if there were. Even when errors are present, the file may have partial // were no errors, false if there were. Even when errors are present, the file may have partial
// content which can be fed into later stages of parsing in order to find more errors. // content which can be fed into later stages of parsing in order to find more errors.
...@@ -46,7 +49,7 @@ class Lexer { ...@@ -46,7 +49,7 @@ class Lexer {
// into your own parsers. // into your own parsers.
public: public:
Lexer(Orphanage orphanage); Lexer(Orphanage orphanage, ErrorReporter& errorReporter);
// `orphanage` is used to allocate Cap'n Proto message objects in the result. `inputStart` is // `orphanage` is used to allocate Cap'n Proto message objects in the result. `inputStart` is
// a pointer to the beginning of the input, used to compute byte offsets. // a pointer to the beginning of the input, used to compute byte offsets.
...@@ -62,6 +65,9 @@ public: ...@@ -62,6 +65,9 @@ public:
explicit ParserInput(ParserInput& parent) explicit ParserInput(ParserInput& parent)
: IteratorInput<char, const char*>(parent), begin(parent.begin) {} : IteratorInput<char, const char*>(parent), begin(parent.begin) {}
inline uint32_t getBest() {
return IteratorInput<char, const char*>::getBest() - begin;
}
inline uint32_t getPosition() { inline uint32_t getPosition() {
return IteratorInput<char, const char*>::getPosition() - begin; return IteratorInput<char, const char*>::getPosition() - begin;
} }
...@@ -85,6 +91,7 @@ public: ...@@ -85,6 +91,7 @@ public:
private: private:
Orphanage orphanage; Orphanage orphanage;
ErrorReporter& errorReporter;
kj::Arena arena; kj::Arena arena;
Parsers parsers; Parsers parsers;
}; };
......
This diff is collapsed.
...@@ -24,21 +24,21 @@ ...@@ -24,21 +24,21 @@
#ifndef CAPNP_COMPILER_PARSER_H_ #ifndef CAPNP_COMPILER_PARSER_H_
#define CAPNP_COMPILER_PARSER_H_ #define CAPNP_COMPILER_PARSER_H_
#include "grammar.capnp.h" #include <capnp/compiler/grammar.capnp.h>
#include "lexer.capnp.h" #include <capnp/compiler/lexer.capnp.h>
#include <kj/parse/common.h> #include <kj/parse/common.h>
#include <kj/arena.h> #include <kj/arena.h>
#include "error-reporter.h"
namespace capnp { namespace capnp {
namespace compiler { namespace compiler {
bool parseFile(List<Statement>::Reader statements, ParsedFile::Builder result); void parseFile(List<Statement>::Reader statements, ParsedFile::Builder result,
ErrorReporter& errorReporter);
// Parse a list of statements to build a ParsedFile. // Parse a list of statements to build a ParsedFile.
//
class ErrorReporter { // If any errors are reported, then the output is not usable. However, it may be passed on through
public: // later stages of compilation in order to detect additional errors.
virtual void addError(uint32_t startByte, uint32_t endByte, kj::String message) = 0;
};
class CapnpParser { class CapnpParser {
// Advanced parser interface. This interface exposes the inner parsers so that you can embed // Advanced parser interface. This interface exposes the inner parsers so that you can embed
...@@ -51,13 +51,16 @@ public: ...@@ -51,13 +51,16 @@ public:
~CapnpParser(); ~CapnpParser();
KJ_DISALLOW_COPY(CapnpParser);
using ParserInput = kj::parse::IteratorInput<Token::Reader, List<Token>::Reader::Iterator>; using ParserInput = kj::parse::IteratorInput<Token::Reader, List<Token>::Reader::Iterator>;
struct DeclParserResult; struct DeclParserResult;
template <typename Output> template <typename Output>
using Parser = kj::parse::ParserRef<ParserInput, Output>; using Parser = kj::parse::ParserRef<ParserInput, Output>;
using DeclParser = Parser<DeclParserResult>; using DeclParser = Parser<DeclParserResult>;
Orphan<Declaration> parseStatement(Statement::Reader statement, const DeclParser& parser); kj::Maybe<Orphan<Declaration>> parseStatement(
Statement::Reader statement, const DeclParser& parser);
// Parse a statement using the given parser. In addition to parsing the token sequence itself, // Parse a statement using the given parser. In addition to parsing the token sequence itself,
// this takes care of parsing the block (if any) and copying over the doc comment (if any). // this takes care of parsing the block (if any) and copying over the doc comment (if any).
...@@ -99,6 +102,10 @@ public: ...@@ -99,6 +102,10 @@ public:
Parser<Orphan<DeclName>> declName; Parser<Orphan<DeclName>> declName;
Parser<Orphan<TypeExpression>> typeExpression; Parser<Orphan<TypeExpression>> typeExpression;
Parser<Orphan<ValueExpression>> valueExpression; Parser<Orphan<ValueExpression>> valueExpression;
Parser<Orphan<ValueExpression>> parenthesizedValueExpression;
Parser<Orphan<Declaration::AnnotationApplication>> annotation;
Parser<Orphan<LocatedInteger>> uid;
Parser<Orphan<LocatedInteger>> ordinal;
DeclParser usingDecl; DeclParser usingDecl;
DeclParser constDecl; DeclParser constDecl;
......
...@@ -138,8 +138,8 @@ struct OrphanGetImpl<T, Kind::STRUCT> { ...@@ -138,8 +138,8 @@ struct OrphanGetImpl<T, Kind::STRUCT> {
} }
}; };
template <typename T> template <typename T, Kind k>
struct OrphanGetImpl<List<T>, Kind::LIST> { struct OrphanGetImpl<List<T, k>, Kind::LIST> {
static inline typename List<T>::Builder apply(_::OrphanBuilder& builder) { static inline typename List<T>::Builder apply(_::OrphanBuilder& builder) {
return typename List<T>::Builder(builder.asList(_::ElementSizeForType<T>::value)); return typename List<T>::Builder(builder.asList(_::ElementSizeForType<T>::value));
} }
...@@ -147,7 +147,7 @@ struct OrphanGetImpl<List<T>, Kind::LIST> { ...@@ -147,7 +147,7 @@ struct OrphanGetImpl<List<T>, Kind::LIST> {
template <typename T> template <typename T>
struct OrphanGetImpl<List<T, Kind::STRUCT>, Kind::LIST> { struct OrphanGetImpl<List<T, Kind::STRUCT>, Kind::LIST> {
static inline typename T::Builder apply(_::OrphanBuilder& builder) { static inline typename List<T>::Builder apply(_::OrphanBuilder& builder) {
return typename List<T>::Builder(builder.asStructList(_::structSize<T>())); return typename List<T>::Builder(builder.asStructList(_::structSize<T>()));
} }
}; };
...@@ -207,7 +207,7 @@ struct Orphanage::NewOrphanListImpl<List<T, k>> { ...@@ -207,7 +207,7 @@ struct Orphanage::NewOrphanListImpl<List<T, k>> {
template <typename T> template <typename T>
struct Orphanage::NewOrphanListImpl<List<T, Kind::STRUCT>> { struct Orphanage::NewOrphanListImpl<List<T, Kind::STRUCT>> {
static inline _::OrphanBuilder apply(_::BuilderArena* arena, uint size) { static inline _::OrphanBuilder apply(_::BuilderArena* arena, uint size) {
return _::OrphanBuilder::initList(arena, size * ELEMENTS, _::structSize<T>()); return _::OrphanBuilder::initStructList(arena, size * ELEMENTS, _::structSize<T>());
} }
}; };
......
...@@ -164,6 +164,17 @@ TEST(Stringify, Unions) { ...@@ -164,6 +164,17 @@ TEST(Stringify, Unions) {
EXPECT_EQ("u3f0s64(123456789012345678)", kj::str(root.getUnion3())); EXPECT_EQ("u3f0s64(123456789012345678)", kj::str(root.getUnion3()));
} }
TEST(Stringify, StructUnions) {
MallocMessageBuilder builder;
auto root = builder.initRoot<test::TestStructUnion>();
auto allTypes = root.getUn().initAllTypes();
allTypes.setUInt32Field(12345);
allTypes.setTextField("foo");
EXPECT_EQ("(un = allTypes(uInt32Field = 12345, textField = \"foo\"))", kj::str(root));
}
TEST(Stringify, MoreValues) { TEST(Stringify, MoreValues) {
EXPECT_EQ("123", kj::str(DynamicValue::Reader(123))); EXPECT_EQ("123", kj::str(DynamicValue::Reader(123)));
EXPECT_EQ("1.23e47", kj::str(DynamicValue::Reader(123e45))); EXPECT_EQ("1.23e47", kj::str(DynamicValue::Reader(123e45)));
......
...@@ -34,7 +34,7 @@ namespace { ...@@ -34,7 +34,7 @@ namespace {
static const char HEXDIGITS[] = "0123456789abcdef"; static const char HEXDIGITS[] = "0123456789abcdef";
static void print(std::ostream& os, const DynamicValue::Reader& value, static void print(std::ostream& os, const DynamicValue::Reader& value,
schema::Type::Body::Which which) { schema::Type::Body::Which which, bool alreadyParenthesized = false) {
// Print an arbitrary message via the dynamic API by // Print an arbitrary message via the dynamic API by
// iterating over the schema. Look at the handling // iterating over the schema. Look at the handling
// of STRUCT in particular. // of STRUCT in particular.
...@@ -127,7 +127,7 @@ static void print(std::ostream& os, const DynamicValue::Reader& value, ...@@ -127,7 +127,7 @@ static void print(std::ostream& os, const DynamicValue::Reader& value,
break; break;
} }
case DynamicValue::STRUCT: { case DynamicValue::STRUCT: {
os << "("; if (!alreadyParenthesized) os << "(";
auto structValue = value.as<DynamicStruct>(); auto structValue = value.as<DynamicStruct>();
bool first = true; bool first = true;
for (auto member: structValue.getSchema().getMembers()) { for (auto member: structValue.getSchema().getMembers()) {
...@@ -151,7 +151,7 @@ static void print(std::ostream& os, const DynamicValue::Reader& value, ...@@ -151,7 +151,7 @@ static void print(std::ostream& os, const DynamicValue::Reader& value,
} }
} }
} }
os << ")"; if (!alreadyParenthesized) os << ")";
break; break;
} }
case DynamicValue::UNION: { case DynamicValue::UNION: {
...@@ -159,7 +159,8 @@ static void print(std::ostream& os, const DynamicValue::Reader& value, ...@@ -159,7 +159,8 @@ static void print(std::ostream& os, const DynamicValue::Reader& value,
KJ_IF_MAYBE(tag, unionValue.which()) { KJ_IF_MAYBE(tag, unionValue.which()) {
os << tag->getProto().getName().cStr() << "("; os << tag->getProto().getName().cStr() << "(";
print(os, unionValue.get(), print(os, unionValue.get(),
tag->getProto().getBody().getFieldMember().getType().getBody().which()); tag->getProto().getBody().getFieldMember().getType().getBody().which(),
true /* alreadyParenthesized */);
os << ")"; os << ")";
} else { } else {
// Unknown union member; must have come from newer // Unknown union member; must have come from newer
......
...@@ -376,3 +376,10 @@ struct TestNewVersion { ...@@ -376,3 +376,10 @@ struct TestNewVersion {
new1 @3 :Int64 = 987; new1 @3 :Int64 = 987;
new2 @4 :Text = "baz"; new2 @4 :Text = "baz";
} }
struct TestStructUnion {
un @0 union {
allTypes @1 :TestAllTypes;
object @2 :TestObject;
}
}
...@@ -227,6 +227,77 @@ TEST(CommonParsers, ManyParserCountOnly) { ...@@ -227,6 +227,77 @@ TEST(CommonParsers, ManyParserCountOnly) {
} }
} }
TEST(CommonParsers, TimesParser) {
StringPtr text = "foobar";
auto parser = sequence(exactly('f'), times(any, 4));
{
Input input(text.begin(), text.begin() + 4);
Maybe<Array<char>> result = parser(input);
EXPECT_TRUE(result == nullptr);
EXPECT_TRUE(input.atEnd());
}
{
Input input(text.begin(), text.begin() + 5);
Maybe<Array<char>> result = parser(input);
KJ_IF_MAYBE(s, result) {
EXPECT_EQ("ooba", heapString(*s));
} else {
ADD_FAILURE() << "Expected string, got null.";
}
EXPECT_TRUE(input.atEnd());
}
{
Input input(text.begin(), text.end());
Maybe<Array<char>> result = parser(input);
KJ_IF_MAYBE(s, result) {
EXPECT_EQ("ooba", heapString(*s));
} else {
ADD_FAILURE() << "Expected string, got null.";
}
EXPECT_FALSE(input.atEnd());
}
}
TEST(CommonParsers, TimesParserCountOnly) {
StringPtr text = "foooob";
auto parser = sequence(exactly('f'), times(exactly('o'), 4));
{
Input input(text.begin(), text.begin() + 4);
Maybe<Tuple<>> result = parser(input);
EXPECT_TRUE(result == nullptr);
EXPECT_TRUE(input.atEnd());
}
{
Input input(text.begin(), text.begin() + 5);
Maybe<Tuple<>> result = parser(input);
EXPECT_TRUE(result != nullptr);
EXPECT_TRUE(input.atEnd());
}
{
Input input(text.begin(), text.end());
Maybe<Tuple<>> result = parser(input);
EXPECT_TRUE(result != nullptr);
EXPECT_FALSE(input.atEnd());
}
text = "fooob";
{
Input input(text.begin(), text.end());
Maybe<Tuple<>> result = parser(input);
EXPECT_TRUE(result == nullptr);
EXPECT_FALSE(input.atEnd());
}
}
TEST(CommonParsers, ManyParserSubResult) { TEST(CommonParsers, ManyParserSubResult) {
StringPtr text = "foooob"; StringPtr text = "foooob";
......
...@@ -356,7 +356,7 @@ class Many_ { ...@@ -356,7 +356,7 @@ class Many_ {
struct Impl; struct Impl;
public: public:
explicit constexpr Many_(SubParser&& subParser) explicit constexpr Many_(SubParser&& subParser)
: subParser(kj::mv(subParser)) {} : subParser(kj::fwd<SubParser>(subParser)) {}
template <typename Input> template <typename Input>
auto operator()(Input& input) const auto operator()(Input& input) const
...@@ -395,6 +395,8 @@ struct Many_<SubParser, atLeastOne>::Impl { ...@@ -395,6 +395,8 @@ struct Many_<SubParser, atLeastOne>::Impl {
template <typename SubParser, bool atLeastOne> template <typename SubParser, bool atLeastOne>
template <typename Input> template <typename Input>
struct Many_<SubParser, atLeastOne>::Impl<Input, Tuple<>> { struct Many_<SubParser, atLeastOne>::Impl<Input, Tuple<>> {
// If the sub-parser output is Tuple<>, just return a count.
static Maybe<uint> apply(const SubParser& subParser, Input& input) { static Maybe<uint> apply(const SubParser& subParser, Input& input) {
uint count = 0; uint count = 0;
...@@ -437,6 +439,82 @@ constexpr Many_<SubParser, true> oneOrMore(SubParser&& subParser) { ...@@ -437,6 +439,82 @@ constexpr Many_<SubParser, true> oneOrMore(SubParser&& subParser) {
return Many_<SubParser, true>(kj::fwd<SubParser>(subParser)); return Many_<SubParser, true>(kj::fwd<SubParser>(subParser));
} }
// -------------------------------------------------------------------
// times()
// Output = Array of output of sub-parser, or Tuple<> if sub-parser returns Tuple<>.
template <typename SubParser>
class Times_ {
template <typename Input, typename Output = OutputType<SubParser, Input>>
struct Impl;
public:
explicit constexpr Times_(SubParser&& subParser, uint count)
: subParser(kj::fwd<SubParser>(subParser)), count(count) {}
template <typename Input>
auto operator()(Input& input) const
-> decltype(Impl<Input>::apply(instance<const SubParser&>(), instance<uint>(), input));
private:
SubParser subParser;
uint count;
};
template <typename SubParser>
template <typename Input, typename Output>
struct Times_<SubParser>::Impl {
static Maybe<Array<Output>> apply(const SubParser& subParser, uint count, Input& input) {
auto results = heapArrayBuilder<OutputType<SubParser, Input>>(count);
while (results.size() < count) {
if (input.atEnd()) {
return nullptr;
} else KJ_IF_MAYBE(subResult, subParser(input)) {
results.add(kj::mv(*subResult));
} else {
return nullptr;
}
}
return results.finish();
}
};
template <typename SubParser>
template <typename Input>
struct Times_<SubParser>::Impl<Input, Tuple<>> {
// If the sub-parser output is Tuple<>, just return a count.
static Maybe<Tuple<>> apply(const SubParser& subParser, uint count, Input& input) {
uint actualCount = 0;
while (actualCount < count) {
if (input.atEnd()) {
return nullptr;
} else KJ_IF_MAYBE(subResult, subParser(input)) {
++actualCount;
} else {
return nullptr;
}
}
return tuple();
}
};
template <typename SubParser>
template <typename Input>
auto Times_<SubParser>::operator()(Input& input) const
-> decltype(Impl<Input>::apply(instance<const SubParser&>(), instance<uint>(), input)) {
return Impl<Input, OutputType<SubParser, Input>>::apply(subParser, count, input);
}
template <typename SubParser>
constexpr Times_<SubParser> times(SubParser&& subParser, uint count) {
// Constructs a parser that repeats the subParser exactly `count` times.
return Times_<SubParser>(kj::fwd<SubParser>(subParser), count);
}
// ------------------------------------------------------------------- // -------------------------------------------------------------------
// optional() // optional()
// Output = Maybe<output of sub-parser> // Output = Maybe<output of sub-parser>
...@@ -445,7 +523,7 @@ template <typename SubParser> ...@@ -445,7 +523,7 @@ template <typename SubParser>
class Optional_ { class Optional_ {
public: public:
explicit constexpr Optional_(SubParser&& subParser) explicit constexpr Optional_(SubParser&& subParser)
: subParser(kj::mv(subParser)) {} : subParser(kj::fwd<SubParser>(subParser)) {}
template <typename Input> template <typename Input>
Maybe<Maybe<OutputType<SubParser, Input>>> operator()(Input& input) const { Maybe<Maybe<OutputType<SubParser, Input>>> operator()(Input& input) const {
...@@ -482,9 +560,8 @@ class OneOf_; ...@@ -482,9 +560,8 @@ class OneOf_;
template <typename FirstSubParser, typename... SubParsers> template <typename FirstSubParser, typename... SubParsers>
class OneOf_<FirstSubParser, SubParsers...> { class OneOf_<FirstSubParser, SubParsers...> {
public: public:
template <typename T, typename... U> explicit constexpr OneOf_(FirstSubParser&& firstSubParser, SubParsers&&... rest)
explicit constexpr OneOf_(T&& firstSubParser, U&&... rest) : first(kj::fwd<FirstSubParser>(firstSubParser)), rest(kj::fwd<SubParsers>(rest)...) {}
: first(kj::fwd<T>(firstSubParser)), rest(kj::fwd<U>(rest)...) {}
template <typename Input> template <typename Input>
Maybe<OutputType<FirstSubParser, Input>> operator()(Input& input) const { Maybe<OutputType<FirstSubParser, Input>> operator()(Input& input) const {
...@@ -653,7 +730,7 @@ template <typename SubParser, typename Condition> ...@@ -653,7 +730,7 @@ template <typename SubParser, typename Condition>
class AcceptIf_ { class AcceptIf_ {
public: public:
explicit constexpr AcceptIf_(SubParser&& subParser, Condition&& condition) explicit constexpr AcceptIf_(SubParser&& subParser, Condition&& condition)
: subParser(kj::mv(subParser)), condition(kj::mv(condition)) {} : subParser(kj::fwd<SubParser>(subParser)), condition(kj::fwd<Condition>(condition)) {}
template <typename Input> template <typename Input>
Maybe<OutputType<SubParser, Input>> operator()(Input& input) const { Maybe<OutputType<SubParser, Input>> operator()(Input& input) const {
...@@ -692,7 +769,8 @@ constexpr AcceptIf_<SubParser, Condition> acceptIf(SubParser&& subParser, Condit ...@@ -692,7 +769,8 @@ constexpr AcceptIf_<SubParser, Condition> acceptIf(SubParser&& subParser, Condit
template <typename SubParser> template <typename SubParser>
class NotLookingAt_ { class NotLookingAt_ {
public: public:
explicit constexpr NotLookingAt_(SubParser&& subParser): subParser(kj::mv(subParser)) {} explicit constexpr NotLookingAt_(SubParser&& subParser)
: subParser(kj::fwd<SubParser>(subParser)) {}
template <typename Input> template <typename Input>
Maybe<Tuple<>> operator()(Input& input) const { Maybe<Tuple<>> operator()(Input& input) const {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment