📄 codegen.c
字号:
/* * Copyright (c) 1994 David I. Bell * Permission is granted to use, distribute, or modify this source, * provided that this copyright notice remains intact. * * Module to generate opcodes from the input tokens. */#include "calc.h"#include "token.h"#include "symbol.h"#include "label.h"#include "opcodes.h"#include "string.h"#include "func.h"#include "config.h"static BOOL rdonce; /* TRUE => do not reread this file */FUNC *curfunc;static BOOL getfilename(), getid();static void getshowcommand(), getfunction(), getbody(), getdeclarations();static void getstatement(), getobjdeclaration(), getobjvars();static void getmatdeclaration(), getsimplebody(), getonedeclaration();static void getcondition(), getmatargs(), getelement(), usesymbol();static void definesymbol(), getcallargs();static int getexprlist(), getassignment(), getaltcond(), getorcond();static int getandcond(), getrelation(), getsum(), getproduct();static int getorexpr(), getandexpr(), getshiftexpr(), getterm();static int getidexpr();static long getinitlist();/* * Read all the commands from an input file. * These are either declarations, or else are commands to execute now. * In general, commands are terminated by newlines or semicolons. * Exceptions are function definitions and escaped newlines. * Commands are read and executed until the end of file. * The toplevel flag indicates whether we are at the top interactive level. */voidgetcommands(toplevel) BOOL toplevel;{ char name[PATHSIZE+1]; /* program name */ if (!toplevel) enterfilescope(); for (;;) { (void) tokenmode(TM_NEWLINES); switch (gettoken()) { case T_DEFINE: getfunction(); break; case T_EOF: if (!toplevel) exitfilescope(); return; case T_HELP: if (!getfilename(name, FALSE, NULL)) { strcpy(name, DEFAULTCALCHELP); } givehelp(name); break; case T_READ: if (!getfilename(name, TRUE, &rdonce)) break; switch (opensearchfile(name,calcpath,CALCEXT,rdonce)) { case 0: getcommands(FALSE); break; case 1: /* previously read and -once was given */ break; default: scanerror(T_NULL, "Cannot open \"%s\"\n", name); break; } break; case T_WRITE: if (!getfilename(name, TRUE, NULL)) break; if (writeglobals(name)) scanerror(T_NULL, "Error writing \"%s\"\n", name); break; case T_SHOW: rescantoken(); getshowcommand(); break; case T_NEWLINE: case T_SEMICOLON: break; default: rescantoken(); initstack(); if (evaluate(FALSE)) updateoldvalue(curfunc); } }}/* * Evaluate a line of statements. * This is done by treating the current line as a function body, * compiling it, and then executing it. Returns TRUE if the line * successfully compiled and executed. The last expression result * is saved in the f_savedvalue element of the current function. * The nestflag variable should be FALSE for the outermost evaluation * level, and TRUE for all other calls (such as the 'eval' function). * The function name begins with an asterisk to indicate specialness. */BOOLevaluate(nestflag) BOOL nestflag; /* TRUE if this is a nested evaluation */{ char *funcname; BOOL gotstatement; funcname = (nestflag ? "**" : "*"); beginfunc(funcname, nestflag); gotstatement = FALSE; for (;;) { switch (gettoken()) { case T_SEMICOLON: break; case T_NEWLINE: case T_EOF: goto done; case T_GLOBAL: case T_LOCAL: case T_STATIC: if (gotstatement) { scanerror(T_SEMICOLON, "Declarations must be used before code"); return FALSE; } rescantoken(); getdeclarations(); break; default: rescantoken(); getstatement(NULL_LABEL, NULL_LABEL, NULL_LABEL, NULL_LABEL); gotstatement = TRUE; } }done: addop(OP_UNDEF); addop(OP_RETURN); checklabels(); if (errorcount) return FALSE; calculate(curfunc, 0); return TRUE;}/* * Get a function declaration. * func = name '(' '' | name [ ',' name] ... ')' simplebody * | name '(' '' | name [ ',' name] ... ')' body. */static voidgetfunction(){ char *name; /* parameter name */ int type; /* type of token read */ (void) tokenmode(TM_DEFAULT); if (gettoken() != T_SYMBOL) { scanerror(T_NULL, "Function name expected"); return; } beginfunc(tokenstring(), FALSE); enterfuncscope(); if (gettoken() != T_LEFTPAREN) { scanerror(T_SEMICOLON, "Left parenthesis expected for function"); return; } for (;;) { type = gettoken(); if (type == T_RIGHTPAREN) break; if (type != T_SYMBOL) { scanerror(T_COMMA, "Bad function definition"); return; } name = tokenstring(); switch (symboltype(name)) { case SYM_UNDEFINED: case SYM_GLOBAL: case SYM_STATIC: (void) addparam(name); break; default: scanerror(T_NULL, "Parameter \"%s\" is already defined", name); } type = gettoken(); if (type == T_RIGHTPAREN) break; if (type != T_COMMA) { scanerror(T_COMMA, "Bad function definition"); return; } } switch (gettoken()) { case T_ASSIGN: rescantoken(); getsimplebody(); break; case T_LEFTBRACE: rescantoken(); getbody(NULL_LABEL, NULL_LABEL, NULL_LABEL, NULL_LABEL, TRUE); break; default: scanerror(T_NULL, "Left brace or equals sign expected for function"); return; } addop(OP_UNDEF); addop(OP_RETURN); endfunc(); exitfuncscope();}/* * Get a simple assignment style body for a function declaration. * simplebody = '=' assignment '\n'. */static voidgetsimplebody(){ if (gettoken() != T_ASSIGN) { scanerror(T_SEMICOLON, "Missing equals for simple function body"); return; } (void) tokenmode(TM_NEWLINES); (void) getexprlist(); addop(OP_RETURN); if (gettoken() != T_SEMICOLON) rescantoken(); if (gettoken() != T_NEWLINE) scanerror(T_NULL, "Illegal function definition");}/* * Get the body of a function, or a subbody of a function. * body = '{' [ declarations ] ... [ statement ] ... '}' * | [ declarations ] ... [statement ] ... '\n' */static voidgetbody(contlabel, breaklabel, nextcaselabel, defaultlabel, toplevel) LABEL *contlabel, *breaklabel, *nextcaselabel, *defaultlabel; BOOL toplevel;{ BOOL gotstatement; /* TRUE if seen a real statement yet */ int oldmode; if (gettoken() != T_LEFTBRACE) { scanerror(T_SEMICOLON, "Missing left brace for function body"); return; } oldmode = tokenmode(TM_DEFAULT); gotstatement = FALSE; while (TRUE) { switch (gettoken()) { case T_RIGHTBRACE: (void) tokenmode(oldmode); return; case T_GLOBAL: case T_LOCAL: case T_STATIC: if (!toplevel) { scanerror(T_SEMICOLON, "Declarations must be at the top of the function"); return; } if (gotstatement) { scanerror(T_SEMICOLON, "Declarations must be used before code"); return; } rescantoken(); getdeclarations(); break; default: rescantoken(); getstatement(contlabel, breaklabel, nextcaselabel, defaultlabel); gotstatement = TRUE; } }}/* * Get a line of possible local, global, or static variable declarations. * declarations = { LOCAL | GLOBAL | STATIC } onedeclaration * [ ',' onedeclaration ] ... ';'. */static voidgetdeclarations(){ int type; type = gettoken(); if ((type != T_LOCAL) && (type != T_GLOBAL) && (type != T_STATIC)) { rescantoken(); return; } while (TRUE) { getonedeclaration(type); switch (gettoken()) { case T_COMMA: continue; case T_NEWLINE: case T_SEMICOLON: return; default: scanerror(T_SEMICOLON, "Bad syntax in declaration statement"); return; } }}/* * Get a single declaration of a symbol of the specified type. * onedeclaration = name [ '=' getassignment ] * | 'obj' type name [ '=' objvalues ] * | 'mat' name '[' matargs ']' [ '=' matvalues ]. */static voidgetonedeclaration(type) int type;{ char *name; /* name of symbol seen */ int symtype; /* type of symbol */ int vartype; /* type of variable being defined */ LABEL label; switch (type) { case T_LOCAL: symtype = SYM_LOCAL; break; case T_GLOBAL: symtype = SYM_GLOBAL; break; case T_STATIC: symtype = SYM_STATIC; clearlabel(&label); addoplabel(OP_INITSTATIC, &label); break; default: symtype = SYM_UNDEFINED; break; } vartype = gettoken(); switch (vartype) { case T_SYMBOL: name = tokenstring(); definesymbol(name, symtype); break; case T_MAT: addopone(OP_DEBUG, linenumber()); getmatdeclaration(symtype); if (symtype == SYM_STATIC) setlabel(&label); return; case T_OBJ: addopone(OP_DEBUG, linenumber()); getobjdeclaration(symtype); if (symtype == SYM_STATIC) setlabel(&label); return; default: scanerror(T_COMMA, "Bad syntax for declaration"); return; } if (gettoken() != T_ASSIGN) { rescantoken(); if (symtype == SYM_STATIC) setlabel(&label); return; } /* * Initialize the variable with the expression. If the variable is * static, arrange for the initialization to only be done once. */ addopone(OP_DEBUG, linenumber()); usesymbol(name, FALSE); getassignment(); addop(OP_ASSIGNPOP); if (symtype == SYM_STATIC) setlabel(&label);}/* * Get a statement. * statement = IF condition statement [ELSE statement] * | FOR '(' [assignment] ';' [assignment] ';' [assignment] ')' statement * | WHILE condition statement * | DO statement WHILE condition ';' * | SWITCH condition '{' [caseclause] ... '}' * | CONTINUE ';' * | BREAK ';' * | RETURN assignment ';' * | GOTO label ';' * | MAT name '[' value [ ':' value ] [',' value [ ':' value ] ] ']' ';' * | OBJ type '{' arg [ ',' arg ] ... '}' ] ';' * | OBJ type name [ ',' name ] ';' * | PRINT assignment [, assignment ] ... ';' * | QUIT [ string ] ';' * | SHOW item ';' * | body * | assignment ';' * | label ':' statement * | ';'. */static voidgetstatement(contlabel, breaklabel, nextcaselabel, defaultlabel) LABEL *contlabel; /* label for continue statement */ LABEL *breaklabel; /* label for break statement */ LABEL *nextcaselabel; /* label for next case statement */ LABEL *defaultlabel; /* label for default case */{ LABEL label1, label2, label3, label4; /* locations for jumps */ int type; BOOL printeol; addopone(OP_DEBUG, linenumber()); switch (gettoken()) { case T_NEWLINE: case T_SEMICOLON: return; case T_RIGHTBRACE: scanerror(T_NULL, "Extraneous right brace"); return; case T_CONTINUE: if (contlabel == NULL_LABEL) { scanerror(T_SEMICOLON, "CONTINUE not within FOR, WHILE, or DO"); return; } addoplabel(OP_JUMP, contlabel); break; case T_BREAK: if (breaklabel == NULL_LABEL) { scanerror(T_SEMICOLON, "BREAK not within FOR, WHILE, or DO"); return; } addoplabel(OP_JUMP, breaklabel); break; case T_GOTO: if (gettoken() != T_SYMBOL) { scanerror(T_SEMICOLON, "Missing label in goto"); return; } addop(OP_JUMP); addlabel(tokenstring()); break; case T_RETURN: switch (gettoken()) { case T_NEWLINE: case T_SEMICOLON: addop(OP_UNDEF); addop(OP_RETURN); return; default: rescantoken(); (void) getexprlist(); if (curfunc->f_name[0] == '*') addop(OP_SAVE); addop(OP_RETURN); } break; case T_LEFTBRACE: rescantoken(); getbody(contlabel, breaklabel, nextcaselabel, defaultlabel, FALSE); return; case T_IF: clearlabel(&label1); clearlabel(&label2); getcondition(); addoplabel(OP_JUMPEQ, &label1); getstatement(contlabel, breaklabel, NULL_LABEL, NULL_LABEL); if (gettoken() != T_ELSE) { setlabel(&label1); rescantoken(); return; } addoplabel(OP_JUMP, &label2); setlabel(&label1); getstatement(contlabel, breaklabel, NULL_LABEL, NULL_LABEL); setlabel(&label2); return; case T_FOR: /* for (a; b; c) x */ clearlabel(&label1); clearlabel(&label2); clearlabel(&label3); clearlabel(&label4); contlabel = NULL_LABEL; breaklabel = &label4; if (gettoken() != T_LEFTPAREN) { scanerror(T_SEMICOLON, "Left parenthesis expected"); return; } if (gettoken() != T_SEMICOLON) { /* have 'a' part */ rescantoken(); (void) getexprlist(); addop(OP_POP); if (gettoken() != T_SEMICOLON) { scanerror(T_SEMICOLON, "Missing semicolon"); return; } } if (gettoken() != T_SEMICOLON) { /* have 'b' part */ setlabel(&label1); contlabel = &label1; rescantoken(); (void) getexprlist(); addoplabel(OP_JUMPNE, &label3); addoplabel(OP_JUMP, breaklabel); if (gettoken() != T_SEMICOLON) { scanerror(T_SEMICOLON, "Missing semicolon"); return; } } if (gettoken() != T_RIGHTPAREN) { /* have 'c' part */ if (label1.l_offset <= 0) addoplabel(OP_JUMP, &label3); setlabel(&label2); contlabel = &label2; rescantoken(); (void) getexprlist(); addop(OP_POP); if (label1.l_offset > 0) addoplabel(OP_JUMP, &label1); if (gettoken() != T_RIGHTPAREN) { scanerror(T_SEMICOLON, "Right parenthesis expected"); return; } } setlabel(&label3); if (contlabel == NULL_LABEL) contlabel = &label3; getstatement(contlabel, breaklabel, NULL_LABEL, NULL_LABEL); addoplabel(OP_JUMP, contlabel); setlabel(breaklabel); return; case T_WHILE: contlabel = &label1; breaklabel = &label2; clearlabel(contlabel); clearlabel(breaklabel); setlabel(contlabel); getcondition(); addoplabel(OP_JUMPEQ, breaklabel); getstatement(contlabel, breaklabel, NULL_LABEL, NULL_LABEL); addoplabel(OP_JUMP, contlabel); setlabel(breaklabel); return; case T_DO: contlabel = &label1; breaklabel = &label2; clearlabel(contlabel); clearlabel(breaklabel); clearlabel(&label3); setlabel(&label3); getstatement(contlabel, breaklabel, NULL_LABEL, NULL_LABEL); if (gettoken() != T_WHILE) { scanerror(T_SEMICOLON, "WHILE keyword expected for DO statement"); return; } setlabel(contlabel); getcondition(); addoplabel(OP_JUMPNE, &label3); setlabel(breaklabel); return; case T_SWITCH: breaklabel = &label1; nextcaselabel = &label2; defaultlabel = &label3; clearlabel(breaklabel); clearlabel(nextcaselabel); clearlabel(defaultlabel); getcondition(); if (gettoken() != T_LEFTBRACE) { scanerror(T_SEMICOLON, "Missing left brace for switch statement"); return; } addoplabel(OP_JUMP, nextcaselabel); rescantoken(); getstatement(contlabel, breaklabel, nextcaselabel, defaultlabel); addoplabel(OP_JUMP, breaklabel); setlabel(nextcaselabel); if (defaultlabel->l_offset > 0) addoplabel(OP_JUMP, defaultlabel); else addop(OP_POP); setlabel(breaklabel); return; case T_CASE: if (nextcaselabel == NULL_LABEL) { scanerror(T_SEMICOLON, "CASE not within SWITCH statement"); return; }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -