2.1 How to Run it

We saw before only definitions of separate functions in Refal. Now we want to write a simple program and run it at an IBM microcomputer (XT or AT) under the operating system MS-DOS. Running it with other systems is similar.

A function to translate an Italian word into English was defined in Sec. 1.6. Suppose we want to translate and print out Italian words typed in at the terminal. Let the words in a line be separated by any number of blanks. We want our program to read a line, translate each word, and print out the translated line. Then the program should ask for the next line. If an Italian word cannot be found in the table, it must be replaced by *** .

We need some means to exchange information with the Refal machine. Some basic input and output functions must be provided with the system, because they obviously cannot be defined by Refal sentences. Functions that are not defined in Refal but can still be used in the system are referred to as built-in functions. The list of built-in functions of Refal-5, along with a short description of what they do, can be found in Reference Section C. The reader is advised to go through Reference Section C before starting actual programming in Refal in order to know what built-in functions are available.

There are two built-in functions which we need for our problem: Card and Prout . When <Card> is evaluated, the Refal machine waits for the user to type in a line * and terminate it by a carriage return. Then it reads the line and replaces the function call by the result which is treated as a string of characters. If the user enters the special End-Of-File character, which is Control-Z in MS-DOS, <Card> will be replaced by the number . This is a signal to the Refal program that there are no more lines to read. Recall that characters, words, and numbers are all different symbols of Refal. Since Card treats a line as a sequence of characters, the number 0 cannot be confused with the contents of any line.

When <Prout  E > , where E is some expression, is evaluated the machine prints out E and erases the call. There is also another output function, Print , which does not destroy its argument.

We start programming from the top; i.e., by first defining the general structure of the program, and then working out details. Our program translates the input by lines, therefore we need a function -- let it be called Trans-line -- such that if E is an input line, the evaluation of:

<Trans-line E >

produces its translation. Then we must have a control function which calls <Card> , checks whether the result is 0 and either calls Trans-line or declares the end of work. Let us give the name Job to this top-level function. Since Job is to analyze the result of Card , it must be called in this way:

  <Job <Card>> 

Now let us make an attempt to define Job in two sentences corresponding to the two cases of its argument:

  Job { 0 = ; 
        eX = <Trans-line eX>; }

If the result of <Card> is 0 , the view field becomes empty, i.e., passive and this is the end of work. Otherwise Trans-line will translate the line and put it in the view field. This is not exactly what we want. Firstly, we want the result to be printed out and not just left in the view field. Secondly, a request for the next line should be made which means that <Job_<Card>> must be put into the view field again. Recall that parallel (i.e., not nested in one another) active sub-expressions become active from left to right. Therefore, if we want some actions to be done in succession, we concatenate the corresponding active sub-expressions in the required order. We come to the following definition:

  Job { 0 = ; 
        eX = <Prout <Trans-line eX>>
        <Job <Card>>; }

Now let us define Trans-line . Consider what possible cases of its argument we must distinguish. First, it may be a blank. We should ignore it and apply Trans-line to the rest:

  Trans-line ' 'eX  =  <Trans-line eX>;  

If there are several blanks before the words, this sentence will eliminate all of them, step by step. If the first character is not a blank, it must be a letter which starts a word. Since words are separated by blanks, we can separate the first word using the pattern:

  e.Word ' ' e.Rest 

According to the rules of left-to-right matching, the blank in this pattern will be matched to the first blank in the argument (if there are any). Therefore, e.Word will become the first word. We translate it using the function Trans defined in Sec. 1.6. The English word produced by Trans must be separated by a blank from the words which will be produced by the recursive call of Trans-line . Thus we have the sentence:

  e.Word' 'e.Rest =  
            <Trans (e.Word)<Table>>' '
            <Trans-line e.Rest>; 

There are two more possibilities. One is that the line is used up (i.e., empty) in which case the computation of Trans-line is over. The other is that the whole remaining string is one word. The complete definition is:

  Trans-line {
     ' 'e.X = <Trans-line eX>; 
     e.Word' 'e.Rest = 
                <Trans (e.Word)<Table>>' '
                <Trans-line e.Rest>;
        = ;
     e.Word = <Trans (e.Word)<Table> >' '; }
NOTE: e.Word in the last sentence cannot be empty, because of the preceding sentence.

We have now defined all the functions necessary for the program. But how to put the initial expression in the view field of the Refal machine? Refal-5 uses the convention according to which the initial state of the view field is always:

  <Go> 
This means that among the functions defined in an executable program there must always be the standard function Go with no arguments:
  $ENTRY Go { =  the initial Refal expression  }
The prefix $ENTRY declares the function Go as an entry function which can be called from any module of the program (the modular structure of programs is described in the next section). In our case the initial expression should be <Job <Card>> .

NOTE: Function Go cannot be called by itself or any other function. It serves only to initiate the view field.

Now we collect all the functions into one program and add some comments. We follow the rule that line comments are put before -- and other comments after -- the text to which they refer:


  * Translation from Italian into English
  $ENTRY Go { = <Job <Card>>  }
   
  * The top-level control function.
  Job {
     0 = ;                  /* end of file */
     eX = <Prout <Trans-line eX>>
          <Job <Card>>; }
   
  * Translate one line
  Trans-line {
     ' 'eX = <Trans-line eX>; 
     e.Word' 'e.Rest = 
                 <Trans (e.Word) <Table>>' '
                 <Trans-line e.Rest>;
      = ;
     e.Word = <Trans (e.Word) <Table>>' '; }
   
  * Italian-English dictionary table
  Table { = (('cane') 'dog')
            (('gatto') 'cat')
            (('cavallo') 'horse')
            (('rana') 'frog')
            (('porco') 'pig') 
         } 
  * Translate Italian word into English by table
  Trans { 
     (e.It)e1((e.It)e.Eng)e2 = e.Eng;
     (e.It)e1 = '***'; /* word not in table */
        }

In order to run this program, type it in using any text-editor and save it as a file with the extension .ref (e.g., prog1.ref ). Now enter the command:

  refc prog1
It calls the Refal compiler which translates the program into an intermediate language RASL (about which the user need not know anything except that it exists) and stores it under the name prog1.rsl . If the compiler finds syntax errors in the program, it sends the appropriate message and creates a file under the name prog1.lis which contains the listing of the program with the indication of the errors discovered. If there are no syntax errors, no listing is created.

To run our program, call the Refal machine by entering:

  refgo prog1
This will cause the RASL file prog1.rsl to be loaded from the disk into the main storage. Then the interpretation of the program, i.e., the emulation of the abstract Refal machine, will start. When <Card> becomes the primary leading expression in the view field, the Refal machine will stop and wait for us to enter a line from the terminal. Type in:
  rana cane     rana vacca   porco 
The system will print out
  frog dog frog *** pig 
Type in:
  cavallo 
and the answer will be:
  horse 
Enter the End-of-File character (control-Z), and the program terminates.

When you test and debug your program, the Refal tracer is very useful. To use it, enter reftr instead of refgo :

  reftr prog1
The tracer recognizes a dozen commands which control the execution of the program. You can print out the current view field or only the leading active sub-expression. You can make any number of steps and then print out the view field or the active sub-expression again. It is also possible to run the machine until it completes the evaluation of the current active sub-expression and then print out the resulting object expression. A finer way of controlling the computation process is to define a number of patterns as break points. Then the Refal machine will stop and ask for further commands each time the active sub-expression matches one of the patterns.

See Reference Section D for the list of commands of the tracer.

Using the function Go is the basic way to initiate the work of the Refal machine. The other way is through the services of a Refal program with the very short name E (from Evaluator ) which is supplied on the system diskette. This program allows us to put active Refal expressions into the view field and evaluate them without calling refgo or reftr each time. This has two effects. First, an arbitrary expression can be evaluated, not just the standard <Go> . Second, each time we call refgo or reftr we load the program from the disk into main storage. Using E we do it only once, while all subsequent evaluations (in particular, the evaluation of <Go> , if we want to run the whole program repeatedly) are done without reloading the program. The evaluator will be described in Sec. 6.3 (see also Reference Section A). At this point we only want to illustrate how this method works.

Let us add the line:

  $ENTRY Upd {eX = <Mu eX>;}
to our program prog1 . Then enter:
  refgo e+prog1
The following message appears on the terminal:
  Type expression to evaluate. To end: empty line.
  To end session: empty expression
Type in:
  <Table>
and hit the Return key twice to enter an empty line. The system prints out:
  The result is:

    (('cane') 'dog')
    (('gatto') 'cat')
    (('cavallo') 'horse')
    (('rana') 'frog')
    (('porco') 'pig') 
and repeats the invitation to type in another expression. The Refal expression you submit may include calls of any functions defined in prog1 . It also may include calls of standard built-in functions. Suppose you typed in:
  <+ 2 <* 3 <* 4 5>>>
The system answers with the result 62 .

Any combination of functions can be called. In particular, you can call <Go> which refers to the starting function of prog1 . The effect will be the same as if you directly executed prog1 , except that:

  1. The final value of the Go will be printed out (while it would be lost after a direct execution).
  2. After the end of evaluation the system will invite you to submit another expression for evaluation. You can evaluate Go again, without reloading the program from the disk.
Exercise 2.1 Modify prog1 so that the system prints out Please enter a line before expecting input, and The translation is: before printing the translation.

Footnotes:

  1. The name of the function is left over from the times when input information was mostly read from punched cards.