Subversion Repositories tendra.SVN

Rev

Rev 2 | Blame | Compare with Previous | Last modification | View Log | RSS feed

/*
                 Crown Copyright (c) 1997
    
    This TenDRA(r) Computer Program is subject to Copyright
    owned by the United Kingdom Secretary of State for Defence
    acting through the Defence Evaluation and Research Agency
    (DERA).  It is made available to Recipients with a
    royalty-free licence for its use, reproduction, transfer
    to other parties and amendment for any purpose not excluding
    product development provided that any such use et cetera
    shall be deemed to be acceptance of the following conditions:-
    
        (1) Its Recipients shall ensure that this Notice is
        reproduced upon any copies or amended versions of it;
    
        (2) Any amended version of it shall be clearly marked to
        show both the nature of and the organisation responsible
        for the relevant amendment or amendments;
    
        (3) Its onward transfer from a recipient to another
        party shall be deemed to be that party's acceptance of
        these conditions;
    
        (4) DERA gives no warranty or assurance as to its
        quality or suitability for any purpose and DERA accepts
        no liability whatsoever in relation to any use to which
        it may be put.
*/


#include "config.h"
#include "types.h"
#include "eval.h"
#include "node.h"
#include "shape.h"
#include "table.h"
#include "tdf.h"
#include "utility.h"


/*
    CREATE A NAT CORRESPONDING TO THE VALUE n

    This routine creates a node corresponding to the nat with value n.
*/

node *make_nat
    PROTO_N ( ( n ) )
    PROTO_T ( long n )
{
    node *p = new_node () ;
    p->cons = cons_no ( SORT_nat, ENC_make_nat ) ;
    p->son = new_node () ;
    p->son->cons = make_construct ( SORT_small_tdfint ) ;
    p->son->cons->encoding = n ;
    return ( p ) ;
}


/*
    CREATE AN INTEGER CORRESPONDING TO THE VALUE n

    This routine creates a node corresponding to the sign bit and the
    value of n.
*/

node *make_int
    PROTO_N ( ( n ) )
    PROTO_T ( long n )
{
    node *p = new_node () ;
    if ( n < 0 ) {
        p->cons = &true_cons ;
        n = -n ;
    } else {
        p->cons = &false_cons ;
    }
    p->bro = new_node () ;
    p->bro->cons = make_construct ( SORT_small_tdfint ) ;
    p->bro->cons->encoding = n ;
    return ( p ) ;
}


/*
    CREATE A SIGNED_NAT CORRESPONDING TO THE VALUE n

    This routine creates a node corresponding to the signed_nat with value n.
*/

static node *make_signed_nat
    PROTO_N ( ( n ) )
    PROTO_T ( long n )
{
    node *p = new_node () ;
    p->cons = cons_no ( SORT_signed_nat, ENC_make_signed_nat ) ;
    p->son = make_int ( n ) ;
    return ( p ) ;
}


/*
    CREATE A MAKE_INT EXPRESSION CORRESPONDING TO THE VALUE n

    This routine creates a node corresponding to a make_int expression of
    shape sh and value n or val.
*/

static node *make_int_exp
    PROTO_N ( ( sh, n, val ) )
    PROTO_T ( node *sh X long n X char *val )
{
    node *p = new_node () ;
    p->cons = cons_no ( SORT_exp, ENC_make_int ) ;
    p->son = copy_node ( sh->son ) ;
    p->son->bro = make_signed_nat ( n ) ;
    if ( val ) {
        /* Assign large values */
        node *r = p->son->bro->son->bro ;
        r->cons = make_construct ( SORT_tdfint ) ;
        r->cons->name = val ;
    }
    p->shape = sh ;
    return ( p ) ;
}


/*
    IS A NODE A CONSTANT?

    This routine checks whether the node p represents a small integer
    constant.  If so it returns the value of the constant via pn.
*/

