prepend tokens to the input instead of relying on complex buffering schemes

namespace metaboo

import System.Text
import Boo.OMeta
import Boo.PatternMatching
import Boo.Adt
import Boo.Lang.Compiler
import Boo.Lang.Compiler.Ast

/*
Before the first line of the file is read, a single zero
is pushed on the stack; this will never be popped off again.
The numbers pushed on the stack will always be strictly
increasing from bottom to top. At the beginning of each
logical line, the line's indentation level is compared
to the top of the stack.

If it is equal, nothing happens. If it is larger,
it is pushed on the stack, and one INDENT token is generated.
If it is smaller, it must be one of the numbers occurring
on the stack; all numbers on the stack that are larger
are popped off, and for each number popped off
a DEDENT token is generated.

At the end of the file, a DEDENT token is generated
for each number remaining on the stack that is
larger than zero.

http://docs.python.org/ref/indentation.html
*/

data Token(kind as string, value as string)

macro tokens:
	block as Block = tokens.ParentNode

	rules = []
	for stmt in tokens.Block.Statements:
		match stmt:
			case ExpressionStatement(Expression: [| $name = $pattern |]):
				e = [| $name = $pattern >> value ^ makeToken($(name.ToString()), value) |]
				e.LexicalInfo = stmt.LexicalInfo
				block.Add(e)
				rules.Add(name)

	rule as Expression = rules[0]
	for name as Expression in rules[1:]:
		rule = [| $name | $rule |]
	block.Add([| tokens = $rule |])

let stack = [0]

ometa Tokenizer:

	tokens:
		eq = "="
		num = ++digit
		id = (letter | '_'), --(letter | digit | '_')
		colon = ":"
		lparen = "("
		rparen = ")"
		kw = (keywords >> value, ~(letter | digit)) ^ value

	keywords = "class" | "pass" | "def"

	keyword[expected] = ((token["kw"] >> t) and (expected is tokenValue(t))) ^ t

	token[expected] = (scanner >> t and tokenMatches(t, expected)) ^ t

	scanner = (
		(
			  (((_ >> t) and (t isa Token)) ^ t) // token introduced by processDedent
			| (((indentation >> i) and sameIndent(i)) ^ makeToken("eol"))
			| (((indentation >> i) and largerIndent(i)) ^ makeToken("indent"))
			| (((indentation >> i) and smallerIndent(i), $(processDedent(input, i)) >> value) ^ value)
			| ((--whitespace, tokens >> t) ^ t)
		) >> value
	) ^ value

	indentation = (emptyLines, spaces >> value, ~whitespace) ^ value
	emptyLines = ++(~~emptyLine, emptyLine)
	emptyLine = spaces, newline

	spaces = --space

	space = ' ' | '\t'

	module = (--whitespace, ++classDef >> types) ^ types

	classDef = (
		keyword["class"], token["id"] >> className, token["colon"], token["indent"], classBody >> body, token["dedent"]
	) ^ [className, body]

	classBody = (keyword["pass"], eol) ^ null | ++classMember

	classMember = method | classDef

	method = (
		keyword["def"], token["id"], token["lparen"], token["rparen"], token["colon"],
			token["indent"], methodBody, token["dedent"]
	)

	methodBody = ++stmt

	stmt = (assign >> value, eol) ^ value

	eol = ++token["eol"] | ~_

	newline = '\n' | "\r\n" | "\r"

	assign = lvalue, token["eq"], rvalue

	lvalue = token["id"]

	rvalue = token["num"] | token["id"]

	def trace(input as OMetaInput, value):
		print "trace:", value, stack
		return SuccessfulMatch(input, value)

	def sameIndent(i):
		return currentIndent() == len(i)

	def largerIndent(i):
		if len(i) > currentIndent():
			stack.Push(len(i))
			return true

	def processDedent(input as OMetaInput, i):
		while smallerIndent(i):
			input = OMetaInput.ForArgument(makeToken("dedent"), input)
			stack.Pop()
		assert sameIndent(i)
		return SuccessfulMatch(input, makeToken("eol"))

	def smallerIndent(i):
		return len(i) < currentIndent()

	def currentIndent() as int:
		return stack[-1]

	def makeToken(kind):
		return Token(kind, kind)

	def makeToken(kind, value):
		return Token(kind, flatString(value))

	def flatString(value) as string:
		if value isa string: return value
		buffer = StringBuilder()
		flatString buffer, value
		return buffer.ToString()

	def flatString(buffer as StringBuilder, value):
		match value:
			case string():
				buffer.Append(value)
			case char():
				buffer.Append(value)
			otherwise:
				for item in value:
					flatString buffer, item

	def tokenMatches(token as Token, expected):
		return expected is token.kind

	def tokenValue(token as Token):
		return token.value

def scan(text as string):
	t = Tokenizer()
	input = OMetaInput.For(text)
	while true:
		match t.scanner(input):
			case SuccessfulMatch(Input, Value):
				input = Input
				yield Value
			case FailedMatch(Input):
				#assert Input.IsEmpty, Input.ToString()
				print Input
				break

def printTokens(text as string):
	sep = "=" * 20
	print sep
	for token in scan(text):
		print token
	print sep

#print Tokenizer().indentation(" \n  foo")

printTokens """
class class0:

	class Bar:
		pass

	def foo():
		a = 3
		a = 4
"""

assert stack == [0]

code = """
class Foo:
	def foo():
		a = 3
class Bar:
	class Baz:
		pass
	class Gazong:
		pass
"""
printTokens code
print Tokenizer().module(code)

code = """def foo():
	a = 3
"""
printTokens code
print Tokenizer().method(code)

namespace metaboo

import Boo.OMeta
import Boo.PatternMatching
import Boo.Adt
import Boo.Lang.Compiler
import Boo.Lang.Compiler.Ast

/*
Before the first line of the file is read, a single zero
is pushed on the stack; this will never be popped off again.
The numbers pushed on the stack will always be strictly
increasing from bottom to top. At the beginning of each
logical line, the line's indentation level is compared
to the top of the stack.

If it is equal, nothing happens. If it is larger,
it is pushed on the stack, and one INDENT token is generated.
If it is smaller, it must be one of the numbers occurring
on the stack; all numbers on the stack that are larger
are popped off, and for each number popped off
a DEDENT token is generated.

At the end of the file, a DEDENT token is generated
for each number remaining on the stack that is
larger than zero.

http://docs.python.org/ref/indentation.html
*/

data Token(kind as string, value as string)

macro tokens:
	block as Block = tokens.ParentNode

	rules = []
	for stmt in tokens.Block.Statements:
		match stmt:
			case ExpressionStatement(Expression: [| $name = $pattern |]):
				e = [| $name = $pattern >> value ^ makeToken($(name.ToString()), value) |]
				e.LexicalInfo = stmt.LexicalInfo
				block.Add(e)
				rules.Add(name)

	rule as Expression = rules[0]
	for name as Expression in rules[1:]:
		rule = [| $name | $rule |]
	block.Add([| tokens = $rule |])

let stack = [0]

ometa Tokenizer:

	tokens:
		eq = "="
		num = ++digit
		id = ((letter | '_') >> prefix, ++(letter | digit | '_') >> suffix) ^ (prefix.ToString() + join(suffix, ''))
		colon = ":"
		kw = (keywords >> value, ~(letter | digit)) ^ value

	keywords = "class" | "pass"

	keyword[expected] = ((token["kw"] >> t) and (expected == tokenValue(t))) ^ t

	token[expected] = (scanner >> t and tokenMatches(t, expected)) ^ t

	scanner = (
		(
		(((indentation >> i) and sameIndent(i), tokens >> t) ^ t)
		| (((indentation >> i) and largerIndent(i)) ^ makeToken("indent"))
		| (((indentation >> i) and smallerIndent(i)) ^ makeToken("dedent"))
		| ((--whitespace, tokens >> t) ^ t)
		| (((--whitespace, ~_) and bufferedDedent()) ^ makeToken("dedent"))
		) >> value
	) ^ value

	indentation = (emptyLines, ++(' ' | '\t') >> value, ~whitespace) ^ value
	emptyLines = ++(~~emptyLine, emptyLine)
	emptyLine = spaces, newline

	spaces = --(' ' | '\t')

	classDef = keyword["class"], token["id"], token["colon"], token["indent"], classBody, token["dedent"]

	classBody = (keyword["pass"], eos) | classDef

	eos = newline | ~_

	newline = '\n' | "\r\n" | "\r"

	assign = lvalue, token["eq"], rvalue

	lvalue = token["id"]

	rvalue = token["num"] | token["id"]

	def trace(input as OMetaInput, value):
#		print "trace:", value, stack
		return SuccessfulMatch(input, value)

	def sameIndent(i):
#		print "sameIndent", stack, len(i)
		return currentIndent() == len(i)

	def largerIndent(i):
		if len(i) > currentIndent():
#			print "largerIndent", stack
			stack.Push(len(i))
			return true

	def smallerIndent(i):
		if len(i) < currentIndent():
			stack.Pop()
#			print "smallerIndent", stack
			return true

	def bufferedDedent():
#		print "bufferedDedent: ", stack
		if len(stack) > 1:
			stack.Pop()
			return true

	def currentIndent() as int:
		return stack[0]

	def makeToken(kind):
		return Token(kind, kind)

	def makeToken(kind, value):
		return Token(kind, join(value, ''))

	def tokenMatches(token as Token, expected):
		return expected is token.kind

	def tokenValue(token as Token):
		return token.value

def scan(text as string):
	t = Tokenizer()
	input = OMetaInput.For(text)
	while true:
		match t.scanner(input):
			case SuccessfulMatch(Input, Value):
				input = Input
				yield Value
			case FailedMatch(Input):
				assert Input.IsEmpty
				break

