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.c411
1 files changed, 289 insertions, 122 deletions
diff --git a/utils/sbtools/dbparser.c b/utils/sbtools/dbparser.c
index 20f2d66c0e..2dcc21eeba 100644
--- a/utils/sbtools/dbparser.c
+++ b/utils/sbtools/dbparser.c
@@ -22,6 +22,7 @@
22#include <stdio.h> 22#include <stdio.h>
23#include <ctype.h> 23#include <ctype.h>
24#include <stdint.h> 24#include <stdint.h>
25#include <string.h>
25#include "dbparser.h" 26#include "dbparser.h"
26 27
27typedef uint8_t byte; 28typedef uint8_t byte;
@@ -45,6 +46,10 @@ enum lexem_type_t
45 LEX_LBRACE, 46 LEX_LBRACE,
46 LEX_RBRACE, 47 LEX_RBRACE,
47 LEX_RANGLE, 48 LEX_RANGLE,
49 LEX_OR,
50 LEX_LSHIFT,
51 LEX_COLON,
52 LEX_LE,
48 LEX_EOF 53 LEX_EOF
49}; 54};
50 55
@@ -274,6 +279,8 @@ static void next_lexem(struct context_t *ctx, struct lexem_t *lexem)
274 } 279 }
275 if(eof(ctx)) ret_simple(LEX_EOF, 0); 280 if(eof(ctx)) ret_simple(LEX_EOF, 0);
276 char c = cur_char(ctx); 281 char c = cur_char(ctx);
282 bool nv = next_valid(ctx, 1);
283 char nc = nv ? next_char(ctx, 1) : 0;
277 if(c == '(') ret_simple(LEX_LPAREN, 1); 284 if(c == '(') ret_simple(LEX_LPAREN, 1);
278 if(c == ')') ret_simple(LEX_RPAREN, 1); 285 if(c == ')') ret_simple(LEX_RPAREN, 1);
279 if(c == '{') ret_simple(LEX_LBRACE, 1); 286 if(c == '{') ret_simple(LEX_LBRACE, 1);
@@ -281,6 +288,10 @@ static void next_lexem(struct context_t *ctx, struct lexem_t *lexem)
281 if(c == '>') ret_simple(LEX_RANGLE, 1); 288 if(c == '>') ret_simple(LEX_RANGLE, 1);
282 if(c == '=') ret_simple(LEX_EQUAL, 1); 289 if(c == '=') ret_simple(LEX_EQUAL, 1);
283 if(c == ';') ret_simple(LEX_SEMICOLON, 1); 290 if(c == ';') ret_simple(LEX_SEMICOLON, 1);
291 if(c == ',') ret_simple(LEX_COLON, 1);
292 if(c == '|') ret_simple(LEX_OR, 1);
293 if(c == '<' && nv && nc == '<') ret_simple(LEX_LSHIFT, 2);
294 if(c == '<' && nv && nc == '=') ret_simple(LEX_LE, 2);
284 if(c == '"') return parse_string(ctx, lexem); 295 if(c == '"') return parse_string(ctx, lexem);
285 if(c == '\'') return parse_ascii_number(ctx, lexem); 296 if(c == '\'') return parse_ascii_number(ctx, lexem);
286 if(isdigit(c)) return parse_number(ctx, lexem); 297 if(isdigit(c)) return parse_number(ctx, lexem);
@@ -304,6 +315,8 @@ static void log_lexem(struct lexem_t *lexem)
304 case LEX_SEMICOLON: printf(";"); break; 315 case LEX_SEMICOLON: printf(";"); break;
305 case LEX_NUMBER: printf("num(%d)", lexem->num); break; 316 case LEX_NUMBER: printf("num(%d)", lexem->num); break;
306 case LEX_STRING: printf("str(%s)", lexem->str); break; 317 case LEX_STRING: printf("str(%s)", lexem->str); break;
318 case LEX_OR: printf("|"); break;
319 case LEX_LSHIFT: printf("<<"); break;
307 default: printf("<unk>"); 320 default: printf("<unk>");
308 } 321 }
309} 322}
@@ -321,11 +334,15 @@ struct cmd_source_t *db_find_source_by_id(struct cmd_file_t *cmd_file, const cha
321 return NULL; 334 return NULL;
322} 335}
323 336
324static void generate_default_version(struct sb_version_t *ver) 337struct cmd_option_t *db_find_option_by_id(struct cmd_option_t *opt, const char *name)
325{ 338{
326 ver->major = 0x999; 339 while(opt)
327 ver->minor = 0x999; 340 {
328 ver->revision = 0x999; 341 if(strcmp(opt->name, name) == 0)
342 return opt;
343 opt = opt->next;
344 }
345 return NULL;
329} 346}
330 347
331#define INVALID_SB_SUBVERSION 0xffff 348#define INVALID_SB_SUBVERSION 0xffff
@@ -375,6 +392,68 @@ bool db_parse_sb_version(struct sb_version_t *ver, char *str)
375 do { fprintf(stderr, "%s:%d: ", lexem.file, lexem.line); \ 392 do { fprintf(stderr, "%s:%d: ", lexem.file, lexem.line); \
376 fprintf(stderr, __VA_ARGS__); exit(2); } while(0) 393 fprintf(stderr, __VA_ARGS__); exit(2); } while(0)
377 394
395struct lex_ctx_t
396{
397 struct context_t ctx;
398 struct lexem_t lexem;
399};
400
401static inline void next(struct lex_ctx_t *ctx)
402{
403 next_lexem(&ctx->ctx, &ctx->lexem);
404}
405
406static uint32_t parse_term_expr(struct lex_ctx_t *ctx, struct cmd_option_t *const_list)
407{
408 uint32_t ret = 0;
409 if(ctx->lexem.type == LEX_NUMBER)
410 ret = ctx->lexem.num;
411 else if(ctx->lexem.type == LEX_IDENTIFIER)
412 {
413 struct cmd_option_t *c = db_find_option_by_id(const_list, ctx->lexem.str);
414 if(c == NULL)
415 parse_error(ctx->lexem, "Undefined reference to constant '%s'\n", ctx->lexem.str);
416 if(c->is_string)
417 parse_error(ctx->lexem, "Internal error: constant '%s' is not an integer\n", ctx->lexem.str);
418 ret = c->val;
419 }
420 else
421 parse_error(ctx->lexem, "Number or constant identifier expected\n");
422 next(ctx);
423 return ret;
424}
425
426static uint32_t parse_shift_expr(struct lex_ctx_t *ctx, struct cmd_option_t *const_list)
427{
428 uint32_t v = parse_term_expr(ctx, const_list);
429 while(ctx->lexem.type == LEX_LSHIFT)
430 {
431 next(ctx);
432 v <<= parse_term_expr(ctx, const_list);
433 }
434 return v;
435}
436
437static uint32_t parse_or_expr(struct lex_ctx_t *ctx, struct cmd_option_t *const_list)
438{
439 uint32_t v = parse_shift_expr(ctx, const_list);
440 while(ctx->lexem.type == LEX_OR)
441 {
442 next(ctx);
443 v |= parse_shift_expr(ctx, const_list);
444 }
445 return v;
446}
447
448static uint32_t parse_intexpr(struct lex_ctx_t *ctx, struct cmd_option_t *const_list)
449{
450 return parse_or_expr(ctx, const_list);
451}
452
453#define NR_INITIAL_CONSTANTS 4
454static char *init_const_name[NR_INITIAL_CONSTANTS] = {"true", "false", "yes", "no"};
455static uint32_t init_const_value[NR_INITIAL_CONSTANTS] = {1, 0, 1, 0};
456
378struct cmd_file_t *db_parse_file(const char *file) 457struct cmd_file_t *db_parse_file(const char *file)
379{ 458{
380 size_t size; 459 size_t size;
@@ -394,19 +473,58 @@ struct cmd_file_t *db_parse_file(const char *file)
394 struct cmd_file_t *cmd_file = xmalloc(sizeof(struct cmd_file_t)); 473 struct cmd_file_t *cmd_file = xmalloc(sizeof(struct cmd_file_t));
395 memset(cmd_file, 0, sizeof(struct cmd_file_t)); 474 memset(cmd_file, 0, sizeof(struct cmd_file_t));
396 475
397 generate_default_version(&cmd_file->product_ver); 476 /* add initial constants */
398 generate_default_version(&cmd_file->component_ver); 477 for(int i = 0; i < NR_INITIAL_CONSTANTS; i++)
478 {
479 struct cmd_option_t *opt = xmalloc(sizeof(struct cmd_option_t));
480 memset(opt, 0, sizeof(struct cmd_option_t));
481 opt->name = init_const_name[i];
482 opt->is_string = false;
483 opt->val = init_const_value[i];
484 opt->next = cmd_file->constant_list;
485 cmd_file->constant_list = opt;
486 }
399 487
400 struct lexem_t lexem; 488 struct lex_ctx_t lctx;
401 struct context_t ctx; 489 lctx.ctx.file = file;
402 ctx.file = file; 490 lctx.ctx.line = 1;
403 ctx.line = 1; 491 lctx.ctx.begin = buf;
404 ctx.begin = buf; 492 lctx.ctx.ptr = buf;
405 ctx.ptr = buf; 493 lctx.ctx.end = buf + size;
406 ctx.end = buf + size; 494 #define next() next(&lctx)
407 #define next() next_lexem(&ctx, &lexem) 495 #define lexem lctx.lexem
408 /* init lexer */ 496 /* init lexer */
409 next(); 497 next();
498 /* constants ? */
499 if(lexem.type == LEX_IDENTIFIER && !strcmp(lexem.str, "constants"))
500 {
501 next();
502 if(lexem.type != LEX_LBRACE)
503 parse_error(lexem, "'{' expected after 'constants'\n");
504
505 while(true)
506 {
507 struct cmd_option_t *opt = xmalloc(sizeof(struct cmd_option_t));
508 memset(opt, 0, sizeof(struct cmd_option_t));
509 next();
510 if(lexem.type == LEX_RBRACE)
511 break;
512 if(lexem.type != LEX_IDENTIFIER)
513 parse_error(lexem, "Identifier expected in constants\n");
514 opt->name = lexem.str;
515 next();
516 if(lexem.type != LEX_EQUAL)
517 parse_error(lexem, "'=' expected after identifier\n");
518 next();
519 opt->is_string = false;
520 opt->val = parse_intexpr(&lctx, cmd_file->constant_list);
521 opt->next = cmd_file->constant_list;
522 cmd_file->constant_list = opt;
523 if(lexem.type != LEX_SEMICOLON)
524 parse_error(lexem, "';' expected after string\n");
525 }
526 next();
527 }
410 /* options ? */ 528 /* options ? */
411 if(lexem.type == LEX_IDENTIFIER && !strcmp(lexem.str, "options")) 529 if(lexem.type == LEX_IDENTIFIER && !strcmp(lexem.str, "options"))
412 { 530 {
@@ -416,31 +534,31 @@ struct cmd_file_t *db_parse_file(const char *file)
416 534
417 while(true) 535 while(true)
418 { 536 {
537 struct cmd_option_t *opt = xmalloc(sizeof(struct cmd_option_t));
538 memset(opt, 0, sizeof(struct cmd_option_t));
419 next(); 539 next();
420 if(lexem.type == LEX_RBRACE) 540 if(lexem.type == LEX_RBRACE)
421 break; 541 break;
422 if(lexem.type != LEX_IDENTIFIER) 542 if(lexem.type != LEX_IDENTIFIER)
423 parse_error(lexem, "Identifier expected in options\n"); 543 parse_error(lexem, "Identifier expected in options\n");
424 char *opt = lexem.str; 544 opt->name = lexem.str;
425 next(); 545 next();
426 if(lexem.type != LEX_EQUAL) 546 if(lexem.type != LEX_EQUAL)
427 parse_error(lexem, "'=' expected after identifier\n"); 547 parse_error(lexem, "'=' expected after identifier\n");
428 next(); 548 next();
429 if(!strcmp(opt, "productVersion") || !strcmp(opt, "componentVersion")) 549 if(lexem.type == LEX_STRING)
430 { 550 {
431 if(lexem.type != LEX_STRING) 551 opt->is_string = true;
432 parse_error(lexem, "String expected after '='\n"); 552 opt->str = lexem.str;
433 bool ret; 553 next();
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 } 554 }
441 else 555 else
442 parse_error(lexem, "Unknown option '%s'\n", opt); 556 {
443 next(); 557 opt->is_string = false;
558 opt->val = parse_intexpr(&lctx, cmd_file->constant_list);
559 }
560 opt->next = cmd_file->opt_list;
561 cmd_file->opt_list = opt;
444 if(lexem.type != LEX_SEMICOLON) 562 if(lexem.type != LEX_SEMICOLON)
445 parse_error(lexem, "';' expected after string\n"); 563 parse_error(lexem, "';' expected after string\n");
446 } 564 }
@@ -460,7 +578,6 @@ struct cmd_file_t *db_parse_file(const char *file)
460 break; 578 break;
461 struct cmd_source_t *src = xmalloc(sizeof(struct cmd_source_t)); 579 struct cmd_source_t *src = xmalloc(sizeof(struct cmd_source_t));
462 memset(src, 0, sizeof(struct cmd_source_t)); 580 memset(src, 0, sizeof(struct cmd_source_t));
463 src->next = cmd_file->source_list;
464 if(lexem.type != LEX_IDENTIFIER) 581 if(lexem.type != LEX_IDENTIFIER)
465 parse_error(lexem, "identifier expected in sources\n"); 582 parse_error(lexem, "identifier expected in sources\n");
466 src->identifier = lexem.str; 583 src->identifier = lexem.str;
@@ -468,16 +585,34 @@ struct cmd_file_t *db_parse_file(const char *file)
468 if(lexem.type != LEX_EQUAL) 585 if(lexem.type != LEX_EQUAL)
469 parse_error(lexem, "'=' expected after identifier\n"); 586 parse_error(lexem, "'=' expected after identifier\n");
470 next(); 587 next();
471 if(lexem.type != LEX_STRING) 588 if(lexem.type == LEX_STRING)
472 parse_error(lexem, "String expected after '='\n"); 589 {
473 src->filename = lexem.str; 590 src->is_extern = false;
474 next(); 591 src->filename = lexem.str;
592 next();
593 }
594 else if(lexem.type == LEX_IDENTIFIER && !strcmp(lexem.str, "extern"))
595 {
596 src->is_extern = true;
597 src->filename = "<extern>";
598 next();
599 if(lexem.type != LEX_LPAREN)
600 parse_error(lexem, "'(' expected after 'extern'\n");
601 next();
602 src->extern_nr = parse_intexpr(&lctx, cmd_file->constant_list);
603 if(lexem.type != LEX_RPAREN)
604 parse_error(lexem, "')' expected\n");
605 next();
606 }
607 else
608 parse_error(lexem, "String or 'extern' expected after '='\n");
475 if(lexem.type != LEX_SEMICOLON) 609 if(lexem.type != LEX_SEMICOLON)
476 parse_error(lexem, "';' expected after string\n"); 610 parse_error(lexem, "';' expected\n");
477 if(db_find_source_by_id(cmd_file, src->identifier) != NULL) 611 if(db_find_source_by_id(cmd_file, src->identifier) != NULL)
478 parse_error(lexem, "Duplicate source identifier\n"); 612 parse_error(lexem, "Duplicate source identifier\n");
479 /* type filled later */ 613 /* type filled later */
480 src->type = CMD_SRC_UNK; 614 src->type = CMD_SRC_UNK;
615 src->next = cmd_file->source_list;
481 cmd_file->source_list = src; 616 cmd_file->source_list = src;
482 } 617 }
483 618
@@ -497,117 +632,143 @@ struct cmd_file_t *db_parse_file(const char *file)
497 if(lexem.type != LEX_LPAREN) 632 if(lexem.type != LEX_LPAREN)
498 parse_error(lexem, "'(' expected after 'section'\n"); 633 parse_error(lexem, "'(' expected after 'section'\n");
499 next(); 634 next();
500 /* can be a number or a 4 character long string */ 635 /* can be any number */
501 if(lexem.type == LEX_NUMBER) 636 sec->identifier = parse_intexpr(&lctx, cmd_file->constant_list);
637 /* options ? */
638 if(lexem.type == LEX_SEMICOLON)
502 { 639 {
503 sec->identifier = lexem.num; 640 do
641 {
642 next();
643 struct cmd_option_t *opt = xmalloc(sizeof(struct cmd_option_t));
644 memset(opt, 0, sizeof(struct cmd_option_t));
645 if(lexem.type != LEX_IDENTIFIER)
646 parse_error(lexem, "Identifier expected for section option\n");
647 opt->name = lexem.str;
648 next();
649 if(lexem.type != LEX_EQUAL)
650 parse_error(lexem, "'=' expected after option identifier\n");
651 next();
652 if(lexem.type == LEX_STRING)
653 {
654 opt->is_string = true;
655 opt->str = lexem.str;
656 next();
657 }
658 else
659 {
660 opt->is_string = false;
661 opt->val = parse_intexpr(&lctx, cmd_file->constant_list);
662 }
663 opt->next = sec->opt_list;
664 sec->opt_list = opt;
665 }while(lexem.type == LEX_COLON);
504 } 666 }
505 else
506 parse_error(lexem, "Number expected as section identifier\n");
507
508 next();
509 if(lexem.type != LEX_RPAREN) 667 if(lexem.type != LEX_RPAREN)
510 parse_error(lexem, "')' expected after section identifier\n"); 668 parse_error(lexem, "')' expected after section identifier\n");
511 next(); 669 next();
512 if(lexem.type != LEX_LBRACE) 670 if(lexem.type == LEX_LBRACE)
513 parse_error(lexem, "'{' expected after section directive\n");
514 /* commands */
515 while(true)
516 { 671 {
517 struct cmd_inst_t *inst = xmalloc(sizeof(struct cmd_inst_t)); 672 sec->is_data = false;
518 memset(inst, 0, sizeof(struct cmd_inst_t)); 673 /* commands */
519 next(); 674 while(true)
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 { 675 {
676 struct cmd_inst_t *inst = xmalloc(sizeof(struct cmd_inst_t));
677 memset(inst, 0, sizeof(struct cmd_inst_t));
678 next();
679 if(lexem.type == LEX_RBRACE)
680 break;
538 if(lexem.type != LEX_IDENTIFIER) 681 if(lexem.type != LEX_IDENTIFIER)
539 parse_error(lexem, "Identifier expected after instruction\n"); 682 parse_error(lexem, "Instruction expected in section\n");
540 inst->identifier = lexem.str; 683 if(strcmp(lexem.str, "load") == 0)
541 if(db_find_source_by_id(cmd_file, inst->identifier) == NULL) 684 inst->type = CMD_LOAD;
542 parse_error(lexem, "Undefined reference to source '%s'\n", inst->identifier); 685 else if(strcmp(lexem.str, "call") == 0)
686 inst->type = CMD_CALL;
687 else if(strcmp(lexem.str, "jump") == 0)
688 inst->type = CMD_JUMP;
689 else if(strcmp(lexem.str, "mode") == 0)
690 inst->type = CMD_MODE;
691 else
692 parse_error(lexem, "Instruction expected in section\n");
543 next(); 693 next();
544 if(lexem.type == LEX_RANGLE) 694
545 { 695 if(inst->type == CMD_LOAD)
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 { 696 {
697 if(lexem.type != LEX_IDENTIFIER)
698 parse_error(lexem, "Identifier expected after instruction\n");
561 inst->identifier = lexem.str; 699 inst->identifier = lexem.str;
562 if(db_find_source_by_id(cmd_file, inst->identifier) == NULL) 700 if(db_find_source_by_id(cmd_file, inst->identifier) == NULL)
563 parse_error(lexem, "Undefined reference to source '%s'\n", inst->identifier); 701 parse_error(lexem, "Undefined reference to source '%s'\n", inst->identifier);
564 next(); 702 next();
703 if(lexem.type == LEX_RANGLE)
704 {
705 // load at
706 inst->type = CMD_LOAD_AT;
707 next();
708 inst->addr = parse_intexpr(&lctx, cmd_file->constant_list);
709 }
710 if(lexem.type != LEX_SEMICOLON)
711 parse_error(lexem, "';' expected after command\n");
565 } 712 }
566 else if(lexem.type == LEX_NUMBER) 713 else if(inst->type == CMD_CALL || inst->type == CMD_JUMP)
567 { 714 {
568 inst->type = (inst->type == CMD_CALL) ? CMD_CALL_AT : CMD_JUMP_AT; 715 if(lexem.type == LEX_IDENTIFIER)
569 inst->addr = lexem.num; 716 {
570 next(); 717 inst->identifier = lexem.str;
718 if(db_find_source_by_id(cmd_file, inst->identifier) == NULL)
719 parse_error(lexem, "Undefined reference to source '%s'\n", inst->identifier);
720 next();
721 }
722 else
723 {
724 inst->type = (inst->type == CMD_CALL) ? CMD_CALL_AT : CMD_JUMP_AT;
725 inst->addr = parse_intexpr(&lctx, cmd_file->constant_list);
726 }
727
728 if(lexem.type == LEX_LPAREN)
729 {
730 next();
731 inst->argument = parse_intexpr(&lctx, cmd_file->constant_list);
732 if(lexem.type != LEX_RPAREN)
733 parse_error(lexem, "Expected closing brace\n");
734 next();
735 }
736 if(lexem.type != LEX_SEMICOLON)
737 parse_error(lexem, "';' expected after command\n");
738 }
739 else if(inst->type == CMD_MODE)
740 {
741 inst->argument = parse_intexpr(&lctx, cmd_file->constant_list);
742 if(lexem.type != LEX_SEMICOLON)
743 parse_error(lexem, "Expected ';' after command\n");
571 } 744 }
572 else 745 else
573 parse_error(lexem, "Identifier or number expected after jump/load\n"); 746 parse_error(lexem, "Internal error");
574 747 if(end_list == NULL)
575 if(lexem.type == LEX_LPAREN)
576 { 748 {
577 next(); 749 sec->inst_list = inst;
578 if(lexem.type != LEX_NUMBER) 750 end_list = inst;
579 parse_error(lexem, "Expected numeral expression after (\n"); 751 }
580 inst->argument = lexem.num; 752 else
581 next(); 753 {
582 if(lexem.type != LEX_RPAREN) 754 end_list->next = inst;
583 parse_error(lexem, "Expected closing brace\n"); 755 end_list = inst;
584 next();
585 } 756 }
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 } 757 }
610 } 758 }
759 else if(lexem.type == LEX_LE)
760 {
761 sec->is_data = true;
762 next();
763 if(lexem.type != LEX_IDENTIFIER)
764 parse_error(lexem, "Identifier expected after '<='\n");
765 sec->source_id = lexem.str;
766 next();
767 if(lexem.type != LEX_SEMICOLON)
768 parse_error(lexem, "';' expected after identifier\n");
769 }
770 else
771 parse_error(lexem, "'{' or '<=' expected after section directive\n");
611 772
612 if(end_sec == NULL) 773 if(end_sec == NULL)
613 { 774 {
@@ -620,7 +781,13 @@ struct cmd_file_t *db_parse_file(const char *file)
620 end_sec = sec; 781 end_sec = sec;
621 } 782 }
622 } 783 }
784 #undef lexem
623 #undef next 785 #undef next
624 786
625 return cmd_file; 787 return cmd_file;
626} 788}
789
790void db_generate_default_sb_version(struct sb_version_t *ver)
791{
792 ver->major = ver->minor = ver->revision = 0x999;
793}