First usable generator source.
Still issues with usage rules and error reporting, but functional.
This commit is contained in:
80
generator/templates/ArgGrammarDriver.cpp
Normal file
80
generator/templates/ArgGrammarDriver.cpp
Normal file
@@ -0,0 +1,80 @@
|
||||
{{=@@ @@=}}
|
||||
#include <cassert>
|
||||
#include <fstream>
|
||||
|
||||
#include "@@{argspec}@@ArgGrammarDriver.hpp"
|
||||
#include "@@{argspec}@@ArgGrammarScanner.hpp"
|
||||
|
||||
namespace @@{argspec}@@ArgGrammar {
|
||||
Driver::Driver(int argc, char* argv[]) : argc{ argc }, argv{ argv }, scanner{}, parser{} {
|
||||
assert(argc > 0 && "Arguments must include program invocation");
|
||||
}
|
||||
|
||||
Driver::~Driver() {
|
||||
delete scanner;
|
||||
delete parser;
|
||||
}
|
||||
|
||||
Driver::Result Driver::parse() {
|
||||
if (scanner)
|
||||
delete scanner;
|
||||
|
||||
try {
|
||||
scanner = new Scanner(argc, argv, *this);
|
||||
} catch (const std::bad_alloc& ba) {
|
||||
std::cerr << "Failed to allocate scanner: \"" << ba.what() << "\". Exiting!\n";
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (parser)
|
||||
delete parser;
|
||||
try {
|
||||
parser = new 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 result;
|
||||
}
|
||||
|
||||
void Driver::addArg(const FlagArg flag) {
|
||||
flagArguments.insert(flag);
|
||||
}@@#any_parameters@@
|
||||
|
||||
void Driver::addArg(const ParamArg flag, std::vector<std::string> parameters) {
|
||||
paramArguments.insert_or_assign(flag, parameters);
|
||||
}@@/any_parameters@@
|
||||
|
||||
bool Driver::getArg(const FlagArg flag) const {
|
||||
return (flagArguments.find(flag) != flagArguments.end());
|
||||
}@@#any_parameters@@
|
||||
|
||||
std::optional<std::vector<std::string>> Driver::getArg(const ParamArg flag) const {
|
||||
try {
|
||||
return std::make_optional(paramArguments.at(flag));
|
||||
} catch (const std::out_of_range& ignored) {
|
||||
return std::nullopt;
|
||||
}
|
||||
}@@/any_parameters@@
|
||||
|
||||
void Driver::setResult(Driver::Result result) {
|
||||
this->result = result;
|
||||
}
|
||||
|
||||
const std::set<FlagArg>& Driver::getFlagArgs() const {
|
||||
return flagArguments;
|
||||
}@@#any_parameters@@
|
||||
|
||||
const std::map<ParamArg, std::vector<std::string>>& Driver::getParamArgs() const {
|
||||
return paramArguments;
|
||||
}@@/any_parameters@@
|
||||
|
||||
char** Driver::getArgv() {
|
||||
return argv;
|
||||
}
|
||||
} // namespace trueArgGrammar
|
||||
58
generator/templates/ArgGrammarDriver.hpp
Normal file
58
generator/templates/ArgGrammarDriver.hpp
Normal file
@@ -0,0 +1,58 @@
|
||||
{{=@@ @@=}}
|
||||
#pragma once
|
||||
|
||||
#include "@@{argspec}@@ArgGrammarParser.tab.hh"
|
||||
#include <iostream>
|
||||
#include <set>@@#any_parameters@@
|
||||
#include <map>
|
||||
#include <optional>@@/any_parameters@@
|
||||
#include <string>
|
||||
|
||||
namespace @@{argspec}@@ArgGrammar {
|
||||
enum class FlagArg { @@#argument_tokens@@@@^has_parameters@@@@{clean_token}@@, @@/has_parameters@@@@/argument_tokens@@ };@@#any_parameters@@
|
||||
enum class ParamArg { @@#argument_tokens@@@@#has_parameters@@@@{clean_token}@@, @@/has_parameters@@@@/argument_tokens@@ };@@/any_parameters@@
|
||||
|
||||
class Scanner;
|
||||
|
||||
class Driver {
|
||||
public:
|
||||
enum class Result { success, completedAction, wrongArgument };
|
||||
|
||||
Driver(int argc, char* argv[]);
|
||||
virtual ~Driver();
|
||||
|
||||
Result parse();
|
||||
|
||||
void addArg(const FlagArg flag);@@#any_parameters@@
|
||||
void addArg(const ParamArg flag, const std::vector<std::string> arguments);@@/any_parameters@@
|
||||
|
||||
bool getArg(const FlagArg flag) const;@@#any_parameters@@
|
||||
std::optional<std::vector<std::string>> getArg(const ParamArg flag) const;@@/any_parameters@@
|
||||
void setResult(Result result);
|
||||
|
||||
const std::set<FlagArg>& getFlagArgs() const;@@#any_parameters@@
|
||||
const std::map<ParamArg, std::vector<std::string>>& getParamArgs() const;@@/any_parameters@@
|
||||
|
||||
std::ostream& print(std::ostream& oss);
|
||||
|
||||
char** getArgv();
|
||||
|
||||
private:
|
||||
int argc;
|
||||
char** argv;
|
||||
|
||||
Result result = Result::success;
|
||||
|
||||
void parseHelper(std::istream& iss);
|
||||
|
||||
@@{argspec}@@ArgGrammar::Parser* parser = nullptr;
|
||||
@@{argspec}@@ArgGrammar::Scanner* scanner = nullptr;
|
||||
|
||||
/*
|
||||
* specialised data store
|
||||
*/
|
||||
|
||||
std::set<FlagArg> flagArguments;@@#any_parameters@@
|
||||
std::map<ParamArg, std::vector<std::string>> paramArguments;@@/any_parameters@@
|
||||
};
|
||||
} // namespace trueArgGrammar
|
||||
94
generator/templates/ArgGrammarParser.yy
Normal file
94
generator/templates/ArgGrammarParser.yy
Normal file
@@ -0,0 +1,94 @@
|
||||
{{=@@ @@=}}
|
||||
%skeleton "lalr1.cc"
|
||||
%require "3.2"
|
||||
%debug
|
||||
%defines
|
||||
%define api.namespace {@@{argspec}@@ArgGrammar}
|
||||
%define api.parser.class {Parser}
|
||||
%file-prefix "@@{argspec}@@ArgGrammarParser"
|
||||
|
||||
%code requires{
|
||||
namespace @@{argspec}@@ArgGrammar {
|
||||
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 <string>
|
||||
#include <map>
|
||||
#include <utility>
|
||||
|
||||
#include "@@{argspec}@@ArgGrammarDriver.hpp"
|
||||
#include "@@{argspec}@@ArgGrammarScanner.hpp"
|
||||
|
||||
#undef yylex
|
||||
#define yylex scanner.yylex
|
||||
}
|
||||
|
||||
%define api.value.type variant
|
||||
%define parse.assert
|
||||
|
||||
%token <std::string> POSITIONAL_ARGUMENT@@#argument_tokens@@
|
||||
%token ARGUMENT_@@{clean_token}@@@@/argument_tokens@@
|
||||
|
||||
%start ARGUMENTS
|
||||
|
||||
%%
|
||||
|
||||
ARGUMENTS
|
||||
: ARGUMENT_help {
|
||||
std::cout
|
||||
<< "Usage:\n"
|
||||
<< "\n"@@#usage@@
|
||||
<< driver.getArgv()[0] << "@@#flags@@ @@{.}@@@@/flags@@@@#positional@@ <@@{.}@@>@@/positional@@\n"@@/usage@@
|
||||
<< driver.getArgv()[0] << " --help\n"
|
||||
<< driver.getArgv()[0] << " --version\n"
|
||||
<< driver.getArgv()[0] << " --license\n"
|
||||
<< "\n"
|
||||
<< "Arguments:\n"
|
||||
<< "\n"@@#argument_explanations@@
|
||||
<< "@@{.}@@\n"
|
||||
<< "\n"@@/argument_explanations@@@@#argument_tokens@@
|
||||
<< " @@#short_argument@@-@@{short_argument}@@,@@/short_argument@@@@^short_argument@@ @@/short_argument@@ --@@{argument}@@@@{parameter_align_spacing}@@@@#parameters@@ <@@{name}@@>@@#next_state@@,@@/next_state@@@@/parameters@@@@^parameters@@ @@/parameters@@ @@{explain_align_spacing}@@@@{usage}@@\n"@@/argument_tokens@@
|
||||
<< "\n"
|
||||
<< "@@{help_addendum}@@"@@/help_addendum@@
|
||||
<< std::endl;
|
||||
driver.addArg(@@{argspec}@@ArgGrammar::FlagArg::help);
|
||||
driver.setResult(Driver::Result::completedAction);
|
||||
}
|
||||
| ARGUMENT_version {
|
||||
std::cout << "@@{version}@@" << std::endl;
|
||||
driver.addArg(@@{argspec}@@ArgGrammar::FlagArg::version);
|
||||
driver.setResult(Driver::Result::completedAction);
|
||||
}
|
||||
| ARGUMENT_license {
|
||||
std::cout << "@@{license}@@" << std::endl;
|
||||
driver.addArg(@@{argspec}@@ArgGrammar::FlagArg::license);
|
||||
driver.setResult(Driver::Result::completedAction);
|
||||
} @@#usage@@
|
||||
|@@#flags@@ ARGUMENT_@@{.}@@@@/flags@@@@#positional@@ @@{.}@@@@/positional@@@@^flags@@@@^positional@@ %empty@@/positional@@@@/flags@@@@/usage@@
|
||||
;@@#usage_rules@@
|
||||
|
||||
ARGUMENT_@@{rule_name}@@
|
||||
:@@#options@@ ARGUMENT_@@{option}@@@@#has_next@@
|
||||
|@@/has_next@@@@/options@@
|
||||
;@@/usage_rules@@
|
||||
|
||||
%%
|
||||
|
||||
void @@{argspec}@@ArgGrammar::Parser::error(const std::string& err_message)
|
||||
{
|
||||
std::cerr << "Error: " << err_message << '\n';
|
||||
}
|
||||
31
generator/templates/ArgGrammarScanner.cpp
Normal file
31
generator/templates/ArgGrammarScanner.cpp
Normal file
@@ -0,0 +1,31 @@
|
||||
{{=@@ @@=}}
|
||||
#include "@@{argspec}@@ArgGrammarScanner.hpp"
|
||||
#include "@@{argspec}@@ArgGrammarDriver.hpp"
|
||||
|
||||
namespace @@{argspec}@@ArgGrammar {
|
||||
Scanner::Scanner(int argc, char* argv[], Driver& driver)
|
||||
: yyFlexLexer{}, argc{ argc }, argv{ argv }, argi{ 1 }, streamInput{}, resetVal{}, driver{
|
||||
driver
|
||||
} {
|
||||
if (argi < argc)
|
||||
streamInput << argv[argi];
|
||||
|
||||
switch_streams(&streamInput);
|
||||
}
|
||||
|
||||
int Scanner::yywrap() {
|
||||
++argi;
|
||||
bool more = (argi < argc);
|
||||
|
||||
if (more) {
|
||||
streamInput << argv[argi];
|
||||
resetOnWrap();
|
||||
}
|
||||
|
||||
return more ? 0 : 1;
|
||||
}
|
||||
|
||||
void Scanner::setResult(Scanner::Result result) {
|
||||
driver.setResult(result);
|
||||
}
|
||||
} // namespace @@{argspec}@@ArgGrammar
|
||||
38
generator/templates/ArgGrammarScanner.hpp
Normal file
38
generator/templates/ArgGrammarScanner.hpp
Normal file
@@ -0,0 +1,38 @@
|
||||
{{=@@ @@=}}
|
||||
#pragma once
|
||||
|
||||
#if !defined(yyFlexLexerOnce)
|
||||
# include <FlexLexer.h>
|
||||
#endif
|
||||
|
||||
#include "@@{argspec}@@ArgGrammarDriver.hpp"
|
||||
#include <cstring>
|
||||
#include <optional>
|
||||
#include <sstream>
|
||||
|
||||
namespace @@{argspec}@@ArgGrammar {
|
||||
class Scanner : public yyFlexLexer {
|
||||
public:
|
||||
using Result = Driver::Result;
|
||||
|
||||
Scanner(int argc, char* argv[], Driver& driver);
|
||||
virtual ~Scanner() = default;
|
||||
|
||||
using FlexLexer::yylex;
|
||||
|
||||
virtual int yylex(Parser::semantic_type* const lval);
|
||||
void resetOnWrap();
|
||||
void setResult(Result result);
|
||||
|
||||
private:
|
||||
int argc, argi;
|
||||
char** argv;
|
||||
Driver& driver;
|
||||
std::stringstream streamInput;
|
||||
std::optional<int> resetVal;
|
||||
Parser::semantic_type* yyval = nullptr;
|
||||
|
||||
protected:
|
||||
int yywrap() override;
|
||||
};
|
||||
} // namespace trueArgGrammar
|
||||
123
generator/templates/ArgGrammarScanner.ll
Normal file
123
generator/templates/ArgGrammarScanner.ll
Normal file
@@ -0,0 +1,123 @@
|
||||
{{=@@ @@=}}
|
||||
%{
|
||||
#include <stdexcept>
|
||||
#include "@@{argspec}@@ArgGrammarParser.tab.hh"
|
||||
|
||||
#include "@@{argspec}@@ArgGrammarScanner.hpp"
|
||||
#undef YY_DECL
|
||||
#define YY_DECL int @@{argspec}@@ArgGrammar::Scanner::yylex(@@{argspec}@@ArgGrammar::Parser::semantic_type* const lval)
|
||||
|
||||
using token = @@{argspec}@@ArgGrammar::Parser::token;
|
||||
%}
|
||||
|
||||
%option debug
|
||||
%option nodefault
|
||||
%option yyclass="@@{argspec}@@ArgGrammar::Scanner"
|
||||
%option noyywrap
|
||||
%option c++
|
||||
%option outfile="@@{argspec}@@ArgGrammarScannerDef.cpp"
|
||||
|
||||
%x SHORT_ARGUMENTS ARGUMENT_VALUE POSITIONAL_ARGUMENTS@@#argument_tokens@@@@#parameters@@ @@{clean_token}@@_PARAMETER_@@{index}@@@@/parameters@@@@/argument_tokens@@
|
||||
|
||||
ANYTHING .|\n
|
||||
LONGARG [[:alnum:]][[:alnum:]\-]*
|
||||
|
||||
%%
|
||||
|
||||
%{ /** Code executed at the beginning of yylex **/
|
||||
yyval = lval;
|
||||
static std::vector<std::string> parameters;
|
||||
%}@@#argument_tokens@@@@#has_parameters@@
|
||||
|
||||
<@@#parameters@@@@{clean_token}@@_PARAMETER_@@{index}@@@@#next_state@@,@@/next_state@@@@/parameters@@>-- { /** invalid usage of --@@{argument}@@ flag **/
|
||||
BEGIN(POSITIONAL_ARGUMENTS);
|
||||
std::cerr << "--@@{argument}@@ has not been given sufficient parameters:\n";
|
||||
|
||||
for (const auto& parameter : parameters)
|
||||
std::cerr << "\t- \'" << parameter << "\'\n";
|
||||
|
||||
setResult(Result::wrongArgument);
|
||||
}@@/has_parameters@@@@/argument_tokens@@
|
||||
|
||||
<*>-- { /* end of arguments */
|
||||
BEGIN(POSITIONAL_ARGUMENTS);
|
||||
}
|
||||
|
||||
/** Arguments **/
|
||||
@@#argument_tokens@@
|
||||
|
||||
<INITIAL>--@@{argument}@@ {@@#has_parameters@@
|
||||
BEGIN(@@{clean_token}@@_PARAMETER_1);
|
||||
parameters = {};@@/has_parameters@@@@^has_parameters@@
|
||||
driver.addArg(@@{argspec}@@ArgGrammar::FlagArg::@@{clean_token}@@);
|
||||
return token::ARGUMENT_@@{clean_token}@@;@@/has_parameters@@
|
||||
}@@#short_argument@@
|
||||
|
||||
<SHORT_ARGUMENTS>@@{short_argument}@@ {@@#has_parameters@@
|
||||
BEGIN(@@{clean_token}@@_PARAMETER_1);
|
||||
resetVal = std::nullopt;
|
||||
parameters = {};@@/has_parameters@@@@^has_parameters@@
|
||||
driver.addArg(@@{argspec}@@ArgGrammar::FlagArg::@@{clean_token}@@);
|
||||
return token::ARGUMENT_@@{clean_token}@@;@@/has_parameters@@
|
||||
}@@/short_argument@@@@#parameters@@
|
||||
|
||||
<@@{clean_token}@@_PARAMETER_@@{index}@@>{ANYTHING}+ {@@#next_state@@
|
||||
BEGIN(@@{clean_token}@@_PARAMETER_@@{.}@@);@@/next_state@@@@^next_state@@
|
||||
BEGIN(INITIAL);@@/next_state@@
|
||||
parameters.push_back(yytext);@@^next_state@@
|
||||
driver.addArg(@@{argspec}@@ArgGrammar::ParamArg::@@{clean_token}@@, parameters);
|
||||
return token::ARGUMENT_@@{clean_token}@@;@@/next_state@@
|
||||
}@@/parameters@@
|
||||
@@/argument_tokens@@
|
||||
|
||||
/** Default (error) arguments */
|
||||
|
||||
<INITIAL>--{LONGARG} {
|
||||
std::cerr << "Unknown argument: \'" << yytext << "\' found\n";
|
||||
setResult(Result::wrongArgument);
|
||||
}
|
||||
|
||||
<INITIAL>--{ANYTHING}+ { /* show common error for invalid option */
|
||||
std::cerr << "Invalid argument: \'" << yytext << "\' found\n";
|
||||
setResult(Result::wrongArgument);
|
||||
}
|
||||
|
||||
<SHORT_ARGUMENTS>{ANYTHING} {
|
||||
std::cerr << "Unknown argument \'-" << yytext << "\' found\n";
|
||||
setResult(Result::wrongArgument);
|
||||
}
|
||||
|
||||
/** Check for short argument **/
|
||||
|
||||
<INITIAL>-/{ANYTHING}+ {
|
||||
BEGIN(SHORT_ARGUMENTS);
|
||||
resetVal = INITIAL;
|
||||
}
|
||||
|
||||
/** Check for possible unknown arguments **/
|
||||
|
||||
<POSITIONAL_ARGUMENTS>{ANYTHING}+ {
|
||||
/* std::cerr << "Unknown positional argument \'" << yytext << "\' found\n";
|
||||
setResult(Result::wrongArgument); */
|
||||
yyval->build<std::string>(yytext);
|
||||
return token::POSITIONAL_ARGUMENT;
|
||||
}
|
||||
|
||||
<INITIAL>{ANYTHING}* { /* unknown positional argument */
|
||||
BEGIN(POSITIONAL_ARGUMENTS);
|
||||
/* std::cerr << "Unknown positional argument \'" << yytext << "\' found\n";
|
||||
setResult(Result::wrongArgument); */
|
||||
yyval->build<std::string>(yytext);
|
||||
return token::POSITIONAL_ARGUMENT;
|
||||
}
|
||||
|
||||
%%
|
||||
|
||||
namespace @@{argspec}@@ArgGrammar {
|
||||
void Scanner::resetOnWrap() {
|
||||
if (resetVal)
|
||||
BEGIN(*resetVal);
|
||||
|
||||
resetVal = INITIAL;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user