251cced1f8
Various minor things done to project files Updated sample extension project file and updated makefile to the new unified version (more changes likely on the way) Updated regex project file and makefile --HG-- extra : convert_revision : svn%3A39bc706e-5318-0410-9160-8a85361fbb7c/trunk%401971
376 lines
10 KiB
C
376 lines
10 KiB
C
/* 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/variable 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, only for functions), 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-2006
|
|
*
|
|
* 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$
|
|
*/
|
|
#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 <alloc/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));
|
|
} /* 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;
|
|
|
|
assert(listid>=0);
|
|
if (listid==0)
|
|
return -1;
|
|
|
|
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];
|
|
}
|
|
|
|
static int checkconflict(statelist *psrc,statelist *ptgt)
|
|
{
|
|
int s,t;
|
|
|
|
assert(psrc!=NULL);
|
|
assert(ptgt!=NULL);
|
|
for (s=0; s<psrc->numstates; s++)
|
|
for (t=0; t<ptgt->numstates; t++)
|
|
if (psrc->states[s]==ptgt->states[t])
|
|
return 1; /* state conflict */
|
|
return 0;
|
|
}
|
|
|
|
/* 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;
|
|
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 */
|
|
if (checkconflict(psrc,ptgt))
|
|
error(84,sym->name); /* state conflict */
|
|
} /* for (tgtptr) */
|
|
} /* for (srcptr) */
|
|
} /* for (sym) */
|
|
}
|
|
|
|
/* check whether the two state lists (whose ids are passed in) share any
|
|
* states
|
|
*/
|
|
SC_FUNC int state_conflict_id(int listid1,int listid2)
|
|
{
|
|
statelist *psrc,*ptgt;
|
|
|
|
psrc=state_getlist_ptr(listid1);
|
|
assert(psrc!=NULL);
|
|
ptgt=state_getlist_ptr(listid2);
|
|
assert(ptgt!=NULL);
|
|
return checkconflict(psrc,ptgt);
|
|
}
|