Do not print empty FalseBlock

Do not print empty FalseBlock

namespace BooJs.Compiler.Visitors

import Boo.Lang.Environments
import Boo.Lang.Compiler.Ast
import Boo.Lang.Compiler.TypeSystem
import Boo.Lang.PatternMatching

import BooJs.Compiler.Utils
import BooJs.Compiler.TypeSystem(RuntimeMethodCache)
import BooJs.Compiler.Mozilla as Moz

class MozillaAstVisitor(FastDepthFirstVisitor):
"""
Transforms a Boo AST into a Mozilla AST
"""
    # Holds the latest conversion from Boo node to a Moz node
    _return as Moz.Node

    protected def Return(node as Moz.Node):
        _return = node

    protected def Apply(node as Node) as Moz.Node:
        _return = null
        Visit node
        return _return

    protected def loc(node as Node):
        obj = Moz.SourceLocation()
        obj.source = node.LexicalInfo.FileName
        obj.start = Moz.Position(line: node.LexicalInfo.Line, column: node.LexicalInfo.Column-1)

        # Compute an approximate end position if not available
        if node.EndSourceLocation and node.EndSourceLocation.Line >= 0:
            line, column = (node.EndSourceLocation.Line, node.EndSourceLocation.Column-1)
        else:
            parts = node.ToCodeString().Split(char('\n'))
            line, column = (node.LexicalInfo.Line, node.LexicalInfo.Column-1)
            if len(parts) > 1:
                line += len(parts) - 1
                column = 0
            column += len(parts[-1])

        obj.end = Moz.Position(line: line, column: column)

        return obj

    def Run(node as Node) as Moz.Node:
        Visit node
        return _return

    def OnBoolLiteralExpression(node as BoolLiteralExpression):
        Return Moz.Literal(loc: loc(node), value: node.Value)

    def OnNullLiteralExpression(node as NullLiteralExpression):
        Return Moz.Literal(loc: loc(node), value: null)

    def OnIntegerLiteralExpression(node as IntegerLiteralExpression):
        n as Moz.IExpression
        if node.Value < 0:
            n = Moz.Literal(loc: loc(node), value: -node.Value)
            n = Moz.UnaryExpression(loc: loc(node), operator: '-', argument: n)
         else:
            n = Moz.Literal(loc: loc(node), value: node.Value)

        Return n

    def OnDoubleLiteralExpression(node as DoubleLiteralExpression):
        n as Moz.IExpression
        if node.Value < 0:
            n = Moz.Literal(loc: loc(node), value: -node.Value)
            n = Moz.UnaryExpression(loc: loc(node), operator: '-', argument: n)
         else:
            n = Moz.Literal(loc: loc(node), value: node.Value)

        Return n

    def OnStringLiteralExpression(node as StringLiteralExpression):
        Return Moz.Literal(loc: loc(node), value: node.Value)

    def OnRELiteralExpression(node as RELiteralExpression):
        Return Moz.Literal(loc: loc(node), value: node.Value, regexp: true)

    def OnSelfLiteralExpression(node as SelfLiteralExpression):
        Return Moz.ThisExpression(loc: loc(node))

    def OnListLiteralExpression(node as ListLiteralExpression):
        n = Moz.ArrayExpression(loc: loc(node))
        for itm in node.Items:
            exp = Apply(itm)
            n.elements.Add(exp)
        Return n

    def OnHashLiteralExpression(node as HashLiteralExpression):
        n = Moz.ObjectExpression(loc: loc(node))
        for pair in node.Items:
            prop = Moz.ObjectExpressionProp(key: Apply(pair.First), value: Apply(pair.Second))
            n.properties.Add(prop)
        Return n

    def OnEnumDefinition(node as EnumDefinition):
        o = Moz.ObjectExpression(loc: loc(node))
        for member as EnumMember in node.Members:
            assert member.Initializer != null, 'Enum definition without an initializer value!'
            prop = Moz.ObjectExpressionProp(key: Moz.Literal(value: member.Name), value: Apply(member.Initializer))
            o.properties.Add(prop)

        # Assign the object to a variable
        v = Moz.VariableDeclarator(loc: loc(node))
        v.id = Moz.Identifier(loc: loc(node), name: node.FullName)
        v.init = o
        n = Moz.VariableDeclaration(loc: loc(node), kind: 'var')
        n.declarations.Add(v)

        Return n

    def OnModule(node as Module):
        n = Moz.Program(loc: loc(node))

        st as Moz.IStatement
        for member in node.Members:
            st = Apply(member)
            if st is null:
                continue

            if st isa Moz.BlockStatement:
                n.body += (st as Moz.BlockStatement).body
            else:
                n.body.Add(st)

        for global in node.Globals.Statements:
            st = Apply(global)
            n.body.Add(st)

        n['module'] = node

        Return n

    def OnClassDefinition(node as ClassDefinition):
        n = Moz.BlockStatement()

        st as Moz.IStatement
        for member in node.Members:
            st = Apply(member)
            if st is null:
                continue
            elif st isa Moz.BlockStatement:
                n.body += (st as Moz.BlockStatement).body
            else:
                n.body.Add(st)

        Return n

    def OnField(node as Field):
        pass

    def OnConstructor(node as Constructor):
        pass

    def OnMethod(node as Method):
        n = Moz.FunctionDeclaration(loc: loc(node))
        n.id = Moz.Identifier(loc: loc(node), name: node.Name)
        for param in node.Parameters:
            p = Moz.Identifier(loc: loc(param), name: param.Name)
            n.params.Add(p)

        b = Moz.BlockStatement()
        for local in node.Locals:
            st = Apply(local)
            b.body.Add(st) if st

        b.body += (Apply(node.Body) as Moz.BlockStatement).body
        n.body = b
        Return n

    def OnDeclarationStatement(node as DeclarationStatement):
        # TODO: Generate type annotated comments
        v = Moz.VariableDeclarator(loc: loc(node))
        v.id = Moz.Identifier(name: node.Declaration.Name)
        v.init = Apply(node.Initializer)

        vd = Moz.VariableDeclaration(loc: loc(node), kind: 'var')
        vd.declarations.Add(v)
        Return vd

    def OnBlock(node as Block):
        b = Moz.BlockStatement(loc: loc(node))
        for st in node.Statements:
            mst = Apply(st) as Moz.IStatement
            if mst:
                b.body.Add(mst)

        Return b

    def OnExpressionStatement(node as ExpressionStatement):
        n = Moz.ExpressionStatement(loc: loc(node))
        n.expression = Apply(node.Expression)
        Return n

    def OnReturnStatement(node as ReturnStatement):
        n = Moz.ReturnStatement(loc: loc(node))
        n.argument = Apply(node.Expression)
        Return n

    def OnIfStatement(node as IfStatement):

        ifst = Moz.IfStatement(loc: loc(node))
        ifst.test = Apply(node.Condition)
        ifst.consequent = Apply(node.TrueBlock)
        if node.FalseBlock and not node.FalseBlock.IsEmpty:
            blk = node.FalseBlock
            if len(blk.Statements) == 1 and blk.FirstStatement isa IfStatement:
                ifst.alternate = Apply(blk.FirstStatement)
            else:
                ifst.alternate = Apply(blk)

        Return ifst

    def OnForStatement(node as ForStatement):
        # Only for statements over range should have survived
        mie = node.Iterator as MethodInvocationExpression
        if len(mie.Arguments) == 1:
            start = Moz.Literal(0)
            length = Apply(mie.Arguments[0])
        else:
            start = Apply(mie.Arguments[0])
            length = Apply(mie.Arguments[1])

        if node.ContainsAnnotation('loop-index'):
            index = Moz.Identifier(node['loop-index'])
        else:
            index = Moz.Identifier(node.Declarations[0].Name)

        fst = Moz.ForStatement(loc: loc(node))
        fst.init = Moz.AssignmentExpression(
            left: index,
            operator: '=',
            right: start
        )
        fst.test = Moz.BinaryExpression(
            left: index,
            operator: '<',
            right: length
        )
        fst.update = Moz.UpdateExpression(
            argument: index,
            operator: '++',
            prefix: false
        )

        fst.body = Apply(node.Block)
        Return fst

    def OnWhileStatement(node as WhileStatement):
        wst = Moz.WhileStatement(loc: loc(node))
        wst.test = Apply(node.Condition)
        wst.body = Apply(node.Block)

        Return wst

    def OnTryStatement(node as TryStatement):

        n = Moz.TryStatement(loc: loc(node))
        n.block = Apply(node.ProtectedBlock)

        assert len(node.ExceptionHandlers) <= 1, 'Multiple exceptions handlers should be processed in previous steps'
        if len(node.ExceptionHandlers):
            hdl = node.ExceptionHandlers[0]
            h = Moz.CatchClause(loc: loc(hdl))
            h.param = Moz.Identifier(loc: loc(hdl.Declaration), name: hdl.Declaration.Name)
            h.body = Apply(hdl.Block)
            n.handlers.Add(h)

        if node.EnsureBlock:
            n.finalizer = Apply(node.EnsureBlock)

        Return n

    def OnBreakStatement(node as BreakStatement):
        Return Moz.BreakStatement(loc: loc(node))

    def OnContinueStatement(node as ContinueStatement):
        Return Moz.ContinueStatement(loc: loc(node))

    def OnLabelStatement(node as LabelStatement):
        n = Moz.LabeledStatement(loc: loc(node))
        n.label = Moz.Identifier(loc: loc(node), name: node.Name)

        # In Mozilla AST labels are always associated with a statement.
        stmts = (node.ParentNode as Block).Statements
        idx = stmts.IndexOf(node)
        st = stmts[idx + 1]
        stmts.RemoveAt(idx + 1)

        if st.NodeType not in (NodeType.ForStatement, NodeType.WhileStatement):
            raise 'Javascript only allows label on looping statements'
        n.body = Apply(st)

        Return n

    def OnGotoStatement(node as GotoStatement):
        n = Moz.ContinueStatement(loc: loc(node))
        n.label = Moz.Identifier(loc: loc(node.Label), name: node.Label.Name)
        Return n

    def OnRaiseStatement(node as RaiseStatement):
        # TODO: Move this to clean step
        /*
        if false and Context.Parameters.Debug:
            n = Moz.CallExpression(loc: loc(node))
            n.callee = Moz.MemberExpression(
                loc: loc(node),
                object: Moz.Identifier(name: 'Boo'),
                property: Moz.Identifier(name: 'raise')
            )
            # TODO: We should make sure it's a constructor
            if node.Exception isa MethodInvocationExpression:
                c = Moz.NewExpression(loc: loc(node.Exception))
                c._constructor = Apply(node.Exception)
                n.arguments.Add(c)
            else:
                n.arguments.Add(Apply(node.Exception))

            lex = node.Exception.LexicalInfo
            n.arguments.Add(Moz.Literal(value: lex.FileName))
            n.arguments.Add(Moz.Literal(value: lex.Line))

            Return Moz.ExpressionStatement(loc: loc(node), expression: n)
        else:
        */
        t = Moz.ThrowStatement(loc: loc(node))
        t.argument = Apply(node.Exception)
        # TODO: We should make sure it's a constructor
        /*
        if false and node.Exception isa MethodInvocationExpression:
            c = Moz.NewExpression(loc: loc(node.Exception))
            c._constructor = Apply(node.Exception)
            t.argument = c
        else:
        */

        Return t

    def OnReferenceExpression(node as ReferenceExpression):
        Return Moz.Identifier(loc: loc(node), name: node.Name)

    def OnMemberReferenceExpression(node as MemberReferenceExpression):
        n = Moz.MemberExpression(loc: loc(node), property: Moz.Identifier(loc: loc(node), name: node.Name))
        n.object = Apply(node.Target)
        Return n

    def OnConditionalExpression(node as ConditionalExpression):
    """ Convert to the ternary operator.
            (10 if true else 20)  -->  true ? 10 : 20
    """
        c = Moz.ConditionalExpression(loc: loc(node))
        c.test = Apply(node.Condition)
        c.consequent = Apply(node.TrueValue)
        c.alternate = Apply(node.FalseValue)
        Return c

    def OnSlicingExpression(node as SlicingExpression):
        n = Moz.MemberExpression(
            loc: loc(node),
            object: Apply(node.Target),
            property: Apply(node.Indices[0].Begin),
            computed: true
        )

        Return n

    def OnMacroStatement(node as MacroStatement):
        # Special handling for switch/case constructs (used in generators)
        if node.Name == 'switch':
            switch = Moz.SwitchStatement(loc: loc(node))
            switch.discriminant = Apply(node.Arguments[0])
            for casemacro as MacroStatement in node.Body.Statements:
                assert casemacro isa MacroStatement

                case = Moz.SwitchCase(loc: loc(casemacro))
                case.test = Apply(casemacro.Arguments[0])
                for st in casemacro.Body.Statements:
                    case.consequent.Add(Apply(st))

                switch.cases.Add(case)

            Return switch
        else:
            raise 'Macro statements should have been already resolved'

    def OnMethodInvocationExpression(node as MethodInvocationExpression):
        # Detect constructors

        if node.Target.Entity isa IConstructor and not isFactory(node.Target):
            c = Moz.NewExpression(loc: loc(node))
            c._constructor = Apply(node.Target)
            for arg in node.Arguments:
                c.arguments.Add(Apply(arg))
            Return c
        # Detect sequences
        elif node.Target.ToString() == '@':
            s = Moz.SequenceExpression(loc: loc(node))
            for arg in node.Arguments:
                s.expressions.Add(Apply(arg))
            Return s

        else:
            n = Moz.CallExpression(loc: loc(node))
            n.callee = Apply(node.Target)
            for arg in node.Arguments:
                n.arguments.Add(Apply(arg))

            # Detect calls to eval() with a simple string argument to define a
            # verbatim version of the expression.
            if node.Target.Entity is my(RuntimeMethodCache).Eval and \
               len(node.Arguments) == 1 and \
               node.Arguments[0].NodeType == NodeType.StringLiteralExpression:
                n.verbatim = (node.Arguments[0] as StringLiteralExpression).Value

            Return n

    def OnBlockExpression(node as BlockExpression):
        n = Moz.FunctionExpression(loc: loc(node))
        for param in node.Parameters:
            p = Moz.Identifier(loc: loc(param), name: param.Name)
            n.params.Add(p)

        n.body = Apply(node.Body)
        Return n

    def OnBinaryExpression(node as BinaryExpression):
        n = Moz.BinaryExpression(loc: loc(node))
        match node.Operator:
            case BinaryOperatorType.ReferenceEquality:
                if NodeType.NullLiteralExpression in (node.Left.NodeType, node.Right.NodeType):
                    Return(ProcessIsNull(node))
                    return

                n.operator = '==='
            case BinaryOperatorType.ReferenceInequality:
                if NodeType.NullLiteralExpression in (node.Left.NodeType, node.Right.NodeType):
                    ue = Moz.UnaryExpression(loc: loc(node), operator: '!')
                    ue.argument = ProcessIsNull(node)
                    Return ue
                    return

                n.operator = '!=='

            case BinaryOperatorType.Equality:
                n.operator = '=='
            case BinaryOperatorType.Inequality:
                n.operator = '!='

            case BinaryOperatorType.Member:
                n.operator = 'in'
            case BinaryOperatorType.Addition:
                n.operator = '+'
            case BinaryOperatorType.Subtraction:
                n.operator = '-'
            case BinaryOperatorType.Multiply:
                n.operator = '*'
            case BinaryOperatorType.Division:
                n.operator = '/'
            case BinaryOperatorType.Modulus:
                n.operator = '%'
            case BinaryOperatorType.BitwiseAnd:
                n.operator = '&'
            case BinaryOperatorType.BitwiseOr:
                n.operator = '|'
            case BinaryOperatorType.ExclusiveOr:
                n.operator = '^'
            case BinaryOperatorType.GreaterThan:
                n.operator = '>'
            case BinaryOperatorType.GreaterThanOrEqual:
                n.operator = '>='
            case BinaryOperatorType.LessThan:
                n.operator = '<'
            case BinaryOperatorType.LessThanOrEqual:
                n.operator = '<='
            case BinaryOperatorType.ShiftLeft:
                n.operator = '<<'
            case BinaryOperatorType.ShiftRight:
                n.operator = '>>'

            case BinaryOperatorType.Assign:
                n = Moz.AssignmentExpression(loc: loc(node), operator: '=')
            case BinaryOperatorType.InPlaceBitwiseAnd:
                n = Moz.AssignmentExpression(loc: loc(node), operator: '&=')
            case BinaryOperatorType.InPlaceBitwiseOr:
                n = Moz.AssignmentExpression(loc: loc(node), operator: '|=')
            case BinaryOperatorType.InPlaceExclusiveOr:
                n = Moz.AssignmentExpression(loc: loc(node), operator: '^=')

            case BinaryOperatorType.And:
                n = Moz.LogicalExpression(loc: loc(node), operator: '&&')
            case BinaryOperatorType.Or:
                n = Moz.LogicalExpression(loc: loc(node), operator: '||')

            otherwise:
                raise 'Operator not supported ' + node.Operator

        n.left = Apply(node.Left)
        n.right = Apply(node.Right)
        Return n

    def OnUnaryExpression(node as UnaryExpression):
        match node.Operator:
            case UnaryOperatorType.UnaryNegation:
                n = Moz.UnaryExpression(loc: loc(node), operator: '-')
            case UnaryOperatorType.LogicalNot:
                n = Moz.UnaryExpression(loc: loc(node), operator: '!')
            case UnaryOperatorType.OnesComplement:
                n = Moz.UnaryExpression(loc: loc(node), operator: '~')
            case UnaryOperatorType.Increment:
                n = Moz.UpdateExpression(loc: loc(node), operator: '++', prefix: true)
            case UnaryOperatorType.Decrement:
                n = Moz.UpdateExpression(loc: loc(node), operator: '--', prefix: true)
            case UnaryOperatorType.PostIncrement:
                n = Moz.UpdateExpression(loc: loc(node), operator: '++', prefix: false)
            case UnaryOperatorType.PostDecrement:
                n = Moz.UpdateExpression(loc: loc(node), operator: '--', prefix: false)
            otherwise:
                raise 'Operator not supported ' + node.Operator

        n.argument = Apply(node.Operand)
        Return n

    protected def ProcessIsNull(node as BinaryExpression) as Moz.IExpression:
        itm = (node.Right if node.Left.NodeType == NodeType.NullLiteralExpression else node.Left)
        call = Moz.CallExpression(loc: loc(node))
        call.callee = Moz.Identifier('Boo.isNull')
        call.arguments.Add(Apply(itm))
        return call

