blob: 8fd761e9faaf73c4234ccac9263f05da3b407365 [file] [log] [blame]
// 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 "lib/fit/function.h"
#include "src/developer/shell/parser/parse_result.h"
#ifndef SRC_DEVELOPER_SHELL_PARSER_TEXT_MATCH_H_
#define SRC_DEVELOPER_SHELL_PARSER_TEXT_MATCH_H_
namespace shell::parser {
// Produce a parser which parses any single character from the given list.
fit::function<ParseResult(ParseResult)> AnyChar(std::string_view chars);
// Produce a parser which parses any single character not in the given list.
fit::function<ParseResult(ParseResult)> AnyCharBut(std::string_view chars);
// Produce a parser which parses any single character.
ParseResult AnyChar(ParseResult prefix);
// Similar to AnyChar but the input string is a regex style range group like "a-zA-Z0-9".
fit::function<ParseResult(ParseResult)> CharGroup(std::string_view chars);
// Produce a parser to parse a fixed text string.
template <typename T = ast::Terminal>
fit::function<ParseResult(ParseResult)> Token(const std::string& token) {
return [token](ParseResult prefix) {
if (prefix.tail().substr(0, token.size()) == token) {
return prefix.Advance<T>(token.size());
}
return ParseResult::kEnd;
};
}
template <typename T = ast::Terminal>
fit::function<ParseResult(ParseResult)> Token(fit::function<ParseResult(ParseResult)> parser) {
return [parser = std::move(parser)](ParseResult prefix) {
auto result = parser(std::move(prefix).Mark()).Reduce<ast::TokenResult>();
if (!result) {
return ParseResult::kEnd;
}
return result.MapNode(
[unit = result.unit()](std::shared_ptr<ast::Node> node) -> std::shared_ptr<ast::Node> {
// The goal here is to return a single terminal representing the parsed region of the
// result, but terminals don't have children, and thus can't have errors. As such, if we
// have error children, we return an unnamed non-terminal, where we combine all the
// regular parse results into single tokens, but leave the error tokens in place. So if we
// parsed ('foo' 'bar' 'baz'), we'd finish with just 'foobarbaz' on the stack, but if we
// parsed ('foo' 'bar' E[Expected 'baz']) we'd end with ('foobar' E[Expected 'baz']).
std::optional<size_t> start = std::nullopt;
size_t end;
std::vector<std::shared_ptr<ast::Node>> children;
for (const auto& child : node->Children()) {
if (child->IsError()) {
if (start) {
children.push_back(std::make_shared<T>(
*start, child->start() - *start, unit.substr(*start, child->start() - *start)));
}
children.push_back(child);
start = std::nullopt;
} else {
if (!start) {
start = child->start();
}
end = child->start() + child->Size();
}
}
if (start) {
size_t size = end - *start;
children.push_back(std::make_shared<T>(*start, size, unit.substr(*start, size)));
}
if (children.size() == 1) {
return children.front();
} else {
return std::make_shared<ast::TokenResult>(node->start(), std::move(children));
}
});
};
}
} // namespace shell::parser
#endif // SRC_DEVELOPER_SHELL_PARSER_TEXT_MATCH_H_