fixed detection of exports module

fixed detection of exports module

namespace BooJs.Compiler.Steps

import Boo.Lang.Compiler
import Boo.Lang.Compiler.Ast
import Boo.Lang.Compiler.Steps

import Boo.Lang.Environments
import Boo.Lang.Compiler.TypeSystem(EntityType)
import Boo.Lang.Compiler.TypeSystem.Services.RuntimeMethodCache as BooRuntimeMethodCache
import Boo.Lang.Compiler.TypeSystem.Reflection(ExternalType)
import BooJs.Compiler.TypeSystem(RuntimeMethodCache)
import BooJs.Lang.Extensions

import System.Runtime.CompilerServices(CompilerGeneratedAttribute)

class PrepareAst(AbstractTransformerCompilerStep):
"""
    Prepare the AST for its conversion to Javascript

        - Remove unneeded nodes
        - Convert builtins and runtime references

"""
    [getter(MethodCache)]
    private _methodCache as RuntimeMethodCache

    [getter(BooMethodCache)]
    private _booMethodCache as BooRuntimeMethodCache

    protected def GetAttribute[of T(System.Attribute)](node as Node) as T:
        entity = node.Entity as TypeSystem.IExternalEntity
        return (GetAttribute[of T](entity) if entity else null as T)

    protected def GetAttribute[of T(System.Attribute)](entity as TypeSystem.IExternalEntity) as T:
        return System.Attribute.GetCustomAttribute(entity.MemberInfo, typeof(T))

    protected def IsBuiltin(node as Node) as bool:
        entity = node.Entity as TypeSystem.Reflection.ExternalType
        if not entity and centity = node.Entity as TypeSystem.ExternalMethod:
            entity = centity.DeclaringType

        return false if not entity
        return true if entity.Type is TypeSystemServices.BuiltinsType
        return entity.DeclaringType == TypeSystemServices.BuiltinsType

    protected def IsGlobal(node as Node) as bool:
        if entity = node.Entity as TypeSystem.Reflection.ExternalType:
            return entity.ActualType.Namespace == 'BooJs.Lang.Globals'

        return false

    def Initialize(context as CompilerContext):
        super(context)

        _methodCache = EnvironmentProvision[of RuntimeMethodCache]()
        _booMethodCache = EnvironmentProvision[of BooRuntimeMethodCache]()

    def EnterModule(node as Module):
        if node.Name == 'CompilerGenerated':
            RemoveCurrentNode()
            return false

        return true

    def EnterClassDefinition(node as ClassDefinition):
        if node.Name == 'CompilerGeneratedExtensions':
            RemoveCurrentNode()
            return false

        if GetAttribute[of CompilerGeneratedAttribute](node):
            RemoveCurrentNode()
            return false

        return true

    def OnExpressionStatement(node as ExpressionStatement):
        # Ignore the assignment of locals produced by closures instrumentation
        be = node.Expression as BinaryExpression
        if be and be.Operator == BinaryOperatorType.Assign and be.Left.ToString() == '$locals':
            RemoveCurrentNode
            return

        # Ignore initialization value calls (__initobj__). We do it elsewhere.
        mie = node.Expression as MethodInvocationExpression
        if mie and mie.Target.Entity == TypeSystem.BuiltinFunction.InitValueType:
            RemoveCurrentNode
            return

        super(node)

    protected def ProcessReference(node as ReferenceExpression) as Node:
        if TransformAttribute.HasAttribute(node):
            result = TransformAttribute.Resolve(node, null)
            Visit result
            return result

        # Primitive type references
        if entity = node.Entity as ExternalType and TypeSystemServices.IsLiteralPrimitive(entity):
            return StringLiteralExpression(node.LexicalInfo, entity.FullName)

        # Check for builtins references
        if IsBuiltin(node):
            name = node.Name.Split(char('.'))[-1]
            refe = [| Boo.$(ReferenceExpression(Name: name)) |].withLexicalInfoFrom(node)
            refe.Entity = node.Entity
            return refe

        # Entities in the Global namespace are never prefixed
        if IsGlobal(node):
            node.Name = node.Name.Split(char('.'))[-1]
            return node

        # Members of the module are placed in the top scope
        # TODO: This detection algorithm is very weak! We need to come out with something better
        ientity = node.Entity as TypeSystem.IMember
        if ientity and ientity.DeclaringType and ientity.DeclaringType.IsClass and ientity.DeclaringType.Name == ientity.DeclaringType.FullName:
            # Anything not being a constructor is excluded
            if node.Entity.EntityType != EntityType.Constructor and not ientity.IsStatic:
                return node

            mre = MemberReferenceExpression(node.LexicalInfo)
            mre.ExpressionType = node.ExpressionType
            mre.Entity = node.Entity
            mre.Target = ReferenceExpression('exports', LexicalInfo: node.LexicalInfo)
            mre.Name = node.Name.Split(char('.'))[-1]
            return mre

        return node

    def OnReferenceExpression(node as ReferenceExpression):
        ReplaceCurrentNode ProcessReference(node)

    def OnMemberReferenceExpression(node as MemberReferenceExpression):
        if TransformAttribute.HasAttribute(node):
            result = TransformAttribute.Resolve(node, null)
            Visit result
            ReplaceCurrentNode result
            return

        # Convert from `$locals.$variable` to `variable`
        if node.Target.NodeType == NodeType.ReferenceExpression:
            if (node.Target as ReferenceExpression).Name == '$locals':
                refexp = ReferenceExpression(node.Name[1:], LexicalInfo: node.LexicalInfo)
                ReplaceCurrentNode refexp
                return

        # Members of the module are placed in the top scope
        # TODO: This detection algorithm is very weak! We need to come out with something better
        ientity = node.Entity as TypeSystem.IMember
        if ientity and ientity.DeclaringType and ientity.DeclaringType.IsClass and ientity.DeclaringType.Name == ientity.DeclaringType.FullName:
            # Anything not being a constructor is excluded
            if node.Entity.EntityType != EntityType.Constructor and not ientity.IsStatic:
                return
            node.Target = ReferenceExpression('exports', LexicalInfo: node.LexicalInfo)
            return

        # Check for builtins references
        if IsBuiltin(node.Target):
            node.Target = [| Boo |].withLexicalInfoFrom(node.Target)

        super(node)

    def OnMethodInvocationExpression(node as MethodInvocationExpression):
        # Convert Transform attribute metadata to an AST annotation
        if TransformAttribute.HasAttribute(node.Target):
            result = TransformAttribute.Resolve(node.Target, node.Arguments)
            result = VisitNode(result)
            ReplaceCurrentNode result
            return

        if GetAttribute[of VarArgsAttribute](node.Target) and not node.ContainsAnnotation('varargs-processed'):
            node['varargs-processed'] = true
            if node.Arguments[-1] isa ListLiteralExpression:
                last = node.Arguments[-1] as ListLiteralExpression
                node.Arguments.Remove(last)
                for itm in last.Items:
                    node.Arguments.Add(itm)
                return

            params = ListLiteralExpression()
            for i in range(0, len(node.Arguments)-1):
                params.Items.Add( node.Arguments[i] )
            mie = [| $(params).concat($(node.Arguments[-1])) |]
            if node.Target isa MemberReferenceExpression:
                target = (node.Target as MemberReferenceExpression).Target
            else:
                target = [| Boo.UNDEF |]
            node = [| $(node.Target).apply($target, $mie) |]
            ReplaceCurrentNode node

        /*
        rts = TypeSystemServices.RuntimeType
        rtsInvoke = ResolveMethod(rts, 'Invoke')
        rtsInvokeCallable = ResolveMethod(rts, 'InvokeCallable')
        rtsInvokeBinaryOperator = ResolveMethod(rts, 'InvokeBinaryOperator')
        rtsInvokeUnaryOperator = ResolveMethod(rts, 'InvokeUnaryOperator')
        rtsSetProperty = ResolveMethod(rts, 'SetProperty')
        rtsGetProperty = ResolveMethod(rts, 'GetProperty')
        rtsSetSlice = ResolveMethod(rts, 'SetSlice')
        rtsGetSlice = ResolveMethod(rts, 'GetSlice')
        */

        if node.Target.Entity == MethodCache.InvokeBinaryOperator:

            # Convert it back into a binary expression
            be = BinaryExpression(node.LexicalInfo)
            be.ExpressionType = node.ExpressionType
            be.Left = node.Arguments[1]
            be.Right = node.Arguments[2]

            # Remove the op_ to find the operator type
            operator = (node.Arguments[0] as StringLiteralExpression).Value[3:]
            be.Operator = System.Enum.Parse(BinaryOperatorType, operator)

            ReplaceCurrentNode Visit(be)
            return

        if node.Target.Entity == MethodCache.InvokeUnaryOperator:
            # Convert it back into a unary expression
            ue = UnaryExpression(node.LexicalInfo)
            ue.ExpressionType = node.ExpressionType
            ue.Operand = node.Arguments[1]

            # Remove the op_ to find the operator type
            operator = (node.Arguments[0] as StringLiteralExpression).Value[3:]
            ue.Operator = System.Enum.Parse(UnaryOperatorType, operator)

            ReplaceCurrentNode Visit(ue)
            return

        super(node)

    protected def LocalsToDecls(locals as LocalCollection):
        # Convert locals back to simple declaration statements
        found = ['$locals']
        for local in locals:
            # Avoid duplicates
            if local.Name is null or local.Name in found:
                continue

            found.Push(local.Name)

            decl = Declaration(LexicalInfo: local.LexicalInfo)
            decl.Name = local.Name

            # Detect local type
            initializer as Expression = NullLiteralExpression()
            entity = local.Entity as TypeSystem.Internal.InternalLocal
            if entity:
                if entity.OriginalDeclaration:
                    # Skip declarations for those flagged as global
                    continue if entity.OriginalDeclaration.ContainsAnnotation('global')
                    decl.Type = entity.OriginalDeclaration.Type
                else:
                    # TODO: Can we generate proper type annotations with this?
                    decl.Type = SimpleTypeReference(Entity: entity)

                # Initialize with a default value
                if TypeSystemServices.IsNumber(entity.Type):
                    initializer = IntegerLiteralExpression(0)
                elif entity.Type == TypeSystemServices.TimeSpanType:
                    initializer = IntegerLiteralExpression(0)
                elif entity.Type == TypeSystemServices.BoolType:
                    initializer = BoolLiteralExpression(false)
                elif entity.Type == TypeSystemServices.StringType:
                    initializer = StringLiteralExpression('')

            yield DeclarationStatement(LexicalInfo: local.LexicalInfo, Declaration: decl, Initializer: initializer)

    def OnBlockExpression(node as BlockExpression):
    """ Convert locals annotation to declarations
    """
        if node.ContainsAnnotation('locals'):
            for st in LocalsToDecls(node['locals']):
                node.Body.Insert(0, st)
            node.RemoveAnnotation('locals')

        Visit node.Body

    def OnMethod(node as Method):
    """ Process locals and detect the Main method to move its statements into the Module globals
    """
        # Skip compiler generated methods
        if node.IsSynthetic and node.IsInternal:
            RemoveCurrentNode
            return

        for st in LocalsToDecls(node.Locals):
            node.Body.Insert(0, st)

        # Detect the Main method and move its statements to the Module globals
        if IsEntryPoint(node):
            Visit node.Body
            node.EnclosingModule.Globals = node.Body
            RemoveCurrentNode
            return

        super(node)

    def IsEntryPoint(node as Method):
        # Note: We cannot use ContextAnnotations.GetEntryPoint(Context) to detect
        # the entry point since when emitting the assembly we have to clone the AST
        # and then the node instance is different.
        return node.Name == IntroduceModuleClasses.EntryPointMethodName \
               and node.IsSynthetic and node.IsStatic and node.IsPrivate

    def OnCastExpression(node as CastExpression):
        mie = [| Boo.$(ReferenceExpression('cast'))() |]
        mie.LexicalInfo = node.LexicalInfo
        mie.Arguments.Add(node.Target)
        ReplaceCurrentNode( ProcessCast(mie, node.Type) )

    def OnTryCastExpression(node as TryCastExpression):
        mie = [| Boo.trycast() |]
        mie.LexicalInfo = node.LexicalInfo
        mie.Arguments.Add(node.Target)
        ReplaceCurrentNode( ProcessCast(mie, node.Type) )

    protected def ProcessCast(mie as MethodInvocationExpression, type as Node) as MethodInvocationExpression:
        if not type isa Expression:
            type = TypeofExpression(type.LexicalInfo, type)

        mie.Arguments.Add(type)
        return Visit(mie)

    def OnCharLiteralExpression(node as CharLiteralExpression):
    """ There is no char type in BooJs. char('c') and char(int) are converted to a string of length 1
    """
        ReplaceCurrentNode StringLiteralExpression(LexicalInfo: node.LexicalInfo, Value: node.Value)

    def OnRELiteralExpression(node as RELiteralExpression):
    """ Almost a direct translation to Javascript regexp literals. The only modification
        is that we ignore whitespace to allow the regexps to be somehow more readable while
        Boo's handling is to escape it as part of the match.

            / foo | bar /i  ->  /foo|bar/i
            @/ foo | bar /  ->  /foo|bar/i

        TODO: The ones prefixed with @ should be preprocessed to support new lines
    """
        re = node.Value
        start = re.IndexOf('/')
        stop = re.LastIndexOf('/')

        modifier = re[stop:]
        re = re[start:stop]

        # Ignore white space
        re = /\s+/.Replace(re, '')

        # Javascript does not support the single-line modifier (dot matches new lines).
        # We emulate it by converting dots to the [\s\S] expression in the regex.
        # NOTE: The algorithm will break when a dot is preceeded by an escaped back slash (\\.)
        if modifier.Contains('s'):
            re = /(?<!\\)\./.Replace(re, '[\\s\\S]')
            modifier = modifier.Replace('s', '')

        node.Value = re + modifier

    def OnArrayLiteralExpression(node as ArrayLiteralExpression):
    """ In BooJs arrays are muttable (Boo's Lists)
    """
        super(node)
        list = ListLiteralExpression(node.LexicalInfo, Items: node.Items)
        ReplaceCurrentNode list

    def OnUnlessStatement(node as UnlessStatement):
    """ Negate the condition to convert it into a simple if statement
    """
        ifst = IfStatement(LexicalInfo: node.LexicalInfo)
        ifst.Condition = UnaryExpression(
            Operator: UnaryOperatorType.LogicalNot,
            Operand: node.Condition,
            LexicalInfo: node.LexicalInfo
        )
        ifst.TrueBlock = node.Block

        ReplaceCurrentNode ifst

    def OnBinaryExpression(node as BinaryExpression):
        if node.Operator == BinaryOperatorType.TypeTest:
            mie = MethodInvocationExpression(node.LexicalInfo)
            mie.Target = ReferenceExpression(node.LexicalInfo, 'Boo.isa')
            mie.Arguments.Add(node.Left)
            mie.Arguments.Add(node.Right)
            Visit mie.Arguments
            ReplaceCurrentNode mie
            return

        super(node)

    def OnTypeofExpression(node as TypeofExpression):
        if st = node.Type as SimpleTypeReference:

            # Primitives are just enclosed as strings
            if TypeSystemServices.IsPrimitive(st.Name):
                ReplaceCurrentNode StringLiteralExpression(node.LexicalInfo, st.Name)
                return

            # If we have an entity use it
            if exent = st.Entity as TypeSystem.Reflection.ExternalType:
                refe = CodeBuilder.CreateReference(node.LexicalInfo, exent.ActualType)
                ReplaceCurrentNode ProcessReference(refe)
                return

            # Just rely on the type name
            refe = ReferenceExpression(node.LexicalInfo, st.Name)
            ReplaceCurrentNode ProcessReference(refe)

        elif node.Type isa ArrayTypeReference:

            refe = CodeBuilder.CreateReference(node.LexicalInfo, TypeSystemServices.ArrayType)
            ReplaceCurrentNode ProcessReference(refe)
            return

        else:
            raise 'Unsupported TypeReference: ' + node.Type + ' (' + node.Type.NodeType + ')'

    def OnUnaryExpression(node as UnaryExpression):
        # Logical Not folding
        if node.Operator == UnaryOperatorType.LogicalNot:
            # not(not(expr)) -> expr
            if ue = node.Operand as UnaryExpression and ue.Operator == UnaryOperatorType.LogicalNot:
                ReplaceCurrentNode Visit(ue.Operand)
                return

            if be = node.Operand as BinaryExpression:
                inverses = {
                    BinaryOperatorType.Equality: BinaryOperatorType.Inequality,
                    BinaryOperatorType.Inequality: BinaryOperatorType.Equality,
                    BinaryOperatorType.GreaterThan: BinaryOperatorType.LessThanOrEqual,
                    BinaryOperatorType.LessThanOrEqual: BinaryOperatorType.GreaterThan,
                    BinaryOperatorType.LessThan: BinaryOperatorType.GreaterThanOrEqual,
                    BinaryOperatorType.GreaterThanOrEqual: BinaryOperatorType.LessThan,
                }

                if be.Operator in inverses:
                    be.Operator = inverses[be.Operator]
                    ReplaceCurrentNode Visit(be)
                    return

        super(node)

    def OnBlock(node as Block):

        Visit node.Statements

        # Flatten nested blocks
        ofs = 0
        while ofs < len(node.Statements):
            st = node.Statements[ofs]
            if st.NodeType == NodeType.Block:
                node.Statements.Remove(st)
                for st2 in (st as Block).Statements:
                    node.Statements.Insert(ofs, st2)
                    ofs++
            else:
                ofs++

