Added first example.
This commit is contained in:
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