mirror of
https://github.com/lcn2/calc.git
synced 2025-08-16 01:03:29 +03:00
Converted all ASCII tabs to ASCII spaces using a 8 character tab stop, for all files, except for all Makefiles (plus rpm.mk). The `git diff -w` reports no changes.
706 lines
22 KiB
C
706 lines
22 KiB
C
/*
|
|
* addop - add opcodes to a function being compiled
|
|
*
|
|
* Copyright (C) 1999-2007,2021-2023 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.
|
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
*
|
|
* Under source code control: 1990/02/15 01:48:10
|
|
* File existed as early as: before 1990
|
|
*
|
|
* Share and enjoy! :-) http://www.isthe.com/chongo/tech/comp/calc/
|
|
*/
|
|
|
|
|
|
#include <stdio.h>
|
|
#include "calc.h"
|
|
#include "alloc.h"
|
|
#include "opcodes.h"
|
|
#include "str.h"
|
|
#include "func.h"
|
|
#include "token.h"
|
|
#include "label.h"
|
|
#include "symbol.h"
|
|
|
|
|
|
#include "errtbl.h"
|
|
#include "banned.h" /* include after system header <> includes */
|
|
|
|
|
|
#define FUNCALLOCSIZE 20 /* reallocate size for functions */
|
|
#define OPCODEALLOCSIZE 100 /* reallocate size for opcodes in functions */
|
|
|
|
|
|
STATIC unsigned long maxopcodes;/* number of opcodes available */
|
|
STATIC long newindex; /* index of new function */
|
|
STATIC char *newname; /* name of new function */
|
|
STATIC long oldop; /* previous opcode */
|
|
STATIC long oldoldop; /* opcode before previous opcode */
|
|
STATIC long debugline; /* line number of latest debug opcode */
|
|
STATIC long funccount; /* number of functions */
|
|
STATIC long funcavail; /* available number of functions */
|
|
STATIC FUNC *functemplate; /* function definition template */
|
|
STATIC FUNC **functions; /* table of functions */
|
|
STATIC STRINGHEAD funcnames; /* function names */
|
|
|
|
|
|
/*
|
|
* Initialize the table of user defined functions.
|
|
*/
|
|
void
|
|
initfunctions(void)
|
|
{
|
|
initstr(&funcnames);
|
|
maxopcodes = OPCODEALLOCSIZE;
|
|
functemplate = (FUNC *) malloc(funcsize(maxopcodes));
|
|
if (functemplate == NULL) {
|
|
math_error("Cannot allocate function template");
|
|
not_reached();
|
|
}
|
|
functions = (FUNC **) malloc(sizeof(FUNC *) * FUNCALLOCSIZE);
|
|
if (functions == NULL) {
|
|
math_error("Cannot allocate function table");
|
|
not_reached();
|
|
}
|
|
funccount = 0;
|
|
funcavail = FUNCALLOCSIZE;
|
|
}
|
|
|
|
|
|
/*
|
|
* Show the list of user defined functions.
|
|
*/
|
|
void
|
|
showfunctions(void)
|
|
{
|
|
FUNC *fp; /* current function */
|
|
long count;
|
|
long index;
|
|
|
|
count = 0;
|
|
if (funccount > 0) {
|
|
if (conf->resource_debug & RSCDBG_FUNC_INFO)
|
|
math_str("Index\tName \tArgs\tOpcodes\n"
|
|
"-----\t------ \t---- \t------\n");
|
|
else
|
|
math_str("Name\tArguments\n"
|
|
"----\t---------\n");
|
|
for (index = 0; index < funccount; index++) {
|
|
fp = functions[index];
|
|
if (conf->resource_debug & RSCDBG_FUNC_INFO) {
|
|
|
|
math_fmt("%5ld\t%-12s\t", index,
|
|
namestr(&funcnames,index));
|
|
if (fp) {
|
|
count++;
|
|
math_fmt("%-5d\t%-5ld\n",
|
|
fp->f_paramcount, fp->f_opcodecount);
|
|
} else {
|
|
math_str("null\t0\n");
|
|
}
|
|
} else {
|
|
if (fp == NULL)
|
|
continue;
|
|
count++;
|
|
math_fmt("%-12s\t%-2d\n", namestr(&funcnames,
|
|
index), fp->f_paramcount);
|
|
}
|
|
}
|
|
}
|
|
if (conf->resource_debug & RSCDBG_FUNC_INFO) {
|
|
math_fmt("\nNumber non-null: %ld\n", count);
|
|
math_fmt("Number null: %ld\n", funccount - count);
|
|
math_fmt("Total number: %ld\n", funccount);
|
|
} else {
|
|
if (count > 0)
|
|
math_fmt("\nNumber: %ld\n", count);
|
|
else
|
|
math_str("No user functions defined\n");
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* Initialize a function for definition.
|
|
* Newflag is true if we should allocate a new function structure,
|
|
* instead of the usual overwriting of the template function structure.
|
|
* The new structure is returned in the global curfunc variable.
|
|
*
|
|
* given:
|
|
* name name of function
|
|
* newflag true if need new structure
|
|
*/
|
|
void
|
|
beginfunc(char *name, bool newflag)
|
|
{
|
|
register FUNC *fp; /* current function */
|
|
|
|
newindex = adduserfunc(name);
|
|
maxopcodes = OPCODEALLOCSIZE;
|
|
fp = functemplate;
|
|
if (newflag) {
|
|
fp = (FUNC *) malloc(funcsize(maxopcodes));
|
|
if (fp == NULL) {
|
|
math_error("Cannot allocate temporary function");
|
|
not_reached();
|
|
}
|
|
}
|
|
fp->f_next = NULL;
|
|
fp->f_localcount = 0;
|
|
fp->f_opcodecount = 0;
|
|
fp->f_savedvalue.v_type = V_NULL;
|
|
fp->f_savedvalue.v_subtype = V_NOSUBTYPE;
|
|
newname = namestr(&funcnames, newindex);
|
|
fp->f_name = newname;
|
|
curfunc = fp;
|
|
initlocals();
|
|
initlabels();
|
|
oldop = OP_NOP;
|
|
oldoldop = OP_NOP;
|
|
debugline = 0;
|
|
errorcount = 0;
|
|
}
|
|
|
|
|
|
/*
|
|
* Commit the just defined function for use.
|
|
* This replaces any existing definition for the function.
|
|
* This should only be called for normal user-defined functions.
|
|
*/
|
|
void
|
|
endfunc(void)
|
|
{
|
|
register FUNC *fp; /* function just finished */
|
|
size_t size; /* size of just created function */
|
|
unsigned long index;
|
|
|
|
if (oldop != OP_RETURN) {
|
|
addop(OP_UNDEF);
|
|
addop(OP_RETURN);
|
|
}
|
|
|
|
checklabels();
|
|
|
|
if (errorcount) {
|
|
scanerror(T_NULL,"Compilation of \"%s\" failed: %ld error(s)",
|
|
newname, errorcount);
|
|
return;
|
|
}
|
|
size = funcsize(curfunc->f_opcodecount);
|
|
fp = (FUNC *) malloc(size);
|
|
if (fp == NULL) {
|
|
math_error("Cannot commit function");
|
|
not_reached();
|
|
}
|
|
memcpy((char *) fp, (char *) curfunc, size);
|
|
if (curfunc != functemplate)
|
|
free(curfunc);
|
|
if (newname[0] != '*' && (conf->traceflags & TRACE_FNCODES)) {
|
|
dumpnames = true;
|
|
for (size = 0; size < fp->f_opcodecount; ) {
|
|
printf("%ld: ", (unsigned long)size);
|
|
size += dumpop(&fp->f_opcodes[size]);
|
|
}
|
|
}
|
|
if ((inputisterminal() && conf->resource_debug & RSCDBG_STDIN_FUNC) ||
|
|
(!inputisterminal() && conf->resource_debug & RSCDBG_FILE_FUNC)) {
|
|
printf("%s(", newname);
|
|
for (index = 0; index < fp->f_paramcount; index++) {
|
|
if (index)
|
|
putchar(',');
|
|
printf("%s", paramname(index));
|
|
}
|
|
printf(") ");
|
|
if (functions[newindex])
|
|
printf("re");
|
|
printf("defined\n");
|
|
}
|
|
if (functions[newindex]) {
|
|
freenumbers(functions[newindex]);
|
|
free(functions[newindex]);
|
|
}
|
|
functions[newindex] = fp;
|
|
}
|
|
|
|
|
|
/*
|
|
* Find the user function with the specified name, and return its index.
|
|
* If the function does not exist, its name is added to the function table
|
|
* and an error will be generated when it is called if it is still undefined.
|
|
*
|
|
* given:
|
|
* name name of function
|
|
*/
|
|
long
|
|
adduserfunc(char *name)
|
|
{
|
|
long index; /* index of function */
|
|
|
|
index = findstr(&funcnames, name);
|
|
if (index >= 0)
|
|
return index;
|
|
if (funccount >= funcavail) {
|
|
functions = (FUNC **) realloc(functions,
|
|
sizeof(FUNC *) * (funcavail + FUNCALLOCSIZE));
|
|
if (functions == NULL) {
|
|
math_error("Failed to reallocate function table");
|
|
not_reached();
|
|
}
|
|
funcavail += FUNCALLOCSIZE;
|
|
}
|
|
if (addstr(&funcnames, name) == NULL) {
|
|
math_error("Cannot save function name");
|
|
not_reached();
|
|
}
|
|
index = funccount++;
|
|
functions[index] = NULL;
|
|
return index;
|
|
}
|
|
|
|
/*
|
|
* Remove user defined function
|
|
*/
|
|
void
|
|
rmuserfunc(char *name)
|
|
{
|
|
long index; /* index of function */
|
|
|
|
index = findstr(&funcnames, name);
|
|
if (index < 0) {
|
|
warning("No function named \"%s\" to be undefined", name);
|
|
return;
|
|
}
|
|
if (functions[index] == NULL) {
|
|
warning("No defined function \"%s\" to be undefined", name);
|
|
return;
|
|
}
|
|
freenumbers(functions[index]);
|
|
free(functions[index]);
|
|
if ((inputisterminal() && conf->resource_debug & RSCDBG_STDIN_FUNC) ||
|
|
(!inputisterminal() && conf->resource_debug & RSCDBG_FILE_FUNC))
|
|
printf("%s() undefined\n", name);
|
|
functions[index] = NULL;
|
|
}
|
|
|
|
|
|
/*
|
|
* Free memory used to store function and its constants
|
|
*/
|
|
void
|
|
freefunc(FUNC *fp)
|
|
{
|
|
long index;
|
|
unsigned long i;
|
|
|
|
if (fp == NULL)
|
|
return;
|
|
if (fp == curfunc) {
|
|
index = newindex;
|
|
} else {
|
|
for (index = 0; index < funccount; index++) {
|
|
if (functions[index] == fp)
|
|
break;
|
|
}
|
|
if (index == funccount) {
|
|
math_error("Bad call to freefunc!!!");
|
|
not_reached();
|
|
}
|
|
}
|
|
if (newname[0] != '*' && (conf->traceflags & TRACE_FNCODES)) {
|
|
printf("Freeing function \"%s\"\n",namestr(&funcnames,index));
|
|
dumpnames = false;
|
|
for (i = 0; i < fp->f_opcodecount; ) {
|
|
printf("%ld: ", i);
|
|
i += dumpop(&fp->f_opcodes[i]);
|
|
}
|
|
}
|
|
freenumbers(fp);
|
|
if (fp != functemplate)
|
|
free(fp);
|
|
}
|
|
|
|
|
|
void
|
|
rmalluserfunc(void)
|
|
{
|
|
FUNC *fp;
|
|
long index;
|
|
|
|
for (index = 0; index < funccount; index++) {
|
|
fp = functions[index];
|
|
if (fp) {
|
|
freefunc(fp);
|
|
functions[index] = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* get index of defined user function with specified name, or -1 if there
|
|
* is none or if it has been undefined
|
|
*/
|
|
long
|
|
getuserfunc(char *name)
|
|
{
|
|
long index;
|
|
|
|
index = findstr(&funcnames, name);
|
|
if (index >= 0 && functions[index] != NULL)
|
|
return index;
|
|
return -1L;
|
|
}
|
|
|
|
|
|
/*
|
|
* Clear any optimization that may be done for the next opcode.
|
|
* This is used when defining a label.
|
|
*/
|
|
void
|
|
clearopt(void)
|
|
{
|
|
oldop = OP_NOP;
|
|
oldoldop = OP_NOP;
|
|
debugline = 0;
|
|
}
|
|
|
|
|
|
/*
|
|
* Find a function structure given its index.
|
|
*/
|
|
FUNC *
|
|
findfunc(long index)
|
|
{
|
|
if (index >= funccount) {
|
|
math_error("Undefined function");
|
|
not_reached();
|
|
}
|
|
return functions[index];
|
|
}
|
|
|
|
|
|
/*
|
|
* Return the name of a function given its index.
|
|
*/
|
|
char *
|
|
namefunc(long index)
|
|
{
|
|
return namestr(&funcnames, index);
|
|
}
|
|
|
|
|
|
/*
|
|
* Let a matrix indexing operation know that it will be treated as a write
|
|
* reference instead of just as a read reference.
|
|
*/
|
|
void
|
|
writeindexop(void)
|
|
{
|
|
if (oldop == OP_INDEXADDR)
|
|
curfunc->f_opcodes[curfunc->f_opcodecount - 1] = true;
|
|
}
|
|
|
|
|
|
/*
|
|
* Add an opcode to the current function being compiled.
|
|
* Note: This can change the curfunc global variable when the
|
|
* function needs expanding.
|
|
*/
|
|
void
|
|
addop(long op)
|
|
{
|
|
register FUNC *fp; /* current function */
|
|
NUMBER *q, *q1, *q2;
|
|
unsigned long count;
|
|
bool cut;
|
|
int diff;
|
|
|
|
fp = curfunc;
|
|
count = fp->f_opcodecount;
|
|
cut = true;
|
|
diff = 2;
|
|
q = NULL;
|
|
if ((count + 5) >= maxopcodes) {
|
|
maxopcodes += OPCODEALLOCSIZE;
|
|
fp = (FUNC *) malloc(funcsize(maxopcodes));
|
|
if (fp == NULL) {
|
|
math_error("cannot malloc function");
|
|
not_reached();
|
|
}
|
|
memcpy((char *) fp, (char *) curfunc,
|
|
funcsize(curfunc->f_opcodecount));
|
|
if (curfunc != functemplate)
|
|
free(curfunc);
|
|
curfunc = fp;
|
|
}
|
|
|
|
/*
|
|
* Check the current opcode against the previous opcode and try to
|
|
* slightly optimize the code depending on the various combinations.
|
|
*/
|
|
switch (op) {
|
|
case OP_GETVALUE:
|
|
switch (oldop) {
|
|
case OP_NUMBER:
|
|
case OP_ZERO:
|
|
case OP_ONE:
|
|
case OP_IMAGINARY:
|
|
case OP_GETEPSILON:
|
|
case OP_SETEPSILON:
|
|
case OP_STRING:
|
|
case OP_UNDEF:
|
|
case OP_GETCONFIG:
|
|
case OP_SETCONFIG:
|
|
return;
|
|
case OP_DUPLICATE:
|
|
diff = 1;
|
|
oldop = OP_DUPVALUE;
|
|
break;
|
|
case OP_FIADDR:
|
|
diff = 1;
|
|
oldop = OP_FIVALUE;
|
|
break;
|
|
case OP_GLOBALADDR:
|
|
diff = 1 + PTR_SIZE;
|
|
oldop = OP_GLOBALVALUE;
|
|
break;
|
|
case OP_LOCALADDR:
|
|
oldop = OP_LOCALVALUE;
|
|
break;
|
|
case OP_PARAMADDR:
|
|
oldop = OP_PARAMVALUE;
|
|
break;
|
|
case OP_ELEMADDR:
|
|
oldop = OP_ELEMVALUE;
|
|
break;
|
|
default:
|
|
cut = false;
|
|
|
|
}
|
|
if (cut) {
|
|
fp->f_opcodes[count - diff] = oldop;
|
|
return;
|
|
}
|
|
break;
|
|
case OP_POP:
|
|
switch (oldop) {
|
|
case OP_ASSIGN:
|
|
fp->f_opcodes[count-1] = OP_ASSIGNPOP;
|
|
oldop = OP_ASSIGNPOP;
|
|
return;
|
|
case OP_NUMBER:
|
|
case OP_IMAGINARY:
|
|
q = constvalue(fp->f_opcodes[count-1]);
|
|
qfree(q);
|
|
break;
|
|
case OP_STRING:
|
|
sfree(findstring((long)fp->f_opcodes[count-1]));
|
|
break;
|
|
case OP_LOCALADDR:
|
|
case OP_PARAMADDR:
|
|
break;
|
|
case OP_GLOBALADDR:
|
|
diff = 1 + PTR_SIZE;
|
|
break;
|
|
case OP_UNDEF:
|
|
fp->f_opcodecount -= 1;
|
|
oldop = OP_NOP;
|
|
oldoldop = OP_NOP;
|
|
return;
|
|
default:
|
|
cut = false;
|
|
}
|
|
if (cut) {
|
|
fp->f_opcodecount -= diff;
|
|
oldop = OP_NOP;
|
|
oldoldop = OP_NOP;
|
|
warning("Constant before comma operator");
|
|
return;
|
|
}
|
|
break;
|
|
case OP_NEGATE:
|
|
if (oldop == OP_NUMBER) {
|
|
q = constvalue(fp->f_opcodes[count-1]);
|
|
fp->f_opcodes[count-1] = addqconstant(qneg(q));
|
|
qfree(q);
|
|
return;
|
|
}
|
|
}
|
|
if (oldop == OP_NUMBER) {
|
|
if (oldoldop == OP_NUMBER) {
|
|
q1 = constvalue(fp->f_opcodes[count - 3]);
|
|
q2 = constvalue(fp->f_opcodes[count - 1]);
|
|
switch (op) {
|
|
case OP_DIV:
|
|
if (qiszero(q2)) {
|
|
cut = false;
|
|
break;
|
|
}
|
|
q = qqdiv(q1,q2);
|
|
break;
|
|
case OP_MUL:
|
|
q = qmul(q1,q2);
|
|
break;
|
|
case OP_ADD:
|
|
q = qqadd(q1,q2);
|
|
break;
|
|
case OP_SUB:
|
|
q = qsub(q1,q2);
|
|
break;
|
|
case OP_POWER:
|
|
if (qisfrac(q2) || qisneg(q2))
|
|
cut = false;
|
|
else
|
|
q = qpowi(q1,q2);
|
|
break;
|
|
default:
|
|
cut = false;
|
|
}
|
|
if (cut) {
|
|
qfree(q1);
|
|
qfree(q2);
|
|
fp->f_opcodes[count - 3] = addqconstant(q);
|
|
fp->f_opcodecount -= 2;
|
|
oldoldop = OP_NOP;
|
|
return;
|
|
}
|
|
} else if (op != OP_NUMBER) {
|
|
q = constvalue(fp->f_opcodes[count - 1]);
|
|
if (op == OP_POWER) {
|
|
if (qcmpi(q, 2L) == 0) {
|
|
fp->f_opcodecount--;
|
|
fp->f_opcodes[count - 2] = OP_SQUARE;
|
|
qfree(q);
|
|
oldop = OP_SQUARE;
|
|
return;
|
|
}
|
|
if (qcmpi(q, 4L) == 0) {
|
|
fp->f_opcodes[count - 2] = OP_SQUARE;
|
|
fp->f_opcodes[count - 1] = OP_SQUARE;
|
|
qfree(q);
|
|
oldop = OP_SQUARE;
|
|
return;
|
|
}
|
|
}
|
|
if (qiszero(q)) {
|
|
qfree(q);
|
|
fp->f_opcodes[count - 2] = OP_ZERO;
|
|
fp->f_opcodecount--;
|
|
} else if (qisone(q)) {
|
|
qfree(q);
|
|
fp->f_opcodes[count - 2] = OP_ONE;
|
|
fp->f_opcodecount--;
|
|
}
|
|
}
|
|
}
|
|
/*
|
|
* No optimization possible, so store the opcode.
|
|
*/
|
|
fp->f_opcodes[fp->f_opcodecount] = op;
|
|
fp->f_opcodecount++;
|
|
oldoldop = oldop;
|
|
oldop = op;
|
|
}
|
|
|
|
|
|
/*
|
|
* Add an opcode and and one integer argument to the current function
|
|
* being compiled.
|
|
*/
|
|
void
|
|
addopone(long op, long arg)
|
|
{
|
|
if (op == OP_DEBUG) {
|
|
if ((conf->traceflags & TRACE_NODEBUG) || (arg == debugline))
|
|
return;
|
|
debugline = arg;
|
|
if (oldop == OP_DEBUG) {
|
|
curfunc->f_opcodes[curfunc->f_opcodecount - 1] = arg;
|
|
return;
|
|
}
|
|
}
|
|
addop(op);
|
|
curfunc->f_opcodes[curfunc->f_opcodecount] = arg;
|
|
curfunc->f_opcodecount++;
|
|
}
|
|
|
|
|
|
/*
|
|
* Add an opcode and and two integer arguments to the current function
|
|
* being compiled.
|
|
*/
|
|
void
|
|
addoptwo(long op, long arg1, long arg2)
|
|
{
|
|
addop(op);
|
|
curfunc->f_opcodes[curfunc->f_opcodecount++] = arg1;
|
|
curfunc->f_opcodes[curfunc->f_opcodecount++] = arg2;
|
|
}
|
|
|
|
|
|
/*
|
|
* Add an opcode and a character pointer to the function being compiled.
|
|
*/
|
|
void
|
|
addopptr(long op, char *ptr)
|
|
{
|
|
char **ptraddr;
|
|
|
|
addop(op);
|
|
ptraddr = (char **) &curfunc->f_opcodes[curfunc->f_opcodecount];
|
|
*ptraddr = ptr;
|
|
curfunc->f_opcodecount += PTR_SIZE;
|
|
}
|
|
|
|
|
|
/*
|
|
* Add an opcode and an index and an argument count for a function call.
|
|
*/
|
|
void
|
|
addopfunction(long op, long index, int count)
|
|
{
|
|
long newop;
|
|
|
|
if ((op == OP_CALL) && ((newop = builtinopcode(index)) != OP_NOP)) {
|
|
if ((newop == OP_SETCONFIG) && (count == 1))
|
|
newop = OP_GETCONFIG;
|
|
if ((newop == OP_SETEPSILON) && (count == 0))
|
|
newop = OP_GETEPSILON;
|
|
if ((newop == OP_ABS) && (count == 1))
|
|
addop(OP_GETEPSILON);
|
|
addop(newop);
|
|
return;
|
|
}
|
|
addop(op);
|
|
curfunc->f_opcodes[curfunc->f_opcodecount++] = index;
|
|
curfunc->f_opcodes[curfunc->f_opcodecount++] = count;
|
|
}
|
|
|
|
|
|
/*
|
|
* Add a jump-type opcode and a label to the function being compiled.
|
|
*
|
|
* given:
|
|
* label label to be added
|
|
*/
|
|
void
|
|
addoplabel(long op, LABEL *label)
|
|
{
|
|
addop(op);
|
|
uselabel(label);
|
|
}
|