WIP concrete/unrestricted with Bisonc++

git-svn-id: https://cppannotations.svn.sourceforge.net/svnroot/cppannotations/trunk@615 f6dd340e-d3f9-0310-b409-bdd246841980
This commit is contained in:
Frank B. Brokken 2012-01-09 17:45:24 +00:00
parent 3ab434a93e
commit 3c45354ff5
25 changed files with 557 additions and 11 deletions

View file

@ -0,0 +1,99 @@
Typedefs are commonly defined using the tt(typedef) keyword. In addition,
C++11 allows the ti(using) keyword to associate a type and and identifier, but
different from ti(typedef) it does em(not) define a type. In practice
tt(typedef) and tt(using) can be used interchangeably.
In addition to function and class templates, C++11 also uses templates to
define an alias for a set of types. This is called a emi(template
alias). Template aliases can be specialized. The name of a template alias
spacialization is a type name.
The name of a template alias is a template name.
A template argument for a template template parameter is either a class
template or it is a template alias (expressed as 'id expression'
Alias declarations cannot be (partially, explicitly) specialized. Template
aliases can be specialized.
Examples:
verb(
template <typename T> struct X {...};
template <typename T>
using VecT = vector<T, Alloc<T>>;
Vec<int> vi; // same as vector<int, Alloc<int>> vi;
--------------------
template alias (formerly known as "template typedef")
How can we make a template that's "just like another template" but possibly with a couple of template arguments specified (bound)? Consider:
template<class T>
using Vec = std::vector<T,My_alloc<T>>; // standard vector using my allocator
Vec<int> fib = { 1, 2, 3, 5, 8, 13 }; // allocates elements using My_alloc
vector<int,My_alloc<int>> verbose = fib; // verbose and fib are of the same type
The keyword using is used to get a linear notation "name followed by what it refers to." We tried with the conventional and convoluted typedef solution, but never managed to get a complete and coherent solution until we settled on a less obscure syntax.
Specialization works (you can alias a set of specializations but you cannot specialize an alias) For example:
template<int>
struct int_exact_traits { // idea: int_exact_trait<N>::type is a type with exactly N bits
typedef int type;
};
template<>
struct int_exact_traits<8> {
typedef char type;
};
template<>
struct int_exact_traits<16> {
typedef char[2] type;
};
// ...
template<int N>
using int_exact = typename int_exact_traits<N>::type; // define alias for convenient notation
int_exact<8> a = 7; // int_exact<8> is an int with 8 bits
In addition to being important in connection with templates, type aliases can also be used as a different (and IMO better) syntax for ordinary type aliases:
typedef void (*PFD)(double); // C style
using PF = void (*)(double); // using plus C-style type
using P = [](double)->void; // using plus suffix return type
-------------------------------------------------
template <typename T>
using Dictionary = std::map< std::string, T >;
Dictionary<int> ints;
ints[ "one" ] = 1;
ints[ "two" ] = 2;
---------------------- the really useful case:
#include <vector>
template <typename Type, template <typename> class Container>
struct Wrapper: public Container<Type>
{};
template <typename Type>
using VT = std::vector<Type>;
int main()
{
Wrapper<int, VT> wi;
// Wrapper<int, std::vector> wi2; ERR!
}

View file

@ -145,8 +145,8 @@ includefile(concrete/bisonflex)
subsubsect(The scanner using a polymorphic semantic value type)
includefile(concrete/semscanner.yo)
lsubsect(BISONSEM)(Using unrestricted unions semantic values with Bisonc++)
includefile(concrete/unrestricted) // TODO
subsect(Using unrestricted unions as semantic values (C++11))
includefile(concrete/unrestricted)

View file

@ -2,17 +2,15 @@ Bisonc++ may use polymorphic semantic values. How this is realized is covered
in this section. The described method is a direct result of a suggestion
initially brought forward by Dallas A. Clement in September 2007.
One may wonder why a tt(union) is still used by Bisonc++ as bf(C++) offers
One may wonder why tt(union)s are still used by Bisonc++, as bf(C++) offers
inherently superior constructs to combine multiple types into one type. The
bf(C++) way to combine types into one type is by defining a polymorphic base
class and a series of derived classes implementing the alternative data
types. Bisonc++ supports the tt(union) approach (and the unrestricted unions
with C++11) for various (e.g., backward compatibility)
reasons. bi(Bison) and bi(bison++) both support the tt(%union) directive.
support by C++11 for unrestricted unions may result in tt(union)s being
re-evaluated, but bf(C++) traditionally offers a good alternative for the use
of tt(union)s: a polymorphic base class and a series of derived classes
implementing the alternative data types.
An alternative to using a tt(union) is using a polymorphic base class. Such a
class is developed below (the class tt(Base)). As it is a polymorphic
base class it has the following characteristics:
Below, a polymorphic base class (the class tt(Base)) is developed. As it is a
polymorphic base class it has the following characteristics:
itemization(
it() Its destructor is virtual (and has a default implementation);
it() Objects of the derived classes may be obtained from a

View file

@ -0,0 +1,15 @@
Unions revived at the introduction of C++11's unrestricted unions.
In this section we'll have a look at how to used them as
semantic values in a Bisonc++ generated parser.
Bisonc++'s semantic value must support
itemization(
itt(int) values;
itt(textual) values of type tt(string);
)
Here is tt(Semantic)'s interface:
verbinclude(poly/semantic/semantic.h)

View file

@ -0,0 +1 @@
semantic

View file

@ -0,0 +1,112 @@
// Inspect the following #defines. Change them to taste. If you don't
// need a particular option, change its value into an empty string
// should commands be echoed (ON) or not (OFF) ?
#define USE_ECHO ON
// The final program and source containing main():
// ===============================================
// define the name of the program to create:
#define BINARY "../../poly"
// define the name of the source containing main():
#define MAIN "main.cc"
// #defines used for compilation and linking:
// ==========================================
// define the compiler to use:
#define COMPILER "g++"
// define the compiler options to use:
#define COMPILER_OPTIONS "-g --std=c++0x -Wall -O2"
// define the pattern to locate sources in a directory:
#define SOURCES "*.cc"
// define the options used for linking:
#define LINKER_OPTIONS "-s"
// define any additional libraries BINARY may need:
#define ADD_LIBRARIES "bobcat"
// define any additional paths (other than the standard paths) the
// additional libraries are located in:
#define ADD_LIBRARY_PATHS ""
// #defines used for the final product:
// ====================================
#define BIN_INSTALL "/usr/local/bin"
// Some advanced #defines, used to create parsers and lexical scanners
// ===================================================================
// Lexical Scanner section
// =======================
// Should a lexical scanner be constructed? If so, define the subdirectory
// containing the scanner's specification file.
#define SCANNER_DIR "scanner"
// What is the program generating the lexical scanner?
#define SCANGEN "flex"
// Flags to provide SCANGEN with:
#define SCANFLAGS "-I"
// Name of the lexical scanner specification file
#define SCANSPEC "lexer"
// Name of the file generated by the lexical scanner
#define SCANOUT "yylex.cc"
// Parser section
// ==============
// Should a parser be constructed? If so, define the subdirectory
// containing the parser's specification file
#define PARSER_DIR "parser"
// If a parser must be constructed, should the script (provided in the
// skeleton file parser/gramspec/grambuild) `parser/gramspec/grambuild'
// **NOT** be called? If it must NOT be called, comment out the following
// #define directive:
// #define GRAMBUILD
// What it the program generating a parser?
#define PARSGEN "bisonc++"
// What it the grammar specificication file?
#define PARSSPEC "grammar"
// Flags to provide PARSGEN with:
#define PARSFLAGS "-V -l"
// Name of the file generated by the parser generator containing the
// parser function
#define PARSOUT "parse.cc"
// Additional defines, which should normally not be modified
// =========================================================
// Directory below this directory to contain temporary results
#define TMP_DIR "tmp"
// Local program library to use (change to an empty string if you want to
// use the object modules themselves, rather than a library)
#define LIBRARY "modules"
// The extension of object modules:
#define OBJ_EXT ".o"
// below #define DEFCOM "program" or "library" may be added by icmstart
#define DEFCOM "program"

View file

@ -0,0 +1,4 @@
abc(def);
xyz = 5;

View file

@ -0,0 +1,12 @@
#include "main.ih"
int main(int argc, char **argv)
{
Parser parser;
parser.setDebug(argc == 1);
parser.parse();
return 0;
}

View file

@ -0,0 +1,6 @@
#include <iostream>
#include <string>
#include <vector>
#include "parser/preinclude.h"
#include "parser/parser.h"

View file

@ -0,0 +1,33 @@
%class-name Parser
%filenames parser
%parsefun-source parse.cc
%scanner ../scanner/scanner.h
// %debug
%baseclass-preinclude preinclude.h
%stype Semantic
%token INT IDENTIFIER
%%
rules:
rules rule
|
rule
;
rule:
IDENTIFIER '(' IDENTIFIER ')' ';'
{
cout << $1 << " " << $3 << '\n';
}
|
IDENTIFIER '=' INT ';'
{
cout << $1 << " " << $3 << '\n';
}
;

View file

@ -0,0 +1,53 @@
#ifndef Parser_h_included
#define Parser_h_included
// $insert baseclass
#include "parserbase.h"
// $insert scanner.h
#include "../scanner/scanner.h"
#undef Parser
class Parser: public ParserBase
{
// $insert scannerobject
Scanner d_scanner;
public:
Parser();
int parse();
private:
void error(char const *msg); // called on (syntax) errors
int lex(); // returns the next token from the
// lexical scanner.
void print(); // use, e.g., d_token, d_loc
// support functions for parse():
void executeAction(int ruleNr);
void errorRecovery();
int lookup(bool recovery);
void nextToken();
};
inline void Parser::error(char const *msg)
{
std::cerr << msg << '\n';
}
// $insert lex
inline int Parser::lex()
{
return d_scanner.yylex();
}
inline void Parser::print() // use d_token, d_loc
{}
inline Parser::Parser()
:
d_scanner(&d_val__)
{}
#endif

View file

@ -0,0 +1,6 @@
// Include this file in the sources of the class Parser.
// $insert class.h
#include "parser.h"
using namespace std;

View file

@ -0,0 +1,6 @@
#ifndef INCLUDED_PREINCLUDE_H_
#define INCLUDED_PREINCLUDE_H_
#include "../semantic/semantic.h"
#endif

View file

@ -0,0 +1,34 @@
%{
#define SKIP_FLEXLEXER_
#include "scanner.ih"
#include "../parser/preinclude.h"
#include "../parser/parserbase.h"
%}
%option yyclass="Scanner" outfile="yylex.cc"
%option c++ 8bit warn noyywrap yylineno
%option debug
%%
[ \t]+ // skip white space
\n // same
[0-9]+ {
*d_semval = Semantic(Semantic::INT, yytext);
return Parser::INT;
}
[a-zA-Z_][a-zA-Z0-9_]* {
*d_semval = Semantic(Semantic::IDENTIFIER, yytext);
return Parser::IDENTIFIER;
}
. return yytext[0];
%%

View file

@ -0,0 +1,25 @@
#ifndef SCANNER_H_
#define SCANNER_H_
#include "../semantic/semantic.h"
#if ! defined(SKIP_FLEXLEXER_) && ! defined(SYSINC_FLEXLEXER_H_)
#include <FlexLexer.h>
#define SYSINC_FLEXLEXER_H_
#endif
class Scanner: public yyFlexLexer
{
Semantic *d_semval; // received fm the parser
public:
Scanner(Semantic *semval);
int yylex();
};
inline Scanner::Scanner(Semantic *semval)
:
d_semval(semval)
{}
#endif

View file

@ -0,0 +1,30 @@
/*
Declare here
what's only used in the Scanner class
and let Scanner's sources include "scanner.ih"
*/
#include "scanner.h"
//#include <iostream>
// #include <fstream>
// #include <strstream>
// #include <string>
/*
In the current Debian distribution of flex, YY_CURRENT_BUFFER and YY_START
are not available to members of the flex-generated class (See `Values
Available To The User' in the flex info-file). Use the following macros to
make these values available. (Note that the need for these defines may be
superfluous in the near future):
*/
// uncomment if you want to use YY_CURRENT_BUFFER in the scanner's members:
/*
#define YY_CURRENT_BUFFER ( (yy_buffer_stack) \
? (yy_buffer_stack)[(yy_buffer_stack_top)] \
: NULL)
*/
// uncomment if you want to use YY_START in the scanner's members:
// #define YY_START (((yy_start) - 1) / 2)
// end of scanner.ih

View file

@ -0,0 +1,7 @@
#include "semantic.ih"
Semantic::~Semantic()
{
if (d_int.first == IDENTIFIER)
d_str.second.~string(); // destroy the string's memory
}

View file

@ -0,0 +1,5 @@
#include "semantic.ih"
Semantic::
{
}

View file

@ -0,0 +1,8 @@
#include "semantic.ih"
Semantic &Semantic::operator=(Semantic const &rhs)
{
Semantic tmp(rhs);
swap(tmp);
return *this;
}

View file

@ -0,0 +1,15 @@
#include "semantic.ih"
std::ostream &operator<<(std::ostream &out, Semantic const &obj)
{
switch (obj.d_int.first)
{
case Semantic::INT:
return out << obj.d_int.second;
case Semantic::IDENTIFIER:
return out << obj.d_str.second;
}
return out << "<*UNDEFINED*>";
}

View file

@ -0,0 +1,38 @@
#ifndef INCLUDED_SEMANTIC_
#define INCLUDED_SEMANTIC_
#include <utility>
#include <string>
union Semantic
{
friend std::ostream &operator<<(std::ostream &out, Semantic const &obj);
std::pair<int, int> d_int;
std::pair<int, std::string> d_str;
public:
enum Type
{
UNDEFINED,
INT,
IDENTIFIER
};
Semantic();
Semantic(Type type, char const *txt);
Semantic(Semantic const &other); // 2
~Semantic();
Semantic &operator=(Semantic const &rhs);
void swap(Semantic &other);
};
inline Semantic::Semantic()
:
d_int {UNDEFINED, 0}
{}
#endif

View file

@ -0,0 +1,6 @@
#include "semantic.h"
#include <sstream>
#include <cstring>
using namespace std;

View file

@ -0,0 +1,14 @@
#include "semantic.ih"
Semantic::Semantic(Type type, char const *txt)
{
d_int.first = static_cast<int>(type);
if (type == IDENTIFIER)
new (&d_str.second) string(txt);
else
{
istringstream in(txt);
in >> d_int.second;
}
}

View file

@ -0,0 +1,9 @@
#include "semantic.ih"
Semantic::Semantic(Semantic const &other)
:
d_int(other.d_int) // blunt copy of d_int
{
if (d_int.first == IDENTIFIER) // if a string: copy it
new (&d_str.second) string(other.d_str.second);
}

View file

@ -0,0 +1,10 @@
#include "semantic.ih"
void Semantic::swap(Semantic &other)
{
char buffer[sizeof(Semantic)];
memcpy(buffer, this, sizeof(Semantic));
memcpy(this, &other, sizeof(Semantic));
memcpy(&other, buffer, sizeof(Semantic));
}