First usable generator source.

Still issues with usage rules and error reporting, but functional.
This commit is contained in:
2019-09-28 16:01:40 +01:00
parent 54de004ede
commit 60d0446cf0
24 changed files with 1605 additions and 0 deletions

100
generator/src/argument.cpp Normal file
View File

@@ -0,0 +1,100 @@
#include "argument.hpp"
#include <numeric>
Argument::Argument(std::string argument, std::optional<char> shortArgument,
std::string usage)
: argument{argument}, usage{usage}, shortArgument{shortArgument} {}
Argument::~Argument() {}
mstch::map Argument::render() const {
mstch::map result{
{std::string{"argument"}, argument},
{"usage", usage},
{"short_argument", shortArgument
? mstch::node{std::string{*shortArgument}}
: mstch::node{false}},
{"clean_token", cleanToken()},
{"has_parameters", hasParameters()},
};
return result;
}
std::string Argument::cleanToken(const std::string &token) {
std::string result(token.size(), static_cast<char>(NULL));
std::transform(token.begin(), token.end(), result.begin(),
[](char character) {
if (isalpha(character) || isdigit(character))
return character;
else
return '_';
});
return result;
}
std::string Argument::cleanToken() const { return cleanToken(argument); }
#include <iostream>
bool Argument::operator==(const Argument &other) const {
return this->argument == other.argument;
}
bool Argument::operator<(const Argument &other) const {
return this->argument < other.argument;
}
bool Argument::operator>(const Argument &other) const {
return this->argument > other.argument;
}
size_t Argument::argStrLength() const { return argument.size(); }
FlagArgument::FlagArgument(std::string argument,
std::optional<char> shortArgument, std::string usage)
: Argument{argument, shortArgument, usage} {}
mstch::map FlagArgument::render() const {
auto result = Argument::render();
result.insert({"parameters", mstch::array{}});
return result;
}
bool FlagArgument::hasParameters() const { return false; }
size_t FlagArgument::paramStrLength() const { return 0; }
ParameterArgument::ParameterArgument(std::string argument,
std::optional<char> shortArgument,
std::vector<std::string> parameters,
std::string usage)
: Argument{argument, shortArgument, usage}, parameters{parameters} {}
mstch::map ParameterArgument::render() const {
auto result = Argument::render();
mstch::array resultParameters{};
for (int i = 0; i < parameters.size(); i++) {
mstch::map resultParameter{{"index", i + 1}, {"name", parameters[i]}};
if (i + 1 < parameters.size())
resultParameter.insert({"next", i + 2});
resultParameters.push_back(resultParameter);
}
result.insert({"parameters", resultParameters});
return result;
}
bool ParameterArgument::hasParameters() const { return true; }
size_t ParameterArgument::paramStrLength() const {
size_t totalSize = std::accumulate(
parameters.begin(), parameters.end(), 0,
[](size_t count, const std::string &str) { return count + str.size(); });
return totalSize + (parameters.size() - 1);
}

View File

@@ -0,0 +1,52 @@
#pragma once
#include <mstch/mstch.hpp>
#include <optional>
#include <string>
class Argument {
public:
Argument(std::string argument, std::optional<char> shortArgument,
std::string usage);
virtual ~Argument() = 0;
virtual mstch::map render() const = 0;
static std::string cleanToken(const std::string &longToken);
std::string cleanToken() const;
virtual bool hasParameters() const = 0;
virtual size_t argStrLength() const;
virtual size_t paramStrLength() const = 0;
bool operator==(const Argument &other) const;
bool operator<(const Argument &other) const;
bool operator>(const Argument &other) const;
public:
std::string argument, usage;
std::optional<char> shortArgument;
};
class FlagArgument : public Argument {
public:
FlagArgument(std::string argument, std::optional<char> shortArgument,
std::string usage);
~FlagArgument() = default;
mstch::map render() const override;
bool hasParameters() const override;
size_t paramStrLength() const override;
bool operator<(const FlagArgument &other) const;
};
class ParameterArgument : public Argument {
public:
ParameterArgument(std::string argument, std::optional<char> shortArgument,
std::vector<std::string> parameters, std::string usage);
mstch::map render() const override;
bool hasParameters() const override;
size_t paramStrLength() const override;
public:
std::vector<std::string> parameters;
};

