SS0,SZ=12PT,12PT,UF=STANDARD,OF=0DI,0DI,LM=1DI,RM=77DI,FL,TS=8DI,16DI,24DI,32DI,40DI,48DI,56DI,64DI,72DI,IP=0DI,0DI,0DI,HY=0,AL=0,MD=NM,NJ,RT=0,FD=273LI,PL=273LI,273LI,273LIUS0NT/0


Blind Execution of Commands in XPL: A Brief Tutorial
For XyWrite v4+ (DOS and Windows) and Nota Bene for Windows
C.L. Distefano rev. 9/16/02
IP+5DI,+5DI,+5DI
Read this file in XyWrite or Nota Bene.  Use eXPanded (Show Codes) view to inspect the examples.  A basic knowledge of XyWrite Programming Language (XPL) is assumed.
LD-    
US0
XyWrite 4 (actually, its immediate predecessor, Signature) introduced a new XPL function to execute commands invisibly, without first displaying them on the command line: BX, for "Blind eXecute".  Paired with function Q2 -- as in command-- it executes native commands without blanking and rewriting the command line (and without the overhead associated with those operations).  BX is the standard way to execute commands in XPL programs running under XyWrite 4, XyWrite for Windows and Nota Bene for Windows.  It replaces the old method -- command 	-- in most contexts.  (The BC method still works, and is useful in special situations.)

With plain-text commands, blind execution is simply a matter of substituting funcs BX...Q2 for BC...XC.  With more complex commands, however, the usages differ in ways that may confound new users.  This text describes the underlying design of func BX and provides examples.  Once that design is understood, BX will be appreciated as well-behaved and straight-forward to use.


** Overview **

The long and the short of BX is this: Everything between func BX and func Q2 is processed as though it were displayed on the command line ("CMline") and executed as a command.  Consider what that means:

A.  Funcs BX and Q2 are complementary halves of a single command-executing entity; neither BX nor Q2 individually has any standalone meaning.  (An alternate use for Q2 -- to suspend spell-checking -- is irrelevant here.)  When func BX is encountered, the XPL interpreter reads the succeeding command and waits -- indefinitely if necessary -- for a closing func Q2.  Thus, every BX statement MUST conclude with Q2 before other instructions can be executed.  This is a major difference from funcs BC and XC, which are independent of each other: BC blanks the CMline and XC executes the command on the CMline.  BC's operation in no way depends on a later appearance of func XC (and vice versa).  BX and Q2 are a linked pair.

B.  Everything between BX and Q2 is treated literally, as part of the command to be executed.  The crucial concepts here are "everything" and "literally".  If something is not part of the command to be executed, it must be situated outside of the BX...Q2 statement.  A prime example is a 3-byte function intended to be executed (as opposed to treated as an inert string).  A useful consequence of this design is that 3-byte functions can be incorporated in commands as inert strings -- meaning, primarily, that they can be made the subject of SEarch and CHange commands.  (See "SEarch and CHange Operations on Functions", below.)

C.  XPL expressions enclosed in guillemets are *interpreted*, not treated as part of the command to be executed.  (The most common such XPL expression is one like PV01, which puts the value saved to Save/Get 01 (in NB parlance, Phrase 01).  This behavior is consistent with the way the CMline itself works: If you attempt to copy an XPL expression to the CMline, it is interpreted (i.e., it executes); it is not displayed.  Interpreted XPL expressions within BX...Q2 statements lend power and flexibility to blind-executed commands.


** Examples **

BX...Q2 usage follows CMline usage.  That is, every BX command must appear exactly as if it were going to be executed manually from the CMline.  That general rule applies across the board to 1) search wildcards, 2) carriage returns and line feeds, 3) guillemets and 4) 3-byte functions.  Special considerations apply to 5) XPL expressions enclosed in guillemets which (as per C. above) are executed rather than displayed when placed on the CMline.

Note: Jumbo U2 users can execute any of the following examples by DeFining the code and commanding RUNCODE<Helpkey>.

1) Wildcards

SEarch wildcards must appear as wildcards (3-byte reverse-video characters), not as functions:

se "ABCXYZ";*; (not se "ABCXYZ", which searches for the string literal "ABCXYZ")

se "";*; Search for next sentence separator

2) Carriage Returns (Ascii-13), Line Feeds (Ascii-10) 
   and Newlines (Ascii 13+10)

Carriage returns (Ascii-13), linefeeds (Ascii-10) and newlines (CrLf or Ascii 13+10) may appear either in true form or as wildcards.  Read the following examples in eXPanded view:

se "
";*; Search for CrLf (literal method)
se "";*; Search for CrLf (wildcard method)
se "^R";*; Search for CrLf (NB only)

se "
";*; Search for Ascii-13 carriage return (literal method)
se "";*; Search for Ascii-13 carriage return (wildcard method)

Note: se "0D" finds the *3-byte* Ascii-13, not CrLf or Ascii-13 (another example of the literalness of BX).

se "
";*; Search for Ascii-10 line feed (literal method)
se "";*; Search for Ascii-10 line feed (wildcard method)

3) Guillemets (Embedded command delimiters)

Strings containing balanced pairs of guillemets can be searched for literally: 

se "US0";*; SEarch for embedded command US0

The above search can also be done using guillemet wildcards (. and /):

se ".US0/";*; SEarch for embedded command US0

Guillemet wildcards are *required* when: (a) the SEarch string includes unbalanced guillemets, (b) using real guillemets would have the effect of embedding an undesired command, or (c) the SEarch string is an XPL expression.

se ".IP";*; SEarch for any embedded IP command (unbalanced guillemet)

se ".IP2IN,2IN,2IN/";*; SEarch for a particular embedded IP command (avoids unwanted formatting effect of using real guillemets)

se ".USMAIN/";*; SEarch for Use Style command (avoids formatting error due to absence of preceding SSMAIN statement)

se ".IF.EI/";*; SEarch for IF *or* EndIf in XPL code

When in doubt about whether to use real guillemets or guillemet wildcards, use the wildcards.  They always work.

4) SEarch and CHange Operations on Functions

In general, three-byte functions within the BX...Q2 statement are handled as strings; they are NOT executed as functions.  Thus, SEarch and CHange operations can be performed on functions just as on any other string:

se "k";*; Search for "k"
cv /k/G/;*; Change-Verify "k" to "G"

The two exceptions to the rule are funcs ]and _(which, when executed, produce the guillemet wildcards, . and /, respectively).  When used in a SEarch statement, "]" and "_" do not flag themselves; rather, they flag the 1-byte guillemets, Ascii 174 and 175.  No rhyme or reason; that's just the way it is.  Thus -- read the following two examples in eXPanded view:

se "]";*; finds the left guillemet in "CP"
se "_";*; finds the right guillement in "CP"

Due to the anomalous behavior of "]" and "_", BX...Q2 *cannot* be used to SEarch for "]" or "_".  SEarching for these funcs requires BC...XC and the 5-byte form of "]" or "_", thus:

se "FF]"	;*; puts se "]" on the CMline and finds "]"
se "FF_"	;*; puts se "_" on the CMline and finds "_"

Compare:

se "FF]";*; finds "FF]"
se "FF_";*; finds "FF_"

The point to take away from this discussion is that the vast majority of functions, when used with BX...Q2 in SEarch and CHange commands, flag *themselves*, i.e., they act as string literals within such commands.

5) XPL Expressions Enclosed in Guillemets

XPL instructions can be used within BX...Q2 statements to construct commands that have generality and flexibility not achievable with literal commands.  "XPL instruction" in this context means any XPL statement enclosed in guillemets; it does not include 3-byte functions (cf. "SEarch and CHange Operations on Functions", above).  The power of such instructions lies in the fact that they are interpreted, not treated as part of the command to be executed.  In this way, commands can be constructed that incorporate information saved in Save/Gets (Phrases), or that execute a variant of the command depending on the existence of a condition (such as whether text is DeFined), or that branch to different arguments depending on a condition, etc., etc.  The following examples only hint at the possibilities:

ca PV01;*; CAll the file named in Save/Get 01

Note that PV01 is interpreted; that is, the filename saved in variable 01 is inserted in the command at the point where the PV statement appears.  Now compare

SV01,these/w "PV01";*; Finds the word "the" (but not "thesaurus", "blithe", etc.)

with

se ".PV01/";*; Finds the string literal "PV01" (NOT the contents of Save/Get 01)

In the preceding example, the use of guillemet wildcards instead of actual guillemets changes the search string from interpreted XPL to one that represents the inert string literal "PV01".

Other examples:

saIFVA$DE>0dEI/nv D:\PATH\FILENAME;*; SAve current file (or, if text is DeFined, only the DeFined portion) to D:\PATH\FILENAME.  Executes the SAD command if text is DeFined, otherwise SA.  Should be preceded by G, to close any partially DeFined block.

