1 /*
2  * addop - add opcodes to a function being compiled
3  *
4  * Copyright (C) 1999-2007,2021  David I. Bell and Ernest Bowen
5  *
6  * Primary author:  David I. Bell
7  *
8  * Calc is open software; you can redistribute it and/or modify it under
9  * the terms of the version 2.1 of the GNU Lesser General Public License
10  * as published by the Free Software Foundation.
11  *
12  * Calc is distributed in the hope that it will be useful, but WITHOUT
13  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
14  * or FITNESS FOR A PARTICULAR PURPOSE.	 See the GNU Lesser General
15  * Public License for more details.
16  *
17  * A copy of version 2.1 of the GNU Lesser General Public License is
18  * distributed with calc under the filename COPYING-LGPL.  You should have
19  * received a copy with calc; if not, write to Free Software Foundation, Inc.
20  * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
21  *
22  * Under source code control:	1990/02/15 01:48:10
23  * File existed as early as:	before 1990
24  *
25  * Share and enjoy!  :-)	http://www.isthe.com/chongo/tech/comp/calc/
26  */
27 
28 
29 #include <stdio.h>
30 #include "calc.h"
31 #include "alloc.h"
32 #include "opcodes.h"
33 #include "str.h"
34 #include "func.h"
35 #include "token.h"
36 #include "label.h"
37 #include "symbol.h"
38 
39 
40 #include "banned.h"	/* include after system header <> includes */
41 
42 
43 #define FUNCALLOCSIZE	20	/* reallocate size for functions */
44 #define OPCODEALLOCSIZE 100	/* reallocate size for opcodes in functions */
45 
46 
47 STATIC unsigned long maxopcodes;/* number of opcodes available */
48 STATIC long newindex;		/* index of new function */
49 STATIC char *newname;		/* name of new function */
50 STATIC long oldop;		/* previous opcode */
51 STATIC long oldoldop;		/* opcode before previous opcode */
52 STATIC long debugline;		/* line number of latest debug opcode */
53 STATIC long funccount;		/* number of functions */
54 STATIC long funcavail;		/* available number of functions */
55 STATIC FUNC *functemplate;	/* function definition template */
56 STATIC FUNC **functions;	/* table of functions */
57 STATIC STRINGHEAD funcnames;	/* function names */
58 
59 
60 /*
61  * Initialize the table of user defined functions.
62  */
63 void
initfunctions(void)64 initfunctions(void)
65 {
66 	initstr(&funcnames);
67 	maxopcodes = OPCODEALLOCSIZE;
68 	functemplate = (FUNC *) malloc(funcsize(maxopcodes));
69 	if (functemplate == NULL) {
70 		math_error("Cannot allocate function template");
71 		/*NOTREACHED*/
72 	}
73 	functions = (FUNC **) malloc(sizeof(FUNC *) * FUNCALLOCSIZE);
74 	if (functions == NULL) {
75 		math_error("Cannot allocate function table");
76 		/*NOTREACHED*/
77 	}
78 	funccount = 0;
79 	funcavail = FUNCALLOCSIZE;
80 }
81 
82 
83 /*
84  * Show the list of user defined functions.
85  */
86 void
showfunctions(void)87 showfunctions(void)
88 {
89 	FUNC *fp;		/* current function */
90 	long count;
91 	long index;
92 
93 	count = 0;
94 	if (funccount > 0) {
95 		if (conf->resource_debug & RSCDBG_FUNC_INFO)
96 			math_str("Index\tName        \tArgs\tOpcodes\n"
97 				 "-----\t------     \t---- \t------\n");
98 		else
99 			math_str("Name\tArguments\n"
100 				 "----\t---------\n");
101 		for (index = 0; index < funccount; index++) {
102 			fp = functions[index];
103 			if (conf->resource_debug & RSCDBG_FUNC_INFO) {
104 
105 				math_fmt("%5ld\t%-12s\t", index,
106 					namestr(&funcnames,index));
107 				if (fp) {
108 					count++;
109 					math_fmt("%-5d\t%-5ld\n",
110 					 fp->f_paramcount, fp->f_opcodecount);
111 				} else {
112 					math_str("null\t0\n");
113 				}
114 			} else {
115 				if (fp == NULL)
116 					continue;
117 				count++;
118 				math_fmt("%-12s\t%-2d\n", namestr(&funcnames,
119 					index), fp->f_paramcount);
120 			}
121 		}
122 	}
123 	if (conf->resource_debug & RSCDBG_FUNC_INFO) {
124 		math_fmt("\nNumber non-null: %ld\n", count);
125 		math_fmt("Number null: %ld\n", funccount - count);
126 		math_fmt("Total number: %ld\n", funccount);
127 	} else {
128 		if (count > 0)
129 			math_fmt("\nNumber: %ld\n", count);
130 		else
131 			math_str("No user functions defined\n");
132 	}
133 }
134 
135 
136 /*
137  * Initialize a function for definition.
138  * Newflag is TRUE if we should allocate a new function structure,
139  * instead of the usual overwriting of the template function structure.
140  * The new structure is returned in the global curfunc variable.
141  *
142  * given:
143  *	name		name of function
144  *	newflag		TRUE if need new structure
145  */
146 void
beginfunc(char * name,BOOL newflag)147 beginfunc(char *name, BOOL newflag)
148 {
149 	register FUNC *fp;		/* current function */
150 
151 	newindex = adduserfunc(name);
152 	maxopcodes = OPCODEALLOCSIZE;
153 	fp = functemplate;
154 	if (newflag) {
155 		fp = (FUNC *) malloc(funcsize(maxopcodes));
156 		if (fp == NULL) {
157 			math_error("Cannot allocate temporary function");
158 			/*NOTREACHED*/
159 		}
160 	}
161 	fp->f_next = NULL;
162 	fp->f_localcount = 0;
163 	fp->f_opcodecount = 0;
164 	fp->f_savedvalue.v_type = V_NULL;
165 	fp->f_savedvalue.v_subtype = V_NOSUBTYPE;
166 	newname = namestr(&funcnames, newindex);
167 	fp->f_name = newname;
168 	curfunc = fp;
169 	initlocals();
170 	initlabels();
171 	oldop = OP_NOP;
172 	oldoldop = OP_NOP;
173 	debugline = 0;
174 	errorcount = 0;
175 }
176 
177 
178 /*
179  * Commit the just defined function for use.
180  * This replaces any existing definition for the function.
181  * This should only be called for normal user-defined functions.
182  */
183 void
endfunc(void)184 endfunc(void)
185 {
186 	register FUNC *fp;		/* function just finished */
187 	size_t size;			/* size of just created function */
188 	unsigned long index;
189 
190 	if (oldop != OP_RETURN) {
191 		addop(OP_UNDEF);
192 		addop(OP_RETURN);
193 	}
194 
195 	checklabels();
196 
197 	if (errorcount) {
198 		scanerror(T_NULL,"Compilation of \"%s\" failed: %ld error(s)",
199 			 newname, errorcount);
200 		return;
201 	}
202 	size = funcsize(curfunc->f_opcodecount);
203 	fp = (FUNC *) malloc(size);
204 	if (fp == NULL) {
205 		math_error("Cannot commit function");
206 		/*NOTREACHED*/
207 	}
208 	memcpy((char *) fp, (char *) curfunc, size);
209 	if (curfunc != functemplate)
210 		free(curfunc);
211 	if (newname[0] != '*' && (conf->traceflags & TRACE_FNCODES)) {
212 		dumpnames = TRUE;
213 		for (size = 0; size < fp->f_opcodecount; ) {
214 			printf("%ld: ", (unsigned long)size);
215 			size += dumpop(&fp->f_opcodes[size]);
216 		}
217 	}
218 	if ((inputisterminal() && conf->resource_debug & RSCDBG_STDIN_FUNC) ||
219 	    (!inputisterminal() && conf->resource_debug & RSCDBG_FILE_FUNC)) {
220 		printf("%s(", newname);
221 		for (index = 0; index <	 fp->f_paramcount; index++) {
222 			if (index)
223 				putchar(',');
224 			printf("%s", paramname(index));
225 		}
226 		printf(") ");
227 		if (functions[newindex])
228 			printf("re");
229 		printf("defined\n");
230 	}
231 	if (functions[newindex]) {
232 		freenumbers(functions[newindex]);
233 		free(functions[newindex]);
234 	}
235 	functions[newindex] = fp;
236 }
237 
238 
239 /*
240  * Find the user function with the specified name, and return its index.
241  * If the function does not exist, its name is added to the function table
242  * and an error will be generated when it is called if it is still undefined.
243  *
244  * given:
245  *	name		name of function
246  */
247 long
adduserfunc(char * name)248 adduserfunc(char *name)
249 {
250 	long index;		/* index of function */
251 
252 	index = findstr(&funcnames, name);
253 	if (index >= 0)
254 		return index;
255 	if (funccount >= funcavail) {
256 		functions = (FUNC **) realloc(functions,
257 			sizeof(FUNC *) * (funcavail + FUNCALLOCSIZE));
258 		if (functions == NULL) {
259 			math_error("Failed to reallocate function table");
260 			/*NOTREACHED*/
261 		}
262 		funcavail += FUNCALLOCSIZE;
263 	}
264 	if (addstr(&funcnames, name) == NULL) {
265 		math_error("Cannot save function name");
266 		/*NOTREACHED*/
267 	}
268 	index = funccount++;
269 	functions[index] = NULL;
270 	return index;
271 }
272 
273 /*
274  * Remove user defined function
275  */
276 void
rmuserfunc(char * name)277 rmuserfunc(char *name)
278 {
279 	long index;		/* index of function */
280 
281 	index = findstr(&funcnames, name);
282 	if (index < 0) {
283 		warning("No function named \"%s\" to be undefined", name);
284 		return;
285 	}
286 	if (functions[index] == NULL) {
287 		warning("No defined function \"%s\" to be undefined", name);
288 		return;
289 	}
290 	freenumbers(functions[index]);
291 	free(functions[index]);
292 	if ((inputisterminal() && conf->resource_debug & RSCDBG_STDIN_FUNC) ||
293 	    (!inputisterminal() && conf->resource_debug & RSCDBG_FILE_FUNC))
294 		printf("%s() undefined\n", name);
295 	functions[index] = NULL;
296 }
297 
298 
299 /*
300  * Free memory used to store function and its constants
301  */
302 void
freefunc(FUNC * fp)303 freefunc(FUNC *fp)
304 {
305 	long index;
306 	unsigned long i;
307 
308 	if (fp == NULL)
309 		return;
310 	if (fp == curfunc) {
311 		index = newindex;
312 	} else {
313 		for (index = 0; index < funccount; index++) {
314 			if (functions[index] == fp)
315 				break;
316 		}
317 		if (index == funccount) {
318 			math_error("Bad call to freefunc!!!");
319 			/*NOTREACHED*/
320 		}
321 	}
322 	if (newname[0] != '*' && (conf->traceflags & TRACE_FNCODES)) {
323 		printf("Freeing function \"%s\"\n",namestr(&funcnames,index));
324 		dumpnames = FALSE;
325 		for (i = 0; i < fp->f_opcodecount; ) {
326 			printf("%ld: ", i);
327 			i += dumpop(&fp->f_opcodes[i]);
328 		}
329 	}
330 	freenumbers(fp);
331 	if (fp != functemplate)
332 		free(fp);
333 }
334 
335 
336 void
rmalluserfunc(void)337 rmalluserfunc(void)
338 {
339 	FUNC *fp;
340 	long index;
341 
342 	for (index = 0; index < funccount; index++) {
343 		fp = functions[index];
344 		if (fp) {
345 			freefunc(fp);
346 			functions[index] = NULL;
347 		}
348 	}
349 }
350 
351 
352 /*
353  * get index of defined user function with specified name, or -1 if there
354  * is none or if it has been undefined
355  */
356 long
getuserfunc(char * name)357 getuserfunc(char *name)
358 {
359 	long index;
360 
361 	index = findstr(&funcnames, name);
362 	if (index >= 0 && functions[index] != NULL)
363 		return index;
364 	return -1L;
365 }
366 
367 
368 /*
369  * Clear any optimization that may be done for the next opcode.
370  * This is used when defining a label.
371  */
372 void
clearopt(void)373 clearopt(void)
374 {
375 	oldop = OP_NOP;
376 	oldoldop = OP_NOP;
377 	debugline = 0;
378 }
379 
380 
381 /*
382  * Find a function structure given its index.
383  */
384 FUNC *
findfunc(long index)385 findfunc(long index)
386 {
387 	if (index >= funccount) {
388 		math_error("Undefined function");
389 		/*NOTREACHED*/
390 	}
391 	return functions[index];
392 }
393 
394 
395 /*
396  * Return the name of a function given its index.
397  */
398 char *
namefunc(long index)399 namefunc(long index)
400 {
401 	return namestr(&funcnames, index);
402 }
403 
404 
405 /*
406  * Let a matrix indexing operation know that it will be treated as a write
407  * reference instead of just as a read reference.
408  */
409 void
writeindexop(void)410 writeindexop(void)
411 {
412 	if (oldop == OP_INDEXADDR)
413 		curfunc->f_opcodes[curfunc->f_opcodecount - 1] = TRUE;
414 }
415 
416 
417 /*
418  * Add an opcode to the current function being compiled.
419  * Note: This can change the curfunc global variable when the
420  * function needs expanding.
421  */
422 void
addop(long op)423 addop(long op)
424 {
425 	register FUNC *fp;		/* current function */
426 	NUMBER *q, *q1, *q2;
427 	unsigned long count;
428 	BOOL cut;
429 	int diff;
430 
431 	fp = curfunc;
432 	count = fp->f_opcodecount;
433 	cut = TRUE;
434 	diff = 2;
435 	q = NULL;
436 	if ((count + 5) >= maxopcodes) {
437 		maxopcodes += OPCODEALLOCSIZE;
438 		fp = (FUNC *) malloc(funcsize(maxopcodes));
439 		if (fp == NULL) {
440 			math_error("cannot malloc function");
441 			/*NOTREACHED*/
442 		}
443 		memcpy((char *) fp, (char *) curfunc,
444 			funcsize(curfunc->f_opcodecount));
445 		if (curfunc != functemplate)
446 			free(curfunc);
447 		curfunc = fp;
448 	}
449 
450 	/*
451 	 * Check the current opcode against the previous opcode and try to
452 	 * slightly optimize the code depending on the various combinations.
453 	 */
454 	switch (op) {
455 	case OP_GETVALUE:
456 		switch (oldop) {
457 		case OP_NUMBER:
458 		case OP_ZERO:
459 		case OP_ONE:
460 		case OP_IMAGINARY:
461 		case OP_GETEPSILON:
462 		case OP_SETEPSILON:
463 		case OP_STRING:
464 		case OP_UNDEF:
465 		case OP_GETCONFIG:
466 		case OP_SETCONFIG:
467 			return;
468 		case OP_DUPLICATE:
469 			diff = 1;
470 			oldop = OP_DUPVALUE;
471 			break;
472 		case OP_FIADDR:
473 			diff = 1;
474 			oldop = OP_FIVALUE;
475 			break;
476 		case OP_GLOBALADDR:
477 			diff = 1 + PTR_SIZE;
478 			oldop = OP_GLOBALVALUE;
479 			break;
480 		case OP_LOCALADDR:
481 			oldop = OP_LOCALVALUE;
482 			break;
483 		case OP_PARAMADDR:
484 			oldop = OP_PARAMVALUE;
485 			break;
486 		case OP_ELEMADDR:
487 			oldop = OP_ELEMVALUE;
488 			break;
489 		default:
490 			cut = FALSE;
491 
492 		}
493 		if (cut) {
494 			fp->f_opcodes[count - diff] = oldop;
495 			return;
496 		}
497 		break;
498 	case OP_POP:
499 		switch (oldop) {
500 		case OP_ASSIGN:
501 			fp->f_opcodes[count-1] = OP_ASSIGNPOP;
502 			oldop = OP_ASSIGNPOP;
503 			return;
504 		case OP_NUMBER:
505 		case OP_IMAGINARY:
506 			q = constvalue(fp->f_opcodes[count-1]);
507 			qfree(q);
508 			break;
509 		case OP_STRING:
510 			sfree(findstring((long)fp->f_opcodes[count-1]));
511 			break;
512 		case OP_LOCALADDR:
513 		case OP_PARAMADDR:
514 			break;
515 		case OP_GLOBALADDR:
516 			diff = 1 + PTR_SIZE;
517 			break;
518 		case OP_UNDEF:
519 			fp->f_opcodecount -= 1;
520 			oldop = OP_NOP;
521 			oldoldop = OP_NOP;
522 			return;
523 		default:
524 			cut = FALSE;
525 		}
526 		if (cut) {
527 			fp->f_opcodecount -= diff;
528 			oldop = OP_NOP;
529 			oldoldop = OP_NOP;
530 			warning("Constant before comma operator");
531 			return;
532 		}
533 		break;
534 	case OP_NEGATE:
535 		if (oldop == OP_NUMBER) {
536 			q = constvalue(fp->f_opcodes[count-1]);
537 			fp->f_opcodes[count-1] = addqconstant(qneg(q));
538 			qfree(q);
539 			return;
540 		}
541 	}
542 	if (oldop == OP_NUMBER) {
543 		if (oldoldop == OP_NUMBER) {
544 			q1 = constvalue(fp->f_opcodes[count - 3]);
545 			q2 = constvalue(fp->f_opcodes[count - 1]);
546 			switch (op) {
547 			case OP_DIV:
548 				if (qiszero(q2)) {
549 					cut = FALSE;
550 					break;
551 				}
552 				q = qqdiv(q1,q2);
553 				break;
554 			case OP_MUL:
555 				q = qmul(q1,q2);
556 				break;
557 			case OP_ADD:
558 				q = qqadd(q1,q2);
559 				break;
560 			case OP_SUB:
561 				q = qsub(q1,q2);
562 				break;
563 			case OP_POWER:
564 				if (qisfrac(q2) || qisneg(q2))
565 					cut = FALSE;
566 				else
567 					q = qpowi(q1,q2);
568 				break;
569 			default:
570 				cut = FALSE;
571 			}
572 			if (cut) {
573 				qfree(q1);
574 				qfree(q2);
575 				fp->f_opcodes[count - 3] = addqconstant(q);
576 				fp->f_opcodecount -= 2;
577 				oldoldop = OP_NOP;
578 				return;
579 			}
580 		} else if (op != OP_NUMBER) {
581 			q = constvalue(fp->f_opcodes[count - 1]);
582 			if (op == OP_POWER) {
583 				if (qcmpi(q, 2L) == 0) {
584 					fp->f_opcodecount--;
585 					fp->f_opcodes[count - 2] = OP_SQUARE;
586 					qfree(q);
587 					oldop = OP_SQUARE;
588 					return;
589 				}
590 				if (qcmpi(q, 4L) == 0) {
591 					fp->f_opcodes[count - 2] = OP_SQUARE;
592 					fp->f_opcodes[count - 1] = OP_SQUARE;
593 					qfree(q);
594 					oldop = OP_SQUARE;
595 					return;
596 				}
597 			}
598 			if (qiszero(q)) {
599 				qfree(q);
600 				fp->f_opcodes[count - 2] = OP_ZERO;
601 				fp->f_opcodecount--;
602 			} else if (qisone(q)) {
603 				qfree(q);
604 				fp->f_opcodes[count - 2] = OP_ONE;
605 				fp->f_opcodecount--;
606 			}
607 		}
608 	}
609 	/*
610 	 * No optimization possible, so store the opcode.
611 	 */
612 	fp->f_opcodes[fp->f_opcodecount] = op;
613 	fp->f_opcodecount++;
614 	oldoldop = oldop;
615 	oldop = op;
616 }
617 
618 
619 /*
620  * Add an opcode and and one integer argument to the current function
621  * being compiled.
622  */
623 void
addopone(long op,long arg)624 addopone(long op, long arg)
625 {
626 	if (op == OP_DEBUG) {
627 		if ((conf->traceflags & TRACE_NODEBUG) || (arg == debugline))
628 			return;
629 		debugline = arg;
630 		if (oldop == OP_DEBUG) {
631 			curfunc->f_opcodes[curfunc->f_opcodecount - 1] = arg;
632 			return;
633 		}
634 	}
635 	addop(op);
636 	curfunc->f_opcodes[curfunc->f_opcodecount] = arg;
637 	curfunc->f_opcodecount++;
638 }
639 
640 
641 /*
642  * Add an opcode and and two integer arguments to the current function
643  * being compiled.
644  */
645 void
addoptwo(long op,long arg1,long arg2)646 addoptwo(long op, long arg1, long arg2)
647 {
648 	addop(op);
649 	curfunc->f_opcodes[curfunc->f_opcodecount++] = arg1;
650 	curfunc->f_opcodes[curfunc->f_opcodecount++] = arg2;
651 }
652 
653 
654 /*
655  * Add an opcode and a character pointer to the function being compiled.
656  */
657 void
addopptr(long op,char * ptr)658 addopptr(long op, char *ptr)
659 {
660 	char **ptraddr;
661 
662 	addop(op);
663 	ptraddr = (char **) &curfunc->f_opcodes[curfunc->f_opcodecount];
664 	*ptraddr = ptr;
665 	curfunc->f_opcodecount += PTR_SIZE;
666 }
667 
668 
669 /*
670  * Add an opcode and an index and an argument count for a function call.
671  */
672 void
addopfunction(long op,long index,int count)673 addopfunction(long op, long index, int count)
674 {
675 	long newop;
676 
677 	if ((op == OP_CALL) && ((newop = builtinopcode(index)) != OP_NOP)) {
678 		if ((newop == OP_SETCONFIG) && (count == 1))
679 			newop = OP_GETCONFIG;
680 		if ((newop == OP_SETEPSILON) && (count == 0))
681 			newop = OP_GETEPSILON;
682 		if ((newop == OP_ABS) && (count == 1))
683 			addop(OP_GETEPSILON);
684 		addop(newop);
685 		return;
686 	}
687 	addop(op);
688 	curfunc->f_opcodes[curfunc->f_opcodecount++] = index;
689 	curfunc->f_opcodes[curfunc->f_opcodecount++] = count;
690 }
691 
692 
693 /*
694  * Add a jump-type opcode and a label to the function being compiled.
695  *
696  * given:
697  *	label		label to be added
698  */
699 void
addoplabel(long op,LABEL * label)700 addoplabel(long op, LABEL *label)
701 {
702 	addop(op);
703 	uselabel(label);
704 }
705