Skip to content

Instantly share code, notes, and snippets.

@shintakezou shintakezou/mcalcpp.yy
Last active Aug 29, 2015

Embed
What would you like to do?
Bison Calc Test 1 (single file)
/*
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
You can’t perform that action at this time.