Mercurial > hg > index.cgi
annotate lwbasic/parser.c @ 35:cdb0175e1063
More work on expressions
author | Lost Wizard (lost@starbug3) |
---|---|
date | Sat, 05 Feb 2011 14:22:54 -0700 |
parents | bfea77812e64 |
children | 5325b640424d |
rev | line source |
---|---|
25 | 1 /* |
2 compiler.c | |
3 | |
4 Copyright © 2011 William Astle | |
5 | |
6 This file is part of LWTOOLS. | |
7 | |
8 LWTOOLS is free software: you can redistribute it and/or modify it under the | |
9 terms of the GNU General Public License as published by the Free Software | |
10 Foundation, either version 3 of the License, or (at your option) any later | |
11 version. | |
12 | |
13 This program is distributed in the hope that it will be useful, but WITHOUT | |
14 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
15 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | |
16 more details. | |
17 | |
18 You should have received a copy of the GNU General Public License along with | |
19 this program. If not, see <http://www.gnu.org/licenses/>. | |
20 */ | |
21 | |
22 /* | |
23 This is the actual compiler bit; it drives the parser and code generation | |
24 */ | |
25 | |
26 #include <stdio.h> | |
27 | |
26
26aa76da75ad
Additional parsing in function/sub; emission of prolog/epilog code
lost@l-w.ca
parents:
25
diff
changeset
|
28 #include <lw_alloc.h> |
26aa76da75ad
Additional parsing in function/sub; emission of prolog/epilog code
lost@l-w.ca
parents:
25
diff
changeset
|
29 #include <lw_string.h> |
26aa76da75ad
Additional parsing in function/sub; emission of prolog/epilog code
lost@l-w.ca
parents:
25
diff
changeset
|
30 |
25 | 31 #include "lwbasic.h" |
32
49d608aecc4d
Framework for handling local stack frame and/or variables
lost@l-w.ca
parents:
31
diff
changeset
|
32 #include "symtab.h" |
49d608aecc4d
Framework for handling local stack frame and/or variables
lost@l-w.ca
parents:
31
diff
changeset
|
33 |
34 | 34 static void expect(cstate *state, int tt) |
35 { | |
36 if (state -> lexer_token != tt) | |
37 lwb_error("Expecting %s, got %s\n", lexer_token_name(tt), lexer_return_token(state)); | |
38 lexer(state); | |
39 } | |
40 | |
32
49d608aecc4d
Framework for handling local stack frame and/or variables
lost@l-w.ca
parents:
31
diff
changeset
|
41 |
49d608aecc4d
Framework for handling local stack frame and/or variables
lost@l-w.ca
parents:
31
diff
changeset
|
42 /* size of a type */ |
49d608aecc4d
Framework for handling local stack frame and/or variables
lost@l-w.ca
parents:
31
diff
changeset
|
43 static int sizeof_type(int type) |
49d608aecc4d
Framework for handling local stack frame and/or variables
lost@l-w.ca
parents:
31
diff
changeset
|
44 { |
49d608aecc4d
Framework for handling local stack frame and/or variables
lost@l-w.ca
parents:
31
diff
changeset
|
45 /* everything is an "int" right now; 2 bytes */ |
49d608aecc4d
Framework for handling local stack frame and/or variables
lost@l-w.ca
parents:
31
diff
changeset
|
46 return 2; |
49d608aecc4d
Framework for handling local stack frame and/or variables
lost@l-w.ca
parents:
31
diff
changeset
|
47 } |
25 | 48 |
49 /* parse a type; the next token will be acquired as a result */ | |
50 /* the token advancement is to provide consistency */ | |
51 static int parse_type(cstate *state) | |
52 { | |
53 int pt = -1; | |
54 | |
55 switch (state -> lexer_token) | |
56 { | |
57 case token_kw_integer: | |
58 pt = 1; | |
59 break; | |
60 | |
61 default: | |
62 lwb_error("Invalid type specification"); | |
63 } | |
64 lexer(state); | |
65 /* look for "unsigned" modifier for integer types */ | |
66 return pt; | |
67 } | |
68 | |
35 | 69 static int parse_expression(cstate *state) |
70 { | |
71 state -> expression = 1; | |
72 | |
73 state -> expression = 0; | |
74 return 1; | |
75 } | |
76 | |
32
49d608aecc4d
Framework for handling local stack frame and/or variables
lost@l-w.ca
parents:
31
diff
changeset
|
77 static void parse_decls(cstate *state) |
49d608aecc4d
Framework for handling local stack frame and/or variables
lost@l-w.ca
parents:
31
diff
changeset
|
78 { |
49d608aecc4d
Framework for handling local stack frame and/or variables
lost@l-w.ca
parents:
31
diff
changeset
|
79 /* declarations */ |
33 | 80 /* the first thing that doesn't look like a declaration is assumed */ |
81 /* to be a statement and will trigger a bailout */ | |
82 int vt; | |
83 char *vn; | |
84 symtab_entry_t *se; | |
85 | |
86 for (;;) | |
32
49d608aecc4d
Framework for handling local stack frame and/or variables
lost@l-w.ca
parents:
31
diff
changeset
|
87 { |
33 | 88 switch (state -> lexer_token) |
89 { | |
90 /* DIM keyword */ | |
91 case token_kw_dim: | |
92 lexer(state); | |
93 if (state -> lexer_token != token_identifier) | |
94 { | |
95 lwb_error("Expecting identifier, got %s\n", lexer_return_token(state)); | |
96 } | |
97 vn = lw_strdup(state -> lexer_token_string); | |
98 lexer(state); | |
99 if (state -> lexer_token != token_kw_as) | |
100 { | |
101 lwb_error("Expecting AS, got %s\n", lexer_return_token(state)); | |
102 } | |
103 lexer(state); | |
104 vt = parse_type(state); | |
105 | |
106 se = symtab_find(state -> local_syms, vn); | |
107 if (se) | |
108 { | |
109 lwb_error("Multiply defined local variable %s", vn); | |
110 } | |
111 state -> framesize += sizeof_type(vt); | |
112 symtab_register(state -> local_syms, vn, -(state -> framesize), symtype_var, NULL); | |
113 | |
114 lw_free(vn); | |
115 break; | |
34 | 116 |
117 /* blank lines allowed */ | |
118 case token_eol: | |
119 break; | |
120 | |
33 | 121 default: |
122 return; | |
123 } | |
124 if (state -> lexer_token != token_eol) | |
34 | 125 lwb_error("Expecting end of line; got %s\n", lexer_return_token(state)); |
126 lexer(state); | |
127 } | |
128 } | |
129 | |
130 static void parse_statements(cstate *state) | |
131 { | |
132 symtab_entry_t *se; | |
35 | 133 int et; |
34 | 134 |
135 for (;;) | |
136 { | |
137 switch (state -> lexer_token) | |
138 { | |
139 /* blank lines allowed */ | |
140 case token_eol: | |
141 break; | |
142 | |
143 /* variable assignment */ | |
144 case token_identifier: | |
145 se = symtab_find(state -> local_syms, state -> lexer_token_string); | |
146 if (!se) | |
147 { | |
148 se = symtab_find(state -> global_syms, state -> lexer_token_string); | |
149 } | |
150 if (!se) | |
151 lwb_error("Unknown variable %s\n", state -> lexer_token_string); | |
152 lexer(state); | |
35 | 153 /* ensure the first token of the expression will be parsed correctly */ |
154 state -> expression = 1; | |
34 | 155 expect(state, token_op_assignment); |
156 | |
157 /* parse the expression */ | |
35 | 158 et = parse_expression(state); |
159 | |
160 /* check type compatibility */ | |
34 | 161 |
162 /* actually do the assignment */ | |
163 | |
164 break; | |
165 | |
166 /* anything we don't recognize as a statement token breaks out */ | |
167 default: | |
168 return; | |
169 } | |
170 if (state -> lexer_token != token_eol) | |
171 lwb_error("Expecting end of line; got %s\n", lexer_return_token(state)); | |
33 | 172 lexer(state); |
32
49d608aecc4d
Framework for handling local stack frame and/or variables
lost@l-w.ca
parents:
31
diff
changeset
|
173 } |
49d608aecc4d
Framework for handling local stack frame and/or variables
lost@l-w.ca
parents:
31
diff
changeset
|
174 } |
49d608aecc4d
Framework for handling local stack frame and/or variables
lost@l-w.ca
parents:
31
diff
changeset
|
175 |
25 | 176 |
177 /* issub means RETURNS is not allowed; !issub means RETURNS is required */ | |
32
49d608aecc4d
Framework for handling local stack frame and/or variables
lost@l-w.ca
parents:
31
diff
changeset
|
178 |
25 | 179 static void parse_subfunc(cstate *state, int issub) |
180 { | |
32
49d608aecc4d
Framework for handling local stack frame and/or variables
lost@l-w.ca
parents:
31
diff
changeset
|
181 int pt, rt; |
49d608aecc4d
Framework for handling local stack frame and/or variables
lost@l-w.ca
parents:
31
diff
changeset
|
182 char *subname, *pn; |
26
26aa76da75ad
Additional parsing in function/sub; emission of prolog/epilog code
lost@l-w.ca
parents:
25
diff
changeset
|
183 int vis = 0; |
32
49d608aecc4d
Framework for handling local stack frame and/or variables
lost@l-w.ca
parents:
31
diff
changeset
|
184 symtab_entry_t *se; |
49d608aecc4d
Framework for handling local stack frame and/or variables
lost@l-w.ca
parents:
31
diff
changeset
|
185 int paramsize = 0; |
49d608aecc4d
Framework for handling local stack frame and/or variables
lost@l-w.ca
parents:
31
diff
changeset
|
186 |
49d608aecc4d
Framework for handling local stack frame and/or variables
lost@l-w.ca
parents:
31
diff
changeset
|
187 state -> local_syms = symtab_init(); |
49d608aecc4d
Framework for handling local stack frame and/or variables
lost@l-w.ca
parents:
31
diff
changeset
|
188 state -> framesize = 0; |
49d608aecc4d
Framework for handling local stack frame and/or variables
lost@l-w.ca
parents:
31
diff
changeset
|
189 |
25 | 190 lexer(state); |
191 if (state -> lexer_token != token_identifier) | |
192 { | |
193 lwb_error("Invalid sub name '%s'", state -> lexer_token_string); | |
194 } | |
195 | |
26
26aa76da75ad
Additional parsing in function/sub; emission of prolog/epilog code
lost@l-w.ca
parents:
25
diff
changeset
|
196 subname = lw_strdup(state -> lexer_token_string); |
25 | 197 |
198 lexer(state); | |
199 if (state -> lexer_token == token_kw_public || state -> lexer_token == token_kw_private) | |
200 { | |
26
26aa76da75ad
Additional parsing in function/sub; emission of prolog/epilog code
lost@l-w.ca
parents:
25
diff
changeset
|
201 if (state -> lexer_token == token_kw_public) |
26aa76da75ad
Additional parsing in function/sub; emission of prolog/epilog code
lost@l-w.ca
parents:
25
diff
changeset
|
202 vis = 1; |
25 | 203 lexer(state); |
204 } | |
205 | |
206 /* ignore the "PARAMS" keyword if present */ | |
207 if (state -> lexer_token == token_kw_params) | |
208 lexer(state); | |
209 | |
26
26aa76da75ad
Additional parsing in function/sub; emission of prolog/epilog code
lost@l-w.ca
parents:
25
diff
changeset
|
210 if (state -> lexer_token == token_eol || state -> lexer_token == token_kw_returns) |
25 | 211 goto noparms; |
212 | |
213 paramagain: | |
214 if (state -> lexer_token != token_identifier) | |
215 { | |
31
574931d87abd
Created a function to prettyprint the current lexer token
lost@l-w.ca
parents:
30
diff
changeset
|
216 lwb_error("Parameter name expected, got %s\n", lexer_return_token(state)); |
25 | 217 } |
32
49d608aecc4d
Framework for handling local stack frame and/or variables
lost@l-w.ca
parents:
31
diff
changeset
|
218 pn = lw_strdup(state -> lexer_token_string); |
25 | 219 lexer(state); |
220 | |
221 if (state -> lexer_token != token_kw_as) | |
222 lwb_error("Expecting AS\n"); | |
223 lexer(state); | |
224 | |
225 pt = parse_type(state); | |
32
49d608aecc4d
Framework for handling local stack frame and/or variables
lost@l-w.ca
parents:
31
diff
changeset
|
226 |
49d608aecc4d
Framework for handling local stack frame and/or variables
lost@l-w.ca
parents:
31
diff
changeset
|
227 se = symtab_find(state -> local_syms, pn); |
49d608aecc4d
Framework for handling local stack frame and/or variables
lost@l-w.ca
parents:
31
diff
changeset
|
228 if (se) |
49d608aecc4d
Framework for handling local stack frame and/or variables
lost@l-w.ca
parents:
31
diff
changeset
|
229 { |
49d608aecc4d
Framework for handling local stack frame and/or variables
lost@l-w.ca
parents:
31
diff
changeset
|
230 lwb_error("Duplicate parameter name %s\n", pn); |
49d608aecc4d
Framework for handling local stack frame and/or variables
lost@l-w.ca
parents:
31
diff
changeset
|
231 } |
49d608aecc4d
Framework for handling local stack frame and/or variables
lost@l-w.ca
parents:
31
diff
changeset
|
232 symtab_register(state -> local_syms, pn, paramsize, symtype_param, NULL); |
49d608aecc4d
Framework for handling local stack frame and/or variables
lost@l-w.ca
parents:
31
diff
changeset
|
233 paramsize += sizeof_type(pt); |
49d608aecc4d
Framework for handling local stack frame and/or variables
lost@l-w.ca
parents:
31
diff
changeset
|
234 lw_free(pn); |
25 | 235 |
236 if (state -> lexer_token == token_char && state -> lexer_token_string[0] == ',') | |
237 { | |
238 lexer(state); | |
239 goto paramagain; | |
240 } | |
241 | |
32
49d608aecc4d
Framework for handling local stack frame and/or variables
lost@l-w.ca
parents:
31
diff
changeset
|
242 noparms: |
49d608aecc4d
Framework for handling local stack frame and/or variables
lost@l-w.ca
parents:
31
diff
changeset
|
243 rt = -1; |
25 | 244 if (!issub) |
245 { | |
246 if (state -> lexer_token != token_kw_returns) | |
247 { | |
248 lwb_error("FUNCTION must have RETURNS\n"); | |
249 } | |
250 lexer(state); | |
32
49d608aecc4d
Framework for handling local stack frame and/or variables
lost@l-w.ca
parents:
31
diff
changeset
|
251 /* if (state -> lexer_token == token_identifier) |
25 | 252 { |
253 printf("Return value named: %s\n", state -> lexer_token_string); | |
32
49d608aecc4d
Framework for handling local stack frame and/or variables
lost@l-w.ca
parents:
31
diff
changeset
|
254 |
25 | 255 lexer(state); |
256 if (state -> lexer_token != token_kw_as) | |
257 lwb_error("Execting AS after RETURNS"); | |
258 lexer(state); | |
259 } | |
32
49d608aecc4d
Framework for handling local stack frame and/or variables
lost@l-w.ca
parents:
31
diff
changeset
|
260 */ |
25 | 261 rt = parse_type(state); |
262 } | |
263 else | |
264 { | |
265 if (state -> lexer_token == token_kw_returns) | |
266 { | |
267 lwb_error("SUB cannot specify RETURNS\n"); | |
268 } | |
269 } | |
270 | |
271 | |
272 if (state -> lexer_token != token_eol) | |
273 { | |
31
574931d87abd
Created a function to prettyprint the current lexer token
lost@l-w.ca
parents:
30
diff
changeset
|
274 lwb_error("EOL expected; found %s\n", lexer_return_token(state)); |
25 | 275 } |
26
26aa76da75ad
Additional parsing in function/sub; emission of prolog/epilog code
lost@l-w.ca
parents:
25
diff
changeset
|
276 |
26aa76da75ad
Additional parsing in function/sub; emission of prolog/epilog code
lost@l-w.ca
parents:
25
diff
changeset
|
277 |
32
49d608aecc4d
Framework for handling local stack frame and/or variables
lost@l-w.ca
parents:
31
diff
changeset
|
278 se = symtab_find(state -> global_syms, subname); |
49d608aecc4d
Framework for handling local stack frame and/or variables
lost@l-w.ca
parents:
31
diff
changeset
|
279 if (se) |
49d608aecc4d
Framework for handling local stack frame and/or variables
lost@l-w.ca
parents:
31
diff
changeset
|
280 { |
49d608aecc4d
Framework for handling local stack frame and/or variables
lost@l-w.ca
parents:
31
diff
changeset
|
281 lwb_error("Multiply defined symbol %s\n", subname); |
49d608aecc4d
Framework for handling local stack frame and/or variables
lost@l-w.ca
parents:
31
diff
changeset
|
282 } |
49d608aecc4d
Framework for handling local stack frame and/or variables
lost@l-w.ca
parents:
31
diff
changeset
|
283 |
49d608aecc4d
Framework for handling local stack frame and/or variables
lost@l-w.ca
parents:
31
diff
changeset
|
284 symtab_register(state -> global_syms, subname, -1, issub ? symtype_sub : symtype_func, NULL); |
26
26aa76da75ad
Additional parsing in function/sub; emission of prolog/epilog code
lost@l-w.ca
parents:
25
diff
changeset
|
285 |
26aa76da75ad
Additional parsing in function/sub; emission of prolog/epilog code
lost@l-w.ca
parents:
25
diff
changeset
|
286 state -> currentsub = subname; |
32
49d608aecc4d
Framework for handling local stack frame and/or variables
lost@l-w.ca
parents:
31
diff
changeset
|
287 state -> returntype = rt; |
26
26aa76da75ad
Additional parsing in function/sub; emission of prolog/epilog code
lost@l-w.ca
parents:
25
diff
changeset
|
288 /* consume EOL */ |
26aa76da75ad
Additional parsing in function/sub; emission of prolog/epilog code
lost@l-w.ca
parents:
25
diff
changeset
|
289 lexer(state); |
26aa76da75ad
Additional parsing in function/sub; emission of prolog/epilog code
lost@l-w.ca
parents:
25
diff
changeset
|
290 |
26aa76da75ad
Additional parsing in function/sub; emission of prolog/epilog code
lost@l-w.ca
parents:
25
diff
changeset
|
291 /* variable declarations */ |
32
49d608aecc4d
Framework for handling local stack frame and/or variables
lost@l-w.ca
parents:
31
diff
changeset
|
292 parse_decls(state); |
26
26aa76da75ad
Additional parsing in function/sub; emission of prolog/epilog code
lost@l-w.ca
parents:
25
diff
changeset
|
293 |
26aa76da75ad
Additional parsing in function/sub; emission of prolog/epilog code
lost@l-w.ca
parents:
25
diff
changeset
|
294 /* output function/sub prolog */ |
32
49d608aecc4d
Framework for handling local stack frame and/or variables
lost@l-w.ca
parents:
31
diff
changeset
|
295 emit_prolog(state, vis); |
26
26aa76da75ad
Additional parsing in function/sub; emission of prolog/epilog code
lost@l-w.ca
parents:
25
diff
changeset
|
296 |
26aa76da75ad
Additional parsing in function/sub; emission of prolog/epilog code
lost@l-w.ca
parents:
25
diff
changeset
|
297 /* parse statement block */ |
34 | 298 parse_statements(state); |
26
26aa76da75ad
Additional parsing in function/sub; emission of prolog/epilog code
lost@l-w.ca
parents:
25
diff
changeset
|
299 |
26aa76da75ad
Additional parsing in function/sub; emission of prolog/epilog code
lost@l-w.ca
parents:
25
diff
changeset
|
300 if (issub) |
26aa76da75ad
Additional parsing in function/sub; emission of prolog/epilog code
lost@l-w.ca
parents:
25
diff
changeset
|
301 { |
26aa76da75ad
Additional parsing in function/sub; emission of prolog/epilog code
lost@l-w.ca
parents:
25
diff
changeset
|
302 if (state -> lexer_token != token_kw_endsub) |
26aa76da75ad
Additional parsing in function/sub; emission of prolog/epilog code
lost@l-w.ca
parents:
25
diff
changeset
|
303 { |
31
574931d87abd
Created a function to prettyprint the current lexer token
lost@l-w.ca
parents:
30
diff
changeset
|
304 lwb_error("Expecting ENDSUB, got %s\n", lexer_return_token(state)); |
26
26aa76da75ad
Additional parsing in function/sub; emission of prolog/epilog code
lost@l-w.ca
parents:
25
diff
changeset
|
305 } |
26aa76da75ad
Additional parsing in function/sub; emission of prolog/epilog code
lost@l-w.ca
parents:
25
diff
changeset
|
306 } |
26aa76da75ad
Additional parsing in function/sub; emission of prolog/epilog code
lost@l-w.ca
parents:
25
diff
changeset
|
307 else |
26aa76da75ad
Additional parsing in function/sub; emission of prolog/epilog code
lost@l-w.ca
parents:
25
diff
changeset
|
308 { |
26aa76da75ad
Additional parsing in function/sub; emission of prolog/epilog code
lost@l-w.ca
parents:
25
diff
changeset
|
309 if (state -> lexer_token != token_kw_endfunction) |
26aa76da75ad
Additional parsing in function/sub; emission of prolog/epilog code
lost@l-w.ca
parents:
25
diff
changeset
|
310 { |
31
574931d87abd
Created a function to prettyprint the current lexer token
lost@l-w.ca
parents:
30
diff
changeset
|
311 lwb_error("Expecting ENDFUNCTION, got %s\n", lexer_return_token(state)); |
26
26aa76da75ad
Additional parsing in function/sub; emission of prolog/epilog code
lost@l-w.ca
parents:
25
diff
changeset
|
312 } |
26aa76da75ad
Additional parsing in function/sub; emission of prolog/epilog code
lost@l-w.ca
parents:
25
diff
changeset
|
313 } |
26aa76da75ad
Additional parsing in function/sub; emission of prolog/epilog code
lost@l-w.ca
parents:
25
diff
changeset
|
314 /* output function/sub epilog */ |
32
49d608aecc4d
Framework for handling local stack frame and/or variables
lost@l-w.ca
parents:
31
diff
changeset
|
315 emit_epilog(state); |
26
26aa76da75ad
Additional parsing in function/sub; emission of prolog/epilog code
lost@l-w.ca
parents:
25
diff
changeset
|
316 |
26aa76da75ad
Additional parsing in function/sub; emission of prolog/epilog code
lost@l-w.ca
parents:
25
diff
changeset
|
317 lw_free(state -> currentsub); |
26aa76da75ad
Additional parsing in function/sub; emission of prolog/epilog code
lost@l-w.ca
parents:
25
diff
changeset
|
318 state -> currentsub = NULL; |
32
49d608aecc4d
Framework for handling local stack frame and/or variables
lost@l-w.ca
parents:
31
diff
changeset
|
319 symtab_destroy(state -> local_syms); |
49d608aecc4d
Framework for handling local stack frame and/or variables
lost@l-w.ca
parents:
31
diff
changeset
|
320 state -> local_syms = NULL; |
25 | 321 } |
322 | |
30
bcd532a90e53
Renamed "compiler" to "parser" for more consistent terminology
lost@l-w.ca
parents:
27
diff
changeset
|
323 void parser(cstate *state) |
25 | 324 { |
325 state -> lexer_curchar = -1; | |
32
49d608aecc4d
Framework for handling local stack frame and/or variables
lost@l-w.ca
parents:
31
diff
changeset
|
326 state -> global_syms = symtab_init(); |
49d608aecc4d
Framework for handling local stack frame and/or variables
lost@l-w.ca
parents:
31
diff
changeset
|
327 |
25 | 328 /* now look for a global declaration */ |
329 for (;;) | |
330 { | |
331 state -> parser_state = parser_state_global; | |
332 lexer(state); | |
333 switch (state -> lexer_token) | |
334 { | |
335 case token_kw_function: | |
336 printf("Function\n"); | |
337 parse_subfunc(state, 0); | |
338 break; | |
339 | |
340 case token_kw_sub: | |
341 printf("Sub\n"); | |
342 parse_subfunc(state, 1); | |
343 break; | |
344 | |
345 /* blank lines are allowed */ | |
346 case token_eol: | |
347 continue; | |
348 | |
349 /* EOF is allowed - end of parsing */ | |
350 case token_eof: | |
351 return; | |
352 | |
353 default: | |
31
574931d87abd
Created a function to prettyprint the current lexer token
lost@l-w.ca
parents:
30
diff
changeset
|
354 lwb_error("Invalid token '%s' in global state\n", lexer_return_token(state)); |
25 | 355 } |
356 } | |
357 } |