Last active
August 29, 2015 13:59
-
-
Save shintakezou/10596841 to your computer and use it in GitHub Desktop.
Bison Calc Test 1 (single file)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
bison test 1 (single file), | |
widely based on examples in the bison manual at | |
http://www.gnu.org/software/bison/manual/ | |
It should compile with | |
$ bison mcalcpp.yy | |
$ g++ mcalcpp.tab.cc | |
or alike. (bison v>3.0; any C++98 compiler should go) | |
*/ | |
%{ | |
#include <iostream> | |
#include <string> | |
#include <sstream> | |
#include <map> | |
#include <cctype> | |
#include <cstdio> | |
#include <cmath> | |
int yylex(void); | |
void yyerror(const char* c); | |
std::map<std::string, double> syms; | |
#if __cplusplus >= 201103L | |
#include <limits> | |
#define SMALL_DOUBLE (std::numeric_limits<double>::epsilon() * 4) | |
#else | |
#include <cfloat> | |
#define SMALL_DOUBLE (DBL_MIN * 4) | |
#endif | |
%} | |
%require "3.0" | |
%define api.value.type union | |
%token <double> NUM | |
%token <std::string*> SYM | |
%token ASSIGN | |
%type <double> exp | |
/* valgrind finds leaks when using vars; this seems to be | |
in accordance with | |
http://www.gnu.org/software/bison/manual/html_node/Destructor-Decl.html | |
nonetheless, it can make feel someone annoyed */ | |
%destructor { std::cerr << "dtor\n"; delete $$; } SYM | |
/* | |
mfcalc example uses %precedence '=', | |
this is surely better; but, is my approach worse or | |
the same? I will study the matter, in the future | |
*/ | |
//%precedence ASSIGN | |
%left '-' '+' | |
%left '*' '/' | |
%left '^' | |
%left ASSIGN | |
%precedence NEG | |
%% | |
input: | |
%empty | |
| input line | |
; | |
// "line" is an assignment or a standalone expression | |
line: | |
';' | |
| SYM ASSIGN exp ';' | |
{ | |
syms[*$1] = $3; | |
} | |
| exp ';' { std::cout << $1 << "\n"; } | |
; | |
exp: | |
NUM { $$ = $1; } | |
/* not sure of this; if misplaced, makes bison complain | |
of shift/reduce conflicts. The aim is to give a more | |
natural syntax for multiplication, e.g. .5a as in MF */ | |
| NUM '(' exp ')' { $$ = $1 * $3; } | |
| '(' exp ')' NUM { $$ = $2 * $4; } | |
| SYM | |
{ | |
if (syms.count(*$1) > 0) { | |
$$ = syms[*$1]; | |
} else { | |
std::cerr << "unknown symbol \"" << *$1 << "\" at " << | |
@1.first_line << ":" << @1.first_column << "\n"; | |
YYERROR; | |
} | |
} | |
/* how to factorize the following? likely rewriting the rules (?) */ | |
| SYM '(' exp ')' | |
{ | |
if (syms.count(*$1) > 0) { | |
$$ = syms[*$1] * $3; | |
} else { | |
std::cerr << "unknown symbol \"" << *$1 << "\" at " << | |
@1.first_line << ":" << @1.first_column << "\n"; | |
YYERROR; | |
} | |
} | |
| '(' exp ')' SYM | |
{ | |
if (syms.count(*$4) > 0) { | |
$$ = syms[*$4] * $2; | |
} else { | |
std::cerr << "unknown symbol \"" << *$4 << "\" at " << | |
@4.first_line << ":" << @4.first_column << "\n"; | |
YYERROR; | |
} | |
} | |
| NUM SYM | |
{ | |
if (syms.count(*$2) > 0) { | |
$$ = syms[*$2] * $1; | |
} else { | |
std::cerr << "unknown symbol \"" << *$2 << "\" at " << | |
@2.first_line << ":" << @2.first_column << "\n"; | |
YYERROR; | |
} | |
} | |
| exp '+' exp { $$ = $1 + $3; } | |
| exp '-' exp { $$ = $1 - $3; } | |
| exp '*' exp { $$ = $1 * $3; } | |
| exp '/' exp | |
{ | |
double t3 = $3; | |
if (t3 > SMALL_DOUBLE || t3 < -SMALL_DOUBLE) { | |
$$ = $1 / $3; | |
} else { | |
std::cerr << "division by zero at " << | |
@3.first_line << ":" << | |
@3.first_column << "\n"; | |
YYERROR; | |
} | |
} | |
| '-' exp %prec NEG { $$ = -$2; } | |
| exp '^' exp { $$ = pow($1, $3); } | |
| '(' exp ')' { $$ = $2; } | |
; | |
%% | |
/* handmade rough lex, grown up from the example, statusless. */ | |
int yylex(void) | |
{ | |
int c; | |
while ((c = getchar()) != EOF) { | |
if (isspace(c) || isblank(c)) { | |
++yylloc.first_column; | |
if (c == '\n') { | |
yylloc.first_column = 0; | |
++yylloc.first_line; | |
} | |
continue; | |
} else if (isdigit(c) || c == '.') { | |
ungetc(c, stdin); | |
int ps = scanf("%lf", &yylval.NUM); | |
if (ps > 0) { | |
std::stringstream ss; | |
ss << yylval.NUM; | |
yylloc.first_column += ss.str().length(); | |
} else { | |
yylval.NUM = 0.0; | |
} | |
return NUM; | |
} else if (c == ':') { | |
int c1 = getchar(); | |
if (c1 == '=') { | |
yylloc.first_column += 2; | |
return ASSIGN; | |
} | |
ungetc(c1, stdin); | |
} else if (isalpha(c)) { | |
std::stringstream ss; | |
ss.put(c); | |
int c1; | |
while ((c1 = getchar()) != EOF && isalpha(c1)) { | |
ss.put(c1); | |
} | |
if (c1 != EOF) | |
ungetc(c1, stdin); | |
yylval.SYM = new std::string(ss.str()); | |
yylloc.first_column += yylval.SYM->length(); | |
return SYM; | |
} | |
++yylloc.first_column; | |
return c; | |
} | |
return 0; | |
} | |
void yyerror(const char *s) | |
{ | |
std::cerr << s << "\n"; | |
} | |
int main(void) | |
{ | |
yylloc.first_line = yylloc.last_line = 1; | |
/* is column tracking accurate? */ | |
yylloc.first_column = yylloc.last_column = 0; | |
return yyparse(); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment