HELP DEFINE_FORM                                      A.Sloman July 1988

DEFINE_FORM

This is a syntax word for specifying new "define....enddefine" syntax
constructions, using the format:

    define :define_form <form>; .... enddefine

thereafter allowing:

    define :<form> ..... enddefine;

This makes it easier to define new syntactic constructs that are
understood by old utilities, e.g. ved_f, ved_mcp, ved_lcp, ved_jcp.

         CONTENTS - (Use <ENTER> g to access required sections)

 -- Introduction
 -- The use of "define :<form name>"
 -- Defining a new form using:- define :define_form ...
 -- Autoloading forms
 -- Pre-defined forms
 -- define :define_form <name> ; <body> enddefine;
 -- define :define_form global <name> ; <body> enddefine;
 -- Importing and exporting from sections
 -- define <name> = <expression> enddefine;
 -- Example: define :define_form class_print
 -- Example: using define <name> = to assign a property to an identifier
 -- Example: using define <name> = to define a prefix operation
 -- Example: a special form for defining closures
 -- Example: a form for declaring and initialising variables
 -- Example: Making POP-11 procedures available from Prolog
 -- Exercise: Defining procedures to operate on patterns
 -- define_inline: inline macro expressions
 -- See also

-- Introduction -------------------------------------------------------

The main use of the words "define" and "enddefine" is to define
procedures whose body consists of program instructions to be executed.
The differences between different types of procedure definitions are
indicated by the "header" i.e. whatever occurs between "define" and the
first semicolon.

There are two main types
- ordinary procedures and infix operations, whose instructions are
    executed when the procedure is invoked by running another procedure,

- macros and syntax words, whose instructions are executed when the name
    is encountered by -itemread- in the compilation stream.
    (See HELP * DEFINE, * MACRO, * SYNTAX, * OPERATION).

POP-11 allows the user to define macros and syntax words to extend the
syntax, for instance providing new forms of procedure definition or
structure specification. Unfortunately if new opening and closing
brackets are used then some existing utilities will not be able to cope
with them, for instance editor utilities that know how to find the
beginning and end of the "current" procedure definition, or know how to
search for the definition of a given procedure.

The facilities described in this file enable the user to extend the use
of "define" and "enddefine" for new purposes, making it unnecessary to
introduce new openers and closers.


-- The use of "define :<form name>" ------------------------------------

The key idea is that the word "define" can be followed by a "form"
specification to indicate what sort of thing is being defined. The
format used is

    define :<form name> <remainder of header>;
        <body>
    enddefine;

Here <form name> is a word which should have associated with it a
procedure to be run instead of the ordinary "define" syntax procedure,
to take over compilation of everything up to "enddefine".


-- Defining a new form using:- define :define_form ... ----------------

One use of the "define :<form name>" format is for defining new
instances of the format. This is done using the <form name> "define_form"
as follows.

In order to introduce a new form, called "myproctype" use the following
form of definition.

    define :define_form myproctype;
        <body>
    enddefine;

(The body should include instructions that can be used to read in and
plant code for a definition of the required form.)

This definition of "myproctype" is equivalent to:

    define syntax define_myproctype; <body>  enddefine;

Thereafter "myproctype" can be used in the role of <form name> to define
instances of its form, thus:

    define :myproctype foo....;
        .....
    enddefine;

The initial portion "define :myproctype" invokes the "define_form"
procedure -define_myproctype-, which might call the usual compiler and
code planting routines defined in REF * VMCODE. Examples are given
below.

Note that if you wish a new define_form to be available in all sections,
below the current section, then you must include the word "global" after
"define_form", as in

    define :define_form global foo ....

This turns into the definition

    define global syntax define_foo


-- Autoloading forms --------------------------------------------------

If there is an autoloadable library file whose name is define_foo.p
defining a syntax procedure called define_foo, then the use of

    define :foo .... enddefine;

will autoload the syntax procedure.


-- Pre-defined forms --------------------------------------------------

The following options for <formname> are pre-defined.

-- define :define_form <name> ; <body> enddefine; ---------------------

This defines <name> as the name of a new syntax form. It is equivalent
to the following Pop-11 instructions:-

    define syntax define_<name>; <body>  enddefine;

As a result of this, future occurrences of

    define :<name> ..... enddefine;

will invoke the code in <body>, which should consume all the instructions
"....." up to "enddefine". I.e.

    define :<name> .... enddefine;

is thereafter equivalent to

    define_name ..... enddefine;


-- define :define_form global <name> ; <body> enddefine; --------------

This is very similar except that the new syntax word is declared as
global in the current section.


-- Importing and exporting from sections ------------------------------