static boolean is_constant
    PROTO_N ( ( p, pn ) )
    PROTO_T ( node *p X long *pn )
{
    if ( p ) {
        sortname s = p->cons->sortnum ;
        long n = p->cons->encoding ;
        if ( s == SORT_exp && n == ENC_make_int ) {
            p = p->son->bro ;
            s = p->cons->sortnum ;
            n = p->cons->encoding ;
        }
        if ( s == SORT_signed_nat && n == ENC_make_signed_nat ) {
            /* Allow signed integer literals */
            long negate = p->son->cons->encoding ;
            p = p->son->bro ;
            s = p->cons->sortnum ;
            n = p->cons->encoding ;
            if ( negate ) n = -n ;
        } else if ( s == SORT_nat && n == ENC_make_nat ) {
            /* Allow integer literals */
            p = p->son ;
            s = p->cons->sortnum ;
            n = p->cons->encoding ;
        } else if ( s == SORT_bool ) {
            /* Allow boolean literals */
            if ( n == ENC_false ) {
                *pn = 0 ;
                return ( 1 ) ;
            }
            if ( n == ENC_true ) {
                *pn = 1 ;
                return ( 1 ) ;
            }
        }
        if ( s == SORT_small_tdfint ) {
            /* Small constant found */
            *pn = n ;
            return ( 1 ) ;
        }
    }
    return ( 0 ) ;
}


/*
    INTEGER TYPE MASKS

    These values give the maximum values for the various known integral
    types.
*/

static long var_max = 32 ;
static unsigned long *var_mask ;


/*
    IS A SHAPE A KNOWN INTEGRAL TYPE?

    This routine checks whether the shape sh represents a known integral
    type.  If so it returns the sign via pn and the size via pm.
*/

static boolean is_var_width
    PROTO_N ( ( sh, pn, pm ) )
    PROTO_T ( node *sh X long *pn X long *pm )
{
    if ( sh && sh->cons->encoding == ENC_integer ) {
        if ( sh->son->cons->encoding == ENC_var_width ) {
            node *q = sh->son->son ;
            if ( is_constant ( q, pn ) ) {
                if ( is_constant ( q->bro, pm ) ) {
                    return ( 1 ) ;
                }
            }
        }
    }
    return ( 0 ) ;
}


/*
    CALCULATE 1 << n

    This routine calculates '1 << n' as a string of octal digits.
*/

static char *shift_one
    PROTO_N ( ( n ) )
    PROTO_T ( long n )
{
    long i ;
    char buff [100] ;
    switch ( n % 3 ) {
        case 0 : buff [0] = '1' ; break ;
        case 1 : buff [0] = '2' ; break ;
        case 2 : buff [0] = '4' ; break ;
    }
    for ( i = 0 ; i < n / 3 ; i++ ) {
        buff [ i + 1 ] = '0' ;
    }
    return ( string_copy ( buff, ( int ) ( i + 1 ) ) ) ;
}


/*
    CALCULATE val - 1

    This routine calculates 'val - 1' for the string of octal digits val,
    returning the result as a string of octal digits.
*/

static char *minus_one
    PROTO_N ( ( val ) )
    PROTO_T ( char *val )
{
    int i, n = ( int ) strlen ( val ) ;
    char *res = string_copy ( val, n ) ;
    for ( i = n - 1 ; i >= 0 ; i-- ) {
        char c = res [i] ;
        if ( c != '0' ) {
            res [i] = c - 1 ;
            break ;
        }
        res [i] = '7' ;
    }
    if ( res [0] == '0' ) res++ ;
    return ( res ) ;
}


/*
    EVALUATE A CONSTANT EXPRESSION

    This routine evaluates the constant expression given by the operation
    op applied to the operands a and b in the type indicated by the shape
    sh.  err gives the associated overflow error treatment, if any.  The
    routine returns null if the value cannot be calculated.
*/

