Assume I have function that accept 1 callable F and variadic list of arguments:
template <class F, class ...Args>
void combine(F& f, Args&& ... args)
F may have any number of arguments from 1 to sizeof...(Args). So I need using c++17 to invoke F and after that just put not used args to std::cout like that
{
std::invoke(f, [some way to restrict args]);
std::cout << [The rest of args] << "\n";
}
The great problem is detect the number of arguments of F.
As pointed by Igor Tandetnik in a comment, this isn't possible in general, because the f func can be variadic, of a lambda with a template operator(), or a instance of a class/struct with multiple operator().
Anyway... in some cases you can detect the number of the arguments with something as follows
template <typename T>
struct num_args
: public num_args<decltype(&T::operator())>
{ };
template <typename R, typename ... Args>
struct num_args<R(*)(Args...)>
: public std::integral_constant<std::size_t, sizeof...(Args)>
{ };
// other specialization follows
At this point, you can write combine() to call an helper function with an appropriate couple of indexes and a tuple of arguments
template <typename F, typename ... Args>
void combine (F & func, Args && ... as)
{
constexpr auto n1 { num_args<F>::value };
constexpr auto n2 { sizeof...(Args) - n1 };
combine_helper(std::make_index_sequence<n1>{},
std::make_index_sequence<n2>{},
func,
std::forward_as_tuple(std::forward<Args>(as)...));
}
and the helper function can be simply as follows
template <std::size_t ... Is1, std::size_t ... Is2, typename F, typename T>
void combine_helper (std::index_sequence<Is1...>,
std::index_sequence<Is2...>,
F & func, T && t)
{
func(std::get<Is1>(std::forward<T>(t))...);
(std::cout << ... << std::get<sizeof...(Is1)+Is2>(std::forward<T>(t)))
<< '\n';
}
The following is a full compiling C++17 example
#include <iostream>
#include <utility>
#include <tuple>
template <typename T>
struct num_args
: public num_args<decltype(&T::operator())>
{ };
template <typename R, typename ... Args>
struct num_args<R(*)(Args...)>
: public std::integral_constant<std::size_t, sizeof...(Args)>
{ };
template <typename R, typename ... Args>
struct num_args<R(Args...)>
: public std::integral_constant<std::size_t, sizeof...(Args)>
{ };
template <typename R, typename C, typename ... Args>
struct num_args<R(C::*)(Args...)>
: public std::integral_constant<std::size_t, sizeof...(Args)>
{ };
template <typename R, typename C, typename ... Args>
struct num_args<R(C::*)(Args...) const>
: public std::integral_constant<std::size_t, sizeof...(Args)>
{ };
template <std::size_t ... Is1, std::size_t ... Is2, typename F, typename T>
void combine_helper (std::index_sequence<Is1...>,
std::index_sequence<Is2...>,
F & func, T && t)
{
func(std::get<Is1>(std::forward<T>(t))...);
(std::cout << ... << std::get<sizeof...(Is1)+Is2>(std::forward<T>(t)))
<< '\n';
}
template <typename F, typename ... Args>
void combine (F & func, Args && ... as)
{
constexpr auto n1 { num_args<F>::value };
constexpr auto n2 { sizeof...(Args) - n1 };
combine_helper(std::make_index_sequence<n1>{},
std::make_index_sequence<n2>{},
func,
std::forward_as_tuple(std::forward<Args>(as)...));
}
void func_1 (int a, int b, int c)
{ std::cout << "the product is: " << a*b*c << '\n'; }
int main()
{
auto extra { 100 };
auto func_2 { [&](int a, int b, int c, int d)
{ std::cout << "the extra sum is: " << extra+a+b+c+d << '\n'; } };
combine(func_1, 1, 2, 3, 4, 5, 6);
combine(func_2, 1, 2, 3, 4, 5, 6);
}
I have tried different approaches and asked several specific questions regarding sub-problems of my requirement here. But my solution doesn't really work as expected, so I am taking a step back and ask here from a more general perspective. Please bear in mind that I am not a C++ pro. Not a beginner either, but I am still learning the language.
So, I have the following requirement. I need to read-in text files, which contain conditions, like "Greater" or "Equals", all like functions that return boolean values. The text files also include parameters for these conditions. Note that these parameters can be of different types (integer, decimal, etc.), and each such condition can take different number of parameters (e.g. "Equals" takes 2 paramters, while "Between" would take 3 parameters). Thus the file could look something like this:
Greater, 2, 3
Greater, 2.4, 1.0
Equals, true, true
Between, 20, 10, 30
The logic to read-in that file and parsing it is already done. Now I need to "concatenate" all these boolean functions with their parameters and check if all of them are true.
So I thought I would create functions or a class with static methods to represent these boolean test functions, then create a map of function pointers to these functions, mapped by their name. At runtime I would then read in the file, call the respective function pointer and pass-in the paramteres. That seemed easy to me, but in reality I am struggling mostly with the fact that these boolean functions can take a different number of parameters, and that these can be of different types.
Can you recommend a way to tackle that requirement in C++? I am not asking for a complete solution, but for a suitable C++ approach, or a guideline I could follow. Thanks in advance!
Here's a quick&dirty Spirit grammar for the input shown.
UPDATE
Now added invocation and implementation of the predicate functions (GreaterImpl and EqualsImpl).
I tried to be smart allowing comparisons between mixed arithmetic types (but not e.g. Greater(bool,string). If you compare incompatible types you will get a std::runtime_error exception that provides type feedback to the caller.
Live On Coliru
#include <deque>
#include <boost/spirit/include/qi.hpp>
#include <boost/fusion/adapted/struct.hpp>
namespace qi = boost::spirit::qi;
namespace Ast {
using Value = boost::variant<int, double, bool, std::string>;
using BinaryPred = std::function<bool(Value, Value)>;
using TernaryPred = std::function<bool(Value, Value, Value)>;
using Pred = boost::variant<BinaryPred, TernaryPred>;
using Values = std::vector<Value>;
struct Invocation { Pred pred; Values args; };
using Invocations = std::vector<Invocation>;
}
BOOST_FUSION_ADAPT_STRUCT(Ast::Invocation, pred, args)
namespace Predicates {
using Ast::Value;
struct Greater : boost::static_visitor<bool> {
bool operator()(Value const& a, Value const& b) const {
return boost::apply_visitor(*this, a, b);
}
template <typename T> bool operator()(T const& a, T const& b) const { return std::greater<T>{}(a, b); }
template <typename T, typename U>
typename std::enable_if<std::is_arithmetic<T>() && std::is_arithmetic<U>(), bool>::type
operator()(T const& a, U const& b) const { return a > b; }
template <typename T, typename U>
typename std::enable_if<not (std::is_arithmetic<T>() && std::is_arithmetic<U>()), bool>::type
operator()(T const&, U const&) const { throw std::runtime_error("Type Mismatch"); }
};
struct Equals : boost::static_visitor<bool> {
bool operator()(Value const& a, Value const& b) const {
return boost::apply_visitor(*this, a, b);
}
template <typename T> bool operator()(T const& a, T const& b) const { return std::equal_to<T>{}(a, b); }
template <typename T, typename U, typename enable = typename std::enable_if<std::is_arithmetic<T>() && std::is_arithmetic<U>()>::type >
bool operator()(T const& a, U const& b) const { return a == b; }
template <typename T, typename U>
typename std::enable_if<not (std::is_arithmetic<T>() && std::is_arithmetic<U>()), bool>::type
operator()(T const&, U const&) const { throw std::runtime_error("Type Mismatch"); }
};
struct Between {
bool operator()(Value const& v, Value const& lower, Value const& upper) const {
return Greater{}(v,lower) && Greater{}(upper,v);
}
};
}
static inline bool evaluate(Ast::Invocation const& i) {
struct Invoker {
using result_type = bool;
Ast::Values const& args;
result_type operator()(Ast::BinaryPred const& p) const {
if (args.size() != 2) throw std::runtime_error("Arity Mismatch");
return p(args.at(0), args.at(1));
}
result_type operator()(Ast::TernaryPred const& p) const {
if (args.size() != 3) throw std::runtime_error("Arity Mismatch");
return p(args.at(0), args.at(1), args.at(2));
}
};
return boost::apply_visitor(Invoker{i.args}, i.pred);
}
template <typename It>
struct Grammar : qi::grammar<It, Ast::Invocations()> {
Grammar() : Grammar::base_type(start) {
using namespace qi;
start = skip(blank) [ invocation % eol ];
invocation = pred >> -("," >> args);
args = arg % ",";
arg = my_double_ | qi::int_ | qi::bool_ | lexeme['"' > *~char_('"') > '"'];
}
private:
struct pred_t : qi::symbols<char, Ast::Pred> {
pred_t() {
this->add
("Greater", Predicates::Greater{})
("Equals", Predicates::Equals{})
("Between", Predicates::Between{})
;
}
} const pred;
qi::rule<It, Ast::Invocations()> start;
qi::rule<It, Ast::Invocation(), qi::blank_type> invocation;
qi::rule<It, Ast::Values(), qi::blank_type> args;
qi::rule<It, Ast::Value(), qi::blank_type> arg;
qi::real_parser<double, qi::strict_real_policies<double> > my_double_;
};
#include <sstream>
int main() {
using It = boost::spirit::istream_iterator;
std::deque<std::string> testcases {
// one multiline case:
"Between, 20, 10, 30\n"
"Between, NaN, NaN, NaN\n"
"Between, \"q\", \"a\", \"z\""
};
// many single line cases for easy test reporting
for (std::string op : {"Greater","Equals"})
for (auto rhs : { "42", "0.0", "true", "\"hello\"" })
for (auto lhs : { "41", "-0.0", "false", "\"bye\"" }) {
testcases.push_front(op + ", " + lhs + ", " + rhs);
}
for (auto testcase : testcases) {
std::cout << "--- Testcase '" << testcase << "' -> ";
std::istringstream iss(testcase);
It f(iss >> std::noskipws), l;
Ast::Invocations parsed;
if (qi::parse(f, l, Grammar<It>(), parsed)) {
for (auto& invocation : parsed) {
try {
std::cout << std::boolalpha << evaluate(invocation) << "; ";
} catch(std::exception const& e) {
std::cout << e.what() << "; ";
}
}
std::cout << "\n";
} else {
std::cout << "Parse failed\n";
}
if (f != l)
std::cout << "Remaining unparsed input: '" << std::string(f,l) << "'\n";
}
}
Prints:
--- Testcase 'Equals, "bye", "hello"' -> false;
--- Testcase 'Equals, false, "hello"' -> Type Mismatch;
--- Testcase 'Equals, -0.0, "hello"' -> Type Mismatch;
--- Testcase 'Equals, 41, "hello"' -> Type Mismatch;
--- Testcase 'Equals, "bye", true' -> Type Mismatch;
--- Testcase 'Equals, false, true' -> false;
--- Testcase 'Equals, -0.0, true' -> false;
--- Testcase 'Equals, 41, true' -> false;
--- Testcase 'Equals, "bye", 0.0' -> Type Mismatch;
--- Testcase 'Equals, false, 0.0' -> true;
--- Testcase 'Equals, -0.0, 0.0' -> true;
--- Testcase 'Equals, 41, 0.0' -> false;
--- Testcase 'Equals, "bye", 42' -> Type Mismatch;
--- Testcase 'Equals, false, 42' -> false;
--- Testcase 'Equals, -0.0, 42' -> false;
--- Testcase 'Equals, 41, 42' -> false;
--- Testcase 'Greater, "bye", "hello"' -> false;
--- Testcase 'Greater, false, "hello"' -> Type Mismatch;
--- Testcase 'Greater, -0.0, "hello"' -> Type Mismatch;
--- Testcase 'Greater, 41, "hello"' -> Type Mismatch;
--- Testcase 'Greater, "bye", true' -> Type Mismatch;
--- Testcase 'Greater, false, true' -> false;
--- Testcase 'Greater, -0.0, true' -> false;
--- Testcase 'Greater, 41, true' -> true;
--- Testcase 'Greater, "bye", 0.0' -> Type Mismatch;
--- Testcase 'Greater, false, 0.0' -> false;
--- Testcase 'Greater, -0.0, 0.0' -> false;
--- Testcase 'Greater, 41, 0.0' -> true;
--- Testcase 'Greater, "bye", 42' -> Type Mismatch;
--- Testcase 'Greater, false, 42' -> false;
--- Testcase 'Greater, -0.0, 42' -> false;
--- Testcase 'Greater, 41, 42' -> false;
--- Testcase 'Between, 20, 10, 30
Between, NaN, NaN, NaN
Between, "q", "a", "z"' -> true; false; true;
boost variant is the easiest way IMHO:
#include <boost/variant.hpp>
#include <boost/operators.hpp>
#include <string>
#include <iostream>
#include <iomanip>
// define the concept of equality in my scripting language
struct is_equal : boost::static_visitor<bool>
{
// x == x is easy
template<class T>
bool operator()(const T& l, const T& r) const {
return l == r;
}
// define the concept of comparing strings to integers
bool operator()(const std::string& l, const int& r) const {
return l == std::to_string(r);
}
// and integers to strings
bool operator()(const int& l, const std::string& r) const {
return (*this)(r, l);
}
};
struct is_less : boost::static_visitor<bool>
{
// x == x is easy
template<class T>
bool operator()(const T& l, const T& r) const {
return l < r;
}
// define the concept of comparing strings to integers
bool operator()(const std::string& l, const int& r) const {
return std::stoi(l) < r;
}
// and integers to strings
bool operator()(const int& l, const std::string& r) const {
return l < std::stoi(r);
}
};
struct emit : boost::static_visitor<std::ostream&>
{
emit(std::ostream& os) : os_(os) {}
// x == x is easy
template<class T>
std::ostream& operator()(const T& l) const {
return os_ << l;
}
std::ostream& operator()(const std::string& s) const {
return os_ << std::quoted(s);
}
std::ostream& os_;
};
struct scriptable_value
: boost::less_than_comparable<scriptable_value>
, boost::equality_comparable<scriptable_value>
{
using variant_type = boost::variant<std::string, int>;
scriptable_value(std::string v) : variant_(std::move(v)) {}
scriptable_value(int v) : variant_(v) {}
variant_type const& as_variant() const {
return variant_;
}
private:
variant_type variant_;
};
bool operator==(scriptable_value const& l, scriptable_value const& r)
{
return boost::apply_visitor(is_equal(), l.as_variant(), r.as_variant());
}
bool operator<(scriptable_value const& l, scriptable_value const& r)
{
return boost::apply_visitor(is_less(), l.as_variant(), r.as_variant());
}
std::ostream& operator<<(std::ostream& os, scriptable_value const& r)
{
return boost::apply_visitor(emit(os), r.as_variant());
}
int main()
{
auto x = scriptable_value(10);
auto y = scriptable_value("10");
auto x2 = scriptable_value(9);
auto y2 = scriptable_value("9");
std::cout << x << " == " << y << " : " << std::boolalpha << (x == y) << std::endl;
std::cout << x << " != " << y << " : " << std::boolalpha << (x != y) << std::endl;
std::cout << x << " == " << y2 << " : " << std::boolalpha << (x == y2) << std::endl;
std::cout << x << " != " << y2 << " : " << std::boolalpha << (x != y2) << std::endl;
std::cout << x << " < " << y << " : " << std::boolalpha << (x < y) << std::endl;
std::cout << x << " >= " << y << " : " << std::boolalpha << (x >= y) << std::endl;
std::cout << x << " < " << y2 << " : " << std::boolalpha << (x < y2) << std::endl;
std::cout << x << " >= " << y2 << " : " << std::boolalpha << (x >= y2) << std::endl;
std::cout << x << " == " << x2 << " : " << std::boolalpha << (x == x2) << std::endl;
std::cout << x << " != " << x2 << " : " << std::boolalpha << (x != x2) << std::endl;
}
expected output:
10 == "10" : true
10 != "10" : false
10 == "9" : false
10 != "9" : true
10 < "10" : false
10 >= "10" : true
10 < "9" : false
10 >= "9" : true
10 == 9 : false
10 != 9 : true
Your fundamental issue is that C++ is a statically-typed language. General-purpose programming languages tend to fall into two rough categories: statically and dynamically typed. In a dynamically-typed language, like Perl, the type of an object is determined at runtime. In a statically-typed language, like C++, the type of an object is specified at compile-time.
That doesn't mean that this is not possible in C++ in a type-safe manner. It is, but it requires some work.
The usual approach is to encapsulate all types into classes that are derived from some base class that defines virtual methods, with the subclasses implementing them. Let's use just ints and floats.
// Forward declarations
class FloatNumber;
class IntNumber;
class Number {
// virtual methods to be defined later.
};
class FloatNumber : public Number {
float value;
// Implements the virtual methods for float values.
};
class IntNumber : public Number {
int value;
// Implements the virtual methods for int values.
};
Now, you can implement basic operations. In the Number base class, you define the conversion methods:
virtual FloatNumber asFloat() const = 0;
virtual IntNumber asInt() const = 0;
In each subclass you will implement these in the obvious manner, returning either *this, if it's the same type, or constructing the other subclass and returning the newly-constructed class.
Now, you can implement basic operations. Say, equals:
virtual bool equals(const Number &other) const =0;
Now, you get to implement this virtual method in each subclass. In FloatNumber::equals(), for example, you would call other.asFloat(), and compare its val to its own val. Ditto for IntNumber::equals(). If both Numbers that are compared are of the same type, this ends up comparing the two values directly; otherwise an automatic type conversion takes place.
Now, this is not a perfect approach, since if the first number is an IntNumber, the FloatNumber ends up getting down-converted to an int, and you really want the conversion to go the other way. There are classical ways to solve this also, in a type-safe manner. But first, you should get this basic approach implemented first, and then worry about handling the various corner-cases.
In this manner you can proceed and build up a class hierarchy that implements generic operation on numbers. This is likely more work than you expected, but this is how to properly do this kind of thing in C++, in a completely type-safe manner. Modern C++ compilers are quite efficient, and the end result will be quite small, and compact.
The final step for you is to read the file, parse the values, and have a simple lookup table that maps "Equals" to the "equals" method, and so on...
I am parsing the following file:
BEGIN BLOCK BLK_ROWDEC
NAME cell_rowdec
SIZE UNI_rowdecSize
ITERATE itr_rows
DIRECTION lgDir_rowdec
STRAP STRD1,STRD3,STRD2
WRAP WRD1
VIA VIAB,VIAC,VIAD
ENDS BLK_ROWDEC
My flex and bison file are as follows:
lexa.l
%{
#include <iostream>
#include <stdio.h>
const char s[2] = " ";
#include "yacc.tab.h"
char *token;
#define YY_DECL extern "C" int yylex()
int line_num = 1;
using namespace std;
%}
DOT "."
COLON ":"
SEMICOLON ";"
COMMA ","
ANGLE_LEFT "<"
ANGLE_RIGHT ">"
AT "#"
EQUAL "="
SQUARE_OPEN "["
SQUARE_CLOSE [^\\]"]"
OPENBRACE "\("
CLOSEBRACE "\)"
QUOTE "\""
QUOTE_OPEN "\""
QUOTE_CLOSE [^\\]"\""
SPACE " "
TAB "\t"
CRLF "\r\n"
QUOTED_PAIR "\\"[^\r\n]
DIGIT [0-9]
ALPHA [a-zA-Z]
QTEXT [0-9a-zA-Z!#$%&'()*+,\-.\/:;<=>?#\[\]^_`{|}~]
%%
[ \t] ;
^BEGIN(.*)\r?\n+\s*BEGIN(.*) { printf("\nError : two continous BEGIN is not allowed : "); }
^ENDS(.*)\r?\n+\s*ENDS(.*) { printf("\nError : two continous END is not allowed : \n"); }
NAME { yylval.sval = strdup(yytext);
return TOK_NAME; }
SIZE { yylval.sval = strdup(yytext);
return TOK_SIZE; }
ITERATE { yylval.sval = strdup(yytext);
return TOK_ITERATE; }
DIRECTION { yylval.sval = strdup(yytext);
return TOK_DIRECTION; }
STRAP { yylval.sval = strdup(yytext);
return TOK_STRAP; }
WRAP { yylval.sval = strdup(yytext);
return TOK_WRAP; }
VIA { yylval.sval = strdup(yytext);
return TOK_VIA; }
ENDS { yylval.sval = strdup(yytext);
return TOK_END; }
BEGIN { yylval.sval = strdup(yytext);
return TOK_BEGIN; }
BLOCK { yylval.sval = strdup(yytext);
return TOK_BLOCK; }
[a-zA-Z0-9_,]+ { yylval.sval = strdup(yytext);
return TOK_STRING; }
{SPACE}* { return TOK_SPACE; }
^ENDS(.*)$ {}
^{CRLF} { return TOK_EMPTY_LINE; }
{CRLF} {}
. {}/* ignore unknown chars */
\n { ++line_num; return ENDL; }
yacca.y
%{
#include <cstdio>
#include <cstring>
#include <iostream>
#include <stdio.h>
#define YYDEBUG 1
using namespace std;
extern "C" int yylex();
extern "C" FILE *yyin;
extern int line_num;
void yyerror(const char* s);
%}
// Symbols.
%union
{
char* sval;
};
%token <sval> TOK_NAME
%token <sval> TOK_SIZE
%token <sval> TOK_STRING
%token <sval> TOK_ITERATE
%token <sval> TOK_DIRECTION
%token <sval> TOK_STRAP
%token <sval> TOK_WRAP
%token <sval> TOK_VIA
%token <sval> TOK_EMPTY_LINE
%token <sval> TOK_BLOCK
%token <sval> TOK_LINE
%token <sval> TOK_BEGIN
%token <sval> TOK_END
%token TOK_SPACE
%token END ENDL
%%
language : program ;
program : block
| program block
;
block : TOK_BEGIN TOK_BLOCK TOK_SPACE TOK_STRING blockcontents TOK_END TOK_SPACE TOK_STRING
{
cout << endl << "SHAILAVI" << $4 << " ";
}
;
blockcontents : item
| blockcontents item
;
item : TOK_SPACE TOK_NAME TOK_SPACE TOK_STRING
{
cout << endl << "Value:" << $2 << "->" << $4 << " ";
}
| TOK_SPACE TOK_SIZE TOK_SPACE TOK_STRING { cout << $2 << "->" << $4 << " "; }
| TOK_SPACE TOK_ITERATE TOK_SPACE TOK_STRING { cout << $2 << "->" << $4 << " "; }
| TOK_SPACE TOK_DIRECTION TOK_SPACE TOK_STRING { cout << $2 << "->" << $4 << " " << endl; }
| TOK_SPACE TOK_STRAP TOK_SPACE TOK_STRING { cout << "ref:" << $2 << "->" << $4 << " "; }
| TOK_SPACE TOK_WRAP TOK_SPACE TOK_STRING { cout << $2 << "->" << $4 << " "; }
| TOK_SPACE TOK_VIA TOK_SPACE TOK_STRING { cout << $2 << "->" << $4 << " " << endl; }
;
%%
int main(void) {
FILE * pt = fopen("file", "r" );
if(!pt)
{
cout << "Bad Input.Noexistant file" << endl;
return -1;
}
yyin = pt;
do
{
yydebug = 1;
yyparse();
}while (!feof(yyin));
}
void yyerror(const char *s) {
cout << "parse error on line " << line_num << "! Message: " << s << endl;
exit(-1);
}
#include "lex.yy.c"
Compilation steps:
flex lexa.l
bison -d yacca.y
g++ yacca.tab.c -lfl -o scanner.exe
At the time of execution it gives syntax error near blockcontents
Please help me to identify the mistake I have done.
Thank You so much.
It took me a while, but I've found the flaw.
In your lexer, you skip all sequences of tabs and blanks (first rule).
But your parser expects white space every now and then. Hence the syntax error.
Since you don't do anything with the white space, simply eat them within the lexer (as you already do now actually, but it is better to eliminate the {SPACE}* rule too) and eliminate the TOK_SPACE in the parser.
---- edit to give some hints ----
What I did to track down the bug is:
make the lexer verbose
I added (hash signs omitted; it confuses the renderer for some reason)
#ifdef DEBUG
#define RETURN(x) cerr << "\n--> found " << #x << "\n"; return x;
#else
#define RETURN(x) return x;
#endif
and replaced all "return something" by RETURN(something)
I compile the bison/flex files separately and link them afterwards
flex lexa.l && \
bison -d yacca.y && \
g++ -c -DDEBUG -I . lex.yy.c && \
g++ -c -I . yacca.tab.c && \
g++ lex.yy.o yacca.tab.o -o scanner
(working on linux here)
As requested the working example
%{
#include <cstdio>
#include <cstring>
#include <iostream>
#include <stdio.h>
#define YYDEBUG 1
using namespace std;
extern "C" int yylex();
extern "C" FILE *yyin;
extern int line_num;
void yyerror(const char* s);
%}
// Symbols.
%union
{
char* sval;
};
%token TOK_NAME
%token TOK_SIZE
%token TOK_STRING
%token TOK_ITERATE
%token TOK_DIRECTION
%token TOK_STRAP
%token TOK_WRAP
%token TOK_VIA
%token TOK_EMPTY_LINE
%token TOK_BLOCK
%token TOK_LINE
%token TOK_BEGIN
%token TOK_END
%token END ENDL
%%
language : program ;
program : block
| program block
;
block : TOK_BEGIN TOK_BLOCK TOK_STRING blockcontents TOK_END TOK_STRING
{
cout << endl << "SHAILAVI" << $3 << " ";
}
;
blockcontents : item
| blockcontents item
;
item : TOK_NAME TOK_STRING { cout << endl << "Value:" << $1 << "->" << $2 << " "; }
| TOK_SIZE TOK_STRING { cout << $1 << "->" << $2 " << $2 " << $2 " << $2 << " "; }
| TOK_WRAP TOK_STRING { cout << $1 << "->" << $2 << " "; }
| TOK_VIA TOK_STRING { cout << $1 << "->" << $2 << " " << endl; }
;
%%
int main(void) {
FILE * pt = fopen("./input", "r" );
if(!pt)
{
cout << "Bad Input.Nonexistent file" << endl;
return -1;
}
yyin = pt;
do
{
yydebug = 1;
yyparse();
}while (!feof(yyin));
}
void yyerror(const char *s) {
cout << "parse error on line " << line_num << "! Message: " << s << endl;
exit(-1);
}
extern "C" int yywrap()
{
return (1 == 1);
}
And the lexer
%{
#include
#include
const char s[2] = " ";
#include "yacca.tab.h"
char *token;
#define YY_DECL extern "C" int yylex()
int line_num = 1;
#ifdef DEBUG
#define RETURN(x) cerr << "\n--> found " << #x << "\n"; return x;
#else
#define RETURN(x) return x;
#endif
using namespace std;
%}
DOT "."
COLON ":"
SEMICOLON ";"
COMMA ","
ANGLE_LEFT ""
AT "#"
EQUAL "="
SQUARE_OPEN "["
SQUARE_CLOSE [^\\]"]"
OPENBRACE "\("
CLOSEBRACE "\)"
QUOTE "\""
QUOTE_OPEN "\""
QUOTE_CLOSE [^\\]"\""
SPACE " "
TAB "\t"
CRLF "\r\n"
QUOTED_PAIR "\\"[^\r\n]
DIGIT [0-9]
ALPHA [a-zA-Z]
QTEXT [0-9a-zA-Z!#$%&'()*+,\-.\/:;?#\[\]^_`{|}~]
/* [ \t] ; */
%%
^BEGIN(.*)\r?\n+\s*BEGIN(.*) { printf("\nError : two continous BEGIN is not allowed : "); }
^ENDS(.*)\r?\n+\s*ENDS(.*) { printf("\nError : two continous END is not allowed : \n"); }
NAME { yylval.sval = strdup(yytext);
RETURN(TOK_NAME); }
SIZE { yylval.sval = strdup(yytext);
RETURN(TOK_SIZE); }
ITERATE { yylval.sval = strdup(yytext);
RETURN(TOK_ITERATE); }
DIRECTION { yylval.sval = strdup(yytext);
RETURN(TOK_DIRECTION); }
STRAP { yylval.sval = strdup(yytext);
RETURN(TOK_STRAP); }
WRAP { yylval.sval = strdup(yytext);
RETURN(TOK_WRAP); }
VIA { yylval.sval = strdup(yytext);
RETURN(TOK_VIA); }
ENDS { yylval.sval = strdup(yytext);
RETURN(TOK_END); }
BEGIN { yylval.sval = strdup(yytext);
RETURN(TOK_BEGIN); }
BLOCK { yylval.sval = strdup(yytext);
RETURN(TOK_BLOCK); }
[a-zA-Z0-9_,]+ { yylval.sval = strdup(yytext); RETURN(TOK_STRING); }
^ENDS(.*)$ {}
^{CRLF} { RETURN(TOK_EMPTY_LINE); }
{CRLF} {}
. {}/* ignore unknown chars */
\n { ++line_num; /* RETURN(ENDL); */ }
There's only one problem left. It doesn't really like the EOF. I'll leave that as an exercise.