NewT(1)

NAME
NewT - A tool for creating interpreters for C libraries

SYNOPSIS
An interpreter created using NewT can be invoked in the following way:

fooint [-ipt] [-dD] [-C [-u][-h headerfile]] [file [file2...]]

DESCRIPTION
NewT, which stands for New Tool, consists of a preprocessor, and a set of C files that can be linked in with user-supplied source files in order to create an interpreter for any set of C functions. The user must supply a set of files describing the interface to his or her functions; these files are preprocessed into a C file, which is compiled along with a set of standard NewT files and the user's source files implementing the desired functions.

Interpreters created in this way can take commands from the keyboard or from a script file, and include some high-level programming constructs such as loops and "if" statements, as well as an interactive debugger. The interpreter can also generate C translations of its input.

This man page consists of two main sections. The first section, INTERPRETER CREATION, is devoted to the process of creating an interpreter. The second, INTERPRETER USAGE, is devoted to usage of the resulting interpreter, and includes descriptions of the language it accepts, command line options, and debugging facilities. The interpreter created can be given any appropriate name; Our examples will create and use an interpreter called fooint.

INTERPRETER CREATION

  Interpreter Files

The following input files are needed to create the interpreter.

foo.m - This file contains the functional interface specifications of all interpreter commands. C style comments are allowed. A special argument type VARARG may be used to specify variable number of arguments(see example below).

    e.g. /*To set an integer variable to some value.
      Usage: foo = set_integer(123)
               foo = set_integet(ONE)*/ int set_integer(int) /*To implement the C library function "printf". Since the interpreter cannot
      call library functions with variable parameters directly, we define "printf"
      as an escape function (using "!" syntax) so that the user function "_printf"
      will be called by the interpreter instead.
      Usage: printf("%s is %d\n", "one",1)
             printf("%s",HELLO)*/ !void printf(char*,VARARG) /*To set a 32 bit variable to some value according to the specified type.
      The number of arguments depends on the type.
      Usage: assign(&IntVar,"int",123)
               assign(&FloatVar,"float",1.23)
               assign(&CharArrayVar1,"char[]",3,'a','b','c')
               assign(&CharArrayVar2,"char[]",5,'a','b','c','d','e')*/ void assign(T32bit*,char*,VARARG) /*To print a 32 bit variable according to the specified type
      The number of arguments depends on the type.
      Usage: dump(IntVar,"int")
               dump(FloatVar,"float")
               dump(CharArrayVar1,"char[]",3)
               dump(CharArrayVar2,"char[]",5)*/ void dump(T32bit,char*,VARARG) /*to sleep for some seconds
      usage: sleep(10)*/ void sleep(int)

foo.c - This file contains the C implementation of any undefined functions which are specified in the .m file.

    e.g. #include "foo.h" typedef MAXSTRLEN 256
    #define eqstr(a,b) !strcmp((a),(b)) int set_integer(intval)
            int intval;
    {
            return(intval);
    } /*NewT provides its own macros for the standard vararg facility, a user
      has to include the file "NewTvarargs.h" when using vararg with NewT.*/ #ifdef NEWT
    #include "NewTvarargs.h"
    #else
    #include "varargs.h"
    #endif void
    _printf(fmt,va_alist)    /* notice the '_' before the function name */
        /* This is a partial implementation of the print function */
            char *fmt;
            va_dcl
    {
            va_list ap;
            char *p;
            char *fp;
            char form[MAXSTRLEN];
            if (strlen(fmt) >= MAXSTRLEN) {
                    printf("_prinf: format string exceeded maximum length);
                    return;
            }
            va_start(ap);
            for(p = fmt; *p; p++) {
                    if (*p != '%') {
                            putchar(*p);
                            continue;
                    }
                    /* just found %, search for ...d, ...f or ...s */
                    for (fp=form,*fp++=*p++;*p;p++,fp++) {
                            sprintf(fp,"%c",*p);
                            if (*p == 'd') {
                            printf(form,va_arg(ap,int));
                            fflush(stdout);
                            break;
                            } else if (*p == 'f') {
                            printf(form,va_arg(ap,double));
                            fflush(stdout);
                            break;
                            } else if (*p == 's') {
                            printf(form,va_arg(ap,char*));
                            fflush(stdout);
                            break;
                            }
                    }
                    /*
                      search for ...d, ...f or ...s failed,
                      just print out what's accumulated so far
                    */
                    if (*p == '\0') {
                            printf("%s",form);
                            break;
                    }
            }
            va_end(ap);
    } void assign(var,type,va_alist)
            T32bit *var;
            char* type;
            va_dcl
    {
            int i,total_items;
            va_list ap;

            va_start(ap);
            if (eqstr(type,"int"))
                    *((int*)var) = va_arg(ap,int);
            else if (eqstr(type,"float"))
                    *((float*)var) = *va_arg(ap,double);
            else if (eqstr(type,"char[]")) {
                    total_items = va_arg(ap,int);
                    *((char**)var) = (char*)malloc(sizeof(char) * total_items);
                    for (i=0; i< total_items; ++i)
                            (*((char**)var))[i] = va_arg(ap,char);
            } else
                    printf("assign: invalid type\n");
            va_end(ap);
    } void dump(var,type,va_alist)
            T32bit var;
            char* type;
            va_dcl
    {
            int i,total_items;
            va_list ap;
            va_start(ap);
            if (eqstr(type,"int"))
                    printf("%d\n", (int)var);
            else if (eqstr(type,"float"))
                    printf("%g\n", *(float*)&var);
            else if (eqstr(type,"char[]")) {
                    total_items = va_arg(ap,int);
                    for (i=0; i< total_items; ++i)
                            printf("%c ",((char*)var)[i] );
                    putchar('\n');
            } else
                    printf("dump: invalid type\n");
            va_end(ap);
    }

foo.h - This file contains external function declarations, type definitions, defined constants and inclusion of any other necessary files.

    e.g. #define ONE 1
    #define TWO 2
    #define FOUR 4
    #define HELLO "hello world!"
    typedef int T32bit; extern void _printf();
    extern void dump();
    extern void assign();
    extern int set_integer();

foo.k - This file contains a listing of defined integer constants and enumerated type elements that the interpreter is expected to recognize. C-style comments are allowed.

    e.g. ONE TWO FOUR HELLO

config.h - This file contains the definition of MAXARG, the maximum number of arguments allowed for interpeter commands.

    e.g. #define MAXARG 16

  Interpreter Creation Process

A program called ipp is used to create an interface module (interface.c and interface.h) from the required .m file and optional .k and .h files. The .h file, if present, will be included in interface.c. foo.m + [foo.k] + [foo.h] -> <ipp> -> interface.c + interface.h ipp usage:

ipp [-n] [-k src1.k [-k src2.k]...] [-i src1.h [-i src2.h]...] [-u prefix [-u prefix]...] [-l prefix] [src.m...]
 -k: to specify a .k file
 -i: to specify a file to be included in the automatically created interface module
 -u: to use an interface library with the specified prefix
 -l: to specify a prefix for an interface library
 -n: to use the new style interface See the FILES section for examples about the usage of interface library.

    ipp -k foo.k -i foo.h -n

The "n" option is required for producing a new style interface that works with all the NewT functionalities mentioned in this document. By default, ipp generates an old style interface (still exist for backward compatibility) which requires function names be prepended with underscores and arguments be passed with an extra level of indirection for all functions defined in foo.c. Function assignment and C translation won't work with the old style interface.

The C-implementation file, config.h, other NewT files and any necessary libraries will be compiled with the previously generated interface module to create the NewT interpreter. foo.c + interface.c + interface.h + config.h + looper src + debugger src + interpreter src + [other libraries] -> NewT interpreter looper src: looper.yacc looper.lex looper.h var.c var.h debugger src: debugger.c debugger.h interpreter src: interpret.c interpret.h

INTERPRETER USAGE
Your interpreter can be thought of as having two parts: a front end called the looper, and a back end interpreter. The looper parses the input, and handles loops, if-statements, macro definitions, the reading of other files, its own kind of variables, called looper variables. It passes simple commands on to the actual interpreter, which interprets the command name and arguments and makes calls to the actual C code implementing a particular command.

  THE INTERPRETER LANGUAGE

  Basic Predicate Syntax

The basic commands of the interpreter, called predicates, are of the form

    pred(arg1 [,arg2,...])[;]

These correspond to the user-defined functions that have been built into the interpreter. The input is free-format, so the placement of extra spaces, tabs, and carriage returns is unimportant. Comments are allowed, both C-style (/* ... */) and sh-style (#...<end of line>). The C-style comments are permitted anywhere. The sh-style comments are only allowed in certain places (anywhere a predicate is allowed, or after the semicolon following a foreach value), but have the advantage of being passed on into an automatically-generated C translation of the script, in the appropriate place. The semicolon is optional, and is intended to allow fragments of C code to be interpreted. Spaces are not allowed in an argument, unless the argument is a string between double quotes.

Arguments can take the form of identifier(variable), integer constant (hexadecimal, octal, decimal, enumerated elements or C-defined symbol), float constant, string constant, character constants or expressions (&identifier,*identifier or integer | integer [|...]).

Note: If the named variable does not exist, it will be created (according to the type specified in .m file). Moreover, arguments of the form &var or *var will also result in the creation of the appropriate variable type, and the expected use of the address of var as the predicate's argument. Declarations are thus rendered unnecessary. dumpvar() is a built-in predicate which list all variables and types created by the interpreter. Variables created by the interpreter will sometimes be referred to as interpreter variables in this document, to help differentiate them from looper variables, defined below.

  Function assignments
If a predicate's specification in its .m file gives a return type, the predicate may be invoked as a function and have its value assigned to an interpreter variable:

    varname = pred(arg1,arg2,...)

If the named variable does not exist, the interpreter will create it. The variable's name may be a simple identifier, or a looper variable whose value is a simple identifier (note that if a looper variable is used, C translation may not produce a correct output -- see below). The predicate whose value is assigned to a variable may not be a macro (see below).

  Looper variable assignments

The looper front end maintains a list of special variables, called looper variables, whose values are treated as strings. A predicate may have looper variables among its arguments, and these strings are evaluated before the complete predicate is passed on to the interpreter. Looper variables are of the form @varname, where varname begins with a letter and may contain letters, digits, and underscores. Statements of the form

    @loopervarname = argument

assign the given value to the variable. This variable can then be used as an argument to a predicate, or in any of the special constructs described below (foreach and for loops, macros, and if statements). Looper variables may be used alone as arguments, or they may be embedded in larger arguments (in quoted strings, for instance). More than one variable may be embedded in a single predicate argument. If a variable appears at the beginning of a larger argument, it should be preceded by a "\" character (e.g. @x = MASK1; @y = 2; Set_Bitmask(\@x|@y)).

  Foreach Loops

The foreach loop has as arguments a list of looper variables, and a semicolon-separated list of values to assign to these variables for each iteration of the loop. The statements in the loop body may use the loop variables as arguments. The form is as follows:

    foreach @var1 [@var2...@varN] (val1; val2; ...valM)
    	loop body
    end[;]

For each iteration of the loop, the looper takes N values from the value list and assigns them to the corresponding loop variables, and executes the loop body. The number of values must be a multiple of the number of variables. Foreach loops may be nested.

  For Loops
The for loop takes as arguments a looper variable, an optional starting value (default: 1), an ending value, and an optional step value (default: 1). The start, end, and step values must be literal integers or looper variables that evaluate to integers. The form is:

    for (@var; [start;] end [; step])
    	loop body
    end[;]

Note that a step value cannot be specified unless a start value is also given -- "for (@var; 10; 20)" will be interpreted as a loop from 10 to 20, and the step value will default to 1.

  If Statements
The if statement takes as an argument a single value, and evaluates whether it is zero or not. This value may be a literal integer, a defined constant known to the interpreter, an interpreter variable (of appropriate type, such as char or integer), or a looper variable which evaluates to any of these. There is an optional else statement.

    if ( value ) [then]
    	body to be executed if true
     [else
    	body to be executed if false ]
     endif

  Macro Definitions

Looper macros allow multiple statements to be compressed into one command, with arguments which can be used anywhere in the macro body. The macro arguments are expressed as looper variables, but are translated into longer names as the macro body is read in (Thus, macro parameters may not be embedded in longer arguments as normal looper variables may -- if it is necessary to embed a macro parameter in an argument within the macro body, assign it to an intermediate variable within the macro and embed that variable instead). The syntax is as follows:

    macro macname([@arg1, @arg2,...])
    	macro body, which may reference arguments
    endmacro[;]

The macro can then be invoked as if it were a built-in predicate. If regular looper variables are referenced inside the macro body, they will take on the value they have at the time the macro is executed.

  Reading Source Files

The user may direct the looper to read commands from another file using the source command, followed by the filename.

     source  filename

This command causes the looper to read commands from the named file until it encounters either the resume command, or the end of the file. If the resume command is found, the looper continues execution from the input file in which the source command was executed; if the end of file is reached without finding the resume command, looper execution stops.

The environment variable MACROPATH, if present, is used by the interpreter to find files named in a source command. The form is of a colon-separated list. Note that if this variable is defined, the current directory is not searched before the others in the path. Thus, it is suggested that this variable always start with a ".", as in ".:../shared_macros".

A file read with the source command may in turn read other files, to a depth of up to 16 (provided, of course, that the system can handle that many open files). Source commands may be embedded in loops, macros, and if statements; Be aware that the named file is read in at the time the source command is parsed, not executed; this imposes the restriction that the file must exist at the time execution starts, but gives the freedom to enclose the command in a loop without worrying about the overhead of re-reading the file with each loop iteration.

OPTIONS

-i  Invoke the interpreter in interactive mode: it causes a ">>" prompt to be printed for each command, and a ">" prompt for lines within a command. These prompts are sent to the standard error. Commands are executed immediately in interactive mode; otherwise, the entire input is read in and parsed before execution begins.

-p  for "print only"; in this mode, commands coming from the looper front end are simply printed out, rather than executed. Useful for debugging loops and other constructs.

-t  for "trace"; in trace mode, commands coming from the looper front end are printed out before being passed on to the interpreter.

-C  Invokes C translation mode. The input file is parsed and translated into C. The C file is given a name derived from the name of the source interpreter script by substituting ".c" for the filename's extension, or appending ".c" if it has no extension. If the input is being taken from standard input, the C translation is placed on the standard output. If some simple rules are followed when writing interpreter scripts, the translation should produce a legal and equivalent C program without need for any manual modification:
      * Do not embed looper variables within larger arguments, such as strings, unless the variable's value is constant throughout the execution of the program.
      * If a foreach loop loops on variable names (assigning different interpreter variable names to looper variables), predicates which have the effect of making assignments to these variables should not be used (the same effect can be obtained legally if the looper variable is used as an address, e.g. "assign(&@loopvar,x)"). This constraint arises from how foreach loops are translated to C: the different loop values are assigned to C variables at each loop iteration. This also applies to functional assignments -- if you loop on variable names and do assignments to these variables, the C translation will not be correct.

-h headerfile
    In translation mode, specifies a file to be inserted at the head of the resulting C program, before main(). This may include "#include"s, type definitions, etc.

-u  In translation mode, causes prepending of an underscore character to all predicate names in the resulting C translation.

-d  Two process debug mode. A single filename must be supplied with this option. The file is parsed, and a debugging prompt is printed; the user may then use any of the debugger commands listed below. The two process mode guaranteer a clean environment when the NewT debugger rerun a script.

-D  Single process debug mode. A single filename must be supplied with this option. The file is parsed, and a debugging prompt is printed; the user may then use any of the debugger commands listed below. The single process mode is intended to facilitate the running of dbx on top of the NewT debugger. But it does not guaranteer a clean environment when the NewT debugger rerun a script.

  Debugger Commands Usage

list [startline[,endline]]

    List the next ten lines from current source file. List from startline through endline.

stop at sourceline

    Stop execution when the sourceline is reached. Can stop at any function call, macro call, foreach loop, for loop, if statement or source statement.

delete all
delete cmd-no[,cmd-no]...

    Remove all stops or those corresponding to each cmd-no (as displayed by status).

status

    Display active stop commands and associated command numbers.

run

    Start executing specified script.

step [n]

    Execute the next n source lines. If omitted, n is taken to be 1. Can step into macro calls, if statements, foreach loops, for loops and source statements.

next [n]

    Execute the next n source lines. If omitted, n is taken to be 1. Next steps past macro calls, if statements, foreach loops, for loops and source statements.

cont [at sourceline]

    Continue execution from where it stopped. If at sourceline is specified, resume starting at sourceline.

quit

    Exit.

call procedure(parameters)

    Call the Interpreter to execute the named procedure.(no function assignment) The parameters should not contain any looper variables.

alias new-command-name character-sequence

    Respond to new-command-name as though it were character-sequence.

The file .NewTdbinit is processed by the debugger at initialization time if that file exists in the current directory, or in the user's home directory. This file takes the format of one alias command per line.

EXAMPLES
Below is a sample session using the looper in interactive, print-only mode, allowing its output to be printed directly to the terminal. The formatting of the loop values and body are arbitrary
    hobbes% looper -ip
    >> hello(world)
    hello(world)
    >> @x=HELLO_OVER_THERE
    >> say(@x)
    say(HELLO_OVER_THERE)
    >> @y="Good Dog!"
    >> foreach @dog @thing (Lassie; @y;
    >                       Rex;    "Sit";
    >                       Rover;  @y;
    >                       Spuds;  "Give me a brewski")
    >       Tell_Dog(@dog,@thing)
    >       Tell_Dog(@dog, "rollover");  /* note the optional semicolon */
    > end;			/* semicolon is optional here, too */
    Tell_Dog(Lassie, "Good Dog!")
    Tell_Dog(Lassie, "rollover")
    Tell_Dog(Rex, "Sit")
    Tell_Dog(Rex, "rollover")
    Tell_Dog(Rover, "Good Dog!")
    Tell_Dog(Rover, "rollover")
    Tell_Dog(Spuds, "Give me a brewski")
    Tell_Dog(Spuds, "rollover")
    >> Please(@thing)  /* loop variable will keep last value */
    Please("Give me a brewski")

The next example illustrates the definition of a macro. Since the variables representing the macro arguments cannot themselves be embedded in larger arguments, some regular looper variables are used to embed the arguments in a string.
    >> macro line(@x1, @y1, @x2, @y2)
    >       MoveTo(@x1, @y1)
    >       Lineto(@x2, @y2)
    >	/* macro parameters cannot be directly embedded! */
    >       @mx1 = @x1; @my1 = @y1; @mx2 = @x2; @my2 = @y2;
    >       print("Line drawn from (@mx1, @my1) to (@mx2, @my2)")
    > endmacro
    >> line(0, 0, 400, 500)
    MoveTo(0, 0)
    Lineto(400, 500)
    print("Line drawn from (0, 0) to (400, 500)")
    >>

FILES
goldbach:/usr/graphics/src/newt/* 		source code and SCCS directory
goldbach:/usr/graphics/src/newt/utiltest
     					This directory contains a sample makefile and set of source files
     					for a sample interpreter, implementing a useful set of utility functions.
					The makefile is easily enhanced to make more complicated interpreters,
					which can include the set of utility functions.
goldbach:/usr/graphics/src/newt/phigstest
     					This directory contains a sample makefile and set of source files
     					for an experimental SunPHIGS interpreter, The makefile is easily
					enhanced to make more complicated interpreters.
					This example also illustrates the usage interface library.

SEE ALSO