#print Tokenizer().indentation(" \n  foo")

code = """
class class0:

	class Bar:
		pass
"""
for token in scan(code):
	print token

print Tokenizer().classDef(code)


Malkovich! Malkovich! Malkovich!

Malkovich! Malkovich! Malkovich!

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

"""
Malkovich!
Malkovich!
Malkovich!
"""
callable Malkovich() as Malkovich

def malkovich() as Malkovich:
	print("Malkovich!")
	return malkovich

malkovich()()()

"""
Echo!
Echo!
Echo!
"""
callable Echo() as Echo

def echo() as Echo:
	print("Echo!")
	return echo

echo()()()


Fixed some generator instrumentation corner cases

namespace BooJs.Compiler.Steps

import System
import Boo.Lang.Compiler(CompilerErrorFactory)
import Boo.Lang.Compiler.Ast
import Boo.Lang.Compiler.Steps
import Boo.Lang.Compiler.TypeSystem.Internal
import Boo.Lang.PatternMatching

class ProcessGenerators(AbstractTransformerCompilerStep):
"""
    Specialized step to process generators replacing Boo's one. Code is
    transformed to a reentrant state machine, allowing to halt execution
    where a yield is found and resume later on from that same point.
"""
    class TransformGenerator(FastDepthFirstVisitor):
        [getter(States)]
        _states = StatementCollection()

        property State as int = 0

        Current as Block:
            get: return States[State]

        _statemachinelabel = ReferenceExpression('statemachine')
        _gotostatemachine = GotoStatement(Label: _statemachinelabel)

        def constructor():
            CreateState()

        def CreateState():
            block = Block()
            States.Add(block)
            return len(States)-1

        def OnExpressionStatement(node as ExpressionStatement):
            Current.Add(node)

        def OnForStatement(node as ForStatement):
            Current.Add(node)

        def OnYieldStatement(node as YieldStatement):
            if node.GetAncestor(NodeType.IfStatement) or node.GetAncestor(NodeType.UnlessStatement):
                raise CompilerErrorFactory.NotImplemented(node, 'Yield is not currently supported inside if or unless statements')

            # Create a re-entry state
            nextstate = CreateState()

            # Setup jump to next state and return value
            Current.Add([| __state = $(nextstate) |])
            Current.Add([| return $(node.Expression) |])

            # Continue in the re-entry state
            State = nextstate

        def OnReturnStatement(node as ReturnStatement):
            if node.GetAncestor(NodeType.IfStatement) or node.GetAncestor(NodeType.UnlessStatement):
                raise CompilerErrorFactory.NotImplemented(node, 'Return is not currently supported inside if or unless statements')

            # If it has an expression just handle as a yield
            if node.Expression:
                ynode = YieldStatement(node.LexicalInfo, Expression: node.Expression)
                OnYieldStatement(ynode)
            # Otherwise just create a new state where the generator terminates
            else:
                nextstate = CreateState()
                Current.Add([| __state = $(nextstate) |])
                Current.Add(_gotostatemachine)
                State = nextstate

            # At this point we want to always stop the generator
            Current.Add([| raise Boo.STOP |])

        def OnWhileStatement(node as WhileStatement):
            # Loop always starts in a new step
            loopstate = CreateState()

            # Make sure the current state ends up in the loop one
            Current.Add([| __state = $(loopstate) |])
            Current.Add(_gotostatemachine)

            State = loopstate

            # Create new step for exiting the loop but don't use it yet
            exitstate = CreateState()

            # Check if we should skip the loop by negating the condition
            ifs = [|
                if not $(node.Condition):
                    __state = $(exitstate)
                    $(_gotostatemachine)
            |]
            Current.Add(ifs)

            # Process the loop body
            Visit node.Block

            # Go back to the start of the loop to check if we have more iterations left
            Current.Add([| __state = $(loopstate) |])
            Current.Add(_gotostatemachine)

            # Continue in the exit step previously created
            State = exitstate

        def OnTryStatement(node as TryStatement):
            # A try block always initiates an state
            trystate = CreateState()

            # Make sure the current state ends up in the new one
            Current.Add([| __state = $(trystate) |])
            Current.Add(_gotostatemachine)

            State = trystate

            # Register ensure block
            if node.EnsureBlock:
                Current.Add([| __final.push( $(BlockExpression(Body: node.EnsureBlock)) ) |])

            Visit node.ProtectedBlock

            # Execute ensure block after we reach the end of the protected block
            if node.EnsureBlock:
                Current.Add( [| __final.pop()() |] )

            # Create a new state for the exception handler
            exceptstate = CreateState()

            # Register the states covered by this protected block
            tryblock = States[trystate] as Block
            catchst = [| $exceptstate |]
            for state in range(trystate, exceptstate):
                catchst = [| __catch[$state] = $catchst |]
            tryblock.Insert(0, catchst)

            # Create a new state for exiting the try/except block
            afterstate = CreateState()

            # Skip the exception handler state
            Current.Add( [| __state = $afterstate |] )
            Current.Add(_gotostatemachine)

            # Produce the exception handler
            State = exceptstate
            Visit node.ExceptionHandlers

            # Continue in the after state
            State = afterstate

        def OnUnlessStatement(node as UnlessStatement):
        """ We just include them in the current state, no special processing is performed
            currently. This means that neither yield nor return statements can be used inside.
        """
            super(node)
            Current.Add(node)

        def OnIfStatement(node as IfStatement):
        """ We just include them in the current state, no special processing is performed
            currently. This means that neither yield nor return statements can be used inside.
        """
            super(node)
            Current.Add(node)

    override def Run():
        if len(Errors) > 0:
            return
        Visit CompileUnit

    def HasTryStatement(node as Block) as bool:
        for st in node.Statements:
            match st:
                case TryStatement():
                    return true
                case wst=WhileStatement():
                    return HasTryStatement(wst.Block)
                case ist=IfStatement():
                    return HasTryStatement(ist.TrueBlock) or HasTryStatement(ist.FalseBlock)
                case bst=Block():
                    return HasTryStatement(bst)
                otherwise:
                    continue

        return false

    override def EnterMethod(node as Method):
        # Types should be already resolved so we can just check if it was flagged as a generator
        entity = node.Entity as InternalMethod
        return entity and entity.IsGenerator

    override def LeaveMethod(node as Method):

        has_try = HasTryStatement(node.Body)

        # Transform the method body into a list of states
        transformer = TransformGenerator()
        transformer.OnBlock(node.Body)

        # If the method is a closure we look for the original method node to declare locals in it
        method = node
        if node.IsSynthetic:
            for member in (node.ParentNode as TypeDefinition).Members:
                if member.LexicalInfo.Equals(node.LexicalInfo):
                    method = member
                    break

        # Remove the original method contents
        node.Body.Clear()
        # Create a new local to keep the current step of the generator
        CodeBuilder.DeclareLocal(method, '__state', TypeSystemServices.IntType)
        if has_try:
            CodeBuilder.DeclareLocal(method, '__catch', TypeSystemServices.HashType)
            CodeBuilder.DeclareLocal(method, '__final', TypeSystemServices.ArrayType)
            node.Body.Add( CodeBuilder.CreateAssignment(ReferenceExpression('__catch'), HashLiteralExpression()) )
            node.Body.Add( CodeBuilder.CreateAssignment(ReferenceExpression('__final'), ListLiteralExpression()) )

        # Add value check to first state
        valuecheck = [|
            if __value is not Boo.UNDEF or __error is not Boo.UNDEF:
                raise TypeError('Generator not started yet, unable to process sent value/error')
        |]
        (transformer.States[0] as Block).Insert(0, valuecheck)

        # Wrap the steps into a switch/case construct
        switch = MacroStatement(Name: 'switch')
        switch.Arguments.Add(ReferenceExpression('__state'))
        for idx as int, step as Block in enumerate(transformer.States):
            case = MacroStatement()
            case.Name = 'case'
            case.Arguments.Add(IntegerLiteralExpression(idx))
            case.Body = step
            switch.Body.Add(case)

        statemachine as Statement
        statemachine = [|
            # NOTE: To simplify we will raise the sent error from the top of the generator
            #       function and not from the current step. Unless real world scenarios show
            #       that it's desirable to have it raised from more exact locations, it doesn't
            #       seem to have a great value added in contrast to its cost.
            if __error:
                raise __error

            $switch

            # Set the state to an imposible value
            __state = -1
            raise Boo.STOP
        |]

        # Wrap the state machine to support exception handling
        if has_try:
            statemachine = [|
                try:
                    $statemachine
                except:
                    if __state in __catch:
                        __error = null  # Make sure any reported error is removed
                        __state = __catch[__state]
                        goto statemachine

                    # Position the generator to the terminating state
                    __state = -1

                    # Ensure all finally blocks are executed
                    while __final.length:
                        __final.pop()()

                    # Re-raise the exception
                    raise __e
            |]

        # Make the method return a generator
        generator = [|
            return Boo.generator do(__value, __error):
                :statemachine
                $statemachine
        |]
        node.Body.Add(generator)

namespace BooJs.Compiler.Steps

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

class ProcessGenerators(AbstractTransformerCompilerStep):
"""
    Specialized step to process generators replacing Boo's one. Code is
    transformed to a reentrant state machine, allowing to halt execution
    where a yield is found and resume later on from that same point.
"""
    class TransformGenerator(FastDepthFirstVisitor):
        [getter(States)]
        _states = StatementCollection()

        property State as int = 0

        Current as Block:
            get: return States[State]

        _statemachinelabel = ReferenceExpression('statemachine')
        _gotostatemachine = GotoStatement(Label: _statemachinelabel)

        def constructor():
            CreateState()

        def CreateState():
            block = Block()
            States.Add(block)
            return len(States)-1

        def OnExpressionStatement(node as ExpressionStatement):
            Current.Add(node)

        def OnForStatement(node as ForStatement):
            Current.Add(node)

        def OnYieldStatement(node as YieldStatement):
            # Create a re-entry state
            nextstate = CreateState()

            # Setup jump to next state and return value
            Current.Add([| __state = $(nextstate) |])
            Current.Add([| return $(node.Expression) |])

            # Continue in the re-entry state
            State = nextstate

        def OnReturnStatement(node as ReturnStatement):
            # If it has an expression just handle as a yield
            if node.Expression:
                ynode = YieldStatement(node.LexicalInfo, Expression: node.Expression)
                OnYieldStatement(ynode)
            # Otherwise just create a new state where the generator terminates
            else:
                nextstate = CreateState()
                Current.Add([| __state = $(nextstate) |])
                Current.Add(_gotostatemachine)
                State = nextstate

            # At this point we want to always stop the generator
            Current.Add([| raise Boo.STOP |])

        def OnWhileStatement(node as WhileStatement):
            # Loop always starts in a new step
            State = loopstate = CreateState()

            # Create new step for exiting the loop but don't use it yet
            exitstate = CreateState()

            # Check if we should skip the loop by negating the condition
            ifs = [|
                if not $(node.Condition):
                    __state = $(exitstate)
                    $(_gotostatemachine)
            |]
            Current.Add(ifs)

            # Process the loop body
            Visit node.Block

            # Go back to the start of the loop to check if we have more iterations left
            Current.Add([| __state = $(loopstate) |])
            Current.Add(_gotostatemachine)

            # Continue in the exit step previously created
            State = exitstate

        def OnTryStatement(node as TryStatement):
            # A try block always initiates an state
            State = trystate = CreateState()

            # Register ensure block
            if node.EnsureBlock:
                Current.Add([| __final.push( $(BlockExpression(Body: node.EnsureBlock)) ) |])

            Visit node.ProtectedBlock

            # Execute ensure block after we reach the end of the protected block
            if node.EnsureBlock:
                Current.Add( [| __final.pop()() |] )

            # Create a new state for the exception handler
            exceptstate = CreateState()

            # Register the states covered by this protected block
            tryblock = States[trystate] as Block
            catchst = [| $exceptstate |]
            for state in range(trystate, exceptstate):
                catchst = [| __catch[$state] = $catchst |]
            tryblock.Insert(0, catchst)

            # Create a new state for exiting the try/except block
            afterstate = CreateState()

            # Skip the exception handler state
            Current.Add( [| __state = $afterstate |] )
            Current.Add(_gotostatemachine)

            # Produce the exception handler
            State = exceptstate
            Visit node.ExceptionHandlers

            # Continue in the after state
            State = afterstate

    override def Run():
        if len(Errors) > 0:
            return
        Visit CompileUnit

    def HasTryStatement(node as Block) as bool:
        for st in node.Statements:
            match st:
                case TryStatement():
                    return true
                case wst=WhileStatement():
                    return HasTryStatement(wst.Block)
                case ist=IfStatement():
                    return HasTryStatement(ist.TrueBlock) or HasTryStatement(ist.FalseBlock)
                case bst=Block():
                    return HasTryStatement(bst)
                otherwise:
                    continue

        return false

    override def EnterMethod(node as Method):
        # Types should be already resolved so we can just check if it was flagged as a generator
        entity = node.Entity as InternalMethod
        return entity and entity.IsGenerator

    override def LeaveMethod(node as Method):

        has_try = HasTryStatement(node.Body)

        # Transform the method body into a list of states
        transformer = TransformGenerator()
        transformer.OnBlock(node.Body)

        # If the method is a closure we look for the original method node to declare locals in it
        method = node
        if node.IsSynthetic:
            for member in (node.ParentNode as TypeDefinition).Members:
                if member.LexicalInfo.Equals(node.LexicalInfo):
                    method = member
                    break

        # Remove the original method contents
        node.Body.Clear()
        # Create a new local to keep the current step of the generator
        CodeBuilder.DeclareLocal(method, '__state', TypeSystemServices.IntType)
        if has_try:
            CodeBuilder.DeclareLocal(method, '__catch', TypeSystemServices.HashType)
            CodeBuilder.DeclareLocal(method, '__final', TypeSystemServices.ArrayType)
            node.Body.Add( CodeBuilder.CreateAssignment(ReferenceExpression('__catch'), HashLiteralExpression()) )
            node.Body.Add( CodeBuilder.CreateAssignment(ReferenceExpression('__final'), ListLiteralExpression()) )

        # Add value check to first state
        valuecheck = [|
            if __value is not Boo.UNDEF or __error is not Boo.UNDEF:
                raise TypeError('Generator not started yet, unable to process sent value/error')
        |]
        (transformer.States[0] as Block).Insert(0, valuecheck)

        # Wrap the steps into a switch/case construct
        switch = MacroStatement(Name: 'switch')
        switch.Arguments.Add(ReferenceExpression('__state'))
        for idx as int, step as Block in enumerate(transformer.States):
            case = MacroStatement()
            case.Name = 'case'
            case.Arguments.Add(IntegerLiteralExpression(idx))
            case.Body = step
            switch.Body.Add(case)

        statemachine as Statement
        statemachine = [|
            # NOTE: To simplify we will raise the sent error from the top of the generator
            #       function and not from the current step. Unless real world scenarios show
            #       that it's desirable to have it raised from more exact locations, it doesn't
            #       seem to have a great value added in contrast to its cost.
            if __error:
                raise __error

            $switch

            # Set the state to an imposible value
            __state = -1
            raise Boo.STOP
        |]

        # Wrap the state machine to support exception handling
        if has_try:
            statemachine = [|
                try:
                    $statemachine
                except:
                    if __state in __catch:
                        __error = null  # Make sure any reported error is removed
                        __state = __catch[__state]
                        goto statemachine

                    # Position the generator to the terminating state
                    __state = -1

                    # Ensure all finally blocks are executed
                    while __final.length:
                        __final.pop()()

                    # Re-raise the exception
                    raise __e
            |]

        # Make the method return a generator
        generator = [|
            return Boo.generator do(__value, __error):
                :statemachine
                $statemachine
        |]
        node.Body.Add(generator)


friendly PatternMatching error reporting

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

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

namespace Boo.Lang.PatternMatching

import Boo.Lang.PatternMatching.Impl

# TODO: check for unreacheable patterns
macro match:
"""
Pattern matching facility:

	match <expression>:
		case <Pattern1>:
			<block1>
			.
			.
			.
		case <PatternN>:
			<blockN>
		otherwise:
			<blockOtherwise>

The following patterns are supported:

    Type() -- type test pattern
    Type(Property1: Pattern1, ...) -- object pattern
    Pattern1 | Pattern2 -- either pattern
	Pattern1 & Pattern2 -- both pattern (NOT IMPLEMENTED)
    Pattern1 and condition -- constrained pattern  (NOT IMPLEMENTED)
    Pattern1 or condition -- constrained pattern  (NOT IMPLEMENTED)
    (Pattern1, Pattern2) -- fixed size iteration pattern
    [Pattern1, Pattern2] -- arbitrary size iteration pattern (NOT IMPLEMENTED)
    x = Pattern -- variable binding
    x -- variable binding
    BinaryOperatorType.Assign -- constant pattern
    42 -- constant pattern
    "42" -- constant pattern
    null -- null test pattern

If no pattern matches MatchError is raised.
"""
	macro case:
		caseListFor(match).Add(case)

	macro otherwise:
		otherwiseNode = match["otherwise"] as Boo.Lang.Compiler.Ast.Node
		assert otherwiseNode is null, "`otherwise' is already defined at: ${otherwiseNode.LexicalInfo}"
		match["otherwise"] = otherwise

	assert 0 == len(match.Body.Statements), "Only `case' or `otherwise' are allowed in `match'. Offending statement at: ${match.Body.Statements[0].LexicalInfo}"
	assert 0 != len(caseListFor(match)), "`match' must contain at least one `case'"
	return MatchExpansion(Context, match).Value

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

namespace Boo.Lang.PatternMatching

import Boo.Lang.PatternMatching.Impl

# TODO: check for unreacheable patterns
macro match:
"""
Pattern matching facility:

	match <expression>:
		case <Pattern1>:
			<block1>
			.
			.
			.
		case <PatternN>:
			<blockN>
		otherwise:
			<blockOtherwise>

The following patterns are supported:

    Type() -- type test pattern
    Type(Property1: Pattern1, ...) -- object pattern
    Pattern1 | Pattern2 -- either pattern
	Pattern1 & Pattern2 -- both pattern (NOT IMPLEMENTED)
    Pattern1 and condition -- constrained pattern  (NOT IMPLEMENTED)
    Pattern1 or condition -- constrained pattern  (NOT IMPLEMENTED)
    (Pattern1, Pattern2) -- fixed size iteration pattern
    [Pattern1, Pattern2] -- arbitrary size iteration pattern (NOT IMPLEMENTED)
    x = Pattern -- variable binding
    x -- variable binding
    BinaryOperatorType.Assign -- constant pattern
    42 -- constant pattern
    "42" -- constant pattern
    null -- null test pattern

If no pattern matches MatchError is raised.
"""
	macro case:
		caseListFor(match).Add(case)

	macro otherwise:
		assert match["otherwise"] is null
		match["otherwise"] = otherwise

	assert 0 == len(match.Body.Statements)
	return MatchExpansion(Context, match).Value


code cleanup

