/* * Copyright (c) 1993 David I. Bell * Permission is granted to use, distribute, or modify this source, * provided that this copyright notice remains intact. * * Built-in functions implemented here */ #include <sys/types.h> #include <sys/times.h> #include <time.h> #include "calc.h" #include "opcodes.h" #include "token.h" #include "func.h" #include "string.h" /* if HZ & CLK_TCK are not defined, pick typical values, hope for the best */ #if !defined(HZ) # define HZ 60 #endif #if !defined(CLK_TCK) # undef CLK_TCK # define CLK_TCK HZ #endif extern int errno; /* * Totally numeric functions. */ static NUMBER *f_cfsim(); /* simplify number using continued fractions */ static NUMBER *f_ilog(); /* return log of one number to another */ static NUMBER *f_faccnt(); /* count of divisions */ static NUMBER *f_min(); /* minimum of several arguments */ static NUMBER *f_max(); /* maximum of several arguments */ static NUMBER *f_hmean(); /* harmonic mean */ static NUMBER *f_trunc(); /* truncate number to specified decimal places */ static NUMBER *f_btrunc(); /* truncate number to specified binary places */ static NUMBER *f_gcd(); /* greatest common divisor */ static NUMBER *f_lcm(); /* least common multiple */ static NUMBER *f_xor(); /* xor of several arguments */ static NUMBER *f_ceil(); /* ceiling of a fraction */ static NUMBER *f_floor(); /* floor of a fraction */ static NUMBER *f_meq(); /* numbers are same modular value */ static NUMBER *f_isrel(); /* two numbers are relatively prime */ static NUMBER *f_ismult(); /* whether one number divides another */ static NUMBER *f_mne(); /* whether a and b are not equal modulo c */ static NUMBER *f_isset(); /* tests if a bit of a num (base 2) is set */ static NUMBER *f_highbit(); /* high bit number in base 2 representation */ static NUMBER *f_lowbit(); /* low bit number in base 2 representation */ static NUMBER *f_near(); /* whether two numbers are near each other */ static NUMBER *f_legtoleg(); /* positive form of leg to leg */ static NUMBER *f_ilog10(); /* integer log of number base 10 */ static NUMBER *f_ilog2(); /* integer log of number base 2 */ static NUMBER *f_digits(); /* number of digits of number */ static NUMBER *f_digit(); /* digit at specified decimal place of number */ static NUMBER *f_places(); /* number of decimal places of number */ static NUMBER *f_primetest(); /* primality test */ static NUMBER *f_issquare(); /* whether number is a square */ static NUMBER *f_runtime(); /* user runtime in seconds */ /* * General functions. */ static VALUE f_bround(); /* round number to specified binary places */ static VALUE f_round(); /* round number to specified decimal places */ static VALUE f_det(); /* determinant of matrix */ static VALUE f_mattrans(); /* return transpose of matrix */ static VALUE f_matdim(); /* dimension of matrix */ static VALUE f_matmax(); /* maximum index of matrix dimension */ static VALUE f_matmin(); /* minimum index of matrix dimension */ static VALUE f_matfill(); /* fill matrix with values */ static VALUE f_listpush(); /* push element onto front of list */ static VALUE f_listpop(); /* pop element from front of list */ static VALUE f_listappend(); /* append element to end of list */ static VALUE f_listremove(); /* remove element from end of list */ static VALUE f_listinsert(); /* insert element into list */ static VALUE f_listdelete(); /* delete element from list */ static VALUE f_strlen(); /* length of string */ static VALUE f_char(); /* character value of integer */ static VALUE f_substr(); /* extract substring */ static VALUE f_strcat(); /* concatenate strings */ static VALUE f_ord(); /* get ordinal value for character */ static VALUE f_avg(); /* average of several arguments */ static VALUE f_ssq(); /* sum of squares */ static VALUE f_poly(); /* result of evaluating polynomial */ static VALUE f_sqrt(); /* square root of a number */ static VALUE f_root(); /* number taken to root of another */ static VALUE f_exp(); /* complex exponential */ static VALUE f_ln(); /* complex natural logarithm */ static VALUE f_power(); /* one value to another power */ static VALUE f_cos(); /* complex cosine */ static VALUE f_sin(); /* complex sine */ static VALUE f_polar(); /* polar representation of complex number */ static VALUE f_arg(); /* argument of complex number */ static VALUE f_list(); /* create a list */ static VALUE f_size(); /* number of elements in object */ static VALUE f_search(); /* search matrix or list for match */ static VALUE f_rsearch(); /* search matrix or list backwards for match */ static VALUE f_cp(); /* cross product of vectors */ static VALUE f_dp(); /* dot product of vectors */ static VALUE f_prompt(); /* prompt for input line */ static VALUE f_eval(); /* evaluate string into value */ static VALUE f_str(); /* convert value to string */ static VALUE f_fopen(); /* open file for reading or writing */ static VALUE f_fprintf(); /* print data to file */ static VALUE f_strprintf(); /* return printed data as a string */ static VALUE f_fgetline(); /* read next line from file */ static VALUE f_fgetc(); /* read next char from file */ static VALUE f_fflush(); /* flush output to file */ static VALUE f_printf(); /* print data to stdout */ static VALUE f_fclose(); /* close file */ static VALUE f_ferror(); /* whether error occurred */ static VALUE f_feof(); /* whether end of file reached */ static VALUE f_files(); /* return file handle or number of files */ #define IN 100 /* maximum number of arguments */ #define FE 0x01 /* flag to indicate default epsilon argument */ #define FA 0x02 /* preserve addresses of variables */ /* * List of primitive built-in functions */ static struct builtin { char *b_name; /* name of built-in function */ short b_minargs; /* minimum number of arguments */ short b_maxargs; /* maximum number of arguments */ short b_flags; /* special handling flags */ short b_opcode; /* opcode which makes the call quick */ NUMBER *(*b_numfunc)(); /* routine to calculate numeric function */ VALUE (*b_valfunc)(); /* routine to calculate general values */ char *b_desc; /* description of function */ } builtins[] = { "abs", 1, 2, 0, OP_ABS, 0, 0, "absolute value within accuracy b", "acos", 1, 2, FE, OP_NOP, qacos, 0, "arccosine of a within accuracy b", "acosh", 1, 2, FE, OP_NOP, qacosh, 0, "hyperbolic arccosine of a within accuracy b", "append", 2, 2, FA, OP_NOP, 0, f_listappend, "append value to end of list", "appr", 1, 2, FE, OP_NOP, qbappr, 0, "approximate a with simpler fraction to within b", "arg", 1, 2, 0, OP_NOP, 0, f_arg, "argument (the angle) of complex number", "asin", 1, 2, FE, OP_NOP, qasin, 0, "arcsine of a within accuracy b", "asinh", 1, 2, FE, OP_NOP, qasinh, 0, "hyperbolic arcsine of a within accuracy b", "atan", 1, 2, FE, OP_NOP, qatan, 0, "arctangent of a within accuracy b", "atan2", 2, 3, FE, OP_NOP, qatan2, 0, "angle to point (b,a) within accuracy c", "atanh", 1, 2, FE, OP_NOP, qatanh, 0, "hyperbolic arctangent of a within accuracy b", "avg", 1, IN, 0, OP_NOP, 0, f_avg, "arithmetic mean of values", "bround", 1, 2, 0, OP_NOP, 0, f_bround, "round value a to b number of binary places", "btrunc", 1, 2, 0, OP_NOP, f_btrunc, 0, "truncate a to b number of binary places", "ceil", 1, 1, 0, OP_NOP, f_ceil, 0, "smallest integer greater than or equal to number", "cfappr", 1, 2, FE, OP_NOP, qcfappr, 0, "approximate a within accuracy b using continued fractions", "cfsim", 1, 1, 0, OP_NOP, f_cfsim, 0, "simplify number using continued fractions", "char", 1, 1, 0, OP_NOP, 0, f_char, "character corresponding to integer value", "cmp", 2, 2, 0, OP_CMP, 0, 0, "compare values returning -1, 0, or 1", "comb", 2, 2, 0, OP_NOP, qcomb, 0, "combinatorial number a!/b!(a-b)!", "config", 1, 2, 0, OP_SETCONFIG, 0, 0, "set or read configuration value", "conj", 1, 1, 0, OP_CONJUGATE, 0, 0, "complex conjugate of value", "cos", 1, 2, 0, OP_NOP, 0, f_cos, "cosine of value a within accuracy b", "cosh", 1, 2, FE, OP_NOP, qcosh, 0, "hyperbolic cosine of a within accuracy b", "cp", 2, 2, 0, OP_NOP, 0, f_cp, "Cross product of two vectors", "delete", 2, 2, FA, OP_NOP, 0, f_listdelete, "delete element from list a at position b", "den", 1, 1, 0, OP_DENOMINATOR, qden, 0, "denominator of fraction", "det", 1, 1, 0, OP_NOP, 0, f_det, "determinant of matrix", "digit", 2, 2, 0, OP_NOP, f_digit, 0, "digit at specified decimal place of number", "digits", 1, 1, 0, OP_NOP, f_digits, 0, "number of digits in number", "dp", 2, 2, 0, OP_NOP, 0, f_dp, "Dot product of two vectors", "epsilon", 0, 1, 0, OP_SETEPSILON, 0, 0, "set or read allowed error for real calculations", "eval", 1, 1, 0, OP_NOP, 0, f_eval, "Evaluate expression from string to value", "exp", 1, 2, 0, OP_NOP, 0, f_exp, "exponential of value a within accuracy b", "fcnt", 2, 2, 0, OP_NOP, f_faccnt, 0, "count of times one number divides another", "fib", 1, 1, 0, OP_NOP, qfib, 0, "Fibonacci number F(n)", "frem", 2, 2, 0, OP_NOP, qfacrem, 0, "number with all occurrences of factor removed", "fact", 1, 1, 0, OP_NOP, qfact, 0, "factorial", "fclose", 1, 1, 0, OP_NOP, 0, f_fclose, "close file", "feof", 1, 1, 0, OP_NOP, 0, f_feof, "whether EOF reached for file", "ferror", 1, 1, 0, OP_NOP, 0, f_ferror, "whether error occurred for file", "fflush", 1, 1, 0, OP_NOP, 0, f_fflush, "flush output to file", "fgetc", 1, 1, 0, OP_NOP, 0, f_fgetc, "read next char from file", "fgetline", 1, 1, 0, OP_NOP, 0, f_fgetline, "read next line from file", "files", 0, 1, 0, OP_NOP, 0, f_files, "return opened file or max number of opened files", "floor", 1, 1, 0, OP_NOP, f_floor, 0, "greatest integer less than or equal to number", "fopen", 2, 2, 0, OP_NOP, 0, f_fopen, "open file name a in mode b", "fprintf", 2, IN, 0, OP_NOP, 0, f_fprintf, "print formatted output to opened file", "frac", 1, 1, 0, OP_FRAC, qfrac, 0, "fractional part of value", "gcd", 1,IN, 0, OP_NOP, f_gcd, 0, "greatest common divisor", "gcdrem", 2, 2, 0, OP_NOP, qgcdrem, 0, "a divided repeatedly by gcd with b", "highbit", 1, 1, 0, OP_NOP, f_highbit, 0, "high bit number in base 2 representation", "hmean", 1,IN, 0, OP_NOP, f_hmean, 0, "harmonic mean of values", "hypot", 2, 3, FE, OP_NOP, qhypot, 0, "hypotenuse of right triangle within accuracy c", "ilog", 2, 2, 0, OP_NOP, f_ilog, 0, "integral log of one number with another", "ilog10", 1, 1, 0, OP_NOP, f_ilog10, 0, "integral log of a number base 10", "ilog2", 1, 1, 0, OP_NOP, f_ilog2, 0, "integral log of a number base 2", "im", 1, 1, 0, OP_IM, 0, 0, "imaginary part of complex number", "insert", 3, 3, FA, OP_NOP, 0, f_listinsert, "insert value c into list a at position b", "int", 1, 1, 0, OP_INT, qint, 0, "integer part of value", "inverse", 1, 1, 0, OP_INVERT, 0, 0, "multiplicative inverse of value", "iroot", 2, 2, 0, OP_NOP, qiroot, 0, "integer b'th root of a", "iseven", 1, 1, 0, OP_ISEVEN, 0, 0, "whether a value is an even integer", "isfile", 1, 1, 0, OP_ISFILE, 0, 0, "whether a value is a file", "isint", 1, 1, 0, OP_ISINT, 0, 0, "whether a value is an integer", "islist", 1, 1, 0, OP_ISLIST, 0, 0, "whether a value is a list", "ismat", 1, 1, 0, OP_ISMAT, 0, 0, "whether a value is a matrix", "ismult", 2, 2, 0, OP_NOP, f_ismult, 0, "whether a is a multiple of b", "isnull", 1, 1, 0, OP_ISNULL, 0, 0, "whether a value is the null value", "isnum", 1, 1, 0, OP_ISNUM, 0, 0, "whether a value is a number", "isobj", 1, 1, 0, OP_ISOBJ, 0, 0, "whether a value is an object", "isodd", 1, 1, 0, OP_ISODD, 0, 0, "whether a value is an odd integer", "isqrt", 1, 1, 0, OP_NOP, qisqrt, 0, "integer part of square root", "isreal", 1, 1, 0, OP_ISREAL, 0, 0, "whether a value is a real number", "isset", 2, 2, 0, OP_NOP, f_isset, 0, "whether bit b of abs(a) (in base 2) is set", "isstr", 1, 1, 0, OP_ISSTR, 0, 0, "whether a value is a string", "isrel", 2, 2, 0, OP_NOP, f_isrel, 0, "whether two numbers are relatively prime", "issimple", 1, 1, 0, OP_ISSIMPLE, 0, 0, "whether value is a simple type", "issq", 1, 1, 0, OP_NOP, f_issquare, 0, "whether or not number is a square", "istype", 2, 2, 0, OP_ISTYPE, 0, 0, "whether the type of a is same as the type of b", "jacobi", 2, 2, 0, OP_NOP, qjacobi, 0, "-1 => a is not quadratic residue mod b\n\t\t 1 => b is composite, or a is quad residue of b", "lcm", 1, IN, 0, OP_NOP, f_lcm, 0, "least common multiple", "lcmfact", 1, 1, 0, OP_NOP, qlcmfact, 0, "lcm of all integers up till number", "lfactor", 2, 2, 0, OP_NOP, qlowfactor, 0, "lowest prime factor of a in first b primes", "list", 0, IN, 0, OP_NOP, 0, f_list, "create list of specified values", "ln", 1, 2, 0, OP_NOP, 0, f_ln, "natural logarithm of value a within accuracy b", "lowbit", 1, 1, 0, OP_NOP, f_lowbit, 0, "low bit number in base 2 representation", "ltol", 1, 2, FE, OP_NOP, f_legtoleg, 0, "leg-to-leg of unit right triangle (sqrt(1 - a^2))", "matdim", 1, 1, 0, OP_NOP, 0, f_matdim, "number of dimensions of matrix", "matfill", 2, 3, FA, OP_NOP, 0, f_matfill, "fill matrix with value b (value c on diagonal)", "matmax", 2, 2, 0, OP_NOP, 0, f_matmax, "maximum index of matrix a dim b", "matmin", 2, 2, 0, OP_NOP, 0, f_matmin, "minimum index of matrix a dim b", "mattrans", 1, 1, 0, OP_NOP, 0, f_mattrans, "transpose of matrix", "max", 1, IN, 0, OP_NOP, f_max, 0, "maximum value", "meq", 3, 3, 0, OP_NOP, f_meq, 0, "whether a and b are equal modulo c", "min", 1, IN, 0, OP_NOP, f_min, 0, "minimum value", "minv", 2, 2, 0, OP_NOP, qminv, 0, "inverse of a modulo b", "mmin", 2, 2, 0, OP_NOP, qminmod, 0, "a mod b value with smallest abs value", "mne", 3, 3, 0, OP_NOP, f_mne, 0, "whether a and b are not equal modulo c", "near", 2, 3, 0, OP_NOP, f_near, 0, "sign of (abs(a-b) - c)", "norm", 1, 1, 0, OP_NORM, 0, 0, "norm of a value (square of absolute value)", "null", 0, 0, 0, OP_UNDEF, 0, 0, "null value", "num", 1, 1, 0, OP_NUMERATOR, qnum, 0, "numerator of fraction", "ord", 1, 1, 0, OP_NOP, 0, f_ord, "integer corresponding to character value", "param", 1, 1, 0, OP_ARGVALUE, 0, 0, "value of parameter n (or parameter count if n is zero)", "perm", 2, 2, 0, OP_NOP, qperm, 0, "permutation number a!/(a-b)!", "pfact", 1, 1, 0, OP_NOP, qpfact, 0, "product of primes up till number", "pi", 0, 1, FE, OP_NOP, qpi, 0, "value of pi accurate to within epsilon", "places", 1, 1, 0, OP_NOP, f_places, 0, "places after decimal point (-1 if infinite)", "pmod", 3, 3, 0, OP_NOP, qpowermod,0, "mod of a power (a ^ b (mod c))", "polar", 2, 3, 0, OP_NOP, 0, f_polar, "complex value of polar coordinate (a * exp(b*1i))", "poly", 2, IN, 0, OP_NOP, 0, f_poly, "(a1,a2,...,an,x) = a1*x^n+a2*x^(n-1)+...+an", "pop", 1, 1, FA, OP_NOP, 0, f_listpop, "pop value from front of list", "power", 2, 3, 0, OP_NOP, 0, f_power, "value a raised to the power b within accuracy c", "ptest", 2, 2, 0, OP_NOP, f_primetest, 0, "probabilistic primality test", "printf", 1, IN, 0, OP_NOP, 0, f_printf, "print formatted output to stdout", "prompt", 1, 1, 0, OP_NOP, 0, f_prompt, "prompt for input line using value a", "push", 2, 2, FA, OP_NOP, 0, f_listpush, "push value onto front of list", "quomod", 4, 4, 0, OP_QUOMOD, 0, 0, "set c and d to quotient and remainder of a divided by b", "rcin", 2, 2, 0, OP_NOP, qredcin, 0, "convert normal number a to REDC number mod b", "rcmul", 3, 3, 0, OP_NOP, qredcmul, 0, "multiply REDC numbers a and b mod c", "rcout", 2, 2, 0, OP_NOP, qredcout, 0, "convert REDC number a mod b to normal number", "rcpow", 3, 3, 0, OP_NOP, qredcpower, 0, "raise REDC number a to power b mod c", "rcsq", 2, 2, 0, OP_NOP, qredcsquare, 0, "square REDC number a mod b", "re", 1, 1, 0, OP_RE, 0, 0, "real part of complex number", "remove", 1, 1, FA, OP_NOP, 0, f_listremove, "remove value from end of list", "root", 2, 3, 0, OP_NOP, 0, f_root, "value a taken to the b'th root within accuracy c", "round", 1, 2, 0, OP_NOP, 0, f_round, "round value a to b number of decimal places", "rsearch", 2, 3, 0, OP_NOP, 0, f_rsearch, "reverse search matrix or list for value b starting at index c", "runtime", 0, 0, 0, OP_NOP, f_runtime, 0, "user mode cpu time in seconds", "scale", 2, 2, 0, OP_SCALE, 0, 0, "scale value up or down by a power of two", "search", 2, 3, 0, OP_NOP, 0, f_search, "search matrix or list for value b starting at index c", "sgn", 1, 1, 0, OP_SGN, qsign, 0, "sign of value (-1, 0, 1)", "sin", 1, 2, 0, OP_NOP, 0, f_sin, "sine of value a within accuracy b", "sinh", 1, 2, FE, OP_NOP, qsinh, 0, "hyperbolic sine of a within accuracy b", "size", 1, 1, 0, OP_NOP, 0, f_size, "total number of elements in value", "sqrt", 1, 2, 0, OP_NOP, 0, f_sqrt, "square root of value a within accuracy b", "ssq", 1, IN, 0, OP_NOP, 0, f_ssq, "sum of squares of values", "str", 1, 1, 0, OP_NOP, 0, f_str, "simple value converted to string", "strcat", 1,IN, 0, OP_NOP, 0, f_strcat, "concatenate strings together", "strlen", 1, 1, 0, OP_NOP, 0, f_strlen, "length of string", "strprintf", 1, IN, 0, OP_NOP, 0, f_strprintf, "return formatted output as a string", "substr", 3, 3, 0, OP_NOP, 0, f_substr, "substring of a from position b for c chars", "swap", 2, 2, 0, OP_SWAP, 0, 0, "swap values of variables a and b (can be dangerous)", "tan", 1, 2, FE, OP_NOP, qtan, 0, "tangent of a within accuracy b", "tanh", 1, 2, FE, OP_NOP, qtanh, 0, "hyperbolic tangent of a within accuracy b", "trunc", 1, 2, 0, OP_NOP, f_trunc, 0, "truncate a to b number of decimal places", "xor", 1, IN, 0, OP_NOP, f_xor, 0, "logical xor", NULL, 0, 0, 0, OP_NOP, 0, 0, NULL /* end of table */ }; /* * Call a built-in function. * Arguments to the function are on the stack, but are not removed here. * Functions are either purely numeric, or else can take any value type. */ VALUE builtinfunc(index, argcount, stackp) long index; VALUE *stackp; /* arguments on the stack */ { VALUE *sp; /* pointer to stack entries */ VALUE **vpp; /* pointer to current value address */ struct builtin *bp; /* builtin function to be called */ long i; /* index */ NUMBER *numargs[IN]; /* numeric arguments for function */ VALUE *valargs[IN]; /* addresses of actual arguments */ VALUE result; /* general result of function */ if ((unsigned long)index >= (sizeof(builtins) / sizeof(builtins[0])) - 1) error("Bad built-in function index"); bp = &builtins[index]; if (argcount < bp->b_minargs) error("Too few arguments for builtin function \"%s\"", bp->b_name); if ((argcount > bp->b_maxargs) || (argcount > IN)) error("Too many arguments for builtin function \"%s\"", bp->b_name); /* * If an address was passed, then point at the real variable, * otherwise point at the stack value itself (unless the function * is very special). */ sp = stackp - argcount + 1; vpp = valargs; for (i = argcount; i > 0; i--) { if ((sp->v_type != V_ADDR) || (bp->b_flags & FA)) *vpp = sp; else *vpp = sp->v_addr; sp++; vpp++; } /* * Handle general values if the function accepts them. */ if (bp->b_valfunc) { vpp = valargs; if ((bp->b_minargs == 1) && (bp->b_maxargs == 1)) result = (*bp->b_valfunc)(vpp[0]); else if ((bp->b_minargs == 2) && (bp->b_maxargs == 2)) result = (*bp->b_valfunc)(vpp[0], vpp[1]); else if ((bp->b_minargs == 3) && (bp->b_maxargs == 3)) result = (*bp->b_valfunc)(vpp[0], vpp[1], vpp[2]); else result = (*bp->b_valfunc)(argcount, vpp); return result; } /* * Function must be purely numeric, so handle that. */ vpp = valargs; for (i = 0; i < argcount; i++) { if ((*vpp)->v_type != V_NUM) error("Non-real argument for builtin function %s", bp->b_name); numargs[i] = (*vpp)->v_num; vpp++; } result.v_type = V_NUM; if (!(bp->b_flags & FE) && (bp->b_minargs != bp->b_maxargs)) { result.v_num = (*bp->b_numfunc)(argcount, numargs); return result; } if ((bp->b_flags & FE) && (argcount < bp->b_maxargs)) numargs[argcount++] = _epsilon_; switch (argcount) { case 0: result.v_num = (*bp->b_numfunc)(); break; case 1: result.v_num = (*bp->b_numfunc)(numargs[0]); break; case 2: result.v_num = (*bp->b_numfunc)(numargs[0], numargs[1]); break; case 3: result.v_num = (*bp->b_numfunc)(numargs[0], numargs[1], numargs[2]); break; default: error("Bad builtin function call"); } return result; } static VALUE f_eval(vp) VALUE *vp; { FUNC *oldfunc; FUNC *newfunc; VALUE result; if (vp->v_type != V_STR) error("Evaluating non-string argument"); (void) openstring(vp->v_str); oldfunc = curfunc; if (evaluate(TRUE)) { freevalue(stack--); newfunc = curfunc; curfunc = oldfunc; result = newfunc->f_savedvalue; newfunc->f_savedvalue.v_type = V_NULL; if (newfunc != oldfunc) free(newfunc); return result; } newfunc = curfunc; curfunc = oldfunc; freevalue(&newfunc->f_savedvalue); newfunc->f_savedvalue.v_type = V_NULL; if (newfunc != oldfunc) free(newfunc); error("Evaluation error"); /*NOTREACHED*/ } static VALUE f_prompt(vp) VALUE *vp; { VALUE result; char *cp; char *newcp; if (inputisterminal()) { printvalue(vp, PRINT_SHORT); math_flush(); } cp = nextline(); if (cp == NULL) error("End of file while prompting"); if (*cp == '\0') { result.v_type = V_STR; result.v_subtype = V_STRLITERAL; result.v_str = ""; return result; } newcp = (char *)malloc(strlen(cp) + 1); if (newcp == NULL) error("Cannot allocate string"); strcpy(newcp, cp); result.v_str = newcp; result.v_type = V_STR; result.v_subtype = V_STRALLOC; return result; } static VALUE f_str(vp) VALUE *vp; { VALUE result; char *cp; switch (vp->v_type) { case V_STR: copyvalue(vp, &result); return result; case V_NULL: result.v_str = ""; result.v_type = V_STR; result.v_subtype = V_STRLITERAL; return result; case V_NUM: divertio(); qprintnum(vp->v_num, MODE_DEFAULT); cp = getdivertedio(); break; case V_COM: divertio(); comprint(vp->v_com); cp = getdivertedio(); break; default: error("Non-simple type for string conversion"); } result.v_str = cp; result.v_type = V_STR; result.v_subtype = V_STRALLOC; return result; } static VALUE f_poly(count, vals) VALUE **vals; { VALUE *x; VALUE result, tmp; x = vals[--count]; copyvalue(*vals++, &result); while (--count > 0) { mulvalue(&result, x, &tmp); freevalue(&result); addvalue(*vals++, &tmp, &result); freevalue(&tmp); } return result; } static NUMBER * f_mne(val1, val2, val3) NUMBER *val1, *val2, *val3; { return itoq((long) qcmpmod(val1, val2, val3)); } static NUMBER * f_isrel(val1, val2) NUMBER *val1, *val2; { if (qisfrac(val1) || qisfrac(val2)) error("Non-integer for isrel"); return itoq((long) zrelprime(val1->num, val2->num)); } static NUMBER * f_issquare(vp) NUMBER *vp; { return itoq((long) qissquare(vp)); } static NUMBER * f_primetest(val1, val2) NUMBER *val1, *val2; { return itoq((long) qprimetest(val1, val2)); } static NUMBER * f_isset(val1, val2) NUMBER *val1, *val2; { if (qisfrac(val2)) error("Non-integral bit position"); if (qiszero(val1) || (qisint(val1) && qisneg(val2))) return qlink(&_qzero_); if (isbig(val2->num)) { if (qisneg(val2)) error("Very large bit position"); return qlink(&_qzero_); } return itoq((long) qisset(val1, qtoi(val2))); } static NUMBER * f_digit(val1, val2) NUMBER *val1, *val2; { if (qisfrac(val2)) error("Non-integral digit position"); if (qiszero(val1) || (qisint(val1) && qisneg(val2))) return qlink(&_qzero_); if (isbig(val2->num)) { if (qisneg(val2)) error("Very large digit position"); return qlink(&_qzero_); } return itoq((long) qdigit(val1, qtoi(val2))); } static NUMBER * f_digits(val) NUMBER *val; { return itoq((long) qdigits(val)); } static NUMBER * f_places(val) NUMBER *val; { return itoq((long) qplaces(val)); } static NUMBER * f_xor(count, vals) NUMBER **vals; { NUMBER *val, *tmp; val = qlink(*vals); while (--count > 0) { tmp = qxor(val, *++vals); qfree(val); val = tmp; } return val; } static NUMBER * f_min(count, vals) NUMBER **vals; { NUMBER *val, *tmp; val = qlink(*vals); while (--count > 0) { tmp = qmin(val, *++vals); qfree(val); val = tmp; } return val; } static NUMBER * f_max(count, vals) NUMBER **vals; { NUMBER *val, *tmp; val = qlink(*vals); while (--count > 0) { tmp = qmax(val, *++vals); qfree(val); val = tmp; } return val; } static NUMBER * f_gcd(count, vals) NUMBER **vals; { NUMBER *val, *tmp; val = qlink(*vals); while (--count > 0) { tmp = qgcd(val, *++vals); qfree(val); val = tmp; if (qisunit(val)) break; } return val; } static NUMBER * f_lcm(count, vals) NUMBER **vals; { NUMBER *val, *tmp; val = qlink(*vals); while (--count > 0) { tmp = qlcm(val, *++vals); qfree(val); val = tmp; } return val; } static VALUE f_avg(count, vals) VALUE **vals; { int i; VALUE result; VALUE tmp; VALUE div; result.v_num = qlink(&_qzero_); result.v_type = V_NUM; for (i = count; i > 0; i--) { addvalue(&result, *vals++, &tmp); freevalue(&result); result = tmp; } if (count <= 1) return result; div.v_num = itoq((long) count); div.v_type = V_NUM; divvalue(&result, &div, &tmp); qfree(div.v_num); return tmp; } static NUMBER * f_hmean(count, vals) NUMBER **vals; { NUMBER *val, *tmp, *tmp2; val = qinv(*vals); while (--count > 0) { tmp2 = qinv(*++vals); tmp = qadd(val, tmp2); qfree(tmp2); qfree(val); val = tmp; } tmp = qinv(val); qfree(val); return tmp; } static VALUE f_ssq(count, vals) VALUE **vals; { VALUE result, tmp1, tmp2; squarevalue(*vals++, &result); while (--count > 0) { squarevalue(*vals++, &tmp1); addvalue(&tmp1, &result, &tmp2); freevalue(&tmp1); freevalue(&result); result = tmp2; } return result; } static NUMBER * f_ismult(val1, val2) NUMBER *val1, *val2; { return itoq((long) qdivides(val1, val2)); } static NUMBER * f_meq(val1, val2, val3) NUMBER *val1, *val2, *val3; { NUMBER *tmp, *res; tmp = qsub(val1, val2); res = itoq((long) qdivides(tmp, val3)); qfree(tmp); return res; } static VALUE f_exp(count, vals) VALUE **vals; { VALUE result; NUMBER *err; err = _epsilon_; if (count == 2) { if (vals[1]->v_type != V_NUM) error("Non-real epsilon value for exp"); err = vals[1]->v_num; } switch (vals[0]->v_type) { case V_NUM: result.v_num = qexp(vals[0]->v_num, err); result.v_type = V_NUM; break; case V_COM: result.v_com = cexp(vals[0]->v_com, err); result.v_type = V_COM; break; default: error("Bad argument type for exp"); } return result; } static VALUE f_ln(count, vals) VALUE **vals; { VALUE result; COMPLEX temp; NUMBER *err; err = _epsilon_; if (count == 2) { if (vals[1]->v_type != V_NUM) error("Non-real epsilon value for ln"); err = vals[1]->v_num; } switch (vals[0]->v_type) { case V_NUM: if (!qisneg(vals[0]->v_num) && !qiszero(vals[0]->v_num)) { result.v_num = qln(vals[0]->v_num, err); result.v_type = V_NUM; break; } temp.real = vals[0]->v_num; temp.imag = &_qzero_; result.v_com = cln(&temp, err); result.v_type = V_COM; break; case V_COM: result.v_com = cln(vals[0]->v_com, err); result.v_type = V_COM; break; default: error("Bad argument type for ln"); } return result; } static VALUE f_cos(count, vals) VALUE **vals; { VALUE result; COMPLEX *c; NUMBER *err; err = _epsilon_; if (count == 2) { if (vals[1]->v_type != V_NUM) error("Non-real epsilon value for cos"); err = vals[1]->v_num; } switch (vals[0]->v_type) { case V_NUM: result.v_num = qcos(vals[0]->v_num, err); result.v_type = V_NUM; break; case V_COM: c = ccos(vals[0]->v_com, err); result.v_com = c; result.v_type = V_COM; if (cisreal(c)) { result.v_num = qlink(c->real); result.v_type = V_NUM; comfree(c); } break; default: error("Bad argument type for cos"); } return result; } static VALUE f_sin(count, vals) VALUE **vals; { VALUE result; COMPLEX *c; NUMBER *err; err = _epsilon_; if (count == 2) { if (vals[1]->v_type != V_NUM) error("Non-real epsilon value for sin"); err = vals[1]->v_num; } switch (vals[0]->v_type) { case V_NUM: result.v_num = qsin(vals[0]->v_num, err); result.v_type = V_NUM; break; case V_COM: c = csin(vals[0]->v_com, err); result.v_com = c; result.v_type = V_COM; if (cisreal(c)) { result.v_num = qlink(c->real); result.v_type = V_NUM; comfree(c); } break; default: error("Bad argument type for sin"); } return result; } static VALUE f_arg(count, vals) VALUE **vals; { VALUE result; COMPLEX *c; NUMBER *err; err = _epsilon_; if (count == 2) { if (vals[1]->v_type != V_NUM) error("Non-real epsilon value for arg"); err = vals[1]->v_num; } result.v_type = V_NUM; switch (vals[0]->v_type) { case V_NUM: if (qisneg(vals[0]->v_num)) result.v_num = qpi(err); else result.v_num = qlink(&_qzero_); break; case V_COM: c = vals[0]->v_com; if (ciszero(c)) result.v_num = qlink(&_qzero_); else result.v_num = qatan2(c->imag, c->real, err); break; default: error("Bad argument type for arg"); } return result; } static NUMBER * f_legtoleg(val1, val2) NUMBER *val1, *val2; { return qlegtoleg(val1, val2, FALSE); } static NUMBER * f_trunc(count, vals) NUMBER **vals; { NUMBER *val; val = &_qzero_; if (count == 2) val = vals[1]; return qtrunc(*vals, val); } static VALUE f_bround(count, vals) VALUE **vals; { VALUE *vp, tmp, res; if (count > 1) vp = vals[1]; else { tmp.v_type = V_INT; tmp.v_num = 0; vp = &tmp; } broundvalue(vals[0], vp, &res); return res; } static VALUE f_round(count, vals) VALUE **vals; { VALUE *vp, tmp, res; if (count > 1) vp = vals[1]; else { tmp.v_type = V_INT; tmp.v_num = 0; vp = &tmp; } roundvalue(vals[0], vp, &res); return res; } static NUMBER * f_btrunc(count, vals) NUMBER **vals; { NUMBER *val; val = &_qzero_; if (count == 2) val = vals[1]; return qbtrunc(*vals, val); } static NUMBER * f_near(count, vals) NUMBER **vals; { NUMBER *val; val = _epsilon_; if (count == 3) val = vals[2]; return itoq((long) qnear(vals[0], vals[1], val)); } static NUMBER * f_cfsim(val) NUMBER *val; { return qcfappr(val, NULL); } static NUMBER * f_ceil(val) NUMBER *val; { NUMBER *val2; if (qisint(val)) return qlink(val); val2 = qint(val); if (qisneg(val2)) return val2; val = qinc(val2); qfree(val2); return val; } static NUMBER * f_floor(val) NUMBER *val; { NUMBER *val2; if (qisint(val)) return qlink(val); val2 = qint(val); if (!qisneg(val2)) return val2; val = qdec(val2); qfree(val2); return val; } static NUMBER * f_highbit(val) NUMBER *val; { if (qiszero(val)) error("Highbit of zero"); if (qisfrac(val)) error("Highbit of non-integer"); return itoq(zhighbit(val->num)); } static NUMBER * f_lowbit(val) NUMBER *val; { if (qiszero(val)) error("Lowbit of zero"); if (qisfrac(val)) error("Lowbit of non-integer"); return itoq(zlowbit(val->num)); } static VALUE f_sqrt(count, vals) VALUE **vals; { VALUE *vp, err, result; if (count > 1) vp = vals[1]; else { err.v_num = _epsilon_; err.v_type = V_NUM; vp = &err; } sqrtvalue(vals[0], vp, &result); return result; } static VALUE f_root(count, vals) VALUE **vals; { VALUE *vp, err, result; if (count > 2) vp = vals[3]; else { err.v_num = _epsilon_; err.v_type = V_NUM; vp = &err; } rootvalue(vals[0], vals[1], vp, &result); return result; } static VALUE f_power(count, vals) VALUE **vals; { VALUE *vp, err, result; if (count > 2) vp = vals[2]; else { err.v_num = _epsilon_; err.v_type = V_NUM; vp = &err; } powervalue(vals[0], vals[1], vp, &result); return result; } static VALUE f_polar(count, vals) VALUE **vals; { VALUE *vp, err, result; COMPLEX *c; if (count > 2) vp = vals[2]; else { err.v_num = _epsilon_; err.v_type = V_NUM; vp = &err; } if ((vals[0]->v_type != V_NUM) || (vals[1]->v_type != V_NUM)) error("Non-real argument for polar"); if ((vp->v_type != V_NUM) || qisneg(vp->v_num) || qiszero(vp->v_num)) error("Bad epsilon value for polar"); c = cpolar(vals[0]->v_num, vals[1]->v_num, vp->v_num); result.v_com = c; result.v_type = V_COM; if (cisreal(c)) { result.v_num = qlink(c->real); result.v_type = V_NUM; comfree(c); } return result; } static NUMBER * f_ilog(val1, val2) NUMBER *val1, *val2; { return itoq(qilog(val1, val2)); } static NUMBER * f_ilog2(val) NUMBER *val; { return itoq(qilog2(val)); } static NUMBER * f_ilog10(val) NUMBER *val; { return itoq(qilog10(val)); } static NUMBER * f_faccnt(val1, val2) NUMBER *val1, *val2; { return itoq(qdivcount(val1, val2)); } static VALUE f_matfill(count, vals) VALUE **vals; { VALUE *v1, *v2, *v3; VALUE result; v1 = vals[0]; v2 = vals[1]; v3 = (count == 3) ? vals[2] : NULL; if (v1->v_type != V_ADDR) error("Non-variable argument for matfill"); v1 = v1->v_addr; if (v1->v_type != V_MAT) error("Non-matrix for matfill"); if (v2->v_type == V_ADDR) v2 = v2->v_addr; if (v3 && (v3->v_type == V_ADDR)) v3 = v3->v_addr; matfill(v1->v_mat, v2, v3); result.v_type = V_NULL; return result; } static VALUE f_mattrans(vp) VALUE *vp; { VALUE result; if (vp->v_type != V_MAT) error("Non-matrix argument for mattrans"); result.v_type = V_MAT; result.v_mat = mattrans(vp->v_mat); return result; } static VALUE f_det(vp) VALUE *vp; { if (vp->v_type != V_MAT) error("Non-matrix argument for det"); return matdet(vp->v_mat); } static VALUE f_matdim(vp) VALUE *vp; { VALUE result; if (vp->v_type != V_MAT) error("Non-matrix argument for matdim"); result.v_type = V_NUM; result.v_num = itoq((long) vp->v_mat->m_dim); return result; } static VALUE f_matmin(v1, v2) VALUE *v1, *v2; { VALUE result; NUMBER *q; long i; if ((v1->v_type != V_MAT) || (v2->v_type != V_NUM)) error("Bad argument type for matmin"); q = v2->v_num; i = qtoi(q); if (qisfrac(q) || qisneg(q) || (i <= 0) || (i > v1->v_mat->m_dim)) error("Bad dimension value for matmin"); result.v_type = V_NUM; result.v_num = itoq(v1->v_mat->m_min[i - 1]); return result; } static VALUE f_matmax(v1, v2) VALUE *v1, *v2; { VALUE result; NUMBER *q; long i; if ((v1->v_type != V_MAT) || (v2->v_type != V_NUM)) error("Bad argument type for matmax"); q = v2->v_num; i = qtoi(q); if (qisfrac(q) || qisneg(q) || (i <= 0) || (i > v1->v_mat->m_dim)) error("Bad dimension value for matmax"); result.v_type = V_NUM; result.v_num = itoq(v1->v_mat->m_max[i - 1]); return result; } static VALUE f_cp(v1, v2) VALUE *v1, *v2; { VALUE result; if ((v1->v_type != V_MAT) || (v2->v_type != V_MAT)) error("Non-matrix argument for cross product"); result.v_type = V_MAT; result.v_mat = matcross(v1->v_mat, v2->v_mat); return result; } static VALUE f_dp(v1, v2) VALUE *v1, *v2; { if ((v1->v_type != V_MAT) || (v2->v_type != V_MAT)) error("Non-matrix argument for dot product"); return matdot(v1->v_mat, v2->v_mat); } static VALUE f_strlen(vp) VALUE *vp; { VALUE result; if (vp->v_type != V_STR) error("Non-string argument for strlen"); result.v_type = V_NUM; result.v_num = itoq((long) strlen(vp->v_str)); return result; } static VALUE f_strcat(count, vals) VALUE **vals; { register VALUE **vp; register char *cp; int i; long len; long lengths[IN]; VALUE result; len = 1; vp = vals; for (i = 0; i < count; i++) { if ((*vp)->v_type != V_STR) error("Non-string argument for strcat"); lengths[i] = strlen((*vp)->v_str); len += lengths[i]; vp++; } cp = (char *)malloc(len); if (cp == NULL) error("No memory for strcat"); result.v_str = cp; result.v_type = V_STR; result.v_subtype = V_STRALLOC; i = 0; for (vp = vals; count-- > 0; vp++) { strcpy(cp, (*vp)->v_str); cp += lengths[i++]; } return result; } static VALUE f_substr(v1, v2, v3) VALUE *v1, *v2, *v3; { NUMBER *q1, *q2; long i1, i2, len; char *cp; VALUE result; if (v1->v_type != V_STR) error("Non-string argument for substr"); if ((v2->v_type != V_NUM) || (v3->v_type != V_NUM)) error("Non-numeric positions for substr"); q1 = v2->v_num; q2 = v3->v_num; if (qisfrac(q1) || qisneg(q1) || qisfrac(q2) || qisneg(q2)) error("Illegal positions for substr"); i1 = qtoi(q1); i2 = qtoi(q2); cp = v1->v_str; len = strlen(cp); result.v_type = V_STR; if (i1 > 0) i1--; if (i1 >= len) { /* indexing off of end */ result.v_subtype = V_STRLITERAL; result.v_str = ""; return result; } cp += i1; len -= i1; if ((i2 >= len) && (v1->v_subtype == V_STRLITERAL)) { result.v_subtype = V_STRLITERAL; result.v_str = cp; return result; } if (len > i2) len = i2; if (len == 1) { result.v_subtype = V_STRLITERAL; result.v_str = charstr(*cp); return result; } result.v_subtype = V_STRALLOC; result.v_str = (char *)malloc(len + 1); if (result.v_str == NULL) error("No memory for substr"); strncpy(result.v_str, cp, len); result.v_str[len] = '\0'; return result; } static VALUE f_char(vp) VALUE *vp; { long num; NUMBER *q; VALUE result; if (vp->v_type != V_NUM) error("Non-numeric argument for char"); q = vp->v_num; num = qtoi(q); if (qisneg(q) || qisfrac(q) || isbig(q->num) || (num > 255)) error("Illegal number for char"); result.v_type = V_STR; result.v_subtype = V_STRLITERAL; result.v_str = charstr((int) num); return result; } static VALUE f_ord(vp) VALUE *vp; { char *str; VALUE result; if (vp->v_type != V_STR) error("Non-string argument for ord"); str = vp->v_str; if (str[0] && str[1]) error("Multi-character string given for ord"); result.v_type = V_NUM; result.v_num = itoq((long) (*str & 0xff)); return result; } static VALUE f_size(vp) VALUE *vp; { long count; VALUE result; switch (vp->v_type) { case V_NULL: count = 0; break; case V_MAT: count = vp->v_mat->m_size; break; case V_LIST: count = vp->v_list->l_count; break; case V_OBJ: count = vp->v_obj->o_actions->count; break; default: count = 1; break; } result.v_type = V_NUM; result.v_num = itoq(count); return result; } static VALUE f_search(count, vals) VALUE **vals; { VALUE *v1, *v2; NUMBER *q; long start; long index; VALUE result; v1 = *vals++; v2 = *vals++; start = 0; if (count == 3) { if ((*vals)->v_type != V_NUM) error("Non-numeric start index for search"); q = (*vals)->v_num; if (qisfrac(q) || qisneg(q)) error("Bad start index for search"); start = qtoi(q); } switch (v1->v_type) { case V_MAT: index = matsearch(v1->v_mat, v2, start); break; case V_LIST: index = listsearch(v1->v_list, v2, start); break; default: error("Bad argument type for search"); } result.v_type = V_NULL; if (index >= 0) { result.v_type = V_NUM; result.v_num = itoq(index); } return result; } static VALUE f_rsearch(count, vals) VALUE **vals; { VALUE *v1, *v2; NUMBER *q; long start; long index; VALUE result; v1 = *vals++; v2 = *vals++; start = MAXFULL; if (count == 3) { if ((*vals)->v_type != V_NUM) error("Non-numeric start index for rsearch"); q = (*vals)->v_num; if (qisfrac(q) || qisneg(q)) error("Bad start index for rsearch"); start = qtoi(q); } switch (v1->v_type) { case V_MAT: index = matrsearch(v1->v_mat, v2, start); break; case V_LIST: index = listrsearch(v1->v_list, v2, start); break; default: error("Bad argument type for rsearch"); } result.v_type = V_NULL; if (index >= 0) { result.v_type = V_NUM; result.v_num = itoq(index); } return result; } static VALUE f_list(count, vals) VALUE **vals; { VALUE result; result.v_type = V_LIST; result.v_list = listalloc(); while (count-- > 0) insertlistlast(result.v_list, *vals++); return result; } static VALUE f_listinsert(v1, v2, v3) VALUE *v1, *v2, *v3; { VALUE result; if ((v1->v_type != V_ADDR) || (v1->v_addr->v_type != V_LIST)) error("Inserting into non-list variable"); if (v2->v_type == V_ADDR) v2 = v2->v_addr; if ((v2->v_type != V_NUM) || qisfrac(v2->v_num)) error("Non-integral index for list insert"); if (v3->v_type == V_ADDR) v3 = v3->v_addr; insertlistmiddle(v1->v_addr->v_list, qtoi(v2->v_num), v3); result.v_type = V_NULL; return result; } static VALUE f_listpush(v1, v2) VALUE *v1, *v2; { VALUE result; if ((v1->v_type != V_ADDR) || (v1->v_addr->v_type != V_LIST)) error("Pushing onto non-list variable"); if (v2->v_type == V_ADDR) v2 = v2->v_addr; insertlistfirst(v1->v_addr->v_list, v2); result.v_type = V_NULL; return result; } static VALUE f_listappend(v1, v2) VALUE *v1, *v2; { VALUE result; if ((v1->v_type != V_ADDR) || (v1->v_addr->v_type != V_LIST)) error("Appending to non-list variable"); if (v2->v_type == V_ADDR) v2 = v2->v_addr; insertlistlast(v1->v_addr->v_list, v2); result.v_type = V_NULL; return result; } static VALUE f_listdelete(v1, v2) VALUE *v1, *v2; { VALUE result; if ((v1->v_type != V_ADDR) || (v1->v_addr->v_type != V_LIST)) error("Deleting from non-list variable"); if (v2->v_type == V_ADDR) v2 = v2->v_addr; if ((v2->v_type != V_NUM) || qisfrac(v2->v_num)) error("Non-integral index for list delete"); removelistmiddle(v1->v_addr->v_list, qtoi(v2->v_num), &result); return result; } static VALUE f_listpop(vp) VALUE *vp; { VALUE result; if ((vp->v_type != V_ADDR) || (vp->v_addr->v_type != V_LIST)) error("Popping from non-list variable"); removelistfirst(vp->v_addr->v_list, &result); return result; } static VALUE f_listremove(vp) VALUE *vp; { VALUE result; if ((vp->v_type != V_ADDR) || (vp->v_addr->v_type != V_LIST)) error("Removing from non-list variable"); removelistlast(vp->v_addr->v_list, &result); return result; } /* * Return the current runtime of calc in seconds. * This is the user mode time only. */ static NUMBER * f_runtime() { struct tms buf; times(&buf); return iitoq((long) buf.tms_utime, (long) CLK_TCK); } static VALUE f_fopen(v1, v2) VALUE *v1, *v2; { VALUE result; FILEID id; if (v1->v_type != V_STR) error("Non-string filename for fopen"); if (v2->v_type != V_STR) error("Non-string mode for fopen"); id = openid(v1->v_str, v2->v_str); if (id == FILEID_NONE) { result.v_type = V_NUM; result.v_num = itoq((long) errno); } else { result.v_type = V_FILE; result.v_file = id; } return result; } static VALUE f_fclose(vp) VALUE *vp; { VALUE result; if (vp->v_type != V_FILE) error("Non-file for fclose"); if (closeid(vp->v_file)) { result.v_type = V_NUM; result.v_num = itoq((long) errno); } else result.v_type = V_NULL; return result; } static VALUE f_ferror(vp) VALUE *vp; { VALUE result; if (vp->v_type != V_FILE) error("Non-file for ferror"); result.v_type = V_NUM; result.v_num = itoq((long) errorid(vp->v_file)); return result; } static VALUE f_feof(vp) VALUE *vp; { VALUE result; if (vp->v_type != V_FILE) error("Non-file for feof"); result.v_type = V_NUM; result.v_num = itoq((long) eofid(vp->v_file)); return result; } static VALUE f_fflush(vp) VALUE *vp; { VALUE result; if (vp->v_type != V_FILE) error("Non-file for fflush"); flushid(vp->v_file); result.v_type = V_NULL; return result; } static VALUE f_fprintf(count, vals) VALUE **vals; { VALUE result; if (vals[0]->v_type != V_FILE) error("Non-file for fprintf"); if (vals[1]->v_type != V_STR) error("Non-string format for fprintf"); idprintf(vals[0]->v_file, vals[1]->v_str, count - 2, vals + 2); result.v_type = V_NULL; return result; } static VALUE f_printf(count, vals) VALUE **vals; { VALUE result; if (vals[0]->v_type != V_STR) error("Non-string format for printf"); idprintf(FILEID_STDOUT, vals[0]->v_str, count - 1, vals + 1); result.v_type = V_NULL; return result; } static VALUE f_strprintf(count, vals) VALUE **vals; { VALUE result; if (vals[0]->v_type != V_STR) error("Non-string format for strprintf"); divertio(); idprintf(FILEID_STDOUT, vals[0]->v_str, count - 1, vals + 1); result.v_str = getdivertedio(); result.v_type = V_STR; result.v_subtype = V_STRALLOC; return result; } static VALUE f_fgetc(vp) VALUE *vp; { VALUE result; int ch; if (vp->v_type != V_FILE) error("Non-file for fgetc"); ch = getcharid(vp->v_file); result.v_type = V_NULL; if (ch != EOF) { result.v_type = V_STR; result.v_subtype = V_STRLITERAL; result.v_str = charstr(ch); } return result; } static VALUE f_fgetline(vp) VALUE *vp; { VALUE result; char *str; if (vp->v_type != V_FILE) error("Non-file for fgetline"); readid(vp->v_file, &str); result.v_type = V_NULL; if (str) { result.v_type = V_STR; result.v_subtype = V_STRALLOC; result.v_str = str; } return result; } static VALUE f_files(count, vals) VALUE **vals; { VALUE result; if (count == 0) { result.v_type = V_NUM; result.v_num = itoq((long) MAXFILES); return result; } if ((vals[0]->v_type != V_NUM) || qisfrac(vals[0]->v_num)) error("Non-integer for files"); result.v_type = V_NULL; result.v_file = indexid(qtoi(vals[0]->v_num)); if (result.v_file != FILEID_NONE) result.v_type = V_FILE; return result; } /* * Show the list of primitive built-in functions */ void showbuiltins() { register struct builtin *bp; /* current function */ printf("\nName\tArgs\tDescription\n\n"); for (bp = builtins; bp->b_name; bp++) { printf("%-9s ", bp->b_name); if (bp->b_maxargs == IN) printf("%d+ ", bp->b_minargs); else if (bp->b_minargs == bp->b_maxargs) printf("%-6d", bp->b_minargs); else printf("%d-%-4d", bp->b_minargs, bp->b_maxargs); printf(" %s\n", bp->b_desc); } printf("\n"); } /* * Return the index of a built-in function given its name. * Returns minus one if the name is not known. */ getbuiltinfunc(name) char *name; { register struct builtin *bp; for (bp = builtins; bp->b_name; bp++) { if ((*name == *bp->b_name) && (strcmp(name, bp->b_name) == 0)) return (bp - builtins); } return -1; } /* * Given the index of a built-in function, return its name. */ char * builtinname(index) long index; { if ((unsigned long)index >= (sizeof(builtins) / sizeof(builtins[0])) - 1) return ""; return builtins[index].b_name; } /* * Given the index of a built-in function, and the number of arguments seen, * determine if the number of arguments are legal. This routine is called * during parsing time. */ void builtincheck(index, count) long index; { register struct builtin *bp; if ((unsigned long)index >= (sizeof(builtins) / sizeof(builtins[0])) - 1) error("Unknown built in index"); bp = &builtins[index]; if (count < bp->b_minargs) scanerror(T_NULL, "Too few arguments for builtin function \"%s\"", bp->b_name); if (count > bp->b_maxargs) scanerror(T_NULL, "Too many arguments for builtin function \"%s\"", bp->b_name); } /* * Return the opcode for a built-in function that can be used to avoid * the function call at all. */ builtinopcode(index) long index; { if ((unsigned long)index >= (sizeof(builtins) / sizeof(builtins[0])) - 1) return OP_NOP; return builtins[index].b_opcode; } /* END CODE */