To each define_form name N there corresponds a syntax procedure whose
name is got by prefixing N with "define_". This makes it possible to
import and export such facilities from sections.

-- define <name> = <expression> enddefine; ----------------------------

This is equivalent to

    vars procedure <name>;
    <expression> -> <name>;
    "<name>" -> pdprops(<name>);

I.e. it is used to assign the result of evaluating an expression
to a new procedure name. The result assigned should be a procedure,
closure or property. The name is stored in the pdprops of the procedure.

The above is a special case of the more general form below, which allows
the syntactic category of <name> to be declared.

    define <declaration for <name>> <name> = <expression> enddefine;

The declaration may be of any of the forms allowed in REF *POPSYNTAX
for declaring a single identifier <name>, e.g. starting with any of

    lvars dlvars lconstant vars constant global dlocal

The above definition is equivalent to the following

    <declaration for <name>>
    <expression> -> <name>
    "<name>" -> pdprops(<name>);

Notice that the <expression> must produce a procedure type
object (e.g. procedure, closure, array, property), since otherwise the
updater of -pdprops- will report an error.


-- Example: define :define_form class_print ---------------------------

The following sort of thing is common. After defining a new record
class, e.g.

    recordclass triple first second third;

it is useful to modify the default class_printer, e.g.

    define print_triple(t);
        lvars t;
        pr('{triple:');
        pr(first(t)); pr(",");
        pr(second(t)); pr(",");
        pr(third(t)); pr("}");
    enddefine;

    print_triple -> class_print(triple_key);

We can make this a neater definition, as follows


;;; first define a form:
;;;     define :class_print (class);


define :define_form class_print;
    lvars classname, pdrname, keyname, declarators, next;
    ;;; read in syntax words like lconstant, procedure, etc.
    [%while isstartstring("syntax",identprops(readitem() ->> next)) do
        readitem()
    endwhile %] -> declarators;
    next -> classname;
    consword('print_' sys_>< classname) -> pdrname;
    consword(classname sys_>< '_key') -> keyname;
    if identprops(keyname) == undef or not(iskey(valof(keyname))) then
        mishap(classname, 1, 'CLASS NAME NEEDED')
    endif;
    [define ^^declarators ^pdrname ^^proglist] -> proglist;
    pop11_comp_expr();  ;;; compile the procedure definition
    ;;; now make it the class_print procedure
    sysPUSH(pdrname);
    sysPUSH(keyname);
    sysUCALLQ(class_print)
enddefine;


;;; We can now define the printer for the class triple, thus:

define :class_print triple(t);
    pr('{triple:');
    pr(first(t)); pr(",");
    pr(second(t)); pr(",");
    pr(third(t)); pr("}");
enddefine;

vars t = constriple([cat],[dog],[mouse]);
t =>
** {triple:[cat],[dog],[mouse]}


Note:
Things like lconstant, global, etc can follow
    define :class_print.

A similar strategy could be used to define the class_apply. We
may add define_class_print and define_class_apply to the library.
See HELP NEWS


-- Example: using define <name> = to assign a property to an identifier -

define species =
    newproperty([[fido dog][mickey cat][donald duck]], 10, false, true)
enddefine;

species =>
** <property species>

species("donald") =>
** duck

species("goofy") =>
** <false>


-- Example: using define <name> = to define a prefix operation -------------

;;; The precedence of the operation iscolour is to be 1

define global constant 1 iscolour =
    member(%[red green blue yellow orange white]%)
enddefine;

identprops("iscolour") =>
** 1

iscolour "red" =>
** <true>

iscolour 99 =>
** <false>


-- Example: a special form for defining closures ----------------------

;;; First a checking utility
define check_closure(item) -> item;
    ;;; test that top of stack is a closure, and if so leave it there
    lvars item;
    unless isclosure(item) then
        mishap(item,1,'CLOSURE NEEDED')
    endunless
enddefine;

;;; Now declare a new form :new_closure, used with format
;;;     define new_closure <name>; <body> enddefine;
define :define_form new_closure;
    lvars name;
    itemread() -> name;
    sysSYNTAX(name, "procedure",false);
    sysneed(";") ->;
    pop11_comp_stmnt_seq_to("enddefine") ->;
    ;;; check its a closure, and assign to the name
    sysCALLQ (check_closure);
    sysPOP(name);
    ;;; now assign the name to the pdprops of the closure
    sysPUSHQ(name);
    sysPUSH(name);
    sysUCALLQ(pdprops);
enddefine;

;;; Example of use of new_closure

define :new_closure square_roots;
    maplist(% sqrt<>round %)
enddefine;

square_roots =>
** <procedure square_roots>