code cleanup

namespace UnityScript.Steps

import System
import Boo.Lang.Environments
import Boo.Lang.Compiler
import Boo.Lang.Compiler.Ast
import Boo.Lang.Compiler.TypeSystem
import Boo.Lang.Compiler.TypeSystem.Services
import UnityScript.Macros
import UnityScript.Scripting

class EvaluationDomainProviderImplementor(AbstractCompilerComponent):

	def ImplementIEvaluationDomainProviderOn(node as ClassDefinition):
		domainField = ReferenceExpression(Context.GetUniqueName("domain"))
		evaluationDomainProviderImpl = [|
			class _($IEvaluationDomainProvider):

				private $domainField as $EvaluationDomain

				def GetEvaluationDomain():
					return $domainField or ($domainField = $EvaluationDomain())

				def GetImports():
					return $(ImportsArrayFor(node))

				def GetAssemblyReferences():
					return $(AssemblyReferencesArray())
		|]

		my(CodeReifier).MergeInto(node, evaluationDomainProviderImpl)

	def StaticEvaluationDomainProviderFor(node as ClassDefinition) as IField:
		perNode node:
			return CreateStaticEvaluationDomainProviderReferenceOn(node)

	private def AssemblyReferencesArray() as Expression:
		perNode CompileUnit:
			return CodeBuilder.CreateReference(CreateAssemblyReferencesArray())

	private def ImportsArrayFor(node as ClassDefinition):
		result = [| (of $string:,) |]
		result.Items.Extend([| $(i.Namespace) |] for i in node.EnclosingModule.Imports)
		return result

	private def CreateStaticEvaluationDomainProviderReferenceOn(node as ClassDefinition):
		evaluationDomainProviderImpl = [|
			internal class StaticEvaluationDomainProvider($SimpleEvaluationDomainProvider):

				public static final Instance = StaticEvaluationDomainProvider()

				def constructor():
					super($(ImportsArrayFor(node)))

				override def GetAssemblyReferences():
					return $(AssemblyReferencesArray())
		|]
		my(CodeReifier).ReifyInto(node, evaluationDomainProviderImpl)
		return evaluationDomainProviderImpl.Members["Instance"].Entity

	private def CreateAssemblyReferencesArray() as IField:
		references = [| (System.Reflection.Assembly.GetExecutingAssembly(),) |]

		for assembly in ReferencedAssemblies():
			publicType = AnyPublicTypeOf(assembly)
			continue if publicType is null
			references.Items.Add([| typeof($publicType).Assembly |])

		arrayHolder = [|
			internal class EvalAssemblyReferences:
				public static final Value = $references
		|]
		my(CodeReifier).ReifyInto(CompileUnit.Modules[0], arrayHolder)
		return arrayHolder.Members["Value"].Entity

	private def AnyPublicTypeOf(assembly as System.Reflection.Assembly):
		for t in assembly.GetTypes():
			return t if t.IsPublic and not t.IsGenericType

	private def ReferencedAssemblies():
		collector = ReferencedAssemblyCollector()
		CompileUnit.Accept(collector)
		return collector.ReferencedAssemblies

class ReferencedAssemblyCollector(DepthFirstVisitor):

	[getter(ReferencedAssemblies)]
	_assemblies = Boo.Lang.Compiler.Util.Set of Assembly()

	override def OnReferenceExpression(node as ReferenceExpression):
		CheckTypeReference(node)

	override def LeaveMemberReferenceExpression(node as MemberReferenceExpression):
		CheckTypeReference(node)

	override def OnSimpleTypeReference(node as SimpleTypeReference):
		CheckTypeReference(node)

	def CheckTypeReference(node as Node):
		type = node.Entity as Boo.Lang.Compiler.TypeSystem.Reflection.ExternalType
		if type is null: return
		_assemblies.Add(type.ActualType.Assembly)

namespace UnityScript.Steps

import System
import Boo.Lang.Environments
import Boo.Lang.Compiler
import Boo.Lang.Compiler.Ast
import Boo.Lang.Compiler.TypeSystem
import Boo.Lang.Compiler.TypeSystem.Services
import Boo.Lang.Compiler.TypeSystem.Reflection
import UnityScript.Macros
import UnityScript.Scripting

class EvaluationDomainProviderImplementor(AbstractCompilerComponent):

	def ImplementIEvaluationDomainProviderOn(node as ClassDefinition):
		domainField = ReferenceExpression(Context.GetUniqueName("domain"))
		evaluationDomainProviderImpl = [|
			class _($IEvaluationDomainProvider):

				private $domainField as $EvaluationDomain

				def GetEvaluationDomain():
					return $domainField or ($domainField = $EvaluationDomain())

				def GetImports():
					return $(ImportsArrayFor(node))

				def GetAssemblyReferences():
					return $(AssemblyReferencesArray())
		|]

		my(CodeReifier).MergeInto(node, evaluationDomainProviderImpl)

	def StaticEvaluationDomainProviderFor(node as ClassDefinition) as IField:
		perNode node:
			return CreateStaticEvaluationDomainProviderReferenceOn(node)

	private def AssemblyReferencesArray() as Expression:
		perNode CompileUnit:
			return CodeBuilder.CreateReference(CreateAssemblyReferencesArray())

	private def ImportsArrayFor(node as ClassDefinition):
		result = [| (of $string:,) |]
		result.Items.Extend([| $(i.Namespace) |] for i in node.EnclosingModule.Imports)
		return result

	private def CreateStaticEvaluationDomainProviderReferenceOn(node as ClassDefinition):
		evaluationDomainProviderImpl = [|
			internal class StaticEvaluationDomainProvider($SimpleEvaluationDomainProvider):

				public static final Instance = StaticEvaluationDomainProvider()

				def constructor():
					super($(ImportsArrayFor(node)))

				override def GetAssemblyReferences():
					return $(AssemblyReferencesArray())
		|]
		my(CodeReifier).ReifyInto(node, evaluationDomainProviderImpl)
		return evaluationDomainProviderImpl.Members["Instance"].Entity

	private def CreateAssemblyReferencesArray() as IField:

		// TODO: only actually used assemblies should be part of this array
		// otherwise we risk bringing the kitchen sink

		result = [| (of $Assembly:,) |]
		result.Items.Add([| System.Reflection.Assembly.GetExecutingAssembly() |])

		for assembly in ReferencedAssemblies():
			publicType = AnyPublicTypeOf(assembly)
			continue if publicType is null
			result.Items.Add([| typeof($publicType).Assembly |])

		arrayHolder = [|
			internal class EvalAssemblyReferences:
				public static final Value = $result
		|]
		my(CodeReifier).ReifyInto(CompileUnit.Modules[0], arrayHolder)
		return arrayHolder.Members["Value"].Entity

	private def AnyPublicTypeOf(assembly as System.Reflection.Assembly):
		for t in assembly.GetTypes():
			return t if t.IsPublic and not t.IsGenericType

	private def ReferencedAssemblies():
		collector = ReferencedAssemblyCollector()
		CompileUnit.Accept(collector)
		return collector.ReferencedAssemblies

class ReferencedAssemblyCollector(DepthFirstVisitor):

	[getter(ReferencedAssemblies)]
	_assemblies = Boo.Lang.Compiler.Util.Set of Assembly()

	override def OnReferenceExpression(node as ReferenceExpression):
		CheckTypeReference(node)

	override def LeaveMemberReferenceExpression(node as MemberReferenceExpression):
		CheckTypeReference(node)

	override def OnSimpleTypeReference(node as SimpleTypeReference):
		CheckTypeReference(node)

	def CheckTypeReference(node as Node):
		type = node.Entity as Boo.Lang.Compiler.TypeSystem.Reflection.ExternalType
		if type is null: return
		_assemblies.Add(type.ActualType.Assembly)


benchmark multiple input sizes for comparison

namespace speed

import Boo.OMeta
import Boo.PatternMatching
import Boo.Pegs

ometa WordCollector:
	parse = ++(numbered | word)
	numbered = ((++letter >> letters, ++digit >> digits), wordbreak) ^ (j(letters) + j(digits))
	word = (++letter >> suffix, wordbreak) ^ j(suffix)
	wordbreak = ++whitespace
	number = (++digit >> n, wordbreak) ^ j(n)

def j(items):
	return join(items, '')

def pegWords(text as string):

	words = []
	append = words.Add

	peg:
		parse = ++[numbered, word]
		numbered = ++letter, ++digit, { append($text) }, wordbreak
		word = ++letter, { append($text) }, wordbreak
		letter = a-z
		digit = 0-9
		wordbreak = ++whitespace()

	assert parse.Match(PegContext(text))
	return words

def time(label as string, block as callable()):
	start = date.Now
	block()
	elapsed = date.Now - start
	print "${label}: ${elapsed}"
	return elapsed

def benchmark(wordCount as int, printValues as bool):

	words = join(("word${i}\n" if 0 == i % 2 else "word\n") for i in range(wordCount))

	peg = time("peg"):
		wordList = pegWords(words)
		if printValues: print wordList

	ometa = time("ometa"):
		input = OMetaInput.For(words)
		match WordCollector().parse(input):
			case SuccessfulMatch(Input, Value):
				assert Input.IsEmpty
				if printValues: print Value

	print "================= FACTOR(${len(words)}):", ometa.TotalMilliseconds / peg.TotalMilliseconds

for i in (10, 100, 1000, 10000, 50000, 100000):
	benchmark i, false

namespace speed

import Boo.OMeta
import Boo.PatternMatching
import Boo.Pegs

