{Begin SubSec TRANSOR}
{Title TRANSOR}
{Text


{index *BEGIN* TRANSOR Fn}


{note TRANSOR was written by J. W. Goodwin.}


{it
Note:  TRANSOR is a LispUsers package contained on the file {lisp TRANSOR.DCOM}.
}



TRANSOR is a LISP-to-LISP translator intended to help the user who has a program coded in one dialect of LISP and wishes to carry it over to another.
The user loads TRANSOR along with a file of transformations.
These transformations describe the differences between the two LISPs, expressed in terms of Interlisp editor commands needed to convert the old to new, i.e. to edit forms written in the source dialect to make them suitable for the target dialect.  TRANSOR then sweeps through the user's program and applies the edit transformations, producing an object file for the target system.
In addition, TRANSOR produces a file of translation notes, which catalogs the major changes made in the code as well as the forms that require further attention by the user.  Operationally, therefore, TRANSOR is a facility for conducting massive edits, and may be used for any purpose which that may suggest.


Since the edit transformations are fundamental to this process, let us begin with a definition and some examples.  A transformation is a list of edit commands associated with a literal atom, usually a function name.  TRANSOR conducts a sweep through the user's code, until it finds a form whose {fn CAR} is a literal atom which has a transformation.  The sweep then pauses to let the editor execute the list of commands before going on.  For example, suppose the order of arguments for the function {fn TCONC} must be reversed for the target system.
The transformation for {fn TCONC} would then be: {lisp ((SW 2 3))}.  When the sweep encounters the form {lisp (TCONC X (FOO))}, this transformation would be retrieved and executed, converting the expression to {lisp (TCONC (FOO) X)}.
Then the sweep would locate the next form, in this case {lisp (FOO)}, and any transformations for {lisp FOO} would be executed, etc.


Most instances of {fn TCONC} would be successfully translated by this transformation.  However, if there were no second argument to {fn TCONC}, e.g. the form to be translated was {lisp (TCONC X)}, the command {lisp (SW 2 3)} would cause an error, which TRANSOR would catch.  The sweep would go on as before, but a note would appear in the translation listing stating that the transformation for this particular form failed to work.  The user would then have to compare the form and the commands, to figure out what caused the problem.
One might, however, anticipate this difficulty with a more sophisticated transformation: {lisp ((IF (## 3) ((SW 2 3)) ((-2 NIL))))}, which tests for a third element and does {lisp (SW 2 3)} or {lisp (-2 NIL)} as appropriate.  It should be obvious that the translation process is no more sophisticated than the transformations used.


This documentation is divided into two main parts.  The first describes how to use TRANSOR assuming that the user already has a complete set of transformations.  The second documents {index TRANSORSET FN}{fn TRANSORSET}, an interactive routine for building up such sets.  {fn TRANSORSET} contains commands for writing and editing transformations, saving one's work on a file, testing transformations by translating sample forms, etc.


Two transformations files presently exist for translating programs into Interlisp.
{lisp <LISP>SDS940.XFORMS} is for old BBN LISP (SDS 940) programs, and {lisp <LISP>LISP16.XFORMS} is for Stanford AI LISP 1.6 programs.  A set for LISP 1.5 is planned.



{Begin SubSec Using TRANSOR}
{Title Using TRANSOR}
{Text

The first and most exasperating problem in carrying a program from one implementation to another is simply to get it to read in.  For example, SRI LISP uses {lisp /} exactly as Interlisp uses {lisp %}, i.e. as an escape character.
The function {index PRESCAN FN}{fn PRESCAN} exists to help with these problems: the user uses {fn PRESCAN} to perform an initial scan to dispose of these difficulties, rather than attempting to TRANSOR the foreign sourcefiles directly.

{fn PRESCAN} copies a file, performing character-for-character substitutions.
It is hand-coded and is much faster than either {fn READC}'s or text-editors.


{FnDef {Name PRESCAN} {Args FILE CHARLST}
{Text
Makes a new version of {arg FILE}, performing substitutions according to {arg CHARLST}.  Each element of {arg CHARLST} must be a dotted pair of two character codes, {lisp ({arg OLD-CHAR-CODE} . {arg NEW-CHAR-CODE})}.
}}


For example, SRI files are {fn PRESCAN}ed with {arg CHARLST} =
{lisp ((37 . 47) (47 . 37))}, which exchanges slash (47) and percent-sign (37).


The user should also make sure that the treatment of double quotes by the source and target systems is similar.  In Interlisp, an unmatched double-quote (unless protected by the escape character) will cause the rest of the file to read in as a string.

Finally, the lack of a {lisp STOP} at the end of a file is harmless, since TRANSOR will suppress {lisp END OF FILE} errors and exit normally.

}{End SubSec Using TRANSOR}



{Begin SubSec Translating}
{Title Translating}
{Text

{fn TRANSOR} is the top-level function of the translator itself, and takes one argument, a file to be translated.  The file is assumed to contain a sequence of forms, which are read in, translated, and output to a file called {lisp {bracket FILE}.TRAN}.  The translation notes are meanwhile output to {lisp {bracket FILE}.LSTRAN}.{index translation notes (in TRANSOR)}  Thus the usual sequence for bring a foreign file to Interlisp is as follows: {fn PRESCAN} the file; examine code and transformations, making changes to the transformations if needed; {fn TRANSOR} the file; and clean up remaining problems, guided by the notes.  The user can now make a pretty file and proceed to exercise and check out his program.  To export a file, it is usually best to TRANSOR it, then {fn PRESCAN} it, and perform clean-up on the foreign system where the file can be loaded.


{FnDef {Name TRANSOR} {Args FILE}
{Text
Translates {arg FILE}.  Prettyprints translation on {lisp {bracket FILE}.TRAN};
translation listing on {lisp {bracket FILE}.LSTRAN}.
}}


{FnDef {Name TRANSORFORM} {Args FORM}
{Text
{arg FORM} is a LISP form.  Returns the (destructively) translated form.
The translation listing is dumped to the primary output file.
}}


{FnDef {Name TRANSORFNS} {Args FNLST}
{Text
{arg FNLST} is a list of function names whose interpreted definitions
are destructively translated.  Listing to primary output file.
}}


{fn TRANSORFORM} and {fn TRANSORFNS} can be used to translate expressions that are already in core, whereas {fn TRANSOR} itself only works on files.

}{End SubSec Translating}



{Begin SubSec The Translation Notes}
{Title The Translation Notes}
{Text

{index *BEGIN* translation notes (in TRANSOR)}

The translation notes are a catalog of changes made in the user's code, and of problems which require, or may require, further attention from the user.  This catalog consists of two cross-indexed sections: an index of forms and an index of notes.  The first tabulates all the notes applicable to any form, whereas the second tabulates all the forms to which any one note applies.  Forms appear in the index of forms in the order in which they were encountered, i.e. the order in which they appear on the source and output files.  The index of notes shows the name of each note, the entry numbers where it was used, and its text, and is alphabetical by name.  The following sample was made by translating a small test file written in SRI LISP.

{lispcode
     LISTING FROM TRANSORING OF FILE TESTFILE.;7
     DONE ON 1-NOV-71 20:10:47

                                                     INDEX OF FORMS
1. APPLY/EVAL at
  [DEFINEQ
      (FSET (LAMBDA &
            (PROG ...3...
                (SETQ Z (COND
                    ((ATOM (SETQ --))
                     (COND
                       ((ATOM (SETQ Y (NLSETQ "(EVAL W)")))
                        --)
                     --))
                    --))
            --  ]
2. APPLY/EVAL at
  [DEFINEQ 
     (FSET (LAMBDA &
            (PROG ...3...
                 (SETQ Z (COND
                    ((ATOM (SETQ --))
                      (COND
                    ((ATOM (SETQ --))
                         "(EVAL (NCONS W))")
                        --))
                    --))
            --  ]
3. MACHINE-CODE at
  [DEFINEQ 
     (LESS1 (LAMBDA &
            (PROG ...3...
                 (COND
                   ...2...
                   ((NOT (EQUAL (SETQ X2 "(OPENR (MAKNUM & -))"
                               )
                             --))
                    --))
            --   ]
4. MACHINE-CODE at
  [DEFINEQ 
     (LESS1 (LAMBDA &
            (PROG ...3...
                 (COND
                   ...2...
                   ((NOT (EQUAL & (SETQ Y2
                               "(OPENR (MAKNUM & --))")))
                    --))
              --   ]

                                                     INDEX OF NOTES
APPLY/EVAL at 1, 2.
     TRANSOR will translate the arguments of the APPLY or EVAL expression, but the user must make sure that the run-time evaluation of the arguments returns a BBN-compatible expression.
MACHINE-CODE at 3, 4.
     Expression dependent on machine-code.  User must recode.}



The translation notes are generated by the transformations used, and therefore reflect the judgment of their author as to what should be included.  Straightforward conversions are usually made without comment; for example, the {lisp DEFPROP}s in this file were quietly changed to {fn DEFINEQ}s.  TRANSOR found four noteworthy forms on the file, and printed an entry for each in the index of forms, consisting of an entry number, the name of the note, and a printout showing the precise location of the form.  The form appears in double-quotes and is the last thing printed, except for closing parentheses and dashes.  An ampersand represents one non-atomic element not shown, and two or more elements not shown are represented as {lisp ...{arg N}...}, where {arg N} is the number of elements.  Note that the printouts describe expressions on the output file rather than the source file; in the example, the {lisp DEFPROP}s of SRI LISP have been replaced with {fn DEFINEQ}s.

{index *END* translation notes (in TRANSOR)}

}{End SubSec The Translation Notes}




{Begin SubSec Errors and Messages}
{Title Errors and Messages}
{Text


TRANSOR records its progress through the source file by terminal printouts which identify each expression as it is read in.  Progress within large expressions, such as a long {fn DEFINEQ}, is reported every three minutes by a printout showing the location of the sweep.


If a transformation fails, TRANSOR prints a diagnostic to the teletype which identifies the faulty transformation, and resumes the sweep with the next form.
The translation notes will identify the form which caused this failure, and the extent to which the form and its arguments were compromised by the error.


If the transformation for a common function fails repeatedly, the user can type control-H.  When the system goes into a break, he can use {fn TRANSORSET} to repair the transformation, and even test it out (see {lisp TEST} command, {PageRef (Transorset Command) TEST}).  He may then continue the main translation with {lisp OK}.

}{End SubSec Errors and Messages}



{Begin SubSec TRANSORSET}
{Title TRANSORSET}
{Text


To use {index *PRIMARY* TRANSORSET FN}{fn TRANSORSET},
type {lisp (TRANSORSET)} to Interlisp.  {fn TRANSORSET} will respond with a {lisp +} sign, its prompt character, and await input.  The user is now in an executive loop which is like {fn EVALQT} with some extra context and capabilities intended to facilitate the writing of transformations.  {fn TRANSORSET} will thus progress {fn APPLY} and {fn EVAL} input, and execute history commands just as {fn EVALQT} would.  Edit commands, however, are interpreted as additions to the transformation on which the user is currently working.  {fn TRANSORSET} always saves on a variable named {index CURRENTFN Var}{var CURRENTFN} the name of the last function whose transformation was altered or examined by the user.  {var CURRENTFN} thus represents the function whose transformation is currently being worked on.
Whenever edit commands are typed to the {lisp +} sign, {fn TRANSORSET} will add them to the transformation for {var CURRENTFN}.  This is the basic mechanism for writing a transformation.  In addition, {fn TRANSORSET} contains commands for printing out a transformation, editing a transformation, etc., which all assume that the command applies to {var CURRENTFN} if no function is specified.  The following example illustrates this process.

{lispcode
←TRANSORSET()
+FN TCONC                     {it [1]}
TCONC
+(SW 2 3)                     {it [2]}
+TEST (TCONC A B)             {it [3]}
P
(TCONC B A)
+TEST (TCONC X)               {it [4]}
TRANSLATION ERROR: FAULTY TRANSFORMATION
TRANSFORMATION: ((SW 2 3))    {it [5]}
OBJECT FORM:       (TCONC X)

1. TRANSFORMATION ERROR AT    {it [6]}
  "(TCONC X)"

(TCONC X)
+(IF (## 3) ((SW 2 3)) ((-2 NIL]    {it [7]}
+SHOW
TCONC
  [(SW 2 3)
   (IF (## 3)                  {it [8]}
       ((SW 2 3))
       ((-2 NIL]
TCONC
+ERASE                         {it [9]}
TCONC
+REDO IF                       {it [10]}
+SHOW
TCONC
  [(IF (## 3)
       ((SW 2 3))
       ((-2 NIL]
TCONC
+TDST
=TEST                          {it [11]}
(TCONC NIL X)
+}


In this example, the user begins by using the {lisp FN} command to set {var CURRENTFN} to {lisp TCONC} {it [1]}.  He then adds to the (empty) transformation for {fn TCONC} a command to switch the order of the arguments {it [2]} and tests the transformation {it [3]}.  His second {lisp TEST} {it [4]} fails, causing an error diagnostic {it [5]} and a translation note {it [6]}.  He writes a better command {it [7]} but forgets that the original {lisp SW} command is still in the way {it [8]}.  He therefore deletes the entire transformation {it [9]} and redoes the {lisp IF} {it [10]}.  This time, the {lisp TEST} works {it [11]}.

}{End SubSec TRANSORSET}




{Begin SubSec TRANSORSET Commands}
{Title TRANSORSET Commands}
{Text

The following commands for manipulating transformations are all Prog. Asst. commands which treat the rest of their input line as arguments.  All are undoable.



{Def {Type (Transorset Command)}  {Name FN}
{Text
Resets {var CURRENTFN} to its argument, and returns the new value.  In effect {lisp FN} says you are done with the old function (as least for the moment) and wish to work on another.  If the new function already has a transformation, the message {lisp (OLD TRANSFORMATIONS)} is printed, and any editcommands typed in will be added to the end of the existing commands.  {lisp FN} followed by a carriage return will return the value of {var CURRENTFN} without changing it.
}}



{Def {Type (Transorset Command)}  {Name SHOW}
{Text
Command to prettyprint a transformation.
{lisp SHOW} followed by a carriage return will show the transformation for {var CURRENTFN}, and return {var CURRENTFN} as its value.
{lisp SHOW} followed by one or more function names will show each one in turn, reset {var CURRENTFN} to the last one, and return the new value of {var CURRENTFN}.
}}



{Def {Type (Transorset Command)}  {Name EDIT}
{Text
Command to edit a transformation.
Similar to {lisp SHOW} except that instead of prettyprinting the transformation, {lisp EDIT} gives it to {fn EDITE}.
The user can then work on the transformation until he leaves the editor with {lisp OK}.
}}



{Def {Type (Transorset Command)}  {Name ERASE}
{Text
Command to delete a transformation.
Otherwise similar to {lisp SHOW}.
}}



{Def {Type (Transorset Command)}  {Name TEST}
{Text
Command for checking out transformations.  {lisp TEST} takes one argument, a form for translation.  The translation notes, if any, are printed to the teletype, but in an abbreviated format which omits the index of notes.  The value returned is the translated form.  {lisp TEST} saves a copy of its argument on the free variable {var TESTFORM},{index TESTFORM Var} and if no argument is given, it uses {var TESTFORM}, i.e. tries the previous test again.
}}



{Def {Type (Transorset Command)}  {Name DUMP}
{Text
Command to save your work on a file.  {lisp DUMP} takes one argument, a filename.  The argument is saved on the variable {var DUMPFILE},{index DUMPFILE Var} so that if no argument is provided, a new version of the previous file will be created.

The {lisp DUMP} command creates files by {fn MAKEFILE}.  Normally {lisp {arg FILE}FNS} will be unbound, but the user may set it himself;  functions called from a transformation by the {lisp E} command may be saved in this way.
{lisp DUMP} makes sure that the necessary command is included on the {lisp {arg FILE}VARS} to save the user's transformations.  The user may add anything else to his {lisp {arg FILE}VARS} that he wishes.  When a transformation file is loaded, all previous transformations are erased unless the variable {var MERGE}{index MERGE Var} is set to {lisp T}.
}}



{Def {Type (Transorset Command)}  {Name EXIT}
{Text
Exits {fn TRANSORSET}, returning {lisp NIL}.
}}


}{End SubSec TRANSORSET Commands}



{Begin SubSec The REMARK Feature}
{Title The REMARK Feature}
{Text

The translation notes are generated by those transformations that are actually executed via an edit macro called {index REMARK (Transor Command)}{lisp REMARK}.  {lisp REMARK} takes one argument, the name of a note.  When the macro is executed, it saves the appropriate information for the translation notes, and adds one entry to the index of forms.
The location that is printed in the index of forms is the editor's location when the {lisp REMARK} macro is executed.


To write a transformation which makes a new note, one must therefore do two things: define the note, i.e. choose a new name and associate it with the desired text; and call the new note with the {lisp REMARK} macro, i.e. insert the edit command {lisp (REMARK {arg NAME})} in some transformation.  The {index NOTE (Transor Command)}{lisp NOTE} command, described below, is used to define a new note.  The call to the note may be added to a transformation like any other edit command.  Once a note is defined, it may be called from as many different transformations as desired.


The user can also specify a remark with a new text, without bothering to think of a name and perform a separate defining operation, by calling {lisp REMARK} with more than one argument, e.g. {lisp (REMARK {arg TEXT-OF-REMARK})}.
This is interpreted to mean that the arguments are the text.  {fn TRANSORSET} notices all such expressions as they are typed in, and handles naming automatically; a new name is generated{foot
The name generated is the value of {var CURRENTFN} suffixed with a colon,
or with a number and a colon.
}{comment endfootnote}
and defined with the text provided, and the expression itself is edited to be
{lisp (REMARK {arg GENERATED-NAME})}.
The following example illustrates the use of {lisp REMARK}.

{lispcode
←TRANSORSET()
+NOTE GREATERP/LESSP (BBN'S GREATERP AND LESSP ONLY TAKE TWO ARGUMENTS, WHEREAS SRI'S FUNCTIONS TAKE AN INDEFINITE NUMBER. AT THE PLACES NOTED HERE, THE SRI CODE USED MORE THAN TWO ARGUMENTS, AND THE USER MUST RECODE.]   {it [1]}
GREATERP/LESSP
+FN GREATERP
GREATERP
+(IF (IGREATERP (LENGTH (##))3) NIL ((REMARK GREATERP/LESSP]   {it [2]}
+FN LESSP
LESSP
+REDO IF   {it [3]}
+SHOW
LESSP
  [(IF (IGREATERP (LENGTH (##))
                  3)
       NIL
       ((REMARK GREATERP/LESSP]
LESSP
+FN ASCII
(OLD TRANSFORMATIONS)
ASCII
+(REMARK ALTHOUGH THE SRI FUNCTION ASCII IS IDENTICAL TO THE BBN FUNCTION CHARACTER, THE USER MUST MAKE SURE THAT THE CHARACTER BEING CREATED SERVES THE SAME PURPOSE ON BOTH SYSTEMS, SINCE THE CONTROL CHARACTERS ARE ALL ASSIGNED DIFFRENTLY.]   {it [4]}
+SHOW   {it [5]}
ASCII
  ((1 CHARACTER)
   (REMARK ASCII:))
ASCII
+NOTE ASCII:   {it [6]}
EDIT
*NTH -2
*P
... ASSIGNED DIFFRENTLY.)
*(2 DIFFERENTLY.)
OK
ASCII:
+}


In this example, the user defines a note named {lisp GREATERP/LESSP} by using the {lisp NOTE} command {it [1]}, and writes transformations which call this note whenever the sweep encounters a {lisp GREATERP} or {lisp LESSP} with more than two arguments {it [2]} and {it [3]}.  Next, the implicit naming feature is used {it [4]} to add a {lisp REMARK} command to the transformation for ASCII, which has already been partly written.  The user realizes he mistyped part of the text, so he uses the {lisp SHOW} command to find the name chosen for the note {it [5]}.  Then he uses the {lisp NOTE} command on this name, ASCII:, to edit the note {it [6]}.



{Def {Type (Transorset Command)}   {Name NOTE}
{Text
First argument is note name and must be a literal atom.  If already defined, {lisp NOTE} edits the old text; otherwise it defines the name, reading the text either from the rest of the input line or from the next line.  The text may be given as a line or as a list.  Value is name of note.
}}




The text is actually stored.{foot
On the global list {var USERNOTES}.{index USERNOTES Var}
}{comment endfootnote}
as a comment, i.e. a {lisp *} and {lisp %%} are added in front when the note
is first defined.  The text will therefore be lower-cased the first time the user {lisp DUMP}s (see {PageRef Tag LowerCaseComments}).



{Def {Type (Transorset Command)}   {Name DELNOTE}
{Text
Deletes a note completely (although any calls to it remain in the transformations).
}}

}{End SubSec The REMARK Feature}



{Begin SubSec Controlling the Sweep}
{Title Controlling the Sweep}
{Text

{index TRANSOR sweep}

TRANSOR's sweep searches in print-order until it finds a form for which a transformation exists.  The location is marked, and the transformation is executed.
The sweep then takes over again, beginning from the marked location, no matter where the last command of the transformation left the editor.  User transformations can therefore move around freely to examine the context, without worrying about confusing the translator.  However, there are many cases where the user wants his transformation to guide the sweep, usually in order to direct the processing of special forms and {lisp FEXPR}s.  For example, the transformation for {lisp QUOTE} has only one objective: to tell the sweep to skip over the argument to {lisp QUOTE}, which is (presumably) not a LISP form.  {lisp NLAM} is an edit macro that permits this.


{Def {Type (Transorset Command)}   {Name NLAM}
{Text
An atomic edit macro which sets a flag which causes the sweep to skip the arguments of the current form when the sweep resumes.
}}


Special forms such as {fn COND}, {fn PROG}, {fn SELECTQ}, etc., present a more difficult problem.  For example, {lisp (COND (A B))} is processed just like {lisp (FOO (A B))}: i.e. after the transformation for {fn COND} finishes, the sweep will locate the "next form," {lisp (A B)}, retrieve the transformation for the function {lisp A}, if any, and execute it.  Therefore, special forms must have transformations that preempt the sweep and direct the translation themselves.  The following two atomic edit macros permit such transformations to process their forms, translating or skipping over arbitrary subexpressions as desired.

{Def {Type (Transorset Command)}   {Name DOTHIS}
{Text
Translates the editor's current expression, treating it as a single form.
}}


{Def {Type (Transorset Command)}   {Name DOTHESE}
{Text
Translates the editor's current expression, treating it as a list of forms.
}}



For example, a transformation for {fn SETQ} might be {lisp (3 DOTHIS)}.{foot
Recall that a transformation is a list of edit commands.  In this case, there are two commands, {lisp 3} and {lisp DOTHIS}.
}{comment endfootnote}
This translates the second argument to a {fn SETQ} without translating the first.
For {fn COND}, one might write {lisp (1 (LPQ NX DOTHESE))}, which locates each clause of the {fn COND} in turn, and translates it as a list of forms, instead of as a single form.

The user who is starting a completely new set of transformations must begin by writing transformations for all the special forms.  To assist him in this and prevent oversights, the file {lisp <LISP>SPECIAL.XFORMS} contains a set of transformations for LISP special forms, as well as some other transformations which should also be included.  The user will probably have to revise these transformations substantially, since they merely perform sweep control for Interlisp, i.e. they make no changes in the object code.  They are provided chiefly as a checklist and tutorial device, since these transformations are both the first to be written and the most difficult, especially for users new to the Interlisp editor.


When the sweep mechanism encounters a form which is not a list, or a form {fn CAR} of which is not an atom, it retrieves one of the following special transformations.


{VarDef {Name NLISTPCOMS}
{Text
Global value is used as a transformation for any form which is not a list.
}}


For example, if the user wished to make sure that all strings were quoted, he might set {var NLISTPCOMS} to {lisp ((IF (STRINGP (##)) ((ORR ((← QUOTE))((MBD QUOTE)))) NIL))}.


{VarDef {Name LAMBDACOMS}
{Text
Global value is used as a transformation for any form, {fn CAR} of which is not an atom.
}}


These variables are initialized by {lisp <LISP>SPECIAL.XFORMS} and are saved by the {lisp DUMP} command.  {var NLISTPCOMS} is initially {lisp NIL}, making it a NO-OP.  {var LAMBDACOMS} is initialized to check first for open {lisp LAMBDA} expressions, processing them without translation notes unless the expression is badly formed.  Any other forms with a non-atomic {fn CAR} are simply treated as lists of forms and are always mentioned in the translation notes.  The user can change or add to this algorithm simply by editing or resetting {var LAMBDACOMS}.

}{End SubSec Controlling the Sweep}


{index *END* TRANSOR Fn}

}{End SubSec TRANSOR}