Working with Command Chaining

Introduction


Starting in version 8.1, SPFLite provides a new editing concept: Command Chaining.  To help you understand this concept as quickly as possible, the key features of Command Chaining are presented below in question and answer format.


What is Command Chaining ?


Command Chaining allows you to perform a series of Edit primary commands, passing a common set of data lines from one command to the next, so you can apply several command operations to the same set of lines.


Only Primary commands can be chained.  There is presently no support to chain line commands.  This facility could properly be called the "Primary Command Chaining" facility, but we'll drop the “Primary” to be brief.


Command Chaining only applies to data lines, not to “special” lines like NOTE lines.


What kind of primary commands can be chained ?


Chaining of primary commands is provided to perform a set of operations on a related set of data lines.  Such commands are called chainable.


Chainable commands can be found in the Primary Commands section of the Help document.  Only commands that show a syntax containing a line-control-range operand are chainable.  


Commands like UP and DOWN do not take a line-control-range, so they are not chainable.  However, "non-chainable" commands have no impact on commands that are chainable.  So, if a non-chainable command like UP or DOWN is issued in between chainable commands, it does not affect or disturb the way the chain operates.  In this sense, non-chainable commands are "ignored" for purposes of the command chain.


Is Command Chaining related to issuing multiple commands using a command separator ?


No, not directly.  The command separator character, normally the ; semicolon, allows you to issue more than one primary command on the same Edit primary command line.  For example,


X ALL ; FIND ABC ALL


first excludes all lines in the file, and then finds (and unexcludes) all lines containing the string ABC.  However, the X and FIND commands have no relationship to each other.  They are simply two commands that happen to be performed one after the other.  These two commands here are not a command chain.


You may find issuing a command chain using multiple commands separated by semicolon to be convenient, but that is not required.  Chained commands can also be issued individually, without the semicolon notation.


Why would I want to use Command Chaining ?


The main reason to do this is when you want to apply a number of commands to a set of lines that are determined by the outcome of some initial command like FIND ALL or CHANGE ALL.


Couldn't I use regular tags, X|NX lines, or U|NU lines to do what Command Chaining does ?


Yes, and for some situations, you may still want to do that.  However, those techniques require a "set-up phase" where you have to define a set of tagged or marked lines.  That is useful if your set of lines is "going to be around for a while".


The advantage of Command Chaining is that when you have a common set of lines needed by several commands, but it's a "one time" situation, using a Command Chain is easier, because you don't need any "set-up" process.  You can simply chain your commands together, without needing to first define tags or User Lines, and then having to remove such tags or User-Line marks afterwards.


How is Command Chaining different than multiple commands per command line ?


SPFLite maintains “lists” of which lines are “involved” in a particular command.  You can then use one of these “lists” in a subsequent command.  The use of a such a list is how command chaining is implemented.


The multiple commands involved in a sequence of chained commands can appear on one primary command line (separated by semicolons), or on separate lines.  So, the two features can be combined or used individually, since they are separate and distinct concepts.


What do I need to know about these “lists” of lines ?


For Command Chaining, SPFLite deals with four kinds of “lists” of data lines when executing a primary command.  These are eligible lines, reached lines, affected lines and unaffected lines.


Each list of lines may contain zero or more data lines, and are discussed below.


What are eligible lines ?


When a primary command is issued that accepts a line-control-range operand, SPFLite takes into account any X|NX, U|NU, line-range labels and/or tags that may be specified, and whether PREV, NEXT, FIRST, LAST or ALL is used, as well as any implied operands.  


The list of all lines that SPFLite could possibly look at, in order to perform a command such as CHANGE, is the eligible list


Unless a data line is eligible, it will not be involved in command chaining.


What are reached lines ?


A reached line is an eligible line that a primary command has “looked at” in the course of its operation.  The list of all reached lines is the reached list.


Why would the reached lines be different than the eligible lines?


The main reason is whether the operand ALL appeared on the command, or if ALL was implied by a particular command.


For example, suppose you had a 10 line file, and only line 5 contains the string ABC.  


If the file is positioned at the Top of Data, and you said CHANGE ABC DEF, the command has an implied NEXT operand.  That is, it will change the next ABC into DEF, if one exists.  In your file, the CHANGE command would start at line 1 looking for ABC.  When it got to line 5, it finds ABC, changes it to DEF, and stops.  So, it has reached lines 1 through 5, because it had to “look” at those lines in order to find the first ABC (the “next” one, starting from line 1).  Since the CHANGE command stopped looking after it got to line 5, lines 6 through 10 were not reached.