ometa WordCollector:
	parse = ++(numbered | word)
	numbered = ((++letter >> letters, ++digit >> digits), wordbreak) ^ (j(letters) + j(digits))
	word = (++letter >> suffix, wordbreak) ^ j(suffix)
	wordbreak = ++whitespace
	number = (++digit >> n, wordbreak) ^ j(n)

def j(items):
	return join(items, '')

def pegWords(text as string):

	words = []
	append = words.Add

	peg:
		parse = ++[numbered, word]
		numbered = ++letter, ++digit, { append($text) }, wordbreak
		word = ++letter, { append($text) }, wordbreak
		letter = a-z
		digit = 0-9
		wordbreak = ++whitespace()

	assert parse.Match(PegContext(text))
	return words

def time(label as string, block as callable()):
	start = date.Now
	block()
	elapsed = date.Now - start
	print "${label}: ${elapsed}"

words = join(("word${i}\n" if 0 == i % 2 else "word\n") for i in range(100000))

printValues = false

time "peg":
	wordList = pegWords(words)
	if printValues: print wordList

time "ometa":
	input = OMetaInput.For(words)
	match WordCollector().parse(input):
		case SuccessfulMatch(Input, Value):
			assert Input.IsEmpty
			if printValues: print Value


map the root exception type to java.lang.Exception

namespace Boojay.Compilation

import Boo.Lang.Compiler
import Boo.Lang.Compiler.TypeSystem

class JavaTypeSystem(TypeSystemServices):

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

	override ExceptionType:
		get: return Map(java.lang.Exception)

	def ReplaceMapping(existing as System.Type, new as System.Type):
		mapping = Map(new)
		Cache(existing, mapping)
		return mapping

namespace Boojay.Compilation

import Boo.Lang.Compiler
import Boo.Lang.Compiler.TypeSystem

class JavaTypeSystem(TypeSystemServices):

	def constructor(context as CompilerContext):
		super(context)
		self.StringType = ReplaceMapping(System.String, java.lang.String)
		AddPrimitiveType("string", self.StringType)

#		self.ExceptionType = ReplaceMapping(System.Exception, java.lang.RuntimeException)

	def ReplaceMapping(existing as System.Type, new as System.Type):
		mapping = Map(new)
		Cache(existing, mapping)
		return mapping


BOO-107, changed winforms example to allow its execution inside boox

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

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

import System
import System.Windows.Forms from System.Windows.Forms

class App:

	[getter(Times)]
	_times = 0

	def Run():
		f = Form(Text: "My first boo winforms app")

		button = Button(Text: "click me!")
		button.Click += do:
			print("clicked!")
			++_times

		f.Controls.Add(button)

		if Application.MessageLoop:
			# if we are running inside boox
			# just show a dialog
			f.ShowDialog()
		else:
			f.Closed += Application.Exit
			Application.Run(f)

app = App()
app.Run()
print("The button was clicked ${app.Times} times.")

#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

import System
import System.Windows.Forms from System.Windows.Forms

class App:

	[getter(Times)]
	_times = 0

	def Run():
		f = Form(Text: "My first boo winforms app",
				Closed: Application.Exit)

		button = Button(Text: "click me!")
		button.Click += do:
			print("clicked!")
			++_times

		f.Controls.Add(button)
		f.Show()

		Application.Run(f)

app = App()
app.Run()
print("The button was clicked ${app.Times} times.")


Add OnExplicitMemberInfo to CodeDomVisitor.

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

namespace BooBinding

import System
import System.Collections
import System.CodeDom
import System.Text
import Boo.Lang.Compiler
import Boo.Lang.Compiler.Ast
import Boo.Lang.Compiler.Ast.Visitors
import Boo.Lang.Parser
import SharpDevelop.Internal.Parser

