Legato
Legato

GoFiler Legato Script Reference

 

Legato v 1.4j

Application v 5.22b

  

 

Chapter TwoLanguage Overview

2.1 Some Simple Programs

2.1.1 Overview

This chapter illustrates some basic Legato programs to provide a quick overview of Legato’s operation and structure. While some of the examples have little or nothing to do with text processing or EDGAR, they demonstrate basic program structure and give the developer a flavor of program operation.

This section covers script examples at a generalized level while the following sections provide a brief description of Legato variables, flow control, and program operation. If the reader is familiar with structured language concepts, the manner in which these example programs function will be apparent. For those not familiar with such concepts, these examples give an overview of how simple programs operate in a structured environment. All of these examples can be copied and pasted into the application and run. Please note that if the plain text is copied to a text window, instead of a Legato IDE window, the application will not consider the text a Legato script and will therefore not run it. The functions employed in these scripts can be located in chapters 5 and 6.

Generally, Legato operates within the confines of the desktop application such as GoFiler. However, Legato can operate as a console application, in which case, the default interface is the console interface. In console mode, many high-level functions, such a those relating to edit windows, are not available.

2.1.2 Temperature Conversion

The following is a simple program to convert Fahrenheit temperatures to Celsius:

//
//      Create Simple Fahrenheit to Celsius Table
//
//      Based on K&R
//

    int         fahr, celsius;                  // Working values
    int         lower, upper;                   // Limits
    int         step;                           // Step amount
    
    lower = 0;                                  // Set the lower limit
    upper = 300;                                // Set the upper limit
    step = 5;                                   // Step amount 5 deg Fahrenheit
    
    fahr = lower;                               // Set starting position
    while (fahr <= upper) {                     // Loop until end upper limit
      celsius = 5 * (fahr - 32) / 9;            // Basic conversion 
      AddMessage("Fahr %d Cel %d", fahr, celsius);
      fahr += step;                             // Move forward on steps
      }                                         // Continue

To use this program in GoFiler, copy it to the clipboard. Within the application, press File | New, then select Legato Script (at the bottom of the list). Paste the program and press F7 (run). A table will appear in the Information View under the Legato Log tab:

This table is the result of the script just run. In addition, the Legato Errors tab contains information about script execution, including errors and warnings. In the event of an error, the Legato Log tab may not appear. For more information about the Legato IDE, see Section 2.6 Integrated Development Environment (IDE).

The above script consists of three basic sections: variable declaration, initialization, and a calculation loop. In the declaration stage, all the of variables are declared as basic integers (32-bit signed values). This means that fractional amounts are automatically truncated (‘3.2’ becomes ‘3’). The initialization stage sets the range from 0 degrees to 300 degrees and a step value of 5 for creating the table. Finally, there is the loop that runs until the value of fahr exceeds the value of upper range.

Resulting data is formatted for presentation to the default log using the AddMessage function. C/C++ programmers will note the similarity to the printf statement for formatting strings.

Some notes of interest: All program statements are ended with a semicolon (‘;’) or the start of a block as denoted by an open brace (‘{’). Braces indicate a group of code to be executed together and must be started with an open brace and ended with a corresponding closing brace (‘}’). All variables must be declared. Finally, in the above case, there is no entry point. By default, if the script engine does not locate a ‘main’ or ‘entry’ routine, it starts running the first line of global code it encounters.

2.1.3 A Temperature Variation

While temperature can be expressed as whole number values, temperature conversion might be better derived using real numbers. Integers can only represent whole numbers, but floating-point numbers contain real numbers and thus fractional values. To change the variables’ data types from int to float, simply alter the declarations as follows:

    float       fahr, celsius;                  // Working values
    float       lower, upper;                   // Limits
    float       step;                           // Step Amount
    

Since fractional values can now be printed, the output message should be changed to reflect this:

      AddMessage("Fahr %3.0f Cel %1.1f", fahr, celsius);

This changes the %d to %f with a format modification. The %d translates the corresponding parameter to a decimal integer while the %f indicates the parameter is expected to be floating-point. The 3.0 indicates the whole number portion should be printed and the 1.1 indicates that the script should print out at least one leading digit and only one trailing digit. The output is now:

    Fahr   0 Cel -17.8
    Fahr   5 Cel -15.0
    Fahr  10 Cel -12.2
    Fahr  15 Cel -9.4
    Fahr  20 Cel -6.7
    Fahr  25 Cel -3.9
    Fahr  30 Cel -1.1
    Fahr  35 Cel 1.7
    Fahr  40 Cel 4.4

In addition, using a convert subroutine simplifies the logic:

float convert (float);