However, if you said, CHANGE ABC DEF ALL, SPFLite has to “look” at every line in the file, to be certain that every occurrence of ABC is located.  Thus, it will have reached all lines in the file, from 1 to 10.  In that case, the command will have reached every eligible line, and so the eligible list and the reached list would be the same set of lines.


How can I remember the difference between eligible and reached lines ?


When a primary command has the ALL operand, the eligible and reached lines will be the same.  


When certain primary commands like UC are used, they are applied to all lines in the line-control-range, even if you don't use the ALL keyword.  For UC, the “ALL” is implied.  In such commands, the eligible and reached lines will be the same.


When the primary command does not have either an explicit ALL or an implied ALL, it is possible the number lines reached is fewer than the number of eligible lines.


What are affected lines ?


An affected line is a reached line on which the primary command does something, such as find or change a string, or modify the data on the line in some way.  The list of all affected lines is the affected list.


What are unaffected lines ?


An unaffected line is a reached line on which the primary command did not find a search string or otherwise did not modify the data on the line in any way.  The list of all unaffected lines is the unaffected list.


To be precise, an unaffected line is a reached line that does not fall under the category of “affected”.  That is, you could say, unaffected = reached – affected.


How do I reference the various types of lines that SPFLite maintains ?


Because each of these various sets of lines could contain zero or more lines of data, and they might not be contiguous (adjacent) lines, it is very similar to the way SPFLite handles user-defined tags.  Thus, SPFLite uses tag notation to describe these lists of lines.


The tags for these lines begin with a Z, and so they are called “Z tags”.



The list of eligible lines for a given command is internally managed by SPFLite.  There is no Z tag for the eligible list.


What do I need to know about Z tags ?





When are the affected lines the same as the reached lines ?


When a command like FIND ABC ALL is executed, some lines may have ABC and some lines may not.  So, for any given line, the outcome of the search for the string ABC is conditional, because it may or may not be found.  In such cases :Z and :ZF may not contain the same list of lines.


For commands like UC, every eligible line is converted to upper case, and so it is unconditional, since it is not dependent on whether a string is found or not.


If a command is unconditional, the lines reached and the lines affected will be the same.  That means in such cases that :Z and :ZF will contain the same list of lines, and so :Z and :ZF could be used interchangeably for such commands.


Which command and which lines do Z tags refer to ?


When a Z tag appears on a primary command, it refers back to the prior primary command that immediately preceded the command where the Z tag itself appears.


For example, consider the following command chain:


FIND ABC ALL ; UC :ZF


The Z tag :ZF refers back to the command which immediately preceded it, which is the FIND command.


In this example, SPFLite creates the various Z lists for the current command, but you refer to lists that were created on the previous command using the Z tags.


That only makes sense.  A Z tag like :ZF couldn't refer to lines on the current command, because :ZF is a list of lines that had been found already, and that list of lines is the very place where the current command is going to 'look' to perform its operations.  If :ZF meant lines on the current command, it would be referring to "found" lines that hadn't even been found yet.


A Z tag never refers to lines that are reached, affected or unaffected by the command that a Z tag itself appears on.


For example, in the command,


CHANGE ABC DEF ALL :ZF


the Z tag :ZF does not refer to any lines changed by this CHANGE command itself, but by whatever command immediately preceded this CHANGE command.


A Z tag always "points to the left" to the prior command.


Do the Z tags :ZF and :ZNF mean Found and Not Found ?


Sometimes, but not always.  




Presently, any chainable commands that take a search string are conditional commands, and will define the :ZF and :ZNF tags in a way that will mean Found or Not Found.  Commands like UC that are unconditional also define the :ZF and :ZNF tags, but they are not important, and you only need to be concerned with the tag :Z for these commands.


How is the notion of affected and unaffected lines handled for “Negative logic” commands like NFIND ?


For NFIND, the command locates all lines that do not contain a search string.  So, the object of the NFIND command is to find lines rather than to find strings.  That is, the lines located by NFIND ABC ALL are the lines that did not contain ABC.


If SPFLite strictly followed the rules discussed above, lines not containing the search string would be the "affected" lines, which would be represented by :ZF, rather than by by :ZNF.  For example, the commands X ALL ; NFIND ABC ALL would result in unexcluding all lines not containing ABC.  So, the finding of ABC, and the unexcluding affect, would never occur on the same lines.


That would have meant that the Z tag :ZF would be the list of lines where ABC was not found, and :ZNF would be where ABC was found.