namespace BooJs.Compiler.Visitors

import Boo.Lang.Environments
import Boo.Lang.Compiler.Ast
import Boo.Lang.Compiler.TypeSystem
import Boo.Lang.PatternMatching

import BooJs.Compiler.Utils
import BooJs.Compiler.TypeSystem(RuntimeMethodCache)
import BooJs.Compiler.Mozilla as Moz

class MozillaAstVisitor(FastDepthFirstVisitor):
"""
Transforms a Boo AST into a Mozilla AST
"""
    # Holds the latest conversion from Boo node to a Moz node
    _return as Moz.Node

    protected def Return(node as Moz.Node):
        _return = node

    protected def Apply(node as Node) as Moz.Node:
        _return = null
        Visit node
        return _return

    protected def loc(node as Node):
        obj = Moz.SourceLocation()
        obj.source = node.LexicalInfo.FileName
        obj.start = Moz.Position(line: node.LexicalInfo.Line, column: node.LexicalInfo.Column-1)

        # Compute an approximate end position if not available
        if node.EndSourceLocation and node.EndSourceLocation.Line >= 0:
            line, column = (node.EndSourceLocation.Line, node.EndSourceLocation.Column-1)
        else:
            parts = node.ToCodeString().Split(char('\n'))
            line, column = (node.LexicalInfo.Line, node.LexicalInfo.Column-1)
            if len(parts) > 1:
                line += len(parts) - 1
                column = 0
            column += len(parts[-1])

        obj.end = Moz.Position(line: line, column: column)

        return obj

    def Run(node as Node) as Moz.Node:
        Visit node
        return _return

    def OnBoolLiteralExpression(node as BoolLiteralExpression):
        Return Moz.Literal(loc: loc(node), value: node.Value)

    def OnNullLiteralExpression(node as NullLiteralExpression):
        Return Moz.Literal(loc: loc(node), value: null)

    def OnIntegerLiteralExpression(node as IntegerLiteralExpression):
        n as Moz.IExpression
        if node.Value < 0:
            n = Moz.Literal(loc: loc(node), value: -node.Value)
            n = Moz.UnaryExpression(loc: loc(node), operator: '-', argument: n)
         else:
            n = Moz.Literal(loc: loc(node), value: node.Value)

        Return n

    def OnDoubleLiteralExpression(node as DoubleLiteralExpression):
        n as Moz.IExpression
        if node.Value < 0:
            n = Moz.Literal(loc: loc(node), value: -node.Value)
            n = Moz.UnaryExpression(loc: loc(node), operator: '-', argument: n)
         else:
            n = Moz.Literal(loc: loc(node), value: node.Value)

        Return n

    def OnStringLiteralExpression(node as StringLiteralExpression):
        Return Moz.Literal(loc: loc(node), value: node.Value)

    def OnRELiteralExpression(node as RELiteralExpression):
        Return Moz.Literal(loc: loc(node), value: node.Value, regexp: true)

    def OnSelfLiteralExpression(node as SelfLiteralExpression):
        Return Moz.ThisExpression(loc: loc(node))

    def OnListLiteralExpression(node as ListLiteralExpression):
        n = Moz.ArrayExpression(loc: loc(node))
        for itm in node.Items:
            exp = Apply(itm)
            n.elements.Add(exp)
        Return n

    def OnHashLiteralExpression(node as HashLiteralExpression):
        n = Moz.ObjectExpression(loc: loc(node))
        for pair in node.Items:
            prop = Moz.ObjectExpressionProp(key: Apply(pair.First), value: Apply(pair.Second))
            n.properties.Add(prop)
        Return n

    def OnEnumDefinition(node as EnumDefinition):
        o = Moz.ObjectExpression(loc: loc(node))
        for member as EnumMember in node.Members:
            assert member.Initializer != null, 'Enum definition without an initializer value!'
            prop = Moz.ObjectExpressionProp(key: Moz.Literal(value: member.Name), value: Apply(member.Initializer))
            o.properties.Add(prop)

        # Assign the object to a variable
        v = Moz.VariableDeclarator(loc: loc(node))
        v.id = Moz.Identifier(loc: loc(node), name: node.FullName)
        v.init = o
        n = Moz.VariableDeclaration(loc: loc(node), kind: 'var')
        n.declarations.Add(v)

        Return n

    def OnModule(node as Module):
        n = Moz.Program(loc: loc(node))

        st as Moz.IStatement
        for member in node.Members:
            st = Apply(member)
            if st is null:
                continue

            if st isa Moz.BlockStatement:
                n.body += (st as Moz.BlockStatement).body
            else:
                n.body.Add(st)

        for global in node.Globals.Statements:
            st = Apply(global)
            n.body.Add(st)

        n['module'] = node

        Return n

    def OnClassDefinition(node as ClassDefinition):
        n = Moz.BlockStatement()

        st as Moz.IStatement
        for member in node.Members:
            st = Apply(member)
            if st is null:
                continue
            elif st isa Moz.BlockStatement:
                n.body += (st as Moz.BlockStatement).body
            else:
                n.body.Add(st)

        Return n

    def OnField(node as Field):
        pass

    def OnConstructor(node as Constructor):
        pass

    def OnMethod(node as Method):
        n = Moz.FunctionDeclaration(loc: loc(node))
        n.id = Moz.Identifier(loc: loc(node), name: node.Name)
        for param in node.Parameters:
            p = Moz.Identifier(loc: loc(param), name: param.Name)
            n.params.Add(p)

        b = Moz.BlockStatement()
        for local in node.Locals:
            st = Apply(local)
            b.body.Add(st) if st

        b.body += (Apply(node.Body) as Moz.BlockStatement).body
        n.body = b
        Return n

    def OnDeclarationStatement(node as DeclarationStatement):
        # TODO: Generate type annotated comments
        v = Moz.VariableDeclarator(loc: loc(node))
        v.id = Moz.Identifier(name: node.Declaration.Name)
        v.init = Apply(node.Initializer)

        vd = Moz.VariableDeclaration(loc: loc(node), kind: 'var')
        vd.declarations.Add(v)
        Return vd

    def OnBlock(node as Block):
        b = Moz.BlockStatement(loc: loc(node))
        for st in node.Statements:
            mst = Apply(st) as Moz.IStatement
            if mst:
                b.body.Add(mst)

        Return b

    def OnExpressionStatement(node as ExpressionStatement):
        n = Moz.ExpressionStatement(loc: loc(node))
        n.expression = Apply(node.Expression)
        Return n

    def OnReturnStatement(node as ReturnStatement):
        n = Moz.ReturnStatement(loc: loc(node))
        n.argument = Apply(node.Expression)
        Return n

    def OnIfStatement(node as IfStatement):

        ifst = Moz.IfStatement(loc: loc(node))
        ifst.test = Apply(node.Condition)
        ifst.consequent = Apply(node.TrueBlock)
        if node.FalseBlock:
            blk = node.FalseBlock
            if len(blk.Statements) == 1 and blk.FirstStatement isa IfStatement:
                ifst.alternate = Apply(blk.FirstStatement)
            else:
                ifst.alternate = Apply(blk)

        Return ifst

    def OnForStatement(node as ForStatement):
        # Only for statements over range should have survived
        mie = node.Iterator as MethodInvocationExpression
        if len(mie.Arguments) == 1:
            start = Moz.Literal(0)
            length = Apply(mie.Arguments[0])
        else:
            start = Apply(mie.Arguments[0])
            length = Apply(mie.Arguments[1])

        if node.ContainsAnnotation('loop-index'):
            index = Moz.Identifier(node['loop-index'])
        else:
            index = Moz.Identifier(node.Declarations[0].Name)

        fst = Moz.ForStatement(loc: loc(node))
        fst.init = Moz.AssignmentExpression(
            left: index,
            operator: '=',
            right: start
        )
        fst.test = Moz.BinaryExpression(
            left: index,
            operator: '<',
            right: length
        )
        fst.update = Moz.UpdateExpression(
            argument: index,
            operator: '++',
            prefix: false
        )

        fst.body = Apply(node.Block)
        Return fst

    def OnWhileStatement(node as WhileStatement):
        wst = Moz.WhileStatement(loc: loc(node))
        wst.test = Apply(node.Condition)
        wst.body = Apply(node.Block)

        Return wst

    def OnTryStatement(node as TryStatement):

        n = Moz.TryStatement(loc: loc(node))
        n.block = Apply(node.ProtectedBlock)

        assert len(node.ExceptionHandlers) <= 1, 'Multiple exceptions handlers should be processed in previous steps'
        if len(node.ExceptionHandlers):
            hdl = node.ExceptionHandlers[0]
            h = Moz.CatchClause(loc: loc(hdl))
            h.param = Moz.Identifier(loc: loc(hdl.Declaration), name: hdl.Declaration.Name)
            h.body = Apply(hdl.Block)
            n.handlers.Add(h)

        if node.EnsureBlock:
            n.finalizer = Apply(node.EnsureBlock)

        Return n

    def OnBreakStatement(node as BreakStatement):
        Return Moz.BreakStatement(loc: loc(node))

    def OnContinueStatement(node as ContinueStatement):
        Return Moz.ContinueStatement(loc: loc(node))

    def OnLabelStatement(node as LabelStatement):
        n = Moz.LabeledStatement(loc: loc(node))
        n.label = Moz.Identifier(loc: loc(node), name: node.Name)

        # In Mozilla AST labels are always associated with a statement.
        stmts = (node.ParentNode as Block).Statements
        idx = stmts.IndexOf(node)
        st = stmts[idx + 1]
        stmts.RemoveAt(idx + 1)

        if st.NodeType not in (NodeType.ForStatement, NodeType.WhileStatement):
            raise 'Javascript only allows label on looping statements'
        n.body = Apply(st)

        Return n

    def OnGotoStatement(node as GotoStatement):
        n = Moz.ContinueStatement(loc: loc(node))
        n.label = Moz.Identifier(loc: loc(node.Label), name: node.Label.Name)
        Return n

    def OnRaiseStatement(node as RaiseStatement):
        # TODO: Move this to clean step
        /*
        if false and Context.Parameters.Debug:
            n = Moz.CallExpression(loc: loc(node))
            n.callee = Moz.MemberExpression(
                loc: loc(node),
                object: Moz.Identifier(name: 'Boo'),
                property: Moz.Identifier(name: 'raise')
            )
            # TODO: We should make sure it's a constructor
            if node.Exception isa MethodInvocationExpression:
                c = Moz.NewExpression(loc: loc(node.Exception))
                c._constructor = Apply(node.Exception)
                n.arguments.Add(c)
            else:
                n.arguments.Add(Apply(node.Exception))

            lex = node.Exception.LexicalInfo
            n.arguments.Add(Moz.Literal(value: lex.FileName))
            n.arguments.Add(Moz.Literal(value: lex.Line))

            Return Moz.ExpressionStatement(loc: loc(node), expression: n)
        else:
        */
        t = Moz.ThrowStatement(loc: loc(node))
        t.argument = Apply(node.Exception)
        # TODO: We should make sure it's a constructor
        /*
        if false and node.Exception isa MethodInvocationExpression:
            c = Moz.NewExpression(loc: loc(node.Exception))
            c._constructor = Apply(node.Exception)
            t.argument = c
        else:
        */

        Return t

    def OnReferenceExpression(node as ReferenceExpression):
        Return Moz.Identifier(loc: loc(node), name: node.Name)

    def OnMemberReferenceExpression(node as MemberReferenceExpression):
        n = Moz.MemberExpression(loc: loc(node), property: Moz.Identifier(loc: loc(node), name: node.Name))
        n.object = Apply(node.Target)
        Return n

    def OnConditionalExpression(node as ConditionalExpression):
    """ Convert to the ternary operator.
            (10 if true else 20)  -->  true ? 10 : 20
    """
        c = Moz.ConditionalExpression(loc: loc(node))
        c.test = Apply(node.Condition)
        c.consequent = Apply(node.TrueValue)
        c.alternate = Apply(node.FalseValue)
        Return c

    def OnSlicingExpression(node as SlicingExpression):
        n = Moz.MemberExpression(
            loc: loc(node),
            object: Apply(node.Target),
            property: Apply(node.Indices[0].Begin),
            computed: true
        )

        Return n

    def OnMacroStatement(node as MacroStatement):
        # Special handling for switch/case constructs (used in generators)
        if node.Name == 'switch':
            switch = Moz.SwitchStatement(loc: loc(node))
            switch.discriminant = Apply(node.Arguments[0])
            for casemacro as MacroStatement in node.Body.Statements:
                assert casemacro isa MacroStatement

                case = Moz.SwitchCase(loc: loc(casemacro))
                case.test = Apply(casemacro.Arguments[0])
                for st in casemacro.Body.Statements:
                    case.consequent.Add(Apply(st))

                switch.cases.Add(case)

            Return switch
        else:
            raise 'Macro statements should have been already resolved'

    def OnMethodInvocationExpression(node as MethodInvocationExpression):
        # Detect constructors

        if node.Target.Entity isa IConstructor and not isFactory(node.Target):
            c = Moz.NewExpression(loc: loc(node))
            c._constructor = Apply(node.Target)
            for arg in node.Arguments:
                c.arguments.Add(Apply(arg))
            Return c
        # Detect sequences
        elif node.Target.ToString() == '@':
            s = Moz.SequenceExpression(loc: loc(node))
            for arg in node.Arguments:
                s.expressions.Add(Apply(arg))
            Return s

        else:
            n = Moz.CallExpression(loc: loc(node))
            n.callee = Apply(node.Target)
            for arg in node.Arguments:
                n.arguments.Add(Apply(arg))

            # Detect calls to eval() with a simple string argument to define a
            # verbatim version of the expression.
            if node.Target.Entity is my(RuntimeMethodCache).Eval and \
               len(node.Arguments) == 1 and \
               node.Arguments[0].NodeType == NodeType.StringLiteralExpression:
                n.verbatim = (node.Arguments[0] as StringLiteralExpression).Value

            Return n

    def OnBlockExpression(node as BlockExpression):
        n = Moz.FunctionExpression(loc: loc(node))
        for param in node.Parameters:
            p = Moz.Identifier(loc: loc(param), name: param.Name)
            n.params.Add(p)

        n.body = Apply(node.Body)
        Return n

    def OnBinaryExpression(node as BinaryExpression):
        n = Moz.BinaryExpression(loc: loc(node))
        match node.Operator:
            case BinaryOperatorType.ReferenceEquality:
                if NodeType.NullLiteralExpression in (node.Left.NodeType, node.Right.NodeType):
                    Return(ProcessIsNull(node))
                    return

                n.operator = '==='
            case BinaryOperatorType.ReferenceInequality:
                if NodeType.NullLiteralExpression in (node.Left.NodeType, node.Right.NodeType):
                    ue = Moz.UnaryExpression(loc: loc(node), operator: '!')
                    ue.argument = ProcessIsNull(node)
                    Return ue
                    return

                n.operator = '!=='

            case BinaryOperatorType.Equality:
                n.operator = '=='
            case BinaryOperatorType.Inequality:
                n.operator = '!='

            case BinaryOperatorType.Member:
                n.operator = 'in'
            case BinaryOperatorType.Addition:
                n.operator = '+'
            case BinaryOperatorType.Subtraction:
                n.operator = '-'
            case BinaryOperatorType.Multiply:
                n.operator = '*'
            case BinaryOperatorType.Division:
                n.operator = '/'
            case BinaryOperatorType.Modulus:
                n.operator = '%'
            case BinaryOperatorType.BitwiseAnd:
                n.operator = '&'
            case BinaryOperatorType.BitwiseOr:
                n.operator = '|'
            case BinaryOperatorType.ExclusiveOr:
                n.operator = '^'
            case BinaryOperatorType.GreaterThan:
                n.operator = '>'
            case BinaryOperatorType.GreaterThanOrEqual:
                n.operator = '>='
            case BinaryOperatorType.LessThan:
                n.operator = '<'
            case BinaryOperatorType.LessThanOrEqual:
                n.operator = '<='
            case BinaryOperatorType.ShiftLeft:
                n.operator = '<<'
            case BinaryOperatorType.ShiftRight:
                n.operator = '>>'

            case BinaryOperatorType.Assign:
                n = Moz.AssignmentExpression(loc: loc(node), operator: '=')
            case BinaryOperatorType.InPlaceBitwiseAnd:
                n = Moz.AssignmentExpression(loc: loc(node), operator: '&=')
            case BinaryOperatorType.InPlaceBitwiseOr:
                n = Moz.AssignmentExpression(loc: loc(node), operator: '|=')
            case BinaryOperatorType.InPlaceExclusiveOr:
                n = Moz.AssignmentExpression(loc: loc(node), operator: '^=')

            case BinaryOperatorType.And:
                n = Moz.LogicalExpression(loc: loc(node), operator: '&&')
            case BinaryOperatorType.Or:
                n = Moz.LogicalExpression(loc: loc(node), operator: '||')

            otherwise:
                raise 'Operator not supported ' + node.Operator

        n.left = Apply(node.Left)
        n.right = Apply(node.Right)
        Return n

    def OnUnaryExpression(node as UnaryExpression):
        match node.Operator:
            case UnaryOperatorType.UnaryNegation:
                n = Moz.UnaryExpression(loc: loc(node), operator: '-')
            case UnaryOperatorType.LogicalNot:
                n = Moz.UnaryExpression(loc: loc(node), operator: '!')
            case UnaryOperatorType.OnesComplement:
                n = Moz.UnaryExpression(loc: loc(node), operator: '~')
            case UnaryOperatorType.Increment:
                n = Moz.UpdateExpression(loc: loc(node), operator: '++', prefix: true)
            case UnaryOperatorType.Decrement:
                n = Moz.UpdateExpression(loc: loc(node), operator: '--', prefix: true)
            case UnaryOperatorType.PostIncrement:
                n = Moz.UpdateExpression(loc: loc(node), operator: '++', prefix: false)
            case UnaryOperatorType.PostDecrement:
                n = Moz.UpdateExpression(loc: loc(node), operator: '--', prefix: false)
            otherwise:
                raise 'Operator not supported ' + node.Operator

        n.argument = Apply(node.Operand)
        Return n

    protected def ProcessIsNull(node as BinaryExpression) as Moz.IExpression:
        itm = (node.Right if node.Left.NodeType == NodeType.NullLiteralExpression else node.Left)
        call = Moz.CallExpression(loc: loc(node))
        call.callee = Moz.Identifier('Boo.isNull')
        call.arguments.Add(Apply(itm))
        return call