static node *eval_exp
    PROTO_N ( ( op, err, sh, a, b ) )
    PROTO_T ( long op X long err X node *sh X long a X long b )
{
    long c = 0 ;
    long sz = 0 ;
    long sgn = 0 ;
    char *val = null ;

    /* Check result shape */
    if ( !is_var_width ( sh, &sgn, &sz ) ) return ( null ) ;
    if ( !sgn && ( a < 0 || b < 0 ) ) return ( null ) ;
    if ( sz < 1 ) return ( null ) ;
    if ( sz > var_max ) {
        if ( sz < 256 ) {
            /* Evaluate some special cases */
            if ( op == ENC_shift_left && a == 1 ) {
                if ( !sgn && b < sz ) val = shift_one ( b ) ;
            } else if ( op == ENC_negate && a == 1 ) {
                if ( !sgn && err == ENC_wrap ) {
                    val = shift_one ( sz ) ;
                    val = minus_one ( val ) ;
                }
            } else if ( op == ENC_minus && a == 0 && b == 1 ) {
                if ( !sgn && err == ENC_wrap ) {
                    val = shift_one ( sz ) ;
                    val = minus_one ( val ) ;
                }
            }
            if ( val ) return ( make_int_exp ( sh, c, val ) ) ;
        }
        return ( null ) ;
    }

    /* Evaluate result */
    switch ( op ) {
        case ENC_abs : {
            c = a ;
            if ( c < 0 ) c = -a ;
            break ;
        }
        case ENC_and : {
            if ( a < 0 || b < 0 ) return ( null ) ;
            c = ( a & b ) ;
            break ;
        }
        case ENC_change_variety : {
            c = a ;
            break ;
        }
        case ENC_div0 :
        case ENC_div1 :
        case ENC_div2 : {
            if ( a < 0 || b <= 0 ) return ( null ) ;
            c = a / b ;
            break ;
        }
        case ENC_maximum : {
            c = ( a >= b ? a : b ) ;
            break ;
        }
        case ENC_minimum : {
            c = ( a < b ? a : b ) ;
            break ;
        }
        case ENC_minus : {
            c = a - b ;
            break ;
        }
        case ENC_mult : {
            c = a * b ;
            break ;
        }
        case ENC_negate : {
            c = -a ;
            break ;
        }
        case ENC_not : {
            if ( sgn || err != ENC_wrap ) return ( null ) ;
            c = ~a ;
            break ;
        }
        case ENC_or : {
            if ( a < 0 || b < 0 ) return ( null ) ;
            c = ( a | b ) ;
            break ;
        }
        case ENC_plus : {
            c = a + b ;
            break ;
        }
        case ENC_rem0 :
        case ENC_rem1 :
        case ENC_rem2 : {
            if ( a < 0 || b <= 0 ) return ( null ) ;
            c = a % b ;
            break ;
        }
        case ENC_shift_left : {
            if ( sgn || err != ENC_wrap ) return ( null ) ;
            if ( b < var_max ) {
                unsigned long ua = ( unsigned long ) a ;
                unsigned long ub = ( unsigned long ) b ;
                c = ( long ) ( ua << ub ) ;
            } else {
                c = 0 ;
            }
            break ;
        }
        case ENC_shift_right : {
            if ( sgn || err != ENC_wrap ) return ( null ) ;
            if ( b < var_max ) {
                unsigned long ua = ( unsigned long ) a ;
                unsigned long ub = ( unsigned long ) b ;
                c = ( long ) ( ua >> ub ) ;
            } else {
                c = 0 ;
            }
            break ;
        }
        case ENC_xor : {
            if ( a < 0 || b < 0 ) return ( null ) ;
            c = ( a ^ b ) ;
            break ;
        }
        case ENC_power :
        case ENC_rotate_left :
        case ENC_rotate_right :
        default : {
            /* NOT YET IMPLEMENTED */
            return ( null ) ;
        }
    }

    /* Check for overflow */
    if ( sgn ) {
        long v = ( long ) var_mask [ sz - 1 ] ;
        if ( c < -( v + 1 ) || c > v ) return ( null ) ;
    } else {
        unsigned long uc ;
        unsigned long uv = var_mask [ sz ] ;
        if ( c < 0 ) {
            if ( err != ENC_wrap ) return ( null ) ;
            uc = ( unsigned long ) -c ;
            uc = ( ( uv - uc + 1 ) & uv ) ;
            if ( uc > var_mask [ var_max - 1 ] ) {
                val = ulong_to_octal ( uc ) ;
                uc = 0 ;
            }
        } else {
            uc = ( unsigned long ) c ;
            if ( uc > uv ) {
                if ( err != ENC_wrap ) return ( null ) ;
                uc &= uv ;
            }
        }
        c = ( long ) uc ;
    }

    /* Create the result */
    return ( make_int_exp ( sh, c, val ) ) ;
}


