amxmodx/compiler/libpc300/scstate.c

348 lines
9.7 KiB
C
Raw Normal View History

2005-07-24 20:00:55 +00:00
/* Pawn compiler
*
* Machine and state maintenance.
*
* Three lists are maintained here:
* - A list of automatons (state machines): these hold a name, a unique id
* (in the "index" field) and the memory address of a cell that holds the
* current state of the automaton (in the "value" field).
* - A list of states for each automaton: a name, an automaton id (in the
* "index" field) and a unique id for the state (unique in the automaton;
* states belonging to different automatons may have the same id).
* - A list of state combinations. Each function may belong to a set of states.
* This list assigns a unique id to the combination of the automaton and all
* states.
*
* For a function that has states, there is a fourth list, which is attached
* to the "symbol" structure. This list contains the code label (in the "name"
* field), the id of the state combinations (the state list id; it is stored
* in the "index" field) and the code address at which the function starts.
* The latter is currently unused.
*
* At the start of the compiled code, a set of stub functions is generated.
* Each stub function looks up the value of the "state selector" value for the
* automaton, and goes with a "switch" instruction to the start address of the
* function. This happens in SC4.C.
*
*
* Copyright (c) ITB CompuPhase, 2005
*
* This software is provided "as-is", without any express or implied warranty.
* In no event will the authors be held liable for any damages arising from
* the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software in
* a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*
* Version: $Id: scstate.c 1724 2005-07-24 20:00:55Z dvander $
2005-07-24 20:00:55 +00:00
*/
#include <assert.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "sc.h"
#if defined LINUX || defined __FreeBSD__ || defined __OpenBSD__
#include <sclinux.h>
#endif
#if defined FORTIFY
#include "fortify.h"
#endif
typedef struct s_statelist {
struct s_statelist *next;
int *states; /* list of states in this combination */
int numstates; /* number of items in the above list */
int fsa; /* automaton id */
int listid; /* unique id for this combination list */
} statelist;
static statelist statelist_tab = { NULL, NULL, 0, 0, 0}; /* state combinations table */
static constvalue *find_automaton(const char *name,int *last)
{
constvalue *ptr;
assert(last!=NULL);
*last=0;
ptr=sc_automaton_tab.next;
while (ptr!=NULL) {
if (strcmp(name,ptr->name)==0)
return ptr;
if (ptr->index>*last)
*last=ptr->index;
ptr=ptr->next;
} /* while */
return NULL;
}
SC_FUNC constvalue *automaton_add(const char *name)
{
constvalue *ptr;
int last;
assert(strlen(name)<sizeof(ptr->name));
ptr=find_automaton(name,&last);
if (ptr==NULL) {
assert(last+1 <= SHRT_MAX);
ptr=append_constval(&sc_automaton_tab,name,(cell)0,(short)(last+1));
/* for every new automaton, create an anonymous (invalid) state */
state_add("",last+1);
} /* if */
return ptr;
}
SC_FUNC constvalue *automaton_find(const char *name)
{
int last;
return find_automaton(name,&last);
}
SC_FUNC constvalue *automaton_findid(int id)
{
constvalue *ptr;
for (ptr=sc_automaton_tab.next; ptr!=NULL && ptr->index!=id; ptr=ptr->next)
/* nothing */;
return ptr;
}
static constvalue *find_state(const char *name,int fsa,int *last)
{
constvalue *ptr;
assert(last!=NULL);
*last=0;
ptr=sc_state_tab.next;
while (ptr!=NULL) {
if (ptr->index==fsa) {
if (strcmp(name,ptr->name)==0)
return ptr;
if ((int)ptr->value>*last)
*last=(int)ptr->value;
} /* if */
ptr=ptr->next;
} /* while */
return NULL;
}
SC_FUNC constvalue *state_add(const char *name,int fsa)
{
constvalue *ptr;
int last;
assert(strlen(name)<sizeof(ptr->name));
ptr=find_state(name,fsa,&last);
if (ptr==NULL) {
assert(fsa <= SHRT_MAX);
ptr=append_constval(&sc_state_tab,name,(cell)(last+1),(short)fsa);
} /* if */
return ptr;
}
SC_FUNC constvalue *state_find(const char *name,int fsa_id)
{
int last; /* dummy */
return find_state(name,fsa_id,&last);
}
SC_FUNC constvalue *state_findid(int id)
{
constvalue *ptr;
for (ptr=sc_state_tab.next; ptr!=NULL && ptr->value!=id; ptr=ptr->next)
/* nothing */;
return ptr;
}
SC_FUNC void state_buildlist(int **list,int *listsize,int *count,int stateid)
{
int idx;
assert(list!=NULL);
assert(listsize!=NULL);
assert(*listsize>=0);
assert(count!=NULL);
assert(*count>=0);
assert(*count<=*listsize);
if (*count==*listsize) {
/* To avoid constantly calling malloc(), the list is grown by 4 states at
* a time.
*/
*listsize+=4;
*list=(int*)realloc(*list,*listsize*sizeof(int));
if (*list==NULL)
error(103); /* insufficient memory */
} /* if */
/* find the insertion point (the list has to stay sorted) */
for (idx=0; idx<*count && *list[idx]<stateid; idx++)
/* nothing */;
if (idx<*count)
memmove(&(*list)[idx+1],&(*list)[idx],(int)((*count-idx+1)*sizeof(int)));
(*list)[idx]=stateid;
*count+=1;
}
static statelist *state_findlist(int *list,int count,int fsa,int *last)
{
statelist *ptr;
int i;
assert(count>0);
assert(last!=NULL);
*last=0;
ptr=statelist_tab.next;
while (ptr!=NULL) {
if (ptr->listid>*last)
*last=ptr->listid;
if (ptr->fsa==fsa && ptr->numstates==count) {
/* compare all states */
for (i=0; i<count && ptr->states[i]==list[i]; i++)
/* nothing */;
if (i==count)
return ptr;
} /* if */
ptr=ptr->next;
} /* while */
return NULL;
}
static statelist *state_getlist_ptr(int listid)
{
statelist *ptr;
assert(listid>0);
for (ptr=statelist_tab.next; ptr!=NULL && ptr->listid!=listid; ptr=ptr->next)
/* nothing */;
return ptr;
}
SC_FUNC int state_addlist(int *list,int count,int fsa)
{
statelist *ptr;
int last;
assert(list!=NULL);
assert(count>0);
ptr=state_findlist(list,count,fsa,&last);
if (ptr==NULL) {
if ((ptr=(statelist*)malloc(sizeof(statelist)))==NULL)
error(103); /* insufficient memory */
if ((ptr->states=(int*)malloc(count*sizeof(int)))==NULL) {
free(ptr);
error(103); /* insufficient memory */
} /* if */
memcpy(ptr->states,list,count*sizeof(int));
ptr->numstates=count;
ptr->fsa=fsa;
ptr->listid=last+1;
ptr->next=statelist_tab.next;
statelist_tab.next=ptr;
} /* if */
assert(ptr!=NULL);
return ptr->listid;
}
SC_FUNC void state_deletetable(void)
{
statelist *ptr;
while (statelist_tab.next!=NULL) {
ptr=statelist_tab.next;
/* unlink first */
statelist_tab.next=ptr->next;
/* then delete */
assert(ptr->states!=NULL);
free(ptr->states);
free(ptr);
} /* while */
}
SC_FUNC int state_getfsa(int listid)
{
statelist *ptr=state_getlist_ptr(listid);
return (ptr!=NULL) ? ptr->fsa : -1; /* fsa 0 exists */
}
SC_FUNC int state_count(int listid)
{
statelist *ptr=state_getlist_ptr(listid);
if (ptr==NULL)
return 0; /* unknown list, no states in it */
return ptr->numstates;
}
SC_FUNC int state_inlist(int listid,int state)
{
statelist *ptr;
int i;
ptr=state_getlist_ptr(listid);
if (ptr==NULL)
return FALSE; /* unknown list, state not in it */
for (i=0; i<ptr->numstates; i++)
if (ptr->states[i]==state)
return TRUE;
return FALSE;
}
SC_FUNC int state_listitem(int listid,int index)
{
statelist *ptr;
ptr=state_getlist_ptr(listid);
assert(ptr!=NULL);
assert(index>=0 && index<ptr->numstates);
return ptr->states[index];
}
/* This function searches whether one of the states in the list of statelist id's
* of a symbol exists in any other statelist id's of the same function; it also
* verifies that all definitions of the symbol are in the same automaton.
*/
SC_FUNC void state_conflict(symbol *root)
{
statelist *psrc,*ptgt;
constvalue *srcptr,*tgtptr;
int s,t;
symbol *sym;
assert(root!=NULL);
for (sym=root->next; sym!=NULL; sym=sym->next) {
if (sym->parent!=NULL || sym->ident!=iFUNCTN)
continue; /* hierarchical data type or no function */
if (sym->states==NULL)
continue; /* this function has no states */
for (srcptr=sym->states->next; srcptr!=NULL; srcptr=srcptr->next) {
if (srcptr->index==-1)
continue; /* state list id -1 is a special case */
psrc=state_getlist_ptr(srcptr->index);
assert(psrc!=NULL);
for (tgtptr=srcptr->next; tgtptr!=NULL; tgtptr=tgtptr->next) {
if (tgtptr->index==-1)
continue; /* state list id -1 is a special case */
ptgt=state_getlist_ptr(tgtptr->index);
assert(ptgt!=NULL);
if (psrc->fsa!=ptgt->fsa && strcmp(sym->name,uENTRYFUNC)!=0)
error(83,sym->name); /* this function is part of another machine */
for (s=0; s<psrc->numstates; s++)
for (t=0; t<ptgt->numstates; t++)
if (psrc->states[s]==ptgt->states[t])
error(84,sym->name); /* state conflict */
} /* for (tgtptr) */
} /* for (srcptr) */
} /* for (sym) */
}