changed test to pass on mono

changed test to pass on mono

git-svn-id: c3b8989232654601f1acea7403f771adaaa44a81@88 2c1201b4-01cd-e047-a400-b836ae1fbc61

"""
Boo.Tests
System.Drawing
"""
using System
using System.Drawing from System.Drawing as SD
using System.Drawing from Boo.Tests

Console.WriteLine(Point(0, 0).GetType().Assembly.GetName().Name)
Console.Write(SD.Point(0, 0).GetType().Assembly.GetName().Name)

"""
MyPoint(0, 0)
{X=0,Y=0}
"""
using System
using System.Drawing from System.Drawing as SD
using System.Drawing from Boo.Tests

Console.WriteLine(Point(0, 0).ToString())
Console.Write(SD.Point(0, 0).ToString())


period not needed for codedom fileextension

git-svn-id: fcce7d6ce0ac15bf24f68871b5145d46fc068279@2052 2c1201b4-01cd-e047-a400-b836ae1fbc61

#region license
// Copyright (c) 2004, Rodrigo B. de Oliveira (rbo@acm.org)
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
//
//     * Redistributions of source code must retain the above copyright notice,
//     this list of conditions and the following disclaimer.
//     * Redistributions in binary form must reproduce the above copyright notice,
//     this list of conditions and the following disclaimer in the documentation
//     and/or other materials provided with the distribution.
//     * Neither the name of Rodrigo B. de Oliveira nor the names of its
//     contributors may be used to endorse or promote products derived from this
//     software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
// THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#endregion