ne/IFVA$VE<"V4.1"1GLAEI25LBA;*; Open Untitled file in draft view if XyWrite or NB version number is earlier than 4.1 (in XyDOS executes NE/1, in XyWin and NBWin, executes NE/25)

PR|Enter a 2-char func mnemonicpfun RKRK;*; Execute PFUN command using two characters input by the user

Note: Consistent with the requirement that the command to be executed appear literally and entirely within the BX...Q2 statement itself, BX will *not* execute a CAll command in "point-and-shoot" mode (i.e., with the cursor pointed at a DIRectory listing).  To blind-execute such a CAll command, it is necessary to save the filename under the cursor to a Save/Get (using VA$DR). Compare

ca;*; Fails even if DIRectory is displayed and cursor is on filename

with

SX01,VA$DRca PV01;*; CAlls filename under cursor in DIRectory listing; can also be written as:

ca SX01,VA$DRPV01;*;

or (awkward but still legal):
SX01,VA$DRca PV01;*; 


** Alternate Usage **

An alternate usage with BX is to enclose the command in parentheses (), square brackets [], or curly braces {}.  One limitation of this usage is that the command itself may not include the closing paren, bracket or curly brace.  It may, however, include the opening member of the pair.  Thus, (se "(") succeeds (SEarch for open paren), but (se ")") (SEarch for close paren) fails.  In the latter statement, the first close paren is interpreted as a signal to execute the command rather than as part of the command to be executed; the balance of the statement is misinterpreted.  The solution is to enclose the command in square brackets or curly braces: [se ")"] or {se ")"}; or, of course, to use BX...Q2: se ")".

In the Jumbo U2 we have adopted the ... usage universally, except in rare instances where the command itself includes the string "".

Compare:

se "";*; SEarch for func BX (succeeds despite second , which is treated literally, as the string to be SEarched for)

with

se "";*; SEarch for func Q2 fails (because, in absence of an opening paren, bracket or brace, the first func Q2 is *interpreted* (executing only part of the command), while the rest of the statement is misinterpreted)

and

(se "");*; SEarch for func Q2 succeeds

** A Note on Keyboard-File Usage **

Another place to consider using parentheses instead of func Q2 is in the keyboard file.  This applies especially if you record and save keystrokes using the built-in keystroke-record facility (func RK and command SAVERK).  Due to a bug in RK, keystrokes encoded as nn=BX,c,o,m,m,a,n,d,Q2 aren't fully captured, whereas in the form nn=BX,(,c,o,m,m,a,n,d,), they are.  The same applies to calls to menus and XPL programs stored in U2 customization files (see "Companion Functions JM and JH", below).  Thus, a call to the New File dialog box (Frame NewB in XY4.DLG) in the form nn=XH,JM,N,e,w,B,Q2 will be incorrectly recorded.  For correct encoding via func RK, use nn=XHJM,(,N,e,w,B,).

** Companion Functions JM and JH **

Two other functions are paired with Q2 (or used with parens, brackets or braces) to perform XyWrite|NB operations.  Func JH is used to call a menu or program ("frame") contained in a loaded Help file, i.e., a customization file that has the identifier ;HL; at the top of the file (and may have the filename extension .HLP).  Func JM is used to call a frame (execute a program or summon a dialog box) contained in a loaded Dialog file, i.e., a file that has the identifier ;DG; at the top of the file (and may have the filename extension .DLG).  Frame names are case-INsensitive.  For example:

EVersion;*; Execute Help frame "Version" ({{5,Version}})
LineSpA;*; Display dialog box {{K,LineSpA}}
(LineSpA);*;  same as above (alternate usage)

Func JM can also be used to execute programs or show dialog boxes contained in any of the series of Help files designated U1 through U9, having top-of-file identifiers ;U1;, ;U2;, ... ;U9; (generically, "U# files").  U# files serve as repositories for user-written XPL and manufacturer or third-party add-ons.  An example is the XyWWWeb Jumbo U2 customization file.

To execute a frame contained in a loaded U# file, the frame name must be prefixed with the U number followed by a dot.  For example, to execute a frame contained in a loaded U2 file:

2.BC2BX;*; Execute U2 routine BC2BX

Funcs JM and JH were introduced without fanfare (along with BX) nearly a decade ago.  They enable a parent program to invoke externally-stored, reusable subroutines (or functions or menus or dialog boxes), and thus move XPL far beyond its humble keystroke-recording origins into the mainstream of structured programming.  For all the polish and convenience of BX, the great leap forward was here.

--
Carl Distefano
cld@despammed.com
http://www.serve.com/xywwweb/
