#include <parser.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <stdarg.h>
#include <libtcod_int.h>
#define BIG_NAME_LEN 128
#define DEF_NAME(d) (((TCOD_struct_int_t *)d)->name)
#define DEF_FLAGS(d) (((TCOD_struct_int_t *)d)->flags)
#define DEF_PROPS(d) (((TCOD_struct_int_t *)d)->props)
#define DEF_LISTS(d) (((TCOD_struct_int_t *)d)->lists)
typedef struct {
char *name;
TCOD_value_type_t value;
bool mandat;
} TCOD_struct_prop_t;
static TCOD_lex_t *lex=NULL;
static const char *symbols[] = {
"{","}","=","/","+","-","[","]",",","#",NULL
};
static const char *keywords[] = {
"struct","bool","char","int","float","string","color","dice",NULL
};
static TCOD_parser_listener_t *listener=NULL;
static bool default_new_struct(TCOD_parser_struct_t str,const char *name);
static bool default_new_flag(const char *name);
static bool default_new_property(const char *propname, TCOD_value_type_t type, TCOD_value_t value);
static bool default_end_struct(TCOD_parser_struct_t str, const char *name);
static void default_error(const char *msg);
static TCOD_parser_listener_t default_listener = {
default_new_struct,
default_new_flag,
default_new_property,
default_end_struct,
default_error
};
static bool string_copy(char *dest, char *source, int len) {
if ( source == NULL ) return false;
strncpy(dest,source,len);
dest[len-1]='\0';
if ( strlen(source) >= (unsigned)len ) return false;
return true;
}
void TCOD_parser_error(const char *msg, ...) {
char buf[2048];
char buf2[2048];
va_list ap;
va_start(ap,msg);
vsprintf(buf,msg,ap);
va_end(ap);
sprintf(buf2,"error in %s line %d : %s",lex->filename,lex->file_line,buf);
listener->error(buf2);
lex->token_type = TCOD_LEX_ERROR;
}
const char *TCOD_struct_get_name(TCOD_parser_struct_t def) {
return ((TCOD_struct_int_t *)def)->name;
}
void TCOD_struct_add_property(TCOD_parser_struct_t def,const char *name,TCOD_value_type_t type, bool mandatory) {
TCOD_struct_prop_t *prop=(TCOD_struct_prop_t *)calloc(1,sizeof(TCOD_struct_prop_t));
prop->name=TCOD_strdup(name);
prop->value=type;
prop->mandat=mandatory;
TCOD_list_push(DEF_PROPS(def),(void *)prop);
}
void TCOD_struct_add_list_property(TCOD_parser_struct_t def,const char *name,TCOD_value_type_t type, bool mandatory) {
TCOD_struct_add_property(def,name,type|TCOD_TYPE_LIST,mandatory);
}
void TCOD_struct_add_value_list_sized(TCOD_parser_struct_t def,const char *name, const char **value_list, int size, bool mandatory) {
char ** newArray = NULL;
TCOD_value_type_t type = (TCOD_value_type_t)((int)TCOD_TYPE_VALUELIST00 + TCOD_list_size(DEF_LISTS(def)));
int i = 0;
if(size) newArray = calloc(size+1, sizeof(char*));
for(i = 0 ; i < size ; i++)
newArray[i] = TCOD_strdup(value_list[i]);
newArray[size] = NULL;
TCOD_struct_add_property(def,name,type,mandatory);
TCOD_list_push(DEF_LISTS(def),(void *)newArray);
}
void TCOD_struct_add_value_list(TCOD_parser_struct_t def,const char *name, const char **value_list, bool mandatory) {
int size = 0;
if(value_list)
{
while(value_list[size] != NULL)
{
size++;
};
}
TCOD_struct_add_value_list_sized(def, name, value_list, size, mandatory);
}
void TCOD_struct_add_flag(TCOD_parser_struct_t def,const char *propname) {
TCOD_list_push(DEF_FLAGS(def),(void *)TCOD_strdup(propname));
}
void TCOD_struct_add_structure(TCOD_parser_struct_t def, TCOD_parser_struct_t sub_definition) {
TCOD_list_push(((TCOD_struct_int_t *)def)->structs,(const void *)sub_definition);
}
bool TCOD_struct_is_mandatory(TCOD_parser_struct_t def, const char *propname) {
TCOD_struct_prop_t **iprop;
for (iprop=(TCOD_struct_prop_t **)TCOD_list_begin(DEF_PROPS(def)); iprop!=(TCOD_struct_prop_t **)TCOD_list_end(DEF_PROPS(def));iprop++) {
if ( strcmp((*iprop)->name,propname) == 0 ) return (*iprop)->mandat;
}
return false;
}
TCOD_value_type_t TCOD_struct_get_type(TCOD_parser_struct_t def, const char *propname) {
TCOD_struct_prop_t **iprop;
char **iflag;
for (iprop=(TCOD_struct_prop_t **)TCOD_list_begin(DEF_PROPS(def)); iprop!=(TCOD_struct_prop_t **)TCOD_list_end(DEF_PROPS(def));iprop++) {
if ( strcmp((*iprop)->name,propname) == 0 ) return (*iprop)->value;
}
for (iflag=(char **)TCOD_list_begin(DEF_FLAGS(def)); iflag!=(char **)TCOD_list_end(DEF_FLAGS(def));iflag++) {
if ( strcmp((*iflag),propname) == 0 ) return TCOD_TYPE_BOOL;
}
return TCOD_TYPE_NONE;
}
TCOD_value_t TCOD_parse_bool_value(void) {
TCOD_value_t ret;
if ( strcmp(lex->tok,"true") == 0 ) ret.b=true;
else if ( strcmp(lex->tok,"false") == 0 ) ret.b=false;
else TCOD_parser_error("parseBoolValue : unknown value %s for bool. 'true' or 'false' expected",lex->tok);
return ret;
}
TCOD_value_t TCOD_parse_char_value(void) {
TCOD_value_t ret;
if ( lex->token_type != TCOD_LEX_CHAR && lex->token_type != TCOD_LEX_INTEGER )
TCOD_parser_error("parseCharValue : char constant expected instead of '%s'",lex->tok);
ret.c=lex->token_int_val;
return ret;
}
TCOD_value_t TCOD_parse_integer_value(void) {
TCOD_value_t ret;
if ( lex->token_type != TCOD_LEX_INTEGER )
TCOD_parser_error("parseIntegerValue : integer constant expected instead of '%s'",lex->tok);
ret.i=lex->token_int_val;
return ret;
}
TCOD_value_t TCOD_parse_float_value(void) {
TCOD_value_t ret;
if ( lex->token_type != TCOD_LEX_FLOAT && lex->token_type != TCOD_LEX_INTEGER )
TCOD_parser_error("parseFloatValue : float constant expected instead of '%s'",lex->tok);
if ( lex->token_type == TCOD_LEX_FLOAT ) ret.f=lex->token_float_val;
else ret.f=(float)lex->token_int_val;
return ret;
}
TCOD_value_t TCOD_parse_string_value(void) {
TCOD_value_t ret;
TCOD_list_t l;
bool end=false;
char **s;
size_t slen=0;
l=TCOD_list_new();
if ( lex->token_type != TCOD_LEX_STRING ) TCOD_parser_error("parseStringValue : string constant expected instead of '%s'",lex->tok);
while ( !end ) {
TCOD_lex_t save;
TCOD_list_push(l,(void *)TCOD_strdup(lex->tok));
TCOD_lex_savepoint(lex,&save);
if (TCOD_lex_parse(lex) != TCOD_LEX_STRING) {
end=true;
TCOD_lex_restore(lex,&save);
}
}
for (s=(char **)TCOD_list_begin(l); s != (char **)TCOD_list_end(l); s++ ) {
slen += strlen(*s);
}
ret.s=(char *)calloc(sizeof(char),slen+1);
for (s=(char **)TCOD_list_begin(l); s != (char **)TCOD_list_end(l); s++ ) {
strcat(ret.s,*s);
free(*s);
}
TCOD_list_delete(l);
return ret;
}
TCOD_value_t TCOD_parse_color_value(void) {
TCOD_value_t ret;
if ( lex->token_type == TCOD_LEX_SYMBOL && lex->tok[0]=='#') {
char tmp[128]="";
int tok=TCOD_lex_parse(lex);
strcat(tmp,"#");
if ( tok == TCOD_LEX_IDEN || tok == TCOD_LEX_INTEGER ) {
strcat(tmp,lex->tok);
strcpy(lex->tok,tmp);
if ( strlen(lex->tok) < 7 && tok == TCOD_LEX_INTEGER ) {
tok=TCOD_lex_parse(lex);
if ( tok == TCOD_LEX_IDEN ) {
strcat(tmp,lex->tok);
strcpy(lex->tok,tmp);
}
}
lex->token_type = TCOD_LEX_STRING;
}
}
if ( lex->token_type != TCOD_LEX_STRING ) TCOD_parser_error("parseColorValue : string constant expected instead of '%s'",lex->tok);
if (lex->tok[0] == '#') {
int r,g,b;
if ( strlen(lex->tok) != 7 ) TCOD_parser_error("parseColorValue : bad color format. '#rrggbb' expected instead of '%s'",lex->tok);
r=(TCOD_lex_hextoint(lex->tok[1])<<4) + TCOD_lex_hextoint(lex->tok[2]);
g=(TCOD_lex_hextoint(lex->tok[3])<<4) + TCOD_lex_hextoint(lex->tok[4]);
b=(TCOD_lex_hextoint(lex->tok[5])<<4) + TCOD_lex_hextoint(lex->tok[6]);
ret.col.r=r;
ret.col.g=g;
ret.col.b=b;
} else {
char *begin=lex->tok;
char *end=strchr(begin,',');
bool ok=true;
if (! end) ok=false;
else {
*end=0;
ret.col.r=atoi(begin);
begin=end+1;
end=strchr(begin,',');
if ( !end ) ok=false;
else {
ret.col.g=atoi(begin);
begin=end+1;
ret.col.b=atoi(begin);
}
}
if (!ok) TCOD_parser_error("parseColorValue : bad color format 'rrr,ggg,bbb' expected instead of '%s'",lex->tok);
}
return ret;
}
TCOD_value_t TCOD_parse_dice_value(void) {
TCOD_value_t ret;
bool minus=false;
char *begin;
char *ptr;
ret.dice.multiplier=1.0f;
ret.dice.addsub=0.0f;
begin=lex->tok;
ptr=strchr(begin,'x');
if (! ptr ) ptr=strchr(begin,'*');
if ( ptr ) {
*ptr=0;
ret.dice.multiplier=(float)atof(lex->tok);
begin=ptr+1;
}
ptr=strchr(begin,'D');
if (!ptr ) ptr=strchr(begin,'d');
if (! ptr ) TCOD_parser_error("parseDiceValue : bad dice format. [<m>(x|*)]<n>(D|d)<f>[(+|-)<a>] expected instead of '%s'",lex->tok);
*ptr=0;
ret.dice.nb_rolls=atoi(begin);
begin=ptr+1;
ptr=strchr(begin,'+');
if (! ptr) {
ptr = strchr(begin,'-');
if ( ptr ) minus=true;
}
if (ptr) *ptr=0;
ret.dice.nb_faces=atoi(begin);
if ( ptr ) {
begin=ptr+1;
ret.dice.addsub=(float)atof(begin);
if ( minus ) ret.dice.addsub=-ret.dice.addsub;
}
return ret;
}
TCOD_value_t TCOD_parse_value_list_value(TCOD_struct_int_t *def,int listnum) {
TCOD_value_t ret;
int i=0;
char *value;
char **value_list=TCOD_list_get(def->lists,listnum);
if ( lex->token_type != TCOD_LEX_STRING) TCOD_parser_error("parseValueListValue : string constant expected instead of '%s'",lex->tok);
value= value_list[i];
while ( value ) {
if ( strcmp(lex->tok,value) == 0 ) break;
i++;
value=value_list[i];
}
if (! value ) TCOD_parser_error("parseValueListValue : incorrect value '%s'",lex->tok);
ret.s = value;
return ret;
}
TCOD_value_t TCOD_parse_property_value(TCOD_parser_int_t *parser, TCOD_parser_struct_t def, char *propname, bool list) {
TCOD_value_type_t type=TCOD_struct_get_type(def,propname);
TCOD_value_t ret={0};
if (! list ) type &= ~ TCOD_TYPE_LIST;
if ( type & TCOD_TYPE_LIST ) {
type &= ~ TCOD_TYPE_LIST;
if ( strcmp(lex->tok,"[") != 0 ) {
TCOD_parser_error("'[' expected for list value instead of '%s'",lex->tok);
}
ret.list=TCOD_list_new();
do {
TCOD_value_t val;
int tok=TCOD_lex_parse(lex);
if ( tok == TCOD_LEX_EOF || tok == TCOD_LEX_ERROR ) {
TCOD_parser_error("Missing ']' in list value");
}
val=TCOD_parse_property_value(parser,def,propname,false);
if ( type == TCOD_TYPE_STRING || (type >= TCOD_TYPE_VALUELIST00 && type <= TCOD_TYPE_VALUELIST15 ) ) {
TCOD_list_push(ret.list,TCOD_strdup(val.s));
} else {
TCOD_list_push(ret.list,val.custom);
}
TCOD_lex_parse(lex);
if ( strcmp(lex->tok,",") != 0 && strcmp(lex->tok,"]") != 0 ) {
TCOD_parser_error("',' or ']' expected in list value instead of '%s'",lex->tok);
}
} while ( strcmp(lex->tok,"]") != 0 );
} else {
switch (type) {
case TCOD_TYPE_BOOL : return TCOD_parse_bool_value(); break;
case TCOD_TYPE_CHAR : return TCOD_parse_char_value(); break;
case TCOD_TYPE_INT : return TCOD_parse_integer_value(); break;
case TCOD_TYPE_FLOAT : return TCOD_parse_float_value(); break;
case TCOD_TYPE_STRING : return TCOD_parse_string_value(); break;
case TCOD_TYPE_COLOR : return TCOD_parse_color_value(); break;
case TCOD_TYPE_DICE : return TCOD_parse_dice_value(); break;
case TCOD_TYPE_VALUELIST00 :
case TCOD_TYPE_VALUELIST01 :
case TCOD_TYPE_VALUELIST02 :
case TCOD_TYPE_VALUELIST03 :
case TCOD_TYPE_VALUELIST04 :
case TCOD_TYPE_VALUELIST05 :
case TCOD_TYPE_VALUELIST06 :
case TCOD_TYPE_VALUELIST07 :
case TCOD_TYPE_VALUELIST08 :
case TCOD_TYPE_VALUELIST09 :
case TCOD_TYPE_VALUELIST10 :
case TCOD_TYPE_VALUELIST11 :
case TCOD_TYPE_VALUELIST12 :
case TCOD_TYPE_VALUELIST13 :
case TCOD_TYPE_VALUELIST14 :
case TCOD_TYPE_VALUELIST15 : {
int listnum = type - TCOD_TYPE_VALUELIST00;
return TCOD_parse_value_list_value((TCOD_struct_int_t *)def,listnum); break;
}
case TCOD_TYPE_CUSTOM00 :
case TCOD_TYPE_CUSTOM01 :
case TCOD_TYPE_CUSTOM02 :
case TCOD_TYPE_CUSTOM03 :
case TCOD_TYPE_CUSTOM04 :
case TCOD_TYPE_CUSTOM05 :
case TCOD_TYPE_CUSTOM06 :
case TCOD_TYPE_CUSTOM07 :
case TCOD_TYPE_CUSTOM08 :
case TCOD_TYPE_CUSTOM09 :
case TCOD_TYPE_CUSTOM10 :
case TCOD_TYPE_CUSTOM11 :
case TCOD_TYPE_CUSTOM12 :
case TCOD_TYPE_CUSTOM13 :
case TCOD_TYPE_CUSTOM14 :
case TCOD_TYPE_CUSTOM15 :
if ( parser->customs[type - TCOD_TYPE_CUSTOM00] ) {
return parser->customs[type-TCOD_TYPE_CUSTOM00](lex,listener,def,propname);
} else {
TCOD_parser_error("parse_property_value : no custom parser for property type %d for entity %s prop %s",
type,DEF_NAME(def),propname);
}
break;
default : TCOD_parser_error("parse_property_value : unknown property type %d for entity %s prop %s",
type,DEF_NAME(def),propname); break;
}
}
return ret;
}
static bool TCOD_parser_parse_entity(TCOD_parser_int_t *parser, TCOD_struct_int_t *def) {
char *name=NULL;
if ( TCOD_lex_parse(lex) == TCOD_LEX_STRING ) {
name=TCOD_strdup(lex->tok);
TCOD_lex_parse(lex);
}
if ( strcmp(lex->tok,"{") != 0 ) {
TCOD_parser_error("Parser::parseEntity : '{' expected");
return false;
}
TCOD_lex_parse(lex);
while ( strcmp(lex->tok,"}") != 0 ) {
bool found=false;
char **iflag;
bool dynStruct=false;
TCOD_value_type_t dynType = TCOD_TYPE_NONE;
if ( lex->token_type == TCOD_LEX_KEYWORD ) {
if ( strcmp(lex->tok,"bool") == 0 ) dynType=TCOD_TYPE_BOOL;
else if ( strcmp(lex->tok,"char") == 0 ) dynType=TCOD_TYPE_CHAR;
else if ( strcmp(lex->tok,"int") == 0 ) dynType=TCOD_TYPE_INT;
else if ( strcmp(lex->tok,"float") == 0 ) dynType=TCOD_TYPE_FLOAT;
else if ( strcmp(lex->tok,"string") == 0 ) dynType=TCOD_TYPE_STRING;
else if ( strcmp(lex->tok,"color") == 0 ) dynType=TCOD_TYPE_COLOR;
else if ( strcmp(lex->tok,"dice") == 0 ) dynType=TCOD_TYPE_DICE;
else if ( strcmp(lex->tok,"struct") == 0 ) dynStruct=true;
else {
TCOD_parser_error("Parser::parseEntity : dynamic declaration of '%s' not supported",lex->tok);
return false;
}
TCOD_lex_parse(lex);
if ( strcmp(lex->tok,"[") == 0 ) {
if ( dynType == TCOD_TYPE_NONE ) {
TCOD_parser_error("Parser::parseEntity : unexpected symbol '['");
return false;
}
TCOD_lex_parse(lex);
if ( strcmp(lex->tok,"]") != 0 ) {
TCOD_parser_error("Parser::parseEntity : syntax error. ']' expected instead of '%s'",lex->tok);
return false;
}
dynType |= TCOD_TYPE_LIST;
TCOD_lex_parse(lex);
}
}
if ( lex->token_type != TCOD_LEX_IDEN ) {
TCOD_parser_error("Parser::parseEntity : identifier expected");
return false;
}
if (! dynStruct && dynType == TCOD_TYPE_NONE) {
for (iflag=(char **)TCOD_list_begin(def->flags);iflag!=(char **)TCOD_list_end(def->flags); iflag++) {
if ( strcmp(*iflag,lex->tok) == 0 ) {
found=true;
if (!listener->new_flag(lex->tok)) return false;
break;
}
}
}
if (!found && ! dynStruct) {
do {
TCOD_struct_prop_t **iprop;
for (iprop=(TCOD_struct_prop_t **)TCOD_list_begin(def->props); iprop!=(TCOD_struct_prop_t **)TCOD_list_end(def->props);iprop++) {
if ( strcmp((*iprop)->name,lex->tok) == 0 ) {
char propname[BIG_NAME_LEN];
string_copy(propname,lex->tok,BIG_NAME_LEN);
TCOD_lex_parse(lex);
if ( strcmp(lex->tok,"=") != 0 ) {
TCOD_parser_error("Parser::parseEntity : '=' expected");
return false;
}
TCOD_lex_parse(lex);
if (!listener->new_property(propname,TCOD_struct_get_type(def,propname),
TCOD_parse_property_value(parser, (TCOD_parser_struct_t *)def,propname,true))) return false;
if ( lex->token_type == TCOD_LEX_ERROR ) return false;
found=true;
break;
}
}
if ( !found && dynType != TCOD_TYPE_NONE ) {
TCOD_struct_add_property(def,lex->tok,dynType,false);
}
} while ( ! found && dynType != TCOD_TYPE_NONE);
}
if (! found ) {
char id[BIG_NAME_LEN*2 + 2];
bool blockFound=false;
do {
TCOD_lex_t save;
char type[BIG_NAME_LEN];
char *subname=NULL;
bool named=false;
TCOD_lex_savepoint(lex,&save);
string_copy(type,lex->tok,BIG_NAME_LEN);
strcpy(id,type);
if ( TCOD_lex_parse(lex) == TCOD_LEX_STRING ) {
TCOD_struct_int_t **sub;
strcat(id,"#");
strcat(id,lex->tok);
named=true;
subname=TCOD_strdup(lex->tok);
TCOD_lex_restore(lex,&save);
for ( sub = (TCOD_struct_int_t **)TCOD_list_begin(def->structs);
sub != (TCOD_struct_int_t **)TCOD_list_end(def->structs); sub ++ ) {
if ( strcmp((*sub)->name,id) == 0 ) {
if (!listener->new_struct((TCOD_parser_struct_t *)(*sub),lex->tok)) return false;
if (!TCOD_parser_parse_entity(parser,*sub)) return false;
blockFound=true;
found=true;
break;
}
}
} else {
TCOD_lex_restore(lex,&save);
}
if (! blockFound ) {
TCOD_struct_int_t **sub;
for ( sub = (TCOD_struct_int_t **)TCOD_list_begin(def->structs);
sub != (TCOD_struct_int_t **)TCOD_list_end(def->structs); sub ++ ) {
if ( strcmp((*sub)->name,type) == 0 ) {
if (!listener->new_struct((TCOD_parser_struct_t *)(*sub),subname)) return false;
if (!TCOD_parser_parse_entity(parser,*sub)) return false;
blockFound=true;
found=true;
break;
}
}
}
if (! blockFound && dynStruct ) {
TCOD_struct_int_t **idef;
TCOD_struct_int_t *s=NULL;
for (idef=(TCOD_struct_int_t **)TCOD_list_begin(parser->structs); idef!=(TCOD_struct_int_t **)TCOD_list_end(parser->structs); idef ++) {
if ( strcmp((*idef)->name,id) == 0 ) {
s=*idef;
break;
}
}
if ( s == NULL && named ) {
for (idef=(TCOD_struct_int_t **)TCOD_list_begin(parser->structs); idef!=(TCOD_struct_int_t **)TCOD_list_end(parser->structs); idef ++) {
if ( strcmp((*idef)->name,type) == 0 ) {
s=*idef;
break;
}
}
}
if ( s == NULL ) {
s = TCOD_parser_new_struct(parser,type);
}
TCOD_struct_add_structure(def,s);
}
} while (!blockFound && dynStruct );
if (! blockFound ) {
TCOD_parser_error("Parser::parseEntity : entity type %s does not contain %s",
def->name,id);
return false;
}
}
TCOD_lex_parse(lex);
}
if (!listener->end_struct((TCOD_parser_struct_t *)def,name)) return false;
return true;
}
TCOD_parser_t TCOD_parser_new(void) {
TCOD_parser_int_t *ent = (TCOD_parser_int_t*)calloc(1,sizeof(TCOD_parser_int_t));
ent->structs=TCOD_list_new();
return (TCOD_parser_t) ent;
}
TCOD_value_type_t TCOD_parser_new_custom_type(TCOD_parser_t parser, TCOD_parser_custom_t custom_type_parser) {
TCOD_parser_int_t *p=(TCOD_parser_int_t *)parser;
TCOD_value_type_t type= TCOD_TYPE_CUSTOM00;
while ( p->customs[ type - TCOD_TYPE_CUSTOM00 ] && type < TCOD_TYPE_CUSTOM15 ) type=(TCOD_value_type_t)(type+1);
if ( p->customs[ type - TCOD_TYPE_CUSTOM00 ] ) {
return TCOD_TYPE_NONE;
}
p->customs[type - TCOD_TYPE_CUSTOM00] = custom_type_parser;
return type;
}
TCOD_parser_struct_t TCOD_parser_new_struct(TCOD_parser_t parser, char *name) {
TCOD_struct_int_t *ent = (TCOD_struct_int_t*)calloc(1,sizeof(TCOD_struct_int_t));
ent->name=TCOD_strdup(name);
ent->flags=TCOD_list_new();
ent->props=TCOD_list_new();
ent->lists=TCOD_list_new();
ent->structs=TCOD_list_new();
TCOD_list_push(((TCOD_parser_int_t *)parser)->structs,ent);
return (TCOD_parser_struct_t )ent;
}
void TCOD_parser_delete(TCOD_parser_t parser) {
TCOD_parser_int_t *p=(TCOD_parser_int_t *)parser;
TCOD_struct_int_t **idef;
TCOD_struct_prop_t **propCleanup;
char *** listCleanup;
int listSize = 0;
for (idef=(TCOD_struct_int_t **)TCOD_list_begin(p->structs); idef!= (TCOD_struct_int_t **)TCOD_list_end(p->structs); idef++) {
free((*idef)->name);
for ( propCleanup = (TCOD_struct_prop_t**) TCOD_list_begin((*idef)->props); propCleanup != (TCOD_struct_prop_t**)TCOD_list_end((*idef)->props); propCleanup++ ) {
free((*propCleanup)->name);
}
TCOD_list_clear_and_delete((*idef)->props);
for ( listCleanup = (char ***) TCOD_list_begin((*idef)->lists); listCleanup != (char ***)TCOD_list_end((*idef)->lists); listCleanup++ ) {
while((*listCleanup)[listSize] != NULL) {
free((*listCleanup)[listSize]);
listSize++;
}
}
TCOD_list_clear_and_delete((*idef)->lists);
}
TCOD_list_clear_and_delete(p->structs);
}
static TCOD_list_t *default_props;
void TCOD_parser_run(TCOD_parser_t parser, const char *filename, TCOD_parser_listener_t *_listener) {
TCOD_parser_int_t *p=(TCOD_parser_int_t *)parser;
if (! _listener && ! p->props ) p->props=TCOD_list_new();
listener=_listener ? _listener : &default_listener;
default_props = p->props;
lex=TCOD_lex_new(symbols,keywords,"//","/*","*/",NULL,"\"",TCOD_LEX_FLAG_NESTING_COMMENT);
if (!TCOD_lex_set_data_file(lex,(char *)filename)) {
char buf[1024];
sprintf(buf,"Fatal error : %s\n",TCOD_lex_get_last_error());
listener->error(buf);
return;
}
while (1) {
bool named=false;
char id[ BIG_NAME_LEN*2 + 2 ];
char type[ BIG_NAME_LEN ];
TCOD_lex_t save;
TCOD_struct_int_t *def=NULL;
TCOD_struct_int_t **idef;
bool dynStruct=false;
TCOD_lex_parse(lex);
if ( lex->token_type == TCOD_LEX_EOF || lex->token_type == TCOD_LEX_ERROR ) break;
if ( lex->token_type == TCOD_LEX_KEYWORD ) {
if ( strcmp(lex->tok,"struct") == 0) {
dynStruct=true;
TCOD_lex_parse(lex);
} else {
TCOD_parser_error("Parser::parse : unexpected keyword '%s'",lex->tok);
return;
}
}
if ( lex->token_type != TCOD_LEX_IDEN ) {
TCOD_parser_error("Parser::parse : identifier token expected");
return;
}
string_copy(type,lex->tok,BIG_NAME_LEN);
strcpy(id,type);
TCOD_lex_savepoint(lex,&save);
if ( TCOD_lex_parse(lex) == TCOD_LEX_STRING ) {
strcat(id,"#");
if ( strlen(lex->tok) >= BIG_NAME_LEN ) {
TCOD_parser_error("Parser::parse : name %s too long. Max %d characters",
lex->tok,BIG_NAME_LEN-1);
return;
}
strcat(id,lex->tok);
named=true;
}
TCOD_lex_restore(lex,&save);
do {
for (idef=(TCOD_struct_int_t **)TCOD_list_begin(p->structs); idef!=(TCOD_struct_int_t **)TCOD_list_end(p->structs); idef ++) {
if ( strcmp((*idef)->name,id) == 0 ) {
def=*idef;
break;
}
}
if ( def == NULL && named ) {
for (idef=(TCOD_struct_int_t **)TCOD_list_begin(p->structs); idef!=(TCOD_struct_int_t **)TCOD_list_end(p->structs); idef ++) {
if ( strcmp((*idef)->name,type) == 0 ) {
def=(*idef);
break;
}
}
}
if ( def == NULL && dynStruct ) {
TCOD_parser_new_struct(parser,type);
}
} while ( def == NULL && dynStruct );
if (def == NULL ) {
TCOD_parser_error("Parser::parse : unknown entity type %s",type);
return;
} else {
if (!listener->new_struct((TCOD_parser_struct_t)def,named ? strchr(id,'#')+1 : NULL )) return;
if (!TCOD_parser_parse_entity(p,def)) return;
}
}
if (lex->token_type == TCOD_LEX_ERROR) {
TCOD_parser_error("Parser::parse : error while parsing");
return;
}
TCOD_lex_delete(lex);
}
typedef struct {
char *name;
TCOD_value_type_t type;
TCOD_value_t value;
} prop_t;
static char cur_prop_name[512]="";
static bool default_new_struct(TCOD_parser_struct_t str,const char *name) {
if ( cur_prop_name[0] ) strcat(cur_prop_name,".");
strcat(cur_prop_name,((TCOD_struct_int_t *)str)->name);
return true;
}
static bool default_new_flag(const char *name) {
char tmp[512];
prop_t *prop=(prop_t *)calloc(sizeof(prop_t),1);
sprintf(tmp,"%s.%s",cur_prop_name,name);
prop->name=TCOD_strdup(tmp);
prop->type=TCOD_TYPE_BOOL;
prop->value.b=true;
TCOD_list_push(default_props,prop);
return true;
}
static bool default_new_property(const char *propname, TCOD_value_type_t type, TCOD_value_t value) {
char tmp[512];
prop_t *prop=(prop_t *)calloc(sizeof(prop_t),1);
sprintf(tmp,"%s.%s",cur_prop_name,propname);
prop->name=TCOD_strdup(tmp);
prop->type=type;
prop->value=value;
TCOD_list_push(default_props,prop);
return true;
}
static bool default_end_struct(TCOD_parser_struct_t str, const char *name) {
char *ptr=strrchr(cur_prop_name,'.');
if ( ptr ) *ptr='\0';
else cur_prop_name[0]='\0';
return true;
}
static void default_error(const char *msg) {
TCOD_fatal_nopar(msg);
}
static const TCOD_value_t * TCOD_get_property(TCOD_parser_t parser, TCOD_value_type_t expectedType, const char *name) {
void **it;
char tmp[512],err[512];
char *ptr,*curname;
TCOD_struct_int_t *str=NULL;
TCOD_value_type_t type;
TCOD_parser_int_t *p=(TCOD_parser_int_t *)parser;
if (! p->props ) return NULL;
for (it=TCOD_list_begin(p->props);it!=TCOD_list_end(p->props);it++) {
prop_t *prop=*((prop_t **)it);
if (strcmp(prop->name,name) == 0 ) {
if ( expectedType == TCOD_TYPE_STRING && prop->type >= TCOD_TYPE_VALUELIST00
&& prop->type <= TCOD_TYPE_VALUELIST15 ) return &prop->value;
if ( expectedType == TCOD_TYPE_CUSTOM00 && prop->type >= TCOD_TYPE_CUSTOM00
&& prop->type <= TCOD_TYPE_CUSTOM15 ) return &prop->value;
if (prop->type != expectedType ) {
sprintf(err,"Fatal error ! Try to read property '%s' width bad type\n",name);
default_error(err);
}
return &prop->value;
}
}
strcpy(tmp,name);
ptr=strchr(tmp,'.');
curname=tmp;
sprintf(err,"Fatal error ! Try to read unknown property '%s'\n",name);
while ( ptr ) {
bool found=false;
*ptr=0;
for (it=TCOD_list_begin(p->structs);!found && it!=TCOD_list_end(p->structs);it++) {
str=*((TCOD_struct_int_t **)it);
if ( strcmp(str->name,curname) == 0 ) found=true;
}
if ( ! found ) {
default_error(err);
}
curname=ptr+1;
ptr=strchr(curname,'.');
}
if ( ! str ) {
default_error(err);
}
type = TCOD_struct_get_type((TCOD_parser_struct_t) str, curname);
if ( type == TCOD_TYPE_NONE ) {
default_error(err);
}
return NULL;
}
bool TCOD_parser_has_property(TCOD_parser_t parser, const char *name) {
void **it;
TCOD_parser_int_t *p=(TCOD_parser_int_t *)parser;
if (! p->props ) return false;
for (it=TCOD_list_begin(p->props);it!=TCOD_list_end(p->props);it++) {
prop_t *prop=*((prop_t **)it);
if (strcmp(prop->name,name) == 0 ) {
return true;
}
}
return false;
}
bool TCOD_parser_get_bool_property(TCOD_parser_t parser, const char *name) {
const TCOD_value_t *value=TCOD_get_property(parser,TCOD_TYPE_BOOL,name);
return value ? value->b : false;
}
int TCOD_parser_get_int_property(TCOD_parser_t parser, const char *name) {
const TCOD_value_t *value=TCOD_get_property(parser,TCOD_TYPE_INT,name);
return value ? value->i : 0;
}
int TCOD_parser_get_char_property(TCOD_parser_t parser, const char *name) {
const TCOD_value_t *value=TCOD_get_property(parser,TCOD_TYPE_CHAR,name);
return value ? value->c : 0;
}
float TCOD_parser_get_float_property(TCOD_parser_t parser, const char *name) {
const TCOD_value_t *value=TCOD_get_property(parser,TCOD_TYPE_FLOAT,name);
return value ? value->f : 0.0f;
}
const char * TCOD_parser_get_string_property(TCOD_parser_t parser, const char *name) {
const TCOD_value_t *value=TCOD_get_property(parser,TCOD_TYPE_STRING,name);
return value ? value->s : NULL;
}
TCOD_color_t TCOD_parser_get_color_property(TCOD_parser_t parser, const char *name) {
const TCOD_value_t *value=TCOD_get_property(parser,TCOD_TYPE_COLOR,name);
return value ? value->col : TCOD_black;
}
TCOD_dice_t TCOD_parser_get_dice_property(TCOD_parser_t parser, const char *name) {
static TCOD_dice_t default_dice={0,0,0.0f,0.0f};
const TCOD_value_t *value=TCOD_get_property(parser,TCOD_TYPE_DICE,name);
return value ? value->dice : default_dice;
}
void TCOD_parser_get_dice_property_py(TCOD_parser_t parser, const char *name, TCOD_dice_t *dice) {
*dice=TCOD_parser_get_dice_property(parser,name);
}
void * TCOD_parser_get_custom_property(TCOD_parser_t parser, const char *name) {
const TCOD_value_t *value=TCOD_get_property(parser,TCOD_TYPE_CUSTOM00,name);
return value ? value->custom : NULL;
}
TCOD_list_t TCOD_parser_get_list_property(TCOD_parser_t parser, const char *name, TCOD_value_type_t type) {
static TCOD_list_t empty_list=NULL;
const TCOD_value_t *value;
if (! empty_list ) empty_list=TCOD_list_new();
value=TCOD_get_property(parser,TCOD_TYPE_LIST|type,name);
return value ? value->list : empty_list;
}