summaryrefslogtreecommitdiff
path: root/utils/sbtools/dbparser.c
diff options
context:
space:
mode:
Diffstat (limited to 'utils/sbtools/dbparser.c')
-rw-r--r--utils/sbtools/dbparser.c626
1 files changed, 626 insertions, 0 deletions
diff --git a/utils/sbtools/dbparser.c b/utils/sbtools/dbparser.c
new file mode 100644
index 0000000000..20f2d66c0e
--- /dev/null
+++ b/utils/sbtools/dbparser.c
@@ -0,0 +1,626 @@
1/***************************************************************************
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
22#include <stdio.h>
23#include <ctype.h>
24#include <stdint.h>
25#include "dbparser.h"
26
27typedef uint8_t byte;
28
29extern bool g_debug;
30extern void *xmalloc(size_t s);
31extern int convxdigit(char digit, byte *val);
32
33#define bug(...) do { fprintf(stderr, __VA_ARGS__); exit(1); } while(0)
34#define bugp(...) do { fprintf(stderr, __VA_ARGS__); perror(" "); exit(1); } while(0)
35
36enum lexem_type_t
37{
38 LEX_IDENTIFIER,
39 LEX_LPAREN,
40 LEX_RPAREN,
41 LEX_NUMBER,
42 LEX_STRING, /* double-quoted string */
43 LEX_EQUAL,
44 LEX_SEMICOLON,
45 LEX_LBRACE,
46 LEX_RBRACE,
47 LEX_RANGLE,
48 LEX_EOF
49};
50
51struct lexem_t
52{
53 enum lexem_type_t type;
54 char *str;
55 uint32_t num;
56 int line;
57 const char *file;
58};
59
60struct context_t
61{
62 const char *file;
63 char *begin;
64 char *end;
65 char *ptr;
66 int line;
67};
68
69#define parse_error(ctx, ...) \
70 do { fprintf(stderr, "%s:%d: ", ctx->file, ctx->line); \
71 fprintf(stderr, __VA_ARGS__); exit(2); } while(0)
72
73static void advance(struct context_t *ctx, int nr_chars)
74{
75 while(nr_chars--)
76 {
77 if(*(ctx->ptr++) == '\n')
78 ctx->line++;
79 }
80}
81
82static inline bool eof(struct context_t *ctx)
83{
84 return ctx->ptr == ctx->end;
85}
86
87static inline bool next_valid(struct context_t *ctx, int nr)
88{
89 return ctx->ptr + nr < ctx->end;
90}
91
92static inline char cur_char(struct context_t *ctx)
93{
94 return *ctx->ptr;
95}
96
97static inline char next_char(struct context_t *ctx, int nr)
98{
99 return ctx->ptr[nr];
100}
101
102static inline void locate_lexem(struct lexem_t *lex, struct context_t *ctx)
103{
104 lex->file = ctx->file;
105 lex->line = ctx->line;
106}
107
108static void __parse_string(struct context_t *ctx, void *user, void (*emit_fn)(void *user, char c))
109{
110 while(!eof(ctx))
111 {
112 if(cur_char(ctx) == '"')
113 break;
114 else if(cur_char(ctx) == '\\')
115 {
116 advance(ctx, 1);
117 if(eof(ctx))
118 parse_error(ctx, "Unfinished string\n");
119 if(cur_char(ctx) == '\\') emit_fn(user, '\\');
120 else if(cur_char(ctx) == '\'') emit_fn(user, '\'');
121 else if(cur_char(ctx) == '\"') emit_fn(user, '\"');
122 else parse_error(ctx, "Unknown escape sequence \\%c\n", cur_char(ctx));
123 advance(ctx, 1);
124 }
125 else
126 {
127 emit_fn(user, cur_char(ctx));
128 advance(ctx, 1);
129 }
130 }
131 if(eof(ctx) || cur_char(ctx) != '"')
132 parse_error(ctx, "Unfinished string\n");
133 advance(ctx, 1);
134}
135
136static void __parse_string_emit(void *user, char c)
137{
138 char **pstr = (char **)user;
139 *(*pstr)++ = c;
140}
141
142static void __parse_string_count(void *user, char c)
143{
144 (void) c;
145 (*(int *)user)++;
146}
147
148static void parse_string(struct context_t *ctx, struct lexem_t *lexem)
149{
150 locate_lexem(lexem, ctx);
151 /* skip " */
152 advance(ctx, 1);
153 /* compute length */
154 struct context_t cpy_ctx = *ctx;
155 int length = 0;
156 __parse_string(&cpy_ctx, (void *)&length, __parse_string_count);
157 /* parse again */
158 lexem->type = LEX_STRING;
159 lexem->str = xmalloc(length + 1);
160 lexem->str[length] = 0;
161 char *pstr = lexem->str;
162 __parse_string(ctx, (void *)&pstr, __parse_string_emit);
163}
164
165static void parse_ascii_number(struct context_t *ctx, struct lexem_t *lexem)
166{
167 locate_lexem(lexem, ctx);
168 /* skip ' */
169 advance(ctx, 1);
170 /* we expect n<=4 character and then ' */
171 int len = 0;
172 uint32_t value = 0;
173 while(!eof(ctx))
174 {
175 if(cur_char(ctx) != '\'')
176 {
177 value = value << 8 | cur_char(ctx);
178 len++;
179 advance(ctx, 1);
180 }
181 else
182 break;
183 }
184 if(eof(ctx) || cur_char(ctx) != '\'')
185 parse_error(ctx, "Unterminated ascii number literal\n");
186 if(len == 0 || len > 4)
187 parse_error(ctx, "Invalid ascii number literal length: only 1 to 4 characters allowed\n");
188 /* skip ' */
189 advance(ctx, 1);
190 lexem->type = LEX_NUMBER;
191 lexem->num = value;
192}
193
194static void parse_number(struct context_t *ctx, struct lexem_t *lexem)
195{
196 locate_lexem(lexem, ctx);
197 /* check base */
198 int base = 10;
199 if(cur_char(ctx) == '0' && next_valid(ctx, 1) && next_char(ctx, 1) == 'x')
200 {
201 advance(ctx, 2);
202 base = 16;
203 }
204
205 lexem->type = LEX_NUMBER;
206 lexem->num = 0;
207 while(!eof(ctx) && isxdigit(cur_char(ctx)))
208 {
209 if(base == 10 && !isdigit(cur_char(ctx)))
210 break;
211 byte v;
212 if(convxdigit(cur_char(ctx), &v))
213 break;
214 lexem->num = base * lexem->num + v;
215 advance(ctx, 1);
216 }
217}
218
219static void parse_identifier(struct context_t *ctx, struct lexem_t *lexem)
220{
221 locate_lexem(lexem, ctx);
222 /* remember position */
223 char *old = ctx->ptr;
224 while(!eof(ctx) && (isalnum(cur_char(ctx)) || cur_char(ctx) == '_'))
225 advance(ctx, 1);
226 lexem->type = LEX_IDENTIFIER;
227 int len = ctx->ptr - old;
228 lexem->str = xmalloc(len + 1);
229 lexem->str[len] = 0;
230 memcpy(lexem->str, old, len);
231}
232
233static void next_lexem(struct context_t *ctx, struct lexem_t *lexem)
234{
235 #define ret_simple(t, adv) \
236 do {locate_lexem(lexem, ctx); \
237 lexem->type = t; \
238 advance(ctx, adv); \
239 return;} while(0)
240 while(!eof(ctx))
241 {
242 char c = cur_char(ctx);
243 /* skip whitespace */
244 if(c == ' ' || c == '\t' || c == '\n' || c == '\r')
245 {
246 advance(ctx, 1);
247 continue;
248 }
249 /* skip C++ style comments */
250 if(c == '/' && next_valid(ctx, 1) && next_char(ctx, 1) == '/')
251 {
252 while(!eof(ctx) && cur_char(ctx) != '\n')
253 advance(ctx, 1);
254 continue;
255 }
256 /* skip C-style comments */
257 if(c == '/' && next_valid(ctx, 1) && next_char(ctx, 1) == '*')
258 {
259 advance(ctx, 2);
260 while(true)
261 {
262 if(!next_valid(ctx, 1))
263 parse_error(ctx, "Unterminated comment");
264 if(cur_char(ctx) == '*' && next_char(ctx, 1) == '/')
265 {
266 advance(ctx, 2);
267 break;
268 }
269 advance(ctx, 1);
270 }
271 continue;
272 }
273 break;
274 }
275 if(eof(ctx)) ret_simple(LEX_EOF, 0);
276 char c = cur_char(ctx);
277 if(c == '(') ret_simple(LEX_LPAREN, 1);
278 if(c == ')') ret_simple(LEX_RPAREN, 1);
279 if(c == '{') ret_simple(LEX_LBRACE, 1);
280 if(c == '}') ret_simple(LEX_RBRACE, 1);
281 if(c == '>') ret_simple(LEX_RANGLE, 1);
282 if(c == '=') ret_simple(LEX_EQUAL, 1);
283 if(c == ';') ret_simple(LEX_SEMICOLON, 1);
284 if(c == '"') return parse_string(ctx, lexem);
285 if(c == '\'') return parse_ascii_number(ctx, lexem);
286 if(isdigit(c)) return parse_number(ctx, lexem);
287 if(isalpha(c) || c == '_') return parse_identifier(ctx, lexem);
288 parse_error(ctx, "Unexpected character '%c'\n", c);
289 #undef ret_simple
290}
291
292#if 0
293static void log_lexem(struct lexem_t *lexem)
294{
295 switch(lexem->type)
296 {
297 case LEX_EOF: printf("<eof>"); break;
298 case LEX_EQUAL: printf("="); break;
299 case LEX_IDENTIFIER: printf("id(%s)", lexem->str); break;
300 case LEX_LPAREN: printf("("); break;
301 case LEX_RPAREN: printf(")"); break;
302 case LEX_LBRACE: printf("{"); break;
303 case LEX_RBRACE: printf("}"); break;
304 case LEX_SEMICOLON: printf(";"); break;
305 case LEX_NUMBER: printf("num(%d)", lexem->num); break;
306 case LEX_STRING: printf("str(%s)", lexem->str); break;
307 default: printf("<unk>");
308 }
309}
310#endif
311
312struct cmd_source_t *db_find_source_by_id(struct cmd_file_t *cmd_file, const char *id)
313{
314 struct cmd_source_t *src = cmd_file->source_list;
315 while(src)
316 {
317 if(strcmp(src->identifier, id) == 0)
318 return src;
319 src = src->next;
320 }
321 return NULL;
322}
323
324static void generate_default_version(struct sb_version_t *ver)
325{
326 ver->major = 0x999;
327 ver->minor = 0x999;
328 ver->revision = 0x999;
329}
330
331#define INVALID_SB_SUBVERSION 0xffff
332
333static uint16_t parse_sb_subversion(char *str)
334{
335 int len = strlen(str);
336 uint16_t n = 0;
337 if(len == 0 || len > 4)
338 return INVALID_SB_SUBVERSION;
339 for(int i = 0; i < len; i++)
340 {
341 if(!isdigit(str[i]))
342 return INVALID_SB_SUBVERSION;
343 n = n << 4 | (str[i] - '0');
344 }
345 return n;
346}
347
348bool db_parse_sb_version(struct sb_version_t *ver, char *str)
349{
350 int len = strlen(str);
351 int cnt = 0;
352 int pos[2];
353
354 for(int i = 0; i < len; i++)
355 {
356 if(str[i] != '.')
357 continue;
358 if(cnt == 2)
359 return false;
360 pos[cnt++] = i + 1;
361 str[i] = 0;
362 }
363 if(cnt != 2)
364 return false;
365 ver->major = parse_sb_subversion(str);
366 ver->minor = parse_sb_subversion(str + pos[0]);
367 ver->revision = parse_sb_subversion(str + pos[1]);
368 return ver->major != INVALID_SB_SUBVERSION &&
369 ver->minor != INVALID_SB_SUBVERSION &&
370 ver->revision != INVALID_SB_SUBVERSION;
371}
372
373#undef parse_error
374#define parse_error(lexem, ...) \
375 do { fprintf(stderr, "%s:%d: ", lexem.file, lexem.line); \
376 fprintf(stderr, __VA_ARGS__); exit(2); } while(0)
377
378struct cmd_file_t *db_parse_file(const char *file)
379{
380 size_t size;
381 FILE *f = fopen(file, "r");
382 if(f == NULL)
383 bugp("Cannot open file '%s'", file);
384 fseek(f, 0, SEEK_END);
385 size = ftell(f);
386 fseek(f, 0, SEEK_SET);
387 char *buf = xmalloc(size);
388 if(fread(buf, size, 1, f) != 1)
389 bugp("Cannot read file '%s'", file);
390 fclose(f);
391
392 if(g_debug)
393 printf("Parsing db file '%s'\n", file);
394 struct cmd_file_t *cmd_file = xmalloc(sizeof(struct cmd_file_t));
395 memset(cmd_file, 0, sizeof(struct cmd_file_t));
396
397 generate_default_version(&cmd_file->product_ver);
398 generate_default_version(&cmd_file->component_ver);
399
400 struct lexem_t lexem;
401 struct context_t ctx;
402 ctx.file = file;
403 ctx.line = 1;
404 ctx.begin = buf;
405 ctx.ptr = buf;
406 ctx.end = buf + size;
407 #define next() next_lexem(&ctx, &lexem)
408 /* init lexer */
409 next();
410 /* options ? */
411 if(lexem.type == LEX_IDENTIFIER && !strcmp(lexem.str, "options"))
412 {
413 next();
414 if(lexem.type != LEX_LBRACE)
415 parse_error(lexem, "'{' expected after 'options'\n");
416
417 while(true)
418 {
419 next();
420 if(lexem.type == LEX_RBRACE)
421 break;
422 if(lexem.type != LEX_IDENTIFIER)
423 parse_error(lexem, "Identifier expected in options\n");
424 char *opt = lexem.str;
425 next();
426 if(lexem.type != LEX_EQUAL)
427 parse_error(lexem, "'=' expected after identifier\n");
428 next();
429 if(!strcmp(opt, "productVersion") || !strcmp(opt, "componentVersion"))
430 {
431 if(lexem.type != LEX_STRING)
432 parse_error(lexem, "String expected after '='\n");
433 bool ret;
434 if(!strcmp(opt, "productVersion"))
435 ret = db_parse_sb_version(&cmd_file->product_ver, lexem.str);
436 else
437 ret = db_parse_sb_version(&cmd_file->component_ver, lexem.str);
438 if(!ret)
439 parse_error(lexem, "Invalid product/component version");
440 }
441 else
442 parse_error(lexem, "Unknown option '%s'\n", opt);
443 next();
444 if(lexem.type != LEX_SEMICOLON)
445 parse_error(lexem, "';' expected after string\n");
446 }
447 next();
448 }
449 /* sources */
450 if(lexem.type != LEX_IDENTIFIER || strcmp(lexem.str, "sources"))
451 parse_error(lexem, "'sources' expected\n");
452 next();
453 if(lexem.type != LEX_LBRACE)
454 parse_error(lexem, "'{' expected after 'sources'\n");
455
456 while(true)
457 {
458 next();
459 if(lexem.type == LEX_RBRACE)
460 break;
461 struct cmd_source_t *src = xmalloc(sizeof(struct cmd_source_t));
462 memset(src, 0, sizeof(struct cmd_source_t));
463 src->next = cmd_file->source_list;
464 if(lexem.type != LEX_IDENTIFIER)
465 parse_error(lexem, "identifier expected in sources\n");
466 src->identifier = lexem.str;
467 next();
468 if(lexem.type != LEX_EQUAL)
469 parse_error(lexem, "'=' expected after identifier\n");
470 next();
471 if(lexem.type != LEX_STRING)
472 parse_error(lexem, "String expected after '='\n");
473 src->filename = lexem.str;
474 next();
475 if(lexem.type != LEX_SEMICOLON)
476 parse_error(lexem, "';' expected after string\n");
477 if(db_find_source_by_id(cmd_file, src->identifier) != NULL)
478 parse_error(lexem, "Duplicate source identifier\n");
479 /* type filled later */
480 src->type = CMD_SRC_UNK;
481 cmd_file->source_list = src;
482 }
483
484 /* sections */
485 struct cmd_section_t *end_sec = NULL;
486 while(true)
487 {
488 struct cmd_section_t *sec = xmalloc(sizeof(struct cmd_section_t));
489 struct cmd_inst_t *end_list = NULL;
490 memset(sec, 0, sizeof(struct cmd_section_t));
491 next();
492 if(lexem.type == LEX_EOF)
493 break;
494 if(lexem.type != LEX_IDENTIFIER || strcmp(lexem.str, "section") != 0)
495 parse_error(lexem, "'section' expected\n");
496 next();
497 if(lexem.type != LEX_LPAREN)
498 parse_error(lexem, "'(' expected after 'section'\n");
499 next();
500 /* can be a number or a 4 character long string */
501 if(lexem.type == LEX_NUMBER)
502 {
503 sec->identifier = lexem.num;
504 }
505 else
506 parse_error(lexem, "Number expected as section identifier\n");
507
508 next();
509 if(lexem.type != LEX_RPAREN)
510 parse_error(lexem, "')' expected after section identifier\n");
511 next();
512 if(lexem.type != LEX_LBRACE)
513 parse_error(lexem, "'{' expected after section directive\n");
514 /* commands */
515 while(true)
516 {
517 struct cmd_inst_t *inst = xmalloc(sizeof(struct cmd_inst_t));
518 memset(inst, 0, sizeof(struct cmd_inst_t));
519 next();
520 if(lexem.type == LEX_RBRACE)
521 break;
522 if(lexem.type != LEX_IDENTIFIER)
523 parse_error(lexem, "Instruction expected in section\n");
524 if(strcmp(lexem.str, "load") == 0)
525 inst->type = CMD_LOAD;
526 else if(strcmp(lexem.str, "call") == 0)
527 inst->type = CMD_CALL;
528 else if(strcmp(lexem.str, "jump") == 0)
529 inst->type = CMD_JUMP;
530 else if(strcmp(lexem.str, "mode") == 0)
531 inst->type = CMD_MODE;
532 else
533 parse_error(lexem, "Instruction expected in section\n");
534 next();
535
536 if(inst->type == CMD_LOAD)
537 {
538 if(lexem.type != LEX_IDENTIFIER)
539 parse_error(lexem, "Identifier expected after instruction\n");
540 inst->identifier = lexem.str;
541 if(db_find_source_by_id(cmd_file, inst->identifier) == NULL)
542 parse_error(lexem, "Undefined reference to source '%s'\n", inst->identifier);
543 next();
544 if(lexem.type == LEX_RANGLE)
545 {
546 // load at
547 inst->type = CMD_LOAD_AT;
548 next();
549 if(lexem.type != LEX_NUMBER)
550 parse_error(lexem, "Number expected for loading address\n");
551 inst->addr = lexem.num;
552 next();
553 }
554 if(lexem.type != LEX_SEMICOLON)
555 parse_error(lexem, "';' expected after command\n");
556 }
557 else if(inst->type == CMD_CALL || inst->type == CMD_JUMP)
558 {
559 if(lexem.type == LEX_IDENTIFIER)
560 {
561 inst->identifier = lexem.str;
562 if(db_find_source_by_id(cmd_file, inst->identifier) == NULL)
563 parse_error(lexem, "Undefined reference to source '%s'\n", inst->identifier);
564 next();
565 }
566 else if(lexem.type == LEX_NUMBER)
567 {
568 inst->type = (inst->type == CMD_CALL) ? CMD_CALL_AT : CMD_JUMP_AT;
569 inst->addr = lexem.num;
570 next();
571 }
572 else
573 parse_error(lexem, "Identifier or number expected after jump/load\n");
574
575 if(lexem.type == LEX_LPAREN)
576 {
577 next();
578 if(lexem.type != LEX_NUMBER)
579 parse_error(lexem, "Expected numeral expression after (\n");
580 inst->argument = lexem.num;
581 next();
582 if(lexem.type != LEX_RPAREN)
583 parse_error(lexem, "Expected closing brace\n");
584 next();
585 }
586 if(lexem.type != LEX_SEMICOLON)
587 parse_error(lexem, "Expected ';' after command\n");
588 }
589 else if(inst->type == CMD_MODE)
590 {
591 if(lexem.type != LEX_NUMBER)
592 parse_error(lexem, "Number expected after 'mode'\n");
593 inst->argument = lexem.num;
594 next();
595 if(lexem.type != LEX_SEMICOLON)
596 parse_error(lexem, "Expected ';' after command\n");
597 }
598 else
599 parse_error(lexem, "Internal error");
600 if(end_list == NULL)
601 {
602 sec->inst_list = inst;
603 end_list = inst;
604 }
605 else
606 {
607 end_list->next = inst;
608 end_list = inst;
609 }
610 }
611
612 if(end_sec == NULL)
613 {
614 cmd_file->section_list = sec;
615 end_sec = sec;
616 }
617 else
618 {
619 end_sec->next = sec;
620 end_sec = sec;
621 }
622 }
623 #undef next
624
625 return cmd_file;
626}