1 Introduction
2 Ground Rules

Building a File System
3 File Systems
4 File Content Data Structure
5 Allocation Cluster Manager
6 Exceptions and Emancipation
7 Base Classes, Testing, and More
8 File Meta Data
9 Native File Class
10 Our File System
11 Allocation Table
12 File System Support Code
13 Initializing the File System
14 Contiguous Files
15 Rebuilding the File System
16 Native File System Support Methods
17 Lookups, Wildcards, and Unicode, Oh My
18 Finishing the File System Class

The Init Program
19 Hardware Abstraction and UOS Architecture
20 Init Command Mode
21 Using Our File System
22 Hardware and Device Lists
23 Fun with Stores: Partitions
24 Fun with Stores: RAID
25 Fun with Stores: RAM Disks
26 Init wrap-up

The Executive
27 Overview of The Executive
28 Starting the Kernel
29 The Kernel
30 Making a Store Bootable
31 The MMC
32 The HMC
33 Loading the components
34 Using the File Processor
35 Symbols and the SSC
36 The File Processor and Device Management
37 The File Processor and File System Management
38 Finishing Executive Startup

Users and Security
39 Introduction to Users and Security
40 More Fun With Stores: File Heaps
41 File Heaps, part 2
42 SysUAF
43 TUser
44 SysUAF API

Terminal I/O
45 Shells and UCL
46 UOS API, the Application Side
47 UOS API, the Executive Side
48 I/O Devices
49 Streams
50 Terminal Output Filters
51 The TTerminal Class
52 Handles
53 Putting it All Together
54 Getting Terminal Input
55 QIO
56 Cooking Terminal Input
57 Putting it all together, part 2
58 Quotas and I/O

UCL
59 UCL Basics
60 Symbol Substitution
61 UCL Command execution
62 UCL Command execution, part 2
63 UCL Command Abbreviation
64 ASTs
65 UCL Expressions, Part 1
66 UCL Expressions, Part 2: Support code
67 UCL Expressions, part 3: Parsing
68 SYS_GETJPIW and SYS_TRNLNM
69 UCL Expressions, part 4: Evaluation

UCL Lexical Functions
70 PROCESS_SCAN
71 PROCESS_SCAN, Part 2
72 TProcess updates
73 Unicode revisted
74 Lexical functions: F$CONTEXT
75 Lexical functions: F$PID
76 Lexical Functions: F$CUNITS
77 Lexical Functions: F$CVSI and F$CVUI
78 UOS Date and Time Formatting
79 Lexical Functions: F$CVTIME
80 LIB_CVTIME
81 Date/Time Contexts
82 SYS_GETTIM, LIB_Get_Timestamp, SYS_ASCTIM, and LIB_SYS_ASCTIM
83 Lexical Functions: F$DELTA_TIME
84 Lexical functions: F$DEVICE
85 SYS_DEVICE_SCAN
86 Lexical functions: F$DIRECTORY
87 Lexical functions: F$EDIT and F$ELEMENT
88 Lexical functions: F$ENVIRONMENT
89 SYS_GETUAI
90 Lexical functions: F$EXTRACT and F$IDENTIFIER
91 LIB_FAO and LIB_FAOL
92 LIB_FAO and LIB_FAOL, part 2
93 Lexical functions: F$FAO
94 File Processing Structures
95 Lexical functions: F$FILE_ATTRIBUTES
96 SYS_DISPLAY
97 UCL Lexical functions: F$GETDVI
98 Parse_GetDVI

Glossary/Index


Download sources
Download binaries

Lexical functions - F$EDIT and F$ELEMENT

The next lexical functions are F$EDIT and F$ELEMENT. The F$EDIT function is described here and F$ELEMENT in the second half of the article.

F$EDIT returns the specified string with the specified edits.

Format
F$EDIT(string, edit-values)

Return Value
A character string containing the specified edits.

Arguments
string

The value to be edited. Quoted sections of the string are not modified.

edit-values