/*
    EVALUATE A CONSTANT CONDITION

    This routine evaluates the condition tst for the values a and b.  It
    returns 0 if the test is false, 1 if it is true and -1 if it cannot
    be evaluated.
*/

static int eval_test
    PROTO_N ( ( tst, a, b ) )
    PROTO_T ( long tst X long a X long b )
{
    int res = 0 ;
    switch ( tst ) {
        case ENC_equal :
        case ENC_not_less_than_and_not_great : {
            if ( a == b ) res = 1 ;
            break ;
        }
        case ENC_not_equal :
        case ENC_less_than_or_greater_than : {
            if ( a != b ) res = 1 ;
            break ;
        }
        case ENC_greater_than :
        case ENC_not_less_than_or_equal : {
            if ( a > b ) res = 1 ;
            break ;
        }
        case ENC_greater_than_or_equal :
        case ENC_not_less_than : {
            if ( a >= b ) res = 1 ;
            break ;
        }
        case ENC_less_than :
        case ENC_not_greater_than_or_equal : {
            if ( a < b ) res = 1 ;
            break ;
        }
        case ENC_less_than_or_equal :
        case ENC_not_greater_than : {
            if ( a <= b ) res = 1 ;
            break ;
        }
        default : {
            res = -1 ;
            break ;
        }
    }
    return ( res ) ;
}


/*
    EVALUATE A DECREMENT EXPRESSION

    This routine evaluates 'p - 1' for the expression node p.  It returns
    null if the value cannot be evaluated.
*/

static node *eval_decr
    PROTO_N ( ( p ) )
    PROTO_T ( node *p )
{
    if ( p->cons->encoding == ENC_make_int ) {
        node *sh = p->shape ;
        if ( sh == null ) sh = sh_integer ( p->son ) ;
        p = p->son->bro ;
        if ( p->cons->encoding == ENC_make_signed_nat ) {
            if ( !p->son->cons->encoding ) {
                p = p->son->bro ;
                if ( p->cons->sortnum == SORT_tdfint ) {
                    long c = 0 ;
                    char *val = minus_one ( p->cons->name ) ;
                    if ( fits_ulong ( val, 1 ) ) {
                        c = ( long ) octal_to_ulong ( val ) ;
                        val = null ;
                    }
                    return ( make_int_exp ( sh, c, val ) ) ;
                }
            }
        }
    }
    return ( null ) ;
}


/*
    EVALUATE A NODE

    This routine evaluates the node p.  p will not be null.
*/