class CodeDomVisitor(IAstVisitor):
"""The CodeDomVisitor is able to convert from the Boo AST to System.CodeDom
It makes use of the SharpDevelop parser service to get necessary additional information about the
types."""
	[Getter(OutputCompileUnit)]
	_compileUnit = CodeCompileUnit()
	_namespace as CodeNamespace
	_class as CodeTypeDeclaration
	_statements as CodeStatementCollection
	_expression as CodeExpression

	def ConvModifiers(member as TypeMember) as MemberAttributes:
		if member isa Field:
			return ConvModifiers(member.Modifiers, MemberAttributes.Family)
		else:
			return ConvModifiers(member.Modifiers, MemberAttributes.Public)

	def ConvModifiers(modifier as TypeMemberModifiers, defaultAttr as MemberAttributes) as MemberAttributes:
		// Boo is not able to convert 0 to MemberAttributes, therefore we need to use
		// a trick to get the default value
		noAttr = MemberAttributes.Abstract & MemberAttributes.Final
		attr = noAttr
		if (modifier & TypeMemberModifiers.Abstract) == TypeMemberModifiers.Abstract:
			attr = attr | MemberAttributes.Abstract
		if (modifier & TypeMemberModifiers.Final) == TypeMemberModifiers.Final:
			attr = attr | MemberAttributes.Const
		if (modifier & TypeMemberModifiers.Internal) == TypeMemberModifiers.Internal:
			attr = attr | MemberAttributes.Assembly
		if (modifier & TypeMemberModifiers.Override) == TypeMemberModifiers.Override:
			attr = attr | MemberAttributes.Override
		if (modifier & TypeMemberModifiers.Private) == TypeMemberModifiers.Private:
			attr = attr | MemberAttributes.Private
		if (modifier & TypeMemberModifiers.Protected) == TypeMemberModifiers.Protected:
			attr = attr | MemberAttributes.Family
		if (modifier & TypeMemberModifiers.Public) == TypeMemberModifiers.Public:
			attr = attr | MemberAttributes.Public
		if (modifier & TypeMemberModifiers.Static) == TypeMemberModifiers.Static:
			attr = attr | MemberAttributes.Static
		if (modifier & TypeMemberModifiers.Virtual) != TypeMemberModifiers.Virtual:
			attr = attr | MemberAttributes.Final
		if attr == noAttr:
			return defaultAttr
		else:
			return attr

	def ConvTypeRef(ref as TypeReference):
		return null if ref == null
		name = ref.ToString()
		expandedName = BooAmbience.ReverseTypeConversionTable[name]
		name = expandedName if expandedName != null
		return CodeTypeReference(name)

	def OnCompileUnit(node as CompileUnit):
		for m as Module in node.Modules:
			m.Accept(self)

	def OnModule(node as Module):
		if node.Namespace == null:
			_namespace = CodeNamespace("Global")
			_compileUnit.Namespaces.Add(_namespace)
		else:
			node.Namespace.Accept(self)
		for i as Import in node.Imports:
			i.Accept(self)
		for m as TypeMember in node.Members:
			m.Accept(self)

	def OnNamespaceDeclaration(node as NamespaceDeclaration):
		_namespace = CodeNamespace(node.Name)
		_compileUnit.Namespaces.Add(_namespace)

	def OnImport(node as Import):
		_namespace.Imports.Add(CodeNamespaceImport(node.Namespace))

	def OnClassDefinition(node as ClassDefinition):
		oldClass = _class
		_class = CodeTypeDeclaration(node.Name)
		_class.IsClass = true

		for b as TypeReference in node.BaseTypes:
			_class.BaseTypes.Add(ConvTypeRef(b))

		for member as TypeMember in node.Members:
			member.Accept(self)

		if oldClass == null:
			_namespace.Types.Add(_class)
		else:
			oldClass.Members.Add(_class)
		_class = oldClass

	def OnStructDefinition(node as StructDefinition):
		oldClass = _class
		_class = CodeTypeDeclaration(node.Name)
		_class.IsStruct = true

		for b as TypeReference in node.BaseTypes:
			_class.BaseTypes.Add(ConvTypeRef(b))

		for member as TypeMember in node.Members:
			member.Accept(self)

		if oldClass == null:
			_namespace.Types.Add(_class)
		else:
			oldClass.Members.Add(_class)
		_class = oldClass

	def OnInterfaceDefinition(node as InterfaceDefinition):
		oldClass = _class
		_class = CodeTypeDeclaration(node.Name)
		_class.IsInterface = true

		for b as TypeReference in node.BaseTypes:
			_class.BaseTypes.Add(ConvTypeRef(b))

		for member as TypeMember in node.Members:
			member.Accept(self)

		if oldClass == null:
			_namespace.Types.Add(_class)
		else:
			oldClass.Members.Add(_class)
		_class = oldClass

	def OnField(node as Field):
		field = CodeMemberField(ConvTypeRef(node.Type), node.Name)
		field.Attributes = ConvModifiers(node)
		if node.Initializer != null:
			_expression = null
			//Visit(node.Initializer)
			field.InitExpression = _expression
		_class.Members.Add(field)

	def OnConstructor(node as Constructor):
		ConvertMethod(node, CodeConstructor())

	def OnMethod(node as Method):
		ConvertMethod(node, CodeMemberMethod(Name: node.Name))

	def OnDestructor(node as Destructor):
		ConvertMethod(node, CodeMemberMethod(Name: "Finalize"))

	def ConvertMethod(node as Method, method as CodeMemberMethod):
		method.Attributes = ConvModifiers(node)
		method.ReturnType = ConvTypeRef(node.ReturnType)
		if node.Parameters != null:
			for p as ParameterDeclaration in node.Parameters:
				method.Parameters.Add(CodeParameterDeclarationExpression(ConvTypeRef(p.Type), p.Name))
		_statements = method.Statements

		if node.Body != null:
			node.Body.Accept(self)

		_class.Members.Add(method)

	def OnArrayLiteralExpression(node as ArrayLiteralExpression):
		pass

	def OnArrayTypeReference(node as ArrayTypeReference):
		pass

	def OnAsExpression(node as AsExpression):
		pass

	def OnAttribute(node as Boo.Lang.Compiler.Ast.Attribute):
		pass

	def OnBinaryExpression(node as BinaryExpression):
		op = node.Operator
		if op == BinaryOperatorType.Assign:
			_expression = null
			node.Left.Accept(self)
			left = _expression
			_expression = null
			node.Right.Accept(self)
			if left != null and _expression != null:
				_statements.Add(CodeAssignStatement(left, _expression))
			_expression = null
			return

	def OnBlock(node as Block):
		for n as Statement in node.Statements:
			n.Accept(self)

	def OnBreakStatement(node as BreakStatement):
		pass

	def OnCallableBlockExpression(node as CallableBlockExpression):
		pass

	def OnCallableDefinition(node as CallableDefinition):
		pass

	def OnCallableTypeReference(node as CallableTypeReference):
		pass

	def OnCastExpression(node as CastExpression):
		pass

	def OnContinueStatement(node as ContinueStatement):
		pass

	def OnDeclaration(node as Declaration):
		pass

	def OnDeclarationStatement(node as DeclarationStatement):
		pass

	def OnEnumDefinition(node as EnumDefinition):
		pass

	def OnEnumMember(node as EnumMember):
		pass

	def OnParameterDeclaration(node as ParameterDeclaration):
		pass

	def OnEvent(node as Event):
		pass

	def OnExceptionHandler(node as ExceptionHandler):
		pass

	def OnExpressionInterpolationExpression(node as ExpressionInterpolationExpression):
		pass

	def OnExpressionPair(node as ExpressionPair):
		pass

	def OnExpressionStatement(node as ExpressionStatement):
		_expression = null
		node.Expression.Accept(self)
		if _expression != null:
			_statements.Add(CodeExpressionStatement(_expression))

	def OnForStatement(node as ForStatement):
		pass

	def OnExtendedGeneratorExpression(node as ExtendedGeneratorExpression):
		pass

	def OnGeneratorExpression(node as GeneratorExpression):
		pass

	def OnGivenStatement(node as GivenStatement):
		pass

	def OnGotoStatement(node as GotoStatement):
		_statements.Add(CodeGotoStatement(node.Label.Name))

	def OnNullLiteralExpression(node as NullLiteralExpression):
		_expression = CodePrimitiveExpression(null)

	def OnBoolLiteralExpression(node as BoolLiteralExpression):
		_expression = CodePrimitiveExpression(node.Value)

	def OnStringLiteralExpression(node as StringLiteralExpression):
		_expression = CodePrimitiveExpression(node.Value)

	def OnCharLiteralExpression(node as CharLiteralExpression):
		_expression = CodePrimitiveExpression(node.Value)

	def OnHashLiteralExpression(node as HashLiteralExpression):
		pass

	def OnIntegerLiteralExpression(node as IntegerLiteralExpression):
		_expression = CodePrimitiveExpression(node.Value)

	def OnDoubleLiteralExpression(node as DoubleLiteralExpression):
		_expression = CodePrimitiveExpression(node.Value)

	def OnListLiteralExpression(node as ListLiteralExpression):
		pass

	def OnIfStatement(node as IfStatement):
		pass

	def OnLabelStatement(node as LabelStatement):
		pass

	def OnLocal(node as Local):
		pass

	def OnMacroStatement(node as MacroStatement):
		pass

	def OnMemberReferenceExpression(node as MemberReferenceExpression):
		_expression = null
		node.Target.Accept(self)
		if _expression != null:
			if _expression isa CodeTypeReferenceExpression:
				// TODO: lookup if expression is static member or subtype
				_expression = CodeTypeReferenceExpression("${cast(CodeTypeReferenceExpression, _expression).Type.BaseType}.${node.Name}")
			else:
				_expression = CreateMemberExpression(_expression, node.Name)

	def OnReferenceExpression(node as ReferenceExpression):
		p = GetParserService()
		if p.GetClass(node.Name) != null:
			_expression = CodeTypeReferenceExpression(node.Name)
		elif p.NamespaceExists(node.Name):
			_expression = CodeTypeReferenceExpression(node.Name)
		else:
			_expression = CreateMemberExpression(CodeThisReferenceExpression(), node.Name)

	def CreateMemberExpression(expr as CodeExpression, name as string):
		if expr isa CodeTypeReferenceExpression:
			typeRef = cast(CodeTypeReferenceExpression, _expression).Type.BaseType
			return CreateMemberExpression(expr, typeRef, name, true)
		elif expr isa CodeThisReferenceExpression:
			typeRef = "${_namespace.Name}.${_class.Name}"
			return CreateMemberExpression(expr, typeRef, name, false)
		return CodeFieldReferenceExpression(expr, name)

	def CreateMemberExpression(target as CodeExpression, parentName as string, name as string, isStatic as bool):
		combinedName = "${parentName}.${name}"
		p = GetParserService()
		parentClass = p.GetClass(parentName)
		if parentClass == null:
			if p.GetClass(combinedName) != null:
				return CodeTypeReferenceExpression(combinedName)
			elif p.NamespaceExists(combinedName):
				return CodeTypeReferenceExpression(combinedName)
		else:
			if isStatic:
				for innerClass as IClass in parentClass.InnerClasses:
					if innerClass.Name == name:
						return CodeTypeReferenceExpression(combinedName)
			for c as IClass in parentClass.ClassInheritanceTree:
				for ev as IEvent in c.Events:
					if ev.IsStatic == isStatic:
						return CodeEventReferenceExpression(target, name)
				for me as IMethod in c.Methods:
					if me.IsStatic == isStatic:
						return CodeMethodReferenceExpression(target, name)
				for prop as IProperty in c.Properties:
					if prop.IsStatic == isStatic:
						return CodePropertyReferenceExpression(target, name)
				for field as IField in c.Fields:
					if field.IsStatic == isStatic:
						return CodeFieldReferenceExpression(target, name)
		return CodeFieldReferenceExpression(target, name)

	def GetParserService() as ICSharpCode.SharpDevelop.Services.IParserService:
		return ICSharpCode.Core.Services.ServiceManager.Services.GetService(typeof(ICSharpCode.SharpDevelop.Services.IParserService))

	def OnMethodInvocationExpression(node as MethodInvocationExpression):
		_expression = null
		node.Target.Accept(self)
		if _expression != null:
			if _expression isa CodeTypeReferenceExpression:
				_expression = CodeObjectCreateExpression(cast(CodeTypeReferenceExpression, _expression).Type, ConvertExpressions(node.Arguments))
			elif _expression isa CodeMethodReferenceExpression:
				_expression = CodeMethodInvokeExpression(_expression, ConvertExpressions(node.Arguments))
			elif _expression isa CodeFieldReferenceExpression:
				// when a type is unknown, a MemberReferenceExpression is translated into a CodeFieldReferenceExpression
				cfre as CodeFieldReferenceExpression = _expression
				_expression = CodeMethodInvokeExpression(cfre.TargetObject, cfre.FieldName, ConvertExpressions(node.Arguments))
			else:
				_expression = null

	def ConvertExpressions(expressions as ExpressionCollection):
	"""Converts a list of expressions to CodeDom expressions."""
		args = array(CodeExpression, expressions.Count)
		i = 0
		while i < args.Length:
			_expression = null
			expressions[i].Accept(self)
			args[i] = _expression
			i += 1
		return args

	def OnOmittedExpression(node as OmittedExpression):
		pass

	def OnProperty(node as Property):
		pass

	def OnRaiseStatement(node as RaiseStatement):
		pass

	def OnRELiteralExpression(node as RELiteralExpression):
		pass

	def OnRetryStatement(node as RetryStatement):
		pass

	def OnReturnStatement(node as ReturnStatement):
		_expression = null
		if node.Expression != null:
			node.Expression.Accept(self)
		_statements.Add(CodeMethodReturnStatement(_expression))

	def OnSelfLiteralExpression(node as SelfLiteralExpression):
		_expression = CodeThisReferenceExpression()

	def OnSimpleTypeReference(node as SimpleTypeReference):
		pass

	def OnSlice(node as Slice):
		pass

	def OnSlicingExpression(node as SlicingExpression):
		pass

	def OnStatementModifier(node as StatementModifier):
		pass

	def OnSuperLiteralExpression(node as SuperLiteralExpression):
		_expression = CodeBaseReferenceExpression()

	def OnTernaryExpression(node as TernaryExpression):
		pass

	def OnTimeSpanLiteralExpression(node as TimeSpanLiteralExpression):
		pass

	def OnTryStatement(node as TryStatement):
		pass

	def OnTypeofExpression(node as TypeofExpression):
		pass

	def OnUnaryExpression(node as UnaryExpression):
		pass

	def OnUnlessStatement(node as UnlessStatement):
		pass

	def OnUnpackStatement(node as UnpackStatement):
		pass

	def OnWhenClause(node as WhenClause):
		pass

	def OnWhileStatement(node as WhileStatement):
		pass

	def OnExplicitMemberInfo(node as ExplicitMemberInfo):
		pass

	def OnYieldStatement(node as YieldStatement):
		pass

namespace BooBinding

import System
import System.Collections
import System.CodeDom
import System.Text
import Boo.Lang.Compiler
import Boo.Lang.Compiler.Ast
import Boo.Lang.Compiler.Ast.Visitors
import Boo.Lang.Parser
import SharpDevelop.Internal.Parser