square_roots([4 9 16 25])=>
** [2 3 4 5]

Note, since new_closure was not declared to be global, if it is
to be imported into a section it will have to be referred to as
define_new_closure.


-- Example: a form for declaring and initialising variables -----------

The idea here is to declare a variable, and specify the name before
specifying the syntactic properties. The latter should come after "AS".

The new format to be permitted is

   define :new_ident <name> AS <identifier type>;
       <body>    ;;; value to be assigned to <name>
   enddefine;
or
   define :new_ident <name> ;
       <body>    ;;; value to be assigned to <name>
   enddefine;

To make sure it will work in all sections below the current section,
use "global" after "define_form"

define :define_form global new_ident;
    lvars name, stream, declarator, spec;
    readitem() -> name;

    pop11_need_nextitem([AS ;]) -> spec;
    ;;; Check that there is a syntax word following. (Could list them)
    readitem() -> declarator;
    unless identprops(declarator) == "syntax"
    and isprocedure(valof(declarator))
    then
        mishap(declarator,1,'"VARS"-TYPE SYNTAX WORD EXPECTED')
    elseif declarator == ";" then
        ;;; no type declaration - ordinary vars
        sysSYNTAX(name, 0, false);
    else
        ;;; After "AS" -declarator- should be a word like "vars", "global" etc.
        ;;; Find semi-colon in proglist
        proglist -> stream;
        until hd(stream) == ";" do back(stream) -> stream enduntil;
        ;;; insert name before semicolon
        name -> hd(stream);
        conspair(";",tl(stream)) -> back(stream);
        ;;; Now run the declarator, which should consume proglist to
        ;;; the semi-colon
        valof(declarator)();
    endunless;
    ;;; Compile the body
    pop11_comp_stmnt_seq_to("enddefine") ->;
    ;;; now assign to the name
    sysPOP(name);
enddefine;


section testing;

;;; Using form new_ident to define a global constant called number_list

define :new_ident number_list AS global constant;
    [1 2 3 4 5 6 7 8 9 ]
enddefine;

isconstant("number_list") =>
** <true>

number_list =>
** [1 2 3 4 5 6 7 8 9]

;;; define a 4th root prefix operator, root4
define :new_ident root4 AS global vars 1;
    sqrt <> sqrt
enddefine;

nonop root4 =>
** <procedure>

root4 16 =>
** 2.0

root4 256 =>
** 4.0

endsection; /*testing*/

In some cases it would be useful to alter pop11_define_declare or
pop11_define_props locally in a define_form type procedure.


-- Example: Making POP-11 procedures available from Prolog ------------

Define a define_form "prolog" syntax word so that

    define :prolog foo(x, y, continuation);
        ...
    enddefine;

does the same as:

    define foo(x, y, continuation);
        ...
    enddefine;
    foo -> prolog_valof("foo", pdnargs(foo)-1);

define :define_form prolog;
    lvars pred_name = nextitem();
    ;;; compile the procedure
    nonsyntax define();
    ;;; do the assignment
    sysPUSH(pred_name);
    sysPUSHQ(pred_name);
    sysPUSH(pred_name);
    sysCALLQ(pdnargs);
    sysPUSHQ(1);
    sysCALLQ(nonop -);
    sysUCALL("prolog_valof")
enddefine;


-- Exercise: Defining procedures to operate on patterns ---------------

This application (suggested by Roger Evans) is left as an exercise.

Define a "pattern" define_form such that

    define :pattern foo([arc ?s ?f ??y ?x]) -> [?a ??b];
        ...
    enddefine;

does:

    define foo();
        vars s f x y a b;
        --> [arc ?s ?f ??y ?x];
        ...
        [^a ^^b];
    enddefine;

-- define_inline: inline macro expressions ----------------------------

See HELP *INLINE for an example of how to use define forms to build
Pop-11 "inline" macros similar to those produces by the C
pre-processor's #define directive. For example:

    define :inline calc(a,b);
        (a * 10 - b)
    enddefine;

is equivelant to the C macro:

    #define calc(a,b) (a * 10 - b)

It is simle to use, and looks much like a procedure call:

    calc(1,2) =>


-- See also -----------------------------------------------------------

HELP * DEFINE
HELP * INLINE
REF  * POPCOMPILE
REF  * POPCOMPILE/pop11_define_declare
REF  * POPCOMPILE/pop11_define_props
REF  * POPSYNTAX
REF  * VMCODE

Acknowledgement:
The original idea for this feature was improved as a result of suggestions
by Steve Knight, Roger Evans and John Gibson.

--- C.all/help/define_form ---------------------------------------------
--- Copyright University of Sussex 1988. All rights reserved. ----------