static node *eval_node
    PROTO_N ( ( p ) )
    PROTO_T ( node *p )
{
    sortname s = p->cons->sortnum ;
    long n = p->cons->encoding ;
    if ( s > 0 && n == sort_conds [s] ) {
        /* Conditional constructs */
        long m = 0 ;
        if ( is_constant ( p->son, &m ) ) {
            p = p->son->bro ;
            if ( m == 0 ) p = p->bro ;
            return ( p->son ) ;
        }
    }
    if ( s == SORT_exp ) {
        long m1 = 0, m2 = 0 ;
        switch ( n ) {
            case ENC_make_int : {
                /* Make sure that constants have a shape */
                if ( p->shape == null ) p->shape = sh_integer ( p->son ) ;
                break ;
            }
            case ENC_change_variety : {
                /* Allow for change_variety */
                node *r = p->son->bro ;
                if ( p->shape == null ) p->shape = sh_integer ( r ) ;
                if ( is_constant ( r->bro, &m1 ) ) {
                    long err = p->son->cons->encoding ;
                    node *q = eval_exp ( n, err, p->shape, m1, m2 ) ;
                    if ( q ) p = q ;
                }
                break ;
            }
            case ENC_integer_test : {
                /* Allow for integer_test */
                node *r = p->son->bro->bro->bro ;
                if ( is_constant ( r, &m1 ) ) {
                    if ( is_constant ( r->bro, &m2 ) ) {
                        long tst = p->son->bro->cons->encoding ;
                        int res = eval_test ( tst, m1, m2 ) ;
                        if ( res == 0 ) {
                            node *q = new_node () ;
                            q->cons = cons_no ( SORT_exp, ENC_goto ) ;
                            q->son = copy_node ( p->son->bro->bro ) ;
                            return ( q ) ;
                        }
                        if ( res == 1 ) {
                            node *q = new_node () ;
                            q->cons = cons_no ( SORT_exp, ENC_make_top ) ;
                            return ( q ) ;
                        }
                    }
                }
                break ;
            }
            case ENC_conditional : {
                /* Allow for conditional */
                node *r = p->son->bro ;
                if ( is_constant ( r->bro, &m2 ) ) {
                    if ( is_constant ( r, &m1 ) ) {
                        /* First branch terminates */
                        return ( copy_node ( r ) ) ;
                    }
                    if ( r->cons->encoding == ENC_goto ) {
                        if ( eq_node ( p->son, r->son ) ) {
                            /* First branch is a jump */
                            return ( copy_node ( r->bro ) ) ;
                        }
                    }
                }
                break ;
            }
            case ENC_sequence : {
                /* Allow for sequence */
                boolean reached = 1 ;
                node *q = null ;
                node *r = p->son->son ;
                while ( r != null ) {
                    if ( is_constant ( r, &m1 ) ) {
                        if ( reached ) q = r ;
                    } else if ( r->cons->encoding == ENC_goto ) {
                        if ( reached ) q = r ;
                        reached = 0 ;
                    } else if ( r->cons->encoding == ENC_make_top ) {
                        if ( reached ) q = r ;
                    } else {
                        return ( p ) ;
                    }
                    r = r->bro ;
                }
                r = p->son->bro ;
                if ( is_constant ( r, &m1 ) ) {
                    if ( reached ) q = r ;
                } else if ( r->cons->encoding == ENC_goto ) {
                    if ( reached ) q = r ;
                } else if ( r->cons->encoding == ENC_make_top ) {
                    if ( reached ) q = r ;
                } else {
                    return ( p ) ;
                }
                q = copy_node ( q ) ;
                return ( q ) ;
            }
            case ENC_not : {
                /* Unary operations */
                node *r = p->son ;
                if ( is_constant ( r, &m1 ) ) {
                    long err = ENC_wrap ;
                    node *q = eval_exp ( n, err, r->shape, m1, m2 ) ;
                    if ( q ) p = q ;
                }
                break ;
            }
            case ENC_abs :
            case ENC_negate : {
                /* Unary operations with error treatment */
                node *r = p->son->bro ;
                if ( is_constant ( r, &m1 ) ) {
                    long err = p->son->cons->encoding ;
                    node *q = eval_exp ( n, err, r->shape, m1, m2 ) ;
                    if ( q ) p = q ;
                }
                break ;
            }
            case ENC_and :
            case ENC_maximum :
            case ENC_minimum :
            case ENC_or :
            case ENC_rotate_left :
            case ENC_rotate_right :
            case ENC_shift_right :
            case ENC_xor : {
                /* Binary operations */
                node *r = p->son ;
                if ( is_constant ( r, &m1 ) ) {
                    if ( is_constant ( r->bro, &m2 ) ) {
                        long err = ENC_wrap ;
                        node *q = eval_exp ( n, err, r->shape, m1, m2 ) ;
                        if ( q ) p = q ;
                    }
                }
                break ;
            }
            case ENC_minus :
            case ENC_mult :
            case ENC_plus :
            case ENC_power :
            case ENC_shift_left : {
                /* Binary operations with error treatment */
                node *r = p->son->bro ;
                if ( is_constant ( r->bro, &m2 ) ) {
                    if ( is_constant ( r, &m1 ) ) {
                        long err = p->son->cons->encoding ;
                        node *q = eval_exp ( n, err, r->shape, m1, m2 ) ;
                        if ( q ) p = q ;
                    } else if ( n == ENC_minus && m2 == 1 ) {
                        node *q = eval_decr ( r ) ;
                        if ( q ) p = q ;
                    }
                }
                break ;
            }
            case ENC_div0 :
            case ENC_div1 :
            case ENC_div2 :
            case ENC_rem0 :
            case ENC_rem1 :
            case ENC_rem2 : {
                /* Binary operations with two error treatments */
                node *r = p->son->bro->bro ;
                if ( is_constant ( r, &m1 ) ) {
                    if ( is_constant ( r->bro, &m2 ) ) {
                        long err = p->son->bro->cons->encoding ;
                        node *q = eval_exp ( n, err, r->shape, m1, m2 ) ;
                        if ( q ) p = q ;
                    }
                }
                break ;
            }
        }
    } else if ( s == SORT_nat ) {
        if ( n == ENC_computed_nat ) {
            long m = 0 ;
            if ( is_constant ( p->son, &m ) ) {
                if ( m >= 0 ) return ( make_nat ( m ) ) ;
            }
        }
    } else if ( s == SORT_signed_nat ) {
        if ( n == ENC_computed_signed_nat ) {
            long m = 0 ;
            if ( is_constant ( p->son, &m ) ) {
                return ( make_signed_nat ( m ) ) ;
            }
            if ( p->son->cons->encoding == ENC_make_int ) {
                return ( copy_node ( p->son->son->bro ) ) ;
            }
        } else if ( n == ENC_snat_from_nat ) {
            long m1 = 0, m2 = 0 ;
            if ( is_constant ( p->son, &m1 ) ) {
                if ( is_constant ( p->son->bro, &m2 ) ) {
                    if ( m1 ) m2 = -m2 ;
                    return ( make_signed_nat ( m2 ) ) ;
                }
            }
        }
    }
    return ( p ) ;
}