class CodeDomVisitor(IAstVisitor):
"""The CodeDomVisitor is able to convert from the Boo AST to System.CodeDom
It makes use of the SharpDevelop parser service to get necessary additional information about the
types."""
	[Getter(OutputCompileUnit)]
	_compileUnit = CodeCompileUnit()
	_namespace as CodeNamespace
	_class as CodeTypeDeclaration
	_statements as CodeStatementCollection
	_expression as CodeExpression

	def ConvModifiers(member as TypeMember) as MemberAttributes:
		if member isa Field:
			return ConvModifiers(member.Modifiers, MemberAttributes.Family)
		else:
			return ConvModifiers(member.Modifiers, MemberAttributes.Public)

	def ConvModifiers(modifier as TypeMemberModifiers, defaultAttr as MemberAttributes) as MemberAttributes:
		// Boo is not able to convert 0 to MemberAttributes, therefore we need to use
		// a trick to get the default value
		noAttr = MemberAttributes.Abstract & MemberAttributes.Final
		attr = noAttr
		if (modifier & TypeMemberModifiers.Abstract) == TypeMemberModifiers.Abstract:
			attr = attr | MemberAttributes.Abstract
		if (modifier & TypeMemberModifiers.Final) == TypeMemberModifiers.Final:
			attr = attr | MemberAttributes.Const
		if (modifier & TypeMemberModifiers.Internal) == TypeMemberModifiers.Internal:
			attr = attr | MemberAttributes.Assembly
		if (modifier & TypeMemberModifiers.Override) == TypeMemberModifiers.Override:
			attr = attr | MemberAttributes.Override
		if (modifier & TypeMemberModifiers.Private) == TypeMemberModifiers.Private:
			attr = attr | MemberAttributes.Private
		if (modifier & TypeMemberModifiers.Protected) == TypeMemberModifiers.Protected:
			attr = attr | MemberAttributes.Family
		if (modifier & TypeMemberModifiers.Public) == TypeMemberModifiers.Public:
			attr = attr | MemberAttributes.Public
		if (modifier & TypeMemberModifiers.Static) == TypeMemberModifiers.Static:
			attr = attr | MemberAttributes.Static
		if (modifier & TypeMemberModifiers.Virtual) != TypeMemberModifiers.Virtual:
			attr = attr | MemberAttributes.Final
		if attr == noAttr:
			return defaultAttr
		else:
			return attr

	def ConvTypeRef(ref as TypeReference):
		return null if ref == null
		name = ref.ToString()
		expandedName = BooAmbience.ReverseTypeConversionTable[name]
		name = expandedName if expandedName != null
		return CodeTypeReference(name)

	def OnCompileUnit(node as CompileUnit):
		for m as Module in node.Modules:
			m.Accept(self)

	def OnModule(node as Module):
		if node.Namespace == null:
			_namespace = CodeNamespace("Global")
			_compileUnit.Namespaces.Add(_namespace)
		else:
			node.Namespace.Accept(self)
		for i as Import in node.Imports:
			i.Accept(self)
		for m as TypeMember in node.Members:
			m.Accept(self)

	def OnNamespaceDeclaration(node as NamespaceDeclaration):
		_namespace = CodeNamespace(node.Name)
		_compileUnit.Namespaces.Add(_namespace)

	def OnImport(node as Import):
		_namespace.Imports.Add(CodeNamespaceImport(node.Namespace))

	def OnClassDefinition(node as ClassDefinition):
		oldClass = _class
		_class = CodeTypeDeclaration(node.Name)
		_class.IsClass = true

		for b as TypeReference in node.BaseTypes:
			_class.BaseTypes.Add(ConvTypeRef(b))

		for member as TypeMember in node.Members:
			member.Accept(self)

		if oldClass == null:
			_namespace.Types.Add(_class)
		else:
			oldClass.Members.Add(_class)
		_class = oldClass

	def OnStructDefinition(node as StructDefinition):
		oldClass = _class
		_class = CodeTypeDeclaration(node.Name)
		_class.IsStruct = true

		for b as TypeReference in node.BaseTypes:
			_class.BaseTypes.Add(ConvTypeRef(b))

		for member as TypeMember in node.Members:
			member.Accept(self)

		if oldClass == null:
			_namespace.Types.Add(_class)
		else:
			oldClass.Members.Add(_class)
		_class = oldClass

	def OnInterfaceDefinition(node as InterfaceDefinition):
		oldClass = _class
		_class = CodeTypeDeclaration(node.Name)
		_class.IsInterface = true

		for b as TypeReference in node.BaseTypes:
			_class.BaseTypes.Add(ConvTypeRef(b))

		for member as TypeMember in node.Members:
			member.Accept(self)

		if oldClass == null:
			_namespace.Types.Add(_class)
		else:
			oldClass.Members.Add(_class)
		_class = oldClass

	def OnField(node as Field):
		field = CodeMemberField(ConvTypeRef(node.Type), node.Name)
		field.Attributes = ConvModifiers(node)
		if node.Initializer != null:
			_expression = null
			//Visit(node.Initializer)
			field.InitExpression = _expression
		_class.Members.Add(field)

	def OnConstructor(node as Constructor):
		ConvertMethod(node, CodeConstructor())

	def OnMethod(node as Method):
		ConvertMethod(node, CodeMemberMethod(Name: node.Name))

	def OnDestructor(node as Destructor):
		ConvertMethod(node, CodeMemberMethod(Name: "Finalize"))

	def ConvertMethod(node as Method, method as CodeMemberMethod):
		method.Attributes = ConvModifiers(node)
		method.ReturnType = ConvTypeRef(node.ReturnType)
		if node.Parameters != null:
			for p as ParameterDeclaration in node.Parameters:
				method.Parameters.Add(CodeParameterDeclarationExpression(ConvTypeRef(p.Type), p.Name))
		_statements = method.Statements

		if node.Body != null:
			node.Body.Accept(self)

		_class.Members.Add(method)

	def OnArrayLiteralExpression(node as ArrayLiteralExpression):
		pass

	def OnArrayTypeReference(node as ArrayTypeReference):
		pass

	def OnAsExpression(node as AsExpression):
		pass

	def OnAttribute(node as Boo.Lang.Compiler.Ast.Attribute):
		pass

	def OnBinaryExpression(node as BinaryExpression):
		op = node.Operator
		if op == BinaryOperatorType.Assign:
			_expression = null
			node.Left.Accept(self)
			left = _expression
			_expression = null
			node.Right.Accept(self)
			if left != null and _expression != null:
				_statements.Add(CodeAssignStatement(left, _expression))
			_expression = null
			return

	def OnBlock(node as Block):
		for n as Statement in node.Statements:
			n.Accept(self)

	def OnBreakStatement(node as BreakStatement):
		pass

	def OnCallableBlockExpression(node as CallableBlockExpression):
		pass

	def OnCallableDefinition(node as CallableDefinition):
		pass

	def OnCallableTypeReference(node as CallableTypeReference):
		pass

	def OnCastExpression(node as CastExpression):
		pass

	def OnContinueStatement(node as ContinueStatement):
		pass

	def OnDeclaration(node as Declaration):
		pass

	def OnDeclarationStatement(node as DeclarationStatement):
		pass

	def OnEnumDefinition(node as EnumDefinition):
		pass

	def OnEnumMember(node as EnumMember):
		pass

	def OnParameterDeclaration(node as ParameterDeclaration):
		pass

	def OnEvent(node as Event):
		pass

	def OnExceptionHandler(node as ExceptionHandler):
		pass

	def OnExpressionInterpolationExpression(node as ExpressionInterpolationExpression):
		pass

	def OnExpressionPair(node as ExpressionPair):
		pass

	def OnExpressionStatement(node as ExpressionStatement):
		_expression = null
		node.Expression.Accept(self)
		if _expression != null:
			_statements.Add(CodeExpressionStatement(_expression))

	def OnForStatement(node as ForStatement):
		pass

	def OnExtendedGeneratorExpression(node as ExtendedGeneratorExpression):
		pass

	def OnGeneratorExpression(node as GeneratorExpression):
		pass

	def OnGivenStatement(node as GivenStatement):
		pass

	def OnGotoStatement(node as GotoStatement):
		_statements.Add(CodeGotoStatement(node.Label.Name))

	def OnNullLiteralExpression(node as NullLiteralExpression):
		_expression = CodePrimitiveExpression(null)

	def OnBoolLiteralExpression(node as BoolLiteralExpression):
		_expression = CodePrimitiveExpression(node.Value)

	def OnStringLiteralExpression(node as StringLiteralExpression):
		_expression = CodePrimitiveExpression(node.Value)

	def OnCharLiteralExpression(node as CharLiteralExpression):
		_expression = CodePrimitiveExpression(node.Value)

	def OnHashLiteralExpression(node as HashLiteralExpression):
		pass

	def OnIntegerLiteralExpression(node as IntegerLiteralExpression):
		_expression = CodePrimitiveExpression(node.Value)

	def OnDoubleLiteralExpression(node as DoubleLiteralExpression):
		_expression = CodePrimitiveExpression(node.Value)

	def OnListLiteralExpression(node as ListLiteralExpression):
		pass

	def OnIfStatement(node as IfStatement):
		pass

	def OnLabelStatement(node as LabelStatement):
		pass

	def OnLocal(node as Local):
		pass

	def OnMacroStatement(node as MacroStatement):
		pass

	def OnMemberReferenceExpression(node as MemberReferenceExpression):
		_expression = null
		node.Target.Accept(self)
		if _expression != null:
			if _expression isa CodeTypeReferenceExpression:
				// TODO: lookup if expression is static member or subtype
				_expression = CodeTypeReferenceExpression("${cast(CodeTypeReferenceExpression, _expression).Type.BaseType}.${node.Name}")
			else:
				_expression = CreateMemberExpression(_expression, node.Name)

	def OnReferenceExpression(node as ReferenceExpression):
		p = GetParserService()
		if p.GetClass(node.Name) != null:
			_expression = CodeTypeReferenceExpression(node.Name)
		elif p.NamespaceExists(node.Name):
			_expression = CodeTypeReferenceExpression(node.Name)
		else:
			_expression = CreateMemberExpression(CodeThisReferenceExpression(), node.Name)

	def CreateMemberExpression(expr as CodeExpression, name as string):
		if expr isa CodeTypeReferenceExpression:
			typeRef = cast(CodeTypeReferenceExpression, _expression).Type.BaseType
			return CreateMemberExpression(expr, typeRef, name, true)
		elif expr isa CodeThisReferenceExpression:
			typeRef = "${_namespace.Name}.${_class.Name}"
			return CreateMemberExpression(expr, typeRef, name, false)
		return CodeFieldReferenceExpression(expr, name)

	def CreateMemberExpression(target as CodeExpression, parentName as string, name as string, isStatic as bool):
		combinedName = "${parentName}.${name}"
		p = GetParserService()
		parentClass = p.GetClass(parentName)
		if parentClass == null:
			if p.GetClass(combinedName) != null:
				return CodeTypeReferenceExpression(combinedName)
			elif p.NamespaceExists(combinedName):
				return CodeTypeReferenceExpression(combinedName)
		else:
			if isStatic:
				for innerClass as IClass in parentClass.InnerClasses:
					if innerClass.Name == name:
						return CodeTypeReferenceExpression(combinedName)
			for c as IClass in parentClass.ClassInheritanceTree:
				for ev as IEvent in c.Events:
					if ev.IsStatic == isStatic:
						return CodeEventReferenceExpression(target, name)
				for me as IMethod in c.Methods:
					if me.IsStatic == isStatic:
						return CodeMethodReferenceExpression(target, name)
				for prop as IProperty in c.Properties:
					if prop.IsStatic == isStatic:
						return CodePropertyReferenceExpression(target, name)
				for field as IField in c.Fields:
					if field.IsStatic == isStatic:
						return CodeFieldReferenceExpression(target, name)
		return CodeFieldReferenceExpression(target, name)

	def GetParserService() as ICSharpCode.SharpDevelop.Services.IParserService:
		return ICSharpCode.Core.Services.ServiceManager.Services.GetService(typeof(ICSharpCode.SharpDevelop.Services.IParserService))

	def OnMethodInvocationExpression(node as MethodInvocationExpression):
		_expression = null
		node.Target.Accept(self)
		if _expression != null:
			if _expression isa CodeTypeReferenceExpression:
				_expression = CodeObjectCreateExpression(cast(CodeTypeReferenceExpression, _expression).Type, ConvertExpressions(node.Arguments))
			elif _expression isa CodeMethodReferenceExpression:
				_expression = CodeMethodInvokeExpression(_expression, ConvertExpressions(node.Arguments))
			elif _expression isa CodeFieldReferenceExpression:
				// when a type is unknown, a MemberReferenceExpression is translated into a CodeFieldReferenceExpression
				cfre as CodeFieldReferenceExpression = _expression
				_expression = CodeMethodInvokeExpression(cfre.TargetObject, cfre.FieldName, ConvertExpressions(node.Arguments))
			else:
				_expression = null

	def ConvertExpressions(expressions as ExpressionCollection):
	"""Converts a list of expressions to CodeDom expressions."""
		args = array(CodeExpression, expressions.Count)
		i = 0
		while i < args.Length:
			_expression = null
			expressions[i].Accept(self)
			args[i] = _expression
			i += 1
		return args

	def OnOmittedExpression(node as OmittedExpression):
		pass

	def OnProperty(node as Property):
		pass

	def OnRaiseStatement(node as RaiseStatement):
		pass

	def OnRELiteralExpression(node as RELiteralExpression):
		pass

	def OnRetryStatement(node as RetryStatement):
		pass

	def OnReturnStatement(node as ReturnStatement):
		_expression = null
		if node.Expression != null:
			node.Expression.Accept(self)
		_statements.Add(CodeMethodReturnStatement(_expression))

	def OnSelfLiteralExpression(node as SelfLiteralExpression):
		_expression = CodeThisReferenceExpression()

	def OnSimpleTypeReference(node as SimpleTypeReference):
		pass

	def OnSlice(node as Slice):
		pass

	def OnSlicingExpression(node as SlicingExpression):
		pass

	def OnStatementModifier(node as StatementModifier):
		pass

	def OnSuperLiteralExpression(node as SuperLiteralExpression):
		_expression = CodeBaseReferenceExpression()

	def OnTernaryExpression(node as TernaryExpression):
		pass

	def OnTimeSpanLiteralExpression(node as TimeSpanLiteralExpression):
		pass

	def OnTryStatement(node as TryStatement):
		pass

	def OnTypeofExpression(node as TypeofExpression):
		pass

	def OnUnaryExpression(node as UnaryExpression):
		pass

	def OnUnlessStatement(node as UnlessStatement):
		pass

	def OnUnpackStatement(node as UnpackStatement):
		pass

	def OnWhenClause(node as WhenClause):
		pass

	def OnWhileStatement(node as WhileStatement):
		pass

	def OnYieldStatement(node as YieldStatement):
		pass


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

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

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

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

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

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


