summaryrefslogtreecommitdiff
path: root/web/clip/ms_dos/ex01_a.asc
diff options
context:
space:
mode:
Diffstat (limited to 'web/clip/ms_dos/ex01_a.asc')
-rw-r--r--web/clip/ms_dos/ex01_a.asc274
1 files changed, 274 insertions, 0 deletions
diff --git a/web/clip/ms_dos/ex01_a.asc b/web/clip/ms_dos/ex01_a.asc
new file mode 100644
index 0000000000..b26ff02583
--- /dev/null
+++ b/web/clip/ms_dos/ex01_a.asc
@@ -0,0 +1,274 @@
+
+
+
+ Example 01: Palindrome filter
+
+
+
+
+
+In this appendix we illustrate the CLiP style of literate programming by a
+program to filter palindromic lines from an input file.
+ Two files are involved: the program module (*PALINDROME.PAS*) and a test
+file (*TESTDATA.IN*).
+
+
+A.1. Specification
+
+A palindrome is a sentence with the property that the letters from left to
+right, read the same as from right to left. In the comparison uppercase and
+lowercase letters are considered to be equivalent and all other characters are
+simply ignored. Hence an empty sentence is a palindrome. Other examples are:
+
+ (************* #file "TESTDATA.IN" #comment off **************)
+ Ada
+ 1234567
+ doremifasolosafimerod
+ (******* Testdata #multiple *******)
+ (***************** End of TESTDATA.PAS ************************)
+
+The following sentences do not qualify as a palindrome.
+
+ (******* Testdata #quick *******)
+ Mr. Clinton won the elections over Mr. Bush.
+ This line is not palindromic.
+ Aabbccdd Aabbccdd Aabbccdd Aabbccdd
+ Abracadabra
+ supercalafragilisiticexpielecdosia
+
+The following are examples of more sophisticated palindromes.
+
+ (******* Testdata #quick *******)
+ Able was I, ere I saw Elba.
+ A man, a plan, a canal, Panama.
+ Norma is as selfless as I Am, Ron.
+
+Note that the famous Dutch sentence
+
+ (******* Testdata #quick *******)
+ Koos Eekfeen keek maar door rood kerkraam, maar krek door rood raam keek
+ neef Kees ook.
+
+will not be recognized as a plindrome since it occupies two lines.
+ The program *PALINDROME* reads an input file, filters the lines that are
+palindromic and writes them to an output file.
+
+A.2. Communication with the outside world
+
+The program conforms to the general template of a Pascal program. We introduce
+the files *IN_FILE* and *OUT_FILE* to define its communication with the outside
+world. The actual files have to be specified at run-time. Thus we have
+
+ (***************** #file "PALINDRO.PAS" ***********************)
+ (****************************************************************)
+ (* Program: Palindrome filter program. *)
+ (* Purpose: To filter the palindromic lines from a given input *)
+ (* file to a specified output file. *)
+ (****************************************************************)
+ PROGRAM PALINDROME (INPUT, OUTPUT, IN_FILE, OUT_FILE);
+
+ (******* Palindrome constants #multiple #comment off *******)
+ (******* Palindrome types #multiple #comment off *******)
+ VAR
+ IN_FILE, OUT_FILE: TEXT;
+ (******* Palindrome variables #multiple #comment off *******)
+
+ BEGIN
+ ASSIGN (IN_FILE, 'TESTDATA.IN'); RESET (IN_FILE);
+ ASSIGN (OUT_FILE, 'TESTDATA.OUT'); REWRITE (OUT_FILE);
+
+ (***************** Palindrome (body) **********************)
+ (** Copy the lines of the IN_FILE that are palindromic to **)
+ (** the OUT_FILE. **)
+ (************************************************************)
+
+ END (*PALINDROME*).
+ (******************* End of PALINDROME.PAS ********************)
+
+To prepare the module for future declarations of constants and types we have
+
+ (******* Palindrome constants #leader #quick *******)
+ CONST
+ (******* Palindrome types #leader #quick *******)
+ TYPE
+
+A.3. Processing of the files
+
+The program processes *IN_FILE* line by line. The idea is to buffer an exact
+copy of the current line in *IN_LINE*, while at the same time its letters are
+buffered in *LETTERS*. So *LETTERS* will be empty if the line holds no letters
+at all, in which case the line is considered to be palindromic by definition.
+ We choose the buffers *IN_LINE* and *LETTERS* to be of the same type, *
+TEXT_LINE*, which we will not specify in detail right now. For this purpose we
+introduce a type *ABSTRACT*.
+
+ (******* Palindrome types #quick *******)
+ ABSTRACT = (DEFINED, UNDEFINED);
+
+*TEXT_LINE* will temporarily be declared *ABSTRACT* and its details will be
+defined later. Thus the declaration of *TEXT_LINE*
+
+ (******* Palindrome types *******)
+ (******* Declaration of TEXT_LINE *******)
+ (***************** End of Palindrome types ********************)
+
+is temporarily satisfied with the type *ABSTRACT*.
+
+ (******* Declaration of TEXT_LINE #quick #default *******)
+ TEXT_LINE = ABSTRACT;
+
+The declaration for the variables *IN_LINE* and *LETTERS* becomes
+
+ (******* Palindrome variables #quick *******)
+ IN_LINE,
+ LETTERS: TEXT_LINE;
+
+We have to test *LETTERS* in order to decide whether or not *IN_LINE* contains
+a palindrome. The result of this test is flagged by *IS_PALINDROME*, for which
+we introduce the declaration
+
+ (******* Palindrome variables #quick *******)
+ IS_PALINDROME: BOOLEAN;
+
+Now the body of the Palindrome filter may be expanded as
+
+ (***************** Palindrome (body) **********************)
+ (** Copy the lines of the IN_FILE that are palindromic to **)
+ (** the OUT_FILE. **)
+ WHILE NOT EOF (IN_FILE) DO
+ BEGIN
+ (***************** Palindrome (1) *********************)
+ (** Read a line from IN_FILE into IN_LINE. The letters **)
+ (** of this line are copied to LETTERS. **)
+ (********************************************************)
+
+ READLN (IN_FILE);
+
+ (***************** Palindrome (2) *********************)
+ (** Test palindromicity of LETTERS. Set IS_PALINDROME **)
+ (** to reflect the result of the test. **)
+ (********************************************************)
+
+
+ IF IS_PALINDROME THEN
+ BEGIN
+ (***************** Palindrome (3) *****************)
+ (** Write IN_LINE to OUT_FILE. **)
+ (****************************************************)
+
+ WRITELN (OUT_FILE);
+ END (*IF*);
+ END (*WHILE*);
+ (************* End of Palindrome (body) *******************)
+
+A.4. Choosing the structure of *IN_LINE* and *LETTERS*
+
+Before we can proceed we need to establish a structure for the objects *IN_LINE*
+ and *LETTERS*. Thus we define *TEXT_LINE* as a structure with two components.
+The first component is an array, *CHARS*, which contains the characters to be
+buffered. The second component, *LENGTH*, indicates which part of the array is
+actually occupied. The maximum number of characters that can be buffered by the
+structure is determined by the length, *MAX_L*, of the array. *MAX_L* serves as
+an implementation parameter.
+
+ (******* Palindrome constants #quick *******)
+ MAX_L = 132;
+
+ (******* Declaration of TEXT_LINE #quick *******)
+ TEXT_LINE = RECORD
+ CHARS: ARRAY[1..MAX_L] OF CHAR;
+ LENGTH: 0..MAX_L;
+ END (*RECORD*);
+
+A.5. Reading a line
+
+For efficiency reasons we fill *IN_LINE* and *LETTERS* simultaneously.
+Therefore we buffer every character that is read from *IN_FILE* in the variable
+*IN_CHAR*.
+
+ (******* Palindrome variables #quick *******)
+ IN_CHAR: CHAR;
+
+Only when *IN_CHAR* turns out to be a letter it is copied to *LETTERS*. Since
+this process is crucial for the overall operation, we make provisions for some
+debugging code here.
+
+ (***************** Palindrome (1) *********************)
+ (** Read a line from IN_FILE into IN_LINE. The letters **)
+ (** of this line are copied to LETTERS. **)
+ IN_LINE.LENGTH := 0;
+ LETTERS.LENGTH := 0;
+ WITH IN_LINE DO
+ WHILE NOT EOLN (IN_FILE) DO
+ BEGIN
+ READ (IN_FILE, IN_CHAR);
+ LENGTH := LENGTH + 1;
+ CHARS[LENGTH] := IN_CHAR;
+ IF IN_CHAR IN ['A'..'Z', 'a'..'z'] THEN
+ WITH LETTERS DO
+ BEGIN
+ LENGTH := LENGTH + 1;
+ CHARS[LENGTH] := IN_CHAR;
+ END (*WITH/IF*);
+ END (*WHILE/WITH*);
+
+ (********************* Palindrome (test) **************)
+ (** Check contents of IN_LINE and LETTERS. #optional **)
+ (********************************************************)
+
+ (***************** End of Palindrome (1) **************)
+
+A.6. Testing for palindromicity
+
+We test the palindromicity of *LETTERS* in two steps. First we transform the
+contents of *LETTERS* to uppercase and then we compare the characters of *
+LETTERS* pairwise. The comparison is done starting with the most outside
+characters and progressing inward. The string is assumed a palindrome until the
+opposite is proven through a pair of different characters. With the local
+counter
+
+ (******* Palindrome variables #quick *******)
+ I: INTEGER;
+
+we keep track of the comparing process. Now *Palindrome (2)* can be expanded as
+
+ (***************** Palindrome (2) *********************)
+ (** Test palindromicity of LETTERS. Set IS_PALINDROME **)
+ (** to reflect the result of the test. **)
+ WITH LETTERS DO
+ BEGIN
+ (* Transform lowercase to uppercase. *)
+ FOR I := 1 TO LENGTH DO
+ IF CHARS[I] IN ['a'..'z']
+ THEN CHARS[I] :=
+ CHR(ORD(CHARS[I]) - ORD('a') + ORD('A'));
+
+ (* Perform the palindromicity test. *)
+ IS_PALINDROME := TRUE;
+ I := 1;
+ WHILE IS_PALINDROME AND (I <= LENGTH DIV 2) DO
+ IF CHARS[I] = CHARS[LENGTH-I+1] THEN
+ I := I + 1
+ ELSE
+ IS_PALINDROME := FALSE;
+ END (*WITH*);
+ (***************** End of Palindrome (2) **************)
+
+A.7. Writing the palindrome
+
+The only remaining action is to write the contents of *IN_LINE*. Again we need
+a local counter
+
+ (******* Palindrome variables #quick *******)
+ J: INTEGER;
+
+The writing proceeds straight forward.
+
+ (***************** Palindrome (3) *****************)
+ (** Write IN_LINE to OUT_FILE. **)
+ WITH IN_LINE DO
+ BEGIN
+ FOR J := 1 TO LENGTH DO
+ WRITE (OUT_FILE, CHARS[J]);
+ END (*WITH*);
+ (************* End of Palindrome (3) **************)