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

UCL Expressions, part 4 - Evaluation

In the previous articles, we described how we built the expression tree as we parsed a UCL expression. In this article, we will evaluate the tree and come up with a result from the expression.

function Get_Expression( var Err : integer ; var Context : string ) : string ;

var Root : tExpression_Node ;
    I : int64 ;
    _Err : integer ;

begin // Get_Expression
    Result := '' ;
    Err := 0 ;
    Context := '' ;
    Root := Parse_Expression( _Err, Context ) ; // Parse and validate the expression
    if( Root = nil ) then // Error
    begin
        exit ;
    end ;

    // Process expression tree...
    Result := Process( Root ) ;
    if( Valid_Int( Result, I ) ) then
    begin
        Result := inttostr( I ) ;
    end ;
end ; // Get_Expression
The Get_Expression function takes an expression and calls the expression parser (Parse_Expression) which we covered a couple articles ago. If there was error during parsing, we exit. Otherwise, we call the Process local function to do the actual evaluation of the expression tree. If the result is an integer value, we return it in normalized form. For instance, if a hex value comes back from Process, we convert it to a decimal integer value.

Before we examine the code for the Process function, we need to look at a couple of helper functions.

    function String_From_Boolean( B : boolean ) : string ;

    begin
        if( B ) then
        begin
            Result := '1' ;
        end else
        begin
            Result := '0' ;
        end ;
    end ;


    function Boolean_From_String( S : string ) : boolean ;

    begin
        Result := odd( Strtoint64( UCL_Strtoint( S ) ) ) ;
    end ;
These simple functions handle converting between boolean and string values, according to the rules we specified for UCL expressions.

    function Process( Current : tExpression_Node ) : string ;

    var L, R : string ;
        Ln, Rn : int64 ;
        Unary : boolean ;

    begin
        Result := '' ;
        if( Current = nil ) then
        begin
            exit ;
        end ;
        if( Current.Operator = 0 ) then // Terminal node on tree
        begin
            Result := Current.Value ;
            exit ;
        end ;
We initialize the result to a null string, which would indicate some sort of evaluation failure. If the passed-in expression tree root node is nil, we exit since there is nothing to do. If the root node isn't an operator, then it is a value and, by definition, is the only thing in the tree. In that case, we simply return the node's value and exit.

        Unary := Current.Left = nil ;
        if( Unary ) then // Unary operator
        begin
            L := '' ;
            if( Current.Right = nil ) then
            begin
                Err := UCL_EXPSYN ;
                exit ;
            end ;
        end else
        begin
            L := Process( Current.Left ) ;
            if( Err <> 0 ) then // Some error happened
            begin
                exit ;
            end ;
        end ;
We will be using a technique called "left-first, depth-first traversal" to process the tree. Basically, this means that we follow the links to the farthest left, deepest node and work our way back up the tree. If the left link is nil, we have a unary operator. In that case, if the right link is also nil, we have a problem because we can't have an operator with no operands. Only values can have no links and we've already checked for a value above.
If there is a left link, we recursively call ourselves, passing the left linked node as the root of a subtree to evaluate. If the result of that call is nil, there was an error, so we exit.

        R := Process( Current.Right ) ;
        if( Err <> 0 ) then // Some error happened
        begin
            exit ;
        end ;
        Ln := strtoint64( UCL_Strtoint( L ) ) ;
        Rn := strtoint64( UCL_Strtoint( R ) ) ;
Next, we recurse down the right link. When called, the function will again try the left link, thus driving us down one more level. Upon return, we will have the results of the right subtree. We assign the result of the left subtree to the L variable and the result of the right subtree to the R variable. Then we convert them to integer values. Note that, in some cases, the results will not be numeric, in which case we won't use the numeric versions. It might be a little extra overhead when it isn't needed, but it makes the code much simpler, and we aren't as concerned about overhead outside of the executive as we are within it.

        case Current.Operator of
            Op_EQ : Result := String_From_Boolean( Ln = Rn ) ;
            Op_EQS : Result := String_From_Boolean( L = R ) ;
            Op_NE : Result := String_From_Boolean( Ln <> Rn ) ;
            Op_NES : Result := String_From_Boolean( L <> R ) ;
            Op_GE : Result := String_From_Boolean( Ln >= Rn ) ;
            Op_GES : Result := String_From_Boolean( L >= R ) ;
            Op_LE : Result := String_From_Boolean( Ln <= Rn ) ;
            Op_LES : Result := String_From_Boolean( L <= R ) ;
            Op_GT : Result := String_From_Boolean( Ln > Rn ) ;
            Op_GTS : Result := String_From_Boolean( L > R ) ;
            Op_LT : Result := String_From_Boolean( Ln < Rn ) ;
            Op_LTS : Result := String_From_Boolean( L < R ) ;
            Op_NOT : begin
                         if( not Unary ) then
                         begin
                             Err := UCL_EXPSYN ;
                             exit ;
                         end ;
                         Result := String_From_Boolean( Boolean_From_String( inttostr( not Rn ) ) ) ;
                     end ;
            Op_OR : Result := String_From_Boolean( Boolean_From_String( L ) or Boolean_From_String( R ) ) ;
            Op_AND : Result := String_From_Boolean( Boolean_From_String( L ) and Boolean_From_String( R ) ) ;
            Op_Multiply :
                begin
                    Result := inttostr( Ln * Rn ) ;
                end ;
            Op_Divide :
                begin
                    if( Rn = 0 ) then
                    begin
                        Result := '0' ;
                    end else
                    begin
                        Result := inttostr( Ln div Rn ) ;
                    end ;
                end ;
            Op_Add :
                begin
                    if( Unary ) then
                    begin
                        Result := R ; // Unary + (does nothing)
                    end else
                    begin
                        Result := inttostr( Ln + Rn ) ;
                    end ;
                end ;
            Op_Subtract :
                begin
                    if( Unary ) then
                    begin
                        Result := inttostr( -Rn ) ; // Unary minus
                    end else
                    begin
                        Result := inttostr( Ln - Rn ) ;
                    end ;
                end ;
        end ; // case Current.Operator
    end ; // Get_Expression.Process
When we reach this point in the code, we have an operator node and the values for the left (if no unary) and right subtrees. Based on the operator, we perform the appropriate operation.

To summarize, the function recursively calls itself to process the left and right subtrees, and then evaluate them according to the operator. When done, we will have a single value representing the evaluated expression, which we then return.

In the next several articles, we will be looking at the UCL lexical functions. We will start with a look at a new system call.

 

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