Specifies a string containing one or more of the following keywords:
ValueMeaning
COLLAPSERemove all spaces and tabs.
COMPRESSReplace multiple spaces or tabs with a single space
LOWERCASEChange all uppercase characters to lowercase
TRIMRemove leading and trailing spaces and tabs
UNCOMMENTRemove comments
UPCASEChange all lowercase characters to uppercase
If more than one edit value is specified, separate them with commas (,). The values may not be abbreviated. Any characters within quotation marks (") are unmodified. If both LOWERCASE and UPCASE are specified, UPCASE overrides LOWERCASE. Comments begin with any exclamation (!) that is not within quotes.

Example
$ X = F$EDIT(" Hello world ! This is a comment","LOWERCASE,TRIM,UNCOMMENT")

This would return a value equal to "hello world". Note that the comment was removed, the uppercase "H" was changed to lowercase, and the leading and trailing spaces were removed.

        Function_Edit : begin
                            if( Missing_Parentheses( '(' ) ) then
                            begin
                                exit ;
                            end ;
                            if( Parse_Edit( Err, Context ) ) then
                            begin
                                exit ;
                            end ;
                            if( Missing_Parentheses( ')' ) ) then
                            begin
                                exit ;
                            end ;
                            S := Context ;
                        end ;
We add handling for this lexical function in Function_Reference.

function Parse_Edit( var Err : integer ; var Context : string ) : boolean ;

var I : integer ;
    In_Quote : boolean ;
    Loop : integer ;
    S, S1 : string ;

begin
    Result := True ; // Assume error
    S := Get_Parameter( Err, Context ) ;
    if( Err <> 0 ) then
    begin
        exit ;
    end ;
    if( Get_Token <> ',' ) then
    begin
        Err := UCL_ARGREQ ;
        exit ;
    end ;
    S1 := Edit( Get_Parameter( Err, Context ), -1, 0 ) ;
    if( Err <> 0 ) then
    begin
        exit ;
    end ;
    I := Parse_Edit_Options( S1, Context ) ;
    if( I = 0 ) then
    begin
        Err := UCL_IVKEYW ;
        exit ;
    end ;
This code handles the lexical function. We get both parameters and ensure a comma between them. Then we call Parse_Edit_Options (covered later in this article) to convert the options to a numeric value. If that value is 0, there was an invalid option or else no options at all - in which case we return an error.

    if( ( I and 32768 ) <> 0 ) then // Remove comments
    begin
        In_Quote := False ;
        for Loop := 1 to length( S ) do
        begin
            if( S[ Loop ] = '"' ) then
            begin
                In_Quote := not In_Quote ;
            end else
            if( ( S[ Loop ] = '!' ) and ( not In_Quote ) ) then
            begin
                setlength( S, Loop - 1 ) ;
                break ;
            end ;
        end ; // for Loop := 1 to length( S )
    end ; // if( ( I and 32768 ) <> 0 )
    I := I and 32767 ;
    Context := Edit( S, I or 256, 0 ) ;
    Result := False ; // No error
end ; // Parse_Edit
The number returned from Parse_Edit_Options is a bitmask which indicates which operations to perform on the value. These flags are passed to the Edit function, which performs the operation. But before we call Edit, we have to handle flag 32768. This is the flag that indicates to remove the comments. This is not handled by Edit, so we handle it here. We march through the characters in the string, looking for an exclamation point. As we go, we take note of any quotes. Thus we only pay attention to exclamation points that are not within quotes. When/if found, we trim the value to just before that point and exit the loop. After calling Edit we return the value to the caller.

function Parse_Edit_Options( S : string ; var Context : string ) : integer ;

var I : integer ;
    S1 : string ;

begin
    Result := 0 ;
    while( S <> '' ) do
    begin
        I := pos( ',', S + ',' ) ;
        S1 := copy( S, 1, I - 1 ) ;
        S := copy( S, I + 1, length( S ) ) ;
        if( S1 = 'COLLAPSE' ) then // Removes all spaces or tabs.
        begin
            Result := Result or 2 ;
        end else
        if( S1 = 'COMPRESS' ) then // Replaces multiple spaces or tabs with a single space.
        begin
            Result := Result or 16 ;
        end else
        if( S1 = 'LOWERCASE' ) then // Changes all uppercase characters to lowercase.
        begin
            Result := Result or 512 ;
        end else
        if( S1 = 'TRIM' ) then // Removes leading and trailing spaces or tabs.
        begin
            Result := Result or 8 or 128 ;
        end else
        if( S1 = 'UNCOMMENT' ) then // Removes comments.
        begin
            Result := Result or 32768 ;
        end else
        if( S1 = 'UPCASE' ) then // Change all lowercase characters to uppercase.
        begin
            Result := Result or 32 ;
        end else
        begin
            Result := 0 ;
            Context := S1 ;
            exit ;
        end ;
    end ;
    if( ( Result and 544 ) = 544 ) then // Both UPCASE and LOWERCASE
    begin
        Result := Result and ( not 512 ) ; // Remove lowercase option
    end ;
end ; // Parse_Edit_Options
This function converts one or more text option specifications to the set of flags used by the Edit to accomplish the requested options. Once we've built the bitmask, we check for the case where both UPCASE and LOWERCASE are specified. If so, we remove the LOWERCASE option.


F$ELEMENT returns one element from a string of elements.

Format
F$ELEMENT(index, delimiter, string)

Return Value
A character string containing the specified element.

Arguments
index

Specifies the zero-based index of the element to extract. This is an integer expression. If this value exceeds the number of elements in the string, the function returns the delimiter.

delimiter

This indicates the character used to delimit the elements. Note that this must be a single Unicode character of less than 128.

string

This specifies the string of delimited elements.

Example
$ X = F$ELEMENT(1,",","A,B,C,D,E,F,G,H,I,J")
This would return a value equal to "B".

        Function_Element : begin
                               if( Missing_Parentheses( '(' ) ) then
                               begin
                                   exit ;
                               end ;
                               if( Parse_Element( Err, Context ) ) then
                               begin
                                   exit ;
                               end ;
                               if( Missing_Parentheses( ')' ) ) then
                               begin
                                   exit ;
                               end ;
                               S := Context ;
                           end ;
We add handling for this lexical function in Function_Reference.

function Parse_Element( var Err : integer ; var Context : string ) : boolean ;

var Index, P, Start : int64 ;
    S, S1 : string ;

begin
    Result := True ; // Assume error
    S := Get_Parameter( Err, Context ) ;
    if( Err <> 0 ) then
    begin
        exit ;
    end ;
    Result := Missing_Comma( Err ) ;
    if( Result ) then
    begin
        exit ;
    end ;
    if( S <> '' ) then
    begin
        S := UCL_Strtoint( S ) ;
        if( not trystrtoint64( S, Index ) ) then
        begin
            Err := UCL_EXPSYN ; // Invalid expression syntax
            Context := S ;
            exit ;
        end ;
    end ;
    if( Index < 0 ) then
    begin
        Err := UCL_INVRANGE ;
        Context := S ;
        exit ;
    end ;

    S := Get_Parameter( Err, Context ) ;
    if( Err <> 0 ) then
    begin
        exit ;
    end ;
    if( length( S ) <> 1 ) then
    begin
        Err := UCL_STRTOOLNG ;
        Context := S ;
        exit ;
    end ;
    Result := Missing_Comma( Err ) ;
    if( Result ) then
    begin
        exit ;
    end ;

    S1 := Get_Parameter( Err, Context ) ;
    if( Err <> 0 ) then
    begin
        exit ;
    end ;
This function handles the F$ELEMENT lexical function. We get the first parameter (the index) and verify that it is an integer value greater than or equal to zero. We exit with an error if the parameter is less than 0 or non-numeric. We get the second parameter and if it is not exactly one character in length, we exit with an error. Then we get the third parameter.

    if( S1 <> '' ) then
    begin
        Start := 1 ;
        P := 1 ;
        while( P <= length( S1 ) ) do
        begin
            if( S1[ P ] = S ) then
            begin
                if( Index = 0 ) then
                begin
                    break ;
                end ;
                Start := P + 1 ;
                dec( Index ) ;
            end ; // if( S1[ P ] = S )
            inc( P ) ;
        end ; // while( P < length( S1 ) )
        S1 := copy( S1, Start, P - Start ) ;
    end ; // if( S1 <> '' )
    if( Index > 0 ) then // Index is out of range
    begin
        S1 := S ;
    end ;

    Context := S1 ;
    Result := False ; // No error
end ; // Parse_Element
If the third parameter (in S1) is empty, we just drop through and return a null result. Otherwise, we iterate through the string looking for the delimiter character. Each time we encounter the delimiter character, we decrement the index. If the index reaches 0, we exit the loop. We keep track of the beginning of each element and the current position as we procede through the string. When the loop is over, we return the last segment of the string. However, if Index is not 0 then that means that we ran out of elements before we reached the specificed index. In that case, we simply return the delimiter.

In the next article, we'll look at the next lexical function.

 

Copyright © 2020 by Alan Conroy. This article may be copied in whole or in part as long as this copyright is included.