int main () { 

    float       fahr, celsius;                  // Working values
    float       lower, upper;                   // Limits
    float       step;                           // Step Amount
    
    lower = 0;                                  // Set the lower limit
    upper = 300;                                // Set the upper limit
    step = 5;                                   // Step amount 5 deg Fahrenheit
    
    fahr = lower;                               // Set staring position
    while (fahr <= upper) {                     // Loop until end upper limit
      celsius = convert(fahr);                  // Convert
      AddMessage("Fahr %3.0f Cel %1.1f", fahr, celsius);
      fahr = fahr + step;                       // Move forward on steps
      }                                         // Continue
    }
    
float convert(float f) {
    f = 5.0 * (f - 32.0) / 9.0;                 // '.0' to force float
    return f;
    }

In the above case, a subroutine or function was created that performs the conversion. In addition, for consistency, the main part of the program was moved into a function called main which is now the entry point. The short copy of the function name at the start of the program tells the script engine that convert is a function that expects to have passed to it a float parameter and returns a float parameter. Declaring a function like this is known as prototyping. Placing the conversion routine prior to its first reference makes prototyping unnecessary since the function is already declared by the time the engine encountered its first reference.

2.1.4 Display the Contents of a Folder

In this next example, the contents of a folder will be displayed in the log window. This not the most efficient way of performing this particular task but the verbose method helps illustrate a number of features and functions.

    handle      hEnum;
    string      nTarget;
    string      s1, s2, s3;
    int         rc, files, folders;
    qword       size, total, time, mt;
   

// -----------------------------------------------------------  GetFirstFile
    s1 = GetScriptFilename();
    s1 = GetFilePath(s1);
    nTarget = s1 + "*.*";
    nTarget = "C:\\Windows\\*.*";                    // something known
    hEnum = GetFirstFile(nTarget);
    if (hEnum == 0) {
      MessageBox('X', "Could not open path %s", s1);
      exit();
      }
    AddMessage("Directory for %s", s1);
    AddMessage("");
    AddMessage("-------Modified-------  --Type--   ---Size---   --Name------------------------");

// -----------------------------------------------------------  Dump Folders
    s1 = GetName(hEnum);
    while (s1 != "") {
      s2 = GetFileAttributeString(hEnum);
      rc = InString(s2, "d");
      if (rc > 0) {
        folders++;
        mt = GetFileModifiedTime(hEnum);
        s3 = FormatDate(mt, 0x02000031);
        AddMessage("%-22s  %8s           -- : [%s]", s3, s2, s1);
        }
      GetNextFile(hEnum);
      s1 = GetName(hEnum);
      }
    CloseFile(hEnum);

// -----------------------------------------------------------  Dump Files
    hEnum = GetFirstFile(nTarget);
    s1 = GetName(hEnum);
    while (s1 != "") {
      s2 = GetFileAttributeString(hEnum);
      rc = InString(s2, "d");
      if (rc < 0) {
        files++;
        size = GetFileSize(hEnum);
        total += size;
        mt = GetFileModifiedTime(hEnum);
        s3 = FormatDate(mt, 0x02000031);
        AddMessage("%-22s  %8s %12a : %s", s3, s2, size, s1);
        }
      GetNextFile(hEnum);
      s1 = GetName(hEnum);
      }

// -----------------------------------------------------------  Wrap Up

    AddMessage("");
    AddMessage("Total %a file(s) for %a bytes and %a folder(s)", files, total, folders);
    CloseFile(hEnum);

Note the use of three new new data types: handle, string, and qword. A handle stores a unique identifier to an object and in this case, a FolderEnumeration object. A string contains chunks of text and a qword or ‘quad-word’ defines a 64-bit unsigned integer. The qword type is used to retrieve a date/time value which is measured in 100-nanosecond intervals from January 1, 1601, so it can be a very large number.

At the beginning of the script, the target path is developed. An extra line of code has been added to allow the script to be copied and pasted into the host application and still operate. Since copying the above code into an untitled window would result in an undefined script name (unless saved), the lines of code concerning retrieving the script name and path would not work. The string literal specifying the path for Windows contains double backslashes. The reason for this is that the backslash character has special meaning within a string literal so to use this character as part of the text, it must be escaped. Here “\\” actually means ‘\’. This is covered in more detail in the next chapter.

The main function in this example is the GetFirstFile function which takes the target path along with a wildcard mask (*.*) and enumerates any files and folders located at that specified point. Assuming something is found, a handle value to a FolderEnumeration object, which contains the results, will be returned. If not, the return value will be 0. Note that even empty locations will contain at least the folders ‘.’ and ‘..’. Once a valid handle is returned, the script loops through and dumps folders to the log. A second pass is made to enumerate the files.

During the enumeration process, the total files, folders, and bytes are collected to be printed at the end. The result might look something like:

Directory for C:\Windows\*.*

-------Modified-------  --Type--  ----Size----   --Name-----------------------------
03/26/2014 03:33:37 PM  ---d----           -- : [.]
03/26/2014 03:33:37 PM  ---d----           -- : [..]
07/14/2009 01:32:39 AM  ---d----           -- : [addins]
07/13/2009 11:20:08 PM  ---d----           -- : [AppCompat]
     . . . 
03/30/2011 05:39:01 PM  ----a---       22,387 : Ascd_tmp.ini
09/14/2012 11:00:12 AM  ----a---       38,452 : atiogl.xml
03/30/2011 05:32:09 AM  ----a---            0 : ativpsrm.bin
     . . .
07/13/2009 09:39:57 PM  ----a---       10,240 : write.exe

Total 50 file(s) for 8,659,644 bytes and 62 folder(s)

For brevity, only a few entries are shown.

2.1.5 Make a Wiki Request

The final example illustrates how Legato can be used to make a request to search a wiki page using a wiki API. The the script consists of a test jig and a function that makes the request and then parses the result to retrieve a page ID and name.

                                                  /****************************************/
                                                  /* ** Declarations                      */
    int  wiki_search    (string ss);              /* Prototype                            */
                                                  /*  * Global Data                       */
    string              s1, s2, s3;               /* General                              */
    string              r_page_id;                /* Returned Page ID                     */
    string              r_title;                  /* Returned Title                       */
    int                 rc;                       /* Return Code                          */


                                                  /****************************************/
                                                  /* ** Test Jig                          */
    rc = wiki_search("MSFT");                     /* Perform Search                       */

                                                  /* Result                               */
    MessageBox('I', "Result %d:\r\r    Page ID:%s\r    Title: %s", rc, r_page_id, r_title);
    exit;       

                                                  /****************************************/
int wiki_search(string ss) {                      /* Query Wikipedia                      */
                                                  /****************************************/
    handle              hParse;                   /* Word Parse Handle                    */
    string              wq, wr;                   /* Query/Result                         */
    string              s1;                       /* General                              */
                                                  /*                                      */    
                                                  /* ** Perform Seach                     */
                                                  /*  * Initialize                        */
    r_page_id = "";                               /* Clear the Page ID                    */
    r_title = "";                                 /* Clear the Title                      */
                                                  /*  * Build Query                       */
    wq  = "http://en.wikipedia.org/w/api.php?";   /* Main API query                       */
    wq .= "format=json&action=query";             /* Main query                           */
    wq .= "&titles=" + ss;                        /* Name                                 */
                                                  /*  * Perform Query                     */
    wr = HTTPGetString(wq);                       /* Go back                              */
    if (wr == "") {                               /* No good                              */
      return 0;                                   /* Not found                            */
      }                                           /* end empty                            */
    if (IsInString(wr, "missing")) {              /* No found                             */
      return 0;                                   /* Not found                            */
      }                                           /* end not found                        */
                                                  /*  * Look At JASON                     */
    hParse = WordParseCreate(3, wr);              /* Add in the string, in program mode   */
    s1 = WordParseGetWord(hParse);                /* Get the first item                   */
    while (s1 != "") {                            /* Loop through                         */
      if (s1 == "\"pageid\"") {                   /*  * Page ID                           */
        WordParseGetWord(hParse);                 /* Get the first item                   */
        r_page_id = WordParseGetWord(hParse);     /* Get the get item                     */
        }                                         /* end page ID                          */
      if (s1 == "\"title\"") {                    /*  * Page ID                           */
        WordParseGetWord(hParse);                 /* Get the first item                   */
        r_title = WordParseGetWord(hParse);       /* Get the get item                     */
        }                                         /* end title                            */
      s1 = WordParseGetWord(hParse);              /* Get the first item                   */
      }                                           /* End Scan loop                        */
    return 1;                                     /* Found Item                           */
    }                                             /* end search                           */

This script introduces a couple of new items. First, this example contains both global and local variables. Until now, all variable declarations were global simply because they were placed outside of any function. A global variable is available and visible to the entire program. Local variables exist only while the function is running and are only visible to that specific function. If the function calls other functions, local variables still persist but can only be accessed within the scope of that specific function. In the above example, hParse, wq, wr, and s1 will be created and destroyed every time the wiki_search function is entered and exited. Note that the handle for the WordParse object is not closed, but since it is local variable, it is automatically closed on exit of the function.

A query is formed using the source string ss and creates the contents of the variable wq (wiki query). This is then sent out on the Internet using the HTTPGetString function, the results of which are placed into wr (wiki result). A series of simple tests are performed to determine if anything was returned or if the query resulted in a missing page. The wiki request was made with a format of JSON (JavaScript Object Notation). A positive reply looks something like this:

{"query":{"pages":{"778754":{"pageid":778754,"ns":0,"title":"MSFT"}}}}~

In this case, JSON was selected because of its predictable format. The remainder of the function parses the reply and fills the r_page_id and r_title global variables. The script then displays:

The script queried the string “MSFT”, which is Microsoft’s trading symbol. This example can be built upon. Perhaps a query could be made, the result turned into a URI, and then directed to Internet Explorer or another web browser.