221
generator/src/driver.cpp Normal file
View File

@@ -0,0 +1,221 @@
#include <cassert>
#include <fstream>
#include <numeric>
#include "driver.hpp"
Grammar::Driver::Driver()
: arguments{ArgumentComparator{}}, maxArgLength{0}, maxParamLength{0} {
addArg(std::make_unique<FlagArgument>("help", std::nullopt,
"shows this help message"));
addArg(std::make_unique<FlagArgument>("version", std::nullopt,
"shows the version of this software"));
addArg(std::make_unique<FlagArgument>("license", std::nullopt,
"shows the license of this software"));
}
Grammar::Driver::~Driver() {
delete (scanner);
delete (parser);
}
void Grammar::Driver::parse(const char *const filename) {
assert(filename != nullptr);
std::ifstream iss{filename};
if (!iss)
exit(EXIT_FAILURE);
parse_helper(iss);
}
void Grammar::Driver::parse(std::istream &iss) {
if (!iss.good() && iss.eof())
return;
parse_helper(iss);
}
void Grammar::Driver::parse_helper(std::istream &iss) {
delete scanner;
try {
scanner = new Grammar::Scanner(&iss);
} catch (const std::bad_alloc &ba) {
std::cerr << "Failed to allocate scanner: \"" << ba.what()
<< "\". Exiting!\n";
exit(EXIT_FAILURE);
}
delete parser;
try {
parser = new Grammar::Parser(*scanner, *this);
} catch (const std::bad_alloc &ba) {
std::cerr << "Failed to allocate parser: \"" << ba.what()
<< "\". Exiting!\n";
exit(EXIT_FAILURE);
}
if (parser->parse() != 0) {
std::cerr << "Parsing failure!\n";
}
return;
}
void Grammar::Driver::setProgramName(std::string programName) {
this->programName = programName;
}
void Grammar::Driver::setVersion(std::string version) {
this->version = version;
}
void Grammar::Driver::setLicense(std::string license) {
this->license = license;
}
void Grammar::Driver::setHelpAddendum(std::string addendum) {
this->helpAddendum = addendum;
}
void Grammar::Driver::addArg(std::unique_ptr<Argument> argument) {
maxArgLength = std::max(maxArgLength, argument->argStrLength());
maxParamLength = std::max(maxParamLength, argument->paramStrLength());
const auto prevVal = arguments.find(argument);
if (prevVal != arguments.end()) {
arguments.erase(prevVal);
}
arguments.insert(std::move(argument));
}
void Grammar::Driver::addUsage(Usage usage) { usages.push_back(usage); }
void Grammar::Driver::addRule(std::string ruleName,
std::vector<std::string> options) {
rules.push_back({ruleName, options});
}
mstch::map Grammar::Driver::getContext() const {
return mstch::map{{"argspec", getSafeName()},
{"any_parameters", usesAnyParameters()},
{"argument_tokens", generateArgumentTokens()},
{"argument_explanations", generateArgumentExplanation()},
{"usage", generateUsageList()},
{"usage_rules", generateUsageRuleList()},
{"version", version},
{"license", license},
{"help_addendum", getHelpAddendum()}};
}
std::string Grammar::Driver::getSafeName() const {
std::string safeName{programName};
safeName.erase(std::remove_if(safeName.begin(), safeName.end(),
[](char letter) { return !isalpha(letter); }),
safeName.end());
return safeName;
}
bool Grammar::Driver::usesAnyParameters() const {
for (auto &argument : arguments) {
if (argument->hasParameters())
return true;
}
return false;
}
mstch::array Grammar::Driver::generateArgumentTokens() const {
mstch::array tokens{};
for (auto &argument : arguments) {
tokens.push_back(alignArg(*argument));
}
return tokens;
}
mstch::array Grammar::Driver::generateArgumentExplanation() const {
mstch::array argumentExplanations{};
for (const auto &rule : rules)
argumentExplanations.push_back(explainRule(rule.first, rule.second));
return argumentExplanations;
}
mstch::array Grammar::Driver::generateUsageList() const {
mstch::array usageList{};
for (const auto &usage : usages) {
const mstch::array flags{usage.flags.begin(), usage.flags.end()};
const mstch::array positional{usage.positional.begin(),
usage.positional.end()};
usageList.push_back(
mstch::map{{"flags", flags}, {"positional", positional}});
}
return usageList;
}
mstch::array Grammar::Driver::generateUsageRuleList() const {
mstch::array usageRuleList{};
for (const auto &rulePair : rules) {
mstch::map usageRule{{"rule_name", Argument::cleanToken(rulePair.first)}};
mstch::array ruleOptions{};
for (size_t i = 0; i < rulePair.second.size(); i++) {
mstch::map ruleOption{
{"option", Argument::cleanToken(rulePair.second[i])}};
if (i + 1 < rulePair.second.size())
ruleOption.insert({"has_next", true});
ruleOptions.push_back(ruleOption);
}
usageRule.insert({"options", ruleOptions});
usageRuleList.push_back(usageRule);
}
return usageRuleList;
}
mstch::node Grammar::Driver::getHelpAddendum() const {
return helpAddendum ? mstch::node{*helpAddendum} : mstch::node{false};
}
std::string
Grammar::Driver::explainRule(const std::string &ruleName,
const std::vector<std::string> &ruleOptions) {
return ruleName + " can be " +
std::accumulate(std::next(ruleOptions.begin()), ruleOptions.end(),
"--" + ruleOptions[0],
[](const std::string &left, const std::string &right) {
return left + " or --" + right;
});
}
std::string Grammar::Driver::spaceN(size_t spaceCount) {
std::string spaces(spaceCount, ' ');
return spaces;
}
mstch::map Grammar::Driver::alignArg(Argument &arg) const {
auto result = arg.render();
result.insert(
{"parameter_align_spacing", spaceN(maxArgLength - arg.argStrLength())});
result.insert(
{"explain_align_spacing", spaceN(maxParamLength - arg.paramStrLength())});
return result;
}
bool Grammar::Driver::ArgumentComparator::
operator()(const std::unique_ptr<Argument> &left,
const std::unique_ptr<Argument> &right) const {
return *left < *right;
}

74
generator/src/driver.hpp Normal file
View File

@@ -0,0 +1,74 @@
#pragma once
#include "argument.hpp"
#include "parser.tab.hh"
#include "scanner.hpp"
#include "usage.hpp"
#include <iostream>
#include <memory>
#include <set>
#include <string>
namespace Grammar {
class Driver {
public:
Driver();
virtual ~Driver();
void parse(const char *const filename);
void parse(std::istream &iss);
/*
* functions to store data retrieved by grammar / parser
*/
void setProgramName(std::string programName);
void setVersion(std::string version);
void setLicense(std::string license);
void setHelpAddendum(std::string addendum);
void addUsage(Usage usage);
void addRule(std::string ruleName, std::vector<std::string> options);
void addArg(std::unique_ptr<Argument> argument);
mstch::map getContext() const;
std::string getSafeName() const;
private:
void parse_helper(std::istream &iss);
Grammar::Parser *parser = nullptr;
Grammar::Scanner *scanner = nullptr;
bool usesAnyParameters() const;
mstch::array generateArgumentTokens() const;
mstch::map generateHelpToken() const;
mstch::map generateVersionToken() const;
mstch::map generateLicenseToken() const;
mstch::array generateArgumentExplanation() const;
mstch::array generateUsageList() const;
mstch::array generateUsageRuleList() const;
mstch::node getHelpAddendum() const;
static std::string explainRule(const std::string &ruleName,
const std::vector<std::string> &ruleOptions);
static std::string spaceN(size_t spaceCount);
mstch::map alignArg(Argument &arg) const;
struct ArgumentComparator {
bool operator()(const std::unique_ptr<Argument> &left,
const std::unique_ptr<Argument> &right) const;
};
/*
* specialised data store
*/
friend Argument;
std::string programName, version, license;
std::set<std::unique_ptr<Argument>, ArgumentComparator> arguments;
std::optional<std::string> helpAddendum;
std::vector<Usage> usages;
std::vector<std::pair<std::string, std::vector<std::string>>> rules;
size_t maxArgLength, maxParamLength;
};
} // namespace Grammar

295
generator/src/lexer.ll Normal file
View File

@@ -0,0 +1,295 @@
/* Simply lexes arguments */
%{
#include <stdexcept>
#include <algorithm>
#include <cctype>
#include <vector>
#include "parser.tab.hh"
#include "scanner.hpp"
#include "argument.hpp"
#undef YY_DECL
#define YY_DECL int Grammar::Scanner::yylex(Grammar::Parser::semantic_type* const lval, Grammar::Parser::location_type* lloc)
#define YY_USER_ACTION lloc->step(); lloc->columns(yyleng);
using token = Grammar::Parser::token;
#define LOG std::clog
%}
%option debug
%option nodefault
%option yyclass="Grammar::Scanner"
%option noyywrap
%option c++
%x PROGRAM_SECTION PROGRAM_NAME PROGRAM_VALUE PROGRAM_QUOTED_VALUE PROGRAM_QUOTED_VALUE_END USAGE_SECTION USAGE_DETAILS_BEGIN USAGE_DETAILS USAGE_RULE_BEGIN USAGE_RULE ARGUMENTS_SECTION ARGUMENTS_LONGOPT ARGUMENTS_SHORTOPT ARGUMENTS_PARAMETERS ARGUMENTS_DESCRIPTION_BEGIN ARGUMENTS_DESCRIPTION ARGUMENTS_DESCRIPTION_END
NAME [^[:space:]]+
SL_SPACE [[:space:]]{-}[\n]
LONGOPT [[:alpha:]][[:alnum:]\-]*
SHORTOPT [^[:space:]]
PARAM [[:alpha:]][[:alnum:]]*
RULE_TOKEN [[:alpha:]][[:alpha:]_-]*
%%
%{ /** Code executed at the beginning of yylex **/
yyval = lval;
static std::string quotedVal{};
static std::string programName{};
static std::vector<std::string> arguments{};
%}
<INITIAL>"Program:" {
BEGIN(PROGRAM_SECTION);
}
<PROGRAM_SECTION>"Usage:" {
BEGIN(USAGE_SECTION);
}
<USAGE_SECTION>"Arguments:" {
BEGIN(ARGUMENTS_SECTION);
}
<PROGRAM_SECTION>{SL_SPACE}+ {
/* ignored */
}
<PROGRAM_SECTION>\n {
lloc->lines();
}
<PROGRAM_SECTION>program {
BEGIN(PROGRAM_NAME);
return token::PROGRAM;
}
<PROGRAM_NAME>{SL_SPACE}+ {
/* ignored */
}
<PROGRAM_NAME>{NAME} {
programName = yytext;
yyval->build<std::string>(programName);
return token::VALUE;
}
<PROGRAM_NAME>\n {
BEGIN(PROGRAM_SECTION);
lloc->lines();
}
<PROGRAM_SECTION>version {
BEGIN(PROGRAM_VALUE);
return token::VERSION;
}
<PROGRAM_VALUE>{SL_SPACE}+ {
/* ignored */
}
<PROGRAM_VALUE>\n {
BEGIN(PROGRAM_SECTION);
throw std::runtime_error{ "Error: no value set on line " + std::to_string(lloc->begin.line) };
}
<PROGRAM_VALUE>\" {
BEGIN(PROGRAM_QUOTED_VALUE);
quotedVal = "";
}
<PROGRAM_QUOTED_VALUE>[^\"\\]+ {
quotedVal += yytext;
}
<PROGRAM_QUOTED_VALUE>\\. {
quotedVal += yytext;
}
<PROGRAM_QUOTED_VALUE>\" {
BEGIN(PROGRAM_QUOTED_VALUE_END);
yyval->build<std::string>(quotedVal);
quotedVal = "";
return token::VALUE;
}
<PROGRAM_QUOTED_VALUE_END>[^[:space:]]+$ {
/* received more text at end of line, after quoted value */
throw std::runtime_error{ "Unknown text after value: \'" + std::string{ yytext } + "\'" };
}
<PROGRAM_QUOTED_VALUE_END>\n {
BEGIN(PROGRAM_SECTION);
lloc->lines();
}
<PROGRAM_SECTION>license {
BEGIN(PROGRAM_VALUE);
return token::LICENSE;
}
<PROGRAM_SECTION>help {
BEGIN(PROGRAM_VALUE);
return token::HELP;
}
<USAGE_SECTION>{SL_SPACE}+ {
/* ignored */
}
<USAGE_SECTION>\n {
/* ignored */
lloc->lines();
}
<USAGE_SECTION>{NAME} {
if (yytext != programName) {
BEGIN(USAGE_RULE_BEGIN);
yyless(0);
} else {
BEGIN(USAGE_DETAILS_BEGIN);
}
}
<USAGE_DETAILS_BEGIN>{SL_SPACE}+ {
/* ignore */
BEGIN(USAGE_DETAILS);
}
<USAGE_DETAILS_BEGIN>\n {
BEGIN(USAGE_SECTION);
yyval->build<std::string>();
lloc->lines();
return token::USAGE;
}
<USAGE_DETAILS>[^[:space:]].*$ {
BEGIN(USAGE_SECTION);
const std::string_view usage{ yytext };
auto trimEndIter = std::find_if_not(usage.rend(),
usage.rbegin(),
[](char charac){ return std::isspace(charac); });
const std::string trimUsage{ usage.begin(), trimEndIter.base() };
yyval->build<std::string>(trimUsage);
return token::USAGE;
}
<USAGE_RULE_BEGIN>{RULE_TOKEN} {
BEGIN(USAGE_RULE);
yyval->build<std::string>(yytext);
return token::RULE_NAME;
}
<USAGE_RULE_BEGIN>{NAME} {
throw std::runtime_error{ "Invalid rule name: " + std::string{ yytext } };
}
<USAGE_RULE>{SL_SPACE} {
/* ignored */
}
<USAGE_RULE>= {
return token::RULE_EQUALS;
}
<USAGE_RULE>--{LONGOPT} {
yyval->build<std::string>(yytext + 2);
return token::RULE_TOKEN;
}
<USAGE_RULE>{RULE_TOKEN} {
yyval->build<std::string>(yytext);
return token::RULE_TOKEN;
}
<USAGE_RULE>"|" {
return token::RULE_OR;
}
<USAGE_RULE>\n {
BEGIN(USAGE_SECTION);
lloc->lines();
}
<ARGUMENTS_SECTION>{SL_SPACE}+ {
/* ignored */
}
<ARGUMENTS_SECTION>\n {
lloc->lines();
}
<ARGUMENTS_SECTION>--{LONGOPT} {
yyval->build<std::string>(yytext + 2);
return token::LONGOPT;
}
<ARGUMENTS_SECTION>, {
BEGIN(ARGUMENTS_LONGOPT);
}
<ARGUMENTS_LONGOPT>-{SHORTOPT} {
yyval->build<char>(*(yytext + 1));
arguments = {};
return token::SHORTOPT;
}
<ARGUMENTS_LONGOPT>, {
BEGIN(ARGUMENTS_SHORTOPT);
}
<ARGUMENTS_LONGOPT,ARGUMENTS_SHORTOPT,ARGUMENTS_PARAMETERS,ARGUMENTS_DESCRIPTION_BEGIN>{SL_SPACE}+ {
/* ignored */
}
<ARGUMENTS_SHORTOPT,ARGUMENTS_PARAMETERS>{PARAM} {
BEGIN(ARGUMENTS_PARAMETERS);
arguments.push_back({ yytext });
}
<ARGUMENTS_SHORTOPT,ARGUMENTS_PARAMETERS>, {
BEGIN(ARGUMENTS_DESCRIPTION_BEGIN);
if (!arguments.empty()) {
yyval->build<std::vector<std::string>>(arguments);
arguments = {};
return token::PARAMETERS;
}
}
<ARGUMENTS_DESCRIPTION_BEGIN>\" {
BEGIN(ARGUMENTS_DESCRIPTION);
quotedVal = "";
}
<ARGUMENTS_DESCRIPTION>[^\\\"]+ {
quotedVal += yytext;
}
<ARGUMENTS_DESCRIPTION>\\. {
quotedVal += yytext;
}
<ARGUMENTS_DESCRIPTION>\" {
BEGIN(ARGUMENTS_DESCRIPTION_END);
yyval->build<std::string>(quotedVal);
quotedVal = "";
return token::DESCRIPTION;
}
<ARGUMENTS_DESCRIPTION_END>\n {
BEGIN(ARGUMENTS_SECTION);
lloc->lines();
}
<*>. {
std::cerr << "Scanning error in state: " << YY_START << std::endl;
throw std::runtime_error{ "Invalid input: \'" + std::string{ yytext } + "\' on line " + std::to_string(lloc->begin.line) };
}
%%

