What is Hoc ----------- Hoc, "High Order Calculator", is a simple interpreted language for floating- point calculations. Its most common use is as a powerful and convenient calculator, interactively evaluating expressions such as "1+2*sin(0.7)". But hoc is no ordinary calculator: It also lets you assign values to variables, define your own functions, and use loops, conditionals, and everything else you'd expect in a programming language. The Hoc language was introduced and developed in the book The Unix Programming Environment, by Brian Kernighan and Rob Pike (Addison-Wesley, 1984). Countless people are indebted to this book, and its author, for introducing them to Unix and its fantastic programming tools like the shell, C, and Yacc. Some of these people were strongly impressed by Kernighan and Pike's feat of building an interpreter so easily (something which, before the advent of Yacc, was to be attempted only by experts). One of these people was Nadav Har'El, the author of this package, who in 1986, as an 11 year old boy, typed in the source code of "Hoc 6" from the book, and started using it and once in a while added to it features that he wished for. This package is the result of almost a decade of such slow evolution of Hoc. It has numerous improvements over the book's version, which are described below. The code was also modernized, to compile and run on a large variety of machines and compilers that appeared since 1984, and most importantly on ANSI C compilers (although the code should still work on a K&R C compiler, if you can find one in a museum). In 1995, Kernighan and Pike released their version of Hoc [1], under a BSD-like free software license. This meant that modified versions of Hoc could legally be released, and indeed several such modified versions were released (e.g., [2] and [3]). Over a decade too late, in 2007, this version is also being released. This version is released under the same terms that Kernighan and Pike used - see the LICENSE for more details. [1] http://netlib.bell-labs.com/~bwk/hoc.sh [2] http://www.math.utah.edu/pub/hoc/ [3] http://www.linuxjournal.com/?q=article/5810 User-visible changes since the book version: -------------------------------------------- * The variable "_" contains the result of the last top-level expression evaluation, so you can re-use intermediate values without re-typing them. * A "for" statement was added, with the same syntax as in C. Just like in C, each of the three components is optional. As in ANSI C, the first component may even include a declaration (as in "local i=0"). * Hoc now has local variables in functions and procedures. A local variable is declared with the assigment "local var=value", or "local var" (with value defaulting to 0). * The && and || used to always evaluate both operands. Now they short-circuit as in C, i.e., they don't evaluate the second operand if it isn't needed to decide the value of the expression. * Added a ternary operator 'valt ? cond | valf', which does the same thing as the C operator 'cond ? valt : valf' (return valt if cond is true, or valf if cond is false). Like the C operator, this one also short-circuits (always evaluates just one of the operands valt or valf). Note that the operand order is different than in C. The operand order and the use of the '|' character makes this operator readable like English; For example, the expression 1?cond1 | 2?cond2 | 3 equals 1 if cond1, or 2 if cond2, or else 3. * Constant variables can be declared, with the "const var=value" assignment. When a constant variable is used in a loop or a function body, its value is retrieved just once when the code is parsed - not every time around the loop or every time the function is called. A constant can be reassigned a different value with another "const var=value" command, but this assignment will, as explained above, not affect functions that were previously defined and used this constant. Hoc's builtin constants, PI, E, GAMMA, DEG, and PHI, are now consts. * ++ and -- operators added. These have prefix and postfix versions, with semantics exactly like in C. They work on both variables and function arguments ($1, etc.) * Two modulo operator, % and mod, were added. %'s behavior when the first argument is negative is like that of C's % operator, or its fmod() function: -1 % 5 comes out -1. The "mod" operator behaves more sensibly: -1 mod 5 comes out 4. * Function and procedure arguments can now be reassigned, just like variables, e.g., $1 = 3. However, you can only assign a new value to an existing argument - e.g., $1=3 will cause an error if the function was not given an argument. * +=, -=, *=, /=, ^= and %= assignment operators added, and behave as in C (e.g., a += b means a = a+b). These assignments work on both variables and function arguments ($1, etc.). * The DIGITS variable controls the number of significant digits printed. This defaults to 6, as in the original hoc. Note that this variable affects only printouts - the actual calculation is still done in the machine's default double-precision accuracy (usually, 15 digits of precision). * New builtin math functions: logamma, sinh, cosh, tanh, asin, acos, csc, sec, cot. * New builtin function floor(), similar to int(), but behaves differently for negatives: int(-3.4)=-3, while floor(-3.4)=-4. * New builtin function time(), returning the number number of seconds since the epoch (like C's time function). Because all Hoc's builtin functions take one argument, time() does too, but ignores it. * New builtin functions for random number generation. rnd(limit) returns a random integer between 0 and limit-1, inclusive. randomize(seed) changes the random number seed. Note that unless you call randomize(), the seed starts out the same for every run of Hoc. * Hoc now supports functions and procedures with variable number of arguments. This is achieved with a new builtin function arg(i), returning the i'th argument, and $#, returning the number of arguments. * Traditionally in hoc, functions refer to their arguments by number, as in func divide(){ return $1/$2 }. A new syntax is available for naming (some of) the arguments: func divide(a,b){ return a/b }. Both syntaxes remain valid, and in fact can even be mixed, as in func divide(a){ return a/$2 }. This sort of mixture is especially useful for functions with variable number of arguments, where only the first mandatory few are named. All of the above is true also for procedures, of course. Note that the code generated for the following two definitions is identical: func divide(a,b){ return a/b } func divide(){ local a=$1; local b=$2; return a/b } * There's a new 'include "file"' statement to include another file (typically defining useful functions). If you use the file name "-lname" in an include statement or in hoc's command line, the file is taken from hoc's default library directory (by default, "-la" expands to /usr/local/lib/hoc/liba). * Interrupts are caught; Use Control-D or the new "exit" command (or "quit" or "bye") to exit. * The ';' character can now be used, just like a newline, between statements. Like the original hoc and unlike C, the ';' is not mandatory. * C-like comments, /* ... */, are now ignored. Additionally, when reading commands from a file (but not interactively), lines that start with "." are ignored. This can be used to mix a hoc program with troff formatting, for a nice printout. * \ at the end of line eats newline. * There is now a prompt, "hoc> ", in interactive hoc sessions. * The new "abort" statement aborts hoc's currently executing expression. It can be used in functions that encountered an error and cannot return a value. * The new "eof" statement causes hoc to stop reading its current input file. * The new statement 'cd "directory"' changes hoc's current working directory. * It is now possible to change the flow of loops (while and for) with the new "continue" and "break" statements. These work similarly to the C namesakes, except that like their Bourne Shell namesakes, they take an optional expression: "break 2" means break out of two enclosing loops, and "continue 2" means continue the second enclosing loop (i.e., break out of the direct enclosing loop, and continue the second). * Using a function or procedure name without following it with arguments (in parantheses), now calls this function or procedure without arguments, I.e.., 'f' is now another way to write 'f()'. * The new syntax callproc(name, args...) and callfunc(name, args) are equivalent to name(args...), but is accepted as valid syntax even if name is not yet be a function or procedure at the time of parsing. * The new operator ~~ checks for approximate equality: a~~b if abs(a-b)<=ERROR. The ERROR variable defaults to 1e-06, but this can be changed by the program. A function may even "local" ERROR, to change it only inside the function. The operator !~ checks for approximate inequality: a!~b if abs(a-b)>=ERROR. Note that under this definition, it is possible that a~~b && a!~b (when abs(a-b)==ERROR exactly). * There's a new "set" statement for changing some of hoc's default behaviors: 'set "ignoreundef"' causes undefined variables to have the value of 0 when used (instead of the default 'set "noignoreundef"', causing an error). 'set "tracecalls"' causes calls and returns from functions and procedures to be displayed (for debugging purposes). 'set "allnames"' modifies the behavior of the "names" statement (see below). The statement 'set all' shows all the current settings. The statement 'set' shows just the settings that have changed from the default. The statement 'set "@=..."' is used to change the @ prefix (see below). * The new "names" statement shows all currently defined identifiers (variables, constants, keywords, functions, procedures and builtin functions). By default, names beginning with the underscore character (_) are hidden, but this can be changed by the 'set "allnames"' setting. * There's new rudementary support for namespaces, or file-local variables. It works by choosing a unique prefix with the 'set "@=helloworld"' command, and then using indentifiers beginning with @, like @i which is expanded to helloworldi. The unique prefix should typically be long, and start with an underscore (so that the 'names' command doesn't show these identifiers). It is interesting to note that the @ prefix is NOT limited to the standard identifier character set (alphanumeric or underscore), and may contain any characters whatsoever. * New statement 'sh "command ..."' runs an shell command. * Added a new type(identifier) expression. The value of this expression depends on the type of this identifier when the expression is evaluated (not at the time it was first parsed). Type can be the constant T_VAR is the identifier is a global variable, T_LOCAL if it is a local variable, T_FUNC if it is a function, T_PROC if it is a procedure, T_BLTIN for a builtin function, and T_KEYWORD for a reserved keyword. The type function has two bugs: First, it returns T_KEYWORD for consts. Second, it returns T_LOCAL even for variables that are not local in this function, but are local in a function that called this one. * The new constant OSTYPE gives the OS type. It is equal to OS_UNIX on Unix-like machines, and OS_DOS on DOS. * The new "version" statement prints information about this Hoc version. * The new "level" statement prints debugging information about the current recursion depth and stack usage (the stack is used in hoc to hold function arguments). * It is no longer possible to define a function or procedure with a name of an existing variable. Compiling and Installing ------------------------ First run ./configure, to generate the Makefile. Then, compile and install hoc: make make install