Gri: Adding new commands

Programming Gri 1: Introduction 2: Simple example 3: Fancy example 4: Running Gri 5: Programming Gri 6: General Issues 7: X-Y Plots 8: Contour Plots 9: Image Plots 10: Examples 11: Handling Data 12: Gri Commands 13: Gri Extras 14: Evolution of Gri 15: Installing Gri 16: Gri Bugs 17: System Tools 18: Acknowledgments 19: License 20: Newsgroup 21: Concept Index | ## 5.10: Adding new commands to Gri Gri can be extended easily. Primitive commands (e.g. ``set x name`') can be supplemented with so-called "new commands." New commands are a little like subroutines other programming languages. For example, you might find that you often draw filled curves with a particular graylevel (say 0.5), and then return the graylevel to the previous value. This requires you to do the following each time:
    new .old_graylevel.
    .old_graylevel. = ..graylevel..
    set graylevel 0.5
    draw curve filled to 0 y
    set graylevel .old_graylevel.
    delete .old_graylevel.
This gets a bit tedious, and it would obviously be nicer to just say something like
    Draw my kinda curve
To make this shortcut, you'd tell Gri about the existence of a new command called ``Draw my kinda curve`', and tell it that the new command can be accomplished by the longer code fragment written above. Once you've learned how to make new commands, you are likely to use them a lot. The following explains how you add new commands. For advice on programming style, etc, see Resource File.. Parsing: How Gri parses commands Simple New Command: Simple example of adding new command Complicated New Command: More complicated example ### 5.10.1: How Gri parses commands Whenever Gri reads a command line, it compares it with its list of commands. This list is searched in this order: (1) the universal ``gri.cmd`' file see Options On Command Line, (2) your resource file see Resource File., if it exists, and then (3) your command file itself. Gri stops searching when it finds a Gri command that matches the command line. "Matching" means that the command line is identical in all words in a Gri command, scanning from the left, until it encounters a word containing A quote (e.g. ``"string"`') A synonym name (e.g. ``\file`') A variable name (e.g. ``.number.`') An opening square bracket (e.g. ``[option]`') An opening brace (e.g. ``{a|b}`') An choice between two items (e.g. ``first|second`') When Gri finds a command that matches your command line, it assumes that this is the intended command, and searches no further. This means that you must be careful not to have your command hidden by other commands. For example, if your resource file contained these lines, Gri would never execute the second new command, because calls to it match the first command. To avoid this, you may either reverse the order of the definitions, so that Gri will find the proper routine, or rename one of the routines.
    `Draw foo'
    Draw a foo.
    {
      show "drawing a foo"
    }
    `Draw foo bar'
    Draw a foo bar.
    {
      show "drawing a foo bar"
    }
Gri searches the ``gri.cmd`' file first, so any new command that you create that clash with built-in commands will be ignored by Gri see Options On Command Line. Gri will warn you of this, and proceed, ignoring your newer definition. To get around this, use capital letters to begin the words of your new command. By convention, Gri never uses capital letters in this way, so a clash is impossible (except with any similar command you might have defined previously, such as in your ``~/.grirc`' file). ### 5.10.2: Simple example of creating new command To make a new command called ``Show The Time`' insert the following into your ``~/.grirc`' resource file or into your command-file somewhere near the top, or at least before you use the command.
    `Show The Time'
    New command to show the time of day.
    {
      show "\.time."
    }
- EXPLANATION: The name of the new command is enclosed in angled single-quote marks. The words of the new command should begin with upper-case letters to prevent a name clash with a present or future built-in Gri command. Formatting convention: Make sure that the entire definition string, from the opening angled quote to the ending angled quote, appears on one line. Otherwise Gri will give an error like
      ERROR: Can't extract syntax for new command
Following the name line, you may optionally insert any number of lines which will become the ``help`' information for the new command. See the file ``gri.cmd`' for the recommended stylistic conventions in writing help information see Options On Command Line. Following the help lines, if they exist, the body of the new command is given, sandwiched between a starting line containing an opening brace (``{`') as the only nonwhite character, and an ending line containing a closing brace (``}`') as the only nonwhite character. Any valid Gri commands may be used in the body of the new command. It is acceptable to use other new commands in the body. Recursion is also allowed -- a new command is allowed to call itself (although there is a limit on nesting, of perhaps a thousand or so; this varies with the version of Gri). Formatting convention: It is usual, but not necessary, to use an indentation level of 2 spaces in the body of the new command. The new command is invoked by ``Show The Time`'. Help for the command is found by ``help Show The Time`' or ``help Show`'. ### 5.10.3: Complicated example of creating new command The following example from the global ``gri.cmd`' file illustrates how to parse/check the commandline (see Local Synonyms.), which is a good practice in any code you expect to re-use. The first ``if`' statement checks that the word ``at`' is in the right place (this would not have been checked by the syntax matcher, the word having followed a string). The presence of the keyword ``cm`' is checked for, and user units or cm units are used accordingly. Local variables are created (``new`') and then destroyed (``delete`') so that this new command cannot affect outside code.
    `draw label whiteunder "\string" at .ll_x. .ll_y. [cm]'
    Draw label for plot, located with lower-left corner
    at indicated (x,y) position (specified in user
    units or in cm on the page).  Whiteout is used
    to clean up the area under the label.  BUGS:
    Cannot handle angled text; doesn't check for
    super/subscripts.
    {
        if {rpn "\.word4." "at" !=}
          show "ERROR 5th word must be `at', not `\.word4.'"
          show traceback
          quit
        end if
        new .x. .y. .oldgray. .space.
        if {rpn \.words. 7 ==}
          .x. = {rpn \.word5. xusertocm}
          .y. = {rpn \.word6. yusertocm}
        else if {rpn \.words. 8 ==}
          if {rpn "\.word7." "cm" !=}
            show "ERROR: Require 7th word to be `cm'"
            show traceback
            quit
          end if
          .x. = \.word5.
          .y. = \.word6.
        else
          show "ERROR: Require 7 or 8 words, not \.words."
          show traceback
          quit
        end if
        # Coordinates now in cm.  Next, white out a box
        # under the text (and .space. centimetres
        # beyond text), then draw label.
        .space. = 0.1               # Space of 1mm
        .oldgray. = ..graylevel..
        set graylevel white
        draw box filled                            \
          {rpn .x. .space. -}                    \
          {rpn .y. .space. -}                    \
          {rpn .x. "\.word3." width + .space. +} \
          {rpn .y. "M" ascent + .space. + } cm
        set graylevel .oldgray.
        draw label "\.word3." at .x. .y. cm
        delete .x. .y. .oldgray. .space.
    }