%{
/**************************************************************************
Lex specification for post-process knowledge file
6/96 ALB
Possible areas of improvement:
. allow sets to be referred to anywhere in file, not just
after they are defined. (but then you need to be careful
about infinite loops involving recursively defined sets)
**************************************************************************/
#include "pp_lexer.h"
#include "pp-structures.h"
#include "string-set.h"
#include "utilities.h"
#define YY_NO_INPUT
#define YY_NO_UNPUT
/* forward references for non-exported functions */
static void setup(PPLexTable *lt);
static bool set_label(PPLexTable *lt, const char *label);
static bool add_string_to_label(PPLexTable *lt, const char *str);
static bool add_set_of_strings_to_label(PPLexTable *lt,const char *label_of_set);
static int get_index_of_label(PPLexTable *lt, const char *label);
/* ptr to lex table we're currently filling in */
#define YY_EXTRA_TYPE PPLexTable*
%}
%option reentrant
%s INCLUDE
LABEL [^ \t\"\n:]+:
NONLABELSET @[^ \t\n\":]+
NONLABEL [^ \t\"\n:]+
NONLABELQUOTED \"[^\t\"\n:]+\"
WHITE [ \t\n]*
COMMENT ^[\ \t]*;.*$
%%
{COMMENT} ;
{WHITE} ;
<INITIAL>{LABEL} { set_label(yyextra, yytext); }
<INITIAL>{NONLABELSET} { add_set_of_strings_to_label(yyextra, &(yytext[1])); }
<INITIAL>{NONLABEL} { add_string_to_label(yyextra, yytext); }
<INITIAL>{NONLABELQUOTED} { add_string_to_label(yyextra, yytext); }
. { prt_error("pp_lexer: unable to parse knowledge file (line %i).\n", yylineno); }
%%
/************************ exported functions ******************************/
void pp_lexer_close(PPLexTable *lt)
{
int i;
pp_label_node *node,*next;
for (i=0; i<PP_LEXER_MAX_LABELS; i++)
{
/* free the linked list */
node = lt->nodes_of_label[i];
while (node)
{
next = node->next;
free(node);
node=next;
}
}
string_set_delete(lt->string_set);
if (lt->tokens) free(lt->tokens);
yylex_destroy(lt->scanner);
free(lt);
}
PPLexTable *pp_lexer_open(FILE *f)
{
PPLexTable *lt;
bool yylex_ok = false;
assert(f, "Passed a NULL file pointer");
lt = (PPLexTable*) malloc (sizeof(PPLexTable));
setup(lt);
yylex_init(<->scanner);
yyset_extra(lt, lt->scanner);
struct yyguts_t * yyg = (struct yyguts_t*) lt->scanner;
yyin = f; /* redirect lex to look at the specified file */
if (YY_NULL == yylex(lt->scanner)) yylex_ok = true;
lt->idx_of_active_label=-1;
if (!yylex_ok)
{
pp_lexer_close(lt);
return NULL;
}
return lt;
}
int pp_lexer_set_label(PPLexTable *lt, const char *label)
{
/* set lexer state to first node of this label */
lt->idx_of_active_label = get_index_of_label(lt, label);
if (lt->idx_of_active_label==-1) return 0; /* label not found */
lt->current_node_of_active_label=lt->nodes_of_label[lt->idx_of_active_label];
return 1;
}
int pp_lexer_count_tokens_of_label(PPLexTable *lt)
{
/* counts all tokens, even the commas */
int n;
pp_label_node *p;
if (lt->idx_of_active_label == -1)
{
prt_error("Error: pp_lexer: current label is invalid\n");
return -1;
}
for (n=0, p=lt->nodes_of_label[lt->idx_of_active_label]; p;p=p->next, n++){}
return n;
}
/** Retrieves next token of set label, or NULL if list exhausted. */
const char *pp_lexer_get_next_token_of_label(PPLexTable *lt)
{
if (lt->current_node_of_active_label == NULL) return NULL;
const char *p = lt->current_node_of_active_label->str;
lt->current_node_of_active_label = lt->current_node_of_active_label->next;
return p;
}
int pp_lexer_count_commas_of_label(PPLexTable *lt)
{
int n;
pp_label_node *p;
if (lt->idx_of_active_label == -1)
{
prt_error("Error: pp_lexer: current label is invalid\n");
return -1;
}
for (n=0,p=lt->nodes_of_label[lt->idx_of_active_label];p!=NULL;p=p->next)
if (!strcmp(p->str, ",")) n++;
return n;
}
const char **pp_lexer_get_next_group_of_tokens_of_label(PPLexTable *lt, size_t *n_tokens)
{
/* all tokens until next comma, null-terminated */
int n;
pp_label_node *p;
p = lt->current_node_of_active_label;
for (n=0; p!=NULL && strcmp(p->str,","); n++, p=p->next) {}
if (n > lt->extents) {
lt->extents = n;
lt->tokens = (const char **) realloc (lt->tokens, n * sizeof(const char*));
}
p = lt->current_node_of_active_label;
for (n=0; p!=NULL && strcmp(p->str,","); n++, p=p->next)
lt->tokens[n] = string_set_add(p->str, lt->string_set);
/* advance "current node of label" state */
lt->current_node_of_active_label = p;
if (p!=NULL) lt->current_node_of_active_label = p->next;
*n_tokens = n;
return lt->tokens;
}
int yywrap(void* foo)
{
/* must return 1 for end of input, 0 otherwise */
return 1;
}
/********************** non-exported functions ************************/
static void setup(PPLexTable *lt)
{
int i;
for (i=0; i<PP_LEXER_MAX_LABELS; i++)
{
lt->nodes_of_label[i] = NULL;
lt->last_node_of_label[i] = NULL;
lt->labels[i] = NULL;
}
lt->string_set = string_set_create();
lt->tokens = NULL;
lt->extents = 0;
}
static bool set_label(PPLexTable *lt, const char *label)
{
int i;
char *c;
char *label_sans_colon;
/* check for and then slice off the trailing colon */
label_sans_colon = strdupa(label);
c=&(label_sans_colon[strlen(label_sans_colon)-1]);
if (*c != ':')
{
prt_error("Error: Label %s must end with :\n", label);
return false;
}
*c = 0;
/* have we seen this label already? If so, abort */
for (i=0;lt->labels[i]!=NULL && strcmp(lt->labels[i],label_sans_colon);i++) {}
if (lt->labels[i]!=NULL)
{
prt_error("Error: pp_lexer: label %s multiply defined!\n", label_sans_colon);
return false;
}
/* new label. Store it */
if (i == PP_LEXER_MAX_LABELS-1)
{
prt_error("Error: pp_lexer: too many labels. Raise PP_LEXER_MAX_LABELS\n");
return false;
}
lt->labels[i] = string_set_add(label_sans_colon, lt->string_set);
lt->idx_of_active_label = i;
return true;
}
static bool check_string(const char *str)
{
if (strlen(str)>1 && strchr(str, ',')!=NULL)
{
prt_error("Error: pp_lexer: string %s contains a comma, which is a no-no.\n", str);
return false;
}
return true;
}
static bool add_string_to_label(PPLexTable *lt, const char *str)
{
struct yyguts_t * yyg = (struct yyguts_t*) lt->scanner;
/* add the single string str to the set of strings associated with label */
pp_label_node *new_node;
if (lt->idx_of_active_label == -1)
{
prt_error("Error: pp_lexer: invalid syntax (line %i)\n", yylineno);
return false;
}
/* make sure string is legal */
if (!check_string(str)) return false;
/* create a new node in (as yet to be determined) linked list of strings */
new_node = (pp_label_node *) malloc (sizeof(pp_label_node));
new_node->str = string_set_add(str, lt->string_set);
new_node->next = NULL;
/* stick newly-created node at the *end* of the appropriate linked list */
if (lt->last_node_of_label[lt->idx_of_active_label]==NULL)
{
/* first entry on linked list */
lt->nodes_of_label[lt->idx_of_active_label] = new_node;
lt->last_node_of_label[lt->idx_of_active_label] = new_node;
}
else
{
/* non-first entry on linked list */
lt->last_node_of_label[lt->idx_of_active_label]->next = new_node;
lt->last_node_of_label[lt->idx_of_active_label] = new_node;
}
return true;
}
static bool add_set_of_strings_to_label(PPLexTable *lt,const char *label_of_set)
{
struct yyguts_t * yyg = (struct yyguts_t*) lt->scanner;
/* add the set of strings, defined earlier by label_of_set, to the set of
strings associated with the current label */
pp_label_node *p;
int idx_of_label_of_set;
if (lt->idx_of_active_label==-1)
{
prt_error("Error: pp_lexer: invalid syntax (line %i)\n", yylineno);
return false;
}
if ((idx_of_label_of_set = get_index_of_label(lt, label_of_set))==-1)
{
prt_error("Error: pp_lexer: label %s must be defined before it's referred to (line %i)\n",
label_of_set, yylineno);
return false;
}
for (p=lt->nodes_of_label[idx_of_label_of_set]; p!=NULL; p=p->next)
add_string_to_label(lt, p->str);
return true;
}
static int get_index_of_label(PPLexTable *lt, const char *label)
{
int i;
for (i=0; lt->labels[i]!=NULL; i++)
if (!strcmp(lt->labels[i], label)) return i;
return -1;
}