Skip to content

Commit f896aec

Browse files
committed
Initial commit
1 parent 06a5688 commit f896aec

File tree

7 files changed

+359
-0
lines changed

7 files changed

+359
-0
lines changed

Makefile

+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
# GNU Makefile for cpp-tutor/flex-bison-cpp
2+
# note: can run 'make calc1' or 'make calc2'
3+
4+
FLEX = flex
5+
BISON = bison
6+
CPP = g++
7+
CPPFLAGS =
8+
CALC1 = calc1
9+
CALC2 = calc2
10+
11+
AUTOGEN1 = Parser1.hpp Parser1.cpp Scanner1.hpp Scanner1.cpp
12+
AUTOGEN2 = Parser2.hpp Parser2.cpp Scanner2.cpp
13+
14+
all: $(CALC1) $(CALC2)
15+
16+
$(AUTOGEN1): lexer1.l grammar1.y
17+
$(FLEX) lexer1.l
18+
$(BISON) grammar1.y
19+
20+
$(AUTOGEN2): lexer2.l grammar2.y
21+
$(FLEX) lexer2.l
22+
$(BISON) grammar2.y
23+
24+
$(CALC1): $(AUTOGEN1)
25+
$(CPP) $(CPPFLAGS) -o $(CALC1) Parser1.cpp Scanner1.cpp
26+
27+
$(CALC2): $(AUTOGEN2) Scanner2.hpp
28+
$(CPP) $(CPPFLAGS) -o $(CALC2) Parser2.cpp Scanner2.cpp
29+
30+
clean:
31+
rm -f $(CALC1) $(AUTOGEN1) $(CALC2) $(AUTOGEN2)

Scanner2.hpp

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
namespace calc { // note: depends upon FlexLexer.h and Parser2.hpp
2+
3+
class Scanner : public yyFlexLexer {
4+
public:
5+
Scanner(std::istream& arg_yyin, std::ostream& arg_yyout)
6+
: yyFlexLexer(arg_yyin, arg_yyout) {}
7+
Scanner(std::istream* arg_yyin = nullptr, std::ostream* arg_yyout = nullptr)
8+
: yyFlexLexer(arg_yyin, arg_yyout) {}
9+
int lex(Parser::semantic_type *yylval); // note: this is the prototype we need
10+
};
11+
12+
} // namespace calc

build_VS2019.bat

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
@ECHO OFF
2+
3+
@ECHO Generating: Scanner1.hpp Scanner1.cpp
4+
win_flex --wincompat lexer1.l
5+
@ECHO Generating: Parser1.hpp Parser1.cpp
6+
win_bison grammar1.y
7+
cl /EHsc /FeCalc1.exe Parser1.cpp Scanner1.cpp
8+
9+
@ECHO Generating: Scanner2.cpp
10+
win_flex --wincompat lexer2.l
11+
@ECHO Generating: Parser2.hpp Parser2.cpp
12+
win_bison grammar2.y
13+
cl /EHsc /FeCalc2.exe /I. Parser2.cpp Scanner2.cpp

grammar1.y

+107
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
%{
2+
#include <iostream>
3+
#include <string>
4+
#include <cmath>
5+
#include "Scanner1.hpp"
6+
%}
7+
8+
%require "3.7.4"
9+
%language "C++"
10+
%defines "Parser1.hpp"
11+
%output "Parser1.cpp"
12+
13+
%define api.parser.class {Parser}
14+
%define api.namespace {calc}
15+
%define api.value.type variant
16+
%param {yyscan_t scanner}
17+
18+
%code provides
19+
{
20+
#define YY_DECL \
21+
int yylex(calc::Parser::semantic_type *yylval, yyscan_t yyscanner)
22+
YY_DECL;
23+
}
24+
25+
%token EOL LPAREN RPAREN
26+
%token <long long> INT
27+
%token <double> FLT
28+
%token <char> INTVAR FLTVAR
29+
30+
%nterm <long long> iexp
31+
%nterm <double> fexp
32+
33+
%nonassoc ASSIGN
34+
%left PLUS MINUS
35+
%left MULTIPLY DIVIDE MODULO
36+
%precedence UMINUS
37+
%precedence FACTORIAL
38+
%right EXPONENT
39+
40+
%code
41+
{
42+
namespace calc {
43+
long long ivars['Z' - 'A' + 1];
44+
double fvars['z' - 'a' + 1];
45+
46+
long long factorial(long long n) {
47+
if (n < 2) {
48+
return 1;
49+
}
50+
else {
51+
return n * factorial(n - 1);
52+
}
53+
}
54+
} // namespace calc
55+
} // %code
56+
57+
%%
58+
59+
lines : %empty
60+
| lines line
61+
;
62+
63+
line : EOL { std::cerr << "Read an empty line.\n"; }
64+
| iexp EOL { std::cout << $1 << '\n'; }
65+
| fexp EOL { std::cout << $1 << '\n'; }
66+
| INTVAR ASSIGN iexp EOL { ivars[$1 - 'A'] = $3; }
67+
| FLTVAR ASSIGN fexp EOL { fvars[$1 - 'a'] = $3; }
68+
| error EOL { yyerrok; }
69+
;
70+
71+
iexp : INT { $$ = $1; }
72+
| iexp PLUS iexp { $$ = $1 + $3; }
73+
| iexp MINUS iexp { $$ = $1 - $3; }
74+
| iexp MULTIPLY iexp { $$ = $1 * $3; }
75+
| iexp DIVIDE iexp { $$ = $1 / $3; }
76+
| iexp MODULO iexp { $$ = $1 % $3; }
77+
| MINUS iexp %prec UMINUS { $$ = -$2; }
78+
| iexp FACTORIAL { $$ = factorial($1); }
79+
| LPAREN iexp RPAREN { $$ = $2; }
80+
| INTVAR { $$ = ivars[$1 - 'A']; }
81+
;
82+
83+
fexp : FLT { $$ = $1; }
84+
| fexp PLUS fexp { $$ = $1 + $3; }
85+
| fexp MINUS fexp { $$ = $1 - $3; }
86+
| fexp MULTIPLY fexp { $$ = $1 * $3; }
87+
| fexp DIVIDE fexp { $$ = $1 / $3; }
88+
| fexp EXPONENT fexp { $$ = pow($1, $3); }
89+
| MINUS fexp %prec UMINUS { $$ = -$2; }
90+
| LPAREN fexp RPAREN { $$ = $2; }
91+
| FLTVAR { $$ = fvars[$1 - 'a']; }
92+
;
93+
94+
%%
95+
96+
void calc::Parser::error(const std::string& msg) {
97+
std::cerr << msg << '\n';
98+
}
99+
100+
int main() {
101+
yyscan_t scanner;
102+
yylex_init(&scanner);
103+
calc::Parser parser{ scanner };
104+
std::cout.precision(10);
105+
parser.parse();
106+
yylex_destroy(scanner);
107+
}

