blob: 2ab3524ffce7b89c973a7001aa884bf3558d0bd7 [file] [log] [blame] [edit]
// Copyright 2020 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/developer/shell/parser/combinators.h"
#include <algorithm>
#include <map>
namespace shell::parser {
fit::function<ParseResultStream(ParseResultStream)> Seq(
fit::function<ParseResultStream(ParseResultStream)> a,
fit::function<ParseResultStream(ParseResultStream)> b) {
return [a = std::move(a), b = std::move(b)](ParseResultStream prefixes) mutable {
auto a_result = a(std::move(prefixes));
if (!a_result.ok()) {
// If `a` fails, we don't even want to bother parsing `b` unless we end up in an
// error-correction path, so return a parse result stream that will lazily attempt the parse
// of `b` if parse results are requested.
return ParseResultStream(false, [a_result = std::move(a_result), b = b.share(),
b_result = std::optional<ParseResultStream>()]() mutable {
if (!b_result) {
b_result = b(std::move(a_result));
}
return b_result->Next();
});
} else {
return b(std::move(a_result));
}
};
}
fit::function<ParseResultStream(ParseResultStream)> Seq(
fit::function<ParseResultStream(ParseResultStream)> first) {
return first;
}
fit::function<ParseResultStream(ParseResultStream)> Alt(
fit::function<ParseResultStream(ParseResultStream)> a) {
return a;
}
fit::function<ParseResultStream(ParseResultStream)> Alt(
fit::function<ParseResultStream(ParseResultStream)> a,
fit::function<ParseResultStream(ParseResultStream)> b) {
return [a = std::move(a), b = std::move(b)](ParseResultStream prefixes) mutable {
auto [a_prefixes, b_prefixes] = std::move(prefixes).Fork();
auto a_result = a(std::move(a_prefixes));
if (a_result.ok()) {
return a_result;
} else {
return b(std::move(b_prefixes));
}
};
}
ParseResultStream Empty(ParseResultStream prefixes) { return prefixes; }
ParseResultStream EOS(ParseResultStream prefixes) {
return std::move(prefixes).Follow([](ParseResult result) {
if (result.tail().empty()) {
return ParseResultStream(result);
} else {
return ParseResultStream(result.Skip(result.tail().size())).Fail();
}
});
}
fit::function<ParseResultStream(ParseResultStream)> Not(
fit::function<ParseResultStream(ParseResultStream)> inv) {
return [inv = std::move(inv)](ParseResultStream prefixes) mutable {
return std::move(prefixes).Follow([inv = inv.share()](ParseResult result) {
auto inv_parse = inv(ParseResultStream(result));
if (!inv_parse.ok()) {
return ParseResultStream(result);
} else {
auto got = inv_parse.Next().node();
auto error =
"Ambiguous sequence: '" + std::string(result.tail().substr(0, got->Size())) + "'";
auto error_node = std::make_unique<ast::Error>(result.offset(), 0, error);
return ParseResultStream(result.InjectError(got->Size(), std::move(error_node))).Fail();
}
});
};
}
fit::function<ParseResultStream(ParseResultStream)> Multi(
size_t min, size_t max, fit::function<ParseResultStream(ParseResultStream)> child) {
if (min == 1 && max == 1) {
return child;
} else if (min > 0) {
if (max != std::numeric_limits<size_t>::max()) {
// TODO: If we can find a way to make the structure of Multi combinators constant-size, we
// could just treat the max limit like a regular limit and not a sentinel value, which would
// mean killing this conditional.
max -= 1;
}
// We have to parse at least one instance of the child, so parse it here, then recurse to parse
// the rest of the pattern.
return Seq(child.share(), Multi(min - 1, max, std::move(child)));
} else if (max == std::numeric_limits<size_t>::max()) {
// This is the same principle as the finite version in the last conditional, but we have to
// construct the next combinator in a closure otherwise this combinator recurses infinitely.
return Maybe([child = std::move(child)](ParseResultStream prefixes) mutable {
return Multi(1, std::numeric_limits<size_t>::max(), child.share())(std::move(prefixes));
});
} else if (max == 1) {
// Multi(0, 1, ...) is just Maybe(...)
return Maybe(std::move(child));
} else {
// Min = 0, max = <something finite>. We can wrap this in a
return Maybe(Multi(1, max, std::move(child)));
}
}
fit::function<ParseResultStream(ParseResultStream)> Multi(
size_t count, fit::function<ParseResultStream(ParseResultStream)> child) {
return Multi(count, count, std::move(child));
}
} // namespace shell::parser