Most users would see that as "backwards" and find it hard to understand or remember.


To avoid this confusion, in cases where "Negative logic" commands are used that specify a search string:


So, you can always think of :ZF and :ZNF as representing lines where a search string was either Found, or Not Found, regardless of whether the command was a "Positive logic" or a "Negative logic" type.


Bear in mind that "Negative logic" commands always require a search string, since they act on lines where the string is not found.


The commands affected by this are NDELETE, NFIND, NEXCLUDE, NFLIP, NREVERT, NULINE and NSHOW.


How are DELETE and NDELETE commands with search strings handled ?


When a command of the form DELETE ABC ALL is issued, every line where ABC is found is deleted.  Since every "affected" line is now gone, the affected list is empty.  That means the Z tag :ZF will have no lines.  Any reached lines that were not deleted because ABC was not found will be in :ZNF.


Likewise, when a command of the form NDELETE ABC ALL is issued, every line where ABC is not found is deleted.  Since every "affected" line is now gone, the affected list is empty.  However, because of the considerations noted above about "Negative logic" commands, the roles of :ZF and :ZNF are reversed in this command, so that they reflect whether the search string was found or not.  


That means, for NDELETE, the Z tag :ZNF will have no lines, because the command requests that any lines where ABC is not found is to be deleted.  (There are no longer any "not found" lines, because the command got rid of them all.)  Any reached lines that were not deleted because ABC was found (that is, lines where ABC can still be found) will be in :ZF.


What happens if a command chain is used out of context ?


The first time you issue a command in an Edit session, since no prior command has been issued, the Z tags :Z, :ZF and :ZNF are empty and do not refer to any lines in your file.


If you attempt to reference a  Z tag of :Z, :ZF and :ZNF when it is empty or undefined, you will just get a "not found" or "end of data reached" condition.


Are line commands permitted within a primary command chain ?


Yes.  You can intersperse one or more line commands in between primary commands.  A Z tag on a primary command refers back to the last primary command, not to any line commands you may have issued.  The line commands do not affect or disturb the way the "chain" operates.


As noted above, commands like UP and DOWN are not "chainable" but may appear in between primary commands that are chainable.  Thus, you can use UP and DOWN (probably mapped to Page Up and Page Down keys) to locate a line on which you want to issue one or more line commands, before resuming the use of primary commands within a chain.


Can the LINE primary command be used in chaining ?


Yes.  LINE is a regular primary command that accepts a line-control-range operand, and so it can be chained.


Can an unconditional command be simulated as a conditional one for chaining purposes ?


Sometimes.  For example, the UC command unconditionally converts all lines to upper case.  This is "unconditional" in the sense that lines that are already in upper case, lines with no alphabetic data, and even blank lines can be "successfully" converted to upper case.


Suppose you wanted UC to be conditional, and you wanted to shift all data lines 4 columns to the right if any data lines contained lower cases letters that were converted to upper case letters.  Here is how you could do it:


FIND P'<' ALL .1 .100 ; UC :ZF ALL ; LINE ')4' ALL :Z


The FIND locates all lines having any lower case letters.  The UC converts only those lines with lower case to upper case.  The LINE command applies the right-shift 4 line command )4 to all the lines processed by UC, which were the lines found to have lower case letters by FIND.


Such techniques will work in some, but not all, cases.  


Where this cannot be applied, you may need to use traditional tags, X|NX lines, U|NU lines, or macros to accomplish your task.


If I use multiple commands per command line, where are the messages for them displayed ?


When multiple commands per command line are used (separated by semicolon), the message issued by the last command will appear on the edit screen, preceded by a + plus sign.  If you issue the HELP command (usually mapped to the F1 key) a popup will display the messages produced by each command, in the order issued.


Messages for multiple commands are handled this way regardless of whether the commands involve chaining or not.


Example of command chaining


Suppose you wanted to perform a CHANGE on a set of lines, then you wanted to convert all of those lines just changed to upper case, and finally you wanted to shift all of those lines 4 columns to the right.  How might you go about such a task?


CHANGE ABC DEF .1 .100 ALL ; UC :ZF ALL ; LINE ')4' ALL :Z


Note: The UC applies to all lines changed by the CHANGE command, so :ZF is required.  Since UC is an unconditional command, either :Z or :ZF would work for the LINE command; they would have the same set of lines defined.  We use :Z here instead of :ZF because it is shorter.


Created with the Personal Edition of HelpNDoc: Create cross-platform Qt Help files