forked from mmbarbero/Bison-Flex-Calculator
-
Notifications
You must be signed in to change notification settings - Fork 0
/
gram.y
225 lines (187 loc) · 5.77 KB
/
gram.y
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
%{
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include "func.h"
#include "gram.tab.h"
/* Used for variable stores. Defined in mem.h */
extern double variable_values[100];
extern int variable_set[100];
/* Flex functions */
extern int yylex(void);
extern void yyterminate();
void yyerror(const char *s);
extern FILE* yyin;
%}
/* Bison declarations */
/*
If you have used %union to specify a variety of data types, then you must declare a choice among these
types for each terminal or nonterminal symbol that can have a semantic value.
Then each time you use $$ or $n, its data type is determined by which symbol it refers to in the rule.
(http://dinosaur.compilertools.net/bison/bison_6.html)
*/
%union {
int index;
double num;
}
%token<num> NUMBER
%token<num> L_BRACKET R_BRACKET
%token<num> DIV MUL ADD SUB EQUALS
%token<num> PI
%token<num> POW SQRT FACTORIAL MOD
%token<num> LOG2 LOG10
%token<num> FLOOR CEIL ABS
%token<num> GBP_TO_USD USD_TO_GBP
%token<num> GBP_TO_EURO EURO_TO_GBP
%token<num> USD_TO_EURO EURO_TO_USD
%token<num> COS SIN TAN COSH SINH TANH
%token<num> CEL_TO_FAH FAH_TO_CEL
%token<num> M_TO_KM KM_TO_M
%token<num> VAR_KEYWORD
%token<index> VARIABLE
%token<num> EOL
%type<num> program_input
%type<num> line
%type<num> calculation
%type<num> constant
%type<num> expr
%type<num> function
%type<num> log_function
%type<num> trig_function
%type<num> hyperbolic_function
%type<num> assignment
%type<num> conversion
%type<num> temperature_conversion
%type<num> distance_conversion
/* Set operator precedence, follows BODMAS rules. */
%left SUB
%left ADD
%left MUL
%left DIV
%left POW SQRT
%left L_BRACKET R_BRACKET
/* Grammar rules */
/*Symbols in Bison grammars represent the grammatical classifications of the language.
A terminal symbol (also known as a token type) represents a class of syntactically equivalent tokens. You use the symbol in grammar rules to mean that a token in that class is allowed. The symbol is represented in the Bison parser by a numeric code, and the yylex function returns a token type code to indicate what kind of token has been read. You don't need to know what the code value is; you can use the symbol to stand for it.
A nonterminal symbol stands for a class of syntactically equivalent groupings. The symbol name is used in writing grammar rules. By convention, it should be all lower case.
/* A Bison grammar rule has the following general form:
result: components...
;
where result is the nonterminal symbol that this rule describes and components are various terminal and nonterminal symbols that are put together by this rule.
For example,
exp: exp '+' exp
;
says that two groupings of type exp, with a `+' token in between, can be combined into a larger grouping of type exp.
Whitespace in rules is significant only to separate symbols. You can add extra whitespace as you wish.
Multiple rules for the same result can be written separately or can be joined with the vertical-bar character `|' as follows:
result: rule1-components...
| rule2-components...
...
;
(http://dinosaur.compilertools.net/bison/bison_6.html)
*/
%%
program_input:
| program_input line
;
line:
EOL { printf("Please enter a calculation:\n"); }
| calculation EOL { printf("=%.2f\n",$1); }
;
calculation:
expr
| function
| assignment
;
constant: PI { $$ = 3.142; }
;
expr:
SUB expr { $$ = -$2; }
| NUMBER { $$ = $1; }
| VARIABLE { $$ = variable_values[$1]; }
| constant
| function
| expr DIV expr { if ($3 == 0) { yyerror("Cannot divide by zero"); exit(1); } else $$ = $1 / $3; }
| expr MUL expr { $$ = $1 * $3; }
| L_BRACKET expr R_BRACKET { $$ = $2; }
| expr ADD expr { $$ = $1 + $3; }
| expr SUB expr { $$ = $1 - $3; }
| expr POW expr { $$ = pow($1, $3); }
| expr MOD expr { $$ = modulo($1, $3); }
;
function:
conversion
| log_function
| trig_function
| hyperbolic_function
| SQRT expr { $$ = sqrt($2); }
| expr FACTORIAL { $$ = factorial($1); }
| ABS expr { $$ = abs($2); }
| FLOOR expr { $$ = floor($2); }
| CEIL expr { $$ = ceil($2); }
;
trig_function:
COS expr { $$ = cos($2); }
| SIN expr { $$ = sin($2); }
| TAN expr { $$ = tan($2); }
;
log_function:
LOG2 expr { $$ = log2($2); }
| LOG10 expr { $$ = log10($2); }
;
hyperbolic_function:
COSH expr { $$ = cosh($2); }
| SINH expr { $$ = sinh($2); }
| TANH expr { $$ = tanh($2); }
;
conversion:
temperature_conversion
| distance_conversion
| expr GBP_TO_USD { $$ = gbp_to_usd($1); }
| expr USD_TO_GBP { $$ = usd_to_gbp($1); }
| expr GBP_TO_EURO { $$ = gbp_to_euro($1); }
| expr EURO_TO_GBP { $$ = euro_to_gbp($1); }
| expr USD_TO_EURO { $$ = usd_to_euro($1); }
| expr EURO_TO_USD { $$ = euro_to_usd($1); }
;
temperature_conversion:
expr CEL_TO_FAH { $$ = cel_to_fah($1); }
| expr FAH_TO_CEL { $$ = fah_to_cel($1); }
distance_conversion:
expr M_TO_KM { $$ = m_to_km($1); }
| expr KM_TO_M { $$ = km_to_m($1); }
assignment:
VAR_KEYWORD VARIABLE EQUALS calculation { $$ = set_variable($2, $4); }
;
%%
/* Entry point */
int main(int argc, char **argv)
{
char* c;
printf("Command line or File? (Enter C or F): ");
scanf("%s", c);
if (strcmp(c, "f")==0 || strcmp(c, "F")==0) {
// File input
printf("Ok, please tell me the name of the file: ");
scanf("%s", c);
yyin = fopen(c, "r");
if (!yyin) {
printf("ERROR: Couldn't open file %s\n", c);
exit(-1);
}
yyparse();
printf("All done with %s\n", c);
}
else {
// Command line
printf("Ok, command line it is!\n");
yyin = stdin;
yyparse();
}
}
/* Display error messages */
void yyerror(const char *s)
{
printf("ERROR: %s\n", s);
}