Added first example.
This commit is contained in:
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
.ccls-cache
|
||||||
|
*.o
|
||||||
|
.tup
|
||||||
25
examples/.clang-format
Normal file
25
examples/.clang-format
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
---
|
||||||
|
BasedOnStyle: llvm
|
||||||
|
IndentWidth: 4
|
||||||
|
---
|
||||||
|
AccessModifierOffset: -4
|
||||||
|
AllowShortFunctionsOnASingleLine: Empty
|
||||||
|
AlwaysBreakTemplateDeclarations: Yes
|
||||||
|
BreakBeforeBraces: Attach
|
||||||
|
BreakConstructorInitializers: BeforeColon
|
||||||
|
Cpp11BracedListStyle: false
|
||||||
|
ColumnLimit: 100
|
||||||
|
DerivePointerAlignment: false
|
||||||
|
FixNamespaceComments: true
|
||||||
|
IndentCaseLabels: true
|
||||||
|
IndentPPDirectives: AfterHash
|
||||||
|
Language: Cpp
|
||||||
|
NamespaceIndentation: All
|
||||||
|
PointerAlignment: Left
|
||||||
|
SortIncludes: true
|
||||||
|
SortUsingDeclarations: true
|
||||||
|
Standard: Cpp11
|
||||||
|
TabWidth: 4
|
||||||
|
UseTab: ForIndentation
|
||||||
|
|
||||||
|
...
|
||||||
3
examples/true/.ccls
Normal file
3
examples/true/.ccls
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
clang++
|
||||||
|
%cpp --std=c++17
|
||||||
|
-Igenerated
|
||||||
3
examples/true/.gitignore
vendored
Normal file
3
examples/true/.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
*.tab.*
|
||||||
|
trueArgGrammarScannerDef.cpp
|
||||||
|
true
|
||||||
16
examples/true/Tupfile
Normal file
16
examples/true/Tupfile
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
LEXER = flex
|
||||||
|
PARSER = bison
|
||||||
|
CXX = g++
|
||||||
|
CXXFLAGS = -pedantic -std=c++17 -Wall -Wno-unused-parameter -Wno-reorder -Wno-sign-compare -Wno-address -Wno-noexcept-type -Wno-unknown-attributes -Wno-unknown-warning-option
|
||||||
|
CPPFLAGS =
|
||||||
|
LDFLAGS =
|
||||||
|
LIBS =
|
||||||
|
|
||||||
|
SRC_DIR = .
|
||||||
|
BUILD_DIR = build
|
||||||
|
PROG = true
|
||||||
|
|
||||||
|
: foreach $(SRC_DIR)/*.yy |> $(PARSER) -b %BArgGrammarParser %f |> $(SRC_DIR)/%BArgGrammarParser.tab.cc $(SRC_DIR)/%BArgGrammarParser.tab.hh
|
||||||
|
: foreach $(SRC_DIR)/*.ll | $(SRC_DIR)/*.hh |> $(LEXER) %f |> $(SRC_DIR)/%BArgGrammarScannerDef.cpp
|
||||||
|
: foreach $(SRC_DIR)/*.cpp $(SRC_DIR)/*.cc | $(SRC_DIR)/*.hh |> $(CXX) $(CPPFLAGS) $(CXXFLAGS) -I $(SRC_DIR) -c %f -o %o |> $(BUILD_DIR)/%B.o
|
||||||
|
: $(BUILD_DIR)/*.o |> $(CXX) $(LDFLAGS) $(LIBS) %f -o %o |> $(PROG)
|
||||||
28
examples/true/main.cpp
Normal file
28
examples/true/main.cpp
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
#include "trueArgGrammarDriver.hpp"
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
|
using Result = trueArgGrammar::Driver::Result;
|
||||||
|
|
||||||
|
int main(int argc, char* argv[]) {
|
||||||
|
trueArgGrammar::Driver driver{ argc, argv };
|
||||||
|
auto res = driver.parse();
|
||||||
|
|
||||||
|
if (res != Result::success) {
|
||||||
|
if (res == Result::completedAction)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::cout << "true";
|
||||||
|
|
||||||
|
if (auto hasHelp = driver.getArg(trueArgGrammar::SingleArg::help)) {
|
||||||
|
std::cout << " --help";
|
||||||
|
} else if (auto hasVersion = driver.getArg(trueArgGrammar::SingleArg::version)) {
|
||||||
|
std::cout << " --version";
|
||||||
|
}
|
||||||
|
|
||||||
|
std::cout << std::endl;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
13
examples/true/true.argspec
Normal file
13
examples/true/true.argspec
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
Program:
|
||||||
|
|
||||||
|
true
|
||||||
|
version "true (GNU coreutils) 8.31"
|
||||||
|
license "Copyright (C) 2019 Free Software Foundation, Inc."
|
||||||
|
help "NOTE: your shell may have its own version of true, which usually supersedes the version described here."
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
|
||||||
|
true
|
||||||
|
|
||||||
|
Arguments:
|
||||||
|
|
||||||
85
examples/true/true.ll
Normal file
85
examples/true/true.ll
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
%{
|
||||||
|
#include <stdexcept>
|
||||||
|
#include "trueArgGrammarParser.tab.hh"
|
||||||
|
|
||||||
|
#include "trueArgGrammarScanner.hpp"
|
||||||
|
#undef YY_DECL
|
||||||
|
#define YY_DECL int trueArgGrammar::Scanner::yylex(trueArgGrammar::Parser::semantic_type* const lval)
|
||||||
|
|
||||||
|
using token = trueArgGrammar::Parser::token;
|
||||||
|
%}
|
||||||
|
|
||||||
|
%option debug
|
||||||
|
%option nodefault
|
||||||
|
%option yyclass="trueArgGrammar::Scanner"
|
||||||
|
%option noyywrap
|
||||||
|
%option c++
|
||||||
|
%option outfile="trueArgGrammarScannerDef.cpp"
|
||||||
|
|
||||||
|
%x SHORT_ARGUMENTS ARGUMENT_VALUE POSITIONAL_ARGUMENTS
|
||||||
|
|
||||||
|
%%
|
||||||
|
|
||||||
|
%{ /** Code executed at the beginning of yylex **/
|
||||||
|
yyval = lval;
|
||||||
|
%}
|
||||||
|
|
||||||
|
<INITIAL>-- { /* end of arguments */
|
||||||
|
BEGIN(POSITIONAL_ARGUMENTS);
|
||||||
|
}
|
||||||
|
|
||||||
|
<INITIAL>-/. {
|
||||||
|
BEGIN(SHORT_ARGUMENTS);
|
||||||
|
resetVal = INITIAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Default arguments implemented for all parsers **/
|
||||||
|
|
||||||
|
<INITIAL>--help {
|
||||||
|
return token::ARGUMENT_HELP_LONG;
|
||||||
|
}
|
||||||
|
|
||||||
|
<INITIAL>--version {
|
||||||
|
return token::ARGUMENT_VERSION_LONG;
|
||||||
|
}
|
||||||
|
|
||||||
|
<INITIAL>--[[:alnum:]][[:alnum:]\-]* {
|
||||||
|
std::cerr << "Unknown argument: \'" << yytext << "\' found\n";
|
||||||
|
setResult(Result::wrongArgument);
|
||||||
|
}
|
||||||
|
|
||||||
|
<INITIAL>--.+ { /* show common error for invalid option */
|
||||||
|
std::cerr << "Invalid argument: \'" << yytext << "\' found\n";
|
||||||
|
setResult(Result::wrongArgument);
|
||||||
|
}
|
||||||
|
|
||||||
|
<SHORT_ARGUMENTS>. {
|
||||||
|
std::cerr << "Unknown argument \'-" << yytext << "\' found\n";
|
||||||
|
setResult(Result::wrongArgument);
|
||||||
|
}
|
||||||
|
|
||||||
|
<POSITIONAL_ARGUMENTS>.+ {
|
||||||
|
std::cerr << "Unknown positional argument \'" << yytext << "\' found\n";
|
||||||
|
setResult(Result::wrongArgument);
|
||||||
|
}
|
||||||
|
|
||||||
|
<INITIAL>[^\-].* { /* unknown positional argument */
|
||||||
|
std::cerr << "Unknown positional argument \'" << yytext << "\' found\n";
|
||||||
|
setResult(Result::wrongArgument);
|
||||||
|
}
|
||||||
|
|
||||||
|
<INITIAL>. {
|
||||||
|
std::cerr << "Invalid character \'" << yytext << "\' found\n";
|
||||||
|
setResult(Result::wrongArgument);
|
||||||
|
}
|
||||||
|
|
||||||
|
%%
|
||||||
|
|
||||||
|
namespace trueArgGrammar {
|
||||||
|
void Scanner::resetOnWrap() {
|
||||||
|
if (resetVal)
|
||||||
|
BEGIN(*resetVal);
|
||||||
|
|
||||||
|
resetVal = INITIAL;
|
||||||
|
}
|
||||||
|
}
|
||||||
91
examples/true/true.yy
Normal file
91
examples/true/true.yy
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
%skeleton "lalr1.cc"
|
||||||
|
%require "3.2"
|
||||||
|
%debug
|
||||||
|
%defines
|
||||||
|
%define api.namespace {trueArgGrammar}
|
||||||
|
%define api.parser.class {Parser}
|
||||||
|
|
||||||
|
%code requires{
|
||||||
|
namespace trueArgGrammar {
|
||||||
|
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 "trueArgGrammarDriver.hpp"
|
||||||
|
#include "trueArgGrammarScanner.hpp"
|
||||||
|
|
||||||
|
#undef yylex
|
||||||
|
#define yylex scanner.yylex
|
||||||
|
}
|
||||||
|
|
||||||
|
%define api.value.type variant
|
||||||
|
%define parse.assert
|
||||||
|
|
||||||
|
%token ARGUMENT_HELP_LONG
|
||||||
|
%token ARGUMENT_VERSION_LONG
|
||||||
|
|
||||||
|
%start ARGUMENTS
|
||||||
|
|
||||||
|
%%
|
||||||
|
|
||||||
|
ARGUMENTS
|
||||||
|
: %empty
|
||||||
|
| ARGUMENT_HELP {
|
||||||
|
std::cout
|
||||||
|
<< "Usage:\n"
|
||||||
|
<< "\n"
|
||||||
|
<< driver.getArgv()[0] << "\n"
|
||||||
|
<< driver.getArgv()[0] << " <option>" << "\n"
|
||||||
|
<< "\n"
|
||||||
|
<< "Arguments:\n"
|
||||||
|
<< "\n"
|
||||||
|
<< "option = --help | --version" << "\n"
|
||||||
|
<< "\n"
|
||||||
|
<< "--help display this help and exit\n"
|
||||||
|
<< "--version output version information and exit\n"
|
||||||
|
<< "\n"
|
||||||
|
<< "NOTE: your shell may have its own version of true, which usually supersedes the version described here."
|
||||||
|
<< std::endl;
|
||||||
|
driver.addArg(trueArgGrammar::SingleArg::help, {});
|
||||||
|
driver.setResult(Driver::Result::completedAction);
|
||||||
|
}
|
||||||
|
| ARGUMENT_VERSION {
|
||||||
|
std::cout
|
||||||
|
<< "true (GNU coreutils) 8.3.1"
|
||||||
|
<< std::endl;
|
||||||
|
driver.addArg(trueArgGrammar::SingleArg::version, {});
|
||||||
|
driver.setResult(Driver::Result::completedAction);
|
||||||
|
}
|
||||||
|
;
|
||||||
|
|
||||||
|
ARGUMENT_HELP
|
||||||
|
: ARGUMENT_HELP_LONG
|
||||||
|
;
|
||||||
|
|
||||||
|
ARGUMENT_VERSION
|
||||||
|
: ARGUMENT_VERSION_LONG
|
||||||
|
;
|
||||||
|
|
||||||
|
%%
|
||||||
|
|
||||||
|
void trueArgGrammar::Parser::error(const std::string& err_message)
|
||||||
|
{
|
||||||
|
std::cerr << "Error: " << err_message << '\n';
|
||||||
|
}
|
||||||
67
examples/true/trueArgGrammarDriver.cpp
Normal file
67
examples/true/trueArgGrammarDriver.cpp
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
#include <cassert>
|
||||||
|
#include <fstream>
|
||||||
|
|
||||||
|
#include "trueArgGrammarDriver.hpp"
|
||||||
|
#include "trueArgGrammarScanner.hpp"
|
||||||
|
|
||||||
|
namespace trueArgGrammar {
|
||||||
|
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 SingleArg arg, std::string value) {
|
||||||
|
singleArguments.insert_or_assign(arg, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<std::string> Driver::getArg(const SingleArg arg) const {
|
||||||
|
try {
|
||||||
|
return std::make_optional(singleArguments.at(arg));
|
||||||
|
} catch (const std::out_of_range& ignored) {
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Driver::setResult(Driver::Result result) {
|
||||||
|
this->result = result;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::map<SingleArg, std::string>& Driver::getSingleArgs() const {
|
||||||
|
return singleArguments;
|
||||||
|
}
|
||||||
|
|
||||||
|
char** Driver::getArgv() {
|
||||||
|
return argv;
|
||||||
|
}
|
||||||
|
} // namespace trueArgGrammar
|
||||||
50
examples/true/trueArgGrammarDriver.hpp
Normal file
50
examples/true/trueArgGrammarDriver.hpp
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "trueArgGrammarParser.tab.hh"
|
||||||
|
#include <iostream>
|
||||||
|
#include <map>
|
||||||
|
#include <optional>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace trueArgGrammar {
|
||||||
|
enum class SingleArg { help, version };
|
||||||
|
|
||||||
|
class Scanner;
|
||||||
|
|
||||||
|
class Driver {
|
||||||
|
public:
|
||||||
|
enum class Result { success, completedAction, wrongArgument };
|
||||||
|
|
||||||
|
Driver(int argc, char* argv[]);
|
||||||
|
virtual ~Driver();
|
||||||
|
|
||||||
|
Result parse();
|
||||||
|
|
||||||
|
void addArg(const SingleArg flag, const std::string value);
|
||||||
|
std::optional<std::string> getArg(const SingleArg flag) const;
|
||||||
|
void setResult(Result result);
|
||||||
|
|
||||||
|
const std::map<SingleArg, std::string>& getSingleArgs() const;
|
||||||
|
|
||||||
|
std::ostream& print(std::ostream& oss);
|
||||||
|
|
||||||
|
char** getArgv();
|
||||||
|
|
||||||
|
private:
|
||||||
|
int argc;
|
||||||
|
char** argv;
|
||||||
|
|
||||||
|
Result result = Result::success;
|
||||||
|
|
||||||
|
void parseHelper(std::istream& iss);
|
||||||
|
|
||||||
|
trueArgGrammar::Parser* parser = nullptr;
|
||||||
|
trueArgGrammar::Scanner* scanner = nullptr;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* specialised data store
|
||||||
|
*/
|
||||||
|
|
||||||
|
std::map<SingleArg, std::string> singleArguments;
|
||||||
|
};
|
||||||
|
} // namespace trueArgGrammar
|
||||||
30
examples/true/trueArgGrammarScanner.cpp
Normal file
30
examples/true/trueArgGrammarScanner.cpp
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
#include "trueArgGrammarScanner.hpp"
|
||||||
|
#include "trueArgGrammarDriver.hpp"
|
||||||
|
|
||||||
|
namespace trueArgGrammar {
|
||||||
|
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 trueArgGrammar
|
||||||
37
examples/true/trueArgGrammarScanner.hpp
Normal file
37
examples/true/trueArgGrammarScanner.hpp
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#if !defined(yyFlexLexerOnce)
|
||||||
|
# include <FlexLexer.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "trueArgGrammarDriver.hpp"
|
||||||
|
#include <cstring>
|
||||||
|
#include <optional>
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
|
namespace trueArgGrammar {
|
||||||
|
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(trueArgGrammar::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;
|
||||||
|
trueArgGrammar::Parser::semantic_type* yyval = nullptr;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
int yywrap() override;
|
||||||
|
};
|
||||||
|
} // namespace trueArgGrammar
|
||||||
Reference in New Issue
Block a user