grammar2.y

+102
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
%{
2+
#include <iostream>
3+
#include <string>
4+
#include <cmath>
5+
#include <FlexLexer.h>
6+
%}
7+
8+
%require "3.7.4"
9+
%language "C++"
10+
%defines "Parser2.hpp"
11+
%output "Parser2.cpp"
12+
13+
%define api.parser.class {Parser}
14+
%define api.namespace {calc}
15+
%define api.value.type variant
16+
%parse-param {Scanner* scanner}
17+
18+
%code requires
19+
{
20+
namespace calc {
21+
class Scanner;
22+
} // namespace calc
23+
} // %code requires
24+
25+
%code
26+
{
27+
#include "Scanner2.hpp"
28+
#define yylex(x) scanner->lex(x)
29+
}
30+
31+
%token EOL LPAREN RPAREN
32+
%token <long long> INT
33+
%token <double> FLT
34+
%token <char> INTVAR FLTVAR
35+
36+
%nterm <long long> iexp
37+
%nterm <double> fexp
38+
39+
%nonassoc ASSIGN
40+
%left PLUS MINUS
41+
%left MULTIPLY DIVIDE MODULO
42+
%precedence UMINUS
43+
%precedence FACTORIAL
44+
%right EXPONENT
45+
46+
%code
47+
{
48+
namespace calc {
49+
long long ivars['Z' - 'A' + 1];
50+
double fvars['z' - 'a' + 1];
51+
52+
long long factorial(long long n) {
53+
if (n < 2) {
54+
return 1;
55+
}
56+
return n * factorial(n - 1);
57+
}
58+
} // namespace calc
59+
} // %code
60+
61+
%%
62+
63+
lines : %empty
64+
| lines line
65+
;
66+
67+
line : EOL { std::cerr << "Read an empty line.\n"; }
68+
| iexp EOL { std::cout << $1 << '\n'; }
69+
| fexp EOL { std::cout << $1 << '\n'; }
70+
| INTVAR ASSIGN iexp EOL { ivars[$1 - 'A'] = $3; }
71+
| FLTVAR ASSIGN fexp EOL { fvars[$1 - 'a'] = $3; }
72+
| error EOL { yyerrok; }
73+
;
74+
75+
iexp : INT { $$ = $1; }
76+
| iexp PLUS iexp { $$ = $1 + $3; }
77+
| iexp MINUS iexp { $$ = $1 - $3; }
78+
| iexp MULTIPLY iexp { $$ = $1 * $3; }
79+
| iexp DIVIDE iexp { $$ = $1 / $3; }
80+
| iexp MODULO iexp { $$ = $1 % $3; }
81+
| MINUS iexp %prec UMINUS { $$ = -$2; }
82+
| iexp FACTORIAL { $$ = factorial($1); }
83+
| LPAREN iexp RPAREN { $$ = $2; }
84+
| INTVAR { $$ = ivars[$1 - 'A']; }
85+
;
86+
87+
fexp : FLT { $$ = $1; }
88+
| fexp PLUS fexp { $$ = $1 + $3; }
89+
| fexp MINUS fexp { $$ = $1 - $3; }
90+
| fexp MULTIPLY fexp { $$ = $1 * $3; }
91+
| fexp DIVIDE fexp { $$ = $1 / $3; }
92+
| fexp EXPONENT fexp { $$ = pow($1, $3); }
93+
| MINUS fexp %prec UMINUS { $$ = -$2; }
94+
| LPAREN fexp RPAREN { $$ = $2; }
95+
| FLTVAR { $$ = fvars[$1 - 'a']; }
96+
;
97+
98+
%%
99+
100+
void calc::Parser::error(const std::string& msg) {
101+
std::cerr << msg << '\n';
102+
}

