* This file is part of the MindStudio project.
* Copyright (c) 2025 Huawei Technologies Co.,Ltd.
*
* MindStudio is licensed under Mulan PSL v2.
* You can use this software according to the terms and conditions of the Mulan PSL v2.
* You may obtain a copy of Mulan PSL v2 at:
*
* http://license.coscl.org.cn/MulanPSL2
*
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
* See the Mulan PSL v2 for more details.
* ------------------------------------------------------------------------- */
#ifndef __PARSER_PARSER_H__
#define __PARSER_PARSER_H__
#include <memory>
#include <string>
#include <vector>
#include <iostream>
#include <sstream>
#include "result.h"
#include "common/prof_args.h"
namespace Parser {
constexpr std::size_t shortNamePrefixLen = 1;
constexpr std::size_t longNamePrefixLen = 2;
struct Parser {
virtual Either Parse(TokenS const &stream) const = 0;
};
struct Switch : public Parser {
Switch(char shortName, std::string longName, bool &value)
: value_(value), shortName_(shortName), longName_(std::move(longName)) { }
inline Either Parse(TokenS const &stream) const override;
private:
inline Either ParseShortName(TokenS const &stream, std::string const &arg) const;
inline Either ParseLongName(TokenS const &stream, std::string const &arg) const;
private:
bool& value_;
char shortName_;
std::string longName_;
};
Either Switch::Parse(TokenS const &stream) const
{
if (stream.Eos()) {
return Either::Left({ErrorType::ParseEos, "unexpected eos"});
}
std::string arg = stream.Get();
if (arg.length() < shortNamePrefixLen || arg[0] != '-') {
return Either::Left({ErrorType::ParseNonOption, "unexpected non-option argument " + arg});
}
if (arg.length() < longNamePrefixLen || arg[1] != '-') {
return ParseShortName(stream, arg.substr(shortNamePrefixLen));
} else {
return ParseLongName(stream, arg.substr(longNamePrefixLen));
}
}
Either Switch::ParseShortName(TokenS const &stream, std::string const &arg) const
{
if (shortName_ == '\0') {
return Either::Left({ErrorType::ParseMissMatch, "unexpected argument -" + arg});
}
if (arg.length() == 1 && arg[0] == shortName_) {
value_ = true;
return Either::Right(stream.Consume(1));
} else {
return Either::Left({ErrorType::ParseMissMatch, "unexpected argument -" + arg});
}
}
Either Switch::ParseLongName(TokenS const &stream, std::string const &arg) const
{
if (longName_.empty()) {
return Either::Left({ErrorType::ParseMissMatch, "unexpected argument --" + arg});
} else if (arg == longName_) {
value_ = true;
return Either::Right(stream.Consume(1));
} else {
return Either::Left({ErrorType::ParseMissMatch, "unexpected argument --" + arg});
}
}
template<typename T>
struct Option : public Parser {
Option(char shortName, std::string longName, std::string meta, T &value)
: value_(value), shortName_(shortName), longName_(std::move(longName)),
meta_(std::move(meta)) { }
inline Either Parse(TokenS const &stream) const override;
private:
inline Either ParseShortName(TokenS const &stream, std::string const &arg) const;
inline Either ParseLongName(TokenS const &stream, std::string const &arg) const;
private:
T &value_;
char shortName_;
std::string longName_;
std::string meta_;
};
template<typename T>
Either Option<T>::Parse(const TokenS &stream) const
{
if (stream.Eos()) {
return Either::Left({ErrorType::ParseEos, "unexpected eos"});
}
std::string arg = stream.Get();
if (arg[0] != '-') {
return Either::Left({ErrorType::ParseNonOption, "unexpected non-option argument " + arg});
}
if (arg[1] == '-') {
return ParseLongName(stream, arg.substr(longNamePrefixLen));
} else {
return ParseShortName(stream, arg.substr(shortNamePrefixLen));
}
}
template<typename T>
Either Option<T>::ParseShortName(TokenS const &stream, std::string const &arg) const
{
if (shortName_ == '\0') {
return Either::Left({ErrorType::ParseMissMatch, "unexpected argument -" + arg});
}
if (arg.length() == 1 && arg[0] == shortName_) {
TokenS s = stream.Consume(1);
if (s.Eos()) {
return Either::Left({ErrorType::ParseLeakValue, "argument -" + arg + " miss value"});
}
if (ParseValue(s.Get(), value_)) {
return Either::Right(s.Consume(1));
} else {
return Either::Left({ErrorType::ParseInvalidValue, "argument -" + arg + " " + s.Get() + " is invalid"});
}
} else {
return Either::Left({ErrorType::ParseMissMatch, "unexpected argument -" + arg});
}
}
template<typename T>
Either Option<T>::ParseLongName(TokenS const &stream, std::string const &arg) const
{
if (longName_.empty()) {
return Either::Left({ErrorType::ParseMissMatch, "unexpected argument --" + arg});
}
auto equal = arg.find_first_of('=');
if (arg.substr(0, equal) != longName_) {
return Either::Left({ErrorType::ParseMissMatch, "unexpected argument --" + arg});
} else if (equal == std::string::npos) {
return Either::Left({ErrorType::ParseLeakValue, "argument --" + arg + " miss value"});
} else {
if (ParseValue(arg.substr(equal + 1), value_)) {
return Either::Right(stream.Consume(1));
} else {
return Either::Left({ErrorType::ParseInvalidValue, "argument --" + arg + " is invalid"});
}
}
}
class ArgParser {
public:
inline ArgParser(std::string command, std::string description);
template<typename ParserT>
inline void Add(ParserT const &arg);
inline Either Parse(TokenS const &stream, Common::ProfArgs &config) const;
private:
inline Either ParseEach(TokenS const &stream) const;
inline void ParseNoneOption(TokenS &stream, Common::ProfArgs &config) const;
private:
std::string command_;
std::string description_;
std::vector<std::unique_ptr<Parser>> parsers_;
};
ArgParser::ArgParser(std::string command, std::string description)
: command_{std::move(command)}, description_{description} { }
template<typename ParserT>
void ArgParser::Add(ParserT const &arg)
{
parsers_.emplace_back(new ParserT(arg));
}
void ArgParser::ParseNoneOption(TokenS &stream, Common::ProfArgs &config) const
{
while (!stream.Eos()) {
config.argApps += stream.Get();
stream = stream.Consume(1);
if (!stream.Eos()) {
config.argApps += " ";
}
}
}
Either ArgParser::Parse(const TokenS &stream, Common::ProfArgs &config) const
{
TokenS s{stream};
while (!s.Eos()) {
std::string args = s.Get();
if (args[0] != '-') {
ParseNoneOption(s, config);
return Either::Right(s);
}
Either ret = ParseEach(s);
if (ret.Valid()) {
s = ret.Right();
continue;
}
return ret;
}
return Either::Right(s);
}
Either ArgParser::ParseEach(const TokenS &stream) const
{
Either ret = Either::Right(stream);
for (auto const &p : parsers_) {
ret = p->Parse(stream);
if (ret.Valid()) {
return ret;
}
Error error = ret.Left();
if (error.type != ErrorType::ParseMissMatch) {
return ret;
}
}
return ret;
}
};
#endif