implemented ReturnType in JarMethod

implemented ReturnType in JarMethod

namespace Boojay.Compilation.TypeSystem

import Boo.Lang.Compiler
import Boo.Lang.Compiler.TypeSystem
import Boo.Lang.Compiler.TypeSystem.Services
import Boo.Lang.PatternMatching

class JarMethod(IMethod):

	_declaringType as IType
	_name as string
	_descriptor as string
	_access as int

	def constructor(declaringType as IType, name as string, descriptor as string, access as int):
		_declaringType = declaringType
		_name = name
		_descriptor = descriptor
		_access = access

	EntityType:
		get: return EntityType.Method

	Name:
		get: return _name

	FullName:
		get: return "${_declaringType.FullName}.${_name}"

	DeclaringType:
		get: return _declaringType

	IsPublic:
		get: return (_access & org.objectweb.asm.Opcodes.ACC_PUBLIC) != 0

	IsProtected:
		get: return (_access & org.objectweb.asm.Opcodes.ACC_PROTECTED) != 0

	IsStatic:
		get: return (_access & org.objectweb.asm.Opcodes.ACC_STATIC) != 0

	AcceptVarArgs:
		get: return (_access & org.objectweb.asm.Opcodes.ACC_VARARGS) != 0

	IsExtension:
		get: return false // FIXME

	Type:
		get: return self.CallableType

	CallableType:
		get: return my(TypeSystemServices).GetCallableType(self)

	GenericInfo:
		get: return null

	IsDuckTyped:
		get: return false

	ReturnType as IType:
		get:
			asmType = org.objectweb.asm.Type.getReturnType(_descriptor)
			return ResolveTypeName(asmType)

	private def ResolveTypeName(asmType as org.objectweb.asm.Type):
		asmTypeName = asmType.ToString()
		match asmTypeName:
			case "b": return my(TypeSystemServices).ByteType
			otherwise: return ResolveNonPrimitive(asmTypeName)

	private def ResolveNonPrimitive(typeName as string):
		if typeName[0:1] == "L":
			booTypeName = typeName[1:-1].Replace("/", ".")
			resolvedType = my(NameResolutionService).ResolveQualifiedName(booTypeName)
			raise "ResolveQualifiedName failed to resolve ${booTypeName}" unless resolvedType
			return resolvedType
		raise "Unknown NonPrimitive ${typeName}"

	def GetParameters(): // FIXME
		return array[of IParameter](0)

namespace Boojay.Compilation.TypeSystem

import Boo.Lang.Compiler
import Boo.Lang.Compiler.TypeSystem
import Boo.Lang.Compiler.TypeSystem.Services
import Boo.Lang.PatternMatching

class JarMethod(IMethod):

	_declaringType as IType
	_name as string
	_descriptor as string
	_access as int

	def constructor(declaringType as IType, name as string, descriptor as string, access as int):
		_declaringType = declaringType
		_name = name
		_descriptor = descriptor
		_access = access

	EntityType:
		get: return EntityType.Method

	Name:
		get: return _name

	FullName:
		get: return "${_declaringType.FullName}.${_name}"

	DeclaringType:
		get: return _declaringType

	IsPublic:
		get: return (_access & org.objectweb.asm.Opcodes.ACC_PUBLIC) != 0

	IsProtected:
		get: return (_access & org.objectweb.asm.Opcodes.ACC_PROTECTED) != 0

	IsStatic:
		get: return (_access & org.objectweb.asm.Opcodes.ACC_STATIC) != 0

	AcceptVarArgs:
		get: return (_access & org.objectweb.asm.Opcodes.ACC_VARARGS) != 0

	IsExtension:
		get: return false // FIXME

	Type:
		get: return self.CallableType

	CallableType:
		get: return my(TypeSystemServices).GetCallableType(self)

	ReturnType as IType:
		get:
			asmType = org.objectweb.asm.Type.getReturnType(_descriptor)
			return ResolveTypeName(asmType)

	private def ResolveTypeName(asmType as org.objectweb.asm.Type):
		asmTypeName = asmType.ToString()
		match asmTypeName:
			case "b": return my(TypeSystemServices).ByteType
			otherwise: return ResolveNonPrimitive(asmTypeName)

	private def ResolveNonPrimitive(typeName as string):
		if typeName[0:1] == "L":
			booTypeName = typeName[1:].Replace("/", ".")
			resolvedType = my(NameResolutionService).ResolveQualifiedName(booTypeName)
			raise "ResolveQualifiedName failed to resolve ${booTypeName}" unless resolvedType
			return resolvedType
		raise "Unknown NonPrimitive ${typeName}"

	def GetParameters(): // FIXME
		return array[of IParameter](0)


Use trace helpers to show namespace mappings

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))

        deps = List of Moz.IExpression() { Moz.Literal('exports'), Moz.Literal('Boo') }
        refs = List of Moz.IPattern() { Moz.Identifier('exports'), Moz.Identifier('Boo') }

        # Get namespace mapping annotations
        mapping as Hash = node['nsmapping']
        asmrefs as Hash = node['nsasmrefs']

        for ns in mapping.Keys:
            if ns in asmrefs:
                context().TraceInfo('Namespace mapping {0} => {1}', ns, mapping[ns] + ':' + asmrefs[ns])
                deps.Add( Moz.Literal(ns + ':' + asmrefs[ns]) )
            else:
                context().TraceInfo('Namespace mapping {0} => {1}', ns, mapping[ns])
                deps.Add( Moz.Literal(ns) )
            refs.Add( Moz.Identifier(mapping[ns]) )

        # Use Boo.define to bootstrap the module contents
        call = Moz.CallExpression()
        call.callee = Moz.MemberExpression(
            object: Moz.Identifier(name: 'Boo'),
            property: Moz.Identifier(name: 'define')
        )
        call.arguments.Add(Moz.Literal(value: (node.Namespace.ToString() if node.Namespace else '')))
        call.arguments.Add(Moz.ArrayExpression(elements: deps))
        fn = Moz.FunctionExpression(params: refs, body: Moz.BlockStatement())
        call.arguments.Add(fn)
        n.body.Add(Moz.ExpressionStatement(call))

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

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

        # Export public symbols
        for member in (node.Members[0] as ClassDefinition).Members:
            continue unless member.IsVisible

            expr = Moz.BinaryExpression()
            expr.left = Moz.MemberExpression(
                object: Moz.Identifier('exports'),
                property: Moz.Identifier(member.Name)
            )
            expr.operator = '=';
            expr.right = Moz.Identifier(member.Name)

            st = Moz.ExpressionStatement(expr)
            fn.body.body.Add(st)

        if not node.Globals.IsEmpty:
            rcall = Moz.CallExpression()
            rcall.callee = Moz.MemberExpression(
                object: Moz.Identifier(name: 'Boo'),
                property: Moz.Identifier(name: 'require')
            )
            # Clone the deps list replacing 'exports' by the namespace
            deps = deps.GetRange(0, len(deps))
            deps[0] = Moz.Literal(node.Namespace or '')
            rcall.arguments.Add(Moz.ArrayExpression(elements: deps))
            fn = Moz.FunctionExpression(params: refs, body: Moz.BlockStatement())
            rcall.arguments.Add(fn)
            n.body.Add(Moz.ExpressionStatement(rcall))

            for global in node.Globals.Statements:
                st = Apply(global)
                fn.body.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.ContainsAnnotation('constructor') or \
           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))

        deps = List of Moz.IExpression() { Moz.Literal('exports'), Moz.Literal('Boo') }
        refs = List of Moz.IPattern() { Moz.Identifier('exports'), Moz.Identifier('Boo') }

        # Get namespace mapping annotations
        mapping as Hash = node['nsmapping']
        asmrefs as Hash = node['nsasmrefs']

        for ns in mapping.Keys:
            if ns in asmrefs:
                print '{0} => {1}' % (ns, mapping[ns] + ':' + asmrefs[ns])
                deps.Add( Moz.Literal(ns + ':' + asmrefs[ns]) )
            else:
                print '{0} => {1}' % (ns, mapping[ns])
                deps.Add( Moz.Literal(ns) )
            refs.Add( Moz.Identifier(mapping[ns]) )

        # Use Boo.define to bootstrap the module contents
        call = Moz.CallExpression()
        call.callee = Moz.MemberExpression(
            object: Moz.Identifier(name: 'Boo'),
            property: Moz.Identifier(name: 'define')
        )
        call.arguments.Add(Moz.Literal(value: (node.Namespace.ToString() if node.Namespace else '')))
        call.arguments.Add(Moz.ArrayExpression(elements: deps))
        fn = Moz.FunctionExpression(params: refs, body: Moz.BlockStatement())
        call.arguments.Add(fn)
        n.body.Add(Moz.ExpressionStatement(call))

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

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

        # Export public symbols
        for member in (node.Members[0] as ClassDefinition).Members:
            continue unless member.IsVisible

            expr = Moz.BinaryExpression()
            expr.left = Moz.MemberExpression(
                object: Moz.Identifier('exports'),
                property: Moz.Identifier(member.Name)
            )
            expr.operator = '=';
            expr.right = Moz.Identifier(member.Name)

            st = Moz.ExpressionStatement(expr)
            fn.body.body.Add(st)

        if not node.Globals.IsEmpty:
            rcall = Moz.CallExpression()
            rcall.callee = Moz.MemberExpression(
                object: Moz.Identifier(name: 'Boo'),
                property: Moz.Identifier(name: 'require')
            )
            # Clone the deps list replacing 'exports' by the namespace
            deps = deps.GetRange(0, len(deps))
            deps[0] = Moz.Literal(node.Namespace or '')
            rcall.arguments.Add(Moz.ArrayExpression(elements: deps))
            fn = Moz.FunctionExpression(params: refs, body: Moz.BlockStatement())
            rcall.arguments.Add(fn)
            n.body.Add(Moz.ExpressionStatement(rcall))

            for global in node.Globals.Statements:
                st = Apply(global)
                fn.body.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.ContainsAnnotation('constructor') or \
           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


fixture runner now always outputs the generated source code

import NUnit.Framework

import System.IO(Path, Directory, StreamReader)
import System.Reflection(Assembly)
import System.Timers(Timer)

import Boo.Lang.Compiler.IO
import BooJs.Compiler

# The JavaScript interpretter
import Jurassic(ScriptEngine, JavaScriptException, CompatibilityMode)
import Jurassic.Library(ObjectInstance, FunctionInstance, JSFunctionAttribute)

class ConsoleMock(ObjectInstance):
    _output = []

    def constructor(engine as ScriptEngine):
        super(engine)
        PopulateFunctions()

    [JSFunction(Name:'log')]
    def log(*msg as (object)):
        _output.Add(join(msg, ' '))

    def output():
        return _output.Join("\n")

    def reset():
        _output.Clear()

static class Window:
    timers = Boo.Lang.List[of Timer]()

    def hasTimers():
        return len(timers) > 0

    def clearTimers():
        for timer in timers:
            timer.Stop()
        timers.Clear()

    def waitTimers():
        while hasTimers():
            System.Threading.Thread.Sleep(200)

    [JSFunction(Name:'setTimeout')]
    def setTimeout(callback as FunctionInstance, millisec as int) as int:
        timer = Timer(System.Math.Max(millisec, 1))
        timer.AutoReset = false
        timer.Enabled = true
        timer.Elapsed += do:
            try:
                callback.Call(callback)
            ensure:
                timer.Stop()
                timers.Remove(timer)

        timer.Start()
        timers.Add(timer)
        return len(timers)

class FixtureRunner:

    static _comp as BooJsCompiler
    static _engine as ScriptEngine

    static _tests_path as string

    static def fixture_path(fname):
      if not _tests_path:
        # Find the path where the assembly resides
        path = Assembly.GetExecutingAssembly().CodeBase

        # Look for the tests directory in once of its parent paths
        while path = Path.GetFullPath(Path.Combine(path, '..')):
          if Directory.Exists(Path.Combine(path, 'tests')):
            _tests_path = path
            break
          elif path == Path.GetPathRoot(path):
            raise 'Unable to find tests directory'

      return Path.Combine(_tests_path, fname)

    static def run(file as string):
      file = fixture_path(file)

      timer = System.Diagnostics.Stopwatch()

      comp = setupCompiler()
      comp.Parameters.Ducky = false
      comp.Parameters.Strict = false

      # Enable compiler debug messages
      #comp.Parameters.TraceLevel = System.Diagnostics.TraceLevel.Verbose

      #print 'Setup: ', timer.ElapsedMilliseconds

      # Check if we want to ignore this fixture
      fp = StreamReader(file)
      while (line = fp.ReadLine()) != null:
        if line.IndexOf('#IGNORE') == 0:
          Assert.Ignore(line.Substring(len('#IGNORE')+1))
        elif line.IndexOf('#DUCKY') == 0:
          comp.Parameters.Ducky = true

      comp.Parameters.Input.Clear()
      comp.Parameters.Input.Add(FileInput(file))

      # Add the test runner assembly so we can resolve imports for supporting types
      comp.Parameters.References.Add(Assembly.GetExecutingAssembly())

      timer.Restart()

      result = comp.Run()

      #print 'Compilation: ', timer.ElapsedMilliseconds

      if len(result.Warnings):
        for warn as Boo.Lang.Compiler.CompilerWarning in result.Warnings:
            li = warn.LexicalInfo
            name = System.IO.Path.GetFileName(li.FileName)
            print "$(warn.Code) $(warn.Message) [$name:$(li.Line),$(li.Column)]"

      if len(result.Errors):
        for err as Boo.Lang.Compiler.CompilerError in result.Errors:
            li = err.LexicalInfo
            name = System.IO.Path.GetFileName(li.FileName)
            print "$(err.Code): $(err.Message) [$name:$(li.Line),$(li.Column)]"
        print '-----------------------------------------------------'
        print result.CompileUnit.ToCodeString()
        raise result.Errors[0]

      module = result.CompileUnit.Modules[0]
      expected = module.Documentation or ''

      code = comp.Parameters.OutputWriter.ToString()
      runTest(code, expected)

    static def setupCompiler():
        if not _comp:
            pipeline = Pipelines.PrintJs()
            #pipeline = Pipelines.PrintAst()
            #pipeline = Pipelines.PrintBoo()

            timer = System.Diagnostics.Stopwatch()
            pipeline.BeforeStep += def(pipeline, args):
                //print '-------------------------------------------'
                //print 'Step:', args.Step
                //print args.Context.CompileUnit
                timer.Restart()

            pipeline.AfterStep += def(pipeline, args):
                if timer.ElapsedMilliseconds > 100:
                    print 'Slow Step {0}: {1}ms' % (args.Step.GetType().Name, timer.ElapsedMilliseconds)

            _comp = newBooJsCompiler(pipeline)
            _comp.Parameters.Debug = true
            _comp.Parameters.GenerateInMemory = true

        # Reset the output writer
        _comp.Parameters.OutputWriter = System.IO.StringWriter()
        return _comp

    static def setupInterpreter():
        if not self._engine:
            self._engine = ScriptEngine()
            # Try to target older browser with ES3 (JS 1.5)
            self._engine.CompatibilityMode = CompatibilityMode.ECMAScript3
            # Force strict mode
            self._engine.ForceStrictMode = true

            # TODO: Does this help us somehow to get better error reports?
            #engine.EnableDebugging = true

            # Configure the global environment
            self._engine.SetGlobalFunction('setTimeout', Window.setTimeout)

            console = ConsoleMock(self._engine)
            self._engine.SetGlobalValue('console', console)

            # Load runtime
            stream = typeof(FixtureRunner).Assembly.GetManifestResourceStream('Boo.js')
            reader = System.IO.StreamReader(stream)
            self._engine.Execute(reader.ReadToEnd())

            # Patch the runtime to be compatible with Jurassic
            self._engine.Execute('Boo.AssertionError.prototype.toString = function(){ return this.message; };')

            # Load tests support types
            stream = typeof(FixtureRunner).Assembly.GetManifestResourceStream('BooJs.Tests.Support.js')
            reader = System.IO.StreamReader(stream)
            self._engine.Execute(reader.ReadToEnd())

            self._engine.ExecuteFile('/Users/drslump/www/boojs/src/Boo.Async.js')

        return self._engine

    static def runTest(code as string, expected as string):

        print '----------------------------------------------[code]-'
        print code.Trim()

        # TODO: Check if we can reuse the same engine
        using engine = setupInterpreter():

          console as ConsoleMock = engine.GetGlobalValue('console')
          console.reset()

          try:
            engine.Execute(code)
            Window.waitTimers()
            Assert.AreEqual(expected, console.output())
          except ex:
            Window.clearTimers()

            jsex = ex as Jurassic.JavaScriptException
            if jsex:
                jsobj = jsex.ErrorObject as Jurassic.Library.ObjectInstance
                if jsobj and jsobj.HasProperty('boo_filename'):
                  print join(('Exception: "', ex.Message, '" at ',
                          jsobj.GetPropertyValue('boo_filename'), ':',
                          jsobj.GetPropertyValue('boo_line')
                      ), '')

            print '--------------------------------------------[output]-'
            print console.output()
            print '-----------------------------------------------------'

            raise

import NUnit.Framework

import System.IO(Path, Directory, StreamReader)
import System.Reflection(Assembly)
import System.Timers(Timer)

import Boo.Lang.Compiler.IO
import BooJs.Compiler

# The JavaScript interpretter
import Jurassic(ScriptEngine, JavaScriptException, CompatibilityMode)
import Jurassic.Library(ObjectInstance, FunctionInstance, JSFunctionAttribute)

class ConsoleMock(ObjectInstance):
    _output = []

    def constructor(engine as ScriptEngine):
        super(engine)
        PopulateFunctions()

    [JSFunction(Name:'log')]
    def log(*msg as (object)):
        _output.Add(join(msg, ' '))

    def output():
        return _output.Join("\n")

    def reset():
        _output.Clear()

static class Window:
    timers = Boo.Lang.List[of Timer]()

    def hasTimers():
        return len(timers) > 0

    def clearTimers():
        for timer in timers:
            timer.Stop()
        timers.Clear()

    def waitTimers():
        while hasTimers():
            System.Threading.Thread.Sleep(200)

    [JSFunction(Name:'setTimeout')]
    def setTimeout(callback as FunctionInstance, millisec as int) as int:
        timer = Timer(System.Math.Max(millisec, 1))
        timer.AutoReset = false
        timer.Enabled = true
        timer.Elapsed += do:
            try:
                callback.Call(callback)
            ensure:
                timer.Stop()
                timers.Remove(timer)

        timer.Start()
        timers.Add(timer)
        return len(timers)

class FixtureRunner:

    static _comp as BooJsCompiler
    static _engine as ScriptEngine

    static _tests_path as string

    static def fixture_path(fname):
      if not _tests_path:
        # Find the path where the assembly resides
        path = Assembly.GetExecutingAssembly().CodeBase

        # Look for the tests directory in once of its parent paths
        while path = Path.GetFullPath(Path.Combine(path, '..')):
          if Directory.Exists(Path.Combine(path, 'tests')):
            _tests_path = path
            break
          elif path == Path.GetPathRoot(path):
            raise 'Unable to find tests directory'

      return Path.Combine(_tests_path, fname)

    static def run(file as string):
      file = fixture_path(file)

      timer = System.Diagnostics.Stopwatch()

      comp = setupCompiler()
      comp.Parameters.Ducky = false
      comp.Parameters.Strict = false

      # Enable compiler debug messages
      #comp.Parameters.TraceLevel = System.Diagnostics.TraceLevel.Verbose

      #print 'Setup: ', timer.ElapsedMilliseconds

      # Check if we want to ignore this fixture
      fp = StreamReader(file)
      while (line = fp.ReadLine()) != null:
        if line.IndexOf('#IGNORE') == 0:
          Assert.Ignore(line.Substring(len('#IGNORE')+1))
        elif line.IndexOf('#DUCKY') == 0:
          comp.Parameters.Ducky = true

      comp.Parameters.Input.Clear()
      comp.Parameters.Input.Add(FileInput(file))

      # Add the test runner assembly so we can resolve imports for supporting types
      comp.Parameters.References.Add(Assembly.GetExecutingAssembly())

      timer.Restart()

      result = comp.Run()

      #print 'Compilation: ', timer.ElapsedMilliseconds

      if len(result.Warnings):
        for warn as Boo.Lang.Compiler.CompilerWarning in result.Warnings:
            li = warn.LexicalInfo
            name = System.IO.Path.GetFileName(li.FileName)
            print "$(warn.Code) $(warn.Message) [$name:$(li.Line),$(li.Column)]"

      if len(result.Errors):
        for err as Boo.Lang.Compiler.CompilerError in result.Errors:
            li = err.LexicalInfo
            name = System.IO.Path.GetFileName(li.FileName)
            print "$(err.Code): $(err.Message) [$name:$(li.Line),$(li.Column)]"
        print '-----------------------------------------------------'
        print result.CompileUnit.ToCodeString()
        raise result.Errors[0]

      module = result.CompileUnit.Modules[0]
      expected = module.Documentation or ''

      code = comp.Parameters.OutputWriter.ToString()
      runTest(code, expected)

    static def setupCompiler():
        if not _comp:
            pipeline = Pipelines.PrintJs()
            #pipeline = Pipelines.PrintAst()
            #pipeline = Pipelines.PrintBoo()

            timer = System.Diagnostics.Stopwatch()
            pipeline.BeforeStep += def(pipeline, args):
                //print '-------------------------------------------'
                //print 'Step:', args.Step
                //print args.Context.CompileUnit
                timer.Restart()

            pipeline.AfterStep += def(pipeline, args):
                if timer.ElapsedMilliseconds > 100:
                    print 'Slow Step {0}: {1}ms' % (args.Step.GetType().Name, timer.ElapsedMilliseconds)

            _comp = newBooJsCompiler(pipeline)
            _comp.Parameters.Debug = true
            _comp.Parameters.GenerateInMemory = true

        # Reset the output writer
        _comp.Parameters.OutputWriter = System.IO.StringWriter()
        return _comp

    static def setupInterpreter():
        if not self._engine:
            self._engine = ScriptEngine()
            # Try to target older browser with ES3 (JS 1.5)
            self._engine.CompatibilityMode = CompatibilityMode.ECMAScript3
            # Force strict mode
            self._engine.ForceStrictMode = true

            # TODO: Does this help us somehow to get better error reports?
            #engine.EnableDebugging = true

            # Configure the global environment
            self._engine.SetGlobalFunction('setTimeout', Window.setTimeout)

            console = ConsoleMock(self._engine)
            self._engine.SetGlobalValue('console', console)

            # Load runtime
            stream = typeof(FixtureRunner).Assembly.GetManifestResourceStream('Boo.js')
            reader = System.IO.StreamReader(stream)
            self._engine.Execute(reader.ReadToEnd())

            # Patch the runtime to be compatible with Jurassic
            self._engine.Execute('Boo.AssertionError.prototype.toString = function(){ return this.message; };')

            # Load tests support types
            stream = typeof(FixtureRunner).Assembly.GetManifestResourceStream('BooJs.Tests.Support.js')
            reader = System.IO.StreamReader(stream)
            self._engine.Execute(reader.ReadToEnd())

            self._engine.ExecuteFile('/Users/drslump/www/boojs/src/Boo.Async.js')

        return self._engine

    static def runTest(code as string, expected as string):

        # TODO: Check if we can reuse the same engine
        using engine = setupInterpreter():

          console as ConsoleMock = engine.GetGlobalValue('console')
          console.reset()

          try:
            engine.Execute(code)
            Window.waitTimers()
            Assert.AreEqual(expected, console.output())
          except ex:
            Window.clearTimers()

            jsex = ex as Jurassic.JavaScriptException
            if jsex:
                jsobj = jsex.ErrorObject as Jurassic.Library.ObjectInstance
                if jsobj and jsobj.HasProperty('boo_filename'):
                  print join(('Exception: "', ex.Message, '" at ',
                          jsobj.GetPropertyValue('boo_filename'), ':',
                          jsobj.GetPropertyValue('boo_line')
                      ), '')

            print '----------------------------------------------[code]-'
            print code.Trim()
            print '--------------------------------------------[output]-'
            print console.output()
            print '-----------------------------------------------------'

            raise


regexes with embedded spaces need the @ prefix now

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

import Boo.Lang.Compiler
import Boo.Lang.Compiler.Ast
import Boo.Lang.Compiler.IO
import Boo.Lang.Compiler.Pipelines
import Boo.Lang.Compiler.Steps
import System.IO

class AutoImport(AbstractVisitorCompilerStep):

	override def OnImport(node as Import):

		references = self.Parameters.References
		errors = self.Errors

		for reference in references:
			simpleName = @/, /.Split(reference.FullName)[0]
			return if simpleName == node.Namespace

		result = compile("${node.Namespace}.boo", CompilerOutputType.Library)
		if len(result.Errors):
			for e in result.Errors:
				errors.Add(e)
		else:
			references.Add(result.GeneratedAssembly)

	override def Run():
		Visit(CompileUnit)

def compile(fname as string, outputType as CompilerOutputType):
	pipeline = CompileToMemory()
	pipeline.Insert(1, AutoImport())

	print("compiling ${fname}...")
	compiler = BooCompiler()
	compiler.Parameters.OutputType = outputType
	compiler.Parameters.Input.Add(FileInput(fname))
	compiler.Parameters.Pipeline = pipeline
	result = compiler.Run()
	print("done.")
	return result

result = compile("client.boo", CompilerOutputType.ConsoleApplication)
if len(result.Errors):
	print(result.Errors.ToString(true))
else:
	result.GeneratedAssemblyEntryPoint.Invoke(null, (null,))

import Boo.Lang.Compiler
import Boo.Lang.Compiler.Ast
import Boo.Lang.Compiler.IO
import Boo.Lang.Compiler.Pipelines
import Boo.Lang.Compiler.Steps
import System.IO

class AutoImport(AbstractVisitorCompilerStep):

	override def OnImport(node as Import):

		references = self.Parameters.References
		errors = self.Errors

		for reference in references:
			simpleName = /, /.Split(reference.FullName)[0]
			return if simpleName == node.Namespace

		result = compile("${node.Namespace}.boo", CompilerOutputType.Library)
		if len(result.Errors):
			for e in result.Errors:
				errors.Add(e)
		else:
			references.Add(result.GeneratedAssembly)

	override def Run():
		Visit(CompileUnit)

def compile(fname as string, outputType as CompilerOutputType):
	pipeline = CompileToMemory()
	pipeline.Insert(1, AutoImport())

	print("compiling ${fname}...")
	compiler = BooCompiler()
	compiler.Parameters.OutputType = outputType
	compiler.Parameters.Input.Add(FileInput(fname))
	compiler.Parameters.Pipeline = pipeline
	result = compiler.Run()
	print("done.")
	return result

result = compile("client.boo", CompilerOutputType.ConsoleApplication)
if len(result.Errors):
	print(result.Errors.ToString(true))
else:
	result.GeneratedAssemblyEntryPoint.Invoke(null, (null,))


make sure not to collide with existing names when LOCK_TIMEOUT is defined. (spotted by Marc Dassonneville)

git-svn-id: fcce7d6ce0ac15bf24f68871b5145d46fc068279@3450 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

namespace Boo.Lang.Extensions

import System
import Boo.Lang.Compiler
import Boo.Lang.Compiler.Ast

def createLockedBlock(context as CompilerContext, monitor as Expression, block as Block):
	temp = ReferenceExpression(monitor.LexicalInfo, context.GetUniqueName("lock", "monitor"))

	expireString as string = null
	expire as int = 5000 #expiration time in ms (default 5s)
	if context.Parameters.Defines.TryGetValue("LOCK_TIMEOUT", expireString):
		expire = int.Parse(expireString) if expireString
		_acquired = ReferenceExpression(monitor.LexicalInfo, context.GetUniqueName("lock", "acquired"))
		monitorEntry = [|
			block:
				$(_acquired) = System.Threading.Monitor.TryEnter($temp, $expire)
				if not $(_acquired):
					raise System.TimeoutException(string.Format("Lock at '{0}' could not be acquired within LOCK_TIMEOUT({1}ms) - possible deadlock", $(monitor.LexicalInfo.ToString()), $expire))
		|].Body
	else:
		monitorEntry = [|
			block:
				System.Threading.Monitor.Enter($temp)
		|].Body

	assignment = [| $temp = $monitor |].withLexicalInfoFrom(monitor)
	return [|
		$assignment
		$monitorEntry
		try:
			$block
		ensure:
			System.Threading.Monitor.Exit($temp)
	|]

macro lock:
	if 0 == len(lock.Arguments):
		raise CompilerErrorFactory.InvalidLockMacroArguments(lock)

	expansion = lock.Body
	for arg in reversed(lock.Arguments):
		expansion = createLockedBlock(Context, arg, expansion)
	return expansion

#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 Boo.Lang.Extensions

import System
import Boo.Lang.Compiler
import Boo.Lang.Compiler.Ast

def createLockedBlock(context as CompilerContext, monitor as Expression, block as Block):
	temp = ReferenceExpression(monitor.LexicalInfo, context.GetUniqueName("lock", "monitor"))

	expireString as string = null
	expire as int = 5000 #expiration time in ms (default 5s)
	if context.Parameters.Defines.TryGetValue("LOCK_TIMEOUT", expireString):
		expire = int.Parse(expireString) if expireString
		monitorEntry = [|
			block:
				acquired = System.Threading.Monitor.TryEnter($temp, $expire)
				if not acquired:
					msg = string.Format("Lock at '{0}' could not be acquired within LOCK_TIMEOUT({1}ms) - possible deadlock", $(monitor.LexicalInfo.ToString()), $expire)
					raise System.TimeoutException(msg)
		|].Body
	else:
		monitorEntry = [|
			block:
				System.Threading.Monitor.Enter($temp)
		|].Body

	assignment = [| $temp = $monitor |].withLexicalInfoFrom(monitor)
	return [|
		$assignment
		$monitorEntry
		try:
			$block
		ensure:
			System.Threading.Monitor.Exit($temp)
	|]

macro lock:
	if 0 == len(lock.Arguments):
		raise CompilerErrorFactory.InvalidLockMacroArguments(lock)

	expansion = lock.Body
	for arg in reversed(lock.Arguments):
		expansion = createLockedBlock(Context, arg, expansion)
	return expansion


pipeline option

pipeline option

git-svn-id: fcce7d6ce0ac15bf24f68871b5145d46fc068279@2208 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

namespace Boo.NAnt

import System
import System.Globalization
import System.IO
import System.Text.RegularExpressions
import NAnt.Core
import NAnt.Core.Attributes
import NAnt.Core.Types
import NAnt.Core.Util
import NAnt.DotNet.Types

import NAnt.DotNet.Tasks
import System.Reflection

[TaskName('booc')]
public class BoocTask(CompilerBase):
	#region Private Instance Fields
	private _debugOutput as DebugOutput = DebugOutput.Enable
	private _exe as string

	private _useruntime = true //keep true for mono compatibility (don't call booc.exe directly)
	private _noconfig = false
	private _nostdlib = false
	private _wsa = false
	private _ducky = false
	private _pipeline as string

	#endregion Private Instance Fields
	#region Private Static Fields
	private static _classNameRegex = Regex('^((?<comment>/\\*.*?(\\*/|$))|[\\s\\.\\{]+|class\\s+(?<class>\\w+)|(?<keyword>\\w+))*')

	private static _namespaceRegex = Regex('^((?<comment>/\\*.*?(\\*/|$))|[\\s\\.\\{]+|namespace\\s+(?<namespace>(\\w+(\\.\\w+)*)+)|(?<keyword>\\w+))*')

	#endregion Private Static Fields

	def constructor():
		SupportsKeyFile = true
		SupportsKeyContainer = true

	[FrameworkConfigurable("exename")]
	[TaskAttribute('exename')]
	public override ExeName as string:
		get:
			return _exe
		set:
			_exe = value

	[FrameworkConfigurable("useruntimeengine")]
	[TaskAttribute('useruntimeengine')]
	override UseRuntimeEngine as bool:
		get:
			return _useruntime
		set:
			_useruntime = value

	#region Public Instance Properties

	[TaskAttribute('debug')]
	public DebugOutput as DebugOutput:
		get:
			return _debugOutput
		set:
			_debugOutput = value

	[TaskAttribute('pipeline')]
	public Pipeline:
		get:
			return _pipeline
		set:
			_pipeline = value

	public Debug as bool:
		get:
			return (DebugOutput != DebugOutput.None)
		set:
			if value:
				DebugOutput = DebugOutput.Enable
			else:
				DebugOutput = DebugOutput.None

	[FrameworkConfigurable('noconfig')]
	[TaskAttribute('noconfig')]
	[BooleanValidator]
	public NoConfig as bool:
		get:
			return _noconfig
		set:
			_noconfig = value

	[FrameworkConfigurable('nostdlib')]
	[TaskAttribute('nostdlib')]
	[BooleanValidator]
	public NoStdLib as bool:
		get:
			return _nostdlib
		set:
			_nostdlib = value

	[TaskAttribute('wsa')]
	[BooleanValidator]
	public WhiteSpaceAgnostic as bool:
		get:
			return _wsa
		set:
			_wsa = value

	[TaskAttribute('ducky')]
	[BooleanValidator]
	public Ducky as bool:
		get:
			return _ducky
		set:
			_ducky = value

	private def FindBooc() as string:
		path as string
		dir = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)
		if Project.TargetFramework:
			frameworkname = Project.TargetFramework.Name
			path = Path.Combine(Path.Combine(dir, frameworkname), "booc.exe")
			if File.Exists(path):
				return path
			//treat mono-1.0==net-1.1 and mono-##==net-##
			frameworkname = frameworkname.Replace("mono-1.0","net-1.1").Replace("mono-","net-")
			path = Path.Combine(Path.Combine(dir, frameworkname), "booc.exe")
			if File.Exists(path):
				return path
		path = Path.Combine(dir, "booc.exe")
		if File.Exists(path):
			return path
		_useruntime = false
		return "booc" //try booc in PATH

	protected override def ExecuteTask():
		if not ExeName or ExeName == string.Empty:
			ExeName = FindBooc()
		super()

	#endregion Public Instance Properties
	#region Override implementation of CompilerBase
	protected override def WriteOptions(writer as TextWriter):
		if DebugOutput != DebugOutput.None:
			WriteOption(writer, 'debug')
		else:
			WriteOption(writer, 'debug-')
		if NoConfig and (not Arguments.Contains('-noconfig')):
			Arguments.Add(Argument('-noconfig'))
		if NoStdLib:
			WriteOption(writer, "nostdlib")
		if Verbose:
			WriteOption(writer, "vv")
		if WhiteSpaceAgnostic:
			WriteOption(writer, "wsa")
		if Ducky:
			WriteOption(writer, "ducky")
		if Pipeline:
			WriteOption(writer, "p", _pipeline)

	protected override def WriteOption(writer as TextWriter, name as string):
		writer.WriteLine("-{0}", name)

	protected override def WriteOption(writer as TextWriter, name as string, value as string):
		if value.IndexOf(" ") > 0 and (not value.StartsWith("\"")
						or not value.EndsWith("\"")):
			writer.WriteLine("-{0}:\"{1}\"", name, value)
		else:
			writer.WriteLine("-{0}:{1}", name, value)

	public Extension as string:
		get:
			return 'boo'

	protected ClassNameRegex as Regex:
		get:
			return _classNameRegex

	protected NamespaceRegex as Regex:
		get:
			return _namespaceRegex
	#endregion Override implementation of CompilerBase

#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 Boo.NAnt

import System
import System.Globalization
import System.IO
import System.Text.RegularExpressions
import NAnt.Core
import NAnt.Core.Attributes
import NAnt.Core.Types
import NAnt.Core.Util
import NAnt.DotNet.Types

import NAnt.DotNet.Tasks
import System.Reflection

[TaskName('booc')]
public class BoocTask(CompilerBase):
	#region Private Instance Fields
	private _debugOutput as DebugOutput = DebugOutput.Enable
	private _exe as string

	private _useruntime = true //keep true for mono compatibility (don't call booc.exe directly)
	private _noconfig = false
	private _nostdlib = false
	private _wsa = false
	private _ducky = false

	#endregion Private Instance Fields
	#region Private Static Fields
	private static _classNameRegex = Regex('^((?<comment>/\\*.*?(\\*/|$))|[\\s\\.\\{]+|class\\s+(?<class>\\w+)|(?<keyword>\\w+))*')

	private static _namespaceRegex = Regex('^((?<comment>/\\*.*?(\\*/|$))|[\\s\\.\\{]+|namespace\\s+(?<namespace>(\\w+(\\.\\w+)*)+)|(?<keyword>\\w+))*')

	#endregion Private Static Fields

	def constructor():
		SupportsKeyFile = true
		SupportsKeyContainer = true

	[FrameworkConfigurable("exename")]
	[TaskAttribute('exename')]
	public override ExeName as string:
		get:
			return _exe
		set:
			_exe = value

	[FrameworkConfigurable("useruntimeengine")]
	[TaskAttribute('useruntimeengine')]
	override UseRuntimeEngine as bool:
		get:
			return _useruntime
		set:
			_useruntime = value

	#region Public Instance Properties

	[TaskAttribute('debug')]
	public DebugOutput as DebugOutput:
		get:
			return _debugOutput
		set:
			_debugOutput = value

	public Debug as bool:
		get:
			return (DebugOutput != DebugOutput.None)
		set:
			if value:
				DebugOutput = DebugOutput.Enable
			else:
				DebugOutput = DebugOutput.None

	[FrameworkConfigurable('noconfig')]
	[TaskAttribute('noconfig')]
	[BooleanValidator]
	public NoConfig as bool:
		get:
			return _noconfig
		set:
			_noconfig = value

	[FrameworkConfigurable('nostdlib')]
	[TaskAttribute('nostdlib')]
	[BooleanValidator]
	public NoStdLib as bool:
		get:
			return _nostdlib
		set:
			_nostdlib = value

	[TaskAttribute('wsa')]
	[BooleanValidator]
	public WhiteSpaceAgnostic as bool:
		get:
			return _wsa
		set:
			_wsa = value

	[TaskAttribute('ducky')]
	[BooleanValidator]
	public Ducky as bool:
		get:
			return _ducky
		set:
			_ducky = value

	private def FindBooc() as string:
		path as string
		dir = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)
		if Project.TargetFramework:
			frameworkname = Project.TargetFramework.Name
			path = Path.Combine(Path.Combine(dir, frameworkname), "booc.exe")
			if File.Exists(path):
				return path
			//treat mono-1.0==net-1.1 and mono-##==net-##
			frameworkname = frameworkname.Replace("mono-1.0","net-1.1").Replace("mono-","net-")
			path = Path.Combine(Path.Combine(dir, frameworkname), "booc.exe")
			if File.Exists(path):
				return path
		path = Path.Combine(dir, "booc.exe")
		if File.Exists(path):
			return path
		_useruntime = false
		return "booc" //try booc in PATH

	protected override def ExecuteTask():
		if not ExeName or ExeName == string.Empty:
			ExeName = FindBooc()
		super()

	#endregion Public Instance Properties
	#region Override implementation of CompilerBase
	protected override def WriteOptions(writer as TextWriter):
		if DebugOutput != DebugOutput.None:
			WriteOption(writer, 'debug')
		else:
			WriteOption(writer, 'debug-')
		if NoConfig and (not Arguments.Contains('-noconfig')):
			Arguments.Add(Argument('-noconfig'))
		if NoStdLib:
			WriteOption(writer, "nostdlib")
		if Verbose:
			WriteOption(writer, "vv")
		if WhiteSpaceAgnostic:
			WriteOption(writer, "wsa")
		if Ducky:
			WriteOption(writer, "ducky")

	protected override def WriteOption(writer as TextWriter, name as string):
		writer.WriteLine("-{0}", name)

	protected override def WriteOption(writer as TextWriter, name as string, value as string):
		if value.IndexOf(" ") > 0 and (not value.StartsWith("\"")
						or not value.EndsWith("\"")):
			writer.WriteLine("-{0}:\"{1}\"", name, value)
		else:
			writer.WriteLine("-{0}:{1}", name, value)

	public Extension as string:
		get:
			return 'boo'

	protected ClassNameRegex as Regex:
		get:
			return _classNameRegex

	protected NamespaceRegex as Regex:
		get:
			return _namespaceRegex
	#endregion Override implementation of CompilerBase


last if statement is unnecessary

last if statement is unnecessary

namespace Boo.OMeta

import Boo.Lang.Compiler
import Boo.Lang.Compiler.Ast
import Boo.PatternMatching

macro ometa:

	def enclosingTypeDefinition():
		type as TypeDefinition = ometa.GetAncestor(NodeType.ClassDefinition) or ometa.GetAncestor(NodeType.Module)
		return type

	def expressions():
		for stmt in ometa.Block.Statements:
			match stmt:
				case ExpressionStatement(Expression: e):
					yield e

	def expandGrammarSetup():
		block = Block()
		for e in expressions():
			match e:
				case [| $(ReferenceExpression(Name: name)) = $pattern |]:
					code = [|
						block:
							InstallRule($name) do(grammar as OMetaGrammar, input as OMetaInput):
								//print "> ${$name}"
								//try:
									$(RuleExpander(name).expand(pattern))
								//ensure:
								//	print "< ${$name}"
					|].Block
					block.Add(code)
		return block

	declaration = ometa.Arguments[0]

	type = [|
		class $(grammarName(declaration))(OMetaGrammar):

			_grammar as OMetaGrammar

			def constructor():
				_grammar = $(prototypeFor(declaration))
				$(expandGrammarSetup())

			def InstallRule(ruleName as string, rule as OMetaRule):
				_grammar.InstallRule(ruleName, rule)

			def OMetaGrammar.Apply(context as OMetaGrammar, rule as string, input as OMetaInput):
				return _grammar.Apply(context, rule, input)

			def OMetaGrammar.SuperApply(context as OMetaGrammar, rule as string, input as OMetaInput):
				return _grammar.SuperApply(context, rule, input)

			def Apply(rule as string, input as OMetaInput):
				return _grammar.Apply(self, rule, input)

	|]
	for e in expressions():
		match e:
			case [| $(ReferenceExpression(Name: name)) = $_ |]:
				m = [|
					def $name(input as OMetaInput):
						return Apply($name, input)
				|]
				type.Members.Add(m)

	type.LexicalInfo = ometa.LexicalInfo
	enclosingTypeDefinition().Members.Add(type)

def prototypeFor(e as Expression) as MethodInvocationExpression:
	match e:
		case [| $_ < $prototype |]:
			return [| OMetaDelegatingGrammar($prototype()) |]
		case ReferenceExpression():
			return [| OMetaGrammarPrototype() |]

def grammarName(e as Expression) as string:
	match e:
		case ReferenceExpression(Name: name):
			return name
		case [| $l < $_ |]:
			return grammarName(l)

class RuleExpander:

	_ruleName as string
	_collectingParseTree = DynamicVariable[of bool](true)

	def constructor(ruleName as string):
		_ruleName = ruleName

	def expand(e as Expression) as Block:
		block = expand(e, [| input |], [| lastMatch |])
		block.Add([| return lastMatch |])
		block.LexicalInfo = e.LexicalInfo
		return block

	def expand(e as Expression, input as Expression, lastMatch as ReferenceExpression) as Block:
		block = Block()
		expand block, e, input, lastMatch
		return block

	def expandChoices(block as Block, choices as List, input as Expression, lastMatch as ReferenceExpression):
		temp = uniqueName()

		currentBlock = block
		for choice in choices:
			expand currentBlock, choice, input, temp
			code = [|
				if $temp isa SuccessfulMatch:
					$lastMatch = $temp
				else:
					pass
			|]
			currentBlock.Add(code)
			currentBlock = code.FalseBlock
		currentBlock.Add([| $lastMatch = FailedMatch($input) |])

	def expandRepetition(block as Block, e as Expression, input as Expression, lastMatch as ReferenceExpression):

		temp = lastMatch #uniqueName()
		result = uniqueName()
		code = [|
			block:
				$(expand(e, input, temp))
				smatch = $temp as SuccessfulMatch
				if smatch is not null:
					$result = [smatch.Value]
					while true:
						$(expand(e, [| $temp.Input |], temp))
						smatch = $temp as SuccessfulMatch
						break if smatch is null
						$result.Add(smatch.Value)

					$lastMatch = SuccessfulMatch($temp.Input, $result )
		|].Block
		block.Add(code)

	def collectChoices(choices as List, e as Expression):
		match e:
			case [| $l | $r |]:
				collectChoices choices, l
				collectChoices choices, r
			otherwise:
				choices.Add(e)

	collectingParseTree:
		get: return _collectingParseTree.Value

	def expandSequence(block as Block, sequence as ExpressionCollection, input as Expression, lastMatch as ReferenceExpression):

		if collectingParseTree:
			expandSequenceWithParseTree block, sequence, input, lastMatch
		else:
			expandSequenceWithoutParseTree block, sequence, input, lastMatch

	def expandSequenceWithoutParseTree(block as Block, sequence as ExpressionCollection, input as Expression, lastMatch as ReferenceExpression):

		currentBlock = block
		for item in sequence.ToArray()[:-1]:
			expand currentBlock, item, input, lastMatch
			input = [| $lastMatch.Input |]
			code = [|
				if $lastMatch isa SuccessfulMatch:
					pass
			|]
			currentBlock.Add(code)
			currentBlock = code.TrueBlock
		expand currentBlock, sequence[-1], input, lastMatch

	def expandSequenceWithParseTree(block as Block, sequence as ExpressionCollection, input as Expression, lastMatch as ReferenceExpression):

		result = uniqueName()
		currentBlock = block
		currentBlock.Add([| $result = [] |])

		for item in sequence:
			expand currentBlock, item, input, lastMatch
			input = [| $lastMatch.Input |]
			currentBlock.Add([| smatch = $lastMatch as SuccessfulMatch |])
			code = [|
				if smatch is not null:
					$result.Add(smatch.Value)
			|]
			currentBlock.Add(code)
			currentBlock = code.TrueBlock
		currentBlock.Add([| $lastMatch = SuccessfulMatch(smatch.Input, $result) |])

	def expand(block as Block, e as Expression, input as Expression, lastMatch as ReferenceExpression):

		match e:
			case [| $pattern ^ $value |]:
				_collectingParseTree.With(false):
					expand block, pattern, input, lastMatch
					code = [|
						block:
							smatch = $lastMatch as SuccessfulMatch
							if smatch is not null:
								$lastMatch = SuccessfulMatch(smatch.Input, $value)
					|].Block
					block.Add(code)

			case [| $pattern >> $variable |]:
				_collectingParseTree.With(true):
					expand block, pattern, input, lastMatch
					code = [|
						block:
							smatch = $lastMatch as SuccessfulMatch
							if smatch is not null:
								$variable = smatch.Value
					|].Block
					block.Add(code)

			case [| $_ | $_ |]:
				choices = []
				collectChoices choices, e
				expandChoices block, choices, input, lastMatch

			case StringLiteralExpression():
				block.Add([| $lastMatch = string_($input, $e) |])

			case [| ++$rule |]:
				expandRepetition block, rule, input, lastMatch

			case ReferenceExpression(Name: name):
				block.Add([| $lastMatch = grammar.Apply(grammar, $name, $input) |])

			case [| super |]:
				block.Add([| $lastMatch = grammar.SuperApply(grammar, $_ruleName, $input) |])

			case ArrayLiteralExpression(Items: items):
				expandSequence block, items, input, lastMatch

def uniqueName():
	return ReferenceExpression(Name: "temp${CompilerContext.Current.AllocIndex()}")

namespace Boo.OMeta

import Boo.Lang.Compiler
import Boo.Lang.Compiler.Ast
import Boo.PatternMatching

macro ometa:

	def enclosingTypeDefinition():
		type as TypeDefinition = ometa.GetAncestor(NodeType.ClassDefinition) or ometa.GetAncestor(NodeType.Module)
		return type

	def expressions():
		for stmt in ometa.Block.Statements:
			match stmt:
				case ExpressionStatement(Expression: e):
					yield e

	def expandGrammarSetup():
		block = Block()
		for e in expressions():
			match e:
				case [| $(ReferenceExpression(Name: name)) = $pattern |]:
					code = [|
						block:
							InstallRule($name) do(grammar as OMetaGrammar, input as OMetaInput):
								//print "> ${$name}"
								//try:
									$(RuleExpander(name).expand(pattern))
								//ensure:
								//	print "< ${$name}"
					|].Block
					block.Add(code)
		return block

	declaration = ometa.Arguments[0]

	type = [|
		class $(grammarName(declaration))(OMetaGrammar):

			_grammar as OMetaGrammar

			def constructor():
				_grammar = $(prototypeFor(declaration))
				$(expandGrammarSetup())

			def InstallRule(ruleName as string, rule as OMetaRule):
				_grammar.InstallRule(ruleName, rule)

			def OMetaGrammar.Apply(context as OMetaGrammar, rule as string, input as OMetaInput):
				return _grammar.Apply(context, rule, input)

			def OMetaGrammar.SuperApply(context as OMetaGrammar, rule as string, input as OMetaInput):
				return _grammar.SuperApply(context, rule, input)

			def Apply(rule as string, input as OMetaInput):
				return _grammar.Apply(self, rule, input)

	|]
	for e in expressions():
		match e:
			case [| $(ReferenceExpression(Name: name)) = $_ |]:
				m = [|
					def $name(input as OMetaInput):
						return Apply($name, input)
				|]
				type.Members.Add(m)

	type.LexicalInfo = ometa.LexicalInfo
	enclosingTypeDefinition().Members.Add(type)

def prototypeFor(e as Expression) as MethodInvocationExpression:
	match e:
		case [| $_ < $prototype |]:
			return [| OMetaDelegatingGrammar($prototype()) |]
		case ReferenceExpression():
			return [| OMetaGrammarPrototype() |]

def grammarName(e as Expression) as string:
	match e:
		case ReferenceExpression(Name: name):
			return name
		case [| $l < $_ |]:
			return grammarName(l)

class RuleExpander:

	_ruleName as string
	_collectingParseTree = DynamicVariable[of bool](true)

	def constructor(ruleName as string):
		_ruleName = ruleName

	def expand(e as Expression) as Block:
		block = expand(e, [| input |], [| lastMatch |])
		block.Add([| return lastMatch |])
		block.LexicalInfo = e.LexicalInfo
		return block

	def expand(e as Expression, input as Expression, lastMatch as ReferenceExpression) as Block:
		block = Block()
		expand block, e, input, lastMatch
		return block

	def expandChoices(block as Block, choices as List, input as Expression, lastMatch as ReferenceExpression):
		temp = uniqueName()

		currentBlock = block
		for choice in choices:
			expand currentBlock, choice, input, temp
			code = [|
				if $temp isa SuccessfulMatch:
					$lastMatch = $temp
				else:
					pass
			|]
			currentBlock.Add(code)
			currentBlock = code.FalseBlock
		currentBlock.Add([| $lastMatch = FailedMatch($input) |])

	def expandRepetition(block as Block, e as Expression, input as Expression, lastMatch as ReferenceExpression):

		temp = lastMatch #uniqueName()
		result = uniqueName()
		code = [|
			block:
				$(expand(e, input, temp))
				smatch = $temp as SuccessfulMatch
				if smatch is not null:
					$result = [smatch.Value]
					while true:
						$(expand(e, [| $temp.Input |], temp))
						smatch = $temp as SuccessfulMatch
						break if smatch is null
						$result.Add(smatch.Value)

					$lastMatch = SuccessfulMatch($temp.Input, $result )
		|].Block
		block.Add(code)

	def collectChoices(choices as List, e as Expression):
		match e:
			case [| $l | $r |]:
				collectChoices choices, l
				collectChoices choices, r
			otherwise:
				choices.Add(e)

	collectingParseTree:
		get: return _collectingParseTree.Value

	def expandSequence(block as Block, sequence as ExpressionCollection, input as Expression, lastMatch as ReferenceExpression):

		if collectingParseTree:
			expandSequenceWithParseTree block, sequence, input, lastMatch
		else:
			expandSequenceWithoutParseTree block, sequence, input, lastMatch

	def expandSequenceWithoutParseTree(block as Block, sequence as ExpressionCollection, input as Expression, lastMatch as ReferenceExpression):

		currentBlock = block
		for item in sequence:
			expand currentBlock, item, input, lastMatch
			input = [| $lastMatch.Input |]
			code = [|
				if $lastMatch isa SuccessfulMatch:
					pass
			|]
			currentBlock.Add(code)
			currentBlock = code.TrueBlock

	def expandSequenceWithParseTree(block as Block, sequence as ExpressionCollection, input as Expression, lastMatch as ReferenceExpression):

		result = uniqueName()
		currentBlock = block
		currentBlock.Add([| $result = [] |])

		for item in sequence:
			expand currentBlock, item, input, lastMatch
			input = [| $lastMatch.Input |]
			currentBlock.Add([| smatch = $lastMatch as SuccessfulMatch |])
			code = [|
				if smatch is not null:
					$result.Add(smatch.Value)
			|]
			currentBlock.Add(code)
			currentBlock = code.TrueBlock
		currentBlock.Add([| $lastMatch = SuccessfulMatch(smatch.Input, $result) |])

	def expand(block as Block, e as Expression, input as Expression, lastMatch as ReferenceExpression):

		match e:
			case [| $pattern ^ $value |]:
				_collectingParseTree.With(false):
					expand block, pattern, input, lastMatch
					code = [|
						block:
							smatch = $lastMatch as SuccessfulMatch
							if smatch is not null:
								$lastMatch = SuccessfulMatch(smatch.Input, $value)
					|].Block
					block.Add(code)

			case [| $pattern >> $variable |]:
				_collectingParseTree.With(true):
					expand block, pattern, input, lastMatch
					code = [|
						block:
							smatch = $lastMatch as SuccessfulMatch
							if smatch is not null:
								$variable = smatch.Value
					|].Block
					block.Add(code)

			case [| $_ | $_ |]:
				choices = []
				collectChoices choices, e
				expandChoices block, choices, input, lastMatch

			case StringLiteralExpression():
				block.Add([| $lastMatch = string_($input, $e) |])

			case [| ++$rule |]:
				expandRepetition block, rule, input, lastMatch

			case ReferenceExpression(Name: name):
				block.Add([| $lastMatch = grammar.Apply(grammar, $name, $input) |])

			case [| super |]:
				block.Add([| $lastMatch = grammar.SuperApply(grammar, $_ruleName, $input) |])

			case ArrayLiteralExpression(Items: items):
				expandSequence block, items, input, lastMatch

def uniqueName():
	return ReferenceExpression(Name: "temp${CompilerContext.Current.AllocIndex()}")