namespace BooJs.Compiler.Steps

import Boo.Lang.Compiler
import Boo.Lang.Compiler.Ast
import Boo.Lang.Compiler.Steps

import Boo.Lang.Environments
import Boo.Lang.Compiler.TypeSystem(EntityType)
import Boo.Lang.Compiler.TypeSystem.Services.RuntimeMethodCache as BooRuntimeMethodCache
import Boo.Lang.Compiler.TypeSystem.Reflection(ExternalType)
import BooJs.Compiler.TypeSystem(RuntimeMethodCache)
import BooJs.Lang.Extensions

import System.Runtime.CompilerServices(CompilerGeneratedAttribute)

class PrepareAst(AbstractTransformerCompilerStep):
"""
    Prepare the AST for its conversion to Javascript

        - Remove unneeded nodes
        - Convert builtins and runtime references

"""
    [getter(MethodCache)]
    private _methodCache as RuntimeMethodCache

    [getter(BooMethodCache)]
    private _booMethodCache as BooRuntimeMethodCache

    protected def GetAttribute[of T(System.Attribute)](node as Node) as T:
        entity = node.Entity as TypeSystem.IExternalEntity
        return (GetAttribute[of T](entity) if entity else null as T)

    protected def GetAttribute[of T(System.Attribute)](entity as TypeSystem.IExternalEntity) as T:
        return System.Attribute.GetCustomAttribute(entity.MemberInfo, typeof(T))

    protected def IsBuiltin(node as Node) as bool:
        entity = node.Entity as TypeSystem.Reflection.ExternalType
        if not entity and centity = node.Entity as TypeSystem.ExternalMethod:
            entity = centity.DeclaringType

        return false if not entity
        return true if entity.Type is TypeSystemServices.BuiltinsType
        return entity.DeclaringType == TypeSystemServices.BuiltinsType

    protected def IsGlobal(node as Node) as bool:
        if entity = node.Entity as TypeSystem.Reflection.ExternalType:
            return entity.ActualType.Namespace == 'BooJs.Lang.Globals'

        return false

    def Initialize(context as CompilerContext):
        super(context)

        _methodCache = EnvironmentProvision[of RuntimeMethodCache]()
        _booMethodCache = EnvironmentProvision[of BooRuntimeMethodCache]()

    def EnterModule(node as Module):
        if node.Name == 'CompilerGenerated':
            RemoveCurrentNode()
            return false

        return true

    def EnterClassDefinition(node as ClassDefinition):
        if node.Name == 'CompilerGeneratedExtensions':
            RemoveCurrentNode()
            return false

        if GetAttribute[of CompilerGeneratedAttribute](node):
            RemoveCurrentNode()
            return false

        return true

    def OnExpressionStatement(node as ExpressionStatement):
        # Ignore the assignment of locals produced by closures instrumentation
        be = node.Expression as BinaryExpression
        if be and be.Operator == BinaryOperatorType.Assign and be.Left.ToString() == '$locals':
            RemoveCurrentNode
            return

        # Ignore initialization value calls (__initobj__). We do it elsewhere.
        mie = node.Expression as MethodInvocationExpression
        if mie and mie.Target.Entity == TypeSystem.BuiltinFunction.InitValueType:
            RemoveCurrentNode
            return

        super(node)

    protected def ProcessReference(node as ReferenceExpression) as Node:
        if TransformAttribute.HasAttribute(node):
            result = TransformAttribute.Resolve(node, null)
            Visit result
            return result

        # Primitive type references
        if entity = node.Entity as ExternalType and TypeSystemServices.IsLiteralPrimitive(entity):
            return StringLiteralExpression(node.LexicalInfo, entity.FullName)

        # Check for builtins references
        if IsBuiltin(node):
            name = node.Name.Split(char('.'))[-1]
            refe = [| Boo.$(ReferenceExpression(Name: name)) |].withLexicalInfoFrom(node)
            refe.Entity = node.Entity
            return refe

        # Entities in the Global namespace are never prefixed
        if IsGlobal(node):
            node.Name = node.Name.Split(char('.'))[-1]
            return node

        # Members of the module are placed in the top scope
        # TODO: This detection algorithm is very weak! We need to come out with something better
        ientity = node.Entity as TypeSystem.IMember
        if ientity and ientity.DeclaringType and ientity.DeclaringType.IsClass and ientity.DeclaringType.Name == ientity.DeclaringType.FullName:
            # Anything not being a constructor is excluded
            if node.Entity.EntityType != EntityType.Constructor:
                return node

            mre = MemberReferenceExpression(node.LexicalInfo)
            mre.ExpressionType = node.ExpressionType
            mre.Entity = node.Entity
            mre.Target = ReferenceExpression('exports', LexicalInfo: node.LexicalInfo)
            mre.Name = node.Name.Split(char('.'))[-1]
            return mre

        return node

    def OnReferenceExpression(node as ReferenceExpression):
        ReplaceCurrentNode ProcessReference(node)

    def OnMemberReferenceExpression(node as MemberReferenceExpression):
        if TransformAttribute.HasAttribute(node):
            result = TransformAttribute.Resolve(node, null)
            Visit result
            ReplaceCurrentNode result
            return

        # Convert from `$locals.$variable` to `variable`
        if node.Target.NodeType == NodeType.ReferenceExpression:
            if (node.Target as ReferenceExpression).Name == '$locals':
                refexp = ReferenceExpression(node.Name[1:], LexicalInfo: node.LexicalInfo)
                ReplaceCurrentNode refexp
                return

        # Members of the module are placed in the top scope
        # TODO: This detection algorithm is very weak! We need to come out with something better
        ientity = node.Entity as TypeSystem.IMember
        if ientity and ientity.DeclaringType and ientity.DeclaringType.IsClass and ientity.DeclaringType.Name == ientity.DeclaringType.FullName:
            # Anything not being a constructor is excluded
            if node.Entity.EntityType != EntityType.Constructor:
                return
            #refexp = ReferenceExpression(node.Name, LexicalInfo: node.LexicalInfo)
            #ReplaceCurrentNode refexp
            node.Target = ReferenceExpression('exports', LexicalInfo: node.LexicalInfo)
            return

        # Check for builtins references
        if IsBuiltin(node.Target):
            node.Target = [| Boo |].withLexicalInfoFrom(node.Target)

        super(node)

    def OnMethodInvocationExpression(node as MethodInvocationExpression):
        # Convert Transform attribute metadata to an AST annotation
        if TransformAttribute.HasAttribute(node.Target):
            result = TransformAttribute.Resolve(node.Target, node.Arguments)
            result = VisitNode(result)
            ReplaceCurrentNode result
            return

        if GetAttribute[of VarArgsAttribute](node.Target) and not node.ContainsAnnotation('varargs-processed'):
            node['varargs-processed'] = true
            if node.Arguments[-1] isa ListLiteralExpression:
                last = node.Arguments[-1] as ListLiteralExpression
                node.Arguments.Remove(last)
                for itm in last.Items:
                    node.Arguments.Add(itm)
                return

            params = ListLiteralExpression()
            for i in range(0, len(node.Arguments)-1):
                params.Items.Add( node.Arguments[i] )
            mie = [| $(params).concat($(node.Arguments[-1])) |]
            if node.Target isa MemberReferenceExpression:
                target = (node.Target as MemberReferenceExpression).Target
            else:
                target = [| Boo.UNDEF |]
            node = [| $(node.Target).apply($target, $mie) |]
            ReplaceCurrentNode node

        /*
        rts = TypeSystemServices.RuntimeType
        rtsInvoke = ResolveMethod(rts, 'Invoke')
        rtsInvokeCallable = ResolveMethod(rts, 'InvokeCallable')
        rtsInvokeBinaryOperator = ResolveMethod(rts, 'InvokeBinaryOperator')
        rtsInvokeUnaryOperator = ResolveMethod(rts, 'InvokeUnaryOperator')
        rtsSetProperty = ResolveMethod(rts, 'SetProperty')
        rtsGetProperty = ResolveMethod(rts, 'GetProperty')
        rtsSetSlice = ResolveMethod(rts, 'SetSlice')
        rtsGetSlice = ResolveMethod(rts, 'GetSlice')
        */

        if node.Target.Entity == MethodCache.InvokeBinaryOperator:

            # Convert it back into a binary expression
            be = BinaryExpression(node.LexicalInfo)
            be.ExpressionType = node.ExpressionType
            be.Left = node.Arguments[1]
            be.Right = node.Arguments[2]

            # Remove the op_ to find the operator type
            operator = (node.Arguments[0] as StringLiteralExpression).Value[3:]
            be.Operator = System.Enum.Parse(BinaryOperatorType, operator)

            ReplaceCurrentNode Visit(be)
            return

        if node.Target.Entity == MethodCache.InvokeUnaryOperator:
            # Convert it back into a unary expression
            ue = UnaryExpression(node.LexicalInfo)
            ue.ExpressionType = node.ExpressionType
            ue.Operand = node.Arguments[1]

            # Remove the op_ to find the operator type
            operator = (node.Arguments[0] as StringLiteralExpression).Value[3:]
            ue.Operator = System.Enum.Parse(UnaryOperatorType, operator)

            ReplaceCurrentNode Visit(ue)
            return

        super(node)

    protected def LocalsToDecls(locals as LocalCollection):
        # Convert locals back to simple declaration statements
        found = ['$locals']
        for local in locals:
            # Avoid duplicates
            if local.Name is null or local.Name in found:
                continue

            found.Push(local.Name)

            decl = Declaration(LexicalInfo: local.LexicalInfo)
            decl.Name = local.Name

            # Detect local type
            initializer as Expression = NullLiteralExpression()
            entity = local.Entity as TypeSystem.Internal.InternalLocal
            if entity:
                if entity.OriginalDeclaration:
                    # Skip declarations for those flagged as global
                    continue if entity.OriginalDeclaration.ContainsAnnotation('global')
                    decl.Type = entity.OriginalDeclaration.Type
                else:
                    # TODO: Can we generate proper type annotations with this?
                    decl.Type = SimpleTypeReference(Entity: entity)

                # Initialize with a default value
                if TypeSystemServices.IsNumber(entity.Type):
                    initializer = IntegerLiteralExpression(0)
                elif entity.Type == TypeSystemServices.TimeSpanType:
                    initializer = IntegerLiteralExpression(0)
                elif entity.Type == TypeSystemServices.BoolType:
                    initializer = BoolLiteralExpression(false)
                elif entity.Type == TypeSystemServices.StringType:
                    initializer = StringLiteralExpression('')

            yield DeclarationStatement(LexicalInfo: local.LexicalInfo, Declaration: decl, Initializer: initializer)

    def OnBlockExpression(node as BlockExpression):
    """ Convert locals annotation to declarations
    """
        if node.ContainsAnnotation('locals'):
            for st in LocalsToDecls(node['locals']):
                node.Body.Insert(0, st)
            node.RemoveAnnotation('locals')

        Visit node.Body

    def OnMethod(node as Method):
    """ Process locals and detect the Main method to move its statements into the Module globals
    """
        # Skip compiler generated methods
        if node.IsSynthetic and node.IsInternal:
            RemoveCurrentNode
            return

        for st in LocalsToDecls(node.Locals):
            node.Body.Insert(0, st)

        # Detect the Main method and move its statements to the Module globals
        if IsEntryPoint(node):
            Visit node.Body
            node.EnclosingModule.Globals = node.Body
            RemoveCurrentNode
            return

        super(node)

    def IsEntryPoint(node as Method):
        # Note: We cannot use ContextAnnotations.GetEntryPoint(Context) to detect
        # the entry point since when emitting the assembly we have to clone the AST
        # and then the node instance is different.
        return node.Name == IntroduceModuleClasses.EntryPointMethodName \
               and node.IsSynthetic and node.IsStatic and node.IsPrivate

    def OnCastExpression(node as CastExpression):
        mie = [| Boo.$(ReferenceExpression('cast'))() |]
        mie.LexicalInfo = node.LexicalInfo
        mie.Arguments.Add(node.Target)
        ReplaceCurrentNode( ProcessCast(mie, node.Type) )

    def OnTryCastExpression(node as TryCastExpression):
        mie = [| Boo.trycast() |]
        mie.LexicalInfo = node.LexicalInfo
        mie.Arguments.Add(node.Target)
        ReplaceCurrentNode( ProcessCast(mie, node.Type) )

    protected def ProcessCast(mie as MethodInvocationExpression, type as Node) as MethodInvocationExpression:
        if not type isa Expression:
            type = TypeofExpression(type.LexicalInfo, type)

        mie.Arguments.Add(type)
        return Visit(mie)

    def OnCharLiteralExpression(node as CharLiteralExpression):
    """ There is no char type in BooJs. char('c') and char(int) are converted to a string of length 1
    """
        ReplaceCurrentNode StringLiteralExpression(LexicalInfo: node.LexicalInfo, Value: node.Value)

    def OnRELiteralExpression(node as RELiteralExpression):
    """ Almost a direct translation to Javascript regexp literals. The only modification
        is that we ignore whitespace to allow the regexps to be somehow more readable while
        Boo's handling is to escape it as part of the match.

            / foo | bar /i  ->  /foo|bar/i
            @/ foo | bar /  ->  /foo|bar/i

        TODO: The ones prefixed with @ should be preprocessed to support new lines
    """
        re = node.Value
        start = re.IndexOf('/')
        stop = re.LastIndexOf('/')

        modifier = re[stop:]
        re = re[start:stop]

        # Ignore white space
        re = /\s+/.Replace(re, '')

        # Javascript does not support the single-line modifier (dot matches new lines).
        # We emulate it by converting dots to the [\s\S] expression in the regex.
        # NOTE: The algorithm will break when a dot is preceeded by an escaped back slash (\\.)
        if modifier.Contains('s'):
            re = /(?<!\\)\./.Replace(re, '[\\s\\S]')
            modifier = modifier.Replace('s', '')

        node.Value = re + modifier

    def OnArrayLiteralExpression(node as ArrayLiteralExpression):
    """ In BooJs arrays are muttable (Boo's Lists)
    """
        super(node)
        list = ListLiteralExpression(node.LexicalInfo, Items: node.Items)
        ReplaceCurrentNode list

    def OnUnlessStatement(node as UnlessStatement):
    """ Negate the condition to convert it into a simple if statement
    """
        ifst = IfStatement(LexicalInfo: node.LexicalInfo)
        ifst.Condition = UnaryExpression(
            Operator: UnaryOperatorType.LogicalNot,
            Operand: node.Condition,
            LexicalInfo: node.LexicalInfo
        )
        ifst.TrueBlock = node.Block

        ReplaceCurrentNode ifst

    def OnBinaryExpression(node as BinaryExpression):
        if node.Operator == BinaryOperatorType.TypeTest:
            mie = MethodInvocationExpression(node.LexicalInfo)
            mie.Target = ReferenceExpression(node.LexicalInfo, 'Boo.isa')
            mie.Arguments.Add(node.Left)
            mie.Arguments.Add(node.Right)
            Visit mie.Arguments
            ReplaceCurrentNode mie
            return

        super(node)

    def OnTypeofExpression(node as TypeofExpression):
        if st = node.Type as SimpleTypeReference:

            # Primitives are just enclosed as strings
            if TypeSystemServices.IsPrimitive(st.Name):
                ReplaceCurrentNode StringLiteralExpression(node.LexicalInfo, st.Name)
                return

            # If we have an entity use it
            if exent = st.Entity as TypeSystem.Reflection.ExternalType:
                refe = CodeBuilder.CreateReference(node.LexicalInfo, exent.ActualType)
                ReplaceCurrentNode ProcessReference(refe)
                return

            # Just rely on the type name
            refe = ReferenceExpression(node.LexicalInfo, st.Name)
            ReplaceCurrentNode ProcessReference(refe)

        elif node.Type isa ArrayTypeReference:

            refe = CodeBuilder.CreateReference(node.LexicalInfo, TypeSystemServices.ArrayType)
            ReplaceCurrentNode ProcessReference(refe)
            return

        else:
            raise 'Unsupported TypeReference: ' + node.Type + ' (' + node.Type.NodeType + ')'

    def OnUnaryExpression(node as UnaryExpression):
        # Logical Not folding
        if node.Operator == UnaryOperatorType.LogicalNot:
            # not(not(expr)) -> expr
            if ue = node.Operand as UnaryExpression and ue.Operator == UnaryOperatorType.LogicalNot:
                ReplaceCurrentNode Visit(ue.Operand)
                return

            if be = node.Operand as BinaryExpression:
                inverses = {
                    BinaryOperatorType.Equality: BinaryOperatorType.Inequality,
                    BinaryOperatorType.Inequality: BinaryOperatorType.Equality,
                    BinaryOperatorType.GreaterThan: BinaryOperatorType.LessThanOrEqual,
                    BinaryOperatorType.LessThanOrEqual: BinaryOperatorType.GreaterThan,
                    BinaryOperatorType.LessThan: BinaryOperatorType.GreaterThanOrEqual,
                    BinaryOperatorType.GreaterThanOrEqual: BinaryOperatorType.LessThan,
                }

                if be.Operator in inverses:
                    be.Operator = inverses[be.Operator]
                    ReplaceCurrentNode Visit(be)
                    return

        super(node)

    def OnBlock(node as Block):

        Visit node.Statements

        # Flatten nested blocks
        ofs = 0
        while ofs < len(node.Statements):
            st = node.Statements[ofs]
            if st.NodeType == NodeType.Block:
                node.Statements.Remove(st)
                for st2 in (st as Block).Statements:
                    node.Statements.Insert(ofs, st2)
                    ofs++
            else:
                ofs++


Ability to execute unsaved scripts

Ability to execute unsaved scripts

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

namespace BooExplorer

import ICSharpCode.TextEditor
import ICSharpCode.TextEditor.Document
import ICSharpCode.TextEditor.Actions
import WeifenLuo.WinFormsUI
import System
import System.Windows.Forms
import System.Drawing
import Boo.Lang.Compiler
import Boo.Lang.Compiler.IO
import Boo.Lang.Compiler.Pipeline.Definitions

class BooEditor(Content):

	static NewFileName = "untitled.boo"

	_editor as TextEditorControl
	_main as MainForm

	[getter(FileName)]
	_fname as string

	[getter(IsDirty)]
	_dirty = false

	def constructor(main as MainForm):
		_main = main
		_editor = TextEditorControl(Dock: DockStyle.Fill,
							Font: System.Drawing.Font("Lucida Console", 13.0),
							EnableFolding: true)

		_editor.Encoding = System.Text.Encoding.UTF8
		_editor.Document.FormattingStrategy = BooFormattingStrategy()
		_editor.Document.HighlightingStrategy = GetBooHighlighting()
		_editor.Document.DocumentChanged += _editor_DocumentChanged

		SuspendLayout()
		Controls.Add(_editor)
		self.AllowedStates = ContentStates.Document
		self.Text = NewFileName
		self.DockPadding.All = 1
		self.Menu = CreateMenu()
		ResumeLayout(false)

	TextArea:
		get:
			return _editor.ActiveTextAreaControl.TextArea

	TextContent:
		get:
			return _editor.Document.TextContent

	def GoTo(line as int):
		_editor.ActiveTextAreaControl.JumpTo(line, 1)

	def SaveFile():
		if _fname:
			_editor.SaveFile(_fname)
		else:
			dlg = SaveFileDialog(AddExtension: true,
							DefaultExt: ".boo",
							OverwritePrompt: true,
							Filter: "boo files (*.boo)|*.boo")
			if DialogResult.OK == dlg.ShowDialog():
				_editor.SaveFile(dlg.FileName)
				_fname = dlg.FileName

		_dirty = false
		self.Text = _fname

	def LoadFile([required] fname as string):
		_editor.LoadFile(fname)
		self.Text = _fname = fname
		_dirty = false

	def _editor_DocumentChanged(sender, args as DocumentEventArgs):
		if not _dirty:
			self.Text = "${GetSafeFileName()} (modified)"
			_dirty = true

	def _menuItemRun_Click(sender, args as EventArgs):
		savedCursor = Cursor
		self.Cursor = Cursors.WaitCursor

		compiler = BooCompiler()
		compiler.Parameters.Input.Add(StringInput(GetSafeFileName(), self.TextContent))
		compiler.Parameters.Pipeline.Load(BoomPipelineDefinition)

		started = date.Now
		result = compiler.Run()
		finished = date.Now
		_main.StatusText = "Compilation finished in ${finished-started} with ${len(result.Errors)} error(s)."

		if len(result.Errors):
			print(join(result.Errors, "\n"))
		else:
			try:
				result.GeneratedAssemblyEntryPoint.Invoke(null, (null,))
			except x:
				print(x)

		self.Cursor = savedCursor

	def GetSafeFileName():
		if _fname:
			return _fname
		return NewFileName

	def CreateMenu():
		menu = MainMenu()

		script = MenuItem(Text: "&Script")
		script.MenuItems.Add(MenuItem(Text: "Run",
									Click: _menuItemRun_Click,
									Shortcut: Shortcut.F5))

		menu.MenuItems.AddRange((script,))
		return menu

	def GetBooHighlighting():
		return HighlightingManager.Manager.FindHighlighter("boo")

namespace BooExplorer

import ICSharpCode.TextEditor
import ICSharpCode.TextEditor.Document
import ICSharpCode.TextEditor.Actions
import WeifenLuo.WinFormsUI
import System
import System.Windows.Forms
import System.Drawing
import Boo.Lang.Compiler
import Boo.Lang.Compiler.IO
import Boo.Lang.Compiler.Pipeline.Definitions

class BooEditor(Content):

	_editor as TextEditorControl
	_main as MainForm

	[getter(FileName)]
	_fname as string

	[getter(IsDirty)]
	_dirty = false

	def constructor(main as MainForm):
		_main = main
		_editor = TextEditorControl(Dock: DockStyle.Fill,
							Font: System.Drawing.Font("Lucida Console", 13.0),
							EnableFolding: true)

		_editor.Encoding = System.Text.Encoding.UTF8
		_editor.Document.FormattingStrategy = BooFormattingStrategy()
		_editor.Document.HighlightingStrategy = GetBooHighlighting()
		_editor.Document.DocumentChanged += _editor_DocumentChanged

		SuspendLayout()
		Controls.Add(_editor)
		self.AllowedStates = ContentStates.Document
		self.Text = "unnamed.boo"
		self.DockPadding.All = 1
		self.Menu = CreateMenu()
		ResumeLayout(false)

	TextArea:
		get:
			return _editor.ActiveTextAreaControl.TextArea

	TextContent:
		get:
			return _editor.Document.TextContent

	def GoTo(line as int):
		_editor.ActiveTextAreaControl.JumpTo(line, 1)

	def SaveFile():
		if _fname:
			_editor.SaveFile(_fname)
		else:
			dlg = SaveFileDialog(AddExtension: true,
							DefaultExt: ".boo",
							OverwritePrompt: true,
							Filter: "boo files (*.boo)|*.boo")
			if DialogResult.OK == dlg.ShowDialog():
				_editor.SaveFile(dlg.FileName)
				_fname = dlg.FileName

		_dirty = false
		self.Text = _fname

	def LoadFile([required] fname as string):
		_editor.LoadFile(fname)
		self.Text = _fname = fname
		_dirty = false

	def _editor_DocumentChanged(sender, args as DocumentEventArgs):
		if not _dirty:
			self.Text = "${_fname} (modified)"
			_dirty = true

	def _menuItemRun_Click(sender, args as EventArgs):
		savedCursor = Cursor
		self.Cursor = Cursors.WaitCursor

		compiler = BooCompiler()
		compiler.Parameters.Input.Add(StringInput(self.Text, self.TextContent))
		compiler.Parameters.Pipeline.Load(BoomPipelineDefinition)

		started = date.Now
		result = compiler.Run()
		finished = date.Now
		_main.StatusText = "Compilation finished in ${finished-started} with ${len(result.Errors)} error(s)."

		if len(result.Errors):
			print(join(result.Errors, "\n"))
		else:
			try:
				result.GeneratedAssemblyEntryPoint.Invoke(null, (null,))
			except x:
				print(x)

		self.Cursor = savedCursor

	def CreateMenu():
		menu = MainMenu()

		script = MenuItem(Text: "&Script")
		script.MenuItems.Add(MenuItem(Text: "Run",
									Click: _menuItemRun_Click,
									Shortcut: Shortcut.F5))

		menu.MenuItems.AddRange((script,))
		return menu

	def GetBooHighlighting():
		return HighlightingManager.Manager.FindHighlighter("boo")


flag assignment errors to avoid cascading error reports

namespace UnityScript.Steps

import Boo.Lang.Compiler
import Boo.Lang.Compiler.Ast
import Boo.Lang.Compiler.TypeSystem
import Boo.Lang.Compiler.TypeSystem.Internal
import Boo.Lang.Compiler.Steps

import Boo.Lang.Environments

import UnityScript.Core
import UnityScript.Macros
import UnityScript.TypeSystem

class ProcessUnityScriptMethods(ProcessMethodBodiesWithDuckTyping):

	deferred IEnumerable_GetEnumerator = Types.IEnumerable.GetMethod("GetEnumerator");

	deferred IEnumerator_MoveNext = Types.IEnumerator.GetMethod("MoveNext");

	deferred IEnumerator_get_Current = Types.IEnumerator.GetProperty("Current").GetGetMethod();

	deferred _StartCoroutine = NameResolutionService.ResolveMethod(UnityScriptTypeSystem.ScriptBaseType, "StartCoroutine_Auto")

	deferred _UnityRuntimeServices_GetEnumerator = ResolveUnityRuntimeMethod("GetEnumerator")

	deferred _UnityRuntimeServices_Update = ResolveUnityRuntimeMethod("Update")

	deferred _UnityRuntimeServices_GetTypeOf = ResolveUnityRuntimeMethod("GetTypeOf")

	_strict = false

	_implicit = false

	override def Initialize(context as CompilerContext):
		super(context)

		// don't transform
		//     foo == null
		// into:
		//     foo is null
		// but into:
		//     foo.Equals(null)
		self.OptimizeNullComparisons = false

	def ResolveUnityRuntimeMethod(name as string):
		return NameResolutionService.ResolveMethod(UnityRuntimeServicesType, name)

	def ResolveUnityRuntimeField(name as string):
		return NameResolutionService.ResolveField(UnityRuntimeServicesType, name)

	deferred UnityRuntimeServicesType = TypeSystemServices.Map(UnityScript.Lang.UnityRuntimeServices)

	UnityScriptTypeSystem as UnityScript.TypeSystem.UnityScriptTypeSystem:
		get: return self.TypeSystemServices

	UnityScriptParameters as UnityScript.UnityScriptCompilerParameters:
		get: return _context.Parameters

	override def GetGeneratorReturnType(generator as InternalMethod):
		return TypeSystemServices.IEnumeratorType

	override def IsDuckTyped(e as Expression):
		if _strict: return false
		return super(e)

	override def IsDuckTyped(member as IMember):
		if _strict: return false
		return super(member)

	override protected def MemberNotFound(node as MemberReferenceExpression, ns as INamespace):
		if (not _strict) and (UnityScriptParameters.Expando or super.IsDuckTyped(node.Target)):
			BindQuack(node);
			return
		super(node, ns)

	override protected def LocalToReuseFor(d as Declaration):
		if DeclarationAnnotations.ShouldForceNewVariableFor(d):
			AssertUniqueLocal(d)
			return null
		return super(d)

	override def OnModule(module as Module):
		Parameters.Strict = _strict = module.ContainsAnnotation("strict")
		Parameters.Ducky = not _strict
		_implicit = module.ContainsAnnotation("implicit")
		my(UnityDowncastPermissions).Enabled = module.ContainsAnnotation("downcast")
		super(module)

	override def OnMethod(node as Method):
		super(node)
		CheckForEmptyCoroutine(node)
		return if Parameters.OutputType == CompilerOutputType.Library
		CheckEntryPoint(node)

	def CheckForEmptyCoroutine(node as Method):
		if not IsEmptyCoroutine(node):
			return
		node.Body.Add([| return $(EmptyEnumeratorReference) |])

	def IsEmptyCoroutine(node as Method):
		entity as InternalMethod = GetEntity(node)
		return entity.ReturnType is GetGeneratorReturnType(entity) and HasNeitherReturnNorYield(node)

	deferred EmptyEnumeratorReference = CodeBuilder.CreateMemberReference(ResolveUnityRuntimeField("EmptyEnumerator"))

	def CheckEntryPoint(node as Method):
		if not node.IsStatic: return
		if not node.IsPublic: return
		if node.Name != "Main": return
		if GetType(node.ReturnType) is not TypeSystemServices.VoidType: return

		ContextAnnotations.SetEntryPoint(_context, node)

	override def ProcessAutoLocalDeclaration(node as BinaryExpression, reference as ReferenceExpression):
		if (_strict and not _implicit) and not IsCompilerGenerated(reference):
			EmitUnknownIdentifierError(reference, reference.Name)
		else:
			super(node, reference)

	def IsCompilerGenerated(reference as ReferenceExpression):
		return reference.Name.Contains('$')

	override protected def ProcessBuiltinInvocation(function as BuiltinFunction, node as MethodInvocationExpression):
		if function is UnityScriptTypeSystem.UnityScriptEval:
			EvalAnnotation.Mark(_currentMethod.Method)
			BindExpressionType(node, TypeSystemServices.ObjectType)
			return
		if function is UnityScriptTypeSystem.UnityScriptTypeof:
			ProcessTypeofBuiltin(node);
			return
		super(function, node)

	private def ProcessTypeofBuiltin(node as MethodInvocationExpression):
		if node.Arguments.Count != 1:
			Error(node, CompilerError("UCE0001", node.Target.LexicalInfo, "'typeof' takes a single argument.", null))
			return

		type = node.Arguments[0].Entity as IType
		if type is not null:
			node.ParentNode.Replace(node, CodeBuilder.CreateTypeofExpression(type))
			return

		node.Target = CodeBuilder.CreateReference(_UnityRuntimeServices_GetTypeOf)
		BindExpressionType(node, TypeSystemServices.TypeType)

	override protected def ProcessMethodInvocation(node as MethodInvocationExpression, targetEntity as IEntity):
	"""
	Automatically detects coroutine invocations in assignments and as standalone
	expressions and generates StartCoroutine invocations.
	"""
		super(node, targetEntity)

		if not IsPossibleStartCoroutineInvocation(node):
			return

		method as IMethod = targetEntity
		if method is null or method.IsStatic: return

		tss = self.UnityScriptTypeSystem
		if not tss.IsScriptType(method.DeclaringType): return
		if not tss.IsGenerator(method): return

		parentNode = node.ParentNode
		parentNode.Replace(
			node,
			CodeBuilder.CreateMethodInvocation(
				TargetForStartCoroutineInvocation(node, method),
				_StartCoroutine,
				node))

	def TargetForStartCoroutineInvocation(node as MethodInvocationExpression, method as IMethod):
		target = cast(MemberReferenceExpression, node.Target).Target
		if target isa SuperLiteralExpression: // super becomes self for coroutine invocation
			return CodeBuilder.CreateSelfReference(target.LexicalInfo, method.DeclaringType)
		return target.CloneNode()

	override def ProcessStaticallyTypedAssignment(node as BinaryExpression):
		TryToResolveAmbiguousAssignment(node)
		ApplyImplicitArrayConversion(node)
		if ValidateAssignment(node):
			BindExpressionType(node, GetExpressionType(node.Right))
		else:
			Error(node)

	def ApplyImplicitArrayConversion(node as BinaryExpression):
		left = GetExpressionType(node.Left)
		if not left.IsArray: return

		right = GetExpressionType(node.Right)
		if right is not TypeSystemServices.Map(UnityScript.Lang.Array): return

		node.Right = CodeBuilder.CreateCast(left,
						CodeBuilder.CreateMethodInvocation(
							node.Right,
							ResolveMethod(right, "ToBuiltin"),
							CodeBuilder.CreateTypeofExpression(left.ElementType)))

	override def OnForStatement(node as ForStatement):
		assert 1 == len(node.Declarations)
		Visit(node.Iterator)
		if NeedsUpdateableIteration(node):
			ProcessUpdateableIteration(node)
		else:
			ProcessNormalIteration(node)

	def ProcessNormalIteration(node as ForStatement):
		node.Iterator = ProcessIterator(node.Iterator, node.Declarations)
		VisitForStatementBlock(node)

	def ProcessUpdateableIteration(node as ForStatement):
		newIterator = CodeBuilder.CreateMethodInvocation(_UnityRuntimeServices_GetEnumerator, node.Iterator)
		newIterator.LexicalInfo = LexicalInfo(node.Iterator.LexicalInfo)
		node.Iterator = newIterator
		ProcessDeclarationForIterator(node.Declarations[0], TypeSystemServices.ObjectType)
		VisitForStatementBlock(node)
		TransformIteration(node)

	def TransformIteration(node as ForStatement):
		iterator = CodeBuilder.DeclareLocal(
						_currentMethod.Method,
						_context.GetUniqueName("iterator"),
						TypeSystemServices.IEnumeratorType)
		iterator.IsUsed = true
		body = Block(node.LexicalInfo)
		body.Add(
			CodeBuilder.CreateAssignment(
				node.LexicalInfo,
				CodeBuilder.CreateReference(iterator),
				node.Iterator))

		// while __iterator.MoveNext():
		ws = WhileStatement(node.LexicalInfo)
		ws.Condition = CodeBuilder.CreateMethodInvocation(
						CodeBuilder.CreateReference(iterator),
						IEnumerator_MoveNext)

		current = CodeBuilder.CreateMethodInvocation(
							CodeBuilder.CreateReference(iterator),
							IEnumerator_get_Current)

		//	item = __iterator.Current
		loopVariable as InternalLocal = TypeSystemServices.GetEntity(node.Declarations[0])
		ws.Block.Add(
				CodeBuilder.CreateAssignment(
					node.LexicalInfo,
					CodeBuilder.CreateReference(loopVariable),
					current))
		ws.Block.Add(node.Block)

		LoopVariableUpdater(self, _context, iterator, loopVariable).Visit(node)

		body.Add(ws)
		node.ParentNode.Replace(node, body)

	def NeedsUpdateableIteration(node as ForStatement):
		iteratorType = GetExpressionType(node.Iterator)
		if iteratorType.IsArray: return false
		return true

	class LoopVariableUpdater(DepthFirstVisitor):

		_parent as ProcessUnityScriptMethods
		_context as CompilerContext
		_iteratorVariable as IEntity
		_loopVariable as IEntity
		_found as bool

		def constructor(parent as ProcessUnityScriptMethods, context as CompilerContext, iteratorVariable as IEntity, loopVariable as IEntity):
			_parent = parent
			_context = context
			_iteratorVariable = iteratorVariable
			_loopVariable = loopVariable

		override def OnExpressionStatement(node as ExpressionStatement):
			_found = false
			Visit(node.Expression)
			if not _found: return

			parentNode = node.ParentNode

			codeBuilder = _context.CodeBuilder
			block = Block(node.LexicalInfo)
			block.Add(node)
			block.Add(
				codeBuilder.CreateMethodInvocation(
					_parent._UnityRuntimeServices_Update,
					codeBuilder.CreateReference(_iteratorVariable),
					codeBuilder.CreateReference(_loopVariable)))

			parentNode.Replace(node, block)

		override def OnReferenceExpression(node as ReferenceExpression):
			if _found: return

			referent = node.Entity
			_found = referent is _loopVariable

namespace UnityScript.Steps

import Boo.Lang.Compiler
import Boo.Lang.Compiler.Ast
import Boo.Lang.Compiler.TypeSystem
import Boo.Lang.Compiler.TypeSystem.Internal
import Boo.Lang.Compiler.Steps

import Boo.Lang.Environments

import UnityScript.Core
import UnityScript.Macros
import UnityScript.TypeSystem

class ProcessUnityScriptMethods(ProcessMethodBodiesWithDuckTyping):

	deferred IEnumerable_GetEnumerator = Types.IEnumerable.GetMethod("GetEnumerator");

	deferred IEnumerator_MoveNext = Types.IEnumerator.GetMethod("MoveNext");

	deferred IEnumerator_get_Current = Types.IEnumerator.GetProperty("Current").GetGetMethod();

	deferred _StartCoroutine = NameResolutionService.ResolveMethod(UnityScriptTypeSystem.ScriptBaseType, "StartCoroutine_Auto")

	deferred _UnityRuntimeServices_GetEnumerator = ResolveUnityRuntimeMethod("GetEnumerator")

	deferred _UnityRuntimeServices_Update = ResolveUnityRuntimeMethod("Update")

	deferred _UnityRuntimeServices_GetTypeOf = ResolveUnityRuntimeMethod("GetTypeOf")

	_strict = false

	_implicit = false

	override def Initialize(context as CompilerContext):
		super(context)

		// don't transform
		//     foo == null
		// into:
		//     foo is null
		// but into:
		//     foo.Equals(null)
		self.OptimizeNullComparisons = false

	def ResolveUnityRuntimeMethod(name as string):
		return NameResolutionService.ResolveMethod(UnityRuntimeServicesType, name)

	def ResolveUnityRuntimeField(name as string):
		return NameResolutionService.ResolveField(UnityRuntimeServicesType, name)

	deferred UnityRuntimeServicesType = TypeSystemServices.Map(UnityScript.Lang.UnityRuntimeServices)

	UnityScriptTypeSystem as UnityScript.TypeSystem.UnityScriptTypeSystem:
		get: return self.TypeSystemServices

	UnityScriptParameters as UnityScript.UnityScriptCompilerParameters:
		get: return _context.Parameters

	override def GetGeneratorReturnType(generator as InternalMethod):
		return TypeSystemServices.IEnumeratorType

	override def IsDuckTyped(e as Expression):
		if _strict: return false
		return super(e)

	override def IsDuckTyped(member as IMember):
		if _strict: return false
		return super(member)

	override protected def MemberNotFound(node as MemberReferenceExpression, ns as INamespace):
		if (not _strict) and (UnityScriptParameters.Expando or super.IsDuckTyped(node.Target)):
			BindQuack(node);
			return
		super(node, ns)

	override protected def LocalToReuseFor(d as Declaration):
		if DeclarationAnnotations.ShouldForceNewVariableFor(d):
			AssertUniqueLocal(d)
			return null
		return super(d)

	override def OnModule(module as Module):
		Parameters.Strict = _strict = module.ContainsAnnotation("strict")
		Parameters.Ducky = not _strict
		_implicit = module.ContainsAnnotation("implicit")
		my(UnityDowncastPermissions).Enabled = module.ContainsAnnotation("downcast")
		super(module)

	override def OnMethod(node as Method):
		super(node)
		CheckForEmptyCoroutine(node)
		return if Parameters.OutputType == CompilerOutputType.Library
		CheckEntryPoint(node)

	def CheckForEmptyCoroutine(node as Method):
		if not IsEmptyCoroutine(node):
			return
		node.Body.Add([| return $(EmptyEnumeratorReference) |])

	def IsEmptyCoroutine(node as Method):
		entity as InternalMethod = GetEntity(node)
		return entity.ReturnType is GetGeneratorReturnType(entity) and HasNeitherReturnNorYield(node)

	deferred EmptyEnumeratorReference = CodeBuilder.CreateMemberReference(ResolveUnityRuntimeField("EmptyEnumerator"))

	def CheckEntryPoint(node as Method):
		if not node.IsStatic: return
		if not node.IsPublic: return
		if node.Name != "Main": return
		if GetType(node.ReturnType) is not TypeSystemServices.VoidType: return

		ContextAnnotations.SetEntryPoint(_context, node)

	override def ProcessAutoLocalDeclaration(node as BinaryExpression, reference as ReferenceExpression):
		if (_strict and not _implicit) and not IsCompilerGenerated(reference):
			EmitUnknownIdentifierError(reference, reference.Name)
		else:
			super(node, reference)

	def IsCompilerGenerated(reference as ReferenceExpression):
		return reference.Name.Contains('$')

	override protected def ProcessBuiltinInvocation(function as BuiltinFunction, node as MethodInvocationExpression):
		if function is UnityScriptTypeSystem.UnityScriptEval:
			EvalAnnotation.Mark(_currentMethod.Method)
			BindExpressionType(node, TypeSystemServices.ObjectType)
			return
		if function is UnityScriptTypeSystem.UnityScriptTypeof:
			ProcessTypeofBuiltin(node);
			return
		super(function, node)

	private def ProcessTypeofBuiltin(node as MethodInvocationExpression):
		if node.Arguments.Count != 1:
			Error(node, CompilerError("UCE0001", node.Target.LexicalInfo, "'typeof' takes a single argument.", null))
			return

		type = node.Arguments[0].Entity as IType
		if type is not null:
			node.ParentNode.Replace(node, CodeBuilder.CreateTypeofExpression(type))
			return

		node.Target = CodeBuilder.CreateReference(_UnityRuntimeServices_GetTypeOf)
		BindExpressionType(node, TypeSystemServices.TypeType)

	override protected def ProcessMethodInvocation(node as MethodInvocationExpression, targetEntity as IEntity):
	"""
	Automatically detects coroutine invocations in assignments and as standalone
	expressions and generates StartCoroutine invocations.
	"""
		super(node, targetEntity)

		if not IsPossibleStartCoroutineInvocation(node):
			return

		method as IMethod = targetEntity
		if method is null or method.IsStatic: return

		tss = self.UnityScriptTypeSystem
		if not tss.IsScriptType(method.DeclaringType): return
		if not tss.IsGenerator(method): return

		parentNode = node.ParentNode
		parentNode.Replace(
			node,
			CodeBuilder.CreateMethodInvocation(
				TargetForStartCoroutineInvocation(node, method),
				_StartCoroutine,
				node))

	def TargetForStartCoroutineInvocation(node as MethodInvocationExpression, method as IMethod):
		target = cast(MemberReferenceExpression, node.Target).Target
		if target isa SuperLiteralExpression: // super becomes self for coroutine invocation
			return CodeBuilder.CreateSelfReference(target.LexicalInfo, method.DeclaringType)
		return target.CloneNode()

	override def ProcessStaticallyTypedAssignment(node as BinaryExpression):
		TryToResolveAmbiguousAssignment(node)
		ApplyImplicitArrayConversion(node)
		ValidateAssignment(node)
		BindExpressionType(node, GetExpressionType(node.Right))

	def ApplyImplicitArrayConversion(node as BinaryExpression):
		left = GetExpressionType(node.Left)
		if not left.IsArray: return

		right = GetExpressionType(node.Right)
		if right is not TypeSystemServices.Map(UnityScript.Lang.Array): return

		node.Right = CodeBuilder.CreateCast(left,
						CodeBuilder.CreateMethodInvocation(
							node.Right,
							ResolveMethod(right, "ToBuiltin"),
							CodeBuilder.CreateTypeofExpression(left.ElementType)))

	override def OnForStatement(node as ForStatement):
		assert 1 == len(node.Declarations)
		Visit(node.Iterator)
		if NeedsUpdateableIteration(node):
			ProcessUpdateableIteration(node)
		else:
			ProcessNormalIteration(node)

	def ProcessNormalIteration(node as ForStatement):
		node.Iterator = ProcessIterator(node.Iterator, node.Declarations)
		VisitForStatementBlock(node)

	def ProcessUpdateableIteration(node as ForStatement):
		newIterator = CodeBuilder.CreateMethodInvocation(_UnityRuntimeServices_GetEnumerator, node.Iterator)
		newIterator.LexicalInfo = LexicalInfo(node.Iterator.LexicalInfo)
		node.Iterator = newIterator
		ProcessDeclarationForIterator(node.Declarations[0], TypeSystemServices.ObjectType)
		VisitForStatementBlock(node)
		TransformIteration(node)

	def TransformIteration(node as ForStatement):
		iterator = CodeBuilder.DeclareLocal(
						_currentMethod.Method,
						_context.GetUniqueName("iterator"),
						TypeSystemServices.IEnumeratorType)
		iterator.IsUsed = true
		body = Block(node.LexicalInfo)
		body.Add(
			CodeBuilder.CreateAssignment(
				node.LexicalInfo,
				CodeBuilder.CreateReference(iterator),
				node.Iterator))

		// while __iterator.MoveNext():
		ws = WhileStatement(node.LexicalInfo)
		ws.Condition = CodeBuilder.CreateMethodInvocation(
						CodeBuilder.CreateReference(iterator),
						IEnumerator_MoveNext)

		current = CodeBuilder.CreateMethodInvocation(
							CodeBuilder.CreateReference(iterator),
							IEnumerator_get_Current)

		//	item = __iterator.Current
		loopVariable as InternalLocal = TypeSystemServices.GetEntity(node.Declarations[0])
		ws.Block.Add(
				CodeBuilder.CreateAssignment(
					node.LexicalInfo,
					CodeBuilder.CreateReference(loopVariable),
					current))
		ws.Block.Add(node.Block)

		LoopVariableUpdater(self, _context, iterator, loopVariable).Visit(node)

		body.Add(ws)
		node.ParentNode.Replace(node, body)

	def NeedsUpdateableIteration(node as ForStatement):
		iteratorType = GetExpressionType(node.Iterator)
		if iteratorType.IsArray: return false
		return true

	class LoopVariableUpdater(DepthFirstVisitor):

		_parent as ProcessUnityScriptMethods
		_context as CompilerContext
		_iteratorVariable as IEntity
		_loopVariable as IEntity
		_found as bool

		def constructor(parent as ProcessUnityScriptMethods, context as CompilerContext, iteratorVariable as IEntity, loopVariable as IEntity):
			_parent = parent
			_context = context
			_iteratorVariable = iteratorVariable
			_loopVariable = loopVariable

		override def OnExpressionStatement(node as ExpressionStatement):
			_found = false
			Visit(node.Expression)
			if not _found: return

			parentNode = node.ParentNode

			codeBuilder = _context.CodeBuilder
			block = Block(node.LexicalInfo)
			block.Add(node)
			block.Add(
				codeBuilder.CreateMethodInvocation(
					_parent._UnityRuntimeServices_Update,
					codeBuilder.CreateReference(_iteratorVariable),
					codeBuilder.CreateReference(_loopVariable)))

			parentNode.Replace(node, block)

		override def OnReferenceExpression(node as ReferenceExpression):
			if _found: return

			referent = node.Entity
			_found = referent is _loopVariable


Use enumerator item type attribute for Hash

namespace BooJs.Lang

import BooJs.Lang.Globals
import BooJs.Lang.Extensions

class Builtins:

    [Transform( '%%COMPILER_VERSION%%' )]
    static public final BOO_COMPILER_VERSION = '%%COMPILER_VERSION%%'
    static public final BOO_RUNTIME_VERSION = 'defined in boo.js'

    static public final STOP = 'STOP'

    class Duck(Object, Boo.Lang.IQuackFu):
    """ Implementes the IQuackFu interface
    """
        def QuackGet(name as string, params as (object)) as object:
            pass

        def QuackSet(name as string, params as (object), value as object) as object:
            pass

        def QuackInvoke(name as string, args as (object)) as object:
            pass

    interface ICallable:
    """ Custom ICallable interface using our Object type
    """
        def Call(args as (Object)) as Object

    [EnumeratorItemType(typeof(Object))]
    class Hash(Object, Iterable):
    """ Simple hash/dictionary based on Javascript's object
    """
        [Transform( $2.hasOwnProperty($1) )]
        static def op_Member(lhs as object, rhs as Hash) as bool:
            pass
        [Transform( not $2.hasOwnProperty($1) )]
        static def op_NotMember(lhs as object, rhs as Hash) as bool:
            pass

        self[key as object] as object:
            [Transform( $0[$1] )]
            get: pass
            [Transform( $0[$1] = $2 )]
            set: pass

        [Transform( Boo.Hash() )]
        def constructor():
            pass
        [Transform( Boo.Hash($1) )]
        def constructor(items as Iterable):
            pass

        # Use a transform to ensure we can use it with plain object literals too
        [Transform( Boo.Hash.keys($0) )]
        def keys() as (string):
            pass
        [Transform( Boo.Hash.values($0) )]
        def values() as (object):
            pass
        [Transform( Boo.enumerate($0) )]
        def items() as ((object)):
            pass

    class AssertionError(Error):
    """ BooJs specific error to signal failures in assertions
    """
        def constructor():
            pass
        def constructor(msg as string):
            pass

    class ReturnValue:
        public value as object

        def constructor(val as object):
            value = val

    static def range(stop as int) as (int):
        pass
    static def range(start as int, stop as int) as (int):
        pass
    static def range(start as int, stop as int, step as int) as (int):
        pass
    [Transform( Boo.range($1) )]
    static def xrange(stop as int) as Iterable[of int]:
        pass
    [Transform( Boo.range($1, $2) )]
    static def xrange(start as int, stop as int) as Iterable[of int]:
        pass
    [Transform( Boo.range($1, $2, $3) )]
    static def xrange(start as int, stop as int, step as int) as Iterable[of int]:
        pass

    static def print(*list as (object)) as void:
        pass

    #static def cat(*list as (object)) as (object):
    static def cat(*list as (object)) as Array:
    """ Concatenates the given enumerable arguments """
        pass

    static def join[of T](items as Iterable[of T], separator as String) as String:
        pass
    static def join[of T](items as Iterable[of T]) as String:
        pass
    static def join(items as Array, separator as String) as String:
        pass
    static def join(items as Array) as String:
        pass

    #static def map(items as object*, callback as callable(object)) as object*:
    static def map(items as Array, callback as callable(object)) as Array:
        pass

    static def reduce[of T](items as (T), callback as callable) as T:
        pass
    static def reduce[of T](items as (T), callback as callable, initial as T) as T:
        pass

    static def filter(items as (object), callback as callable) as (object):
        pass

    static def zip(*arrays as (object)) as (object):
        pass

    static def reversed(items as String) as Array:
        pass
    static def reversed(items as Array) as Array:
        pass
    static def reversed[of T](items as Iterable[of T]) as Array[of T]:
        pass

    [TypeInferenceRule(TypeInferenceRules.ArrayOfTypeReferencedByFirstArgument)]
    static def array(type as System.Type, enumerable as Iterable) as Array:
        pass
    [TypeInferenceRule(TypeInferenceRules.ArrayOfTypeReferencedByFirstArgument)]
    static def array(type as System.Type, num as int) as Array:
        pass
    [TypeInferenceRule(TypeInferenceRules.TypeOfFirstArgument)]
    static def array(enumerable as Iterable) as (object):
        pass
    static def array[of T](enumerable as Iterable[of T]) as (T):
        pass

    # Allows to iterate accesing indices/keys
    static def enumerate(enumerable as Iterable) as ((object)):
        pass

namespace BooJs.Lang

import BooJs.Lang.Globals
import BooJs.Lang.Extensions

class Builtins:

    [Transform( '%%COMPILER_VERSION%%' )]
    static public final BOO_COMPILER_VERSION = '%%COMPILER_VERSION%%'
    static public final BOO_RUNTIME_VERSION = 'defined in boo.js'

    static public final STOP = 'STOP'

    class Duck(Object, Boo.Lang.IQuackFu):
    """ Implementes the IQuackFu interface
    """
        def QuackGet(name as string, params as (object)) as object:
            pass

        def QuackSet(name as string, params as (object), value as object) as object:
            pass

        def QuackInvoke(name as string, args as (object)) as object:
            pass

    interface ICallable:
    """ Custom ICallable interface using our Object type
    """
        def Call(args as (Object)) as Object

    class Hash(Object, Iterable):
    """ Simple hash/dictionary based on Javascript's object
    """
        [Transform( $2.hasOwnProperty($1) )]
        static def op_Member(lhs as object, rhs as Hash) as bool:
            pass
        [Transform( not $2.hasOwnProperty($1) )]
        static def op_NotMember(lhs as object, rhs as Hash) as bool:
            pass

        self[key as object] as object:
            [Transform( $0[$1] )]
            get: pass
            [Transform( $0[$1] = $2 )]
            set: pass

        [Transform( Boo.Hash() )]
        def constructor():
            pass
        [Transform( Boo.Hash($1) )]
        def constructor(items as Iterable):
            pass

        # Use a transform to ensure we can use it with plain object literals too
        [Transform( Boo.Hash.keys($0) )]
        def keys() as (string):
            pass
        [Transform( Boo.Hash.values($0) )]
        def values() as (object):
            pass
        [Transform( Boo.enumerate($0) )]
        def items() as ((object)):
            pass

    class AssertionError(Error):
    """ BooJs specific error to signal failures in assertions
    """
        def constructor():
            pass
        def constructor(msg as string):
            pass

    class ReturnValue:
        public value as object

        def constructor(val as object):
            value = val

    static def range(stop as int) as (int):
        pass
    static def range(start as int, stop as int) as (int):
        pass
    static def range(start as int, stop as int, step as int) as (int):
        pass
    [Transform( Boo.range($1) )]
    static def xrange(stop as int) as Iterable[of int]:
        pass
    [Transform( Boo.range($1, $2) )]
    static def xrange(start as int, stop as int) as Iterable[of int]:
        pass
    [Transform( Boo.range($1, $2, $3) )]
    static def xrange(start as int, stop as int, step as int) as Iterable[of int]:
        pass

    static def print(*list as (object)) as void:
        pass

    #static def cat(*list as (object)) as (object):
    static def cat(*list as (object)) as Array:
    """ Concatenates the given enumerable arguments """
        pass

    static def join[of T](items as Iterable[of T], separator as String) as String:
        pass
    static def join[of T](items as Iterable[of T]) as String:
        pass
    static def join(items as Array, separator as String) as String:
        pass
    static def join(items as Array) as String:
        pass

    #static def map(items as object*, callback as callable(object)) as object*:
    static def map(items as Array, callback as callable(object)) as Array:
        pass

    static def reduce[of T](items as (T), callback as callable) as T:
        pass
    static def reduce[of T](items as (T), callback as callable, initial as T) as T:
        pass

    static def filter(items as (object), callback as callable) as (object):
        pass

    static def zip(*arrays as (object)) as (object):
        pass

    static def reversed(items as String) as Array:
        pass
    static def reversed(items as Array) as Array:
        pass
    static def reversed[of T](items as Iterable[of T]) as Array[of T]:
        pass

    [TypeInferenceRule(TypeInferenceRules.ArrayOfTypeReferencedByFirstArgument)]
    static def array(type as System.Type, enumerable as Iterable) as Array:
        pass
    [TypeInferenceRule(TypeInferenceRules.ArrayOfTypeReferencedByFirstArgument)]
    static def array(type as System.Type, num as int) as Array:
        pass
    [TypeInferenceRule(TypeInferenceRules.TypeOfFirstArgument)]
    static def array(enumerable as Iterable) as (object):
        pass
    static def array[of T](enumerable as Iterable[of T]) as (T):
        pass

    # Allows to iterate accesing indices/keys
    static def enumerate(enumerable as Iterable) as ((object)):
        pass


Removed deprectated file

Removed deprectated file

namespace boojs.Tests.Hints

import NUnit.Framework
import boojs.Hints
import boojs.Hints.Messages(Query)
import Boo.Lang.Interpreter.Builtins(repr)

def code(code as string):
	lines = code.Replace('\t', '    ').Split(char('\n'))
	lines = lines[1:-1]
	indent = 1000
	for line in lines:
		if len(line.Trim()):
			indent = System.Math.Min(indent, len(/^\s*/.Match(line).Value))
	code = join([line[indent:] for line in lines], '\n')
	return code

[TestFixture]
class LocalsTests:

	index as ProjectIndex
	commands as Commands

	[SetUp]
	def setUp():
		index = ProjectIndex.Boo()
		commands = Commands(index)

	[Test]
	def LocalsForEmptyMethod():
		q = Query(fname:'test.boo', line:2, code:'namespace foo\ndef foo():\n\t\n\treturn false\n')
		resp = commands.locals(q)
		Assert.AreEqual(resp.scope, '')
		Assert.IsEmpty(resp.hints)

	[Test]
	def LocalsForMethod():
		q = Query(fname:'test.boo', line:2, code:'namespace foo\ndef foo():\n\tx = 10\n\ty = 20\n\tz = 30\n\treturn false\n')
		resp = commands.locals(q)
		Assert.AreEqual('', resp.scope)
		Assert.IsEmpty(resp.hints)

		q.line = 3
		resp = commands.locals(q)
		assert len(resp.hints) == 1
		assert resp.hints[-1].name == 'x'

		q.line = 4
		resp = commands.locals(q)
		assert len(resp.hints) == 2
		assert resp.hints[-1].name == 'y'

		q.line = 5
		resp = commands.locals(q)
		assert len(resp.hints) == 3
		assert resp.hints[-1].name == 'z'

	[Test]
	def LocalsForMethodParams():
		q = Query(fname:'test.boo', line:3, code:'namespace foo\ndef foo(x, y):\n\tx = 10\n\tz = 20\n')

		resp = commands.locals(q)
		assert len(resp.hints) == 2
		assert resp.hints[0].name == 'x'
		assert resp.hints[1].name == 'y'

		q.line = 4
		resp = commands.locals(q)
		assert len(resp.hints) == 3
		assert resp.hints[-1].name == 'z'

	[Test]
	def LocalsForClosure():
		q = Query(fname:'test.boo', code:'a = 10\ndef foo(x, y):\n\tz=20\n\treturn z')

		q.line = 1
		resp = commands.locals(q)
		assert len(resp.hints) == 2
		assert resp.hints[0].name == 'argv'
		assert resp.hints[1].name == 'a'

		q.line = 4
		resp = commands.locals(q)
		assert len(resp.hints) == 6
		assert resp.hints[2].name == 'foo'
		assert resp.hints[3].name == 'x'
		assert resp.hints[4].name == 'y'
		assert resp.hints[5].name == 'z'

	[Test]
	def LocalsForBlock():
		q = Query(fname:'test.boo', code:'def foo(x, y):\n\tbar() do (z):\n\t\treturn z\n\treturn true\n')

		q.line = 1
		resp = commands.locals(q)
		assert len(resp.hints) == 2
		assert resp.hints[-1].name == 'y'

		q.line = 2
		resp = commands.locals(q)
		assert len(resp.hints) == 3
		assert resp.hints[-1].name == 'z'

[TestFixture]
class ParseTests:

	index as ProjectIndex
	commands as Commands

	[SetUp]
	def setUp():
		index = ProjectIndex.Boo()
		commands = Commands(index)

	[Test]
	def ParseEmpty():
		q = Query(fname:'parse.boo', code:'')
		resp = commands.parse(q)
		assert len(resp.errors) == 0
		assert len(resp.warnings) == 0

	[Test]
	def ParseSyntaxError():
		q = Query(fname:'parse.boo', code:code("""
			namespace Foo.Bar

			print!
		"""))
		resp = commands.parse(q)
		assert len(resp.errors) == 1
		err = resp.errors[0]
		assert err.code == 'BCE0044'
		assert err.line == 3
		assert err.column == 6

	[Test]
	def ParseWarning():
		q = Query(fname:'parse.boo', extra:true, code:code("""
			if a=10:
				pass
		"""))
		resp = commands.parse(q)
		assert len(resp.warnings) == 1
		warn = resp.warnings[0]
		assert warn.code == 'BCW0007'
		assert warn.line == 1
		assert warn.column == 5

[TestFixture]
class NamespaceTests:

	index as ProjectIndex
	commands as Commands

	[SetUp]
	def setUp():
		index = ProjectIndex.Boo()
		commands = Commands(index)

	[Test]
	def CompleteRootNamespace():
		src = code("""
			import |
		""")
		q = Query(fname:'ns.boo', code:src, offset:src.IndexOf('|'))
		resp = commands.complete(q)
		assert len(resp.hints) > 0
		assert resp.scope == 'import'
		assert resp.hints.Contains({ h as duck | h.name == 'System' })

	[Test]
	def CompleteNamespace():
		src = code("""
			import System.|
		""")
		q = Query(fname:'ns.boo', code:src, offset:src.IndexOf('|'))
		resp = commands.complete(q)
		assert len(resp.hints) > 0
		assert resp.scope == 'import'
		assert resp.hints.Contains({ h as duck | h.name == 'IO' })
		assert resp.hints.Contains({ h as duck | h.name == 'Math' })
		assert resp.hints.Contains({ h as duck | h.name == 'Reflection' })

	[Test]
	def CompleteTypes():
		src = code("""
			import System.IO.|
		""")
		q = Query(fname:'ns.boo', code:src, offset:src.IndexOf('|'))
		resp = commands.complete(q)
		assert len(resp.hints) > 0
		assert resp.scope == 'import'
		assert resp.hints.Contains({ h as duck | h.name == 'File' })

[TestFixture]
class InternalEntityTests:

	index as ProjectIndex
	commands as Commands

	[SetUp]
	def setUp():
		index = ProjectIndex.Boo()
		commands = Commands(index)

	[Test]
	def CompleteProperty():
		src = code("""
			class Foo:
				property field as int
				def bar():
					|
		""")
		q = Query(fname:'ientity.boo', code:src, offset:src.IndexOf('|'))
		resp = commands.complete(q)
		assert len(resp.hints) > 0
		assert resp.hints.Contains({ h as duck | h.name == 'field' })
		assert resp.hints.Contains({ h as duck | h.name == 'bar' })

	[Test]
	def CompleteMethod():
		src = code("""
			class Foo:
				def foo():
					pass
				def bar():
					|
		""")
		q = Query(fname:'ientity.boo', code:src, offset:src.IndexOf('|'))
		resp = commands.complete(q)
		assert len(resp.hints) > 0
		assert resp.hints.Contains({ h as duck | h.name == 'foo' })
		assert resp.hints.Contains({ h as duck | h.name == 'bar' })

	[Test]
	def CompletePrivateField():
		src = code("""
			class Foo:
				private _field as int
				def foo():
					|
		""")
		q = Query(fname:'ientity.boo', code:src, offset:src.IndexOf('|'))
		resp = commands.complete(q)
		assert len(resp.hints) > 0
		assert resp.hints.Contains({ h as duck | h.name == '_field' })
		assert resp.hints.Contains({ h as duck | h.name == 'foo' })

	[Test]
	def CompleteInheritedProtected():
		src = code("""
			class Foo:
				protected def foo():
					pass

			class Bar(Foo):
				def bar():
					|
		""")
		q = Query(fname:'ientity.boo', code:src, offset:src.IndexOf('|'))
		resp = commands.complete(q)
		assert len(resp.hints) > 0
		assert resp.hints.Contains({ h as duck | h.name == 'foo' })
		assert resp.hints.Contains({ h as duck | h.name == 'bar' })

	[Test]
	def CompleteInstance():
		src = code("""
			class Foo:
				property prop as int
				def foo():
					pass

			foo = Foo()
			foo.|
		""")
		q = Query(fname:'ientity.boo', code:src, offset:src.IndexOf('|'))
		resp = commands.complete(q)
		assert len(resp.hints) > 0
		assert resp.hints.Contains({ h as duck | h.name == 'prop' })
		assert resp.hints.Contains({ h as duck | h.name == 'foo' })

	[Test]
	def CompleteSelf():
		src = code("""
			class Foo:
				field as int
				def foo():
					self.|
		""")
		q = Query(fname:'ientity.boo', code:src, offset:src.IndexOf('|'))
		resp = commands.complete(q)
		assert len(resp.hints) > 0
		assert resp.hints.Contains({ h as duck | h.name == 'field' })
		assert resp.hints.Contains({ h as duck | h.name == 'foo' })

	[Test]
	def CompleteType():
		src = code("""
			class Foo:
				pass

			|
		""")
		q = Query(fname:'ientity.boo', code:src, offset:src.IndexOf('|'))
		resp = commands.complete(q)
		assert len(resp.hints) > 0
		assert resp.hints.Contains({ h as duck | h.name == 'Foo' })

	[Test]
	def CompleteTypeNoGlobals():
		src = code("""
			class Foo:
				pass

			|
		""")
		q = Query(fname:'ientity.boo', code:src, offset:src.IndexOf('|'), params:(of object: true,))
		resp = commands.complete(q)
		assert not resp.hints.Contains({ h as duck | h.name == 'Foo' })

	[Test]
	def CompleteStatic():
		src = code("""
			class Foo:
				static def foo():
					pass

			Foo.|
		""")
		q = Query(fname:'ientity.boo', code:src, offset:src.IndexOf('|'))
		resp = commands.complete(q)
		assert len(resp.hints) > 0
		assert resp.hints.Contains({ h as duck | h.name == 'foo' })


compiler can't infer type for the elements returned by the zip builtin

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

def fibonacci():
	a, b = 0, 1
	while true:
		yield b
		a, b = b, a+b

for index as int, element in zip(range(5), fibonacci()):
	print("${index+1}: ${element}")

def fibonacci():
	a, b = 0, 1
	while true:
		yield b
		a, b = b, a+b

for index, element in zip(range(5), fibonacci()):
	print("${index+1}: ${element}")


in preparation for the 0.9.1 release when we start the strong assembly versioning policy it's no longer safe to depend on an earlier version of Boo.Lang.PatternMatching

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

#region license
// Copyright (c) 2003, 2004, 2005 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.PatternMatching.Impl

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

class PatternExpander:

	private static final Dummy = '_'

	def Expand(matchValue as Expression, pattern as Expression) as Expression:
		if pattern isa MethodInvocationExpression:
			return ExpandObjectPattern(matchValue, pattern)

		if pattern isa MemberReferenceExpression:
			return ExpandValuePattern(matchValue, pattern)

		if pattern isa ReferenceExpression:
			return ExpandBindPattern(matchValue, pattern)

		if pattern isa QuasiquoteExpression:
			return ExpandQuasiquotePattern(matchValue, pattern)

		binary = pattern as BinaryExpression
		if binary is not null:
			if BinaryOperatorType.Assign == binary.Operator:
				return ExpandCapturePattern(matchValue, pattern)

			if BinaryOperatorType.BitwiseOr == binary.Operator:
				return ExpandEitherPattern(matchValue, pattern)

		if pattern isa ArrayLiteralExpression:
			return ExpandFixedSizePattern(matchValue, pattern)

		return ExpandValuePattern(matchValue, pattern)

	def ExpandEitherPattern(matchValue as Expression, node as BinaryExpression) as Expression:
		l = Expand(matchValue, node.Left)
		r = Expand(matchValue, node.Right)
		return [| $l or $r |]

	def ExpandBindPattern(matchValue as Expression, node as ReferenceExpression):
		return [| __eval__($node = $matchValue, true) |]

	def ExpandValuePattern(matchValue as Expression, node as Expression):
		return [| $matchValue == $node |]

	def ExpandCapturePattern(matchValue as Expression, node as BinaryExpression):
		return ExpandObjectPattern(matchValue, node.Left, node.Right)

	def ExpandObjectPattern(matchValue as Expression, node as MethodInvocationExpression) as Expression:
		if len(node.NamedArguments) == 0 and len(node.Arguments) == 0:
			return [| $matchValue isa $(TypeRefFrom(node)) |]
		return ExpandObjectPattern(matchValue, NewTemp(node), node)

	def ExpandObjectPattern(matchValue as Expression, temp as ReferenceExpression, node as MethodInvocationExpression) as Expression:

		condition = [| ($matchValue isa $(TypeRefFrom(node))) and __eval__($temp = cast($(TypeRefFrom(node)), $matchValue), true) |]
		condition.LexicalInfo = node.LexicalInfo

		for member in node.Arguments:
			assert member isa ReferenceExpression, "Invalid argument '${member}' in pattern '${node}'."
			memberRef = MemberReferenceExpression(member.LexicalInfo, temp.CloneNode(), member.ToString())
			condition = [| $condition and __eval__($member = $memberRef, true) |]

		for member in node.NamedArguments:
			namedArgCondition = ExpandMemberPattern(temp.CloneNode(), member)
			condition = [| $condition and $namedArgCondition |]

		return condition

	class QuasiquotePatternBuilder(DepthFirstVisitor):

		static final Ast = [| Boo.Lang.Compiler.Ast |]

		_parent as PatternExpander
		_pattern as Expression

		def constructor(parent as PatternExpander):
			_parent = parent

		def Build(node as QuasiquoteExpression):
			return Expand(node.Node)

		def Expand(node as Node):
			node.Accept(self)
			expansion = _pattern
			_pattern = null
			assert expansion is not null, "Unsupported pattern '${node}'"
			return expansion

		def Push(srcNode as Node, e as Expression):
			assert _pattern is null
			e.LexicalInfo = srcNode.LexicalInfo
			_pattern = e

		override def OnSpliceExpression(node as SpliceExpression):
			_pattern = node.Expression

		override def OnSpliceTypeReference(node as SpliceTypeReference):
			_pattern = node.Expression

		def ExpandFixedSize(items):
			a = [| (,) |]
			for item in items:
				a.Items.Add(Expand(item))
			return a

		override def OnOmittedExpression(node as OmittedExpression):
			_pattern = [| $Ast.OmittedExpression.Instance |]

		override def OnSlice(node as Slice):
			ctor = [| $Ast.Slice() |]
			ExpandProperty ctor, "Begin", node.Begin
			ExpandProperty ctor, "End", node.End
			ExpandProperty ctor, "Step", node.Step
			Push node, ctor

		def ExpandProperty(ctor as MethodInvocationExpression, name as string, value as Expression):
			if value is null: return
			ctor.NamedArguments.Add(ExpressionPair(First: ReferenceExpression(name), Second: Expand(value)))

		override def OnMacroStatement(node as MacroStatement):
			if len(node.Arguments) > 0:
				Push node, [| $Ast.MacroStatement(Name: $(node.Name), Arguments: $(ExpandFixedSize(node.Arguments))) |]
			else:
				Push node, [| $Ast.MacroStatement(Name: $(node.Name)) |]

		override def OnSlicingExpression(node as SlicingExpression):
			Push node, [| $Ast.SlicingExpression(Target: $(Expand(node.Target)), Indices: $(ExpandFixedSize(node.Indices))) |]

		override def OnTryCastExpression(node as TryCastExpression):
			Push node, [| $Ast.TryCastExpression(Target: $(Expand(node.Target)), Type: $(Expand(node.Type))) |]

		override def OnMethodInvocationExpression(node as MethodInvocationExpression):
			if len(node.Arguments) > 0:
				pattern = [| $Ast.MethodInvocationExpression(Target: $(Expand(node.Target)), Arguments: $(ExpandFixedSize(node.Arguments))) |]
			else:
				pattern = [| $Ast.MethodInvocationExpression(Target: $(Expand(node.Target))) |]
			Push node, pattern

		override def OnBoolLiteralExpression(node as BoolLiteralExpression):
			Push node, [| $Ast.BoolLiteralExpression(Value: $node) |]

		override def OnNullLiteralExpression(node as NullLiteralExpression):
			Push node, [| $Ast.NullLiteralExpression() |]

		override def OnUnaryExpression(node as UnaryExpression):
			Push node, [| $Ast.UnaryExpression(Operator: UnaryOperatorType.$(node.Operator.ToString()), Operand: $(Expand(node.Operand))) |]

		override def OnBinaryExpression(node as BinaryExpression):
			Push node, [| $Ast.BinaryExpression(Operator: BinaryOperatorType.$(node.Operator.ToString()), Left: $(Expand(node.Left)), Right: $(Expand(node.Right))) |]

		override def OnReferenceExpression(node as ReferenceExpression):
			Push node, [| $Ast.ReferenceExpression(Name: $(node.Name)) |]

		override def OnSuperLiteralExpression(node as SuperLiteralExpression):
			Push node, [| $Ast.SuperLiteralExpression() |]

	def ObjectPatternFor(node as QuasiquoteExpression):
		return QuasiquotePatternBuilder(self).Build(node)

	def ExpandQuasiquotePattern(matchValue as Expression, node as QuasiquoteExpression) as Expression:
		return ExpandObjectPattern(matchValue, ObjectPatternFor(node))

	def ExpandMemberPattern(matchValue as Expression, member as ExpressionPair):
		memberRef = MemberReferenceExpression(member.First.LexicalInfo, matchValue, member.First.ToString())
		return Expand(memberRef, member.Second)

	def ExpandFixedSizePattern(matchValue as Expression, pattern as ArrayLiteralExpression):
		patternLen = len(pattern.Items)
		condition = [| $(patternLen) == len($matchValue) |]

		if IsCatchAllPattern(last = pattern.Items[patternLen-1]):
			pattern.Items.Remove(last)
			condition = [| $(patternLen) <= len($matchValue)+1 |]

		i = 0
		for item in pattern.Items:
			itemValue = [| $matchValue[$i] |]
			itemPattern = Expand(itemValue, item)
			condition = [| $condition and $itemPattern |]
			++i
		return condition

	def TypeRefFrom(node as MethodInvocationExpression):
		return node.Target

	def IsCatchAllPattern(pattern as Expression) as bool:
		last = pattern as UnaryExpression
		if AstUtil.IsExplodeExpression(last):
			last_re = last.Operand as ReferenceExpression
			return last_re and last_re.Name == Dummy
		return false

internal def NewTemp(e as Expression):
	return ReferenceExpression(
			LexicalInfo: e.LexicalInfo,
			Name: "$match${CompilerContext.Current.AllocIndex()}")

#region license
// Copyright (c) 2003, 2004, 2005 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.PatternMatching.Impl

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

# import the pre compiled version of the match macro
import Boo.Lang.PatternMatching from Boo.Lang.PatternMatching

class PatternExpander:

	private static final Dummy = '_'

	def Expand(matchValue as Expression, pattern as Expression) as Expression:
		match pattern:
			case MethodInvocationExpression():
				return ExpandObjectPattern(matchValue, pattern)

			case MemberReferenceExpression():
				return ExpandValuePattern(matchValue, pattern)

			case ReferenceExpression():
				return ExpandBindPattern(matchValue, pattern)

			case QuasiquoteExpression():
				return ExpandQuasiquotePattern(matchValue, pattern)

			case [| $l = $r |]:
				return ExpandCapturePattern(matchValue, pattern)

			case [| $l | $r |]:
				return ExpandEitherPattern(matchValue, pattern)

			case ArrayLiteralExpression():
				return ExpandFixedSizePattern(matchValue, pattern)

			otherwise:
				return ExpandValuePattern(matchValue, pattern)

	def ExpandEitherPattern(matchValue as Expression, node as BinaryExpression) as Expression:
		l = Expand(matchValue, node.Left)
		r = Expand(matchValue, node.Right)
		return [| $l or $r |]

	def ExpandBindPattern(matchValue as Expression, node as ReferenceExpression):
		return [| __eval__($node = $matchValue, true) |]

	def ExpandValuePattern(matchValue as Expression, node as Expression):
		return [| $matchValue == $node |]

	def ExpandCapturePattern(matchValue as Expression, node as BinaryExpression):
		return ExpandObjectPattern(matchValue, node.Left, node.Right)

	def ExpandObjectPattern(matchValue as Expression, node as MethodInvocationExpression) as Expression:
		if len(node.NamedArguments) == 0 and len(node.Arguments) == 0:
			return [| $matchValue isa $(TypeRefFrom(node)) |]
		return ExpandObjectPattern(matchValue, NewTemp(node), node)

	def ExpandObjectPattern(matchValue as Expression, temp as ReferenceExpression, node as MethodInvocationExpression) as Expression:

		condition = [| ($matchValue isa $(TypeRefFrom(node))) and __eval__($temp = cast($(TypeRefFrom(node)), $matchValue), true) |]
		condition.LexicalInfo = node.LexicalInfo

		for member in node.Arguments:
			assert member isa ReferenceExpression, "Invalid argument '${member}' in pattern '${node}'."
			memberRef = MemberReferenceExpression(member.LexicalInfo, temp.CloneNode(), member.ToString())
			condition = [| $condition and __eval__($member = $memberRef, true) |]

		for member in node.NamedArguments:
			namedArgCondition = ExpandMemberPattern(temp.CloneNode(), member)
			condition = [| $condition and $namedArgCondition |]

		return condition

	class QuasiquotePatternBuilder(DepthFirstVisitor):

		static final Ast = [| Boo.Lang.Compiler.Ast |]

		_parent as PatternExpander
		_pattern as Expression

		def constructor(parent as PatternExpander):
			_parent = parent

		def Build(node as QuasiquoteExpression):
			return Expand(node.Node)

		def Expand(node as Node):
			node.Accept(self)
			expansion = _pattern
			_pattern = null
			assert expansion is not null, "Unsupported pattern '${node}'"
			return expansion

		def Push(srcNode as Node, e as Expression):
			assert _pattern is null
			e.LexicalInfo = srcNode.LexicalInfo
			_pattern = e

		override def OnSpliceExpression(node as SpliceExpression):
			_pattern = node.Expression

		override def OnSpliceTypeReference(node as SpliceTypeReference):
			_pattern = node.Expression

		def ExpandFixedSize(items):
			a = [| (,) |]
			for item in items:
				a.Items.Add(Expand(item))
			return a

		override def OnOmittedExpression(node as OmittedExpression):
			_pattern = [| $Ast.OmittedExpression.Instance |]

		override def OnSlice(node as Slice):
			ctor = [| $Ast.Slice() |]
			ExpandProperty ctor, "Begin", node.Begin
			ExpandProperty ctor, "End", node.End
			ExpandProperty ctor, "Step", node.Step
			Push node, ctor

		def ExpandProperty(ctor as MethodInvocationExpression, name as string, value as Expression):
			if value is null: return
			ctor.NamedArguments.Add(ExpressionPair(First: ReferenceExpression(name), Second: Expand(value)))

		override def OnMacroStatement(node as MacroStatement):
			if len(node.Arguments) > 0:
				Push node, [| $Ast.MacroStatement(Name: $(node.Name), Arguments: $(ExpandFixedSize(node.Arguments))) |]
			else:
				Push node, [| $Ast.MacroStatement(Name: $(node.Name)) |]

		override def OnSlicingExpression(node as SlicingExpression):
			Push node, [| $Ast.SlicingExpression(Target: $(Expand(node.Target)), Indices: $(ExpandFixedSize(node.Indices))) |]

		override def OnTryCastExpression(node as TryCastExpression):
			Push node, [| $Ast.TryCastExpression(Target: $(Expand(node.Target)), Type: $(Expand(node.Type))) |]

		override def OnMethodInvocationExpression(node as MethodInvocationExpression):
			if len(node.Arguments) > 0:
				pattern = [| $Ast.MethodInvocationExpression(Target: $(Expand(node.Target)), Arguments: $(ExpandFixedSize(node.Arguments))) |]
			else:
				pattern = [| $Ast.MethodInvocationExpression(Target: $(Expand(node.Target))) |]
			Push node, pattern

		override def OnBoolLiteralExpression(node as BoolLiteralExpression):
			Push node, [| $Ast.BoolLiteralExpression(Value: $node) |]

		override def OnNullLiteralExpression(node as NullLiteralExpression):
			Push node, [| $Ast.NullLiteralExpression() |]

		override def OnUnaryExpression(node as UnaryExpression):
			Push node, [| $Ast.UnaryExpression(Operator: UnaryOperatorType.$(node.Operator.ToString()), Operand: $(Expand(node.Operand))) |]

		override def OnBinaryExpression(node as BinaryExpression):
			Push node, [| $Ast.BinaryExpression(Operator: BinaryOperatorType.$(node.Operator.ToString()), Left: $(Expand(node.Left)), Right: $(Expand(node.Right))) |]

		override def OnReferenceExpression(node as ReferenceExpression):
			Push node, [| $Ast.ReferenceExpression(Name: $(node.Name)) |]

		override def OnSuperLiteralExpression(node as SuperLiteralExpression):
			Push node, [| $Ast.SuperLiteralExpression() |]

	def ObjectPatternFor(node as QuasiquoteExpression):
		return QuasiquotePatternBuilder(self).Build(node)

	def ExpandQuasiquotePattern(matchValue as Expression, node as QuasiquoteExpression) as Expression:
		return ExpandObjectPattern(matchValue, ObjectPatternFor(node))

	def ExpandMemberPattern(matchValue as Expression, member as ExpressionPair):
		memberRef = MemberReferenceExpression(member.First.LexicalInfo, matchValue, member.First.ToString())
		return Expand(memberRef, member.Second)

	def ExpandFixedSizePattern(matchValue as Expression, pattern as ArrayLiteralExpression):
		patternLen = len(pattern.Items)
		condition = [| $(patternLen) == len($matchValue) |]

		if IsCatchAllPattern(last = pattern.Items[patternLen-1]):
			pattern.Items.Remove(last)
			condition = [| $(patternLen) <= len($matchValue)+1 |]

		i = 0
		for item in pattern.Items:
			itemValue = [| $matchValue[$i] |]
			itemPattern = Expand(itemValue, item)
			condition = [| $condition and $itemPattern |]
			++i
		return condition

	def TypeRefFrom(node as MethodInvocationExpression):
		return node.Target

	def IsCatchAllPattern(pattern as Expression) as bool:
		last = pattern as UnaryExpression
		if AstUtil.IsExplodeExpression(last):
			last_re = last.Operand as ReferenceExpression
			return last_re and last_re.Name == Dummy
		return false

internal def NewTemp(e as Expression):
	return ReferenceExpression(
			LexicalInfo: e.LexicalInfo,
			Name: "$match${CompilerContext.Current.AllocIndex()}")