42
generator/src/main.cpp Normal file
View File

@@ -0,0 +1,42 @@
#include "driver.hpp"
#include "templates.hpp"
#include <fstream>
#include <iostream>
#include <mstch/mstch.hpp>
std::string symbolNameToOutputFile(const std::string &symbolName);
int main(int argc, char *argv[]) {
if (argc != 2) {
std::cerr << "Usage: " << argv[0] << " <filename-to-parse>\n";
return 1;
}
Grammar::Driver driver{};
std::ifstream specFile{argv[1]};
driver.parse(specFile);
const auto context = driver.getContext();
for (const auto &templateFile : templateFiles) {
std::string strTemplate{templateFile.contents};
std::ofstream outputFile{driver.getSafeName() +
symbolNameToOutputFile(templateFile.symbolName)};
outputFile << mstch::render(strTemplate, context);
outputFile.close();
}
return 0;
}
std::string symbolNameToOutputFile(const std::string &symbolName) {
std::string outputFileName(symbolName.size(), static_cast<char>(NULL));
std::transform(symbolName.begin(), symbolName.end(), outputFileName.begin(),
[](char letter) {
if (letter == '_')
return '.';
return letter;
});
return outputFileName;
}

170
generator/src/parser.yy Normal file
View File

@@ -0,0 +1,170 @@
/* Parses an argument specification */
%skeleton "lalr1.cc"
%require "3.2"
%debug
%defines
%define api.namespace {Grammar}
%define api.parser.class {Parser}
%code requires{
# include <optional>
namespace Grammar {
class Driver;
class Scanner;
}
# ifndef YY_NULLPTR
# if defined __cplusplus && 201103L <= __cplusplus
# define YY_NULLPTR nullptr
# else
# define YY_NULLPTR 0
# endif
# endif
}
%parse-param { Scanner& scanner }
%parse-param { Driver& driver }
%code{
# include "driver.hpp"
# include <string>
# include <vector>
# include <utility>
# include <memory>
# undef yylex
# define yylex scanner.yylex
}
%define api.value.type variant
%define parse.assert
%locations
%define api.location.file none
%token PROGRAM
%token VERSION
%token LICENSE
%token HELP
%token <std::string> VALUE
%token <std::string> USAGE
%token <std::string> LONGOPT
%token <char> SHORTOPT
%token <std::vector<std::string>> PARAMETERS
%token <std::string> DESCRIPTION
%token <std::string> RULE_NAME
%token RULE_EQUALS
%token RULE_OR
%token <std::string> RULE_TOKEN
%type <std::optional<char>> OPTIONAL_SHORTOPT
%type <std::optional<std::vector<std::string>>> OPTIONAL_PARAMETERS
%type <std::vector<std::string>> RULE_OPTIONS
%start ARGSPEC
%%
ARGSPEC
: PROGRAM_DETAILS
USAGE_DETAILS
ARGUMENTS_DETAILS
;
PROGRAM_DETAILS
: PROGRAM VALUE
VERSION VALUE
LICENSE VALUE
OPTIONAL_HELP {
driver.setProgramName($2);
driver.setVersion($4);
driver.setLicense($6);
}
;
OPTIONAL_HELP
: %empty
| HELP VALUE {
driver.setHelpAddendum($2);
}
;
USAGE_DETAILS
: USAGE_DETAIL
| USAGE_DETAILS USAGE_DETAIL
;
USAGE_DETAIL
: USAGE {
if ($1.empty())
driver.addUsage(Usage{ {}, {} });
else
driver.addUsage(Usage{ { $1 }, {} });
}
| RULE_NAME RULE_EQUALS RULE_OPTIONS {
driver.addRule($1, $3);
}
;
RULE_OPTIONS
: RULE_TOKEN {
$$ = std::vector{ $1 };
}
| RULE_OPTIONS RULE_OR RULE_TOKEN {
$$ = $1;
$$.push_back($3);
}
;
ARGUMENTS_DETAILS
: %empty
| ARGUMENTS_DETAILS ARGUMENT_DETAILS
;
ARGUMENT_DETAILS
: LONGOPT
OPTIONAL_SHORTOPT OPTIONAL_PARAMETERS
DESCRIPTION {
if ($1 == "help" && $3) {
std::cerr << "--help cannot take parameters\n";
throw std::runtime_error{ "Invalid rule for --help" };
} else if ($1 == "version" && $3) {
std::cerr << "--version cannot take parameters\n";
throw std::runtime_error{ "Invalid rule for --version" };
} else if ($1 == "license" && $3) {
std::cerr << "--license cannot take parameters\n";
throw std::runtime_error{ "Invalid rule for --license" };
}
if ($3) /* if parameters exist */
driver.addArg(std::make_unique<ParameterArgument>($1, $2, *$3, $4));
else
driver.addArg(std::make_unique<FlagArgument>($1, $2, $4));
}
;
OPTIONAL_SHORTOPT
: %empty {
$$ = std::nullopt;
}
| SHORTOPT {
$$ = $1;
}
;
OPTIONAL_PARAMETERS
: %empty {
$$ = std::nullopt;
}
| PARAMETERS {
$$ = $1;
}
;
%%
void Grammar::Parser::error(const location_type& loc, const std::string& err_message)
{
std::cerr << "Error: \'" << err_message << "\' at " << loc << '\n';
}

