blob: b2027e5ad716d6e1d63581a221924824f839f2c0 [file] [log] [blame]
Amaury Pouly9d7df9a2011-09-15 16:10:31 +00001/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2011 Amaury Pouly
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
16 *
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
19 *
20 ****************************************************************************/
21
Amaury Pouly58279372011-11-06 01:49:13 +000022#define _POSIX_C_SOURCE 200809L /* for strdup */
Amaury Pouly9d7df9a2011-09-15 16:10:31 +000023#include <stdio.h>
24#include <ctype.h>
25#include <stdint.h>
Amaury Pouly66dce4b2011-09-16 22:27:16 +000026#include <string.h>
Amaury Pouly9d7df9a2011-09-15 16:10:31 +000027#include "dbparser.h"
Amaury Poulye36471d2011-11-01 11:23:43 +000028#include "misc.h"
Amaury Pouly9d7df9a2011-09-15 16:10:31 +000029
30enum lexem_type_t
31{
32 LEX_IDENTIFIER,
33 LEX_LPAREN,
34 LEX_RPAREN,
35 LEX_NUMBER,
36 LEX_STRING, /* double-quoted string */
37 LEX_EQUAL,
38 LEX_SEMICOLON,
39 LEX_LBRACE,
40 LEX_RBRACE,
41 LEX_RANGLE,
Amaury Pouly66dce4b2011-09-16 22:27:16 +000042 LEX_OR,
43 LEX_LSHIFT,
44 LEX_COLON,
45 LEX_LE,
Amaury Pouly9d7df9a2011-09-15 16:10:31 +000046 LEX_EOF
47};
48
49struct lexem_t
50{
51 enum lexem_type_t type;
Amaury Pouly58279372011-11-06 01:49:13 +000052 /* if str is not NULL, it must be a malloc'd pointer */
Amaury Pouly9d7df9a2011-09-15 16:10:31 +000053 char *str;
54 uint32_t num;
55 int line;
56 const char *file;
57};
58
59struct context_t
60{
61 const char *file;
62 char *begin;
63 char *end;
64 char *ptr;
65 int line;
66};
67
68#define parse_error(ctx, ...) \
69 do { fprintf(stderr, "%s:%d: ", ctx->file, ctx->line); \
70 fprintf(stderr, __VA_ARGS__); exit(2); } while(0)
71
72static void advance(struct context_t *ctx, int nr_chars)
73{
74 while(nr_chars--)
75 {
76 if(*(ctx->ptr++) == '\n')
77 ctx->line++;
78 }
79}
80
81static inline bool eof(struct context_t *ctx)
82{
83 return ctx->ptr == ctx->end;
84}
85
86static inline bool next_valid(struct context_t *ctx, int nr)
87{
88 return ctx->ptr + nr < ctx->end;
89}
90
91static inline char cur_char(struct context_t *ctx)
92{
93 return *ctx->ptr;
94}
95
96static inline char next_char(struct context_t *ctx, int nr)
97{
98 return ctx->ptr[nr];
99}
100
101static inline void locate_lexem(struct lexem_t *lex, struct context_t *ctx)
102{
103 lex->file = ctx->file;
104 lex->line = ctx->line;
105}
106
107static void __parse_string(struct context_t *ctx, void *user, void (*emit_fn)(void *user, char c))
108{
109 while(!eof(ctx))
110 {
111 if(cur_char(ctx) == '"')
112 break;
113 else if(cur_char(ctx) == '\\')
114 {
115 advance(ctx, 1);
116 if(eof(ctx))
117 parse_error(ctx, "Unfinished string\n");
118 if(cur_char(ctx) == '\\') emit_fn(user, '\\');
119 else if(cur_char(ctx) == '\'') emit_fn(user, '\'');
120 else if(cur_char(ctx) == '\"') emit_fn(user, '\"');
121 else parse_error(ctx, "Unknown escape sequence \\%c\n", cur_char(ctx));
122 advance(ctx, 1);
123 }
124 else
125 {
126 emit_fn(user, cur_char(ctx));
127 advance(ctx, 1);
128 }
129 }
130 if(eof(ctx) || cur_char(ctx) != '"')
131 parse_error(ctx, "Unfinished string\n");
132 advance(ctx, 1);
133}
134
135static void __parse_string_emit(void *user, char c)
136{
137 char **pstr = (char **)user;
138 *(*pstr)++ = c;
139}
140
141static void __parse_string_count(void *user, char c)
142{
143 (void) c;
144 (*(int *)user)++;
145}
146
147static void parse_string(struct context_t *ctx, struct lexem_t *lexem)
148{
149 locate_lexem(lexem, ctx);
150 /* skip " */
151 advance(ctx, 1);
152 /* compute length */
153 struct context_t cpy_ctx = *ctx;
154 int length = 0;
155 __parse_string(&cpy_ctx, (void *)&length, __parse_string_count);
156 /* parse again */
157 lexem->type = LEX_STRING;
158 lexem->str = xmalloc(length + 1);
159 lexem->str[length] = 0;
160 char *pstr = lexem->str;
161 __parse_string(ctx, (void *)&pstr, __parse_string_emit);
162}
163
164static void parse_ascii_number(struct context_t *ctx, struct lexem_t *lexem)
165{
166 locate_lexem(lexem, ctx);
167 /* skip ' */
168 advance(ctx, 1);
169 /* we expect n<=4 character and then ' */
170 int len = 0;
171 uint32_t value = 0;
172 while(!eof(ctx))
173 {
174 if(cur_char(ctx) != '\'')
175 {
176 value = value << 8 | cur_char(ctx);
177 len++;
178 advance(ctx, 1);
179 }
180 else
181 break;
182 }
183 if(eof(ctx) || cur_char(ctx) != '\'')
184 parse_error(ctx, "Unterminated ascii number literal\n");
185 if(len == 0 || len > 4)
186 parse_error(ctx, "Invalid ascii number literal length: only 1 to 4 characters allowed\n");
187 /* skip ' */
188 advance(ctx, 1);
189 lexem->type = LEX_NUMBER;
190 lexem->num = value;
191}
192
193static void parse_number(struct context_t *ctx, struct lexem_t *lexem)
194{
195 locate_lexem(lexem, ctx);
196 /* check base */
197 int base = 10;
198 if(cur_char(ctx) == '0' && next_valid(ctx, 1) && next_char(ctx, 1) == 'x')
199 {
200 advance(ctx, 2);
201 base = 16;
202 }
203
204 lexem->type = LEX_NUMBER;
205 lexem->num = 0;
206 while(!eof(ctx) && isxdigit(cur_char(ctx)))
207 {
208 if(base == 10 && !isdigit(cur_char(ctx)))
209 break;
210 byte v;
211 if(convxdigit(cur_char(ctx), &v))
212 break;
213 lexem->num = base * lexem->num + v;
214 advance(ctx, 1);
215 }
216}
217
218static void parse_identifier(struct context_t *ctx, struct lexem_t *lexem)
219{
220 locate_lexem(lexem, ctx);
221 /* remember position */
222 char *old = ctx->ptr;
223 while(!eof(ctx) && (isalnum(cur_char(ctx)) || cur_char(ctx) == '_'))
224 advance(ctx, 1);
225 lexem->type = LEX_IDENTIFIER;
226 int len = ctx->ptr - old;
227 lexem->str = xmalloc(len + 1);
228 lexem->str[len] = 0;
229 memcpy(lexem->str, old, len);
230}
231
232static void next_lexem(struct context_t *ctx, struct lexem_t *lexem)
233{
234 #define ret_simple(t, adv) \
235 do {locate_lexem(lexem, ctx); \
236 lexem->type = t; \
237 advance(ctx, adv); \
238 return;} while(0)
239 while(!eof(ctx))
240 {
241 char c = cur_char(ctx);
242 /* skip whitespace */
243 if(c == ' ' || c == '\t' || c == '\n' || c == '\r')
244 {
245 advance(ctx, 1);
246 continue;
247 }
248 /* skip C++ style comments */
249 if(c == '/' && next_valid(ctx, 1) && next_char(ctx, 1) == '/')
250 {
251 while(!eof(ctx) && cur_char(ctx) != '\n')
252 advance(ctx, 1);
253 continue;
254 }
255 /* skip C-style comments */
256 if(c == '/' && next_valid(ctx, 1) && next_char(ctx, 1) == '*')
257 {
258 advance(ctx, 2);
259 while(true)
260 {
261 if(!next_valid(ctx, 1))
262 parse_error(ctx, "Unterminated comment");
263 if(cur_char(ctx) == '*' && next_char(ctx, 1) == '/')
264 {
265 advance(ctx, 2);
266 break;
267 }
268 advance(ctx, 1);
269 }
270 continue;
271 }
272 break;
273 }
274 if(eof(ctx)) ret_simple(LEX_EOF, 0);
275 char c = cur_char(ctx);
Amaury Pouly66dce4b2011-09-16 22:27:16 +0000276 bool nv = next_valid(ctx, 1);
277 char nc = nv ? next_char(ctx, 1) : 0;
Amaury Pouly9d7df9a2011-09-15 16:10:31 +0000278 if(c == '(') ret_simple(LEX_LPAREN, 1);
279 if(c == ')') ret_simple(LEX_RPAREN, 1);
280 if(c == '{') ret_simple(LEX_LBRACE, 1);
281 if(c == '}') ret_simple(LEX_RBRACE, 1);
282 if(c == '>') ret_simple(LEX_RANGLE, 1);
283 if(c == '=') ret_simple(LEX_EQUAL, 1);
284 if(c == ';') ret_simple(LEX_SEMICOLON, 1);
Amaury Pouly66dce4b2011-09-16 22:27:16 +0000285 if(c == ',') ret_simple(LEX_COLON, 1);
286 if(c == '|') ret_simple(LEX_OR, 1);
287 if(c == '<' && nv && nc == '<') ret_simple(LEX_LSHIFT, 2);
288 if(c == '<' && nv && nc == '=') ret_simple(LEX_LE, 2);
Amaury Pouly9d7df9a2011-09-15 16:10:31 +0000289 if(c == '"') return parse_string(ctx, lexem);
290 if(c == '\'') return parse_ascii_number(ctx, lexem);
291 if(isdigit(c)) return parse_number(ctx, lexem);
292 if(isalpha(c) || c == '_') return parse_identifier(ctx, lexem);
293 parse_error(ctx, "Unexpected character '%c'\n", c);
294 #undef ret_simple
295}
296
297#if 0
298static void log_lexem(struct lexem_t *lexem)
299{
300 switch(lexem->type)
301 {
302 case LEX_EOF: printf("<eof>"); break;
303 case LEX_EQUAL: printf("="); break;
304 case LEX_IDENTIFIER: printf("id(%s)", lexem->str); break;
305 case LEX_LPAREN: printf("("); break;
306 case LEX_RPAREN: printf(")"); break;
307 case LEX_LBRACE: printf("{"); break;
308 case LEX_RBRACE: printf("}"); break;
309 case LEX_SEMICOLON: printf(";"); break;
310 case LEX_NUMBER: printf("num(%d)", lexem->num); break;
311 case LEX_STRING: printf("str(%s)", lexem->str); break;
Amaury Pouly66dce4b2011-09-16 22:27:16 +0000312 case LEX_OR: printf("|"); break;
313 case LEX_LSHIFT: printf("<<"); break;
Amaury Pouly9d7df9a2011-09-15 16:10:31 +0000314 default: printf("<unk>");
315 }
316}
317#endif
318
319struct cmd_source_t *db_find_source_by_id(struct cmd_file_t *cmd_file, const char *id)
320{
321 struct cmd_source_t *src = cmd_file->source_list;
322 while(src)
323 {
324 if(strcmp(src->identifier, id) == 0)
325 return src;
326 src = src->next;
327 }
328 return NULL;
329}
330
Amaury Pouly66dce4b2011-09-16 22:27:16 +0000331struct cmd_option_t *db_find_option_by_id(struct cmd_option_t *opt, const char *name)
Amaury Pouly9d7df9a2011-09-15 16:10:31 +0000332{
Amaury Pouly66dce4b2011-09-16 22:27:16 +0000333 while(opt)
334 {
335 if(strcmp(opt->name, name) == 0)
336 return opt;
337 opt = opt->next;
338 }
339 return NULL;
Amaury Pouly9d7df9a2011-09-15 16:10:31 +0000340}
341
342#define INVALID_SB_SUBVERSION 0xffff
343
344static uint16_t parse_sb_subversion(char *str)
345{
346 int len = strlen(str);
347 uint16_t n = 0;
348 if(len == 0 || len > 4)
349 return INVALID_SB_SUBVERSION;
350 for(int i = 0; i < len; i++)
351 {
352 if(!isdigit(str[i]))
353 return INVALID_SB_SUBVERSION;
354 n = n << 4 | (str[i] - '0');
355 }
356 return n;
357}
358
359bool db_parse_sb_version(struct sb_version_t *ver, char *str)
360{
361 int len = strlen(str);
362 int cnt = 0;
363 int pos[2];
364
365 for(int i = 0; i < len; i++)
366 {
367 if(str[i] != '.')
368 continue;
369 if(cnt == 2)
370 return false;
371 pos[cnt++] = i + 1;
372 str[i] = 0;
373 }
374 if(cnt != 2)
375 return false;
376 ver->major = parse_sb_subversion(str);
377 ver->minor = parse_sb_subversion(str + pos[0]);
378 ver->revision = parse_sb_subversion(str + pos[1]);
379 return ver->major != INVALID_SB_SUBVERSION &&
380 ver->minor != INVALID_SB_SUBVERSION &&
381 ver->revision != INVALID_SB_SUBVERSION;
382}
383
384#undef parse_error
385#define parse_error(lexem, ...) \
386 do { fprintf(stderr, "%s:%d: ", lexem.file, lexem.line); \
387 fprintf(stderr, __VA_ARGS__); exit(2); } while(0)
388
Amaury Pouly66dce4b2011-09-16 22:27:16 +0000389struct lex_ctx_t
390{
391 struct context_t ctx;
392 struct lexem_t lexem;
393};
394
Amaury Pouly58279372011-11-06 01:49:13 +0000395/* When lexems hold strings (like identifier), it might be useful to steal
396 * the pointer and don't clean the lexem but in other case, one don't want
397 * to keep the pointer to the string and just want to release the memory.
398 * Thus clean_lexem should be true except when one keeps a pointer */
399static inline void next(struct lex_ctx_t *ctx, bool clean_lexem)
Amaury Pouly66dce4b2011-09-16 22:27:16 +0000400{
Amaury Pouly58279372011-11-06 01:49:13 +0000401 if(clean_lexem)
402 free(ctx->lexem.str);
403 memset(&ctx->lexem, 0, sizeof(struct lexem_t));
Amaury Pouly66dce4b2011-09-16 22:27:16 +0000404 next_lexem(&ctx->ctx, &ctx->lexem);
405}
406
407static uint32_t parse_term_expr(struct lex_ctx_t *ctx, struct cmd_option_t *const_list)
408{
409 uint32_t ret = 0;
410 if(ctx->lexem.type == LEX_NUMBER)
411 ret = ctx->lexem.num;
412 else if(ctx->lexem.type == LEX_IDENTIFIER)
413 {
414 struct cmd_option_t *c = db_find_option_by_id(const_list, ctx->lexem.str);
415 if(c == NULL)
416 parse_error(ctx->lexem, "Undefined reference to constant '%s'\n", ctx->lexem.str);
417 if(c->is_string)
418 parse_error(ctx->lexem, "Internal error: constant '%s' is not an integer\n", ctx->lexem.str);
419 ret = c->val;
420 }
421 else
422 parse_error(ctx->lexem, "Number or constant identifier expected\n");
Amaury Pouly58279372011-11-06 01:49:13 +0000423 next(ctx, true);
Amaury Pouly66dce4b2011-09-16 22:27:16 +0000424 return ret;
425}
426
427static uint32_t parse_shift_expr(struct lex_ctx_t *ctx, struct cmd_option_t *const_list)
428{
429 uint32_t v = parse_term_expr(ctx, const_list);
430 while(ctx->lexem.type == LEX_LSHIFT)
431 {
Amaury Pouly58279372011-11-06 01:49:13 +0000432 next(ctx, true);
Amaury Pouly66dce4b2011-09-16 22:27:16 +0000433 v <<= parse_term_expr(ctx, const_list);
434 }
435 return v;
436}
437
438static uint32_t parse_or_expr(struct lex_ctx_t *ctx, struct cmd_option_t *const_list)
439{
440 uint32_t v = parse_shift_expr(ctx, const_list);
441 while(ctx->lexem.type == LEX_OR)
442 {
Amaury Pouly58279372011-11-06 01:49:13 +0000443 next(ctx, true);
Amaury Pouly66dce4b2011-09-16 22:27:16 +0000444 v |= parse_shift_expr(ctx, const_list);
445 }
446 return v;
447}
448
449static uint32_t parse_intexpr(struct lex_ctx_t *ctx, struct cmd_option_t *const_list)
450{
451 return parse_or_expr(ctx, const_list);
452}
453
454#define NR_INITIAL_CONSTANTS 4
455static char *init_const_name[NR_INITIAL_CONSTANTS] = {"true", "false", "yes", "no"};
456static uint32_t init_const_value[NR_INITIAL_CONSTANTS] = {1, 0, 1, 0};
457
Amaury Pouly9d7df9a2011-09-15 16:10:31 +0000458struct cmd_file_t *db_parse_file(const char *file)
459{
460 size_t size;
461 FILE *f = fopen(file, "r");
462 if(f == NULL)
463 bugp("Cannot open file '%s'", file);
464 fseek(f, 0, SEEK_END);
465 size = ftell(f);
466 fseek(f, 0, SEEK_SET);
467 char *buf = xmalloc(size);
468 if(fread(buf, size, 1, f) != 1)
469 bugp("Cannot read file '%s'", file);
470 fclose(f);
471
472 if(g_debug)
473 printf("Parsing db file '%s'\n", file);
474 struct cmd_file_t *cmd_file = xmalloc(sizeof(struct cmd_file_t));
475 memset(cmd_file, 0, sizeof(struct cmd_file_t));
476
Amaury Pouly66dce4b2011-09-16 22:27:16 +0000477 /* add initial constants */
478 for(int i = 0; i < NR_INITIAL_CONSTANTS; i++)
479 {
480 struct cmd_option_t *opt = xmalloc(sizeof(struct cmd_option_t));
481 memset(opt, 0, sizeof(struct cmd_option_t));
Amaury Pouly58279372011-11-06 01:49:13 +0000482 opt->name = strdup(init_const_name[i]);
Amaury Pouly66dce4b2011-09-16 22:27:16 +0000483 opt->is_string = false;
484 opt->val = init_const_value[i];
485 opt->next = cmd_file->constant_list;
486 cmd_file->constant_list = opt;
487 }
Amaury Pouly9d7df9a2011-09-15 16:10:31 +0000488
Amaury Pouly66dce4b2011-09-16 22:27:16 +0000489 struct lex_ctx_t lctx;
490 lctx.ctx.file = file;
491 lctx.ctx.line = 1;
492 lctx.ctx.begin = buf;
493 lctx.ctx.ptr = buf;
494 lctx.ctx.end = buf + size;
Amaury Pouly58279372011-11-06 01:49:13 +0000495 #define next(clean_lexem) next(&lctx, clean_lexem)
Amaury Pouly66dce4b2011-09-16 22:27:16 +0000496 #define lexem lctx.lexem
Amaury Pouly9d7df9a2011-09-15 16:10:31 +0000497 /* init lexer */
Amaury Pouly58279372011-11-06 01:49:13 +0000498 next(false); /* don't clean init lexem because it doesn't exist */
Amaury Pouly66dce4b2011-09-16 22:27:16 +0000499 /* constants ? */
500 if(lexem.type == LEX_IDENTIFIER && !strcmp(lexem.str, "constants"))
501 {
Amaury Pouly58279372011-11-06 01:49:13 +0000502 next(true);
Amaury Pouly66dce4b2011-09-16 22:27:16 +0000503 if(lexem.type != LEX_LBRACE)
504 parse_error(lexem, "'{' expected after 'constants'\n");
505
506 while(true)
507 {
508 struct cmd_option_t *opt = xmalloc(sizeof(struct cmd_option_t));
509 memset(opt, 0, sizeof(struct cmd_option_t));
Amaury Pouly58279372011-11-06 01:49:13 +0000510 next(true);
Amaury Pouly66dce4b2011-09-16 22:27:16 +0000511 if(lexem.type == LEX_RBRACE)
512 break;
513 if(lexem.type != LEX_IDENTIFIER)
514 parse_error(lexem, "Identifier expected in constants\n");
515 opt->name = lexem.str;
Amaury Pouly58279372011-11-06 01:49:13 +0000516 next(false); /* lexem string is kept as option name */
Amaury Pouly66dce4b2011-09-16 22:27:16 +0000517 if(lexem.type != LEX_EQUAL)
518 parse_error(lexem, "'=' expected after identifier\n");
Amaury Pouly58279372011-11-06 01:49:13 +0000519 next(true);
Amaury Pouly66dce4b2011-09-16 22:27:16 +0000520 opt->is_string = false;
521 opt->val = parse_intexpr(&lctx, cmd_file->constant_list);
522 opt->next = cmd_file->constant_list;
523 cmd_file->constant_list = opt;
524 if(lexem.type != LEX_SEMICOLON)
525 parse_error(lexem, "';' expected after string\n");
526 }
Amaury Pouly58279372011-11-06 01:49:13 +0000527 next(true);
Amaury Pouly66dce4b2011-09-16 22:27:16 +0000528 }
Amaury Pouly9d7df9a2011-09-15 16:10:31 +0000529 /* options ? */
530 if(lexem.type == LEX_IDENTIFIER && !strcmp(lexem.str, "options"))
531 {
Amaury Pouly58279372011-11-06 01:49:13 +0000532 next(true);
Amaury Pouly9d7df9a2011-09-15 16:10:31 +0000533 if(lexem.type != LEX_LBRACE)
534 parse_error(lexem, "'{' expected after 'options'\n");
535
536 while(true)
537 {
Amaury Pouly58279372011-11-06 01:49:13 +0000538 next(true);
Amaury Pouly9d7df9a2011-09-15 16:10:31 +0000539 if(lexem.type == LEX_RBRACE)
540 break;
Amaury Pouly58279372011-11-06 01:49:13 +0000541 struct cmd_option_t *opt = xmalloc(sizeof(struct cmd_option_t));
542 memset(opt, 0, sizeof(struct cmd_option_t));
Amaury Pouly9d7df9a2011-09-15 16:10:31 +0000543 if(lexem.type != LEX_IDENTIFIER)
544 parse_error(lexem, "Identifier expected in options\n");
Amaury Pouly66dce4b2011-09-16 22:27:16 +0000545 opt->name = lexem.str;
Amaury Pouly58279372011-11-06 01:49:13 +0000546 next(false); /* lexem string is kept as option name */
Amaury Pouly9d7df9a2011-09-15 16:10:31 +0000547 if(lexem.type != LEX_EQUAL)
548 parse_error(lexem, "'=' expected after identifier\n");
Amaury Pouly58279372011-11-06 01:49:13 +0000549 next(true);
Amaury Pouly66dce4b2011-09-16 22:27:16 +0000550 if(lexem.type == LEX_STRING)
Amaury Pouly9d7df9a2011-09-15 16:10:31 +0000551 {
Amaury Pouly66dce4b2011-09-16 22:27:16 +0000552 opt->is_string = true;
553 opt->str = lexem.str;
Amaury Pouly58279372011-11-06 01:49:13 +0000554 next(false); /* lexem string is kept as option name */
Amaury Pouly9d7df9a2011-09-15 16:10:31 +0000555 }
556 else
Amaury Pouly66dce4b2011-09-16 22:27:16 +0000557 {
558 opt->is_string = false;
559 opt->val = parse_intexpr(&lctx, cmd_file->constant_list);
560 }
561 opt->next = cmd_file->opt_list;
562 cmd_file->opt_list = opt;
Amaury Pouly9d7df9a2011-09-15 16:10:31 +0000563 if(lexem.type != LEX_SEMICOLON)
564 parse_error(lexem, "';' expected after string\n");
565 }
Amaury Pouly58279372011-11-06 01:49:13 +0000566 next(true);
Amaury Pouly9d7df9a2011-09-15 16:10:31 +0000567 }
568 /* sources */
569 if(lexem.type != LEX_IDENTIFIER || strcmp(lexem.str, "sources"))
570 parse_error(lexem, "'sources' expected\n");
Amaury Pouly58279372011-11-06 01:49:13 +0000571 next(true);
Amaury Pouly9d7df9a2011-09-15 16:10:31 +0000572 if(lexem.type != LEX_LBRACE)
573 parse_error(lexem, "'{' expected after 'sources'\n");
574
575 while(true)
576 {
Amaury Pouly58279372011-11-06 01:49:13 +0000577 next(true);
Amaury Pouly9d7df9a2011-09-15 16:10:31 +0000578 if(lexem.type == LEX_RBRACE)
579 break;
580 struct cmd_source_t *src = xmalloc(sizeof(struct cmd_source_t));
581 memset(src, 0, sizeof(struct cmd_source_t));
Amaury Pouly9d7df9a2011-09-15 16:10:31 +0000582 if(lexem.type != LEX_IDENTIFIER)
583 parse_error(lexem, "identifier expected in sources\n");
584 src->identifier = lexem.str;
Amaury Pouly58279372011-11-06 01:49:13 +0000585 next(false); /* lexem string is kept as source name */
Amaury Pouly9d7df9a2011-09-15 16:10:31 +0000586 if(lexem.type != LEX_EQUAL)
587 parse_error(lexem, "'=' expected after identifier\n");
Amaury Pouly58279372011-11-06 01:49:13 +0000588 next(true);
Amaury Pouly66dce4b2011-09-16 22:27:16 +0000589 if(lexem.type == LEX_STRING)
590 {
591 src->is_extern = false;
592 src->filename = lexem.str;
Amaury Pouly58279372011-11-06 01:49:13 +0000593 next(false); /* lexem string is kept as file name */
Amaury Pouly66dce4b2011-09-16 22:27:16 +0000594 }
595 else if(lexem.type == LEX_IDENTIFIER && !strcmp(lexem.str, "extern"))
596 {
597 src->is_extern = true;
Amaury Pouly58279372011-11-06 01:49:13 +0000598 src->filename = strdup("<extern>"); /* duplicate because it will be free'd */
599 next(true);
Amaury Pouly66dce4b2011-09-16 22:27:16 +0000600 if(lexem.type != LEX_LPAREN)
601 parse_error(lexem, "'(' expected after 'extern'\n");
Amaury Pouly58279372011-11-06 01:49:13 +0000602 next(true);
Amaury Pouly66dce4b2011-09-16 22:27:16 +0000603 src->extern_nr = parse_intexpr(&lctx, cmd_file->constant_list);
604 if(lexem.type != LEX_RPAREN)
605 parse_error(lexem, "')' expected\n");
Amaury Pouly58279372011-11-06 01:49:13 +0000606 next(true);
Amaury Pouly66dce4b2011-09-16 22:27:16 +0000607 }
608 else
609 parse_error(lexem, "String or 'extern' expected after '='\n");
Amaury Pouly9d7df9a2011-09-15 16:10:31 +0000610 if(lexem.type != LEX_SEMICOLON)
Amaury Pouly66dce4b2011-09-16 22:27:16 +0000611 parse_error(lexem, "';' expected\n");
Amaury Pouly9d7df9a2011-09-15 16:10:31 +0000612 if(db_find_source_by_id(cmd_file, src->identifier) != NULL)
613 parse_error(lexem, "Duplicate source identifier\n");
614 /* type filled later */
615 src->type = CMD_SRC_UNK;
Amaury Pouly66dce4b2011-09-16 22:27:16 +0000616 src->next = cmd_file->source_list;
Amaury Pouly9d7df9a2011-09-15 16:10:31 +0000617 cmd_file->source_list = src;
618 }
619
620 /* sections */
621 struct cmd_section_t *end_sec = NULL;
622 while(true)
623 {
Amaury Pouly58279372011-11-06 01:49:13 +0000624 next(true);
625 if(lexem.type == LEX_EOF)
626 break;
Amaury Pouly9d7df9a2011-09-15 16:10:31 +0000627 struct cmd_section_t *sec = xmalloc(sizeof(struct cmd_section_t));
628 struct cmd_inst_t *end_list = NULL;
629 memset(sec, 0, sizeof(struct cmd_section_t));
Amaury Pouly9d7df9a2011-09-15 16:10:31 +0000630 if(lexem.type != LEX_IDENTIFIER || strcmp(lexem.str, "section") != 0)
631 parse_error(lexem, "'section' expected\n");
Amaury Pouly58279372011-11-06 01:49:13 +0000632 next(true);
Amaury Pouly9d7df9a2011-09-15 16:10:31 +0000633 if(lexem.type != LEX_LPAREN)
634 parse_error(lexem, "'(' expected after 'section'\n");
Amaury Pouly58279372011-11-06 01:49:13 +0000635 next(true);
Amaury Pouly66dce4b2011-09-16 22:27:16 +0000636 /* can be any number */
637 sec->identifier = parse_intexpr(&lctx, cmd_file->constant_list);
638 /* options ? */
639 if(lexem.type == LEX_SEMICOLON)
Amaury Pouly9d7df9a2011-09-15 16:10:31 +0000640 {
Amaury Pouly66dce4b2011-09-16 22:27:16 +0000641 do
642 {
Amaury Pouly58279372011-11-06 01:49:13 +0000643 next(true);
Amaury Pouly66dce4b2011-09-16 22:27:16 +0000644 struct cmd_option_t *opt = xmalloc(sizeof(struct cmd_option_t));
645 memset(opt, 0, sizeof(struct cmd_option_t));
646 if(lexem.type != LEX_IDENTIFIER)
647 parse_error(lexem, "Identifier expected for section option\n");
648 opt->name = lexem.str;
Amaury Pouly58279372011-11-06 01:49:13 +0000649 next(false); /* lexem string is kept as option name */
Amaury Pouly66dce4b2011-09-16 22:27:16 +0000650 if(lexem.type != LEX_EQUAL)
651 parse_error(lexem, "'=' expected after option identifier\n");
Amaury Pouly58279372011-11-06 01:49:13 +0000652 next(true);
Amaury Pouly66dce4b2011-09-16 22:27:16 +0000653 if(lexem.type == LEX_STRING)
654 {
655 opt->is_string = true;
656 opt->str = lexem.str;
Amaury Pouly58279372011-11-06 01:49:13 +0000657 next(false); /* lexem string is kept as option string */
Amaury Pouly66dce4b2011-09-16 22:27:16 +0000658 }
659 else
660 {
661 opt->is_string = false;
662 opt->val = parse_intexpr(&lctx, cmd_file->constant_list);
663 }
664 opt->next = sec->opt_list;
665 sec->opt_list = opt;
666 }while(lexem.type == LEX_COLON);
Amaury Pouly9d7df9a2011-09-15 16:10:31 +0000667 }
Amaury Pouly9d7df9a2011-09-15 16:10:31 +0000668 if(lexem.type != LEX_RPAREN)
669 parse_error(lexem, "')' expected after section identifier\n");
Amaury Pouly58279372011-11-06 01:49:13 +0000670 next(true);
Amaury Pouly66dce4b2011-09-16 22:27:16 +0000671 if(lexem.type == LEX_LBRACE)
Amaury Pouly9d7df9a2011-09-15 16:10:31 +0000672 {
Amaury Pouly66dce4b2011-09-16 22:27:16 +0000673 sec->is_data = false;
674 /* commands */
675 while(true)
Amaury Pouly9d7df9a2011-09-15 16:10:31 +0000676 {
Amaury Pouly58279372011-11-06 01:49:13 +0000677 next(true);
Amaury Pouly66dce4b2011-09-16 22:27:16 +0000678 if(lexem.type == LEX_RBRACE)
679 break;
Amaury Pouly58279372011-11-06 01:49:13 +0000680 struct cmd_inst_t *inst = xmalloc(sizeof(struct cmd_inst_t));
681 memset(inst, 0, sizeof(struct cmd_inst_t));
Amaury Pouly66dce4b2011-09-16 22:27:16 +0000682 if(lexem.type != LEX_IDENTIFIER)
683 parse_error(lexem, "Instruction expected in section\n");
684 if(strcmp(lexem.str, "load") == 0)
685 inst->type = CMD_LOAD;
686 else if(strcmp(lexem.str, "call") == 0)
687 inst->type = CMD_CALL;
688 else if(strcmp(lexem.str, "jump") == 0)
689 inst->type = CMD_JUMP;
690 else if(strcmp(lexem.str, "mode") == 0)
691 inst->type = CMD_MODE;
692 else
693 parse_error(lexem, "Instruction expected in section\n");
Amaury Pouly58279372011-11-06 01:49:13 +0000694 next(true);
Amaury Pouly66dce4b2011-09-16 22:27:16 +0000695
696 if(inst->type == CMD_LOAD)
Amaury Pouly9d7df9a2011-09-15 16:10:31 +0000697 {
Amaury Pouly66dce4b2011-09-16 22:27:16 +0000698 if(lexem.type != LEX_IDENTIFIER)
699 parse_error(lexem, "Identifier expected after instruction\n");
Amaury Pouly9d7df9a2011-09-15 16:10:31 +0000700 inst->identifier = lexem.str;
701 if(db_find_source_by_id(cmd_file, inst->identifier) == NULL)
702 parse_error(lexem, "Undefined reference to source '%s'\n", inst->identifier);
Amaury Pouly58279372011-11-06 01:49:13 +0000703 next(false); /* lexem string kept as identifier */
Amaury Pouly66dce4b2011-09-16 22:27:16 +0000704 if(lexem.type == LEX_RANGLE)
705 {
706 // load at
707 inst->type = CMD_LOAD_AT;
Amaury Pouly58279372011-11-06 01:49:13 +0000708 next(true);
Amaury Pouly66dce4b2011-09-16 22:27:16 +0000709 inst->addr = parse_intexpr(&lctx, cmd_file->constant_list);
710 }
711 if(lexem.type != LEX_SEMICOLON)
712 parse_error(lexem, "';' expected after command\n");
Amaury Pouly9d7df9a2011-09-15 16:10:31 +0000713 }
Amaury Pouly66dce4b2011-09-16 22:27:16 +0000714 else if(inst->type == CMD_CALL || inst->type == CMD_JUMP)
Amaury Pouly9d7df9a2011-09-15 16:10:31 +0000715 {
Amaury Pouly66dce4b2011-09-16 22:27:16 +0000716 if(lexem.type == LEX_IDENTIFIER)
717 {
718 inst->identifier = lexem.str;
719 if(db_find_source_by_id(cmd_file, inst->identifier) == NULL)
720 parse_error(lexem, "Undefined reference to source '%s'\n", inst->identifier);
Amaury Pouly58279372011-11-06 01:49:13 +0000721 next(false); /* lexem string kept as identifier */
Amaury Pouly66dce4b2011-09-16 22:27:16 +0000722 }
723 else
724 {
725 inst->type = (inst->type == CMD_CALL) ? CMD_CALL_AT : CMD_JUMP_AT;
726 inst->addr = parse_intexpr(&lctx, cmd_file->constant_list);
727 }
728
729 if(lexem.type == LEX_LPAREN)
730 {
Amaury Pouly58279372011-11-06 01:49:13 +0000731 next(true);
Amaury Pouly66dce4b2011-09-16 22:27:16 +0000732 inst->argument = parse_intexpr(&lctx, cmd_file->constant_list);
733 if(lexem.type != LEX_RPAREN)
734 parse_error(lexem, "Expected closing brace\n");
Amaury Pouly58279372011-11-06 01:49:13 +0000735 next(true);
Amaury Pouly66dce4b2011-09-16 22:27:16 +0000736 }
737 if(lexem.type != LEX_SEMICOLON)
738 parse_error(lexem, "';' expected after command\n");
739 }
740 else if(inst->type == CMD_MODE)
741 {
742 inst->argument = parse_intexpr(&lctx, cmd_file->constant_list);
743 if(lexem.type != LEX_SEMICOLON)
744 parse_error(lexem, "Expected ';' after command\n");
Amaury Pouly9d7df9a2011-09-15 16:10:31 +0000745 }
746 else
Amaury Pouly66dce4b2011-09-16 22:27:16 +0000747 parse_error(lexem, "Internal error");
748 if(end_list == NULL)
Amaury Pouly9d7df9a2011-09-15 16:10:31 +0000749 {
Amaury Pouly66dce4b2011-09-16 22:27:16 +0000750 sec->inst_list = inst;
751 end_list = inst;
Amaury Pouly9d7df9a2011-09-15 16:10:31 +0000752 }
Amaury Pouly66dce4b2011-09-16 22:27:16 +0000753 else
754 {
755 end_list->next = inst;
756 end_list = inst;
757 }
Amaury Pouly9d7df9a2011-09-15 16:10:31 +0000758 }
759 }
Amaury Pouly66dce4b2011-09-16 22:27:16 +0000760 else if(lexem.type == LEX_LE)
761 {
762 sec->is_data = true;
Amaury Pouly58279372011-11-06 01:49:13 +0000763 next(true);
Amaury Pouly66dce4b2011-09-16 22:27:16 +0000764 if(lexem.type != LEX_IDENTIFIER)
765 parse_error(lexem, "Identifier expected after '<='\n");
766 sec->source_id = lexem.str;
Amaury Pouly58279372011-11-06 01:49:13 +0000767 next(false); /* lexem string is kept as source id */
Amaury Pouly66dce4b2011-09-16 22:27:16 +0000768 if(lexem.type != LEX_SEMICOLON)
769 parse_error(lexem, "';' expected after identifier\n");
770 }
771 else
772 parse_error(lexem, "'{' or '<=' expected after section directive\n");
Amaury Pouly9d7df9a2011-09-15 16:10:31 +0000773
774 if(end_sec == NULL)
775 {
776 cmd_file->section_list = sec;
777 end_sec = sec;
778 }
779 else
780 {
781 end_sec->next = sec;
782 end_sec = sec;
783 }
784 }
Amaury Pouly66dce4b2011-09-16 22:27:16 +0000785 #undef lexem
Amaury Pouly9d7df9a2011-09-15 16:10:31 +0000786 #undef next
787
Amaury Pouly58279372011-11-06 01:49:13 +0000788 free(buf);
Amaury Pouly9d7df9a2011-09-15 16:10:31 +0000789 return cmd_file;
790}
Amaury Pouly66dce4b2011-09-16 22:27:16 +0000791
792void db_generate_default_sb_version(struct sb_version_t *ver)
793{
794 ver->major = ver->minor = ver->revision = 0x999;
795}
Amaury Pouly58279372011-11-06 01:49:13 +0000796
797void db_free_option_list(struct cmd_option_t *opt_list)
798{
799 while(opt_list)
800 {
801 struct cmd_option_t *next = opt_list->next;
802 fflush(stdout);
803 free(opt_list->name);
804 free(opt_list->str);
805 free(opt_list);
806 opt_list = next;
807 }
808}
809
810void db_free(struct cmd_file_t *file)
811{
812 db_free_option_list(file->opt_list);
813 db_free_option_list(file->constant_list);
814 struct cmd_source_t *src = file->source_list;
815 while(src)
816 {
817 struct cmd_source_t *next = src->next;
818 free(src->identifier);
819 fflush(stdout);
820 free(src->filename);
821 if(src->loaded)
822 {
823 if(src->type == CMD_SRC_BIN)
824 free(src->bin.data);
825 if(src->type == CMD_SRC_ELF)
826 elf_release(&src->elf);
827 }
828 free(src);
829 src = next;
830 }
831 struct cmd_section_t *sec = file->section_list;
832 while(sec)
833 {
834 struct cmd_section_t *next = sec->next;
835 db_free_option_list(sec->opt_list);
836 free(sec->source_id);
837 struct cmd_inst_t *inst = sec->inst_list;
838 while(inst)
839 {
840 struct cmd_inst_t *next = inst->next;
841 free(inst->identifier);
842 free(inst);
843 inst = next;
844 }
845 free(sec);
846 sec = next;
847 }
848 free(file);
849}