/* * mc.c : the Mutt compiler * Craig Durland 6/87, modified in late '91 for Mutt2 */ /* Copyright 1990, 1991, 1992 Craig Durland * Distributed under the terms of the GNU General Public License. * Distributed "as is", without warranties of any kind, but comments, * suggestions and bug reports are welcome. */ static char what[] = "@(#)Mutt2 compiler v2.1 2/2/92"; #define WHAT (&what[4]) #include #include #include "mc.h" #include "opcode.h" #include "mm.h" extern address entrypt; /* in code.c */ extern char *malloc(), *strcpy(), *new_ext(), *spoof(), *savestr(); extern int xtn, msize, omsize; extern int32 atoN(); extern MuttCmd muttcmds[]; extern unsigned int codesize(); extern void doc(), dumpcode(), pilefile(); char ebuf[MAXSTRLEN+5], *muttfile = "", *include_list[10]; FILE *lstfile = NULL, *srcfile; int errors = 0, warnings = 0, srcline = 0; main(argc,argv) char *argv[]; { extern char *optarg, optltr; /* in argh.c */ extern int no_warn, no_gripe; /* in supp.c */ char buf[90], *ptr = NULL, *tfname = NULL; int j = 0, list = FALSE, x, stats = FALSE, quiet = FALSE; while ( (x = argh(argc,argv,"I:lst:vq:")) ) switch (x) { case 2: ptr = optarg; break; case 1: switch (optltr) { case 'I': include_list[j++] = optarg; break; case 'l': list = TRUE; break; case 's': stats = TRUE; break; case 't': tfname = optarg; break; case 'v': printf("%s copyright 1987-92 Craig Durland\n",WHAT); exit(0); case 'q': /* quiet */ x = atoi(optarg); quiet = x & 1; no_gripe = x & 2; no_warn = x & 4; break; } } include_list[j] = NULL; if (!quiet) printf("%s\n",WHAT); if (ptr == NULL) { doc(); exit(1); } if (list) { new_ext(buf,ptr,".lst"); if ((lstfile = fopen(buf,"w")) == NULL) bitch("Can't open list file."); } if (tfname) load_ext_token_table(tfname); /* external token file */ init_code_generater(); new_ext(buf,ptr,".mut"); pilefile(buf,FALSE); finishup(); if (errors == 0) dumpcode(ptr); spoof(ebuf,"%d Errors. %d Warnings. %u bytes of code.", errors,warnings,codesize()); if (stats) dump_stats(stdout); if (!quiet) puts(ebuf); if (lstfile) { fprintf(lstfile,"\n%s\n",ebuf); fclose(lstfile); } exit(errors); } void doc() { dump_doc( "MC2 [options] sourcefile[.MUT]", "options: ", " -I dir: An alternate directory for include files. One dir per -I", " -l : Assembler output with source comments. Put into sourcefile.LST", " -q : quiet some messages", " -s : Obscure compiler stats", " -t tokenfile : tokenfile.TOK contains X-tokens", " -v : Display the version of the compiler", "Compiled code is put into sourcefile.MCO", (char *)NULL); } extern char *catstrs(); /* open a file, search path_list if necessary */ FILE *flopen(name,path_list,mode) char *name, *path_list[], *mode; { char buf[300]; FILE *fptr; int j; if ((fptr = fopen(name,mode))) return fptr; for (j = 0; path_list[j]; j++) if ((fptr = fopen(catstrs(buf,path_list[j],"/",name,(char *)NULL),mode))) return fptr; return NULL; } void pilefile(fname,search) char *fname; { char fn[100], *ptr = muttfile; FILE *sf = srcfile; int sline = srcline; srcfile = search ? flopen(fname,include_list,"r") : fopen(fname,"r"); if (srcfile == NULL) bitch(spoof(ebuf,"Can't open %s.",fname)); muttfile = strcpy(fn,fname); srcline = 0; getsrc(); /* prime scan() */ while (compile()) ; muttfile = ptr; srcline = sline; fclose(srcfile); srcfile = sf; } /* ******************************************************************** */ /* ********************* the compiler ********************************* */ /* ******************************************************************** */ extern address getpgm(), pcaddr(); extern int ddone_label, btv; extern MMDatum *getconst(); char token[257], temp[257]; int breaklabel = -1, contlabel = -1; unsigned int class = VOID; MMDatum rv, *vtr; compile() { static int clevel = -1, indefun = FALSE; int l1, ldone, t,z; unsigned int lastclass; clevel++; lastclass = class; get_token(); switch(class) { case SEOF: clevel--; return FALSE; /* hit EOF */ case STRING: gostr(RVSTR,token); break; case NUMBER: gonumx(atoN(token)); break; case BOOLEAN: gonum8(RVBOOL,btv); break; case TOKEN: genvar(token,FALSE); break; case DELIMITER: switch (*token) { case '{': /* { ... } */ while (TRUE) { lookahead(); if (class == DELIMITER) if (*token == '}') break; else if (*token == '{') bitch("Can't nest pgms."); class = lastclass; compile(); lastclass = class; } get_token(); /* suck up } */ class = lastclass; break; case '(': /* ( ... ) */ lookahead(); if (class == DELIMITER && *token == ')') /* () */ { class = EMPTY; goto endexp; } /*class = lastclass;*/ get_token(); switch (class) { case STRING: gostr(RVSTR,token); goto endexp; case NUMBER: gonumx(atoN(token)); goto endexp; case BOOLEAN: gonum8(RVBOOL,btv); goto endexp; case TOKEN: break; default: bitch(spoof(ebuf, "Wanted token, string, number or boolean, got %s.",token)); } if ((t = lookup(token,muttcmds,msize)) != -1) { class = lastclass; switch (t) { case 64: /* (include file) */ get_token(); if (class != TOKEN && class != STRING) bitch("include requires token or string."); clevel--; class = include(token); clevel++; goto done; /* end of this line !!! sleaze */ case 23: class = comp_if(lastclass); break; /* (if ...) */ case 5: class = comp_while(); break; /* (while ...) */ case 76: class = comp_for(); break; /* (for ...) */ case 1: class = comp_cond(); break; /* (cond ...) */ case 4: class = comp_switch(); break; /* (switch ...) */ case 2: /* (defun name pgm) */ if (clevel != 0) moan("Can't nest defuns."); indefun = TRUE; defun(); indefun = FALSE; class = VOID; break; case 8: case 6: /* (label label-name) (goto label) */ get_token(); if (class != TOKEN && class != STRING) bitch("Label must be token or string."); if (!indefun) moan("Labels and gotos can only be used inside defuns."); if ((z = get_named_label(token)) == -1) z = gen_named_label(token); if (t == 6) { gojmp(JMP,z); class = VOID; } /* goto */ else /* label */ { stufflabel(z); class = UNKNOWN; /* can get here from anywhere */ } break; case 7: /* (break) */ if (breaklabel == -1) { moan("break not allowed here."); break; } gojmp(JMP,breaklabel); class = VOID; break; case 71: /* (continue) */ if (contlabel == -1) { moan("continue not allowed here."); break; } gojmp(JMP,contlabel); class = VOID; break; case 9: /* (done) */ if (ddone_label == -1) genop(DONE); else gojmp(JMP,ddone_label); class = VOID; break; case 16: genop(HALT); class = VOID; break; /* (halt) */ case 29: genop(RVVOID); class = VOID; break; /* (novalue) */ case 42: genop(NARGS); class = NUMBER; break; /* (nargs) */ case 43: /* (arg n) */ compile(); type_check(NUMBER,0); genop(ARG); class = UNKNOWN; break; case 15: /* (push-args n) */ compile(); type_check(NUMBER,0); genop(PUSHARGS); class = PUSHEDARGS; break; case 17: /* (push-arg exp) */ compile(); genop(SHOVERV); class = PUSHEDARGS; break; case 0: /* (!= val val) */ compile(); z = class; checkit("!=",STRING,BOOLEAN,NUMBER,0); pushpush(); compile(); if (z != UNKNOWN) type_check(z,0); /* yukk!!! */ genop(CMP); genop(NOT); class = BOOLEAN; break; case 12: /* (== val val ... ) */ compile(); z = class; checkit("==",STRING,BOOLEAN,NUMBER,0); pushpush(); compile(); if (z != UNKNOWN) type_check(z,0); /* yukk!!! */ if (!gaze_ahead(STRING,BOOLEAN,NUMBER,0)) /* (== val val) */ genop(CMP); else /* (== val val val [...]) */ { l1 = genlabel(); do { genop(DUP); genop(CMP); if (!gaze_ahead(STRING,BOOLEAN,NUMBER,0)) break; gojmp(JMPFALSE,l1); compile(); if (z != UNKNOWN) type_check(z,0); /* yukk!!! */ } while (TRUE); stufflabel(l1); genop(POP); } class = BOOLEAN; break; case 21: /* (remove-elements object n z) */ gonum16(PUSHTOKEN,REMOVE_ELS); compile(); checkit("remove-elements", LIST,STRING,0); /* !!!ick */ /* !!!??? can't be a string constant! */ genop(SHOVERV); compile(); type_check(NUMBER,0); genop(SHOVERV); compile(); type_check(NUMBER,0); genop(SHOVERV); genop(DOOP); class = VOID; break; case 18: /* (insert-object object n new-object ...) */ gonum16(PUSHTOKEN,INSERT_OBJ); compile(); checkit("insert-object", LIST,STRING,0); /* !!!ick */ genop(SHOVERV); compile(); type_check(NUMBER,0); genop(SHOVERV); while (gaze_ahead(LIST,STRING,NUMBER,0)) { compile(); genop(SHOVERV); } genop(DOOP); class = UNKNOWN; /* !!!Not really - its STRING or LIST */ break; case 24: /* (extract-element object n) */ gonum16(PUSHTOKEN,EXTRACT_EL); compile(); checkit("extract-element", LIST,STRING,0); /* !!!ick */ /* !!!??? can't be a string constant! */ genop(SHOVERV); compile(); type_check(NUMBER,0); genop(SHOVERV); genop(DOOP); class = UNKNOWN; break; case 25: /* (extract-elements object n z) */ gonum16(PUSHTOKEN,EXTRACT_ELS); compile(); checkit("extract-elements", LIST,STRING,0); /* !!!ick */ /* !!!??? can't be a string constant! */ genop(SHOVERV); compile(); type_check(NUMBER,0); genop(SHOVERV); compile(); type_check(NUMBER,0); genop(SHOVERV); genop(DOOP); class = UNKNOWN; /* !!!Not really - its STRING or LIST */ break; case 19: /* (length-of object) */ compile(); /* get object - can be anything */ genop(LEN_OF); class = NUMBER; break; case 20: /* (convert-to type object) */ compile(); type_check(NUMBER,0); genop(SHOVERV); /* type */ compile(); /* get object - can be anything */ genop(CONVERT_TO); class = UNKNOWN; /* !!!I can (sometimes) figure out the type */ /* !!! do some more checking here */ break; case 28: /* (not) */ compile(); type_check(BOOLEAN,0); genop(NOT); class = BOOLEAN; break; case 3: opmath(ADD); break; /* (+ num num ...) */ case 67: opmath(SUB); break; /* (- num num ...) */ case 65: opmath(MUL); break; /* (* num num ...) */ case 69: opmath(DIV); break; /* (/ num num ...) */ case 63: opeq(ADD); break; /* (+= var num [num ...]) */ case 68: opeq(SUB); break; /* (-= var num [num ...]) */ case 66: opeq(MUL); break; /* (*= var num [num ...]) */ case 70: opeq(DIV); break; /* (/= var num [num ...]) */ case 11: case 14: /* (< num num), (>= num num) */ compile(); z = class; checkit("< or >=",NUMBER,0); pushpush(); compile(); if (z != UNKNOWN) type_check(z,0); /* yukk!!! */ genop(LT); if (t == 14) genop(NOT); /* (x >= y) == !(x < y) */ class = BOOLEAN; break; case 10: case 13: /* (<= num num), (> num num) */ compile(); z = class; checkit("<= or >",NUMBER,0); pushpush(); compile(); if (z != UNKNOWN) type_check(z,0); /* yukk!!! */ genop(LTE); if (t == 13) genop(NOT); /* (x > y) == !(x <= y) */ class = BOOLEAN; break; case 81: /* (or bool ...) */ z = JMPTRUE; andor: ldone = genlabel(); while (TRUE) { compile(); type_check(BOOLEAN,0); lookahead(); if (class == DELIMITER && *token == ')') break; gojmp(z,ldone); } stufflabel(ldone); class = BOOLEAN; break; case 80: z = JMPFALSE; goto andor; /* (and bool bool ...) */ case 26: genop(ASKUSER); break; /* (ask-user) */ case 78: floc(); break; /* (floc fcn-name) */ case 79: loc(); break; /* (loc var-name) */ case 72: /* (pointer var) */ isvarok(clevel,class); pointer(indefun); class = lastclass; break; case 73: /* (array type name subs) */ isvarok(clevel,class); array(indefun ? LOCAL : GLOBAL,FALSE); class = lastclass; break; case 62: /* (bool var [var ...]) */ t = BOOLEAN; defvar: isvarok(clevel,class); vdeclare(t,indefun); class = lastclass; break; case 75: t = INT8; goto defvar; /* (byte var [var ...]) */ case 61: t = INT16; goto defvar; /* (small-int var [var ...]) */ case 31: t = INT32; goto defvar; /* (int var [var ...]) */ case 60: /* (string name [name ...]) */ t = STRING; goto defobject; case 27: /* (list name [name ...]) */ t = LIST; defobject: isvarok(clevel,class); do { get_token(); if (class != TOKEN) bitch(spoof(ebuf,"%s is not a var name.",token)); z = addvar(token, t, 0, (indefun ? LOCAL : GLOBAL)); if (indefun) genobj(CREATE_OBJ, LOCAL, t, voffset(z)); lookahead(); } while (class == TOKEN); class = lastclass; break; case 77: /* (const name val name val ...) */ do { get_token(); if (class != TOKEN) bitch(spoof(ebuf,"%s is not a const name.",token)); strcpy(temp,token); get_token(); rv.type = class; switch (class) { case NUMBER: rv.val.num = atoN(token); break; case BOOLEAN: rv.val.num = btv; break; case STRING: rv.val.str = savestr(token); break; case TOKEN: if (vtr = getconst(token)) { rv = *vtr; break; } /* else fall though and error */ default: moan(spoof(ebuf,"Invalid const type: %s",token)); rv.type = BOOLEAN; } add_const(temp,&rv); lookahead(); } while (class == TOKEN); class = lastclass; break; default: moan(spoof(ebuf,"Compiler is confused by %s.",token)); } goto endexp; } if (other_Mutt_cmd(token)) goto endexp; if (varcompile(TRUE)) goto endexp; if ((t = getpgm(token)) != NIL) goaddr(PUSHADDR,t,token); else if (-1 != (t = lookup_ext_token_by_name(token))) gonum16(PUSHXT,t); else gostr(PUSHNAME,token); vargs(); genop(DOOP); class = UNKNOWN; endexp: lastclass = class; get_token(); if (class != DELIMITER || *token != ')') bitch(spoof(ebuf,"Wanted ) got %s.",token)); class = lastclass; break; default: bitch(spoof(ebuf,"Invalid delimiter: %s ?not enough args?",token)); } break; default: bitch(spoof(ebuf,"I don't reconize %s!",token)); } done: clevel--; return TRUE; }