Added first example.

This commit is contained in:
2019-09-19 14:11:48 +01:00
commit 8a292241e3
13 changed files with 451 additions and 0 deletions

3
examples/true/.ccls Normal file
View File

@@ -0,0 +1,3 @@
clang++
%cpp --std=c++17
-Igenerated

3
examples/true/.gitignore vendored Normal file
View File

@@ -0,0 +1,3 @@
*.tab.*
trueArgGrammarScannerDef.cpp
true

16
examples/true/Tupfile Normal file
View 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
View 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;
}

View 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
View 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
View 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';
}

View 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

View 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

View 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

View 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