Class browser

Class browser

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

namespace BooExplorer

import WeifenLuo.WinFormsUI
import System
import System.Windows.Forms
import System.Drawing
import Boo.AntlrParser
import Boo.Lang.Compiler.Ast

class ClassBrowser(Content):

	_tree = TreeView(Dock: DockStyle.Fill)
	_treeViewVisitor = TreeViewVisitor(_tree)

	def constructor():

		SuspendLayout()

		Controls.Add(_tree)
		self.AllowedStates = (
					ContentStates.Float |
					ContentStates.DockLeft |
					ContentStates.DockRight)
		self.ClientSize = System.Drawing.Size(295, 347)

		self.DockPadding.Bottom = 2
		self.DockPadding.Top = 26
		self.HideOnClose = true
		self.ShowHint = WeifenLuo.WinFormsUI.DockState.DockRight
		self.Text = /*self.TabText =*/ "Class Browser"
		ResumeLayout(false)

	ActiveDocument as BooEditor:
		set:
			Update(value.Text, value.TextContent)

	def Update(fname as string, text as string):
		try:
			UpdateTree(BooParser.ParseString(fname, text))
		except x:
			print(x)

	def UpdateTree(cu as CompileUnit):
		_treeViewVisitor.Switch(cu)

class TreeViewVisitor(DepthFirstSwitcher):

	_tree as TreeView
	_current as TreeNode

	def constructor(tree):
		_tree = tree

	override def OnCompileUnit(node as CompileUnit):
		_tree.SuspendLayout()
		try:
			_tree.Nodes.Clear()
			Switch(node.Modules)
			_tree.ExpandAll()
		ensure:
			_tree.ResumeLayout(false)

	override def OnModule(node as Module):
		if node.Namespace:
			name = node.Namespace.Name
		else:
			name = node.Name
		_current = _tree.Nodes.Add(name)
		Switch(node.Members)

	override def OnProperty(node as Property):
		Add(node.Name, node)

	override def OnField(node as Field):
		Add(node.Name, node)

	override def OnClassDefinition(node as ClassDefinition):
		saved = _current

		_current = Add(node.Name, node)
		Switch(node.Members)

		_current = saved

	override def OnMethod(node as Method):
		Add(node.Name, node)

	def Add(text as string, data):
		node = _current.Nodes.Add(node.Name)
		node.Tag = data
		return node

namespace BooExplorer

import WeifenLuo.WinFormsUI
import System
import System.Windows.Forms
import System.Drawing
import Boo.AntlrParser
import Boo.Lang.Compiler.Ast

class ClassBrowser(Content):

	_tree = TreeView(Dock: DockStyle.Fill)
	_treeViewVisitor = TreeViewVisitor(_tree)

	def constructor():

		SuspendLayout()

		Controls.Add(_tree)
		self.AllowedStates = (
					ContentStates.Float |
					ContentStates.DockLeft |
					ContentStates.DockRight)
		self.ClientSize = System.Drawing.Size(295, 347)

		self.DockPadding.Bottom = 2
		self.DockPadding.Top = 26
		self.HideOnClose = true
		self.ShowHint = WeifenLuo.WinFormsUI.DockState.DockRight
		self.Text = /*self.TabText =*/ "Class Browser"
		ResumeLayout(false)

	ActiveDocument as BooEditor:
		set:
			Update(value.Text, value.TextContent)

	def Update(fname as string, text as string):
		try:
			UpdateTree(BooParser.ParseString(fname, text))
		except x:
			print(x)

	def UpdateTree(cu as CompileUnit):
		_treeViewVisitor.Switch(cu)

class TreeViewVisitor(DepthFirstSwitcher):

	_tree as TreeView
	_current as TreeNode

	def constructor(tree):
		_tree = tree

	override def OnCompileUnit(node as CompileUnit):
		_tree.SuspendLayout()
		try:
			_tree.Nodes.Clear()
			Switch(node.Modules)
			_tree.ExpandAll()
		ensure:
			_tree.ResumeLayout(false)

	override def OnModule(node as Module):
		if node.Namespace:
			name = node.Namespace.Name
		else:
			name = node.Name
		_current = _tree.Nodes.Add(name)
		Switch(node.Members)

	override def OnProperty(node as Property):
		_current.Nodes.Add(node.Name)

	override def OnField(node as Field):
		_current.Nodes.Add(node.Name)

	override def OnClassDefinition(node as ClassDefinition):
		saved = _current

		_current = _current.Nodes.Add(node.Name)
		Switch(node.Members)

		_current = saved

	override def OnMethod(node as Method):
		_current.Nodes.Add(node.Name)