25
generator/src/scanner.hpp Normal file
View File

@@ -0,0 +1,25 @@
#pragma once
#if !defined(yyFlexLexerOnce)
#include <FlexLexer.h>
#endif
#include "parser.tab.hh"
namespace Grammar {
class Scanner : public yyFlexLexer {
public:
Scanner(std::istream *in) : yyFlexLexer{in} {};
virtual ~Scanner(){};
using FlexLexer::yylex;
virtual int yylex(Grammar::Parser::semantic_type *const lval,
Grammar::Parser::location_type *lloc);
private:
Grammar::Parser::semantic_type *yyval = nullptr;
Grammar::Parser::location_type *loc = nullptr;
};
} // namespace Grammar

View File

@@ -0,0 +1,32 @@
#pragma once
#include <cstdint>
#include <string>
#define PREFIX(filename) _binary_templates_ArgGrammar##filename
#define DECLARE_TEMPLATE_FILE(filename) \
extern const char PREFIX(filename##_start); \
extern const char PREFIX(filename##_end);
DECLARE_TEMPLATE_FILE(Driver_cpp)
DECLARE_TEMPLATE_FILE(Driver_hpp)
DECLARE_TEMPLATE_FILE(Scanner_ll)
DECLARE_TEMPLATE_FILE(Parser_yy)
DECLARE_TEMPLATE_FILE(Scanner_cpp)
DECLARE_TEMPLATE_FILE(Scanner_hpp)
struct TemplateFile {
std::string symbolName;
std::string_view contents;
};
#define STRING_TEMPLATE(filename) \
std::string_view(&PREFIX(filename##_start), \
&PREFIX(filename##_end) - &PREFIX(filename##_start))
#define FILE_TEMPLATE(filename) \
TemplateFile { "ArgGrammar" #filename, STRING_TEMPLATE(filename) }
const TemplateFile templateFiles[] = {
FILE_TEMPLATE(Driver_cpp), FILE_TEMPLATE(Driver_hpp),
FILE_TEMPLATE(Scanner_ll), FILE_TEMPLATE(Parser_yy),
FILE_TEMPLATE(Scanner_cpp), FILE_TEMPLATE(Scanner_hpp)};

7
generator/src/usage.hpp Normal file
View File

@@ -0,0 +1,7 @@
#pragma once
#include <string>
#include <vector>
struct Usage {
std::vector<std::string> flags, positional;
};