lexer1.l

+41
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
%{
2+
#include <cstdlib>
3+
#include "Parser1.hpp"
4+
using namespace calc;
5+
%}
6+
7+
%option reentrant interactive noyywrap noyylineno nodefault outfile="Scanner1.cpp" header="Scanner1.hpp"
8+
9+
dseq ([[:digit:]]+)
10+
dseq_opt ({dseq}?)
11+
frac (({dseq_opt}"."{dseq})|{dseq}".")
12+
exp ([eE][+-]?{dseq})
13+
exp_opt ({exp}?)
14+
integer ({dseq})
15+
float (({frac}{exp_opt})|({dseq}{exp}))
16+
intvar ([[:upper:]])
17+
fltvar ([[:lower:]])
18+
19+
%%
20+
21+
{integer} yylval->emplace<long long>(strtoll(yytext, nullptr, 10)); return Parser::token::INT;
22+
{float} yylval->emplace<double>(strtod(yytext, nullptr)); return Parser::token::FLT;
23+
{intvar} yylval->emplace<char>(yytext[0]); return Parser::token::INTVAR;
24+
{fltvar} yylval->emplace<char>(yytext[0]); return Parser::token::FLTVAR;
25+
"+" return Parser::token::PLUS;
26+
"-" return Parser::token::MINUS;
27+
"*" return Parser::token::MULTIPLY;
28+
"/" return Parser::token::DIVIDE;
29+
"%" return Parser::token::MODULO;
30+
"!" return Parser::token::FACTORIAL;
31+
"^" return Parser::token::EXPONENT;
32+
"(" return Parser::token::LPAREN;
33+
")" return Parser::token::RPAREN;
34+
"=" return Parser::token::ASSIGN;
35+
\n return Parser::token::EOL;
36+
<<EOF>> return Parser::token::YYEOF;
37+
. /* no action on unmatched input */
38+
39+
%%
40+
41+
// main() can't go here due to bug in flex 2.6.4

lexer2.l

+53
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
%{
2+
#include "Parser2.hpp"
3+
#include "Scanner2.hpp"
4+
#define YY_DECL int calc::Scanner::lex(calc::Parser::semantic_type *yylval)
5+
// workaround for bug in flex 2.6.4
6+
#define yypanic(X) (void)(X)
7+
%}
8+
9+
%option c++ interactive noyywrap noyylineno nodefault outfile="Scanner2.cpp"
10+
11+
dseq ([[:digit:]]+)
12+
dseq_opt ({dseq}?)
13+
frac (({dseq_opt}"."{dseq})|{dseq}".")
14+
exp ([eE][+-]?{dseq})
15+
exp_opt ({exp}?)
16+
17+
integer ({dseq})
18+
float (({frac}{exp_opt})|({dseq}{exp}))
19+
intvar ([[:upper:]])
20+
fltvar ([[:lower:]])
21+
22+
%%
23+
24+
{integer} yylval->emplace<long long>(strtoll(YYText(), nullptr, 10)); return Parser::token::INT;
25+
{float} yylval->emplace<double>(strtod(YYText(), nullptr)); return Parser::token::FLT;
26+
{intvar} yylval->emplace<char>(YYText()[0]); return Parser::token::INTVAR;
27+
{fltvar} yylval->emplace<char>(YYText()[0]); return Parser::token::FLTVAR;
28+
"+" return Parser::token::PLUS;
29+
"-" return Parser::token::MINUS;
30+
"*" return Parser::token::MULTIPLY;
31+
"/" return Parser::token::DIVIDE;
32+
"%" return Parser::token::MODULO;
33+
"!" return Parser::token::FACTORIAL;
34+
"^" return Parser::token::EXPONENT;
35+
"(" return Parser::token::LPAREN;
36+
")" return Parser::token::RPAREN;
37+
"=" return Parser::token::ASSIGN;
38+
\n return Parser::token::EOL;
39+
<<EOF>> return Parser::token::YYEOF;
40+
. /* no action on unmatched input */
41+
42+
%%
43+
44+
int yyFlexLexer::yylex() {
45+
throw std::runtime_error("Invalid call to yyFlexLexer::yylex()");
46+
}
47+
48+
int main() {
49+
calc::Scanner scanner{ std::cin, std::cerr };
50+
calc::Parser parser{ &scanner };
51+
std::cout.precision(10);
52+
parser.parse();
53+
}

0 commit comments

Comments
 (0)