-
Notifications
You must be signed in to change notification settings - Fork 0
/
script_parser.y
161 lines (131 loc) · 4 KB
/
script_parser.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
%{
#include <cstdio>
#include <iostream>
#include <memory>
using namespace std;
#include "script_parser.tab.h"
#include "script_scanner.h"
void yyerror(yyscan_t scanner, const char *s);
#define scanner_handle state->scanner_ref
%}
%code requires {
#include "Types.h"
#include "SymbolTable.h"
#include <list>
struct pass_to_bison {
shared_ptr<SymbolTable> table;
int line = 0;
void * scanner_ref;
list <StatementBlock*> scopes;
};
unique_ptr<Script> script_parse(std::string input);
}
%pure-parser
%lex-param {(void*)(scanner_handle)}
%parse-param {pass_to_bison*state }
// Bison fundamentally works by asking flex to get the next token, which it
// returns as an object of type "yystype". But tokens could be of any
// arbitrary data type! So we deal with that in Bison by defining a C union
// holding each of the types of tokens that Flex could return, and have Bison
// use that union instead of "int" for the definition of "yystype":
%union {
ScriptInteger* ival;
ScriptString* sval;
Expression* eval;
Statement* stmt;
char * token;
}
// define the "terminal symbol" token types I'm going to use (in CAPS
// by convention), and associate each with a field of the union:
%token <ival> INT
%token <sval> STRING
%token <token> TOKEN
%token ADD
%token RBRACKET
%token LBRACKET
%token ASSIGN
%token SCOLIN
%token VAR
%token LBRACE
%token RBRACE
%token LPAREN
%token RPAREN
%token IF
%token ELSE
%token PRINT
%type <eval> expr
%type <stmt> stmt
%type <stmt> stmtblk
%type <stmt> vardecl
%type <stmt> ifstmt
%type <stmt> elsestmt
%type <stmt> printstmt
%destructor { cout << "freeing char*" << *$$ << endl; free($$); } <token>
%destructor { cout << "freeing INT " << *$$ << endl; free($$); } <ival>
%destructor { cout << "freeing STRING " << *$$ << endl; free($$); } <sval>
%destructor { cout << "freeing Statement " << *$$ << endl; free($$); } <stmt>
%destructor { cout << "freeing Expression " << *$$ << endl; free($$); } <eval>
%right ASSIGN
%left ADD
%left LBRACKET
%%
// this is the actual grammar that bison will parse, but for right now it's just
// something silly to echo to the screen what bison gets from flex. We'll
// make a real one shortly:
stmts:
stmts stmt { state->scopes.back()->addStatement($2); }
| stmt { state->scopes.back()->addStatement($1); }
;
stmt:
expr SCOLIN { $$ = new ExpressionStatement($1); $$->setLineNo(state->line);}
| vardecl SCOLIN { $$ = $1; $$->setLineNo(state->line);}
| stmtblk { $$ = $1; $$->setLineNo(state->line);}
| ifstmt { $$ = $1; $$->setLineNo(state->line);}
| printstmt { $$ = $1; $$->setLineNo(state->line);}
;
ifstmt:
IF LPAREN expr RPAREN stmt elsestmt { $$ = ($6 == NULL ? new IfStatement($3, $5) : new IfStatement($3, $5, $6)); }
;
elsestmt:
ELSE stmt { $$ = $2; $$->setLineNo(state->line); }
| {$$ = NULL;}
;
printstmt:
PRINT expr SCOLIN { $$ = new PrintStatement($2); }
;
stmtblk:
LBRACE {state->scopes.push_back(new StatementBlock());}
stmts
RBRACE { $$ = state->scopes.back(); state->scopes.pop_back();}
;
vardecl:
VAR TOKEN { $$ = new VardeclStatement(state->table,$2); free($2);}
| VAR TOKEN ASSIGN expr { $$ = new VardeclStatement(state->table, $2, $4); free($2); }
;
expr:
INT { $$ = $1; }
| STRING { $$ = $1; }
| TOKEN { $$ = new ScriptVariable(state->table, $1); }
| expr ADD expr { $$ = new Add($1, $3); }
| expr LBRACKET expr RBRACKET { $$ = new Index($1, $3); }
| expr ASSIGN expr { $$ = new Assign($1, $3); }
;
%%
unique_ptr<Script> script_parse(std::string data) {
unique_ptr<Script> my_script(new Script());
pass_to_bison state;
yylex_init(&state.scanner_ref);
yyset_extra(&state, state.scanner_ref);
state.table = unique_ptr<SymbolTable>(new SymbolTable());
state.scopes.push_back(my_script.get());
YY_BUFFER_STATE bp = yy_scan_string(data.c_str(), state.scanner_ref);
yy_switch_to_buffer(bp, state.scanner_ref);
yyparse(&state);
yylex_destroy(state.scanner_ref);
return my_script;
}
void yyerror(yyscan_t scanner, const char *s) {
cerr << "EEK, parse error! Message: " << s << endl;
// might as well halt now:
// exit(-1);
}