📄 codegen.c
字号:
/* * codegen - module to generate opcodes from the input tokens * * Copyright (C) 1999-2006 David I. Bell and Ernest Bowen * * Primary author: David I. Bell * * Calc is open software; you can redistribute it and/or modify it under * the terms of the version 2.1 of the GNU Lesser General Public License * as published by the Free Software Foundation. * * Calc is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General * Public License for more details. * * A copy of version 2.1 of the GNU Lesser General Public License is * distributed with calc under the filename COPYING-LGPL. You should have * received a copy with calc; if not, write to Free Software Foundation, Inc. * 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. * * @(#) $Revision: 29.22 $ * @(#) $Id: codegen.c,v 29.22 2006/12/15 16:25:09 chongo Exp $ * @(#) $Source: /usr/local/src/cmd/calc/RCS/codegen.c,v $ * * Under source code control: 1990/02/15 01:48:13 * File existed as early as: before 1990 * * Share and enjoy! :-) http://www.isthe.com/chongo/tech/comp/calc/ */#include <stdio.h>#include "have_unistd.h"#if defined(HAVE_UNISTD_H)#include <unistd.h>#endif#include "calc.h"#include "token.h"#include "symbol.h"#include "label.h"#include "opcodes.h"#include "string.h"#include "func.h"#include "conf.h"#if defined(_WIN32) && !defined(__CYGWIN__)# include <direct.h>#endifstatic BOOL rdonce; /* TRUE => do not reread this file */FUNC *curfunc;static int getsymvalue(char *name, VALUE *v_p);static int getfilename(char *name, size_t namelen, BOOL *once);static BOOL getid(char *buf);static void getshowstatement(void);static void getfunction(void);static void ungetfunction(void);static void getbody(LABEL *contlabel, LABEL *breaklabel, LABEL *nextcaselabel, LABEL *defaultlabel);static int getdeclarations(int symtype);static int getsimpledeclaration (int symtype);static int getonevariable (int symtype);static void getstatement(LABEL *contlabel, LABEL *breaklabel, LABEL *nextcaselabel, LABEL *defaultlabel);static void getobjdeclaration(int symtype);static void getoneobj(long index, int symtype);static void getobjvars(char *name, int symtype);static void getmatdeclaration(int symtype);static void getonematrix(int symtype);static void creatematrix(void);static void getsimplebody(void);static void getcondition(void);static void getmatargs(void);static void getelement(void);static void usesymbol(char *name, int autodef);static void definesymbol(char *name, int symtype);static void getcallargs(char *name);static void do_changedir(void);static int getexprlist(void);static int getopassignment(void);static int getassignment(void);static int getaltcond(void);static int getorcond(void);static int getandcond(void);static int getrelation(void);static int getsum(void);static int getproduct(void);static int getorexpr(void);static int getandexpr(void);static int getshiftexpr(void);static int getreference(void);static int getincdecexpr(void);static int getterm(void);static int getidexpr(BOOL okmat, int autodef);static long getinitlist(void);#define INDICALLOC 8static int quickindices[INDICALLOC];static int * newindices;static int * indices;static int maxindices;/* * 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(BOOL toplevel){ char name[MAXCMD+1+1]; /* program name */ /* firewall */ name[0] = '\0'; name[MAXCMD+1] = '\0'; abort_now = FALSE; /* getcommands */ if (!toplevel) enterfilescope(); for (;;) { int i; (void) tokenmode(TM_NEWLINES); switch (gettoken()) { case T_DEFINE: getfunction(); break; case T_EOF: if (!toplevel) exitfilescope(); return; case T_HELP: for (i=1;;i++) { switch(getfilename(name, MAXCMD+1, NULL)) { case 1: case -1: if(i == 1) { strcpy(name, DEFAULTCALCHELP); givehelp(name); } break; case 0: givehelp(name); continue; default: break; } break; } break; case T_READ: if (!allow_read) { scanerror(T_NULL, "read command disallowed by -m mode\n"); break; } for (;;) { int open_ret; if (getfilename(name, MAXCMD+1, &rdonce)) break; open_ret = opensearchfile(name,calcpath, CALCEXT,rdonce); switch (open_ret) { case 0: getcommands(FALSE); closeinput(); continue; case 1: /* prev read and -once was given */ continue; case -2: scanerror(T_NULL, "Maximum input depth reached"); break; default: scanerror(T_NULL, "Cannot open \"%s\"", name); continue; } break; } break; case T_WRITE: if (!allow_write) { scanerror(T_NULL, "write command disallowed by -m mode\n"); break; } if (getfilename(name, MAXCMD+1, NULL)) break; if (writeglobals(name)) { scanerror(T_NULL, "Error writing \"%s\"\n", name); } break; case T_CD: do_changedir(); break; case T_NEWLINE: case T_SEMICOLON: break; default: rescantoken(); initstack(); if (evaluate(FALSE)) updateoldvalue(curfunc); freefunc(curfunc); if (abort_now) { if (!stdin_tty) run_state = RUN_EXIT; else if (run_state < RUN_PRE_TOP_LEVEL) run_state = RUN_PRE_TOP_LEVEL; longjmp(jmpbuf, 1); } } }}/* * 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. * * given: * nestflag TRUE if this is a nested evaluation */BOOLevaluate(BOOL nestflag){ char *funcname; int loop = 1; /* 0 => end the main while loop */ funcname = (nestflag ? "**" : "*"); beginfunc(funcname, nestflag); if (gettoken() == T_LEFTBRACE) { getbody(NULL_LABEL, NULL_LABEL, NULL_LABEL, NULL_LABEL); } else { if (nestflag) (void) tokenmode(TM_DEFAULT); rescantoken(); while (loop) { switch (gettoken()) { case T_SEMICOLON: break; case T_NEWLINE: case T_EOF: loop = 0; break; default: rescantoken(); getstatement(NULL_LABEL, NULL_LABEL, NULL_LABEL, NULL_LABEL); } } } addop(OP_UNDEF); addop(OP_RETURN); checklabels(); if (errorcount) return FALSE; calculate(curfunc, 0); return TRUE;}/* * Undefine one or more functions */static voidungetfunction(void){ char *name; int type; for (;;) { switch (gettoken()) { case T_COMMA: continue; case T_SYMBOL: name = tokensymbol(); type = getbuiltinfunc(name); if (type >= 0) { warning( "Cannot undefine builtin function \"%s\"", name); continue; } rmuserfunc(name); continue; case T_MULT: rmalluserfunc(); continue; case T_STATIC: if (gettoken() != T_SYMBOL) { scanerror(T_SEMICOLON, "Non-identifier following \"undefine static\""); return; } name = tokensymbol(); endscope(name, FALSE); continue; case T_NEWLINE: case T_SEMICOLON: case T_EOF: rescantoken(); return; default: scanerror(T_SEMICOLON, "Non-name arg for undefine"); return; } }}/* * Get a function declaration. * func = name '(' '' | name [ ',' name] ... ')' simplebody * | name '(' '' | name [ ',' name] ... ')' body. */static voidgetfunction(void){ char *name; /* parameter name */ int type; /* type of token read */ LABEL label; long index; (void) tokenmode(TM_DEFAULT); if (gettoken() != T_SYMBOL) { scanerror(T_NULL, "Function name was expected"); return; } name = tokensymbol(); type = getbuiltinfunc(name); if (type >= 0) { scanerror(T_SEMICOLON, "Using builtin function name"); return; } beginfunc(name, FALSE); enterfuncscope(); if (gettoken() != T_LEFTPAREN) { scanerror(T_SEMICOLON, "Left parenthesis expected for function"); return; } index = 0; for (;;) { type = gettoken(); if (type == T_RIGHTPAREN) break; if (type != T_SYMBOL) { scanerror(T_COMMA, "Using non-identifier as function parameter"); return; } name = tokensymbol(); switch (symboltype(name)) { case SYM_UNDEFINED: case SYM_GLOBAL: case SYM_STATIC: index = addparam(name); break; default: scanerror(T_NULL, "Parameter \"%s\" is already defined", name); } type = gettoken(); if (type == T_ASSIGN) { clearlabel(&label); addopone(OP_PARAMADDR, index); addoplabel(OP_JUMPNN, &label); getopassignment(); addop(OP_ASSIGNPOP); setlabel(&label); type = gettoken(); } if (type == T_RIGHTPAREN) break; if (type != T_COMMA) { scanerror(T_COMMA, "Using other than comma to separate parameters"); return; } } switch (gettoken()) { case T_ASSIGN: getsimplebody(); break; case T_LEFTBRACE: getbody(NULL_LABEL, NULL_LABEL, NULL_LABEL, NULL_LABEL); break; default: scanerror(T_NULL, "Left brace or equals sign expected for function"); return; } endfunc(); exitfuncscope();}/* * Get a simple assignment style body for a function declaration. * simplebody = '=' assignment '\n'. */static voidgetsimplebody(void){ (void) tokenmode(TM_NEWLINES); (void) getexprlist(); addop(OP_RETURN);}/* * Get the body of a function, or a subbody of a function. * body = '{' [ declarations ] ... [ statement ] ... '}' * | [ declarations ] ... [statement ] ... '\n' *//*ARGSUSED*/static voidgetbody(LABEL *contlabel, LABEL *breaklabel, LABEL *nextcaselabel, LABEL *defaultlabel){ int oldmode; oldmode = tokenmode(TM_DEFAULT); while (TRUE) { switch (gettoken()) { case T_RIGHTBRACE: (void) tokenmode(oldmode); return; case T_EOF: scanerror(T_NULL, "End-of-file in function body"); return; default: rescantoken(); getstatement(contlabel, breaklabel, nextcaselabel, defaultlabel); } }}/* * Get a line of possible local, global, or static variable declarations. * declarations = { LOCAL | GLOBAL | STATIC } onedeclaration * [ ',' onedeclaration ] ... ';'. */static intgetdeclarations(int symtype){ int res = 0; while (TRUE) { switch (gettoken()) { case T_COMMA: continue; case T_NEWLINE: case T_SEMICOLON: case T_RIGHTBRACE: case T_EOF: rescantoken(); return res; case T_SYMBOL: addopone(OP_DEBUG, linenumber()); rescantoken(); if (getsimpledeclaration(symtype)) res = 1; break; case T_MAT: addopone(OP_DEBUG, linenumber()); getmatdeclaration(symtype); res = 1; break; case T_OBJ: addopone(OP_DEBUG, linenumber()); getobjdeclaration(symtype); addop(OP_POP); res = 1; break; default: scanerror(T_SEMICOLON, "Bad syntax in declaration statement"); return res; } }}/* * Get declaration of a sequence of simple identifiers, as in * global a, b = 1, c d = 2, d; * Subsequences end with "," or at end of line; spaces indicate * repeated assignment, e.g. "c d = 2" has the effect of "c = 2, d = 2". */static intgetsimpledeclaration(int symtype){ int res = 0; for (;;) { switch (gettoken()) { case T_SYMBOL: rescantoken(); if (getonevariable(symtype)) { res = 1; addop(OP_POP); } continue; case T_COMMA: continue; default: rescantoken(); return res; } }}/* * Get one variable in a sequence of simple identifiers. * Returns 1 if the subsequence in which the variable occurs ends with * an assignment, e.g. for the variables b, c, d, in * static a, b = 1, c d = 2, d; */static intgetonevariable(int symtype){ char *name; int res = 0; switch(gettoken()) { case T_SYMBOL: name = addliteral(tokensymbol()); res = getonevariable(symtype); definesymbol(name, symtype); if (res) { usesymbol(name, 0); addop(OP_ASSIGNBACK); } return res; case T_ASSIGN: getopassignment(); rescantoken(); return 1; default: rescantoken(); return 0; }}/* * 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 ';' * | PRINT assignment [, assignment ] ... ';' * | QUIT [ string ] ';' * | ABORT [ string ] ';' * | SHOW item ';' * | body * | assignment ';' * | label ':' statement * | ';'. * * given: * contlabel label for continue statement * breaklabel label for break statement * nextcaselabel label for next case statement * defaultlabel label for default case */static voidgetstatement(LABEL *contlabel, LABEL *breaklabel, LABEL *nextcaselabel, LABEL *defaultlabel){ LABEL label; LABEL label1, label2, label3, label4; /* locations for jumps */ int type; BOOL printeol; int oldmode; addopone(OP_DEBUG, linenumber()); switch (gettoken()) { case T_NEWLINE: case T_SEMICOLON: return; case T_GLOBAL: (void) getdeclarations(SYM_GLOBAL); break; case T_STATIC: clearlabel(&label); addoplabel(OP_INITSTATIC, &label); if (getdeclarations(SYM_STATIC)) setlabel(&label); else curfunc->f_opcodecount -= 2; break; case T_LOCAL: (void) getdeclarations(SYM_LOCAL); break; case T_UNDEFINE: ungetfunction(); break; 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(tokensymbol()); 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: getbody(contlabel, breaklabel, nextcaselabel, defaultlabel); return; case T_IF: clearlabel(&label1); clearlabel(&label2); getcondition(); switch(gettoken()) { case T_CONTINUE: if (contlabel == NULL_LABEL) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -