Convert regexp literals

Convert regexp literals

namespace BooJs.Compiler.Visitors

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

import BooJs.Compiler.Mozilla as Moz

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

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

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

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

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

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

        return obj

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

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

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

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

        Return n

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

        Return n

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

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

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

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

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

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

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

        Return n

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

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

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

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

        n['module'] = node

        Return n

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

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

        Return n

    def OnField(node as Field):
        pass

    def OnConstructor(node as Constructor):
        pass

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

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

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

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

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

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

        Return b

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

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

    def OnIfStatement(node as IfStatement):

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

        Return ifst

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

        Return wst

    def OnTryStatement(node as TryStatement):

        assert 1 == len(node.ExceptionHandlers), 'Multiple exceptions handlers should be processed in previous steps'

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

        hdl = node.ExceptionHandlers[0]
        h = Moz.CatchClause(loc: loc(hdl))
        h.param = Moz.Identifier(loc: loc(hdl.Declaration), name: hdl.Declaration.Name)
        h.body = Apply(hdl.Block)

        n.handlers.Add(h)

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

        Return n

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

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

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

        # In Mozilla AST the label statements are associated with an statement.
        stmts = (node.ParentNode as Block).Statements
        idx = stmts.IndexOf(node)
        st = stmts[idx + 1]
        stmts.RemoveAt(idx + 1)

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

        Return n

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

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

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

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

        Return t

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

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

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

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

        Return n

    def OnMethodInvocationExpression(node as MethodInvocationExpression):
        # Detect constructors
        if node.Target.Entity isa IConstructor:
            c = Moz.NewExpression(loc: loc(node))
            c._constructor = Apply(node.Target)
            for arg in node.Arguments:
                c.arguments.Add(Apply(arg))
            Return c
        # Detect sequences
        elif node.Target.ToString() == '@':
            s = Moz.SequenceExpression(loc: loc(node))
            for arg in node.Arguments:
                s.expressions.Add(Apply(arg))
            Return s
        else:
            n = Moz.CallExpression(loc: loc(node))
            n.callee = Apply(node.Target)
            for arg in node.Arguments:
                n.arguments.Add(Apply(arg))
            Return n

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

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

    def OnBinaryExpression(node as BinaryExpression):
        n = Moz.BinaryExpression(loc: loc(node))
        match node.Operator:
            case BinaryOperatorType.Equality:
                n.operator = '=='
            case BinaryOperatorType.Inequality:
                n.operator = '!='
            case BinaryOperatorType.ReferenceEquality:
                n.operator = '==='
            case BinaryOperatorType.ReferenceInequality:
                n.operator = '!=='
            case BinaryOperatorType.Member:
                n.operator = 'in'
            case BinaryOperatorType.Addition:
                n.operator = '+'
            case BinaryOperatorType.Subtraction:
                n.operator = '-'
            case BinaryOperatorType.Multiply:
                n.operator = '*'
            case BinaryOperatorType.Division:
                n.operator = '/'
            case BinaryOperatorType.Modulus:
                n.operator = '%'
            case BinaryOperatorType.BitwiseAnd:
                n.operator = '&'
            case BinaryOperatorType.BitwiseOr:
                n.operator = '|'
            case BinaryOperatorType.ExclusiveOr:
                n.operator = '^'
            case BinaryOperatorType.GreaterThan:
                n.operator = '>'
            case BinaryOperatorType.GreaterThanOrEqual:
                n.operator = '>='
            case BinaryOperatorType.LessThan:
                n.operator = '<'
            case BinaryOperatorType.LessThanOrEqual:
                n.operator = '<='
            case BinaryOperatorType.ShiftLeft:
                n.operator = '<<'
            case BinaryOperatorType.ShiftRight:
                n.operator = '>>'

            case BinaryOperatorType.Assign:
                n = Moz.AssignmentExpression(loc: loc(node), operator: '=')

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

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

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

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

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

namespace BooJs.Compiler.Visitors

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

import BooJs.Compiler.Mozilla as Moz

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

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

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

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

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

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

        return obj

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

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

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

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

        Return n

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

        Return n

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

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

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

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

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

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

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

        Return n

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

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

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

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

        n['module'] = node

        Return n

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

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

        Return n

    def OnField(node as Field):
        pass

    def OnConstructor(node as Constructor):
        pass

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

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

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

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

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

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

        Return b

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

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

    def OnIfStatement(node as IfStatement):

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

        Return ifst

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

        Return wst

    def OnTryStatement(node as TryStatement):

        assert 1 == len(node.ExceptionHandlers), 'Multiple exceptions handlers should be processed in previous steps'

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

        hdl = node.ExceptionHandlers[0]
        h = Moz.CatchClause(loc: loc(hdl))
        h.param = Moz.Identifier(loc: loc(hdl.Declaration), name: hdl.Declaration.Name)
        h.body = Apply(hdl.Block)

        n.handlers.Add(h)

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

        Return n

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

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

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

        # In Mozilla AST the label statements are associated with an statement.
        stmts = (node.ParentNode as Block).Statements
        idx = stmts.IndexOf(node)
        st = stmts[idx + 1]
        stmts.RemoveAt(idx + 1)

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

        Return n

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

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

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

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

        Return t

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

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

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

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

        Return n

    def OnMethodInvocationExpression(node as MethodInvocationExpression):
        # Detect constructors
        if node.Target.Entity isa IConstructor:
            c = Moz.NewExpression(loc: loc(node))
            c._constructor = Apply(node.Target)
            for arg in node.Arguments:
                c.arguments.Add(Apply(arg))
            Return c
        # Detect sequences
        elif node.Target.ToString() == '@':
            s = Moz.SequenceExpression(loc: loc(node))
            for arg in node.Arguments:
                s.expressions.Add(Apply(arg))
            Return s
        else:
            n = Moz.CallExpression(loc: loc(node))
            n.callee = Apply(node.Target)
            for arg in node.Arguments:
                n.arguments.Add(Apply(arg))
            Return n

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

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

    def OnBinaryExpression(node as BinaryExpression):
        n = Moz.BinaryExpression(loc: loc(node))
        match node.Operator:
            case BinaryOperatorType.Equality:
                n.operator = '=='
            case BinaryOperatorType.Inequality:
                n.operator = '!='
            case BinaryOperatorType.ReferenceEquality:
                n.operator = '==='
            case BinaryOperatorType.ReferenceInequality:
                n.operator = '!=='
            case BinaryOperatorType.Member:
                n.operator = 'in'
            case BinaryOperatorType.Addition:
                n.operator = '+'
            case BinaryOperatorType.Subtraction:
                n.operator = '-'
            case BinaryOperatorType.Multiply:
                n.operator = '*'
            case BinaryOperatorType.Division:
                n.operator = '/'
            case BinaryOperatorType.Modulus:
                n.operator = '%'
            case BinaryOperatorType.BitwiseAnd:
                n.operator = '&'
            case BinaryOperatorType.BitwiseOr:
                n.operator = '|'
            case BinaryOperatorType.ExclusiveOr:
                n.operator = '^'
            case BinaryOperatorType.GreaterThan:
                n.operator = '>'
            case BinaryOperatorType.GreaterThanOrEqual:
                n.operator = '>='
            case BinaryOperatorType.LessThan:
                n.operator = '<'
            case BinaryOperatorType.LessThanOrEqual:
                n.operator = '<='
            case BinaryOperatorType.ShiftLeft:
                n.operator = '<<'
            case BinaryOperatorType.ShiftRight:
                n.operator = '>>'

            case BinaryOperatorType.Assign:
                n = Moz.AssignmentExpression(loc: loc(node), operator: '=')

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

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

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

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

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


keep the license simple

keep the license simple

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

#region license
// Copyright (c) 2004, Daniel Grunwald (daniel@danielgrunwald.de)
// All rights reserved.
//
// BooBinding is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// Boo Explorer is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Foobar; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
#endregion

namespace BooBinding

import System
import System.Drawing
import System.Windows.Forms
import ICSharpCode.SharpDevelop.Gui
import ICSharpCode.Core.Services
import booish.gui

class BooishView(AbstractPadContent):

	_box = PromptBox(Font: System.Drawing.Font("Lucida Console", 10))

	def constructor():
		super("booish")

		_box.Interpreter.SetValue("Workbench", WorkbenchSingleton.Workbench)

	override Control as Control:
		get:
			return _box


Fixing #186

Fixing #186

Macros declared inside of a type definition are not supported.  This makes that explicit, rather than giving a less useful error later on.

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

import Boo.Lang.Environments
import Boo.Lang.Compiler
import Boo.Lang.Compiler.Ast
import Boo.Lang.Compiler.TypeSystem
import Boo.Lang.Compiler.TypeSystem.Internal
import Boo.Lang.Compiler.TypeSystem.Reflection
import Boo.Lang.Compiler.TypeSystem.Services

class MacroMacro(LexicalInfoPreservingGeneratorMacro):

	private static final Usage = "Usage: `macro [<parent.>+]<name>[(arg0,...)]`"
	private static final NoNesting = "Macros cannot be declared inside of a type definition"

	_macro as MacroStatement
	_name as string

	_apb as ArgumentsPatternBuilder
	_usingPatternMatching = false

	ArgumentsPattern:
		get:
			return _apb.Pattern if _apb

	ArgumentsPrologue:
		get:
			return _apb.Prologue if _apb

	override protected def ExpandGeneratorImpl(macro as MacroStatement):
		raise Usage if len(macro.Arguments) != 1
		td = macro.GetAncestor[of TypeDefinition]()
		raise NoNesting unless td isa Module

		_macro = macro

		arg = macro.Arguments[0]
		mie = arg as MethodInvocationExpression
		if mie:
			_apb = ArgumentsPatternBuilder(mie.Arguments)
			arg = mie.Target

		if arg.NodeType == NodeType.ReferenceExpression:
			_name = cast(ReferenceExpression, arg).Name
			yield CreateMacroType()
			yield [| import Boo.Lang.PatternMatching |] if _usingPatternMatching
		elif arg.NodeType == NodeType.MemberReferenceExpression:
			if macro.GetAncestor[of MacroStatement]() is not null:
				raise "Nested macro extension cannot be itself a nested macro"
			_name = cast(MemberReferenceExpression, arg).Name
			CreateMacroExtensionType(cast(MemberReferenceExpression, arg))
		else:
			raise Usage

	private static def BuildMacroTypeName(name as string) as string:
		return char.ToUpper(name[0]) + name[1:] + "Macro"

	private static def BuildMacroTypeName(name as Expression) as string:
		return BuildMacroTypeName(name, false)

	private static def BuildMacroTypeName(name as Expression, external as bool) as string:
		sb = System.Text.StringBuilder()
		for part in UnpackReferences(name):
			sb.Append(char('.')) if sb.Length and not external
			sb.Append(char('$')) if external
			sb.Append(BuildMacroTypeName(part))
		return sb.ToString()

	private static def UnpackReferences(refs as Expression) as string*:
		if refs.NodeType == NodeType.ReferenceExpression:
			re = cast(ReferenceExpression, refs)
			raise "Extending macro `macro` is not supported" if re.Name == "macro"
			yield re.Name
		elif refs.NodeType == NodeType.MemberReferenceExpression:
			mre = cast(MemberReferenceExpression, refs)
			for name in UnpackReferences(mre.Target):
				yield name
			yield mre.Name
		else:
			raise "Invalid node type: ${refs.NodeType}"

	private static def GetParentMacroNames(macro as MacroStatement) as string*:
		for parent in macro.GetAncestors[of MacroStatement]():
			continue if parent.Name != 'macro'

			arg = parent.Arguments[0]
			if arg.NodeType == NodeType.MethodInvocationExpression:
				arg = cast(MethodInvocationExpression, arg).Target

			if arg.NodeType == NodeType.ReferenceExpression:
				yield cast(ReferenceExpression, arg).Name

			elif arg.NodeType == NodeType.MemberReferenceExpression:
				for name in UnpackReferences(cast(MemberReferenceExpression, arg)):
					yield name

	private def CreateMacroExtensionType(mre as MemberReferenceExpression):
		#resolve the macro type we want to expand
		parentTypeName = BuildMacroTypeName(mre.Target)
		parent = NameResolutionService.ResolveQualifiedName(parentTypeName) as IType
		if parent is null: #oh, this is a external extension maybe?
			parentTypeName = BuildMacroTypeName(mre.Target, true)
			parent = NameResolutionService.ResolveQualifiedName(parentTypeName) as IType
		if parent is null:
			raise "No macro `${mre.Target.ToString()}` has been found to extend"

		elif parent isa InternalClass:
			#create macro type and add it as usual into its parent macro type
			type = CreateMacroType(UnpackReferences(mre.Target))
			cast(InternalClass, parent).TypeDefinition.Members.Add(type)

		elif parent isa ExternalType:
			#create macro type with parent(s)-based prefix then add an extension method for redirection
			type = CreateMacroType(BuildMacroTypeName(mre, true), UnpackReferences(mre.Target))
			module = _macro.GetAncestor[of Module]()
			module.Members.Add(type)
			method = CreateMacroExtensionProxy(parent, type.Name)
			module.Members.Add(method)

		else:
			raise "Cannot bind a nested macro extension to entity: ${parent}"

	private def CreateMacroExtensionProxy(parent as IType, extension as string):
		return [|
			[Boo.Lang.ExtensionAttribute]
			[System.Runtime.CompilerServices.CompilerGeneratedAttribute]
			static def $(_name)(parent as $(parent.FullName), context as Boo.Lang.Compiler.CompilerContext) as $(extension):
				return $(ReferenceExpression(extension))(context)
		|]

	private def CreateMacroType() as ClassDefinition:
		return CreateMacroType(BuildMacroTypeName(_name), GetParentMacroNames(_macro))

	private def CreateMacroType(parents as string*) as ClassDefinition:
		return CreateMacroType(BuildMacroTypeName(_name), parents)

	private def CreateMacroType(typeName as string, parents as string*) as ClassDefinition:
		if YieldFinder(_macro).Found:
			macroType = CreateGeneratorMacroType(typeName)
		else:
			macroType = CreateOldStyleMacroType(typeName)

		sme = SubMacroExtractor()
		for member in macroType.Members.ToArray():
			method = member as Method
			continue if method is null
			method.Accept(sme)

		#add parent macro(s) accessor(s)
		for parent in parents:
			if not macroType.Members[parent]:
				for accessor in CreateParentMacroAccessor(parent):
					macroType.Members.Add(accessor)
		return macroType

	#BOO-1077 style
	private def CreateGeneratorMacroType(typeName as string) as ClassDefinition:
		compatErrorMsg = "Boo installed version is older than the new macro syntax '${_name}' is using. Read BOO-1077 for more info."
		arg = ReferenceExpression(_name)
		return [|
				public final class $(typeName) (Boo.Lang.Compiler.LexicalInfoPreservingGeneratorMacro):
					[System.Runtime.CompilerServices.CompilerGeneratedAttribute]
					private __macro as Boo.Lang.Compiler.Ast.MacroStatement
					public def constructor():
						super()
					public def constructor(context as Boo.Lang.Compiler.CompilerContext):
						raise System.ArgumentNullException("context") if not context
						super(context)
					override protected def ExpandGeneratorImpl($_name as Boo.Lang.Compiler.Ast.MacroStatement) as Boo.Lang.Compiler.Ast.Node*:
						raise System.ArgumentNullException($_name) if not $arg
						self.__macro = $arg
						$(ExpandBody())

					[System.Runtime.CompilerServices.CompilerGeneratedAttribute]
					override protected def ExpandImpl($_name as Boo.Lang.Compiler.Ast.MacroStatement) as Boo.Lang.Compiler.Ast.Statement:
						raise System.NotImplementedException($compatErrorMsg)
			|]

	private def CreateOldStyleMacroType(typeName as string) as ClassDefinition:
		arg = ReferenceExpression(_name)
		return [|
				public final class $(typeName) (Boo.Lang.Compiler.LexicalInfoPreservingMacro):
					[System.Runtime.CompilerServices.CompilerGeneratedAttribute]
					private __macro as Boo.Lang.Compiler.Ast.MacroStatement
					public def constructor():
						super()
					public def constructor(context as Boo.Lang.Compiler.CompilerContext):
						raise System.ArgumentNullException("context") if not context
						super(context)
					override protected def ExpandImpl($_name as Boo.Lang.Compiler.Ast.MacroStatement) as Boo.Lang.Compiler.Ast.Statement:
						raise System.ArgumentNullException($_name) if not $arg
						self.__macro = $arg
						$(ExpandBody())
			|]

	private def ExpandBody():
		body = _macro.Body

		if ArgumentsPattern:
			if 0 == ArgumentsPattern.Count and not ArgumentsPrologue:
				body = [|
					if __macro.Arguments.Count == 0:
						$body
					else:
						$(CreateRaiseMacroArgumentsMatchError())
				|].ToBlock()
			elif ArgumentsPattern.Count > 0:
				case = CaseStatement()
				case.Pattern = QuasiquoteExpression(pattern = MacroStatement(_name))
				pattern.Arguments = ArgumentsPattern
				if ArgumentsPrologue:
					case.Body = [|
						$ArgumentsPrologue
						$body
					|].ToBlock()
				else:
					case.Body = body
				otherwise = OtherwiseStatement() #TODO: macro overload without arguments def
				otherwise.Body = CreateRaiseMacroArgumentsMatchError().ToBlock()
				body = [|
					$case
					$otherwise
				|]

		if ContainsCase(body):
			return ExpandWithPatternMatching(_name, body)

		if ArgumentsPrologue:
			return [|
				$ArgumentsPrologue
				$body
			|].ToBlock()
		else:
			return body

	def CreateRaiseMacroArgumentsMatchError() as RaiseStatement:
		errorMsg = "`${$(_name)}` macro invocation argument(s) did not match definition: `${$(_macro.Arguments[0].ToString())}`"
		return [| raise $errorMsg |]

	#region PatternMatching
	internal class CustomBlockStatement(CustomStatement):
		public Body as Block

	internal class CaseStatement(CustomBlockStatement):
		public Pattern as Expression

	internal class OtherwiseStatement(CustomBlockStatement):
		pass

	class CaseMacro(LexicalInfoPreservingGeneratorMacro):
		override protected def ExpandGeneratorImpl(case as MacroStatement):
			if len(case.Arguments) != 1:
				raise "Usage: case <pattern>"
			pattern, = case.Arguments
			yield CaseStatement(Pattern: pattern, Body: case.Body)

	class OtherwiseMacro(LexicalInfoPreservingGeneratorMacro):
		override protected def ExpandGeneratorImpl(case as MacroStatement):
			if len(case.Arguments) != 0:
				raise "Usage: otherwise: <block>"
			yield OtherwiseStatement(Body: case.Body)

	private static def ContainsCase(body as Block):
		for stmt in body.Statements:
			return true if stmt isa CaseStatement

	private def ExpandWithPatternMatching(name as string, body as Block):

		_usingPatternMatching = true

		matchBlock = [|
			match $(ReferenceExpression(name)):
				pass
		|]
		statementsEnumerator = body.Statements.GetEnumerator()
		while statementsEnumerator.MoveNext():
			stmt as Statement = statementsEnumerator.Current
			if stmt isa CaseStatement:
				case as CaseStatement = stmt
				caseBlock = [|
					case $(case.Pattern):
						$(case.Body)
				|]
				matchBlock.Body.Add(caseBlock)
			elif stmt isa OtherwiseStatement:
				otherwise as OtherwiseStatement = stmt
				otherwiseBlock = [|
					otherwise:
						$(otherwise.Body)
				|]
				matchBlock.Body.Add(otherwiseBlock)

				// otherwise marks the end of the match block
				resultingBlock = Block()
				resultingBlock.Add(matchBlock)
				for remaining as Statement in statementsEnumerator:
					resultingBlock.Add(remaining)
				return resultingBlock
			else:
				// statements after a sequence of 'case <pattern>' macros
				// also mark the end of a match block
				resultingBlock = Block()
				resultingBlock.Add(matchBlock)
				resultingBlock.Add(stmt)
				for remaining as Statement in statementsEnumerator:
					resultingBlock.Add(remaining)
				return resultingBlock
		return matchBlock
	#endregion

	private static def CreateParentMacroAccessor(name as string):
		cacheField = [|
			[System.Runtime.CompilerServices.CompilerGeneratedAttribute]
			private $("$" + name) as Boo.Lang.Compiler.Ast.MacroStatement
		|]
		yield cacheField

		cacheFieldRef = ReferenceExpression(cacheField.Name)
		prop = [|
			[System.Runtime.CompilerServices.CompilerGeneratedAttribute]
			private $name:
				get:
					$cacheFieldRef = __macro.GetParentMacroByName($name) unless $cacheFieldRef
					return $cacheFieldRef
		|]
		prop.IsSynthetic = true #avoid BCW0014 if not used
		yield prop

	private final class YieldFinder(DepthFirstVisitor):
		private _found = false

		def constructor(macro as MacroStatement):
			macro.Body.Accept(self)

		Found:
			get: return _found

		override def OnMethod(node as Method):
			pass

		override def OnCustomStatement(node as CustomStatement):
			customBlock = node as CustomBlockStatement
			if customBlock is null:
				super(node)
			else:
				customBlock.Body.Accept(self)

		override def OnYieldStatement(node as YieldStatement):
			_found = true

		override def EnterMacroStatement(node as MacroStatement) as bool:
			return false

	private final class ArgumentsPatternBuilder():
		static final MacroField = ReferenceExpression("__macro")

		_input as ExpressionCollection
		_pattern as ExpressionCollection
		_prologue as Block
		_tss = EnvironmentProvision[of TypeSystemServices]()
		_nrs = EnvironmentProvision[of NameResolutionService]()
		_arg as ReferenceExpression
		_argIndex = 0
		_enumerable = false

		def constructor(input as ExpressionCollection):
			_input = input

		Pattern:
			get:
				Build() if not _pattern
				return _pattern

		Prologue:
			get:
				Build() if not _pattern
				return _prologue

		def Build():
			_pattern = ExpressionCollection()
			for arg in _input:
				_enumerable = false
				Append(arg) #FIXME: run-time (duck) dispatch here makes mono cry
				++_argIndex

		private TypeSystemServices:
			get: return _tss.Instance

		private NameResolutionService:
			get: return _nrs.Instance

		private IsBodyArgument:
			get: return _arg.Name == "body"

		private IsLastArgument:
			get: return _argIndex == _input.Count-1

		private def Append(e as Expression):
			if e.NodeType == NodeType.ReferenceExpression:
				_arg = cast(ReferenceExpression, e)
				Append(null as IType)
			elif e.NodeType == NodeType.TryCastExpression:
				Append(e as TryCastExpression)
			else:
				raise "Invalid macro argument declaration: `${e}`"

		private def Append(e as TryCastExpression):
			re = e.Target as ReferenceExpression
			raise "Invalid macro argument name: `${e.Target}`" unless re

			NameResolutionService.ResolveTypeReference(e.Type)
			type = TypeSystemServices.GetType(e.Type)
			_arg = cast(ReferenceExpression, re)
			Append(type)

		private def Append(type as IType):
			if IsBodyArgument and not IsLastArgument:
				raise "`${_arg.Name}` argument must be the last argument"

			if type is null:
				AppendDefaultNode()
			elif IsAstNode(type):
				AppendNode(type)
			elif TypeSystemServices.IsLiteralPrimitive(type):
				AppendPrimitive(type)
			elif not _enumerable and (etype = TypeSystemServices.GetEnumeratorItemType(type)) is not null:
				if not IsLastArgument:
					raise "Enumerable or array type argument `${_arg.Name}` must be the last argument"
				if not IsBodyArgument and _pattern.Count > 0:
					_pattern.Add(SpliceExpression([| *_ |]))
				_enumerable = true
				Append(etype)
			else:
				raise "Unsupported type `${type.FullName}` for argument `${_arg.Name}`, a macro argument type must be a literal-able primitive or an AST node"

		private def IsAstNode(type as IType):
			return type.IsSubclassOf(TypeSystemServices.Map(Boo.Lang.Compiler.Ast.Node))

		private def AppendDefaultNode():
			if IsBodyArgument:
				_prologue = Block()
				_prologue.Add([| $_arg = $(MacroField).Body.Statements |])
			else:
				_pattern.Add(SpliceExpression([| $_arg = Boo.Lang.Compiler.Ast.Expression() |]))

		private def AppendNode(type as IType):
			AssertEnumerable(type.FullName) if IsBodyArgument
			if not _enumerable:
				_pattern.Add(SpliceExpression([| $_arg = $(ReferenceExpression(type.FullName))() |]))
			else:
				_prologue = GetPrologue(type)

		private def AppendPrimitive(type as IType):
			AssertEnumerable(type.FullName) if IsBodyArgument

			if type == TypeSystemServices.StringType:
				AppendString()
			elif type == TypeSystemServices.BoolType:
				AppendBool()
			elif type == TypeSystemServices.LongType or type == TypeSystemServices.ULongType:
				AppendLong()
			elif TypeSystemServices.IsIntegerNumber(type):
				AppendInt()
			elif type == TypeSystemServices.SingleType:
				AppendSingle()
			elif type == TypeSystemServices.DoubleType:
				AppendDouble()
			elif type == TypeSystemServices.RegexType:
				AppendRegex()
			elif type == TypeSystemServices.CharType:
				AppendChar()
			elif type == TypeSystemServices.TimeSpanType:
				AppendTimeSpan()
			else:
				raise "Unknown primitive `${type}`"

		private def AppendString():
			if not _enumerable:
				_pattern.Add(SpliceExpression([| Boo.Lang.Compiler.Ast.StringLiteralExpression(Value: $_arg) |]))
			else:
				_prologue = GetPrologue[of Boo.Lang.Compiler.Ast.StringLiteralExpression, string]()

		private def AppendBool():
			if not _enumerable:
				_pattern.Add(SpliceExpression([| Boo.Lang.Compiler.Ast.BoolLiteralExpression(Value: $_arg) |]))
			else:
				_prologue = GetPrologue[of Boo.Lang.Compiler.Ast.BoolLiteralExpression, bool]()

		private def AppendLong():
			if not _enumerable:
				_pattern.Add(SpliceExpression([| Boo.Lang.Compiler.Ast.IntegerLiteralExpression(Value: $_arg, IsLong: true) |]))
			else:
				_prologue = GetPrologue[of Boo.Lang.Compiler.Ast.IntegerLiteralExpression, long]()

		private def AppendInt():
			if not _enumerable:
				_pattern.Add(SpliceExpression([| Boo.Lang.Compiler.Ast.IntegerLiteralExpression(Value: $_arg, IsLong: false) |]))
			else:
				_prologue = GetPrologue[of Boo.Lang.Compiler.Ast.IntegerLiteralExpression, int]()

		private def AppendSingle():
			if not _enumerable:
				_pattern.Add(SpliceExpression([| Boo.Lang.Compiler.Ast.DoubleLiteralExpression(Value: $_arg, IsSingle: true) |]))
			else:
				_prologue = GetPrologue[of Boo.Lang.Compiler.Ast.DoubleLiteralExpression, single]()

		private def AppendDouble():
			if not _enumerable:
				_pattern.Add(SpliceExpression([| Boo.Lang.Compiler.Ast.DoubleLiteralExpression(Value: $_arg, IsSingle: false) |]))
			else:
				_prologue = GetPrologue[of Boo.Lang.Compiler.Ast.DoubleLiteralExpression, double]()

		private def AppendRegex():
			if not _enumerable:
				_pattern.Add(SpliceExpression([| Boo.Lang.Compiler.Ast.RELiteralExpression(Regex: $_arg) |]))
			else:
				_prologue = GetPrologue[of Boo.Lang.Compiler.Ast.RELiteralExpression, regex]()

		private def AppendChar():
			if not _enumerable:
				_pattern.Add(SpliceExpression([| Boo.Lang.Compiler.Ast.CharLiteralExpression(Value: $_arg) |]))
			else:
				_prologue = GetPrologue[of Boo.Lang.Compiler.Ast.CharLiteralExpression, char]()

		private def AppendTimeSpan():
			if not _enumerable:
				_pattern.Add(SpliceExpression([| Boo.Lang.Compiler.Ast.TimeSpanLiteralExpression(Value: $_arg) |]))
			else:
				_prologue = GetPrologue[of Boo.Lang.Compiler.Ast.TimeSpanLiteralExpression, timespan]()

		private def GetPrologueCollection(collectionType as string):
			typeRef = SimpleTypeReference(collectionType)
			if IsBodyArgument:
				return [| $(MacroField).Body.Statements.Cast[of $(typeRef)]() |]
			else:
				return [| $(MacroField).Arguments.Cast[of $(typeRef)]($_argIndex) |]

		private def GetPrologue(type as IType):
			p = Block()
			p.Add([| $_arg = $(GetPrologueCollection(type.FullName)) |])
			return p

		private def GetPrologue[of TNode, TValue]():
			temp = CreateTemp()
			p = Block()
			p.Add([| $temp = $(GetPrologueCollection(typeof(TNode).FullName)) |])
			p.Add([| $_arg = Boo.Lang.Compiler.Ast.AstUtil.GetValues[of $TNode, $TValue]($temp) |])
			return p

		private def CreateTemp():
			return ReferenceExpression(CompilerContext.Current.GetUniqueName())

		private def AssertEnumerable(typeName as string):
			if not _enumerable:
				typeName = typeof(Boo.Lang.Compiler.Ast.Statement).FullName unless typeName
				raise "`${_arg.Name}` argument must be of enumerable type. Did you mean `${_arg.Name} as ${typeName}*`?"

	class SubMacroExtractor(DepthFirstTransformer):

		override def OnTypeMemberStatement(node as TypeMemberStatement):
			tm = node.TypeMember
			cd = node.GetAncestor[of ClassDefinition]()
			RemoveCurrentNode()
			cd.Members.Add(tm)

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

import Boo.Lang.Environments
import Boo.Lang.Compiler
import Boo.Lang.Compiler.Ast
import Boo.Lang.Compiler.TypeSystem
import Boo.Lang.Compiler.TypeSystem.Internal
import Boo.Lang.Compiler.TypeSystem.Reflection
import Boo.Lang.Compiler.TypeSystem.Services

class MacroMacro(LexicalInfoPreservingGeneratorMacro):

	private static final Usage = "Usage: `macro [<parent.>+]<name>[(arg0,...)]`"

	_macro as MacroStatement
	_name as string

	_apb as ArgumentsPatternBuilder
	_usingPatternMatching = false

	ArgumentsPattern:
		get:
			return _apb.Pattern if _apb

	ArgumentsPrologue:
		get:
			return _apb.Prologue if _apb

	override protected def ExpandGeneratorImpl(macro as MacroStatement):
		raise Usage if len(macro.Arguments) != 1

		_macro = macro

		arg = macro.Arguments[0]
		mie = arg as MethodInvocationExpression
		if mie:
			_apb = ArgumentsPatternBuilder(mie.Arguments)
			arg = mie.Target

		if arg.NodeType == NodeType.ReferenceExpression:
			_name = cast(ReferenceExpression, arg).Name
			yield CreateMacroType()
			yield [| import Boo.Lang.PatternMatching |] if _usingPatternMatching
		elif arg.NodeType == NodeType.MemberReferenceExpression:
			if macro.GetAncestor[of MacroStatement]() is not null:
				raise "Nested macro extension cannot be itself a nested macro"
			_name = cast(MemberReferenceExpression, arg).Name
			CreateMacroExtensionType(cast(MemberReferenceExpression, arg))
		else:
			raise Usage

	private static def BuildMacroTypeName(name as string) as string:
		return char.ToUpper(name[0]) + name[1:] + "Macro"

	private static def BuildMacroTypeName(name as Expression) as string:
		return BuildMacroTypeName(name, false)

	private static def BuildMacroTypeName(name as Expression, external as bool) as string:
		sb = System.Text.StringBuilder()
		for part in UnpackReferences(name):
			sb.Append(char('.')) if sb.Length and not external
			sb.Append(char('$')) if external
			sb.Append(BuildMacroTypeName(part))
		return sb.ToString()

	private static def UnpackReferences(refs as Expression) as string*:
		if refs.NodeType == NodeType.ReferenceExpression:
			re = cast(ReferenceExpression, refs)
			raise "Extending macro `macro` is not supported" if re.Name == "macro"
			yield re.Name
		elif refs.NodeType == NodeType.MemberReferenceExpression:
			mre = cast(MemberReferenceExpression, refs)
			for name in UnpackReferences(mre.Target):
				yield name
			yield mre.Name
		else:
			raise "Invalid node type: ${refs.NodeType}"

	private static def GetParentMacroNames(macro as MacroStatement) as string*:
		for parent in macro.GetAncestors[of MacroStatement]():
			continue if parent.Name != 'macro'

			arg = parent.Arguments[0]
			if arg.NodeType == NodeType.MethodInvocationExpression:
				arg = cast(MethodInvocationExpression, arg).Target

			if arg.NodeType == NodeType.ReferenceExpression:
				yield cast(ReferenceExpression, arg).Name

			elif arg.NodeType == NodeType.MemberReferenceExpression:
				for name in UnpackReferences(cast(MemberReferenceExpression, arg)):
					yield name

	private def CreateMacroExtensionType(mre as MemberReferenceExpression):
		#resolve the macro type we want to expand
		parentTypeName = BuildMacroTypeName(mre.Target)
		parent = NameResolutionService.ResolveQualifiedName(parentTypeName) as IType
		if parent is null: #oh, this is a external extension maybe?
			parentTypeName = BuildMacroTypeName(mre.Target, true)
			parent = NameResolutionService.ResolveQualifiedName(parentTypeName) as IType
		if parent is null:
			raise "No macro `${mre.Target.ToString()}` has been found to extend"

		elif parent isa InternalClass:
			#create macro type and add it as usual into its parent macro type
			type = CreateMacroType(UnpackReferences(mre.Target))
			cast(InternalClass, parent).TypeDefinition.Members.Add(type)

		elif parent isa ExternalType:
			#create macro type with parent(s)-based prefix then add an extension method for redirection
			type = CreateMacroType(BuildMacroTypeName(mre, true), UnpackReferences(mre.Target))
			module = _macro.GetAncestor[of Module]()
			module.Members.Add(type)
			method = CreateMacroExtensionProxy(parent, type.Name)
			module.Members.Add(method)

		else:
			raise "Cannot bind a nested macro extension to entity: ${parent}"

	private def CreateMacroExtensionProxy(parent as IType, extension as string):
		return [|
			[Boo.Lang.ExtensionAttribute]
			[System.Runtime.CompilerServices.CompilerGeneratedAttribute]
			static def $(_name)(parent as $(parent.FullName), context as Boo.Lang.Compiler.CompilerContext) as $(extension):
				return $(ReferenceExpression(extension))(context)
		|]

	private def CreateMacroType() as ClassDefinition:
		return CreateMacroType(BuildMacroTypeName(_name), GetParentMacroNames(_macro))

	private def CreateMacroType(parents as string*) as ClassDefinition:
		return CreateMacroType(BuildMacroTypeName(_name), parents)

	private def CreateMacroType(typeName as string, parents as string*) as ClassDefinition:
		if YieldFinder(_macro).Found:
			macroType = CreateGeneratorMacroType(typeName)
		else:
			macroType = CreateOldStyleMacroType(typeName)

		sme = SubMacroExtractor()
		for member in macroType.Members.ToArray():
			method = member as Method
			continue if method is null
			method.Accept(sme)

		#add parent macro(s) accessor(s)
		for parent in parents:
			if not macroType.Members[parent]:
				for accessor in CreateParentMacroAccessor(parent):
					macroType.Members.Add(accessor)
		return macroType

	#BOO-1077 style
	private def CreateGeneratorMacroType(typeName as string) as ClassDefinition:
		compatErrorMsg = "Boo installed version is older than the new macro syntax '${_name}' is using. Read BOO-1077 for more info."
		arg = ReferenceExpression(_name)
		return [|
				public final class $(typeName) (Boo.Lang.Compiler.LexicalInfoPreservingGeneratorMacro):
					[System.Runtime.CompilerServices.CompilerGeneratedAttribute]
					private __macro as Boo.Lang.Compiler.Ast.MacroStatement
					public def constructor():
						super()
					public def constructor(context as Boo.Lang.Compiler.CompilerContext):
						raise System.ArgumentNullException("context") if not context
						super(context)
					override protected def ExpandGeneratorImpl($_name as Boo.Lang.Compiler.Ast.MacroStatement) as Boo.Lang.Compiler.Ast.Node*:
						raise System.ArgumentNullException($_name) if not $arg
						self.__macro = $arg
						$(ExpandBody())

					[System.Runtime.CompilerServices.CompilerGeneratedAttribute]
					override protected def ExpandImpl($_name as Boo.Lang.Compiler.Ast.MacroStatement) as Boo.Lang.Compiler.Ast.Statement:
						raise System.NotImplementedException($compatErrorMsg)
			|]

	private def CreateOldStyleMacroType(typeName as string) as ClassDefinition:
		arg = ReferenceExpression(_name)
		return [|
				public final class $(typeName) (Boo.Lang.Compiler.LexicalInfoPreservingMacro):
					[System.Runtime.CompilerServices.CompilerGeneratedAttribute]
					private __macro as Boo.Lang.Compiler.Ast.MacroStatement
					public def constructor():
						super()
					public def constructor(context as Boo.Lang.Compiler.CompilerContext):
						raise System.ArgumentNullException("context") if not context
						super(context)
					override protected def ExpandImpl($_name as Boo.Lang.Compiler.Ast.MacroStatement) as Boo.Lang.Compiler.Ast.Statement:
						raise System.ArgumentNullException($_name) if not $arg
						self.__macro = $arg
						$(ExpandBody())
			|]

	private def ExpandBody():
		body = _macro.Body

		if ArgumentsPattern:
			if 0 == ArgumentsPattern.Count and not ArgumentsPrologue:
				body = [|
					if __macro.Arguments.Count == 0:
						$body
					else:
						$(CreateRaiseMacroArgumentsMatchError())
				|].ToBlock()
			elif ArgumentsPattern.Count > 0:
				case = CaseStatement()
				case.Pattern = QuasiquoteExpression(pattern = MacroStatement(_name))
				pattern.Arguments = ArgumentsPattern
				if ArgumentsPrologue:
					case.Body = [|
						$ArgumentsPrologue
						$body
					|].ToBlock()
				else:
					case.Body = body
				otherwise = OtherwiseStatement() #TODO: macro overload without arguments def
				otherwise.Body = CreateRaiseMacroArgumentsMatchError().ToBlock()
				body = [|
					$case
					$otherwise
				|]

		if ContainsCase(body):
			return ExpandWithPatternMatching(_name, body)

		if ArgumentsPrologue:
			return [|
				$ArgumentsPrologue
				$body
			|].ToBlock()
		else:
			return body

	def CreateRaiseMacroArgumentsMatchError() as RaiseStatement:
		errorMsg = "`${$(_name)}` macro invocation argument(s) did not match definition: `${$(_macro.Arguments[0].ToString())}`"
		return [| raise $errorMsg |]

	#region PatternMatching
	internal class CustomBlockStatement(CustomStatement):
		public Body as Block

	internal class CaseStatement(CustomBlockStatement):
		public Pattern as Expression

	internal class OtherwiseStatement(CustomBlockStatement):
		pass

	class CaseMacro(LexicalInfoPreservingGeneratorMacro):
		override protected def ExpandGeneratorImpl(case as MacroStatement):
			if len(case.Arguments) != 1:
				raise "Usage: case <pattern>"
			pattern, = case.Arguments
			yield CaseStatement(Pattern: pattern, Body: case.Body)

	class OtherwiseMacro(LexicalInfoPreservingGeneratorMacro):
		override protected def ExpandGeneratorImpl(case as MacroStatement):
			if len(case.Arguments) != 0:
				raise "Usage: otherwise: <block>"
			yield OtherwiseStatement(Body: case.Body)

	private static def ContainsCase(body as Block):
		for stmt in body.Statements:
			return true if stmt isa CaseStatement

	private def ExpandWithPatternMatching(name as string, body as Block):

		_usingPatternMatching = true

		matchBlock = [|
			match $(ReferenceExpression(name)):
				pass
		|]
		statementsEnumerator = body.Statements.GetEnumerator()
		while statementsEnumerator.MoveNext():
			stmt as Statement = statementsEnumerator.Current
			if stmt isa CaseStatement:
				case as CaseStatement = stmt
				caseBlock = [|
					case $(case.Pattern):
						$(case.Body)
				|]
				matchBlock.Body.Add(caseBlock)
			elif stmt isa OtherwiseStatement:
				otherwise as OtherwiseStatement = stmt
				otherwiseBlock = [|
					otherwise:
						$(otherwise.Body)
				|]
				matchBlock.Body.Add(otherwiseBlock)

				// otherwise marks the end of the match block
				resultingBlock = Block()
				resultingBlock.Add(matchBlock)
				for remaining as Statement in statementsEnumerator:
					resultingBlock.Add(remaining)
				return resultingBlock
			else:
				// statements after a sequence of 'case <pattern>' macros
				// also mark the end of a match block
				resultingBlock = Block()
				resultingBlock.Add(matchBlock)
				resultingBlock.Add(stmt)
				for remaining as Statement in statementsEnumerator:
					resultingBlock.Add(remaining)
				return resultingBlock
		return matchBlock
	#endregion

	private static def CreateParentMacroAccessor(name as string):
		cacheField = [|
			[System.Runtime.CompilerServices.CompilerGeneratedAttribute]
			private $("$" + name) as Boo.Lang.Compiler.Ast.MacroStatement
		|]
		yield cacheField

		cacheFieldRef = ReferenceExpression(cacheField.Name)
		prop = [|
			[System.Runtime.CompilerServices.CompilerGeneratedAttribute]
			private $name:
				get:
					$cacheFieldRef = __macro.GetParentMacroByName($name) unless $cacheFieldRef
					return $cacheFieldRef
		|]
		prop.IsSynthetic = true #avoid BCW0014 if not used
		yield prop

	private final class YieldFinder(DepthFirstVisitor):
		private _found = false

		def constructor(macro as MacroStatement):
			macro.Body.Accept(self)

		Found:
			get: return _found

		override def OnMethod(node as Method):
			pass

		override def OnCustomStatement(node as CustomStatement):
			customBlock = node as CustomBlockStatement
			if customBlock is null:
				super(node)
			else:
				customBlock.Body.Accept(self)

		override def OnYieldStatement(node as YieldStatement):
			_found = true

		override def EnterMacroStatement(node as MacroStatement) as bool:
			return false

	private final class ArgumentsPatternBuilder():
		static final MacroField = ReferenceExpression("__macro")

		_input as ExpressionCollection
		_pattern as ExpressionCollection
		_prologue as Block
		_tss = EnvironmentProvision[of TypeSystemServices]()
		_nrs = EnvironmentProvision[of NameResolutionService]()
		_arg as ReferenceExpression
		_argIndex = 0
		_enumerable = false

		def constructor(input as ExpressionCollection):
			_input = input

		Pattern:
			get:
				Build() if not _pattern
				return _pattern

		Prologue:
			get:
				Build() if not _pattern
				return _prologue

		def Build():
			_pattern = ExpressionCollection()
			for arg in _input:
				_enumerable = false
				Append(arg) #FIXME: run-time (duck) dispatch here makes mono cry
				++_argIndex

		private TypeSystemServices:
			get: return _tss.Instance

		private NameResolutionService:
			get: return _nrs.Instance

		private IsBodyArgument:
			get: return _arg.Name == "body"

		private IsLastArgument:
			get: return _argIndex == _input.Count-1

		private def Append(e as Expression):
			if e.NodeType == NodeType.ReferenceExpression:
				_arg = cast(ReferenceExpression, e)
				Append(null as IType)
			elif e.NodeType == NodeType.TryCastExpression:
				Append(e as TryCastExpression)
			else:
				raise "Invalid macro argument declaration: `${e}`"

		private def Append(e as TryCastExpression):
			re = e.Target as ReferenceExpression
			raise "Invalid macro argument name: `${e.Target}`" unless re

			NameResolutionService.ResolveTypeReference(e.Type)
			type = TypeSystemServices.GetType(e.Type)
			_arg = cast(ReferenceExpression, re)
			Append(type)

		private def Append(type as IType):
			if IsBodyArgument and not IsLastArgument:
				raise "`${_arg.Name}` argument must be the last argument"

			if type is null:
				AppendDefaultNode()
			elif IsAstNode(type):
				AppendNode(type)
			elif TypeSystemServices.IsLiteralPrimitive(type):
				AppendPrimitive(type)
			elif not _enumerable and (etype = TypeSystemServices.GetEnumeratorItemType(type)) is not null:
				if not IsLastArgument:
					raise "Enumerable or array type argument `${_arg.Name}` must be the last argument"
				if not IsBodyArgument and _pattern.Count > 0:
					_pattern.Add(SpliceExpression([| *_ |]))
				_enumerable = true
				Append(etype)
			else:
				raise "Unsupported type `${type.FullName}` for argument `${_arg.Name}`, a macro argument type must be a literal-able primitive or an AST node"

		private def IsAstNode(type as IType):
			return type.IsSubclassOf(TypeSystemServices.Map(Boo.Lang.Compiler.Ast.Node))

		private def AppendDefaultNode():
			if IsBodyArgument:
				_prologue = Block()
				_prologue.Add([| $_arg = $(MacroField).Body.Statements |])
			else:
				_pattern.Add(SpliceExpression([| $_arg = Boo.Lang.Compiler.Ast.Expression() |]))

		private def AppendNode(type as IType):
			AssertEnumerable(type.FullName) if IsBodyArgument
			if not _enumerable:
				_pattern.Add(SpliceExpression([| $_arg = $(ReferenceExpression(type.FullName))() |]))
			else:
				_prologue = GetPrologue(type)

		private def AppendPrimitive(type as IType):
			AssertEnumerable(type.FullName) if IsBodyArgument

			if type == TypeSystemServices.StringType:
				AppendString()
			elif type == TypeSystemServices.BoolType:
				AppendBool()
			elif type == TypeSystemServices.LongType or type == TypeSystemServices.ULongType:
				AppendLong()
			elif TypeSystemServices.IsIntegerNumber(type):
				AppendInt()
			elif type == TypeSystemServices.SingleType:
				AppendSingle()
			elif type == TypeSystemServices.DoubleType:
				AppendDouble()
			elif type == TypeSystemServices.RegexType:
				AppendRegex()
			elif type == TypeSystemServices.CharType:
				AppendChar()
			elif type == TypeSystemServices.TimeSpanType:
				AppendTimeSpan()
			else:
				raise "Unknown primitive `${type}`"

		private def AppendString():
			if not _enumerable:
				_pattern.Add(SpliceExpression([| Boo.Lang.Compiler.Ast.StringLiteralExpression(Value: $_arg) |]))
			else:
				_prologue = GetPrologue[of Boo.Lang.Compiler.Ast.StringLiteralExpression, string]()

		private def AppendBool():
			if not _enumerable:
				_pattern.Add(SpliceExpression([| Boo.Lang.Compiler.Ast.BoolLiteralExpression(Value: $_arg) |]))
			else:
				_prologue = GetPrologue[of Boo.Lang.Compiler.Ast.BoolLiteralExpression, bool]()

		private def AppendLong():
			if not _enumerable:
				_pattern.Add(SpliceExpression([| Boo.Lang.Compiler.Ast.IntegerLiteralExpression(Value: $_arg, IsLong: true) |]))
			else:
				_prologue = GetPrologue[of Boo.Lang.Compiler.Ast.IntegerLiteralExpression, long]()

		private def AppendInt():
			if not _enumerable:
				_pattern.Add(SpliceExpression([| Boo.Lang.Compiler.Ast.IntegerLiteralExpression(Value: $_arg, IsLong: false) |]))
			else:
				_prologue = GetPrologue[of Boo.Lang.Compiler.Ast.IntegerLiteralExpression, int]()

		private def AppendSingle():
			if not _enumerable:
				_pattern.Add(SpliceExpression([| Boo.Lang.Compiler.Ast.DoubleLiteralExpression(Value: $_arg, IsSingle: true) |]))
			else:
				_prologue = GetPrologue[of Boo.Lang.Compiler.Ast.DoubleLiteralExpression, single]()

		private def AppendDouble():
			if not _enumerable:
				_pattern.Add(SpliceExpression([| Boo.Lang.Compiler.Ast.DoubleLiteralExpression(Value: $_arg, IsSingle: false) |]))
			else:
				_prologue = GetPrologue[of Boo.Lang.Compiler.Ast.DoubleLiteralExpression, double]()

		private def AppendRegex():
			if not _enumerable:
				_pattern.Add(SpliceExpression([| Boo.Lang.Compiler.Ast.RELiteralExpression(Regex: $_arg) |]))
			else:
				_prologue = GetPrologue[of Boo.Lang.Compiler.Ast.RELiteralExpression, regex]()

		private def AppendChar():
			if not _enumerable:
				_pattern.Add(SpliceExpression([| Boo.Lang.Compiler.Ast.CharLiteralExpression(Value: $_arg) |]))
			else:
				_prologue = GetPrologue[of Boo.Lang.Compiler.Ast.CharLiteralExpression, char]()

		private def AppendTimeSpan():
			if not _enumerable:
				_pattern.Add(SpliceExpression([| Boo.Lang.Compiler.Ast.TimeSpanLiteralExpression(Value: $_arg) |]))
			else:
				_prologue = GetPrologue[of Boo.Lang.Compiler.Ast.TimeSpanLiteralExpression, timespan]()

		private def GetPrologueCollection(collectionType as string):
			typeRef = SimpleTypeReference(collectionType)
			if IsBodyArgument:
				return [| $(MacroField).Body.Statements.Cast[of $(typeRef)]() |]
			else:
				return [| $(MacroField).Arguments.Cast[of $(typeRef)]($_argIndex) |]

		private def GetPrologue(type as IType):
			p = Block()
			p.Add([| $_arg = $(GetPrologueCollection(type.FullName)) |])
			return p

		private def GetPrologue[of TNode, TValue]():
			temp = CreateTemp()
			p = Block()
			p.Add([| $temp = $(GetPrologueCollection(typeof(TNode).FullName)) |])
			p.Add([| $_arg = Boo.Lang.Compiler.Ast.AstUtil.GetValues[of $TNode, $TValue]($temp) |])
			return p

		private def CreateTemp():
			return ReferenceExpression(CompilerContext.Current.GetUniqueName())

		private def AssertEnumerable(typeName as string):
			if not _enumerable:
				typeName = typeof(Boo.Lang.Compiler.Ast.Statement).FullName unless typeName
				raise "`${_arg.Name}` argument must be of enumerable type. Did you mean `${_arg.Name} as ${typeName}*`?"

	class SubMacroExtractor(DepthFirstTransformer):

		override def OnTypeMemberStatement(node as TypeMemberStatement):
			tm = node.TypeMember
			cd = node.GetAncestor[of ClassDefinition]()
			RemoveCurrentNode()
			cd.Members.Add(tm)


fixed tests

fixed tests

"""
foo: FOO
bar: 2
baz:
-foo: FOO
-bar: 2
-baz:
"""

import jQuery

def named(opts as Hash):
    named('', opts)

def named(prefix as string, opts as Hash):
    for k, v in opts:
        print "$(prefix)$k: $v"

named(foo: 'FOO', bar: 2, baz: null)
named('-', foo: 'FOO', bar: 2, baz: null)

# External methods
jQuery.ajax('http://google.com', one: 1, two: 2)

"""
foo: FOO
bar: 2
baz:
-foo: FOO
-bar: 2
-baz:
"""

import jQuery

def named(opts as Hash):
    named('', opts)

def named(prefix as string, opts as Hash):
    for v, k in opts:
        print "$(prefix)$k: $v"

named(foo: 'FOO', bar: 2, baz: null)
named('-', foo: 'FOO', bar: 2, baz: null)

# External methods
jQuery.ajax('http://google.com', one: 1, two: 2)


keep MonoDevelop project uptodate too

keep MonoDevelop project uptodate too

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

import System.Xml
import System.IO
import System.Resources
import System.Windows.Forms
import Useful.IO from Boo.Lang.Useful

class XmlDocumentWrapper:

	_document as XmlDocument

	def constructor(document as XmlDocument):
		_document = document

	def constructor(fname as string):
		_document = XmlDocument()
		_document.Load(fname)

	NamespaceURI:
		get:
			xmlns = _document.DocumentElement.Attributes["xmlns"]
			if xmlns is null: return null
			return xmlns.Value

	BaseURI:
		get:
			return System.Uri(_document.BaseURI)

	AbsolutePath:
		get:
			return BaseURI.AbsolutePath

	def Save():
		_document.Save(AbsolutePath)

class Project(XmlDocumentWrapper):

	static def Load(fname as string):
		doc = XmlDocument()
		doc.Load(fname)
		if fname.EndsWith(".mdp"):
			return MDProject(doc)
		return VS2005Project(doc)

	_sourceFiles as XmlElement

	def constructor(document as XmlDocument, sourceFiles as XmlElement):
		super(document)
		_sourceFiles = sourceFiles

	def ResetSourceFiles():
		_sourceFiles.RemoveAll()

	def AddSourceFile(item as string):
		_sourceFiles.AppendChild(CreateSourceElement(item))

	protected abstract def CreateSourceElement(src as string) as XmlElement:
		pass

class MDProject(Project):
	def constructor(document as XmlDocument):
		super(document, document.SelectSingleNode("/Project/Contents"))

	override protected def CreateSourceElement(src as string):
		file = _document.CreateElement("File", NamespaceURI)
		file.SetAttribute("name", src)
		file.SetAttribute("buildaction", "Compile")
		file.SetAttribute("subtype", "Code")
		return file

class VS2005Project(Project):

	def constructor(document as XmlDocument):
		super(document, selectCompileItemGroup(document))

	private def selectCompileItemGroup(document as XmlDocument):
		compileItem = document.SelectSingleNode("//*[local-name()='ItemGroup']/*[local-name()='Compile']")
		return compileItem.ParentNode

	override protected def CreateSourceElement(src as string):
		compile = _document.CreateElement("Compile", NamespaceURI)
		compile.SetAttribute("Include", src.Replace('/', '\\'))
		return compile

def updateProjectFile(fname as string):
	project = Project.Load(fname)
	project.ResetSourceFiles()

	baseURI = project.BaseURI
	for item as string in listFiles(Path.GetDirectoryName(baseURI.AbsolutePath)):
		if item =~ /\.svn/: continue
		if not item.EndsWith(".cs"): continue
		uri = baseURI.MakeRelative(System.Uri(item))
		project.AddSourceFile(uri)
	project.Save()

	print project.AbsolutePath

def updateStringResources(txtFile as string):
	fname = Path.ChangeExtension(txtFile, ".resx")
	File.Delete(fname)

	using resourceFile = ResXResourceWriter(fname):

		using file=File.OpenText(txtFile):
			for line in file:
				if line.StartsWith(";"): continue
				line = line.Trim()
				if len(line) == 0: continue
				index = line.IndexOf('=')
				key = line[:index]
				value = line[index+1:]

				resourceFile.AddResource(key, value)

	print fname

def rebase(fname as string):
	#return Path.Combine("c:/projects/boo/", fname)
	return fname

fnames = (
"src/Boo.Lang/Boo.Lang-VS2005.csproj",
"src/Boo.Lang.Parser/Boo.Lang.Parser-VS2005.csproj",
"src/Boo.Lang.Compiler/Boo.Lang.Compiler-VS2005.csproj",
"tests/BooCompiler.Tests/BooCompiler.Tests-VS2005.csproj",

"src/Boo.Lang.Compiler/Boo.Lang.Compiler.mdp",
)
for fname in fnames:
	updateProjectFile(rebase(fname))

updateStringResources(rebase("src/Boo.Lang/Resources/strings.txt"))

import System.Xml
import System.IO
import System.Resources
import System.Windows.Forms
import Useful.IO from Boo.Lang.Useful

class XmlDocumentWrapper:

	_document as XmlDocument

	def constructor(document as XmlDocument):
		_document = document

	def constructor(fname as string):
		_document = XmlDocument()
		_document.Load(fname)

	NamespaceURI:
		get:
			return _document.DocumentElement.Attributes["xmlns"].Value

	BaseURI:
		get:
			return System.Uri(_document.BaseURI)

	AbsolutePath:
		get:
			return BaseURI.AbsolutePath

	def Save():
		_document.Save(AbsolutePath)

class VS2005Project(XmlDocumentWrapper):

	static def Load(fname as string):
		doc = XmlDocument()
		doc.Load(fname)

		compileItem = doc.SelectSingleNode("//*[local-name()='ItemGroup']/*[local-name()='Compile']")
		compileItemGroup = compileItem.ParentNode
		return VS2005Project(doc, compileItemGroup)

	_compileItemGroup as XmlElement

	def constructor(document as XmlDocument, compileItemGroup as XmlElement):
		super(document)
		_compileItemGroup = compileItemGroup

	def ResetCompileItemGroup():
		_compileItemGroup.RemoveAll()

	def AddCompileItem(item as string):
		compile = _document.CreateElement("Compile", NamespaceURI)
		compile.SetAttribute("Include", item.Replace('/', '\\'))
		_compileItemGroup.AppendChild(compile)

def updateProjectFile(fname as string):
	project = VS2005Project.Load(fname)
	project.ResetCompileItemGroup()

	baseURI = project.BaseURI
	for item as string in listFiles(Path.GetDirectoryName(baseURI.AbsolutePath)):
		if item =~ /\.svn/: continue
		if not item.EndsWith(".cs"): continue
		uri = baseURI.MakeRelative(System.Uri(item))
		project.AddCompileItem(uri)
	project.Save()

	print project.AbsolutePath

def updateStringResources(txtFile as string):
	fname = Path.ChangeExtension(txtFile, ".resx")
	File.Delete(fname)

	using resourceFile = ResXResourceWriter(fname):

		using file=File.OpenText(txtFile):
			for line in file:
				if line.StartsWith(";"): continue
				line = line.Trim()
				if len(line) == 0: continue
				index = line.IndexOf('=')
				key = line[:index]
				value = line[index+1:]

				resourceFile.AddResource(key, value)

	print fname

def rebase(fname as string):
	#return Path.Combine("c:/projects/boo/", fname)
	return fname

fnames = (
"src/Boo.Lang/Boo.Lang-VS2005.csproj",
"src/Boo.Lang.Parser/Boo.Lang.Parser-VS2005.csproj",
"src/Boo.Lang.Compiler/Boo.Lang.Compiler-VS2005.csproj",
"tests/BooCompiler.Tests/BooCompiler.Tests-VS2005.csproj"
)
for fname in fnames:
	updateProjectFile(rebase(fname))

updateStringResources(rebase("src/Boo.Lang/Resources/strings.txt"))


minor simplification

minor simplification

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

namespace Boo.NAnt

import System
import System.IO
import NAnt.Core
import NAnt.Core.Attributes
import NAnt.Core.Types
import Boo.Lang.Compiler
import Boo.Lang.Compiler.IO
import Boo.Lang.Compiler.Ast
import Boo.Lang.Compiler.Pipelines
import Boo.Lang.Compiler.Steps

class AbstractScript:

	_project as Project
	_task as BooTask

	Project:
		get: return _project
		set: _project = value

	Task:
		get: return _task
		set: _task = value

	def print(msg):
		_task.LogInfo(msg)

	abstract def Run(argv as (string)):
		pass

class PrepareScriptStep(AbstractCompilerStep):

	override def Run():
		module = CompileUnit.Modules[0]

		method = [|
			override def Run(argv as (string)):
				pass
		|]

		method.Body = module.Globals
		module.Globals = Block()

		script = ClassDefinition(Name: "__Script__")
		script.BaseTypes.Add(SimpleTypeReference("Boo.NAnt.AbstractScript"))
		script.Members.Add(method)
		script.Members.Extend(module.Members)

		module.Members.Clear()
		module.Members.Add(script)

def WithWorkingDir(dir as string, block as callable()):
	_saved = Environment.CurrentDirectory
	Environment.CurrentDirectory = dir
	try:
		block()
	ensure:
		Environment.CurrentDirectory = _saved

[TaskName("boo")]
class BooTask(AbstractBooTask):

	_src as FileInfo
	_code as RawXml
	_arguments = ArgumentCollection()

	[TaskAttribute("src", Required: false)]
	Source:
		get: return _src
		set: _src = value

	[BuildElement("code")]
	Code as RawXml:
		get: return _code
		set: _code = value

	[BuildElementArray("arg")]
	Arguments as ArgumentCollection:
		get: return _arguments

	override def ExecuteTask():

		compiler = BooCompiler()
		parameters = compiler.Parameters
		parameters.OutputType = CompilerOutputType.Library
		if _src:
			parameters.Input.Add(FileInput(_src.ToString()))
		else:
			parameters.Input.Add(StringInput("code", ReIndent(SourceCode())))
		parameters.References.Add(GetType().Assembly)
		parameters.References.Add(typeof(NAnt.Core.Project).Assembly)

		result = RunCompiler(compiler)

		print("script successfully compiled.")
		try:
			scriptType = result.GeneratedAssembly.GetType("__Script__", true)
			script as AbstractScript = scriptType()
			script.Project = Project
			script.Task = self
			WithWorkingDir(Project.BaseDirectory) do:
				script.Run([arg.ToString() for arg in Arguments].ToArray(string))
		except x:
			raise BuildException(x.Message, Location, x)

	override def GetDefaultPipeline():
		pipeline = CompileToMemory()
		pipeline.Insert(1, PrepareScriptStep())
		return pipeline

	private def SourceCode():
		if Code is not null:
			return Code.Xml.InnerText
		return XmlNode.InnerText

def ReIndent(code as string):
	lines = NonEmptyLines(code)

	firstLine = lines[0]
	indentation = /(\s*)/.Match(firstLine).Groups[0].Value
	return code if len(indentation) == 0

	buffer = System.Text.StringBuilder()
	for line in lines:
		if not line.StartsWith(indentation):
			return code // let the parser complain about it
		buffer.AppendLine(line[len(indentation):])
	return buffer.ToString()

def NonEmptyLines(s as string):
	lines = s.Replace("\r\n", "\n").Split(char('\n'))
	return array(line for line in lines if len(line.Trim()))

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

namespace Boo.NAnt

import System
import System.IO
import NAnt.Core
import NAnt.Core.Attributes
import NAnt.Core.Types
import Boo.Lang.Compiler
import Boo.Lang.Compiler.IO
import Boo.Lang.Compiler.Ast
import Boo.Lang.Compiler.Pipelines
import Boo.Lang.Compiler.Steps

class AbstractScript:

	_project as Project
	_task as BooTask

	Project:
		get: return _project
		set: _project = value

	Task:
		get: return _task
		set: _task = value

	def print(msg):
		_task.LogInfo(msg)

	abstract def Run(argv as (string)):
		pass

class PrepareScriptStep(AbstractCompilerStep):

	override def Run():
		module = CompileUnit.Modules[0]

		method = [|
			override def Run(argv as (string)):
				pass
		|]

		method.Body = module.Globals
		module.Globals = Block()

		script = ClassDefinition(Name: "__Script__")
		script.BaseTypes.Add(SimpleTypeReference("Boo.NAnt.AbstractScript"))
		script.Members.Add(method)
		script.Members.Extend(module.Members)

		module.Members.Clear()
		module.Members.Add(script)

def WithWorkingDir(dir as string, block as callable()):
	_saved = Environment.CurrentDirectory
	Environment.CurrentDirectory = dir
	try:
		block()
	ensure:
		Environment.CurrentDirectory = _saved

[TaskName("boo")]
class BooTask(AbstractBooTask):

	_src as FileInfo
	_code as RawXml
	_arguments = ArgumentCollection()

	[TaskAttribute("src", Required: false)]
	Source:
		get: return _src
		set: _src = value

	[BuildElement("code")]
	Code as RawXml:
		get: return _code
		set: _code = value

	[BuildElementArray("arg")]
	Arguments as ArgumentCollection:
		get: return _arguments

	override def ExecuteTask():

		compiler = BooCompiler()
		parameters = compiler.Parameters
		parameters.OutputType = CompilerOutputType.Library
		if _src:
			parameters.Input.Add(FileInput(_src.ToString()))
		else:
			parameters.Input.Add(StringInput("boo", reindent(getSourceCode())))
		parameters.References.Add(GetType().Assembly)
		parameters.References.Add(typeof(NAnt.Core.Project).Assembly)

		result = RunCompiler(compiler)

		print("script successfully compiled.")
		try:
			scriptType = result.GeneratedAssembly.GetType("__Script__", true)
			script as AbstractScript = scriptType()
			script.Project = Project
			script.Task = self
			WithWorkingDir(Project.BaseDirectory) do:
				script.Run([arg.ToString() for arg in Arguments].ToArray(string))
		except x:
			raise BuildException(x.Message, Location, x)

	override def GetDefaultPipeline():
		pipeline = CompileToMemory()
		pipeline.Insert(1, PrepareScriptStep())
		return pipeline

	private def getSourceCode():
		if Code is not null:
			return Code.Xml.InnerText
		return XmlNode.InnerText

	private def reindent(code as string):
		lines = /\n/.Split(code.Replace("\r\n", "\n"))
		lines = array(line for line in lines if len(line.Trim()))

		first = lines[0]
		indent = /(\s*)/.Match(first).Groups[0].Value
		return code if 0 == len(indent)

		buffer = System.Text.StringBuilder()
		for line in lines:
			if not line.StartsWith(indent):
				return code // let the parser complain about it

			buffer.Append(line[len(indent):])
			buffer.Append("\n")
		return buffer.ToString()


fixed example

fixed example

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

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

"""
This example shows how to use IQuackFu.QuackGet to provide
transparent access to xml elements.
"""

import System
import System.Xml from System.Xml
import System.Reflection

[DefaultMember("Item")]
class XmlObject(IQuackFu):

	_element as XmlElement

	def constructor(element as XmlElement):
		_element = element

	def constructor(text as string):
		doc = XmlDocument()
		doc.LoadXml(text)
		_element = doc.DocumentElement

	def QuackInvoke(name as string, args as (object)) as object:
		if name == "op_Addition":
			doc as XmlDocument = _element.ParentNode.CloneNode(true)
			tmp = XmlObject(doc.DocumentElement)
			docFrag = doc.CreateDocumentFragment()
			docFrag.InnerXml = args[1]
			tmp._element.AppendChild(docFrag)
			return tmp
		else:
			raise System.InvalidOperationException("Method ${name} not found in class ${self.GetType()}")

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

	def QuackGet(name as string, parameters as (object)) as object:
		if name == "":
			assert len(parameters) == 1
			return GetAttribute(parameters[0])

		elements = _element.SelectNodes(name)
		if elements is not null:
			return XmlObject(elements[0]) if elements.Count == 1
			return XmlObject(e) for e as XmlElement in elements

	def GetAttribute(name as string):
		item = _element.Attributes.GetNamedItem(name)
		return item.InnerText if item

	override def ToString():
		return _element.InnerText

xml = """
<Person>
	<FirstName>John</FirstName>
	<LastName>Cleese</LastName>
	<Phone place="home">1111-111-111</Phone>
	<Phone place="work">2222-222-222</Phone>
</Person>
"""

person = XmlObject(xml)
print person.FirstName
print person.LastName
person += "<Phone place=\"cell\">3333-333-333</Phone>"
for phone as XmlObject in person.Phone:
	print phone['place'], phone

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

"""
This example shows how to use IQuackFu.QuackGet to provide
transparent access to xml elements.
"""

import System
import System.Xml from System.Xml
import System.Reflection

[DefaultMember("Item")]
class XmlObject(IQuackFu):

	_element as XmlElement

	def constructor(element as XmlElement):
		_element = element

	def constructor(text as string):
		doc = XmlDocument()
		doc.LoadXml(text)
		_element = doc.DocumentElement

	def QuackInvoke(name as string, args as (object)) as object:
		if name == "op_Addition":
			doc as XmlDocument = _element.ParentNode.CloneNode(true)
			tmp = XmlObject(doc.DocumentElement)
			docFrag = doc.CreateDocumentFragment()
			docFrag.InnerXml = args[1]
			tmp._element.AppendChild(docFrag)
			return tmp
		else:
			raise System.InvalidOperationException("Method ${name} not found in class ${self.GetType()}")

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

	def QuackGet(name as string, parameters as (object)) as object:
		if name == "":
			assert len(parameters) == 1
			return GetAttribute(parameters[0])

		elements = _element.SelectNodes(name)
		if elements is not null:
			return XmlObject(elements[0]) if elements.Count == 1
			return XmlObject(e) for e in elements

	def GetAttribute(name as string):
		item = _element.Attributes.GetNamedItem(name)
		return item.InnerText if item

	override def ToString():
		return _element.InnerText

xml = """
<Person>
	<FirstName>John</FirstName>
	<LastName>Cleese</LastName>
	<Phone place="home">1111-111-111</Phone>
	<Phone place="work">2222-222-222</Phone>
</Person>
"""

person = XmlObject(xml)
print person.FirstName
print person.LastName
person += "<Phone place=\"cell\">3333-333-333</Phone>"
for phone as XmlObject in person.Phone:
	print phone['place'], phone