// authors:
// Arron Washington
// Ian MacLean (original C# version)

namespace Boo.Lang.CodeDom

import System
import System.CodeDom.Compiler
class BooCodeProvider(CodeDomProvider):
"""Description of BooCodeProvider"""
	[getter(FileExtension)]
	_ext = "boo"

	def CreateCompiler():
		return BooCodeCompiler()
	def CreateGenerator():
		return BooCodeGenerator()
	override def GetConverter(type as Type) as System.ComponentModel.TypeConverter:
		raise NotImplementedException()

#region license
// Copyright (c) 2004, Rodrigo B. de Oliveira (rbo@acm.org)
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
//
//     * Redistributions of source code must retain the above copyright notice,
//     this list of conditions and the following disclaimer.
//     * Redistributions in binary form must reproduce the above copyright notice,
//     this list of conditions and the following disclaimer in the documentation
//     and/or other materials provided with the distribution.
//     * Neither the name of Rodrigo B. de Oliveira nor the names of its
//     contributors may be used to endorse or promote products derived from this
//     software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
// THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#endregion

// authors:
// Arron Washington
// Ian MacLean (original C# version)

namespace Boo.Lang.CodeDom

import System
import System.CodeDom.Compiler
class BooCodeProvider(CodeDomProvider):
"""Description of BooCodeProvider"""
	[getter(FileExtension)]
	_ext = ".boo"

	def CreateCompiler():
		return BooCodeCompiler()
	def CreateGenerator():
		return BooCodeGenerator()
	override def GetConverter(type as Type) as System.ComponentModel.TypeConverter:
		raise NotImplementedException()


fixed bug with path when using runner

fixed bug with path when using runner

#region license
// Copyright (c) 2004, Rodrigo B. de Oliveira (rbo@acm.org)
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
//
//     * Redistributions of source code must retain the above copyright notice,
//     this list of conditions and the following disclaimer.
//     * Redistributions in binary form must reproduce the above copyright notice,
//     this list of conditions and the following disclaimer in the documentation
//     and/or other materials provided with the distribution.
//     * Neither the name of Rodrigo B. de Oliveira nor the names of its
//     contributors may be used to endorse or promote products derived from this
//     software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
// THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#endregion

namespace booi

from System import AppDomain, Console, Environment, STAThreadAttribute
import System.Linq.Enumerable
from System.Diagnostics import Trace, TraceLevel, TextWriterTraceListener, Stopwatch
from System.IO import TextReader, Directory, File, Path
from System.Reflection import Assembly
from Boo.Lang.Compiler import CompilerContext, CompilerOutputType
from Boo.Lang.Compiler.IO import StringInput, FileInput
from Boo.Lang.Compiler.Pipelines import CompileToMemory, CompileToFile
from Boo.Lang.Useful.CommandLine import CommandLineException

class Program:

    public static final Version = 'Booi ' + BooVersion
    public static final DefaultErrorCode = 127
    public static final DefaultSuccessCode = 0

    _args as (string)
    _cmdline as CommandLine

    _assemblyResolver = AssemblyResolver()
    _params = CompilerParameters()

    def constructor(cmdline, args as (string)):
        _cmdline = cmdline
        _args = args

        # Register a custom resolver to provide compiler assemblies at runtime
        AppDomain.CurrentDomain.AssemblyResolve += _assemblyResolver.AssemblyResolve

    protected def LoadDlls(path as string):
        dlls = Directory.GetFiles(path, '*.dll')
        for dll in dlls:
            Trace.TraceInformation('Loading assembly {0}', dll)
            asm = _params.LoadAssembly(dll)
            _params.References.Add(asm)
            _assemblyResolver.AddAssembly(asm.Assembly)

    protected def LoadPackages(path as string):
        # Load all DLLs in the root directory
        LoadDlls(path)

        # Process NuGet style repositories by looking into package directories,
        # only accessing their latests versions if more than one is present.
        dirs = Directory.GetDirectories(path) \
              .GroupBy({ x | /\.\d.*$/.Replace(x, '') }) \
              .Select({ g | g.OrderBy({ f | f.ToLower() }).Last() }) \
              .ToList()

        # Get executing framework version to discard newer assemblies
        maxversion = typeof(System.Type).Assembly.GetName().Version
        for dir in dirs:
            dir = Path.Combine(dir, 'lib')
            continue unless Directory.Exists(dir)

            # Load any generic assemblies in the lib folder
            LoadDlls(dir)

            # Assume we always target the full framework (netXX)
            path = Directory.GetDirectories(dir, 'net*') \
                 .OrderBy({ f | f[-2:] }) \
                 .Where({ f |
                    maxversion >= System.Version(
                        join(f[-2:].ToCharArray(), '.')
                    )
                 }) \
                 .LastOrDefault()

            LoadDlls(path) if path

    def Run() as int:

        # TODO: Instead of always creating a temp directory to copy
        #       all the references, it should be possible to inject code
        #       in the generated assembly that registers to the AppDomain
        #       assembly load event and loads them based on the configuration
        #       used when compiling. This will allow to load NuGet references
        #       on demand too and should work when using nunit.

        _params.OutputType = CompilerOutputType.Auto
        _params.Debug = _cmdline.Debug
        _params.Ducky = _cmdline.Ducky
        _params.Strict = _cmdline.Strict
        _params.WhiteSpaceAgnostic = _cmdline.Wsa
        _params.Cache = _cmdline.Cache

        if _cmdline.Verbose:
            _params.TraceLevel = TraceLevel.Verbose
            Trace.Listeners.Add(TextWriterTraceListener(Console.Error))

        if _params.Debug or _cmdline.Runner:
            # Save to disk to ensure we get symbols loaded on runtime errors
            _params.GenerateInMemory = false
            _params.Pipeline = CompileToFile()
            # Find a temporary directory name for the output
            while true:
                path = Path.Combine(Path.GetTempPath(), 'booi-' + Path.GetRandomFileName())
                path = path.Replace('.', '-')
                break unless Directory.Exists(path)

            Trace.TraceInformation("Creating temporary directory '{0}'", path)
            Directory.CreateDirectory(path)
            _params.OutputAssembly = Path.Combine(path, 'booi-main.dll')
        else:
            _params.GenerateInMemory = true
            _params.Pipeline = CompileToMemory()

        for d in _cmdline.Defines:
            _params.Defines.Add(d.Key, d.Value)

        for libpath in _cmdline.LibPaths:
            try:
                _params.LibPaths.AddUnique(Path.GetFullPath(libpath))
            except ex as System.ArgumentException:
                Trace.TraceError("LibPath '{0}' not found", libpath)

        sw = System.Diagnostics.Stopwatch()
        sw.Start()

        for reference in _cmdline.References:
            try:
                asmref = _params.LoadAssembly(reference)
                if asmref is null:
                    printerr string.Format(Boo.Lang.Resources.StringResources.BooC_UnableToLoadAssembly, reference)
                    return DefaultErrorCode

                _params.References.Add(asmref)
                _assemblyResolver.AddAssembly(asmref.Assembly)
            except ex:
                printerr "Error loading reference '{0}': {1}", reference, ex
                return DefaultErrorCode

        # Load packages
        for package in _cmdline.Packages:
            LoadPackages(package) if Directory.Exists(package)

        sw.Stop()
        Trace.TraceInformation("Loading references took {0}ms", sw.ElapsedMilliseconds)

        for file in _cmdline.Files:
            continue if file == '-'
            if Directory.Exists(file):
                for f in Directory.GetFiles(file, '*.boo'):
                    _params.Input.Add(FileInput(f))
            else:
                _params.Input.Add(FileInput(file))

        if not len(_cmdline.Files) or _cmdline.Files.Contains('-'):
            _params.Input.Add(StringInput('<stdin>', consume(Console.In)))

        # Set the MAIN define if we are compiling a single script
        if 1 == len(_params.Input) and not _params.Defines.ContainsKey('MAIN'):
            _params.Defines.Add('MAIN', '')

        sw.Restart()
        generated = Compile()
        return DefaultErrorCode if generated is null
        sw.Stop()
        Trace.TraceInformation('Compilation took {0}ms', sw.ElapsedMilliseconds)

        if _cmdline.Runner:
            # Collect all dependencies in a temporary dir
            for asmref in _params.References:
                asm = asmref.Assembly
                # Skip dynamically generated assemblies
                continue if asm.IsDynamic

                try:
                    asmfile = Path.GetFileName(asm.Location)
                    if asmfile in ('System.dll', 'System.Core.dll', 'mscorlib.dll'):
                        continue

                    # HACK: The assembly containing the parser is imported dynamically by
                    #       the compiler, looking for it to be placed next to it.
                    #       Here we ensure that when the compiler is referenced the parser
                    #       is also copied next to it.
                    if asmfile == 'Boo.Lang.Compiler.dll':
                        File.Copy(
                            asm.Location.Replace('Boo.Lang.Compiler.dll', 'Boo.Lang.Parser.dll'),
                            Path.Combine(path, 'Boo.Lang.Parser.dll')
                        )

                    File.Copy(asm.Location, Path.Combine(path, asmfile))
                except ex:
                    Trace.TraceError("Error copying assembly '{0}' to '{1}'", asmref.Name, path)

            # Replace place holders in script arguments
            asmpath = generated.Location
            args = List of string()
            if not len(_args):
                args.Add('"' + asmpath + '"')
            else:
                for arg in _args:
                    arg = arg.Replace('%filename%', Path.GetFileName(asmpath))
                    arg = arg.Replace('%pathname%', Path.GetDirectoryName(asmpath))
                    arg = arg.Replace('%assembly%', asmpath)
                    if arg == '%':
                        arg = asmpath
                    args.Add('"' + arg + '"')

            # Try to execute the runner
            Trace.TraceInformation('Running command: {0} {1}', _cmdline.Runner, join(args, ' '))
            retval = runcommand(_cmdline.Runner, join(args, ' '))

            # Clean up generated files
            Trace.TraceInformation("Removing temporary directory '{0}'", Path.GetDirectoryName(asmpath))
            Directory.Delete(Path.GetDirectoryName(asmpath), true)

        else:
            retval = Execute(generated, _args)

        return retval

    def Compile():
        context = CompilerContext(_params)
        _params.Pipeline.Run(context)

        if _cmdline.Warnings and len(context.Warnings):
            printerr(context.Warnings.ToString())

        if len(context.Errors):
            printerr(context.Errors.ToString(_params.TraceVerbose))
            return null

        if not context.GeneratedAssembly:
            return Assembly.LoadFrom(context.GeneratedAssemblyFileName)

        return context.GeneratedAssembly

    def Execute(asm as Assembly, argv as (string)):
        exitCode = DefaultSuccessCode
        try:
            _assemblyResolver.AddAssembly(asm)

            main = asm.EntryPoint
            if not main:
                printerr 'Error: Entry point not found'
                return DefaultErrorCode

            if len(main.GetParameters()) > 0:
                returnValue = main.Invoke(null, (argv,))
            else:
                returnValue = main.Invoke(null, null)

            exitCode = returnValue if returnValue is not null
        except x as TargetInvocationException:
            printerr x.InnerException
            exitCode = DefaultErrorCode
        ensure:
            Environment.ExitCode = exitCode

        return exitCode

def runcommand(filename, arguments):
    p = System.Diagnostics.Process()
    p.StartInfo.Arguments = arguments
    p.StartInfo.CreateNoWindow = true
    p.StartInfo.UseShellExecute = true
    p.StartInfo.FileName = filename
    p.Start()
    p.WaitForExit()
    return p.ExitCode

def consume(reader as TextReader):
    return join(line for line in reader, "\n")

def printerr(*args):
    for arg in args:
        Console.Error.Write(arg)
        Console.Error.Write(' ')
    Console.Error.WriteLine()

[STAThread]
def Main(argv as (string)) as int:
    # Find the number of arguments that apply to booi
    argc = System.Array.IndexOf(argv, '--')
    if argc >= 0:
        argv = argv[:argc] + argv[argc+1:]
    else:
        argc = 0
        for arg in argv:
            argc++
            break unless arg.StartsWith('-')

    try:
        cmdline = CommandLine(argv[:argc])
    except ex as CommandLineException:
        printerr 'Error:', ex.Message
        return Program.DefaultErrorCode

    if cmdline.DoVersion:
        print Program.Version
        return Program.DefaultSuccessCode

    if not cmdline.IsValid or cmdline.DoHelp:
        print "Usage: booi [options] <script|-> [-- [script options]]"
        print "Options:"
        cmdline.PrintOptions(Console.Out)
        return Program.DefaultErrorCode

    return Program(cmdline, argv[argc:]).Run()

