You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
4085 lines
112 KiB
4085 lines
112 KiB
/* Small compiler |
|
* Function and variable definition and declaration, statement parser. |
|
* |
|
* Copyright (c) ITB CompuPhase, 1997-2003 |
|
* |
|
* 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$ |
|
*/ |
|
|
|
|
|
#ifdef HAVE_CONFIG_H |
|
# include <config.h> |
|
#endif |
|
|
|
#include <assert.h> |
|
#include <ctype.h> |
|
#include <limits.h> |
|
#include <stdarg.h> |
|
#include <stdio.h> |
|
#include <stdlib.h> |
|
#include <string.h> |
|
|
|
#ifdef HAVE_UNISTD_H |
|
# include <unistd.h> |
|
#endif |
|
|
|
#ifdef HAVE_EVIL |
|
# include <Evil.h> |
|
#endif |
|
|
|
#include <Eina.h> |
|
|
|
#include "embryo_cc_sc.h" |
|
#include "embryo_cc_prefix.h" |
|
|
|
#define VERSION_STR "2.4" |
|
#define VERSION_INT 240 |
|
|
|
static void resetglobals(void); |
|
static void initglobals(void); |
|
static void setopt(int argc, char **argv, |
|
char *iname, char *oname, |
|
char *pname, char *rname); |
|
static void setconfig(char *root); |
|
static void about(void); |
|
static void setconstants(void); |
|
static void parse(void); |
|
static void dumplits(void); |
|
static void dumpzero(int count); |
|
static void declfuncvar(int tok, char *symname, |
|
int tag, int fpublic, |
|
int fstatic, int fstock, int fconst); |
|
static void declglb(char *firstname, int firsttag, |
|
int fpublic, int fstatic, int stock, int fconst); |
|
static int declloc(int fstatic); |
|
static void decl_const(int table); |
|
static void decl_enum(int table); |
|
static cell needsub(int *tag); |
|
static void initials(int ident, int tag, |
|
cell * size, int dim[], int numdim); |
|
static cell initvector(int ident, int tag, cell size, int fillzero); |
|
static cell init(int ident, int *tag); |
|
static void funcstub(int native); |
|
static int newfunc(char *firstname, int firsttag, |
|
int fpublic, int fstatic, int stock); |
|
static int declargs(symbol * sym); |
|
static void doarg(char *name, int ident, int offset, |
|
int tags[], int numtags, |
|
int fpublic, int fconst, arginfo * arg); |
|
static void reduce_referrers(symbol * root); |
|
static int testsymbols(symbol * root, int level, |
|
int testlabs, int testconst); |
|
static void destructsymbols(symbol * root, int level); |
|
static constvalue *find_constval_byval(constvalue * table, cell val); |
|
static void statement(int *lastindent, int allow_decl); |
|
static void compound(void); |
|
static void doexpr(int comma, int chkeffect, |
|
int allowarray, int mark_endexpr, |
|
int *tag, int chkfuncresult); |
|
static void doassert(void); |
|
static void doexit(void); |
|
static void test(int label, int parens, int invert); |
|
static void doif(void); |
|
static void dowhile(void); |
|
static void dodo(void); |
|
static void dofor(void); |
|
static void doswitch(void); |
|
static void dogoto(void); |
|
static void dolabel(void); |
|
static symbol *fetchlab(char *name); |
|
static void doreturn(void); |
|
static void dobreak(void); |
|
static void docont(void); |
|
static void dosleep(void); |
|
static void addwhile(int *ptr); |
|
static void delwhile(void); |
|
static int *readwhile(void); |
|
|
|
static int lastst = 0; /* last executed statement type */ |
|
static int nestlevel = 0; /* number of active (open) compound statements */ |
|
static int rettype = 0; /* the type that a "return" expression should have */ |
|
static int skipinput = 0; /* number of lines to skip from the first input file */ |
|
static int wq[wqTABSZ]; /* "while queue", internal stack for nested loops */ |
|
static int *wqptr; /* pointer to next entry */ |
|
static char binfname[PATH_MAX]; /* binary file name */ |
|
|
|
int |
|
main(int argc, char *argv[], char *env[] EINA_UNUSED) |
|
{ |
|
e_prefix_determine(argv[0]); |
|
return sc_compile(argc, argv); |
|
} |
|
|
|
int |
|
sc_error(int number, char *message, char *filename, int firstline, |
|
int lastline, va_list argptr) |
|
{ |
|
static char *prefix[3] = { "error", "fatal error", "warning" }; |
|
|
|
if (number != 0) |
|
{ |
|
char *pre; |
|
|
|
pre = prefix[number / 100]; |
|
if (firstline >= 0) |
|
fprintf(stderr, "%s(%d -- %d) : %s %03d: ", filename, firstline, |
|
lastline, pre, number); |
|
else |
|
fprintf(stderr, "%s(%d) : %s %03d: ", filename, lastline, pre, |
|
number); |
|
} /* if */ |
|
vfprintf(stderr, message, argptr); |
|
fflush(stderr); |
|
return 0; |
|
} |
|
|
|
void * |
|
sc_opensrc(char *filename) |
|
{ |
|
return fopen(filename, "rb"); |
|
} |
|
|
|
void |
|
sc_closesrc(void *handle) |
|
{ |
|
assert(handle != NULL); |
|
fclose((FILE *) handle); |
|
} |
|
|
|
void |
|
sc_resetsrc(void *handle, void *position) |
|
{ |
|
assert(handle != NULL); |
|
fsetpos((FILE *) handle, (fpos_t *) position); |
|
} |
|
|
|
char * |
|
sc_readsrc(void *handle, char *target, int maxchars) |
|
{ |
|
return fgets(target, maxchars, (FILE *) handle); |
|
} |
|
|
|
void * |
|
sc_getpossrc(void *handle) |
|
{ |
|
static fpos_t lastpos; /* may need to have a LIFO stack of |
|
* such positions */ |
|
|
|
fgetpos((FILE *) handle, &lastpos); |
|
return &lastpos; |
|
} |
|
|
|
int |
|
sc_eofsrc(void *handle) |
|
{ |
|
return feof((FILE *) handle); |
|
} |
|
|
|
void * |
|
sc_openasm(int fd) |
|
{ |
|
return fdopen(fd, "w+"); |
|
} |
|
|
|
void |
|
sc_closeasm(void *handle) |
|
{ |
|
if (handle) |
|
fclose((FILE *) handle); |
|
} |
|
|
|
void |
|
sc_resetasm(void *handle) |
|
{ |
|
fflush((FILE *) handle); |
|
fseek((FILE *) handle, 0, SEEK_SET); |
|
} |
|
|
|
int |
|
sc_writeasm(void *handle, char *st) |
|
{ |
|
return fputs(st, (FILE *) handle) >= 0; |
|
} |
|
|
|
char * |
|
sc_readasm(void *handle, char *target, int maxchars) |
|
{ |
|
return fgets(target, maxchars, (FILE *) handle); |
|
} |
|
|
|
void * |
|
sc_openbin(char *filename) |
|
{ |
|
return fopen(filename, "wb"); |
|
} |
|
|
|
void |
|
sc_closebin(void *handle, int deletefile) |
|
{ |
|
fclose((FILE *) handle); |
|
if (deletefile) |
|
unlink(binfname); |
|
} |
|
|
|
void |
|
sc_resetbin(void *handle) |
|
{ |
|
fflush((FILE *) handle); |
|
fseek((FILE *) handle, 0, SEEK_SET); |
|
} |
|
|
|
int |
|
sc_writebin(void *handle, void *buffer, int size) |
|
{ |
|
return (int)fwrite(buffer, 1, size, (FILE *) handle) == size; |
|
} |
|
|
|
long |
|
sc_lengthbin(void *handle) |
|
{ |
|
return ftell((FILE *) handle); |
|
} |
|
|
|
/* "main" of the compiler |
|
*/ |
|
int |
|
sc_compile(int argc, char *argv[]) |
|
{ |
|
int entry, i, jmpcode, fd_out; |
|
int retcode; |
|
char incfname[PATH_MAX]; |
|
char reportname[PATH_MAX]; |
|
FILE *binf; |
|
void *inpfmark; |
|
char lcl_ctrlchar; |
|
int lcl_packstr, lcl_needsemicolon, lcl_tabsize; |
|
char *tmpdir; |
|
|
|
/* set global variables to their initial value */ |
|
binf = NULL; |
|
initglobals(); |
|
errorset(sRESET); |
|
errorset(sEXPRRELEASE); |
|
lexinit(); |
|
|
|
/* make sure that we clean up on a fatal error; do this before the |
|
* first call to error(). */ |
|
if ((jmpcode = setjmp(errbuf)) != 0) |
|
goto cleanup; |
|
|
|
/* allocate memory for fixed tables */ |
|
inpfname = (char *)malloc(PATH_MAX); |
|
litq = (cell *) malloc(litmax * sizeof(cell)); |
|
if (!litq) |
|
error(103); /* insufficient memory */ |
|
if (!phopt_init()) |
|
error(103); /* insufficient memory */ |
|
|
|
setopt(argc, argv, inpfname, binfname, incfname, reportname); |
|
|
|
/* open the output file */ |
|
|
|
#ifndef HAVE_EVIL |
|
tmpdir = getenv("TMPDIR"); |
|
if (!tmpdir) tmpdir = "/tmp"; |
|
#else |
|
tmpdir = (char *)evil_tmpdir_get(); |
|
#endif /* ! HAVE_EVIL */ |
|
|
|
snprintf(outfname, PATH_MAX, "%s/embryo_cc.asm-tmp-XXXXXX", tmpdir); |
|
fd_out = mkstemp(outfname); |
|
if (fd_out < 0) |
|
error(101, outfname); |
|
|
|
setconfig(argv[0]); /* the path to the include files */ |
|
lcl_ctrlchar = sc_ctrlchar; |
|
lcl_packstr = sc_packstr; |
|
lcl_needsemicolon = sc_needsemicolon; |
|
lcl_tabsize = sc_tabsize; |
|
inpf = inpf_org = (FILE *) sc_opensrc(inpfname); |
|
if (!inpf) |
|
error(100, inpfname); |
|
freading = TRUE; |
|
outf = (FILE *) sc_openasm(fd_out); /* first write to assembler |
|
* file (may be temporary) */ |
|
if (!outf) |
|
error(101, outfname); |
|
/* immediately open the binary file, for other programs to check */ |
|
binf = (FILE *) sc_openbin(binfname); |
|
if (!binf) |
|
error(101, binfname); |
|
setconstants(); /* set predefined constants and tagnames */ |
|
for (i = 0; i < skipinput; i++) /* skip lines in the input file */ |
|
if (sc_readsrc(inpf, pline, sLINEMAX)) |
|
fline++; /* keep line number up to date */ |
|
skipinput = fline; |
|
sc_status = statFIRST; |
|
/* do the first pass through the file */ |
|
inpfmark = sc_getpossrc(inpf); |
|
if (incfname[0] != '\0') |
|
{ |
|
if (strcmp(incfname, sDEF_PREFIX) == 0) |
|
{ |
|
plungefile(incfname, FALSE, TRUE); /* parse "default.inc" */ |
|
} |
|
else |
|
{ |
|
if (!plungequalifiedfile(incfname)) /* parse "prefix" include |
|
* file */ |
|
error(100, incfname); /* cannot read from ... (fatal error) */ |
|
} /* if */ |
|
} /* if */ |
|
preprocess(); /* fetch first line */ |
|
parse(); /* process all input */ |
|
|
|
/* second pass */ |
|
sc_status = statWRITE; /* set, to enable warnings */ |
|
|
|
/* ??? for re-parsing the listing file instead of the original source |
|
* file (and doing preprocessing twice): |
|
* - close input file, close listing file |
|
* - re-open listing file for reading (inpf) |
|
* - open assembler file (outf) |
|
*/ |
|
|
|
/* reset "defined" flag of all functions and global variables */ |
|
reduce_referrers(&glbtab); |
|
delete_symbols(&glbtab, 0, TRUE, FALSE); |
|
#if !defined NO_DEFINE |
|
delete_substtable(); |
|
#endif |
|
resetglobals(); |
|
sc_ctrlchar = lcl_ctrlchar; |
|
sc_packstr = lcl_packstr; |
|
sc_needsemicolon = lcl_needsemicolon; |
|
sc_tabsize = lcl_tabsize; |
|
errorset(sRESET); |
|
/* reset the source file */ |
|
inpf = inpf_org; |
|
freading = TRUE; |
|
sc_resetsrc(inpf, inpfmark); /* reset file position */ |
|
fline = skipinput; /* reset line number */ |
|
lexinit(); /* clear internal flags of lex() */ |
|
sc_status = statWRITE; /* allow to write --this variable was reset |
|
* by resetglobals() */ |
|
writeleader(); |
|
setfile(inpfname, fnumber); |
|
if (incfname[0] != '\0') |
|
{ |
|
if (strcmp(incfname, sDEF_PREFIX) == 0) |
|
plungefile(incfname, FALSE, TRUE); /* parse "default.inc" (again) */ |
|
else |
|
plungequalifiedfile(incfname); /* parse implicit include |
|
* file (again) */ |
|
} /* if */ |
|
preprocess(); /* fetch first line */ |
|
parse(); /* process all input */ |
|
/* inpf is already closed when readline() attempts to pop of a file */ |
|
writetrailer(); /* write remaining stuff */ |
|
|
|
entry = testsymbols(&glbtab, 0, TRUE, FALSE); /* test for unused |
|
* or undefined functions and variables */ |
|
if (!entry) |
|
error(13); /* no entry point (no public functions) */ |
|
|
|
cleanup: |
|
if (inpf) /* main source file is not closed, do it now */ |
|
sc_closesrc(inpf); |
|
/* write the binary file (the file is already open) */ |
|
if (errnum == 0 && jmpcode == 0) |
|
{ |
|
assert(binf != NULL); |
|
sc_resetasm(outf); /* flush and loop back, for reading */ |
|
assemble(binf, outf); /* assembler file is now input */ |
|
} /* if */ |
|
if (outf) |
|
sc_closeasm(outf); |
|
unlink (outfname); |
|
if (binf) |
|
sc_closebin(binf, errnum != 0); |
|
|
|
if (inpfname) |
|
free(inpfname); |
|
if (litq) |
|
free(litq); |
|
phopt_cleanup(); |
|
stgbuffer_cleanup(); |
|
assert(jmpcode != 0 || loctab.next == NULL); /* on normal flow, |
|
* local symbols |
|
* should already have been deleted */ |
|
delete_symbols(&loctab, 0, TRUE, TRUE); /* delete local variables |
|
* if not yet done (i.e. |
|
* on a fatal error) */ |
|
delete_symbols(&glbtab, 0, TRUE, TRUE); |
|
delete_consttable(&tagname_tab); |
|
delete_consttable(&libname_tab); |
|
delete_aliastable(); |
|
delete_pathtable(); |
|
#if !defined NO_DEFINE |
|
delete_substtable(); |
|
#endif |
|
if (errnum != 0) |
|
{ |
|
printf("\n%d Error%s.\n", errnum, (errnum > 1) ? "s" : ""); |
|
retcode = 2; |
|
} |
|
else if (warnnum != 0) |
|
{ |
|
printf("\n%d Warning%s.\n", warnnum, (warnnum > 1) ? "s" : ""); |
|
retcode = 1; |
|
} |
|
else |
|
{ |
|
retcode = jmpcode; |
|
} /* if */ |
|
return retcode; |
|
} |
|
|
|
int |
|
sc_addconstant(char *name, cell value, int tag) |
|
{ |
|
errorset(sFORCESET); /* make sure error engine is silenced */ |
|
sc_status = statIDLE; |
|
add_constant(name, value, sGLOBAL, tag); |
|
return 1; |
|
} |
|
|
|
int |
|
sc_addtag(char *name) |
|
{ |
|
cell val; |
|
constvalue *ptr; |
|
int last, tag; |
|
|
|
if (!name) |
|
{ |
|
/* no tagname was given, check for one */ |
|
if (lex(&val, &name) != tLABEL) |
|
{ |
|
lexpush(); |
|
return 0; /* untagged */ |
|
} /* if */ |
|
} /* if */ |
|
|
|
last = 0; |
|
ptr = tagname_tab.next; |
|
while (ptr) |
|
{ |
|
tag = (int)(ptr->value & TAGMASK); |
|
if (strcmp(name, ptr->name) == 0) |
|
return tag; /* tagname is known, return its sequence number */ |
|
tag &= (int)~FIXEDTAG; |
|
if (tag > last) |
|
last = tag; |
|
ptr = ptr->next; |
|
} /* while */ |
|
|
|
/* tagname currently unknown, add it */ |
|
tag = last + 1; /* guaranteed not to exist already */ |
|
if (sc_isupper(*name)) |
|
tag |= (int)FIXEDTAG; |
|
append_constval(&tagname_tab, name, (cell) tag, 0); |
|
return tag; |
|
} |
|
|
|
static void |
|
resetglobals(void) |
|
{ |
|
/* reset the subset of global variables that is modified by the |
|
* first pass */ |
|
curfunc = NULL; /* pointer to current function */ |
|
lastst = 0; /* last executed statement type */ |
|
nestlevel = 0; /* number of active (open) compound statements */ |
|
rettype = 0; /* the type that a "return" expression should have */ |
|
litidx = 0; /* index to literal table */ |
|
stgidx = 0; /* index to the staging buffer */ |
|
labnum = 0; /* number of (internal) labels */ |
|
staging = 0; /* true if staging output */ |
|
declared = 0; /* number of local cells declared */ |
|
glb_declared = 0; /* number of global cells declared */ |
|
code_idx = 0; /* number of bytes with generated code */ |
|
ntv_funcid = 0; /* incremental number of native function */ |
|
curseg = 0; /* 1 if currently parsing CODE, 2 if parsing DATA */ |
|
freading = FALSE; /* no input file ready yet */ |
|
fline = 0; /* the line number in the current file */ |
|
fnumber = 0; /* the file number in the file table (debugging) */ |
|
fcurrent = 0; /* current file being processed (debugging) */ |
|
intest = 0; /* true if inside a test */ |
|
sideeffect = 0; /* true if an expression causes a side-effect */ |
|
stmtindent = 0; /* current indent of the statement */ |
|
indent_nowarn = TRUE; /* do not skip warning "217 loose indentation" */ |
|
sc_allowtags = TRUE; /* allow/detect tagnames */ |
|
sc_status = statIDLE; |
|
} |
|
|
|
static void |
|
initglobals(void) |
|
{ |
|
resetglobals(); |
|
|
|
skipinput = 0; /* number of lines to skip from the first |
|
* input file */ |
|
sc_ctrlchar = CTRL_CHAR; /* the escape character */ |
|
litmax = sDEF_LITMAX; /* current size of the literal table */ |
|
errnum = 0; /* number of errors */ |
|
warnnum = 0; /* number of warnings */ |
|
/* sc_debug=sCHKBOUNDS; by default: bounds checking+assertions */ |
|
sc_debug = 0; /* by default: no debug */ |
|
charbits = 8; /* a "char" is 8 bits */ |
|
sc_packstr = FALSE; /* strings are unpacked by default */ |
|
/* sc_compress=TRUE; compress output bytecodes */ |
|
sc_compress = FALSE; /* compress output bytecodes */ |
|
sc_needsemicolon = FALSE; /* semicolon required to terminate |
|
* expressions? */ |
|
sc_dataalign = 4; |
|
sc_stksize = sDEF_AMXSTACK; /* default stack size */ |
|
sc_tabsize = 8; /* assume a TAB is 8 spaces */ |
|
sc_rationaltag = 0; /* assume no support for rational numbers */ |
|
rational_digits = 0; /* number of fractional digits */ |
|
|
|
outfname[0] = '\0'; /* output file name */ |
|
inpf = NULL; /* file read from */ |
|
inpfname = NULL; /* pointer to name of the file currently |
|
* read from */ |
|
outf = NULL; /* file written to */ |
|
litq = NULL; /* the literal queue */ |
|
glbtab.next = NULL; /* clear global variables/constants table */ |
|
loctab.next = NULL; /* " local " / " " */ |
|
tagname_tab.next = NULL; /* tagname table */ |
|
libname_tab.next = NULL; /* library table (#pragma library "..." |
|
* syntax) */ |
|
|
|
pline[0] = '\0'; /* the line read from the input file */ |
|
lptr = NULL; /* points to the current position in "pline" */ |
|
curlibrary = NULL; /* current library */ |
|
inpf_org = NULL; /* main source file */ |
|
|
|
wqptr = wq; /* initialize while queue pointer */ |
|
|
|
} |
|
|
|
static void |
|
parseoptions(int argc, char **argv, char *iname, char *oname, |
|
char *pname EINA_UNUSED, char *rname EINA_UNUSED) |
|
{ |
|
char str[PATH_MAX]; |
|
int i, stack_size; |
|
size_t len; |
|
|
|
/* use embryo include dir always */ |
|
snprintf(str, sizeof(str), "%s/include/", e_prefix_data_get()); |
|
insert_path(str); |
|
insert_path("./"); |
|
|
|
for (i = 1; i < argc; i++) |
|
{ |
|
if (!strcmp (argv[i], "-i") && (i + 1 < argc) && *argv[i + 1]) |
|
{ |
|
/* include directory */ |
|
i++; |
|
strncpy(str, argv[i], sizeof(str)); |
|
|
|
len = strlen(str); |
|
if (str[len - 1] != DIRSEP_CHAR) |
|
{ |
|
str[len] = DIRSEP_CHAR; |
|
str[len + 1] = '\0'; |
|
} |
|
|
|
insert_path(str); |
|
} |
|
else if (!strcmp (argv[i], "-o") && (i + 1 < argc) && *argv[i + 1]) |
|
{ |
|
/* output file */ |
|
i++; |
|
strcpy(oname, argv[i]); /* FIXME */ |
|
} |
|
else if (!strcmp (argv[i], "-S") && (i + 1 < argc) && *argv[i + 1]) |
|
{ |
|
/* stack size */ |
|
i++; |
|
stack_size = atoi(argv[i]); |
|
|
|
if (stack_size > 64) |
|
sc_stksize = (cell) stack_size; |
|
else |
|
about(); |
|
} |
|
else if (!*iname) |
|
{ |
|
/* input file */ |
|
strcpy(iname, argv[i]); /* FIXME */ |
|
} |
|
else |
|
{ |
|
/* only allow one input filename */ |
|
about(); |
|
} |
|
} |
|
} |
|
|
|
static void |
|
setopt(int argc, char **argv, char *iname, char *oname, |
|
char *pname, char *rname) |
|
{ |
|
*iname = '\0'; |
|
*oname = '\0'; |
|
*pname = '\0'; |
|
*rname = '\0'; |
|
strcpy(pname, sDEF_PREFIX); |
|
|
|
parseoptions(argc, argv, iname, oname, pname, rname); |
|
if (iname[0] == '\0') |
|
about(); |
|
} |
|
|
|
static void |
|
setconfig(char *root) |
|
{ |
|
char path[PATH_MAX]; |
|
char *ptr; |
|
int len; |
|
|
|
path[sizeof(path) - 1] = 0; |
|
|
|
/* add the default "include" directory */ |
|
if (root) |
|
{ |
|
/* path + filename (hopefully) */ |
|
strncpy(path, root, sizeof(path) - 1); |
|
path[sizeof(path) - 1] = 0; |
|
} |
|
/* terminate just behind last \ or : */ |
|
if ((ptr = strrchr(path, DIRSEP_CHAR)) |
|
|| (ptr = strchr(path, ':'))) |
|
{ |
|
/* If there was no terminating "\" or ":", |
|
* the filename probably does not |
|
* contain the path; so we just don't add it |
|
* to the list in that case |
|
*/ |
|
*(ptr + 1) = '\0'; |
|
if (strlen(path) < (sizeof(path) - 1 - 7)) |
|
{ |
|
strcat(path, "include"); |
|
} |
|
len = strlen(path); |
|
path[len] = DIRSEP_CHAR; |
|
path[len + 1] = '\0'; |
|
insert_path(path); |
|
} /* if */ |
|
} |
|
|
|
static void |
|
about(void) |
|
{ |
|
printf("Usage: embryo_cc <filename> [options]\n\n"); |
|
printf("Options:\n"); |
|
#if 0 |
|
printf |
|
(" -A<num> alignment in bytes of the data segment and the\ |
|
stack\n"); |
|
|
|
printf |
|
(" -a output assembler code (skip code generation\ |
|
pass)\n"); |
|
|
|
printf |
|
(" -C[+/-] compact encoding for output file (default=%c)\n", |
|
sc_compress ? '+' : '-'); |
|
printf(" -c8 [default] a character is 8-bits\ |
|
(ASCII/ISO Latin-1)\n"); |
|
|
|
printf(" -c16 a character is 16-bits (Unicode)\n"); |
|
#if defined dos_setdrive |
|
printf(" -Dpath active directory path\n"); |
|
#endif |
|
printf |
|
(" -d0 no symbolic information, no run-time checks\n"); |
|
printf(" -d1 [default] run-time checks, no symbolic\ |
|
information\n"); |
|
printf |
|
(" -d2 full debug information and dynamic checking\n"); |
|
printf(" -d3 full debug information, dynamic checking,\ |
|
no optimization\n"); |
|
#endif |
|
printf(" -i <name> path for include files\n"); |
|
#if 0 |
|
printf(" -l create list file (preprocess only)\n"); |
|
#endif |
|
printf(" -o <name> set base name of output file\n"); |
|
#if 0 |
|
printf |
|
(" -P[+/-] strings are \"packed\" by default (default=%c)\n", |
|
sc_packstr ? '+' : '-'); |
|
printf(" -p<name> set name of \"prefix\" file\n"); |
|
if (!waitkey()) |
|
longjmp(errbuf, 3); |
|
#endif |
|
printf |
|
(" -S <num> stack/heap size in cells (default=%d, min=65)\n", |
|
(int)sc_stksize); |
|
#if 0 |
|
printf(" -s<num> skip lines from the input file\n"); |
|
printf |
|
(" -t<num> TAB indent size (in character positions)\n"); |
|
printf(" -\\ use '\\' for escape characters\n"); |
|
printf(" -^ use '^' for escape characters\n"); |
|
printf(" -;[+/-] require a semicolon to end each statement\ |
|
(default=%c)\n", sc_needsemicolon ? '+' : '-'); |
|
|
|
printf |
|
(" sym=val define constant \"sym\" with value \"val\"\n"); |
|
printf(" sym= define constant \"sym\" with value 0\n"); |
|
#endif |
|
longjmp(errbuf, 3); /* user abort */ |
|
} |
|
|
|
static void |
|
setconstants(void) |
|
{ |
|
int debug; |
|
|
|
assert(sc_status == statIDLE); |
|
append_constval(&tagname_tab, "_", 0, 0); /* "untagged" */ |
|
append_constval(&tagname_tab, "bool", 1, 0); |
|
|
|
add_constant("true", 1, sGLOBAL, 1); /* boolean flags */ |
|
add_constant("false", 0, sGLOBAL, 1); |
|
add_constant("EOS", 0, sGLOBAL, 0); /* End Of String, or '\0' */ |
|
add_constant("cellbits", 32, sGLOBAL, 0); |
|
add_constant("cellmax", INT_MAX, sGLOBAL, 0); |
|
add_constant("cellmin", INT_MIN, sGLOBAL, 0); |
|
add_constant("charbits", charbits, sGLOBAL, 0); |
|
add_constant("charmin", 0, sGLOBAL, 0); |
|
add_constant("charmax", (charbits == 16) ? 0xffff : 0xff, sGLOBAL, 0); |
|
|
|
add_constant("__Small", VERSION_INT, sGLOBAL, 0); |
|
|
|
debug = 0; |
|
if ((sc_debug & (sCHKBOUNDS | sSYMBOLIC)) == (sCHKBOUNDS | sSYMBOLIC)) |
|
debug = 2; |
|
else if ((sc_debug & sCHKBOUNDS) == sCHKBOUNDS) |
|
debug = 1; |
|
add_constant("debug", debug, sGLOBAL, 0); |
|
} |
|
|
|
/* parse - process all input text |
|
* |
|
* At this level, only static declarations and function definitions |
|
* are legal. |
|
*/ |
|
static void |
|
parse(void) |
|
{ |
|
int tok, tag, fconst, fstock, fstatic; |
|
cell val; |
|
char *str; |
|
|
|
while (freading) |
|
{ |
|
/* first try whether a declaration possibly is native or public */ |
|
tok = lex(&val, &str); /* read in (new) token */ |
|
switch (tok) |
|
{ |
|
case 0: |
|
/* ignore zero's */ |
|
break; |
|
case tNEW: |
|
fconst = matchtoken(tCONST); |
|
declglb(NULL, 0, FALSE, FALSE, FALSE, fconst); |
|
break; |
|
case tSTATIC: |
|
/* This can be a static function or a static global variable; |
|
* we know which of the two as soon as we have parsed up to the |
|
* point where an opening parenthesis of a function would be |
|
* expected. To back out after deciding it was a declaration of |
|
* a static variable after all, we have to store the symbol name |
|
* and tag. |
|
*/ |
|
fstock = matchtoken(tSTOCK); |
|
fconst = matchtoken(tCONST); |
|
tag = sc_addtag(NULL); |
|
tok = lex(&val, &str); |
|
if (tok == tNATIVE || tok == tPUBLIC) |
|
{ |
|
error(42); /* invalid combination of class specifiers */ |
|
break; |
|
} /* if */ |
|
declfuncvar(tok, str, tag, FALSE, TRUE, fstock, fconst); |
|
break; |
|
case tCONST: |
|
decl_const(sGLOBAL); |
|
break; |
|
case tENUM: |
|
decl_enum(sGLOBAL); |
|
break; |
|
case tPUBLIC: |
|
/* This can be a public function or a public variable; |
|
* see the comment above (for static functions/variables) |
|
* for details. |
|
*/ |
|
fconst = matchtoken(tCONST); |
|
tag = sc_addtag(NULL); |
|
tok = lex(&val, &str); |
|
if (tok == tNATIVE || tok == tSTOCK || tok == tSTATIC) |
|
{ |
|
error(42); /* invalid combination of class specifiers */ |
|
break; |
|
} /* if */ |
|
declfuncvar(tok, str, tag, TRUE, FALSE, FALSE, fconst); |
|
break; |
|
case tSTOCK: |
|
/* This can be a stock function or a stock *global) variable; |
|
* see the comment above (for static functions/variables) for |
|
* details. |
|
*/ |
|
fstatic = matchtoken(tSTATIC); |
|
fconst = matchtoken(tCONST); |
|
tag = sc_addtag(NULL); |
|
tok = lex(&val, &str); |
|
if (tok == tNATIVE || tok == tPUBLIC) |
|
{ |
|
error(42); /* invalid combination of class specifiers */ |
|
break; |
|
} /* if */ |
|
declfuncvar(tok, str, tag, FALSE, fstatic, TRUE, fconst); |
|
break; |
|
case tLABEL: |
|
case tSYMBOL: |
|
case tOPERATOR: |
|
lexpush(); |
|
if (!newfunc(NULL, -1, FALSE, FALSE, FALSE)) |
|
{ |
|
error(10); /* illegal function or declaration */ |
|
lexclr(TRUE); /* drop the rest of the line */ |
|
} /* if */ |
|
break; |
|
case tNATIVE: |
|
funcstub(TRUE); /* create a dummy function */ |
|
break; |
|
case tFORWARD: |
|
funcstub(FALSE); |
|
break; |
|
case '}': |
|
error(54); /* unmatched closing brace */ |
|
break; |
|
case '{': |
|
error(55); /* start of function body without function header */ |
|
break; |
|
default: |
|
if (freading) |
|
{ |
|
error(10); /* illegal function or declaration */ |
|
lexclr(TRUE); /* drop the rest of the line */ |
|
} /* if */ |
|
} /* switch */ |
|
} /* while */ |
|
} |
|
|
|
/* dumplits |
|
* |
|
* Dump the literal pool (strings etc.) |
|
* |
|
* Global references: litidx (referred to only) |
|
*/ |
|
static void |
|
dumplits(void) |
|
{ |
|
int j, k; |
|
|
|
k = 0; |
|
while (k < litidx) |
|
{ |
|
/* should be in the data segment */ |
|
assert(curseg == 2); |
|
defstorage(); |
|
j = 16; /* 16 values per line */ |
|
while (j && k < litidx) |
|
{ |
|
outval(litq[k], FALSE); |
|
stgwrite(" "); |
|
k++; |
|
j--; |
|
if (j == 0 || k >= litidx) |
|
stgwrite("\n"); /* force a newline after 10 dumps */ |
|
/* Note: stgwrite() buffers a line until it is complete. It recognizes |
|
* the end of line as a sequence of "\n\0", so something like "\n\t" |
|
* so should not be passed to stgwrite(). |
|
*/ |
|
} /* while */ |
|
} /* while */ |
|
} |
|
|
|
/* dumpzero |
|
* |
|
* Dump zero's for default initial values |
|
*/ |
|
static void |
|
dumpzero(int count) |
|
{ |
|
int i; |
|
|
|
if (count <= 0) |
|
return; |
|
assert(curseg == 2); |
|
defstorage(); |
|
i = 0; |
|
while (count-- > 0) |
|
{ |
|
outval(0, FALSE); |
|
i = (i + 1) % 16; |
|
stgwrite((i == 0 || count == 0) ? "\n" : " "); |
|
if (i == 0 && count > 0) |
|
defstorage(); |
|
} /* while */ |
|
} |
|
|
|
static void |
|
aligndata(int numbytes) |
|
{ |
|
if ((((glb_declared + litidx) * sizeof(cell)) % numbytes) != 0) |
|
{ |
|
while ((((glb_declared + litidx) * sizeof(cell)) % numbytes) != 0) |
|
stowlit(0); |
|
} /* if */ |
|
|
|
} |
|
|
|
static void |
|
declfuncvar(int tok, char *symname, int tag, int fpublic, int fstatic, |
|
int fstock, int fconst) |
|
{ |
|
char name[sNAMEMAX + 1]; |
|
|
|
if (tok != tSYMBOL && tok != tOPERATOR) |
|
{ |
|
if (freading) |
|
error(20, symname); /* invalid symbol name */ |
|
return; |
|
} /* if */ |
|
if (tok == tOPERATOR) |
|
{ |
|
lexpush(); |
|
if (!newfunc(NULL, tag, fpublic, fstatic, fstock)) |
|
error(10); /* illegal function or declaration */ |
|
} |
|
else |
|
{ |
|
assert(strlen(symname) <= sNAMEMAX); |
|
strcpy(name, symname); |
|
if (fconst || !newfunc(name, tag, fpublic, fstatic, fstock)) |
|
declglb(name, tag, fpublic, fstatic, fstock, fconst); |
|
/* if not a static function, try a static variable */ |
|
} /* if */ |
|
} |
|
|
|
/* declglb - declare global symbols |
|
* |
|
* Declare a static (global) variable. Global variables are stored in |
|
* the DATA segment. |
|
* |
|
* global references: glb_declared (altered) |
|
*/ |
|
static void |
|
declglb(char *firstname, int firsttag, int fpublic, int fstatic, |
|
int stock, int fconst) |
|
{ |
|
int ident, tag, ispublic; |
|
int idxtag[sDIMEN_MAX]; |
|
char name[sNAMEMAX + 1]; |
|
cell val, size, cidx; |
|
char *str; |
|
int dim[sDIMEN_MAX]; |
|
int numdim, level; |
|
int filenum; |
|
symbol *sym; |
|
|
|
#if !defined NDEBUG |
|
cell glbdecl = 0; |
|
#endif |
|
|
|
filenum = fcurrent; /* save file number at the start of the |
|
* declaration */ |
|
do |
|
{ |
|
size = 1; /* single size (no array) */ |
|
numdim = 0; /* no dimensions */ |
|
ident = iVARIABLE; |
|
if (firstname) |
|
{ |
|
assert(strlen(firstname) <= sNAMEMAX); |
|
strcpy(name, firstname); /* save symbol name */ |
|
tag = firsttag; |
|
firstname = NULL; |
|
} |
|
else |
|
{ |
|
tag = sc_addtag(NULL); |
|
if (lex(&val, &str) != tSYMBOL) /* read in (new) token */ |
|
error(20, str); /* invalid symbol name */ |
|
assert(strlen(str) <= sNAMEMAX); |
|
strcpy(name, str); /* save symbol name */ |
|
} /* if */ |
|
sym = findglb(name); |
|
if (!sym) |
|
sym = findconst(name); |
|
if (sym && (sym->usage & uDEFINE) != 0) |
|
error(21, name); /* symbol already defined */ |
|
ispublic = fpublic; |
|
if (name[0] == PUBLIC_CHAR) |
|
{ |
|
ispublic = TRUE; /* implicitly public variable */ |
|
if (stock || fstatic) |
|
error(42); /* invalid combination of class specifiers */ |
|
} /* if */ |
|
while (matchtoken('[')) |
|
{ |
|
ident = iARRAY; |
|
if (numdim == sDIMEN_MAX) |
|
{ |
|
error(53); /* exceeding maximum number of dimensions */ |
|
return; |
|
} /* if */ |
|
if (numdim > 0 && dim[numdim - 1] == 0) |
|
error(52); /* only last dimension may be variable length */ |
|
size = needsub(&idxtag[numdim]); /* get size; size==0 for |
|
* "var[]" */ |
|
#if INT_MAX < LONG_MAX |
|
if (size > INT_MAX) |
|
error(105); /* overflow, exceeding capacity */ |
|
#endif |
|
if (ispublic) |
|
error(56, name); /* arrays cannot be public */ |
|
dim[numdim++] = (int)size; |
|
} /* while */ |
|
/* if this variable is never used (which can be detected only in |
|
* the second stage), shut off code generation; make an exception |
|
* for public variables |
|
*/ |
|
cidx = 0; /* only to avoid a compiler warning */ |
|
if (sc_status == statWRITE && sym |
|
&& (sym->usage & (uREAD | uWRITTEN | uPUBLIC)) == 0) |
|
{ |
|
sc_status = statSKIP; |
|
cidx = code_idx; |
|
#if !defined NDEBUG |
|
glbdecl = glb_declared; |
|
#endif |
|
} /* if */ |
|
defsymbol(name, ident, sGLOBAL, sizeof(cell) * glb_declared, tag); |
|
begdseg(); /* real (initialized) data in data segment */ |
|
assert(litidx == 0); /* literal queue should be empty */ |
|
if (sc_alignnext) |
|
{ |
|
litidx = 0; |
|
aligndata(sc_dataalign); |
|
dumplits(); /* dump the literal queue */ |
|
sc_alignnext = FALSE; |
|
litidx = 0; /* global initial data is dumped, so restart at zero */ |
|
} /* if */ |
|
initials(ident, tag, &size, dim, numdim); /* stores values in |
|
* the literal queue */ |
|
if (numdim == 1) |
|
dim[0] = (int)size; |
|
dumplits(); /* dump the literal queue */ |
|
dumpzero((int)size - litidx); |
|
litidx = 0; |
|
if (!sym) |
|
{ /* define only if not yet defined */ |
|
sym = |
|
addvariable(name, sizeof(cell) * glb_declared, ident, sGLOBAL, |
|
tag, dim, numdim, idxtag); |
|
} |
|
else |
|
{ /* if declared but not yet defined, adjust the |
|
* variable's address */ |
|
sym->addr = sizeof(cell) * glb_declared; |
|
sym->usage |= uDEFINE; |
|
} /* if */ |
|
if (ispublic) |
|
sym->usage |= uPUBLIC; |
|
if (fconst) |
|
sym->usage |= uCONST; |
|
if (stock) |
|
sym->usage |= uSTOCK; |
|
if (fstatic) |
|
sym->fnumber = filenum; |
|
if (ident == iARRAY) |
|
for (level = 0; level < numdim; level++) |
|
symbolrange(level, dim[level]); |
|
if (sc_status == statSKIP) |
|
{ |
|
sc_status = statWRITE; |
|
code_idx = cidx; |
|
assert(glb_declared == glbdecl); |
|
} |
|
else |
|
{ |
|
glb_declared += (int)size; /* add total number of cells */ |
|
} /* if */ |
|
} |
|
while (matchtoken(',')); /* enddo *//* more? */ |
|
needtoken(tTERM); /* if not comma, must be semicolumn */ |
|
} |
|
|
|
/* declloc - declare local symbols |
|
* |
|
* Declare local (automatic) variables. Since these variables are |
|
* relative to the STACK, there is no switch to the DATA segment. |
|
* These variables cannot be initialized either. |
|
* |
|
* global references: declared (altered) |
|
* funcstatus (referred to only) |
|
*/ |
|
static int |
|
declloc(int fstatic) |
|
{ |
|
int ident, tag; |
|
int idxtag[sDIMEN_MAX]; |
|
char name[sNAMEMAX + 1]; |
|
symbol *sym; |
|
cell val, size; |
|
char *str; |
|
value lval = { NULL, 0, 0, 0, 0, NULL }; |
|
int cur_lit = 0; |
|
int dim[sDIMEN_MAX]; |
|
int numdim, level; |
|
int fconst; |
|
|
|
fconst = matchtoken(tCONST); |
|
do |
|
{ |
|
ident = iVARIABLE; |
|
size = 1; |
|
numdim = 0; /* no dimensions */ |
|
tag = sc_addtag(NULL); |
|
if (lex(&val, &str) != tSYMBOL) /* read in (new) token */ |
|
error(20, str); /* invalid symbol name */ |
|
assert(strlen(str) <= sNAMEMAX); |
|
strcpy(name, str); /* save symbol name */ |
|
if (name[0] == PUBLIC_CHAR) |
|
error(56, name); /* local variables cannot be public */ |
|
/* Note: block locals may be named identical to locals at higher |
|
* compound blocks (as with standard C); so we must check (and add) |
|
* the "nesting level" of local variables to verify the |
|
* multi-definition of symbols. |
|
*/ |
|
if ((sym = findloc(name)) && sym->compound == nestlevel) |
|
error(21, name); /* symbol already defined */ |
|
/* Although valid, a local variable whose name is equal to that |
|
* of a global variable or to that of a local variable at a lower |
|
* level might indicate a bug. |
|
*/ |
|
if (((sym = findloc(name)) && sym->compound != nestlevel) |
|
|| findglb(name)) |
|
error(219, name); /* variable shadows another symbol */ |
|
while (matchtoken('[')) |
|
{ |
|
ident = iARRAY; |
|
if (numdim == sDIMEN_MAX) |
|
{ |
|
error(53); /* exceeding maximum number of dimensions */ |
|
return ident; |
|
} /* if */ |
|
if (numdim > 0 && dim[numdim - 1] == 0) |
|
error(52); /* only last dimension may be variable length */ |
|
size = needsub(&idxtag[numdim]); /* get size; size==0 for "var[]" */ |
|
#if INT_MAX < LONG_MAX |
|
if (size > INT_MAX) |
|
error(105); /* overflow, exceeding capacity */ |
|
#endif |
|
dim[numdim++] = (int)size; |
|
} /* while */ |
|
if (ident == iARRAY || fstatic) |
|
{ |
|
if (sc_alignnext) |
|
{ |
|
aligndata(sc_dataalign); |
|
sc_alignnext = FALSE; |
|
} /* if */ |
|
cur_lit = litidx; /* save current index in the literal table */ |
|
initials(ident, tag, &size, dim, numdim); |
|
if (size == 0) |
|
return ident; /* error message already given */ |
|
if (numdim == 1) |
|
dim[0] = (int)size; |
|
} /* if */ |
|
/* reserve memory (on the stack) for the variable */ |
|
if (fstatic) |
|
{ |
|
/* write zeros for uninitialized fields */ |
|
while (litidx < cur_lit + size) |
|
stowlit(0); |
|
sym = |
|
addvariable(name, (cur_lit + glb_declared) * sizeof(cell), |
|
ident, sSTATIC, tag, dim, numdim, idxtag); |
|
defsymbol(name, ident, sSTATIC, |
|
(cur_lit + glb_declared) * sizeof(cell), tag); |
|
} |
|
else |
|
{ |
|
declared += (int)size; /* variables are put on stack, |
|
* adjust "declared" */ |
|
sym = |
|
addvariable(name, -declared * sizeof(cell), ident, sLOCAL, tag, |
|
dim, numdim, idxtag); |
|
defsymbol(name, ident, sLOCAL, -declared * sizeof(cell), tag); |
|
modstk(-(int)size * sizeof(cell)); |
|
} /* if */ |
|
/* now that we have reserved memory for the variable, we can |
|
* proceed to initialize it */ |
|
sym->compound = nestlevel; /* for multiple declaration/shadowing */ |
|
if (fconst) |
|
sym->usage |= uCONST; |
|
if (ident == iARRAY) |
|
for (level = 0; level < numdim; level++) |
|
symbolrange(level, dim[level]); |
|
if (!fstatic) |
|
{ /* static variables already initialized */ |
|
if (ident == iVARIABLE) |
|
{ |
|
/* simple variable, also supports initialization */ |
|
int ctag = tag; /* set to "tag" by default */ |
|
int explicit_init = FALSE; /* is the variable explicitly |
|
* initialized? */ |
|
if (matchtoken('=')) |
|
{ |
|
doexpr(FALSE, FALSE, FALSE, FALSE, &ctag, TRUE); |
|
explicit_init = TRUE; |
|
} |
|
else |
|
{ |
|
const1(0); /* uninitialized variable, set to zero */ |
|
} /* if */ |
|
/* now try to save the value (still in PRI) in the variable */ |
|
lval.sym = sym; |
|
lval.ident = iVARIABLE; |
|
lval.constval = 0; |
|
lval.tag = tag; |
|
check_userop(NULL, ctag, lval.tag, 2, NULL, &ctag); |
|
store(&lval); |
|
endexpr(TRUE); /* full expression ends after the store */ |
|
if (!matchtag(tag, ctag, TRUE)) |
|
error(213); /* tag mismatch */ |
|
/* if the variable was not explicitly initialized, reset the |
|
* "uWRITTEN" flag that store() set */ |
|
if (!explicit_init) |
|
sym->usage &= ~uWRITTEN; |
|
} |
|
else |
|
{ |
|
/* an array */ |
|
if (litidx - cur_lit < size) |
|
fillarray(sym, size * sizeof(cell), 0); |
|
if (cur_lit < litidx) |
|
{ |
|
/* check whether the complete array is set to a single value; |
|
* if it is, more compact code can be generated */ |
|
cell first = litq[cur_lit]; |
|
int i; |
|
|
|
for (i = cur_lit; i < litidx && litq[i] == first; i++) |
|
/* nothing */ ; |
|
if (i == litidx) |
|
{ |
|
/* all values are the same */ |
|
fillarray(sym, (litidx - cur_lit) * sizeof(cell), |
|
first); |
|
litidx = cur_lit; /* reset literal table */ |
|
} |
|
else |
|
{ |
|
/* copy the literals to the array */ |
|
const1((cur_lit + glb_declared) * sizeof(cell)); |
|
copyarray(sym, (litidx - cur_lit) * sizeof(cell)); |
|
} /* if */ |
|
} /* if */ |
|
} /* if */ |
|
} /* if */ |
|
} |
|
while (matchtoken(',')); /* enddo *//* more? */ |
|
needtoken(tTERM); /* if not comma, must be semicolumn */ |
|
return ident; |
|
} |
|
|
|
static cell |
|
calc_arraysize(int dim[], int numdim, int cur) |
|
{ |
|
if (cur == numdim) |
|
return 0; |
|
return dim[cur] + (dim[cur] * calc_arraysize(dim, numdim, cur + 1)); |
|
} |
|
|
|
/* initials |
|
* |
|
* Initialize global objects and local arrays. |
|
* size==array cells (count), if 0 on input, the routine counts |
|
* the number of elements |
|
* tag==required tagname id (not the returned tag) |
|
* |
|
* Global references: litidx (altered) |
|
*/ |
|
static void |
|
initials(int ident, int tag, cell * size, int dim[], int numdim) |
|
{ |
|
int ctag; |
|
int curlit = litidx; |
|
int d; |
|
|
|
if (!matchtoken('=')) |
|
{ |
|
if (ident == iARRAY && dim[numdim - 1] == 0) |
|
{ |
|
/* declared as "myvar[];" which is senseless (note: this *does* make |
|
* sense in the case of a iREFARRAY, which is a function parameter) |
|
*/ |
|
error(9); /* array has zero length -> invalid size */ |
|
} /* if */ |
|
if (numdim > 1) |
|
{ |
|
/* initialize the indirection tables */ |
|
#if sDIMEN_MAX>2 |
|
#error Array algorithms for more than 2 dimensions are not implemented |
|
#endif |
|
assert(numdim == 2); |
|
*size = calc_arraysize(dim, numdim, 0); |
|
for (d = 0; d < dim[0]; d++) |
|
stowlit((dim[0] + d * (dim[1] - 1)) * sizeof(cell)); |
|
} /* if */ |
|
return; |
|
} /* if */ |
|
|
|
if (ident == iVARIABLE) |
|
{ |
|
assert(*size == 1); |
|
init(ident, &ctag); |
|
if (!matchtag(tag, ctag, TRUE)) |
|
error(213); /* tag mismatch */ |
|
} |
|
else |
|
{ |
|
assert(numdim > 0); |
|
if (numdim == 1) |
|
{ |
|
*size = initvector(ident, tag, dim[0], FALSE); |
|
} |
|
else |
|
{ |
|
cell offs, dsize; |
|
|
|
/* The simple algorithm below only works for arrays with one or |
|
* two dimensions. This should be some recursive algorithm. |
|
*/ |
|
if (dim[numdim - 1] != 0) |
|
/* set size to (known) full size */ |
|
*size = calc_arraysize(dim, numdim, 0); |
|
/* dump indirection tables */ |
|
for (d = 0; d < dim[0]; d++) |
|
stowlit(0); |
|
/* now dump individual vectors */ |
|
needtoken('{'); |
|
offs = dim[0]; |
|
for (d = 0; d < dim[0]; d++) |
|
{ |
|
litq[curlit + d] = offs * sizeof(cell); |
|
dsize = initvector(ident, tag, dim[1], TRUE); |
|
offs += dsize - 1; |
|
if (d + 1 < dim[0]) |
|
needtoken(','); |
|
if (matchtoken('{') || matchtoken(tSTRING)) |
|
/* expect a '{' or a string */ |
|
lexpush(); |
|
else |
|
break; |
|
} /* for */ |
|
matchtoken(','); |
|
needtoken('}'); |
|
} /* if */ |
|
} /* if */ |
|
|
|
if (*size == 0) |
|
*size = litidx - curlit; /* number of elements defined */ |
|
} |
|
|
|
/* initvector |
|
* Initialize a single dimensional array |
|
*/ |
|
static cell |
|
initvector(int ident, int tag, cell size, int fillzero) |
|
{ |
|
cell prev1 = 0, prev2 = 0; |
|
int ctag; |
|
int ellips = FALSE; |
|
int curlit = litidx; |
|
|
|
assert(ident == iARRAY || ident == iREFARRAY); |
|
if (matchtoken('{')) |
|
{ |
|
do |
|
{ |
|
if (matchtoken('}')) |
|
{ /* to allow for trailing ',' after the initialization */ |
|
lexpush(); |
|
break; |
|
} /* if */ |
|
if ((ellips = matchtoken(tELLIPS)) != 0) |
|
break; |
|
prev2 = prev1; |
|
prev1 = init(ident, &ctag); |
|
if (!matchtag(tag, ctag, TRUE)) |
|
error(213); /* tag mismatch */ |
|
} |
|
while (matchtoken(',')); /* do */ |
|
needtoken('}'); |
|
} |
|
else |
|
{ |
|
init(ident, &ctag); |
|
if (!matchtag(tag, ctag, TRUE)) |
|
error(213); /* tagname mismatch */ |
|
} /* if */ |
|
/* fill up the literal queue with a series */ |
|
if (ellips) |
|
{ |
|
cell step = |
|
((litidx - curlit) == 1) ? (cell) 0 : prev1 - prev2; |
|
if (size == 0 || (litidx - curlit) == 0) |
|
error(41); /* invalid ellipsis, array size unknown */ |
|
else if ((litidx - curlit) == (int)size) |
|
error(18); /* initialisation data exceeds declared size */ |
|
while ((litidx - curlit) < (int)size) |
|
{ |
|
prev1 += step; |
|
stowlit(prev1); |
|
} /* while */ |
|
} /* if */ |
|
if (fillzero && size > 0) |
|
{ |
|
while ((litidx - curlit) < (int)size) |
|
stowlit(0); |
|
} /* if */ |
|
if (size == 0) |
|
{ |
|
size = litidx - curlit; /* number of elements defined */ |
|
} |
|
else if (litidx - curlit > (int)size) |
|
{ /* e.g. "myvar[3]={1,2,3,4};" */ |
|
error(18); /* initialisation data exceeds declared size */ |
|
litidx = (int)size + curlit; /* avoid overflow in memory moves */ |
|
} /* if */ |
|
return size; |
|
} |
|
|
|
/* init |
|
* |
|
* Evaluate one initializer. |
|
*/ |
|
static cell |
|
init(int ident, int *tag) |
|
{ |
|
cell i = 0; |
|
|
|
if (matchtoken(tSTRING)) |
|
{ |
|
/* lex() automatically stores strings in the literal table (and |
|
* increases "litidx") */ |
|
if (ident == iVARIABLE) |
|
{ |
|
error(6); /* must be assigned to an array */ |
|
litidx = 1; /* reset literal queue */ |
|
} /* if */ |
|
*tag = 0; |
|
} |
|
else if (constexpr(&i, tag)) |
|
{ |
|
stowlit(i); /* store expression result in literal table */ |
|
} /* if */ |
|
return i; |
|
} |
|
|
|
/* needsub |
|
* |
|
* Get required array size |
|
*/ |
|
static cell |
|
needsub(int *tag) |
|
{ |
|
cell val; |
|
|
|
*tag = 0; |
|
if (matchtoken(']')) /* we've already seen "[" */ |
|
return 0; /* null size (like "char msg[]") */ |
|
constexpr(&val, tag); /* get value (must be constant expression) */ |
|
if (val < 0) |
|
{ |
|
error(9); /* negative array size is invalid; assumed zero */ |
|
val = 0; |
|
} /* if */ |
|
needtoken(']'); |
|
return val; /* return array size */ |
|
} |
|
|
|
/* decl_const - declare a single constant |
|
* |
|
*/ |
|
static void |
|
decl_const(int vclass) |
|
{ |
|
char constname[sNAMEMAX + 1]; |
|
cell val; |
|
char *str; |
|
int tag, exprtag; |
|
int symbolline; |
|
|
|
tag = sc_addtag(NULL); |
|
if (lex(&val, &str) != tSYMBOL) /* read in (new) token */ |
|
error(20, str); /* invalid symbol name */ |
|
symbolline = fline; /* save line where symbol was found */ |
|
strcpy(constname, str); /* save symbol name */ |
|
needtoken('='); |
|
constexpr(&val, &exprtag); /* get value */ |
|
needtoken(tTERM); |
|
/* add_constant() checks for duplicate definitions */ |
|
if (!matchtag(tag, exprtag, FALSE)) |
|
{ |
|
/* temporarily reset the line number to where the symbol was |
|
* defined */ |
|
int orgfline = fline; |
|
|
|
fline = symbolline; |
|
error(213); /* tagname mismatch */ |
|
fline = orgfline; |
|
} /* if */ |
|
add_constant(constname, val, vclass, tag); |
|
} |
|
|
|
/* decl_enum - declare enumerated constants |
|
* |
|
*/ |
|
static void |
|
decl_enum(int vclass) |
|
{ |
|
char enumname[sNAMEMAX + 1], constname[sNAMEMAX + 1]; |
|
cell val, value, size; |
|
char *str; |
|
int tok, tag, explicittag; |
|
cell increment, multiplier; |
|
|
|
/* get an explicit tag, if any (we need to remember whether an |
|
* explicit tag was passed, even if that explicit tag was "_:", so we |
|
* cannot call sc_addtag() here |
|
*/ |
|
if (lex(&val, &str) == tLABEL) |
|
{ |
|
tag = sc_addtag(str); |
|
explicittag = TRUE; |
|
} |
|
else |
|
{ |
|
lexpush(); |
|
tag = 0; |
|
explicittag = FALSE; |
|
} /* if */ |
|
|
|
/* get optional enum name (also serves as a tag if no explicit |
|
* tag was set) */ |
|
if (lex(&val, &str) == tSYMBOL) |
|
{ /* read in (new) token */ |
|
strcpy(enumname, str); /* save enum name (last constant) */ |
|
if (!explicittag) |
|
tag = sc_addtag(enumname); |
|
} |
|
else |
|
{ |
|
lexpush(); /* analyze again */ |
|
enumname[0] = '\0'; |
|
} /* if */ |
|
|
|
/* get increment and multiplier */ |
|
increment = 1; |
|
multiplier = 1; |
|
if (matchtoken('(')) |
|
{ |
|
if (matchtoken(taADD)) |
|
{ |
|
constexpr(&increment, NULL); |
|
} |
|
else if (matchtoken(taMULT)) |
|
{ |
|
constexpr(&multiplier, NULL); |
|
} |
|
else if (matchtoken(taSHL)) |
|
{ |
|
constexpr(&val, NULL); |
|
while (val-- > 0) |
|
multiplier *= 2; |
|
} /* if */ |
|
needtoken(')'); |
|
} /* if */ |
|
|
|
needtoken('{'); |
|
/* go through all constants */ |
|
value = 0; /* default starting value */ |
|
do |
|
{ |
|
if (matchtoken('}')) |
|
{ /* quick exit if '}' follows ',' */ |
|
lexpush(); |
|
break; |
|
} /* if */ |
|
tok = lex(&val, &str); /* read in (new) token */ |
|
if (tok != tSYMBOL && tok != tLABEL) |
|
error(20, str); /* invalid symbol name */ |
|
strcpy(constname, str); /* save symbol name */ |
|
size = increment; /* default increment of 'val' */ |
|
if (tok == tLABEL || matchtoken(':')) |
|
constexpr(&size, NULL); /* get size */ |
|
if (matchtoken('=')) |
|
constexpr(&value, NULL); /* get value */ |
|
/* add_constant() checks whether a variable (global or local) or |
|
* a constant with the same name already exists */ |
|
add_constant(constname, value, vclass, tag); |
|
if (multiplier == 1) |
|
value += size; |
|
else |
|
value *= size * multiplier; |
|
} |
|
while (matchtoken(',')); |
|
needtoken('}'); /* terminates the constant list */ |
|
matchtoken(';'); /* eat an optional ; */ |
|
|
|
/* set the enum name to the last value plus one */ |
|
if (enumname[0] != '\0') |
|
add_constant(enumname, value, vclass, tag); |
|
} |
|
|
|
/* |
|
* Finds a function in the global symbol table or creates a new entry. |
|
* It does some basic processing and error checking. |
|
*/ |
|
symbol * |
|
fetchfunc(char *name, int tag) |
|
{ |
|
symbol *sym; |
|
cell offset; |
|
|
|
offset = code_idx; |
|
if ((sc_debug & sSYMBOLIC) != 0) |
|
{ |
|
offset += opcodes(1) + opargs(3) + nameincells(name); |
|
/* ^^^ The address for the symbol is the code address. But the |
|
* "symbol" instruction itself generates code. Therefore the |
|
* offset is pre-adjusted to the value it will have after the |
|
* symbol instruction. |
|
*/ |
|
} /* if */ |
|
if ((sym = findglb(name))) |
|
{ /* already in symbol table? */ |
|
if (sym->ident != iFUNCTN) |
|
{ |
|
error(21, name); /* yes, but not as a function */ |
|
return NULL; /* make sure the old symbol is not damaged */ |
|
} |
|
else if ((sym->usage & uDEFINE) != 0) |
|
{ |
|
error(21, name); /* yes, and it's already defined */ |
|
} |
|
else if ((sym->usage & uNATIVE) != 0) |
|
{ |
|
error(21, name); /* yes, and it is an native */ |
|
} /* if */ |
|
assert(sym->vclass == sGLOBAL); |
|
if ((sym->usage & uDEFINE) == 0) |
|
{ |
|
/* as long as the function stays undefined, update the address |
|
* and the tag */ |
|
sym->addr = offset; |
|
sym->tag = tag; |
|
} /* if */ |
|
} |
|
else |
|
{ |
|
/* don't set the "uDEFINE" flag; it may be a prototype */ |
|
sym = addsym(name, offset, iFUNCTN, sGLOBAL, tag, 0); |
|
/* assume no arguments */ |
|
sym->dim.arglist = (arginfo *) malloc(1 * sizeof(arginfo)); |
|
sym->dim.arglist[0].ident = 0; |
|
/* set library ID to NULL (only for native functions) */ |
|
sym->x.lib = NULL; |
|
} /* if */ |
|
return sym; |
|
} |
|
|
|
/* This routine adds symbolic information for each argument. |
|
*/ |
|
static void |
|
define_args(void) |
|
{ |
|
symbol *sym; |
|
|
|
/* At this point, no local variables have been declared. All |
|
* local symbols are function arguments. |
|
*/ |
|
sym = loctab.next; |
|
while (sym) |
|
{ |
|
assert(sym->ident != iLABEL); |
|
assert(sym->vclass == sLOCAL); |
|
defsymbol(sym->name, sym->ident, sLOCAL, sym->addr, sym->tag); |
|
if (sym->ident == iREFARRAY) |
|
{ |
|
symbol *sub = sym; |
|
|
|
while (sub) |
|
{ |
|
symbolrange(sub->dim.array.level, sub->dim.array.length); |
|
sub = finddepend(sub); |
|
} /* while */ |
|
} /* if */ |
|
sym = sym->next; |
|
} /* while */ |
|
} |
|
|
|
static int |
|
operatorname(char *name) |
|
{ |
|
int opertok; |
|
char *str; |
|
cell val; |
|
|
|
assert(name != NULL); |
|
|
|
/* check the operator */ |
|
opertok = lex(&val, &str); |
|
switch (opertok) |
|
{ |
|
case '+': |
|
case '-': |
|
case '*': |
|
case '/': |
|
case '%': |
|
case '>': |
|
case '<': |
|
case '!': |
|
case '~': |
|
case '=': |
|
name[0] = (char)opertok; |
|
name[1] = '\0'; |
|
break; |
|
case tINC: |
|
strcpy(name, "++"); |
|
break; |
|
case tDEC: |
|
strcpy(name, "--"); |
|
break; |
|
case tlEQ: |
|
strcpy(name, "=="); |
|
break; |
|
case tlNE: |
|
strcpy(name, "!="); |
|
break; |
|
case tlLE: |
|
strcpy(name, "<="); |
|
break; |
|
case tlGE: |
|
strcpy(name, ">="); |
|
break; |
|
default: |
|
name[0] = '\0'; |
|
error(61); /* operator cannot be redefined |
|
* (or bad operator name) */ |
|
return 0; |
|
} /* switch */ |
|
|
|
return opertok; |
|
} |
|
|
|
static int |
|
operatoradjust(int opertok, symbol * sym, char *opername, int resulttag) |
|
{ |
|
int tags[2] = { 0, 0 }; |
|
int count = 0; |
|
arginfo *arg; |
|
char tmpname[sNAMEMAX + 1]; |
|
symbol *oldsym; |
|
|
|
if (opertok == 0) |
|
return TRUE; |
|
|
|
/* count arguments and save (first two) tags */ |
|
while (arg = &sym->dim.arglist[count], arg->ident != 0) |
|
{ |
|
if (count < 2) |
|
{ |
|
if (arg->numtags > 1) |
|
error(65, count + 1); /* function argument may only have |
|
* a single tag */ |
|
else if (arg->numtags == 1) |
|
tags[count] = arg->tags[0]; |
|
} /* if */ |
|
if (opertok == '~' && count == 0) |
|
{ |
|
if (arg->ident != iREFARRAY) |
|
error(73, arg->name); /* must be an array argument */ |
|
} |
|
else |
|
{ |
|
if (arg->ident != iVARIABLE) |
|
error(66, arg->name); /* must be non-reference argument */ |
|
} /* if */ |
|
if (arg->hasdefault) |
|
error(59, arg->name); /* arguments of an operator may not |
|
* have a default value */ |
|
count++; |
|
} /* while */ |
|
|
|
/* for '!', '++' and '--', count must be 1 |
|
* for '-', count may be 1 or 2 |
|
* for '=', count must be 1, and the resulttag is also important |
|
* for all other (binary) operators and the special '~' |
|
* operator, count must be 2 |
|
*/ |
|
switch (opertok) |
|
{ |
|
case '!': |
|
case '=': |
|
case tINC: |
|
case tDEC: |
|
if (count != 1) |
|
error(62); /* number or placement of the operands does |
|
* not fit the operator */ |
|
break; |
|
case '-': |
|
if (count != 1 && count != 2) |
|
error(62); /* number or placement of the operands does |
|
* not fit the operator */ |
|
break; |
|
default: |
|
if (count != 2) |
|
error(62); /* number or placement of the operands does |
|
* not fit the operator */ |
|
} /* switch */ |
|
|
|
if (tags[0] == 0 |
|
&& ((opertok != '=' && tags[1] == 0) || (opertok == '=' && resulttag == 0))) |
|
error(64); /* cannot change predefined operators */ |
|
|
|
/* change the operator name */ |
|
assert(opername[0] != '\0'); |
|
operator_symname(tmpname, opername, tags[0], tags[1], count, resulttag); |
|
if ((oldsym = findglb(tmpname))) |
|
{ |
|
int i; |
|
|
|
if ((oldsym->usage & uDEFINE) != 0) |
|
{ |
|
char errname[2 * sNAMEMAX + 16]; |
|
|
|
funcdisplayname(errname, tmpname); |
|
error(21, errname); /* symbol already defined */ |
|
} /* if */ |
|
sym->usage |= oldsym->usage; /* copy flags from the previous |
|
* definition */ |
|
for (i = 0; i < oldsym->numrefers; i++) |
|
if (oldsym->refer[i]) |
|
refer_symbol(sym, oldsym->refer[i]); |
|
delete_symbol(&glbtab, oldsym); |
|
} /* if */ |
|
if ((sc_debug & sSYMBOLIC) != 0) |
|
sym->addr += nameincells(tmpname) - nameincells(sym->name); |
|
strcpy(sym->name, tmpname); |
|
sym->hash = namehash(sym->name); /* calculate new hash */ |
|
|
|
/* operators should return a value, except the '~' operator */ |
|
if (opertok != '~') |
|
sym->usage |= uRETVALUE; |
|
|
|
return TRUE; |
|
} |
|
|
|
static int |
|
check_operatortag(int opertok, int resulttag, char *opername) |
|
{ |
|
assert(opername != NULL && opername[0] != '\0'); |
|
switch (opertok) |
|
{ |
|
case '!': |
|
case '<': |
|
case '>': |
|
case tlEQ: |
|
case tlNE: |
|
case tlLE: |
|
case tlGE: |
|
if (resulttag != sc_addtag("bool")) |
|
{ |
|
error(63, opername, "bool:"); /* operator X requires |
|
* a "bool:" result tag */ |
|
return FALSE; |
|
} /* if */ |
|
break; |
|
case '~': |
|
if (resulttag != 0) |
|
{ |
|
error(63, opername, "_:"); /* operator "~" requires |
|
* a "_:" result tag */ |
|
return FALSE; |
|
} /* if */ |
|
break; |
|
} /* switch */ |
|
return TRUE; |
|
} |
|
|
|
static char * |
|
tag2str(char *dest, int tag) |
|
{ |
|
tag &= TAGMASK; |
|
assert(tag >= 0); |
|
sprintf(dest, "0%x", tag); |
|
return sc_isdigit(dest[1]) ? &dest[1] : dest; |
|
} |
|
|
|
char * |
|
operator_symname(char *symname, char *opername, int tag1, int tag2, |
|
int numtags, int resulttag) |
|
{ |
|
char tagstr1[10], tagstr2[10]; |
|
int opertok; |
|
|
|
assert(numtags >= 1 && numtags <= 2); |
|
opertok = (opername[1] == '\0') ? opername[0] : 0; |
|
if (opertok == '=') |
|
sprintf(symname, "%s%s%s", tag2str(tagstr1, resulttag), opername, |
|
tag2str(tagstr2, tag1)); |
|
else if (numtags == 1 || opertok == '~') |
|
sprintf(symname, "%s%s", opername, tag2str(tagstr1, tag1)); |
|
else |
|
sprintf(symname, "%s%s%s", tag2str(tagstr1, tag1), opername, |
|
tag2str(tagstr2, tag2)); |
|
return symname; |
|
} |
|
|
|
static int |
|
parse_funcname(char *fname, int *tag1, int *tag2, char *opname) |
|
{ |
|
char *ptr, *name; |
|
int unary; |
|
|
|
/* tags are only positive, so if the function name starts with a '-', |
|
* the operator is an unary '-' or '--' operator. |
|
*/ |
|
if (*fname == '-') |
|
{ |
|
*tag1 = 0; |
|
unary = TRUE; |
|
ptr = fname; |
|
} |
|
else |
|
{ |
|
*tag1 = (int)strtol(fname, &ptr, 16); |
|
unary = ptr == fname; /* unary operator if it doesn't start |
|
* with a tag name */ |
|
} /* if */ |
|
assert(!unary || *tag1 == 0); |
|
assert(*ptr != '\0'); |
|
for (name = opname; !sc_isdigit(*ptr);) |
|
*name++ = *ptr++; |
|
*name = '\0'; |
|
*tag2 = (int)strtol(ptr, NULL, 16); |
|
return unary; |
|
} |
|
|
|
char * |
|
funcdisplayname(char *dest, char *funcname) |
|
{ |
|
int tags[2]; |
|
char opname[10]; |
|
constvalue *tagsym[2]; |
|
int unary; |
|
|
|
if (sc_isalpha(*funcname) || *funcname == '_' || *funcname == PUBLIC_CHAR |
|
|| *funcname == '\0') |
|
{ |
|
if (dest != funcname) |
|
strcpy(dest, funcname); |
|
return dest; |
|
} /* if */ |
|
|
|
unary = parse_funcname(funcname, &tags[0], &tags[1], opname); |
|
tagsym[1] = find_constval_byval(&tagname_tab, tags[1]); |
|
assert(tagsym[1] != NULL); |
|
if (unary) |
|
{ |
|
sprintf(dest, "operator%s(%s:)", opname, tagsym[1]->name); |
|
} |
|
else |
|
{ |
|
tagsym[0] = find_constval_byval(&tagname_tab, tags[0]); |
|
/* special case: the assignment operator has the return value |
|
* as the 2nd tag */ |
|
if (opname[0] == '=' && opname[1] == '\0') |
|
sprintf(dest, "%s:operator%s(%s:)", tagsym[0]->name, opname, |
|
tagsym[1]->name); |
|
else |
|
sprintf(dest, "operator%s(%s:,%s:)", opname, tagsym[0]->name, |
|
tagsym[1]->name); |
|
} /* if */ |
|
return dest; |
|
} |
|
|
|
static void |
|
funcstub(int native) |
|
{ |
|
int tok, tag; |
|
char *str; |
|
cell val; |
|
char symbolname[sNAMEMAX + 1]; |
|
symbol *sym; |
|
int opertok; |
|
|
|
opertok = 0; |
|
lastst = 0; |
|
litidx = 0; /* clear the literal pool */ |
|
|
|
tag = sc_addtag(NULL); |
|
tok = lex(&val, &str); |
|
if (native) |
|
{ |
|
if (tok == tPUBLIC || tok == tSTOCK || tok == tSTATIC || |
|
(tok == tSYMBOL && *str == PUBLIC_CHAR)) |
|
error(42); /* invalid combination of class specifiers */ |
|
} |
|
else |
|
{ |
|
if (tok == tPUBLIC || tok == tSTATIC) |
|
tok = lex(&val, &str); |
|
} /* if */ |
|
if (tok == tOPERATOR) |
|
{ |
|
opertok = operatorname(symbolname); |
|
if (opertok == 0) |
|
return; /* error message already given */ |
|
check_operatortag(opertok, tag, symbolname); |
|
} |
|
else |
|
{ |
|
if (tok != tSYMBOL && freading) |
|
{ |
|
error(10); /* illegal function or declaration */ |
|
return; |
|
} /* if */ |
|
strcpy(symbolname, str); |
|
} /* if */ |
|
needtoken('('); /* only functions may be native/forward */ |
|
|
|
sym = fetchfunc(symbolname, tag); /* get a pointer to the |
|
* function entry */ |
|
if (!sym) |
|
return; |
|
if (native) |
|
{ |
|
sym->usage = uNATIVE | uRETVALUE | uDEFINE; |
|
sym->x.lib = curlibrary; |
|
} /* if */ |
|
|
|
declargs(sym); |
|
/* "declargs()" found the ")" */ |
|
if (!operatoradjust(opertok, sym, symbolname, tag)) |
|
sym->usage &= ~uDEFINE; |
|
/* for a native operator, also need to specify an "exported" |
|
* function name; for a native function, this is optional |
|
*/ |
|
if (native) |
|
{ |
|
if (opertok != 0) |
|
{ |
|
needtoken('='); |
|
lexpush(); /* push back, for matchtoken() to retrieve again */ |
|
} /* if */ |
|
if (matchtoken('=')) |
|
{ |
|
/* allow number or symbol */ |
|
if (matchtoken(tSYMBOL)) |
|
{ |
|
tokeninfo(&val, &str); |
|
if (strlen(str) > sEXPMAX) |
|
{ |
|
error(220, str, sEXPMAX); |
|
str[sEXPMAX] = '\0'; |
|
} /* if */ |
|
insert_alias(sym->name, str); |
|
} |
|
else |
|
{ |
|
constexpr(&val, NULL); |
|
sym->addr = val; |
|
/* |
|
* ?? Must mark this address, so that it won't be generated again |
|
* and it won't be written to the output file. At the moment, |
|
* I have assumed that this syntax is only valid if val < 0. |
|
* To properly mix "normal" native functions and indexed native |
|
* functions, one should use negative indices anyway. |
|
* Special code for a negative index in sym->addr exists in |
|
* SC4.C (ffcall()) and in SC6.C (the loops for counting the |
|
* number of native variables and for writing them). |
|
*/ |
|
} /* if */ |
|
} /* if */ |
|
} /* if */ |
|
needtoken(tTERM); |
|
|
|
litidx = 0; /* clear the literal pool */ |
|
/* clear local variables queue */ |
|
delete_symbols(&loctab, 0, TRUE, TRUE); |
|
} |
|
|
|
/* newfunc - begin a function |
|
* |
|
* This routine is called from "parse" and tries to make a function |
|
* out of the following text |
|
* |
|
* Global references: funcstatus,lastst,litidx |
|
* rettype (altered) |
|
* curfunc (altered) |
|
* declared (altered) |
|
* glb_declared (altered) |
|
* sc_alignnext (altered) |
|
*/ |
|
static int |
|
newfunc(char *firstname, int firsttag, int fpublic, int fstatic, int stock) |
|
{ |
|
symbol *sym; |
|
int argcnt, tok, tag, funcline; |
|
int opertok, opererror; |
|
char symbolname[sNAMEMAX + 1]; |
|
char *str; |
|
cell val, cidx, glbdecl; |
|
int filenum; |
|
|
|
litidx = 0; /* clear the literal pool ??? */ |
|
opertok = 0; |
|
lastst = 0; /* no statement yet */ |
|
cidx = 0; /* just to avoid compiler warnings */ |
|
glbdecl = 0; |
|
filenum = fcurrent; /* save file number at start of declaration */ |
|
|
|
if (firstname) |
|
{ |
|
assert(strlen(firstname) <= sNAMEMAX); |
|
strcpy(symbolname, firstname); /* save symbol name */ |
|
tag = firsttag; |
|
} |
|
else |
|
{ |
|
tag = (firsttag >= 0) ? firsttag : sc_addtag(NULL); |
|
tok = lex(&val, &str); |
|
assert(!fpublic); |
|
if (tok == tNATIVE || (tok == tPUBLIC && stock)) |
|
error(42); /* invalid combination of class specifiers */ |
|
if (tok == tOPERATOR) |
|
{ |
|
opertok = operatorname(symbolname); |
|
if (opertok == 0) |
|
return TRUE; /* error message already given */ |
|
check_operatortag(opertok, tag, symbolname); |
|
} |
|
else |
|
{ |
|
if (tok != tSYMBOL && freading) |
|
{ |
|
error(20, str); /* invalid symbol name */ |
|
return FALSE; |
|
} /* if */ |
|
assert(strlen(str) <= sNAMEMAX); |
|
strcpy(symbolname, str); |
|
} /* if */ |
|
} /* if */ |
|
/* check whether this is a function or a variable declaration */ |
|
if (!matchtoken('(')) |
|
return FALSE; |
|
/* so it is a function, proceed */ |
|
funcline = fline; /* save line at which the function is defined */ |
|
if (symbolname[0] == PUBLIC_CHAR) |
|
{ |
|
fpublic = TRUE; /* implicitly public function */ |
|
if (stock) |
|
error(42); /* invalid combination of class specifiers */ |
|
} /* if */ |
|
sym = fetchfunc(symbolname, tag); /* get a pointer to the |
|
* function entry */ |
|
if (!sym) |
|
return TRUE; |
|
if (fpublic) |
|
sym->usage |= uPUBLIC; |
|
if (fstatic) |
|
sym->fnumber = filenum; |
|
/* declare all arguments */ |
|
argcnt = declargs(sym); |
|
opererror = !operatoradjust(opertok, sym, symbolname, tag); |
|
if (strcmp(symbolname, uMAINFUNC) == 0) |
|
{ |
|
if (argcnt > 0) |
|
error(5); /* "main()" function may not have any arguments */ |
|
sym->usage |= uREAD; /* "main()" is the program's entry point: |
|
* always used */ |
|
} /* if */ |
|
/* "declargs()" found the ")"; if a ";" appears after this, it was a |
|
* prototype */ |
|
if (matchtoken(';')) |
|
{ |
|
if (!sc_needsemicolon) |
|
error(218); /* old style prototypes used with optional |
|
* semicolumns */ |
|
delete_symbols(&loctab, 0, TRUE, TRUE); /* prototype is done; |
|
* forget everything */ |
|
return TRUE; |
|
} /* if */ |
|
/* so it is not a prototype, proceed */ |
|
/* if this is a function that is not referred to (this can only be |
|
* detected in the second stage), shut code generation off */ |
|
if (sc_status == statWRITE && (sym->usage & uREAD) == 0) |
|
{ |
|
sc_status = statSKIP; |
|
cidx = code_idx; |
|
glbdecl = glb_declared; |
|
} /* if */ |
|
begcseg(); |
|
sym->usage |= uDEFINE; /* set the definition flag */ |
|
if (fpublic) |
|
sym->usage |= uREAD; /* public functions are always "used" */ |
|
if (stock) |
|
sym->usage |= uSTOCK; |
|
if (opertok != 0 && opererror) |
|
sym->usage &= ~uDEFINE; |
|
defsymbol(sym->name, iFUNCTN, sGLOBAL, |
|
code_idx + opcodes(1) + opargs(3) + nameincells(sym->name), tag); |
|
/* ^^^ The address for the symbol is the code address. But the |
|
* "symbol" instruction itself generates code. Therefore the |
|
* offset is pre-adjusted to the value it will have after the |
|
* symbol instruction. |
|
*/ |
|
startfunc(sym->name); /* creates stack frame */ |
|
if ((sc_debug & sSYMBOLIC) != 0) |
|
setline(funcline, fcurrent); |
|
if (sc_alignnext) |
|
{ |
|
alignframe(sc_dataalign); |
|
sc_alignnext = FALSE; |
|
} /* if */ |
|
declared = 0; /* number of local cells */ |
|
rettype = (sym->usage & uRETVALUE); /* set "return type" variable */ |
|
curfunc = sym; |
|
define_args(); /* add the symbolic info for the function arguments */ |
|
statement(NULL, FALSE); |
|
if ((rettype & uRETVALUE) != 0) |
|
sym->usage |= uRETVALUE; |
|
if (declared != 0) |
|
{ |
|
/* This happens only in a very special (and useless) case, where a |
|
* function has only a single statement in its body (no compound |
|
* block) and that statement declares a new variable |
|
*/ |
|
modstk((int)declared * sizeof(cell)); /* remove all local |
|
* variables */ |
|
declared = 0; |
|
} /* if */ |
|
if ((lastst != tRETURN) && (lastst != tGOTO)) |
|
{ |
|
const1(0); |
|
ffret(); |
|
if ((sym->usage & uRETVALUE) != 0) |
|
{ |
|
char symname[2 * sNAMEMAX + 16]; /* allow space for user |
|
* defined operators */ |
|
funcdisplayname(symname, sym->name); |
|
error(209, symname); /* function should return a value */ |
|
} /* if */ |
|
} /* if */ |
|
endfunc(); |
|
if (litidx) |
|
{ /* if there are literals defined */ |
|
glb_declared += litidx; |
|
begdseg(); /* flip to DATA segment */ |
|
dumplits(); /* dump literal strings */ |
|
|