/*
    RECURSIVELY EVALUATE A NODE

    This routine recursively calls eval_node to evaluate the node p and
    all its subnodes.
*/

static node *eval_fully
    PROTO_N ( ( p ) )
    PROTO_T ( node *p )
{
    if ( p ) {
        node *q = p->bro ;
        p->son = eval_fully ( p->son ) ;
        p = eval_node ( p ) ;
        p->bro = eval_fully ( q ) ;
    }
    return ( p ) ;
}


/*
    EVALUATE A TOKEN DEFINITION

    This routine evaluates the definition of the token p.
*/

static void eval_tokdef
    PROTO_N ( ( p ) )
    PROTO_T ( construct *p )
{
    if ( p->encoding != -1 ) {
        tok_info *info = get_tok_info ( p ) ;
        info->def = eval_fully ( info->def ) ;
    }
    return ;
}


/*
    EVALUATE AN ALIGNMENT TAG DEFINITION

    This routine evaluates the definition of the alignment tag p.
*/

static void eval_aldef
    PROTO_N ( ( p ) )
    PROTO_T ( construct *p )
{
    if ( p->encoding != -1 ) {
        al_tag_info *info = get_al_tag_info ( p ) ;
        info->def = eval_fully ( info->def ) ;
    }
    return ;
}


/*
    EVALUATE A TAG DECLARATION AND DEFINITION

    This routine evaluates the declaration and definition of the tag p.
*/

static void eval_tagdef
    PROTO_N ( ( p ) )
    PROTO_T ( construct *p )
{
    if ( p->encoding != -1 ) {
        tag_info *info = get_tag_info ( p ) ;
        info->dec = eval_fully ( info->dec ) ;
        info->def = eval_fully ( info->def ) ;
    }
    return ;
}


/*
    EVALUATE ALL TOKEN DEFINITIONS

    This routine evaluates all token, alignment tag and tag definitions.
*/

void eval_all
    PROTO_Z ()
{
    long i ;
    unsigned long m = 0 ;
    var_max = BYTESIZE * ( long ) sizeof ( long ) ;
    var_mask = alloc_nof ( unsigned long, var_max + 1 ) ;
    var_mask [0] = 0 ;
    for ( i = 1 ; i <= var_max ; i++ ) {
        m = 2 * m + 1 ;
        var_mask [i] = m ;
    }
    init_shapes () ;
    apply_to_all ( eval_tokdef, SORT_token ) ;
    apply_to_all ( eval_aldef, SORT_al_tag ) ;
    apply_to_all ( eval_tagdef, SORT_tag ) ;
    return ;
}