Sophie

Sophie

distrib > Mandriva > 2010.0 > i586 > media > contrib-release > by-pkgid > 1e4be4f6cca2c9a2bfc532dbed99ff6a > files > 28

aikido-1.40-6mdv2010.0.i586.rpm

/*
 * expr.aikido
 *
 * Aikido Language System,
 * export version: 1.00
 * Copyright (c) 2002-2003 Sun Microsystems, Inc.
 *
 * Sun Public License Notice
 * 
 * The contents of this file are subject to the Sun Public License Version 1.0 (the "License"). You
 * may not use this file except in compliance with the License. A copy of the License is available
 * at http://www.opensource.org/licenses/sunpublic.php
 * 
 * The Original Code is Aikido. 
 * The Initial Developer of the Original Code is David Allison on behalf of Sun Microsystems, Inc. 
 * Copyright (C) Sun Microsystems, Inc. 2000-2003. All Rights Reserved.
 * 
 * 
 * Contributor(s): dallison
 *
 * Version:  1.2
 * Created by dallison on 4/19/2002
 * Last modified by dallison on 03/07/29
 */


// expressions
package CParser {

    extend Parser {

        class CompoundStatement...
        class ExpressionStatement...

        class Node extends ParseNode {
            protected var type = null

        public:
            function getType {
                return type
            }

            function hasAddress {
                return false
            }

            public function isConst0 {
                return false
            }

            function evaluate {
                throw "Bad expression"
            }

            public function dump (stream) {
                if (type.id == 0) {
                    type.dump (stream)
                }
                ParseNode.dump (stream)
                [type.id, ' '] -> stream
            }
        }

        class Constant (protected value) extends Node {
            public function getValue {
                return value
            }

            public function evaluate {
                 return value
            }

            public function dump (stream) extends dump (stream) {
                value -> stream
            }
        }

        class IntConst (value) extends Constant (value) {
            switch (lex.postfix) {
            case "L":
                type = new ScalarTypeRecord (CLONG)
                break
            case "U":
                type = new ScalarTypeRecord (CUNSIGNED | CINT)
            case "UL":
                type = new ScalarTypeRecord (CUNSIGNED | CLONG)
                break
            default:
                type = new ScalarTypeRecord (CINT)
            }

            public function print (s, indent) {
                value -> s
            }

            public function isConst0 {
                return value == 0
            }

            public function evaluate {
                 return value
            }

        }

        class CharConst (value) extends Constant (value) {
            type = new ScalarTypeRecord (CCHAR)

            public function print (s, indent) {
                ['\'', value, '\''] -> s
            }

            public function evaluate {
                 return cast<int>(value)
            }
        }

        class StringConst (value, islong) extends Constant (value) {
            if (islong) {
                type = new PointerTypeRecord (new ScalarTypeRecord (CINT))
            } else {
                type = new PointerTypeRecord (new ScalarTypeRecord (CCHAR))
            }

            public function print (s, indent) {
                if (islong) {
                    'L' -> s
                }
                ['\"', value, '\"'] -> s
            }

            public function evaluate {
                 return value
            }

            public function dump (stream) {
                Node.dump (stream)
                foreach ch value {
                    format ("%02x", cast<int>(ch)) -> stream
                }
            }
        }

        class RealConst (value) extends Constant (value) {
            type = new ScalarTypeRecord (CDOUBLE)            // deal with float constants

            public function print (s, indent) {
                value -> s
            }

            public function evaluate {
                 return value
            }
        }


        class Identifier (symbol) extends Node {
            type = symbol.getType()

            public function hasAddress {
                return true
            }

            public function getSymbol {
                return symbol
            }

            public function print (s, indent) {
                symbol.getName() -> s
            }
 
            public function evaluate {
                if (symbol.getStorage() == sENUMCONST) {
                    return symbol.getDetails()
                } else {
                    throw "Non-constant identifier"
                }
            }

            public function dump (stream) {
                if (symbol.id == 0) {
                    symbol.dump (stream)
                }
                Node.dump (stream)
                symbol.id -> stream
            }
        }

        class StructConst (t) extends Node {
            type = t

            public function print (s, indent) {
                '{' -> s
                var comma = false
                var n = type.numMembers()
                foreach i n {
                    if (comma) {
                        ',' -> s
                    }
                    comma = true
                    var mem = type.getMember(i)
                    var initval = mem.getDetails()
                    if (initval != null) {
                        ['.', mem.getName(), " = "] -> s
                        initval.print (s,0)
                    }
                }
                '}' -> s
            }
        }

        class ArrayConst (t, vals) extends Node {
            type = t

            public function print (s, indent) {
                if (typeof (vals) == "string") {
                    ['"', vals, '"'] -> s
                } else {
                    '{' -> s
                    var comma = false
                    foreach v vals {
                        if (comma) {
                            ',' -> s
                        }
                        comma = true
                        v.print (s,0)
                    }
                    '}' -> s
                }
            }
        }

        class Expression (public opcode, public left = null, public right = null) extends Node {

            public function hasAddress {
                 return opcode in [T_DOT, T_ARROW, T_LSQUARE, T_CONTENTS]
            }

            var lefttype = left.getType()
            var righttype = right != null ? right.getType() : null

            // convert the left or right to real if necessary
            function coercereal {
                if (lefttype.isReal() && !righttype.isReal()) {
                    right = new Expression (T_FLT, right)
                    type = lefttype
                    return true
                } elif (righttype.isReal() && !lefttype.isReal()) {
                    left = new Expression (T_FLT, left)
                    type = righttype
                    return true
                }
                return false
            }

            function checkScalar {
                if (lefttype.isScalar() || lefttype.isIntegral()) {
                    if (righttype != null) {
                        if (righttype.isScalar() || righttype.isIntegral()) {
                            return
                        }
                    } else {
                        return
                    }
                }
                error ("Invalid use of non-scalar type")
            }

            switch (opcode) {
            case T_CONTENTS:
                if (lefttype.isPointer()) {
                    type = lefttype.dereference()
                } else {
                    error ("Cannot take contents of this type")
                }
                break
            case T_ADDRESS:             
                if (!left.hasAddress()) {
                    error ("Cannot take address of this type")
                }
                type = new PointerTypeRecord (lefttype)
                break
            case T_LSQUARE:
                if (lefttype.isPointer()) {
                    type = lefttype.dereference()
                } elif (righttype.isPointer()) {
                    type = righttype.dereference()
                } else {
                    error ("Cannot take contents of this type")
                }
                break
            case T_DOT:
            case T_ARROW:
                type = righttype
                break
            case T_LOGAND:
            case T_LOGOR:
            case T_LESS:
            case T_LESSEQ:
            case T_GREATER:
            case T_GREATEREQ:
            case T_EQUAL:
            case T_NOTEQ:
                coercereal()
                type = new ScalarTypeRecord (CINT)
                break
            case T_LSHIFT:
            case T_RSHIFT:
                if (!lefttype.isIntegral() || !righttype.isIntegral()) {
                    error ("Invalid shift")
                } else {
                    type = new ScalarTypeRecord (CINT)
                }
                break
            case T_PLUS:
            case T_MINUS:
                if (lefttype.isPointer() && righttype.isScalar()) {
                    if (righttype.isReal()) {
                        error ("Illegal pointer arithmetic")
                    } else {
                        var size = new IntConst (lefttype.dereference().getSize())
                        right = new Expression (T_STAR, right, size)
                    }
                    type = lefttype
                } elif (righttype.isPointer() && lefttype.isScalar()) {
                    if (lefttype.isReal()) {
                        error ("Illegal pointer arithmetic")
                    } else {
                        var size = new IntConst (righttype.dereference().getSize())
                        left = new Expression (T_STAR, left, size)
                    }
                    type = righttype
                } elif (opcode == T_MINUS && lefttype.isPointer() && righttype.isPointer()) {
                    if (lefttype != righttype) {
                        warning ("Illegal pointer arithmetic")
                    }
                    type = new ScalarTypeRecord (CINT)
                } else {
                    checkScalar()
                    if (!coercereal()) {
                        type = new ScalarTypeRecord (lefttype.getPrimitiveType() | righttype.getPrimitiveType())
                    }
                }
                break

            case T_PLUSPLUS:            // XXX: todo, type checking and scaling
            case T_MINUSMINUS:
            case T_POSTINC:
            case T_POSTDEC:
                type = lefttype
                break
            case T_STAR:
            case T_SLASH:
                checkScalar()
                if (!coercereal()) {
                    type = new ScalarTypeRecord (lefttype.getPrimitiveType() | righttype.getPrimitiveType())
                }
                break
            case T_PERCENT:
                checkScalar()
                if (lefttype.isReal() || righttype.isReal()) {
                    error ("Invalid % operands")
                } else {
                    type = new ScalarTypeRecord (lefttype.getPrimitiveType() | righttype.getPrimitiveType())
                }
                break
            case T_UMINUS:
            case T_UPLUS:
                checkScalar()
                type = lefttype
                break
            case T_TILDE:
                if (!lefttype.isIntegral()) {
                    error ("Integer type expected")
                }
                type = lefttype
                break
            case T_BANG:
                if (!lefttype.isPointer()) {            // pointers are sort of OK
                    checkScalar()
                }
                type = new ScalarTypeRecord (CINT)
                break
            case T_AMPERSAND:
            case T_CARET:
            case T_BITOR:
                if (!lefttype.isIntegral() || !righttype.isIntegral()) {
                    error ("Integer type expected")
                }
                type = lefttype
                break
            case T_ASSIGN:
                if (!left.hasAddress()) {
                    error ("Invalid destination for assignment")
                }
                if (lefttype.isIntegral() && righttype.isReal()) {
                    right = new Expression (T_FIX, right)
                } elif (lefttype.isReal() && righttype.isIntegral()) {
                    right = new Expression (T_FLT, right)
                } elif (lefttype.isPointer()) {
                    if (!righttype.isPointer()) {
                        if (righttype.isScalar()) {
                            warning ("Illegal pointer assignment")
                        } else {
                            error ("Illegal pointer assignment")
                        }
                    } else {
                        var ptrto = lefttype.dereference()
                        if (ptrto.isScalar() &&  ptrto.type != CVOID) {
                            if (lefttype != righttype) {
                                warning ("Illegal pointer assignment")
                            }
                        }
                    }
                } else {
                    if (lefttype != righttype) {
                        warning ("Illegal assignment types")
                    }
                }
                type = lefttype

                break
            case T_QUESTION:
                type = righttype
                break
            case T_COLON:
                if (lefttype != righttype) {
                    warning ("Mismatched types for ?: operator")
                }
                type = lefttype
                break
            case T_COMMA:
                type = righttype
                break
            // XXX: todo
            case T_PLUSEQ:
            case T_MINUSEQ:
            case T_STAREQ:
            case T_SLASHEQ:
            case T_PERCENTEQ:
            case T_ANDEQ:
            case T_OREQ:
            case T_XOREQ:
            case T_LSHIFTEQ:
            case T_RSHIFTEQ:
                type = lefttype
                break
            }

            public function evaluate {
                switch (opcode) {
                case T_CONTENTS:
                case T_ADDRESS:             
                case T_LSQUARE:
                case T_DOT:
                case T_ARROW:
                    throw "Bad operator"
                    break
                case T_LOGAND:
                    var v = left.evaluate()
                    if (v != 0) {
                        return right.evaluate()
                    }
                    return v
                case T_LOGOR:
                    var v = left.evaluate()
                    if (v == 0) {
                        return right.evaluate()
                    }
                    return v
                case T_LESS:
                    return left.evaluate() < right.evaluate()
                case T_LESSEQ:
                    return left.evaluate() <= right.evaluate()
                case T_GREATER:
                    return left.evaluate() > right.evaluate()
                case T_GREATEREQ:
                    return left.evaluate() >= right.evaluate()
                case T_EQUAL:
                    return left.evaluate() == right.evaluate()
                case T_NOTEQ:
                    return left.evaluate() != right.evaluate()
                case T_LSHIFT:
                    return left.evaluate() << right.evaluate()
                case T_RSHIFT:
                    return left.evaluate() >> right.evaluate()
                case T_PLUS:
                    return left.evaluate() + right.evaluate()
                case T_MINUS:
                    return left.evaluate() - right.evaluate()
                case T_STAR:
                    return left.evaluate() * right.evaluate()
                case T_SLASH:
                    return left.evaluate() / right.evaluate()
                case T_PERCENT:
                    return left.evaluate() % right.evaluate()
                case T_UMINUS:
                    return -left.evaluate() 
                case T_UPLUS:
                    return left.evaluate() 
                case T_TILDE:
                    return ~left.evaluate() 
                case T_BANG:
                    return !left.evaluate() 
                case T_AMPERSAND:
                    return left.evaluate() & right.evaluate()
                case T_CARET:
                    return left.evaluate() ^ right.evaluate()
                case T_BITOR:
                    return left.evaluate() | right.evaluate()
                    break
                case T_ASSIGN:
                    throw "Bad operator"
                case T_QUESTION:
                    var v = left.evaluate()
                    if (v != 0) {
                        return right.left.evaluate()
                    } else {
                        return right.left.evaluate()
                    }
                   
                case T_FIX:
                    return cast<int>(left.evaluate())
                case T_FLT:
                    return cast<int>(left.evaluate())
                case T_PLUSEQ:
                case T_MINUSEQ:
                case T_STAREQ:
                case T_SLASHEQ:
                case T_PERCENTEQ:
                case T_ANDEQ:
                case T_OREQ:
                case T_XOREQ:
                case T_LSHIFTEQ:
                case T_RSHIFTEQ:
                    throw "Bad operator"
                }
            }


            public function print (s, indent) {
                switch (opcode) {
                case T_POSTINC:
                case T_POSTDEC:
                    left.print (s,indent)
                    printToken (opcode, s)
                    break
                case T_LSQUARE:
                    left.print (s,indent)
                    '[' -> s
                    right.print (s,indent)
                    ']' -> s
                    break
                default:
                    if (right == null) {
                        printToken (opcode, s)
                        left.print (s,indent)
                    } else {
                        left.print (s,indent)
                        printToken (opcode, s)
                        right.print (s,indent)
                    }
                }
            }

            public function dump (stream) {
                left.dump (stream)
                if (right != null) {
                   right.dump (stream)
                }
                Node.dump (stream)
                [opcode, ' ', left.id] -> stream
                if (right != null) {
                    [' ', right.id] -> stream
                }
            }
        }

        class CastExpression (public left, public casttype) extends Node {
            type = casttype
            
            var lefttype = left.getType()
            if (casttype.isIntegral() && lefttype.isReal()) {
                left = new Expression (T_FIX, left)
            } elif (casttype.isReal() && lefttype.isIntegral()) {
                left = new Expression (T_FLT, left)
            } else {
                if (lefttype.isStruct() && !casttype.isStruct()) {
                    error ("Cannot cast from a struct/union to this type")
                } elif (!lefttype.isStruct() && casttype.isStruct()) {
                    error ("Cannot cast to a struct/union from this type")
                } elif (lefttype.isStruct() && casttype.isStruct()) {
                    if (lefttype != casttype) {
                        error ("Mismatched struct/union cast")
                    }
                }
            }

            public function print (s, indent) {
                '(' -> s
                casttype.print (s, indent)
                ')' -> s
                left.print (s,indent)
            }

            public function evaluate {
                return left.evaluate()
            }

            public function dump (stream) {
                left.dump (stream)
                Node.dump (stream)
                left.id -> stream
            }
        }

        class CallExpression (public left, public arglist) extends Node {
            var argassignments = null           // assignments to arguments
            var value = null                    // return value

            if (left instanceof Identifier) {
                currentFunction.addDependency (left.getSymbol())
            }

            var lefttype = left.getType()

            if (lefttype.isFunction()) {
                type = lefttype.dereference()
            } elif (lefttype.isPointer()) {
                var f = lefttype.dereference()
                if (f.isFunction()) {
                    type = f.dereference()
                } else {
                    error ("Cannot call this type")
                }
            } else {
                error ("Cannot call this type")
            }
            if (type != null) {
                var proto = lefttype.getPrototype()
                var n = sizeof (proto)

                // varargs functions have an extra __builtin_va_alist argument that doesn't
                // count

                if (lefttype.isVarArgs()) {
                    n--
                }

                if (sizeof (arglist) != n) {
                    if (!lefttype.isVarArgs() || sizeof (arglist) < n) {
                        error ("Incorrect number of arguments for call")
                        if (sizeof (arglist) < n) {
                            n = sizeof (arglist)
                        }
                    }
                }
                for (var i = 0 ; i < n ; i++) {
                    arglist[i] = new CastExpression (arglist[i], proto[i].getType())
                }
            }

            public function print (s, indent) {
                left.print (s, indent)
                '(' -> s
                var comma = false
                foreach arg arglist {
                   if (comma) {
                       ',' -> s
                   }
                   comma = true
                   arg.print (s, 0)
                }
                ')' -> s
            }

            public function dump (stream) {
                Node.dump (stream)
            }
        }


        const FLAG_LPAREN_FOUND = 0b1

        function assignmentExpression...
        function expression...

        function singleExpression {
            return assignmentExpression()
        }

        function primaryExpression {
            if (flags & FLAG_LPAREN_FOUND || lex.match (T_LPAREN)) {
                flags &= ~FLAG_LPAREN_FOUND
                var result = expression()
                needbrack (T_RPAREN)
                return result
            } elif (lex.currentToken == IDENTIFIER) {
                var name = lex.spelling
                lex.nextToken()
                var symbol = findSymbol (name)
                if (symbol == null) {
                    if (lex.currentToken == T_LPAREN) {         // function call
                        var builtinvalist = new Symbol ("__builtin_va_alist")
                        builtinvalist.setType (new ArrayTypeRecord (0, new PointerTypeRecord (new ScalarTypeRecord (CVOID))))
                        var frec = new FunctionTypeRecord ([builtinvalist], true, new ScalarTypeRecord (CINT))
                        symbol = new Symbol (name)
                        symbol.setType (frec)
                        symbol.predefine()
                        insertGlobalSymbol (symbol)
                    } else {
                        error ("Undefined symbol " + name)
                        symbol = new Symbol (name)
                        symbol.setType (new ScalarTypeRecord (CINT))
                        insertSymbol (symbol)
                    }
                }
                return new Identifier (symbol)
            } elif (lex.currentToken == NUMBER) {
                var val = lex.number
                lex.nextToken()
                return new IntConst (val)
            } elif (lex.currentToken == CHAR) {
                var val = lex.number
                lex.nextToken()
                return new CharConst (val)
            } elif (lex.currentToken == STRING) {
                var val = lex.spelling
                lex.nextToken()
                while (lex.currentToken == STRING) {
                   val += lex.spelling
                   lex.nextToken()
                }
                return new StringConst (val, false)            
            } elif (lex.currentToken == LONGSTRING) {
                var val = lex.spelling
                lex.nextToken()
                while (lex.currentToken == LONGSTRING) {
                   val += lex.spelling
                   lex.nextToken()
                }
                return new StringConst (val, true)            
            } elif (lex.currentToken == FNUMBER) {
                var val = lex.number
                lex.nextToken()
                return new RealConst (val)
            } else {                                    // XXX: real numbers
                error ("Expression expected")
            }
        }

        function postfixExpression {
            var result = primaryExpression()
            for (;;) {
                if (lex.match (T_LPAREN)) {
                    var arglist = []
                    while (lex.currentToken != T_RPAREN) {
                        var arg = singleExpression()
                        append (arglist, arg)
                        if (!lex.match (T_COMMA)) {
                            break
                        }
                    }
                    needbrack (T_RPAREN)
                    result = new CallExpression (result, arglist)
                } elif (lex.match (T_LSQUARE)) {
                    var index = expression()
                    needbrack (T_RSQUARE)
                    result = new Expression (T_LSQUARE, result, index)
                } elif (lex.match (T_DOT)) {
                    if (!result.getType().isStruct()) {
                        error ("struct/union required for . operator")
                    } else {
                        if (lex.currentToken != IDENTIFIER) {
                            error ("struct/union member name expected")
                        } else {
                            var membername = lex.spelling
                            lex.nextToken()
                            var s = result.getType() 
                            var member = s.findMember (membername)
                            if (member == null) {
                                error (membername + " is not a member of struct/union")
                            } else {
                                result = new Expression (T_DOT, result, new Identifier (member))
                            }
                        }
                    }
                } elif (lex.match (T_ARROW)) {
                    var s = result.getType() 
                    if (!s.isPointer() || !s.dereference().isStruct()) {
                        error ("struct/union pointer required for -> operator")
                    } else {
                        if (lex.currentToken != IDENTIFIER) {
                            error ("struct/union member name expected")
                        } else {
                            var membername = lex.spelling
                            lex.nextToken()
                            s = s.dereference()
                            var member = s.findMember (membername)
                            if (member == null) {
                                error (membername + " is not a member of struct/union")
                            } else {
                                result = new Expression (T_ARROW, result, new Identifier (member))
                            }
                        }
                    }
                } elif (lex.match (T_PLUSPLUS)) {
                    result = new Expression (T_POSTINC, result)
                } elif (lex.match (T_MINUSMINUS)) {
                    result = new Expression (T_POSTDEC, result)
                } else {
                    return result
                }
            }
        }

        function castExpression...

        function unaryExpression {
            if (flags & FLAG_LPAREN_FOUND) {            // open paren found
                return postfixExpression()
            }
            if (lex.match (T_PLUSPLUS)) {
                var left = unaryExpression()
                return new Expression (T_PLUSPLUS, left)
            } elif (lex.match (T_MINUSMINUS)) {
                var left = unaryExpression()
                return new Expression (T_MINUSMINUS, left)
            } elif (lex.match (T_MINUS)) {
                var left = castExpression()
                return new Expression (T_UMINUS, left)
            } elif (lex.match (T_PLUS)) {
                var left = castExpression()
                return new Expression (T_UPLUS, left)
            } elif (lex.match (T_STAR)) {
                var left = castExpression()
                return new Expression (T_CONTENTS, left)
            } elif (lex.match (T_AMPERSAND)) {
                var left = castExpression()
                return new Expression (T_ADDRESS, left)
            } elif (lex.match (T_BANG)) {
                var left = castExpression()
                return new Expression (T_BANG, left)
            } elif (lex.match (T_TILDE)) {
                var left = castExpression()
                return new Expression (T_TILDE, left)
            } elif (lex.match (T_SIZEOF)) {
                if (lex.match (T_LPAREN)) {
                    var decl = declaration(0, type())
                    if (decl != null) {
                        needbrack (T_RPAREN)
                        return new IntConst (decl.getType().getSize())
                    } else {
                        flags |= FLAG_LPAREN_FOUND
                        left = unaryExpression()
                        return new IntConst (left.getType().getSize())
                    }
                } else {
                    left = unaryExpression()
                    return new IntConst (left.getType().getSize())
                }
            } else {                                  
                return postfixExpression()
            }
             
        }

        function castExpression {
            if (lex.match (T_LPAREN)) {
                var decl = declaration(0, type())
                if (decl != null) {
                    needbrack (T_RPAREN)
                    var left = unaryExpression()
                    return new CastExpression (left, decl.getType()) 
                } else {
                    flags |= FLAG_LPAREN_FOUND
                    return unaryExpression()
                }
            } else {
                return unaryExpression()
            }
        }

        function multiplicationExpression {
            var result = castExpression()
            for (;;) {
                if (lex.match (T_STAR)) {
                    var right = castExpression()
                    result = new Expression (T_STAR, result, right)
                } elif (lex.match (T_SLASH)) {
                    var right = castExpression()
                    result = new Expression (T_SLASH, result, right)
                } elif (lex.match (T_PERCENT)) {
                    var right = castExpression()
                    result = new Expression (T_PERCENT, result, right)
                } else {
                    return result
                }
            }
        }

        function additionExpression {
            var result = multiplicationExpression()
            for (;;) {
                if (lex.match (T_PLUS)) {
                    var right = multiplicationExpression()
                    result = new Expression (T_PLUS, result, right)
                } elif (lex.match (T_MINUS)) {
                    var right = multiplicationExpression()
                    result = new Expression (T_MINUS, result, right)
                } else {
                    return result
                }
            }
        }

        function shiftExpression {
            var result = additionExpression()
            for (;;) {
                if (lex.match (T_LSHIFT)) {
                    var right = additionExpression()
                    result = new Expression (T_LSHIFT, result, right)
                } elif (lex.match (T_RSHIFT)) {
                    var right = additionExpression()
                    result = new Expression (T_RSHIFT, result, right)
                } else {
                    return result
                }
            }
        }

        function comparisonExpression {
            var result = shiftExpression()
            for (;;) {
                if (lex.match (T_LESS)) {
                    var right = shiftExpression()
                    result = new Expression (T_LESS, result, right)
                } elif (lex.match (T_GREATER)) {
                    var right = shiftExpression()
                    result = new Expression (T_GREATER, result, right)
                } elif (lex.match (T_LESSEQ)) {
                    var right = shiftExpression()
                    result = new Expression (T_LESSEQ, result, right)
                } elif (lex.match (T_GREATEREQ)) {
                    var right = shiftExpression()
                    result = new Expression (T_GREATEREQ, result, right)
                } else {
                    return result
                }
            }
        }

        function equalityExpression {
            var result = comparisonExpression()
            for (;;) {
                if (lex.match (T_EQUAL)) {
                    var right = comparisonExpression()
                    result = new Expression (T_EQUAL, result, right)
                } elif (lex.match (T_NOTEQ)) {
                    var right = comparisonExpression()
                    result = new Expression (T_NOTEQ, result, right)
                } else {
                    return result
                }
            }
        }

        function bitwiseAndExpression {
            var result = equalityExpression()
            while (lex.match (T_AMPERSAND)) {
                var right = equalityExpression()
                result = new Expression (T_AMPERSAND, result, right)
            }
            return result
        }

        function bitwiseXorExpression {
            var result = bitwiseAndExpression()
            while (lex.match (T_CARET)) {
                var right = bitwiseAndExpression()
                result = new Expression (T_CARET, result, right)
            }
            return result
        }

        function bitwiseOrExpression {
            var result = bitwiseXorExpression()
            while (lex.match (T_BITOR)) {
                var right = bitwiseXorExpression()
                result = new Expression (T_BITOR, result, right)
            }
            return result
        }

 
        function logicalAndExpression {
            var result = bitwiseOrExpression()
            while (lex.match (T_LOGAND)) {
                var right = bitwiseOrExpression()
                result = new Expression (T_LOGAND, result, right)
            }
            return result
        }

        function logicalOrExpression {
            var result = logicalAndExpression()
            while (lex.match (T_LOGOR)) {
                var right = logicalAndExpression()
                result = new Expression (T_LOGOR, result, right)
            }
            return result
        }

        function conditionalExpression {
            var result = logicalOrExpression()
            if (lex.match (T_QUESTION)) {
                var right = singleExpression()
                result = new Expression (T_QUESTION, result, right)
                if (lex.match (T_COLON)) {
                    var right = conditionalExpression()
                    result = new Expression (T_COLON, result, right)
                } else {
                    error ("Missing : for conditional expression")
                }
            }
            return result
        }

        function assignmentExpression {
            var right = null
            var result = conditionalExpression()
            switch (lex.currentToken) {
            case T_ASSIGN:
            case T_PLUSEQ:
            case T_MINUSEQ:
            case T_STAREQ:
            case T_SLASHEQ:
            case T_PERCENTEQ:
            case T_LSHIFTEQ:
            case T_RSHIFTEQ:
            case T_ANDEQ:
            case T_OREQ:
            case T_XOREQ: 
                var tok = lex.currentToken
                lex.nextToken()
                right = assignmentExpression()
                result = new Expression (tok, result, right)
            }
            return result
        }
    
        function expression {
            var result = assignmentExpression()
            while (lex.match (T_COMMA)) {
                var right = assignmentExpression()
                result = new Expression (T_COMMA, result, right)
            }
            return result
        }

        function constantExpression {
            try {
                var tree = conditionalExpression()
                return tree.evaluate()
            } catch (e) {
                error ("Unable to evaluate constant expression: " + e)
                return 0
            }
        }

        //
        // initialization expressions
        //

        function staticInitializer (type, needbraces = true) {
            if (type.isStruct()) {
                var braces = false
                if (lex.match (T_LBRACE)) {
                    braces = true
                } elif (needbraces) {
                    error ("'{' expected for struct/union initializer")
                }
                var n = type.numMembers()
                var numinits = 0
                var i = 0
                while (numinits < n) {
                    if (lex.currentToken == T_RBRACE) {
                        break
                    }
                    var mem = null
                    if (lex.match (T_DOT)) {
                        var name = lex.getIdentifier()
                        if (!lex.match (T_ASSIGN)) {
                            error ("Missing = in initialization designator")
                        }
                        var num = type.getMemberNumber(name)
                        if (num < 0) {
                            error ("No such struct/union member " + name)
                            num = 0
                        } 
                        mem = type.getMember (num)
                        i = num + 1
                    } else {
                        mem = type.getMember (i)        // symbol
                        i++
                    }
                    if (mem.getDetails() != null) {
                        error ("struct/union member " + mem.getName() + " has already been initialized")
                    }
                    var memtype = mem.getType()
                    var v = staticInitializer (memtype, false)
                    mem.setDetails (v)
                    numinits++
                    if (numinits == n) {                // want to keep the comma if we have reached the end
                        break
                    }
                    if (!lex.match (T_COMMA)) {
                        break
                    }
                }
                if (braces) {
                    needbrack (T_RBRACE)
                }
                return new StructConst (type)
            } elif (type.isArray()) {
                if (lex.currentToken == STRING) {
                    var str = lex.spelling
                    lex.nextToken()
                    if (type.dereference().getPrimitiveType() & CCHAR) {
                        var size = type.arraysize
                        if (size == 0) {
                           type.arraysize = sizeof (str)
                        } elif (sizeof (str) > size) {
                            error ("String literal contains too many chars for array")
                        }
                    } else {
                        error ("Cannot use a string literal to initialize this array")
                    }
                    return new ArrayConst (type, str)
                } else {
                    var braces = false
                    if (lex.match (T_LBRACE)) {
                        braces = true
                    } elif (needbraces) {
                        error ("'{' expected for array initializer")
                    }
                    var vals = []
                    var subtype = type.dereference()
                    var n = type.arraysize
                    if (n == 0) {
                        n = 0x7fffffff                  // something pretty large
                    }
                    int numinits = 0
                    while (numinits < n) {
                        if (lex.currentToken == T_RBRACE) {
                            break
                        }
                        var v = staticInitializer (clone (subtype, true), false)
                        append (vals, v)
                        numinits++
                        if (numinits == n) {                // want to keep the comma if we have reached the end
                            break
                        }
                        if (!lex.match (T_COMMA)) {
                            break
                        }
                    }
                    var size = type.arraysize
                    if (size == 0) {
                       type.arraysize = sizeof (vals)
                    }
                    if (braces) {
                        needbrack (T_RBRACE)
                    }
                    return new ArrayConst (type, vals)
                }
            } elif (type.isPointer()) {
                var v = singleExpression()
                if (typeof (v) == "none") {
                    error ("bad expression")
                }
                var vt = v.getType()
                if (!vt.isPointer()) {
                    if (!v.isConst0()) {
                        error ("Illegal pointer initialization")
                    }
                }
                return v
            } else {                            // scalar type
                return singleExpression()
            }
        }
    }

}