#region license
// Copyright (c) 2004, Rodrigo B. de Oliveira (rbo@acm.org)
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
//
//     * Redistributions of source code must retain the above copyright notice,
//     this list of conditions and the following disclaimer.
//     * Redistributions in binary form must reproduce the above copyright notice,
//     this list of conditions and the following disclaimer in the documentation
//     and/or other materials provided with the distribution.
//     * Neither the name of Rodrigo B. de Oliveira nor the names of its
//     contributors may be used to endorse or promote products derived from this
//     software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
// THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#endregion

namespace booi

from System import AppDomain, Console, Environment, STAThreadAttribute
import System.Linq.Enumerable
from System.Diagnostics import Trace, TraceLevel, TextWriterTraceListener, Stopwatch
from System.IO import TextReader, Directory, File, Path
from System.Reflection import Assembly
from Boo.Lang.Compiler import CompilerContext, CompilerOutputType
from Boo.Lang.Compiler.IO import StringInput, FileInput
from Boo.Lang.Compiler.Pipelines import CompileToMemory, CompileToFile
from Boo.Lang.Useful.CommandLine import CommandLineException

class Program:

    public static final Version = 'Booi ' + BooVersion
    public static final DefaultErrorCode = 127
    public static final DefaultSuccessCode = 0

    _args as (string)
    _cmdline as CommandLine

    _assemblyResolver = AssemblyResolver()
    _params = CompilerParameters()

    def constructor(cmdline, args as (string)):
        _cmdline = cmdline
        _args = args

        # Register a custom resolver to provide compiler assemblies at runtime
        AppDomain.CurrentDomain.AssemblyResolve += _assemblyResolver.AssemblyResolve

    protected def LoadDlls(path as string):
        dlls = Directory.GetFiles(path, '*.dll')
        for dll in dlls:
            Trace.TraceInformation('Loading assembly {0}', dll)
            asm = _params.LoadAssembly(dll)
            _params.References.Add(asm)
            _assemblyResolver.AddAssembly(asm.Assembly)

    protected def LoadPackages(path as string):
        # Load all DLLs in the root directory
        LoadDlls(path)

        # Process NuGet style repositories by looking into package directories,
        # only accessing their latests versions if more than one is present.
        dirs = Directory.GetDirectories(path) \
              .GroupBy({ x | /\.\d.*$/.Replace(x, '') }) \
              .Select({ g | g.OrderBy({ f | f.ToLower() }).Last() }) \
              .ToList()

        # Get executing framework version to discard newer assemblies
        maxversion = typeof(System.Type).Assembly.GetName().Version
        for dir in dirs:
            dir = Path.Combine(dir, 'lib')
            continue unless Directory.Exists(dir)

            # Load any generic assemblies in the lib folder
            LoadDlls(dir)

            # Assume we always target the full framework (netXX)
            path = Directory.GetDirectories(dir, 'net*') \
                 .OrderBy({ f | f[-2:] }) \
                 .Where({ f |
                    maxversion >= System.Version(
                        join(f[-2:].ToCharArray(), '.')
                    )
                 }) \
                 .LastOrDefault()

            LoadDlls(path) if path

    def Run() as int:

        # TODO: Instead of always creating a temp directory to copy
        #       all the references, it should be possible to inject code
        #       in the generated assembly that registers to the AppDomain
        #       assembly load event and loads them based on the configuration
        #       used when compiling. This will allow to load NuGet references
        #       on demand too and should work when using nunit.

        _params.OutputType = CompilerOutputType.Auto
        _params.Debug = _cmdline.Debug
        _params.Ducky = _cmdline.Ducky
        _params.Strict = _cmdline.Strict
        _params.WhiteSpaceAgnostic = _cmdline.Wsa
        _params.Cache = _cmdline.Cache

        if _cmdline.Verbose:
            _params.TraceLevel = TraceLevel.Verbose
            Trace.Listeners.Add(TextWriterTraceListener(Console.Error))

        if _params.Debug or _cmdline.Runner:
            # Save to disk to ensure we get symbols loaded on runtime errors
            _params.GenerateInMemory = false
            _params.Pipeline = CompileToFile()
            # Find a temporary directory name for the output
            while true:
                path = Path.Combine(Path.GetTempPath(), 'booi-' + Path.GetRandomFileName())
                path = path.Replace('.', '-')
                break unless Directory.Exists(path)

            Trace.TraceInformation("Creating temporary directory '{0}'", path)
            Directory.CreateDirectory(path)
            _params.OutputAssembly = Path.Combine(path, 'booi-main.dll')
        else:
            _params.GenerateInMemory = true
            _params.Pipeline = CompileToMemory()

        for d in _cmdline.Defines:
            _params.Defines.Add(d.Key, d.Value)

        for path in _cmdline.LibPaths:
            try:
                _params.LibPaths.AddUnique(Path.GetFullPath(path))
            except ex as System.ArgumentException:
                Trace.TraceError("LibPath '{0}' not found", path)

        sw = System.Diagnostics.Stopwatch()
        sw.Start()

        for reference in _cmdline.References:
            try:
                asmref = _params.LoadAssembly(reference)
                if asmref is null:
                    printerr string.Format(Boo.Lang.Resources.StringResources.BooC_UnableToLoadAssembly, reference)
                    return DefaultErrorCode

                _params.References.Add(asmref)
                _assemblyResolver.AddAssembly(asmref.Assembly)
            except ex:
                printerr "Error loading reference '{0}': {1}", reference, ex
                return DefaultErrorCode

        # Load packages
        for package in _cmdline.Packages:
            LoadPackages(package) if Directory.Exists(package)

        sw.Stop()
        Trace.TraceInformation("Loading references took {0}ms", sw.ElapsedMilliseconds)

        for file in _cmdline.Files:
            continue if file == '-'
            if Directory.Exists(file):
                for f in Directory.GetFiles(file, '*.boo'):
                    _params.Input.Add(FileInput(f))
            else:
                _params.Input.Add(FileInput(file))

        if not len(_cmdline.Files) or _cmdline.Files.Contains('-'):
            _params.Input.Add(StringInput('<stdin>', consume(Console.In)))

        # Set the MAIN define if we are compiling a single script
        if 1 == len(_params.Input) and not _params.Defines.ContainsKey('MAIN'):
            _params.Defines.Add('MAIN', '')

        sw.Restart()
        generated = Compile()
        return DefaultErrorCode if generated is null
        sw.Stop()
        Trace.TraceInformation('Compilation took {0}ms', sw.ElapsedMilliseconds)

        if _cmdline.Runner:
            # Collect all dependencies in a temporary dir
            for asmref in _params.References:
                asm = asmref.Assembly
                # Skip dynamically generated assemblies
                continue if asm.IsDynamic

                try:
                    asmfile = Path.GetFileName(asm.Location)
                    if asmfile in ('System.dll', 'System.Core.dll', 'mscorlib.dll'):
                        continue

                    # HACK: The assembly containing the parser is imported dynamically by
                    #       the compiler, looking for it to be placed next to it.
                    #       Here we ensure that when the compiler is referenced the parser
                    #       is also copied next to it.
                    if asmfile == 'Boo.Lang.Compiler.dll':
                        File.Copy(
                            asm.Location.Replace('Boo.Lang.Compiler.dll', 'Boo.Lang.Parser.dll'),
                            Path.Combine(path, 'Boo.Lang.Parser.dll')
                        )

                    File.Copy(asm.Location, Path.Combine(path, asmfile))
                except ex:
                    Trace.TraceError("Error copying assembly '{0}' to '{1}'", asmref.Name, path)

            # Replace place holders in script arguments
            asmpath = generated.Location
            args = List of string()
            if not len(_args):
                args.Add('"' + asmpath + '"')
            else:
                for arg in _args:
                    arg = arg.Replace('%filename%', Path.GetFileName(asmpath))
                    arg = arg.Replace('%pathname%', Path.GetDirectoryName(asmpath))
                    arg = arg.Replace('%assembly%', asmpath)
                    if arg == '%':
                        arg = asmpath
                    args.Add('"' + arg + '"')

            # Try to execute the runner
            Trace.TraceInformation('Running command: {0} {1}', _cmdline.Runner, join(args, ' '))
            retval = runcommand(_cmdline.Runner, join(args, ' '))

            # Clean up generated files
            Trace.TraceInformation("Removing temporary directory '{0}'", Path.GetDirectoryName(asmpath))
            Directory.Delete(Path.GetDirectoryName(asmpath), true)

        else:
            retval = Execute(generated, _args)

        return retval

    def Compile():
        context = CompilerContext(_params)
        _params.Pipeline.Run(context)

        if _cmdline.Warnings and len(context.Warnings):
            printerr(context.Warnings.ToString())

        if len(context.Errors):
            printerr(context.Errors.ToString(_params.TraceVerbose))
            return null

        if not context.GeneratedAssembly:
            return Assembly.LoadFrom(context.GeneratedAssemblyFileName)

        return context.GeneratedAssembly

    def Execute(asm as Assembly, argv as (string)):
        exitCode = DefaultSuccessCode
        try:
            _assemblyResolver.AddAssembly(asm)

            main = asm.EntryPoint
            if not main:
                printerr 'Error: Entry point not found'
                return DefaultErrorCode

            if len(main.GetParameters()) > 0:
                returnValue = main.Invoke(null, (argv,))
            else:
                returnValue = main.Invoke(null, null)

            exitCode = returnValue if returnValue is not null
        except x as TargetInvocationException:
            printerr x.InnerException
            exitCode = DefaultErrorCode
        ensure:
            Environment.ExitCode = exitCode

        return exitCode

def runcommand(filename, arguments):
    p = System.Diagnostics.Process()
    p.StartInfo.Arguments = arguments
    p.StartInfo.CreateNoWindow = true
    p.StartInfo.UseShellExecute = true
    p.StartInfo.FileName = filename
    p.Start()
    p.WaitForExit()
    return p.ExitCode

def consume(reader as TextReader):
    return join(line for line in reader, "\n")

def printerr(*args):
    for arg in args:
        Console.Error.Write(arg)
        Console.Error.Write(' ')
    Console.Error.WriteLine()

[STAThread]
def Main(argv as (string)) as int:
    # Find the number of arguments that apply to booi
    argc = System.Array.IndexOf(argv, '--')
    if argc >= 0:
        argv = argv[:argc] + argv[argc+1:]
    else:
        argc = 0
        for arg in argv:
            argc++
            break unless arg.StartsWith('-')

    try:
        cmdline = CommandLine(argv[:argc])
    except ex as CommandLineException:
        printerr 'Error:', ex.Message
        return Program.DefaultErrorCode

    if cmdline.DoVersion:
        print Program.Version
        return Program.DefaultSuccessCode

    if not cmdline.IsValid or cmdline.DoHelp:
        print "Usage: booi [options] <script|-> [-- [script options]]"
        print "Options:"
        cmdline.PrintOptions(Console.Out)
        return Program.DefaultErrorCode

    return Program(cmdline, argv[argc:]).Run()