blob: 94c76213ae925ae82d2c475374534594868609e2 [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/parse_result.h"
#include <deque>
namespace shell::parser {
namespace {
// Helper class to allow us to fork ParseResultStreams. Forking a stream means we get two streams,
// and each stream will yield the same results if we call Next() on it in order, *independent of*
// whether we call Next() on the other stream.
//
// Doing that means caching certain results when one side of the fork consumes them before the
// other, and this class holds the state involved in that.
class StreamFork {
public:
StreamFork(ParseResultStream stream) : stream_(std::move(stream)) {}
// Handle a call to Next() for the A side of the stream.
ParseResult ANext() {
if (!end_ && a_results_.empty()) {
Enqueue();
}
if (end_) {
return *end_;
}
auto ret = a_results_.front();
a_results_.pop_front();
return ret;
}
// Handle a call to Next() for the B side of the stream.
ParseResult BNext() {
if (!end_ && b_results_.empty()) {
Enqueue();
}
if (end_) {
return *end_;
}
auto ret = b_results_.front();
b_results_.pop_front();
return ret;
}
private:
// Poll the original stream and store its output in each of the two queues that feed our forked
// stream.
void Enqueue() {
if (end_) {
return;
}
auto result = stream_.Next();
if (!result) {
end_ = result;
} else {
a_results_.push_back(result);
b_results_.push_back(result);
}
}
ParseResultStream stream_;
std::deque<ParseResult> a_results_;
std::deque<ParseResult> b_results_;
std::optional<ParseResult> end_ = std::nullopt;
};
} // namespace
std::pair<ParseResultStream, ParseResultStream> ParseResultStream::Fork() && {
bool ok = this->ok();
auto fork = std::make_shared<StreamFork>(std::move(*this));
auto a = ParseResultStream(ok, [fork]() mutable { return fork->ANext(); });
auto b = ParseResultStream(ok, [fork]() mutable { return fork->BNext(); });
return {std::move(a), std::move(b)};
}
ParseResultStream ParseResultStream::Fail() && {
ok_ = false;
return std::move(*this);
}
ParseResultStream ParseResultStream::Follow(fit::function<ParseResultStream(ParseResult)> next) && {
// Takes the least-erroneous result from this stream and uses it as the prefix for the given
// parser. In the future lots of interesting error handling stuff will happen here (backtracking!)
// but for now this will do.
return next(Next());
}
ParseResultStream ParseResultStream::Map(fit::function<ParseResult(ParseResult)> mapper) && {
auto ok = ok_;
return ParseResultStream(ok, [old = std::move(*this), mapper = std::move(mapper)]() mutable {
return mapper(old.Next());
});
}
} // namespace shell::parser