add disposed check to EndXXX

add disposed check to EndXXX

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

#region license
// Copyright (c) 2005, Sorin Ionescu (sorin.ionescu@gmail.com)
// 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 Sorin Ionescu 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.Examples.Attributes

import System;
import System.Runtime.Remoting.Messaging
import Boo.Lang.Compiler
import Boo.Lang.Compiler.Ast
import Boo.Lang.Compiler.Steps

class AsyncAttribute(AbstractAstAttribute):
"""
Adds asynchronous helpers Begin<Method>/End<Method>
for a method.
"""
	_method          as Method
	_accessModifiers as TypeMemberModifiers
	_disposed		 as ReferenceExpression

	def constructor():
		pass

	def constructor(disposed as ReferenceExpression):
		_disposed = disposed

	override def Apply(node as Node):
		assert node isa Method

		_method = node

		if _method.IsProtected and _method.IsInternal:
			_accessModifiers = TypeMemberModifiers.Protected
			_accessModifiers = _accessModifiers | TypeMemberModifiers.Internal

		elif _method.IsPublic:
			_accessModifiers = TypeMemberModifiers.Public

		elif _method.IsProtected:
			_accessModifiers = TypeMemberModifiers.Protected

		elif _method.IsInternal:
			_accessModifiers = TypeMemberModifiers.Internal

		elif _method.IsPrivate:
			_accessModifiers = TypeMemberModifiers.Private

		EmitBeginMethod()
		EmitEndMethod()

	private def EmitBeginMethod():
		beginMethod = Method(
						self.LexicalInfo,
						Name: "Begin" + _method.Name,
						Modifiers: _accessModifiers,
						ReturnType: CodeBuilder.CreateTypeReference(typeof(IAsyncResult)))
		beginMethod.Parameters.ExtendWithClones(_method.Parameters)
		beginMethod.Parameters.Add(
				ParameterDeclaration(
					"callback",
					CodeBuilder.CreateTypeReference(typeof(AsyncCallback))))
		beginMethod.Parameters.Add(
				ParameterDeclaration(
					"state",
					CodeBuilder.CreateTypeReference(typeof(object))))

		asyncInvocation = MethodInvocationExpression(
							Target: MemberReferenceExpression(
										ReferenceExpression(_method.Name),
										"BeginInvoke"))
		for parameter in beginMethod.Parameters:
			asyncInvocation.Arguments.Add(
					ReferenceExpression(parameter.Name))

		EmitDisposedObjectCheck(beginMethod) if _disposed is not null
		beginMethod.Body.Add(ReturnStatement(asyncInvocation))
		_method.DeclaringType.Members.Add(beginMethod)

	private def EmitEndMethod():
		endMethod = Method(
						self.LexicalInfo,
						Name: "End" + _method.Name,
						Modifiers: _accessModifiers)
		endMethod.Parameters.Add(
			ParameterDeclaration(
				"result",
				CodeBuilder.CreateTypeReference(typeof(IAsyncResult))))

		asyncInvocation = MethodInvocationExpression(
							Target: MemberReferenceExpression(
										ReferenceExpression(_method.Name),
										"EndInvoke"))
		asyncInvocation.Arguments.Add(ReferenceExpression("result"))

		EmitDisposedObjectCheck(endMethod) if _disposed is not null
		endMethod.Body.Add(ReturnStatement(asyncInvocation))

		_method.DeclaringType.Members.Add(endMethod)

		# cache the voidType reference because we are going
		# to lose the context after this method returns
		# (see AbstractCompilerComponent.Dispose)
		voidType = Context.TypeSystemServices.VoidType
		Context.Parameters.Pipeline.AfterStep += def (sender, e as CompilerStepEventArgs):
			if e.Step isa ProcessMethodBodies:
				if _method.ReturnType.Entity is voidType:
					returnStatement = endMethod.Body.Statements[-1] as ReturnStatement
					endMethod.Body.Statements.Replace(
							returnStatement,
							ExpressionStatement(returnStatement.Expression))

	private def EmitDisposedObjectCheck(method as Method):
		exceptionCreation = MethodInvocationExpression(
								Target: MemberReferenceExpression(
											ReferenceExpression("System"), "ObjectDisposedException"))
		exceptionCreation.Arguments.Add(StringLiteralExpression(_method.DeclaringType.Name))
		# TODO: Access Boo resources to get the exception message.
		exceptionCreation.Arguments.Add(StringLiteralExpression("<Resources.ObjectDisposedExceptionMessage>"))

		trueBlock = Block()
		trueBlock.Add(RaiseStatement(exceptionCreation))
		method.Body.Add(IfStatement(_disposed.CloneNode(), trueBlock, null))

#region license
// Copyright (c) 2005, Sorin Ionescu (sorin.ionescu@gmail.com)
// 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 Sorin Ionescu 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.Examples.Attributes

import System;
import System.Runtime.Remoting.Messaging
import Boo.Lang.Compiler
import Boo.Lang.Compiler.Ast
import Boo.Lang.Compiler.Steps

class AsyncAttribute(AbstractAstAttribute):
"""
Adds asynchronous helpers Begin<Method>/End<Method>
for a method.
"""
	_method          as Method
	_accessModifiers as TypeMemberModifiers
	_disposed		 as ReferenceExpression

	def constructor():
		pass

	def constructor(disposed as ReferenceExpression):
		_disposed = disposed

	override def Apply(node as Node):
		assert node isa Method

		_method = node

		if _method.IsProtected and _method.IsInternal:
			_accessModifiers = TypeMemberModifiers.Protected
			_accessModifiers = _accessModifiers | TypeMemberModifiers.Internal

		elif _method.IsPublic:
			_accessModifiers = TypeMemberModifiers.Public

		elif _method.IsProtected:
			_accessModifiers = TypeMemberModifiers.Protected

		elif _method.IsInternal:
			_accessModifiers = TypeMemberModifiers.Internal

		elif _method.IsPrivate:
			_accessModifiers = TypeMemberModifiers.Private

		EmitBeginMethod()
		EmitEndMethod()

	private def EmitBeginMethod():
		beginMethod = Method(
						_method.LexicalInfo,
						Name: "Begin" + _method.Name,
						Modifiers: _accessModifiers,
						ReturnType: CodeBuilder.CreateTypeReference(typeof(IAsyncResult)))
		beginMethod.Parameters.ExtendWithClones(_method.Parameters)
		beginMethod.Parameters.Add(
				ParameterDeclaration(
					"callback",
					CodeBuilder.CreateTypeReference(typeof(AsyncCallback))))
		beginMethod.Parameters.Add(
				ParameterDeclaration(
					"state",
					CodeBuilder.CreateTypeReference(typeof(object))))

		asyncInvocation = MethodInvocationExpression(
							Target: MemberReferenceExpression(
										ReferenceExpression(_method.Name),
										"BeginInvoke"))
		for parameter in beginMethod.Parameters:
			asyncInvocation.Arguments.Add(
					ReferenceExpression(parameter.Name))

		EmitDisposedObjectCheck(beginMethod) if _disposed is not null
		beginMethod.Body.Add(ReturnStatement(asyncInvocation))
		_method.DeclaringType.Members.Add(beginMethod)

	private def EmitEndMethod():
		endMethod = Method(
						_method.LexicalInfo,
						Name: "End" + _method.Name,
						Modifiers: _accessModifiers)
		endMethod.Parameters.Add(
			ParameterDeclaration(
				"result",
				CodeBuilder.CreateTypeReference(typeof(IAsyncResult))))

		asyncInvocation = MethodInvocationExpression(
							Target: MemberReferenceExpression(
										ReferenceExpression(_method.Name),
										"EndInvoke"))
		asyncInvocation.Arguments.Add(ReferenceExpression("result"))
		endMethod.Body.Add(ReturnStatement(asyncInvocation))

		_method.DeclaringType.Members.Add(endMethod)

		# cache the voidType reference because we are going
		# to lose the context after this method returns
		# (see AbstractCompilerComponent.Dispose)
		voidType = Context.TypeSystemServices.VoidType
		Context.Parameters.Pipeline.AfterStep += def (sender, e as CompilerStepEventArgs):
			if e.Step isa ProcessMethodBodies:
				if _method.ReturnType.Entity is voidType:
					returnStatement = endMethod.Body.Statements[-1] as ReturnStatement
					endMethod.Body.Statements.Replace(
							returnStatement,
							ExpressionStatement(returnStatement.Expression))

	private def EmitDisposedObjectCheck(method as Method):
		exceptionCreation = MethodInvocationExpression(
								Target: MemberReferenceExpression(
											ReferenceExpression("System"), "ObjectDisposedException"))
		exceptionCreation.Arguments.Add(StringLiteralExpression(_method.DeclaringType.Name))
		# TODO: Access Boo resources to get the exception message.
		exceptionCreation.Arguments.Add(StringLiteralExpression("<Resources.ObjectDisposedExceptionMessage>"))

		trueBlock = Block()
		trueBlock.Add(RaiseStatement(exceptionCreation))
		method.Body.Add(IfStatement(_disposed, trueBlock, null))


Support =~ and !~ operators between strings

namespace BooJs.Lang.Globals

import BooJs.Lang.Extensions

[EnumeratorItemType(String)]
class String(Object, Iterable):

    # IMPORTANT: Boo requires explicit/implicit operators to use the actual types
    [Transform( parseInt($1) )]
    static def op_Explicit(value as String) as NumberInt:
        pass

    [Transform($1 == $2)]
    static def op_Equality(lhs as string, rhs as string) as bool:
        pass

    [Transform($1 + $2)]
    static def op_Addition(lhs as string, rhs as string) as string:
        pass

    [Transform($1 + $2)]
    static def op_Addition(lhs as string, rhs as double) as string:
        pass

    # Multiply operator: 'foo' * 2 --> 'foofoo'
    [Transform( Boo.String.op_Multiply($1, $2) )]
    static def op_Multiply(lhs as string, rhs as int) as string:
        pass
    # Formatting: '{0} {1}' % ('foo', 'bar')
    [Transform( Boo.String.op_Modulus($1, $2) )]
    static def op_Modulus(lhs as String, rhs as Array) as string:
        pass

    [Transform( RegExp($2).test($1) )]
    static def op_Match(lhs as string, rhs as string) as bool:
        pass

    [Transform( not RegExp($2).test($1) )]
    static def op_NotMatch(lhs as string, rhs as string) as bool:
        pass

    # Static methods

    static def fromCharCode(code as int) as string:
        pass

    # Comply with Iterable interface
    def iterator():
        pass

    # Instance members

    self[index as int] as string:
        [Transform( Boo.slice($0, $1) )]
        get: pass

    public length as uint

    def charAt(idx as int) as string:
        pass
    def charCodeAt(idx as int) as int:
        pass
    def concat(str as string) as string:
        pass
    def indexOf(str as string) as int:
        pass
    def lastIndexOf(str as string) as int:
        pass

    def match(re as RegExp) as bool:
        pass
    def replace(re as RegExp, repl as string) as string:
        pass
    def replace(substr as string, repl as string) as string:
        pass
    def replace(re as RegExp, repl as callable) as string:
        pass
    def replace(substr as string, repl as callable) as string:
        pass

    def split(sep as string) as (string):
        pass

    def substr(start as uint, length as int) as string:
        pass
    def substring(start as uint, stop as int) as string:
        pass

    def toUpperCase() as string:
        pass

    def toLowerCase() as string:
        pass

    def trim() as string:
        pass

namespace BooJs.Lang.Globals

import BooJs.Lang.Extensions

[EnumeratorItemType(String)]
class String(Object, Iterable):

    # IMPORTANT: Boo requires explicit/implicit operators to use the actual types
    [Transform( parseInt($1) )]
    static def op_Explicit(value as String) as NumberInt:
        pass

    [Transform($1 == $2)]
    static def op_Equality(lhs as string, rhs as string) as bool:
        pass

    [Transform($1 + $2)]
    static def op_Addition(lhs as string, rhs as string) as string:
        pass

    [Transform($1 + $2)]
    static def op_Addition(lhs as string, rhs as double) as string:
        pass

    # Multiply operator: 'foo' * 2 --> 'foofoo'
    [Transform( Boo.String.op_Multiply($1, $2) )]
    static def op_Multiply(lhs as string, rhs as int) as string:
        pass
    # Formatting: '{0} {1}' % ('foo', 'bar')
    [Transform( Boo.String.op_Modulus($1, $2) )]
    static def op_Modulus(lhs as String, rhs as Array) as string:
        pass

    # Static methods

    static def fromCharCode(code as int) as string:
        pass

    # Comply with Iterable interface
    def iterator():
        pass

    # Instance members

    self[index as int] as string:
        [Transform( Boo.slice($0, $1) )]
        get: pass

    public length as uint

    def charAt(idx as int) as string:
        pass
    def charCodeAt(idx as int) as int:
        pass
    def concat(str as string) as string:
        pass
    def indexOf(str as string) as int:
        pass
    def lastIndexOf(str as string) as int:
        pass

    def match(re as RegExp) as bool:
        pass
    def replace(re as RegExp, repl as string) as string:
        pass
    def replace(substr as string, repl as string) as string:
        pass
    def replace(re as RegExp, repl as callable) as string:
        pass
    def replace(substr as string, repl as callable) as string:
        pass

    def split(sep as string) as (string):
        pass

    def substr(start as uint, length as int) as string:
        pass
    def substring(start as uint, stop as int) as string:
        pass

    def toUpperCase() as string:
        pass

    def toLowerCase() as string:
        pass

    def trim() as string:
        pass


test case methods missing from a previous commit

namespace UnityScript.Tests

import NUnit.Framework

partial class ErrorMessagesTestFixture:

	[Test] def Boo_Lang_List_is_not_visible_by_default():
		RunTestCase("tests/error-messages/Boo.Lang.List-is-not-visible-by-default.js")

	[Test] def array_instantiation_with_array_multiplication():
		RunTestCase("tests/error-messages/array-instantiation-with-array-multiplication.js")

	[Test] def array_struct_warning():
		RunTestCase("tests/error-messages/array-struct-warning.js")

	[Test] def array_type_name():
		RunTestCase("tests/error-messages/array-type-name.js")

	[Test] def bitwise_with_bool_operands_warning():
		RunTestCase("tests/error-messages/bitwise-with-bool-operands-warning.js")

	[Test] def builtins():
		RunTestCase("tests/error-messages/builtins.js")

	[Test] def cannot_convert_error():
		RunTestCase("tests/error-messages/cannot-convert-error.js")

	[Test] def do_while_const_condition():
		RunTestCase("tests/error-messages/do-while-const-condition.js")

	[Test] def double_array_in_float_array_var_initializer():
		RunTestCase("tests/error-messages/double-array-in-float-array-var-initializer.js")

	[Test] def error_for_final_used_as_identifier():
		RunTestCase("tests/error-messages/error-for-final-used-as-identifier.js")

	[Test] def error_for_internal_used_as_identifier():
		RunTestCase("tests/error-messages/error-for-internal-used-as-identifier.js")

	[Test] def for_variable_already_defined():
		RunTestCase("tests/error-messages/for-variable-already-defined.js")

	[Test] def for_with_unreachable_update():
		RunTestCase("tests/error-messages/for-with-unreachable-update.js")

	[Test] def function_type_name():
		RunTestCase("tests/error-messages/function-type-name.js")

	[Test] def generic_type_name():
		RunTestCase("tests/error-messages/generic-type-name.js")

	[Test] def implicit_main_shouldnt_be_redefined():
		RunTestCase("tests/error-messages/implicit-main-shouldnt-be-redefined.js")

	[Test] def incompatible_initializer_1():
		RunTestCase("tests/error-messages/incompatible-initializer-1.js")

	[Test] def inner_text():
		RunTestCase("tests/error-messages/inner_text.js")

	[Test] def interface_errors():
		RunTestCase("tests/error-messages/interface-errors.js")

	[Test] def invalid_attribute_sequence():
		RunTestCase("tests/error-messages/invalid-attribute-sequence.js")

	[Test] def invalid_generator_return_type():
		RunTestCase("tests/error-messages/invalid-generator-return-type.js")

	[Test] def iterator_warning():
		RunTestCase("tests/error-messages/iterator-warning.js")

	[Test] def loop_with_no_unreacheable_code_shouldnt_cause_warning():
		RunTestCase("tests/error-messages/loop-with-no-unreacheable-code-shouldnt-cause-warning.js")

	[Test] def missing_brace():
		RunTestCase("tests/error-messages/missing-brace.js")

	[Test] def nolinenumber():
		RunTestCase("tests/error-messages/nolinenumber.js")

	[Test] def non_strict_does_not_warn_about_downcast():
		RunTestCase("tests/error-messages/non-strict-does-not-warn-about-downcast.js")

	[Test] def obsolete_2():
		RunTestCase("tests/error-messages/obsolete-2.js")

	[Test] def obsolete():
		RunTestCase("tests/error-messages/obsolete.js")

	[Ignore("requires new parser infrastructure")]
	[Test] def overeager_semicolon():
		RunTestCase("tests/error-messages/overeager-semicolon.js")

	[Ignore("requires new parser infrastructure")]
	[Test] def overeager_semicolon2():
		RunTestCase("tests/error-messages/overeager-semicolon2.js")

	[Test] def rethrow_outside_catch():
		RunTestCase("tests/error-messages/rethrow-outside-catch.js")

	[Test] def semicolon_2():
		RunTestCase("tests/error-messages/semicolon-2.js")

	[Test] def semicolon():
		RunTestCase("tests/error-messages/semicolon.js")

	[Test] def static_instance_overload_resolution():
		RunTestCase("tests/error-messages/static-instance-overload-resolution.js")

	[Test] def strict_conditional_compilation():
		RunTestCase("tests/error-messages/strict-conditional-compilation.js")

	[Test] def strict_disallows_dynamic_dispatching():
		RunTestCase("tests/error-messages/strict-disallows-dynamic-dispatching.js")

	[Test] def strict_disallows_implicit_variable_declaration():
		RunTestCase("tests/error-messages/strict-disallows-implicit-variable-declaration.js")

	[Test] def strict_nongeneric_GetComponent():
		RunTestCase("tests/error-messages/strict-nongeneric-GetComponent.js")

	[Test] def strict_warns_about_array_downcast():
		RunTestCase("tests/error-messages/strict-warns-about-array-downcast.js")

	[Test] def strict_warns_about_downcast():
		RunTestCase("tests/error-messages/strict-warns-about-downcast.js")

	[Test] def strict_warns_about_duck_downcast():
		RunTestCase("tests/error-messages/strict-warns-about-duck-downcast.js")

	[Test] def strict_with_downcast_off_disallows_downcast():
		RunTestCase("tests/error-messages/strict-with-downcast-off-disallows-downcast.js")

	[Test] def strict_with_downcast_off_disallows_duck_downcast():
		RunTestCase("tests/error-messages/strict-with-downcast-off-disallows-duck-downcast.js")

	[Test] def strict_with_pragma_downcast_does_not_warn_about_downcast():
		RunTestCase("tests/error-messages/strict-with-pragma-downcast-does-not-warn-about-downcast.js")

	[Test] def switch_has_no_cases():
		RunTestCase("tests/error-messages/switch-has-no-cases.js")

	[Test] def unused_variable():
		RunTestCase("tests/error-messages/unused-variable.js")

	[Test] def virtual_warning():
		RunTestCase("tests/error-messages/virtual-warning.js")

	[Test] def void_declaration():
		RunTestCase("tests/error-messages/void-declaration.js")

	[Test] def void_in_hashtable():
		RunTestCase("tests/error-messages/void-in-hashtable.js")

	[Test] def voidcomparison():
		RunTestCase("tests/error-messages/voidcomparison.js")

	[Test] def yield_from_try_catch_finally():
		RunTestCase("tests/error-messages/yield-from-try-catch-finally.js")

namespace UnityScript.Tests

import NUnit.Framework

partial class ErrorMessagesTestFixture:

	[Test] def Boo_Lang_List_is_not_visible_by_default():
		RunTestCase("tests/error-messages/Boo.Lang.List-is-not-visible-by-default.js")

	[Test] def array_instantiation_with_array_multiplication():
		RunTestCase("tests/error-messages/array-instantiation-with-array-multiplication.js")

	[Test] def array_struct_warning():
		RunTestCase("tests/error-messages/array-struct-warning.js")

	[Test] def array_type_name():
		RunTestCase("tests/error-messages/array-type-name.js")

	[Test] def bitwise_with_bool_operands_warning():
		RunTestCase("tests/error-messages/bitwise-with-bool-operands-warning.js")

	[Test] def builtins():
		RunTestCase("tests/error-messages/builtins.js")

	[Test] def cannot_convert_error():
		RunTestCase("tests/error-messages/cannot-convert-error.js")

	[Test] def do_while_const_condition():
		RunTestCase("tests/error-messages/do-while-const-condition.js")

	[Test] def double_array_in_float_array_var_initializer():
		RunTestCase("tests/error-messages/double-array-in-float-array-var-initializer.js")

	[Test] def error_for_final_used_as_identifier():
		RunTestCase("tests/error-messages/error-for-final-used-as-identifier.js")

	[Test] def error_for_internal_used_as_identifier():
		RunTestCase("tests/error-messages/error-for-internal-used-as-identifier.js")

	[Test] def for_variable_already_defined():
		RunTestCase("tests/error-messages/for-variable-already-defined.js")

	[Test] def function_type_name():
		RunTestCase("tests/error-messages/function-type-name.js")

	[Test] def generic_type_name():
		RunTestCase("tests/error-messages/generic-type-name.js")

	[Test] def implicit_main_shouldnt_be_redefined():
		RunTestCase("tests/error-messages/implicit-main-shouldnt-be-redefined.js")

	[Test] def incompatible_initializer_1():
		RunTestCase("tests/error-messages/incompatible-initializer-1.js")

	[Test] def inner_text():
		RunTestCase("tests/error-messages/inner_text.js")

	[Test] def interface_errors():
		RunTestCase("tests/error-messages/interface-errors.js")

	[Test] def invalid_attribute_sequence():
		RunTestCase("tests/error-messages/invalid-attribute-sequence.js")

	[Test] def invalid_generator_return_type():
		RunTestCase("tests/error-messages/invalid-generator-return-type.js")

	[Test] def iterator_warning():
		RunTestCase("tests/error-messages/iterator-warning.js")

	[Test] def missing_brace():
		RunTestCase("tests/error-messages/missing-brace.js")

	[Test] def nolinenumber():
		RunTestCase("tests/error-messages/nolinenumber.js")

	[Test] def non_strict_does_not_warn_about_downcast():
		RunTestCase("tests/error-messages/non-strict-does-not-warn-about-downcast.js")

	[Test] def obsolete_2():
		RunTestCase("tests/error-messages/obsolete-2.js")

	[Test] def obsolete():
		RunTestCase("tests/error-messages/obsolete.js")

	[Ignore("requires new parser infrastructure")]
	[Test] def overeager_semicolon():
		RunTestCase("tests/error-messages/overeager-semicolon.js")

	[Ignore("requires new parser infrastructure")]
	[Test] def overeager_semicolon2():
		RunTestCase("tests/error-messages/overeager-semicolon2.js")

	[Test] def rethrow_outside_catch():
		RunTestCase("tests/error-messages/rethrow-outside-catch.js")

	[Test] def semicolon_2():
		RunTestCase("tests/error-messages/semicolon-2.js")

	[Test] def semicolon():
		RunTestCase("tests/error-messages/semicolon.js")

	[Test] def static_instance_overload_resolution():
		RunTestCase("tests/error-messages/static-instance-overload-resolution.js")

	[Test] def strict_conditional_compilation():
		RunTestCase("tests/error-messages/strict-conditional-compilation.js")

	[Test] def strict_disallows_dynamic_dispatching():
		RunTestCase("tests/error-messages/strict-disallows-dynamic-dispatching.js")

	[Test] def strict_disallows_implicit_variable_declaration():
		RunTestCase("tests/error-messages/strict-disallows-implicit-variable-declaration.js")

	[Test] def strict_nongeneric_GetComponent():
		RunTestCase("tests/error-messages/strict-nongeneric-GetComponent.js")

	[Test] def strict_warns_about_array_downcast():
		RunTestCase("tests/error-messages/strict-warns-about-array-downcast.js")

	[Test] def strict_warns_about_downcast():
		RunTestCase("tests/error-messages/strict-warns-about-downcast.js")

	[Test] def strict_warns_about_duck_downcast():
		RunTestCase("tests/error-messages/strict-warns-about-duck-downcast.js")

	[Test] def strict_with_downcast_off_disallows_downcast():
		RunTestCase("tests/error-messages/strict-with-downcast-off-disallows-downcast.js")

	[Test] def strict_with_downcast_off_disallows_duck_downcast():
		RunTestCase("tests/error-messages/strict-with-downcast-off-disallows-duck-downcast.js")

	[Test] def strict_with_pragma_downcast_does_not_warn_about_downcast():
		RunTestCase("tests/error-messages/strict-with-pragma-downcast-does-not-warn-about-downcast.js")

	[Test] def switch_has_no_cases():
		RunTestCase("tests/error-messages/switch-has-no-cases.js")

	[Test] def unused_variable():
		RunTestCase("tests/error-messages/unused-variable.js")

	[Test] def virtual_warning():
		RunTestCase("tests/error-messages/virtual-warning.js")

	[Test] def void_declaration():
		RunTestCase("tests/error-messages/void-declaration.js")

	[Test] def void_in_hashtable():
		RunTestCase("tests/error-messages/void-in-hashtable.js")

	[Test] def voidcomparison():
		RunTestCase("tests/error-messages/voidcomparison.js")

	[Test] def yield_from_try_catch_finally():
		RunTestCase("tests/error-messages/yield-from-try-catch-finally.js")


support for optional <code /> element for <boo /> task (makes it easier to use references together with <boo />)

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

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

namespace Boo.NAnt

import System
import System.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:
		get:
			return _project
		set:
			_project = value

	_project as Project

	Task:
		get:
			return _task
		set:
			_task = value
	_task as BooTask

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

	abstract def Run():
		pass

class PrepareScriptStep(AbstractCompilerStep):

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

		method = Method(Name: "Run",
						Modifiers: TypeMemberModifiers.Override,
						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

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

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

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

#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 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:
		get:
			return _project
		set:
			_project = value

	_project as Project

	Task:
		get:
			return _task
		set:
			_task = value
	_task as BooTask

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

	abstract def Run():
		pass

class PrepareScriptStep(AbstractCompilerStep):

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

		method = Method(Name: "Run",
						Modifiers: TypeMemberModifiers.Override,
						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

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

	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()
		except x:
			raise BuildException(x.Message, Location, x)

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

	private def getSourceCode():
		codeNode = self.XmlNode.SelectSingleNode("code")
		return (codeNode or self.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()


InteractiveInterpreterOptions seems like a bad idea

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

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

namespace booish

import System
import Boo.Lang
import Boo.Lang.Compiler
import Boo.Lang.Compiler.Ast
import Boo.Lang.Compiler.TypeSystem
import Boo.Lang.Compiler.IO

class InteractiveInterpreter:

	_compiler = BooCompiler()

	_parser = BooCompiler()

	_values = {}

	_declarations = {}

	_imports = ImportCollection()

	_referenceProcessor = ProcessInterpreterReferences(self)

	[property(RememberLastValue)]
	_rememberLastValue = false

	def constructor():

		pipeline = Pipelines.CompileToMemory()
		pipeline.RemoveAt(0)

		index = pipeline.Find(Steps.ProcessMethodBodiesWithDuckTyping)
		pipeline[index] = ProcessVariableDeclarations(self)
		pipeline.InsertBefore(Steps.EmitAssembly, _referenceProcessor)

		index = pipeline.Find(Steps.IntroduceModuleClasses)
		cast(Steps.IntroduceModuleClasses, pipeline[index]).ForceModuleClass = true

		_compiler.Parameters.Pipeline = pipeline
		_parser.Parameters.Pipeline = Pipelines.Parse()

	LastValue:
		get:
			return GetValue("@value")

	Pipeline:
		get:
			return _compiler.Parameters.Pipeline

	def Eval(code as string):

		result = Parse(code)
		return result if len(result.Errors)

		cu = result.CompileUnit
		module = cu.Modules[0]

		# remember the state of the module as of after parsing
		hasStatements = module.Globals.Statements.Count > 0
		hasMembers = module.Members.Count > 0

		module.Imports.Reject() do (item as Import):
			for existing in _imports:
				return true if existing.Namespace == item.Namespace

		if ((not hasStatements) and
			(not hasMembers) and
			0 == len(module.Imports)):
			return result

		savedImports = module.Imports.Clone()
		module.Imports.ExtendWithClones(_imports)

		if hasStatements:
			if IsSimpleReference(code):
				# simple references will be parsed as macros
				# but we want them to be evaluated
				# as references...
				ms as MacroStatement = module.Globals.Statements[0]
				module.Globals.Statements.ReplaceAt(0,
					ExpressionStatement(
							ReferenceExpression(ms.LexicalInfo, ms.Name)))
			_compiler.Parameters.OutputType = CompilerOutputType.ConsoleApplication
		else:
			_compiler.Parameters.OutputType = CompilerOutputType.Library

		SetValue("@value", null)
		result = _compiler.Run(cu)
		return result if len(result.Errors)

		RecordImports(savedImports)

		asm = result.GeneratedAssembly
		_compiler.Parameters.References.Add(asm) if hasMembers

		InitializeModuleInterpreter(asm, module)

		result.GeneratedAssemblyEntryPoint.Invoke(null, (null,)) if hasStatements
		return result

	def Parse(code as string):
		_parser.Parameters.Input.Clear()
		_parser.Parameters.Input.Add(StringInput("interactive", code))
		return _parser.Run()

	def Declare([required] name as string,
				[required] type as System.Type):
		_declarations.Add(name, type)

	def Lookup([required] name as string):
		type as System.Type = _declarations[name]
		return type if type is not null

		value = GetValue(name)
		return value.GetType() if value is not null

	def SetValue(name as string, value):
		_values[name] = value
		return value

	def GetValue(name as string):
		return _values[name]

	private def InitializeModuleInterpreter(asm as System.Reflection.Assembly,
										module as Module):
		moduleType = asm.GetType(cast(ModuleEntity, module.Entity).ModuleClass.FullName)
		moduleType.GetField("ParentInterpreter").SetValue(null, self)

	private def RecordImports(imports as ImportCollection):
		for imp in imports:
			imp.AssemblyReference = null
			_imports.Add(imp)

	private def IsSimpleReference(s as string):
		return /^\s*[_a-zA-Z][_a-zA-Z\d]*\s*$/.IsMatch(s)

	class InterpreterEntity(ITypedEntity):

		[getter(Name)]
		[getter(FullName)]
		_name as string

		[getter(Type)]
		_type as IType

		def constructor(name, type):
			_name = name
			_type = type

		EntityType:
			get:
				return TypeSystem.EntityType.Custom

		static def IsInterpreterEntity(node as Node):
			return node.Entity is not null and TypeSystem.EntityType.Custom == node.Entity.EntityType

	class InterpreterNamespace(INamespace):

		[getter(ParentNamespace)]
		_parent as INamespace

		_tss as TypeSystemServices

		_interpreter as InteractiveInterpreter

		_declarations = {}

		def constructor(interpreter, tss, parent):
			_interpreter = interpreter
			_tss = tss
			_parent = parent

		def Declare(name as string, type as IType):
			entity = InterpreterEntity(name, type)
			_declarations.Add(name, entity)
			return entity

		def Resolve(targetList as List, name as string, flags as EntityType) as bool:
			return false unless flags == EntityType.Any

			entity as IEntity = _declarations[name]
			if entity is null:
				type = _interpreter.Lookup(name)
				if type is not null:
					if type is object:
						entity = Declare(name, _tss.DuckType)
					else:
						entity = Declare(name, _tss.Map(type))

			if entity is not null:
				targetList.Add(entity)
				return true

			return false

		def GetMembers():
			return array(IEntity, 0)

	class ProcessVariableDeclarations(Steps.ProcessMethodBodiesWithDuckTyping):

		_namespace as InterpreterNamespace
		_interpreter as InteractiveInterpreter
		_isEntryPoint = false

		def constructor(interpreter):
			_interpreter = interpreter

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

			_namespace = InterpreterNamespace(
								_interpreter,
								TypeSystemServices,
								NameResolutionService.GlobalNamespace)
			NameResolutionService.GlobalNamespace = _namespace

		override def Dispose():
			_namespace = null

		override def OnMethod(node as Method):
			_isEntryPoint = node is Steps.ContextAnnotations.GetEntryPoint(Context);
			super(node)

		override def LeaveExpressionStatement(node as ExpressionStatement):
			# force standalone method references types to be completely
			# resolved
			GetConcreteExpressionType(node.Expression)
			super(node)

		override def HasSideEffect(node as Expression):
			return true if _interpreter.RememberLastValue
			return super(node)

		override def CheckLValue(node as Node):
			# prevent 'Expression can't be assigned to' error
			return true if InterpreterEntity.IsInterpreterEntity(node)
			return super(node)

		override def DeclareLocal(name as string, type as IType, privateScope as bool):
			return super(name, type, privateScope) if privateScope or not _isEntryPoint

			external = type as ExternalType
			_interpreter.Declare(name, external.ActualType) if external

			return _namespace.Declare(name, type)

	class ProcessInterpreterReferences(Steps.AbstractTransformerCompilerStep):

		static InteractiveInterpreter_GetValue = typeof(InteractiveInterpreter).GetMethod("GetValue")
		static InteractiveInterpreter_SetValue = typeof(InteractiveInterpreter).GetMethod("SetValue")

		_interpreterField as Field
		_interpreter as InteractiveInterpreter
		_isEntryPoint = false

		def constructor(interpreter):
			_interpreter = interpreter

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

		override def EnterModule(node as Module):

			module = cast(ModuleEntity, node.Entity).ModuleClass
			return false unless module

			_interpreterField = CodeBuilder.CreateField("ParentInterpreter", TypeSystemServices.Map(InteractiveInterpreter))
			_interpreterField.Modifiers = TypeMemberModifiers.Public | TypeMemberModifiers.Static
			module.Members.Add(_interpreterField)

			return true

		override def OnConstructor(node as Constructor):
			OnMethod(node)

		override def OnMethod(node as Method):
			_isEntryPoint = node is Steps.ContextAnnotations.GetEntryPoint(Context);
			super(node)

		override def OnReferenceExpression(node as ReferenceExpression):

			if (InterpreterEntity.IsInterpreterEntity(node) and
					not AstUtil.IsLhsOfAssignment(node)):
				ReplaceCurrentNode(CreateGetValue(node))

		override def LeaveBinaryExpression(node as BinaryExpression):
			if InterpreterEntity.IsInterpreterEntity(node.Left):
				ReplaceCurrentNode(CreateSetValue(node))

		override def LeaveExpressionStatement(node as ExpressionStatement):

			return unless _interpreter.RememberLastValue and _isEntryPoint

			if node.Expression.ExpressionType is not TypeSystemServices.VoidType:
				node.Expression = CreateInterpreterInvocation(
								InteractiveInterpreter_SetValue,
								"@value",
								node.Expression)
			else:
				eval = CodeBuilder.CreateEvalInvocation(node.LexicalInfo)
				eval.Arguments.Add(node.Expression)
				eval.Arguments.Add(
						CreateInterpreterInvocation(
							InteractiveInterpreter_SetValue,
							"@value",
							CodeBuilder.CreateNullLiteral()))
				node.Expression = eval

		def CreateInterpreterInvocation(method as System.Reflection.MethodInfo,
										name as string,
										value as Expression):
			mie = CreateInterpreterInvocation(method, name)
			mie.Arguments.Add(value)
			return mie

		def CreateInterpreterReference():
			return CodeBuilder.CreateReference(_interpreterField)

		def CreateInterpreterInvocation(method as System.Reflection.MethodInfo,
										name as string):
			return CodeBuilder.CreateMethodInvocation(
						CreateInterpreterReference(),
						TypeSystemServices.Map(method),
						CodeBuilder.CreateStringLiteral(name))

		def CreateGetValue(node as ReferenceExpression):
			return CastIfNeeded(
					node,
					CreateInterpreterInvocation(
						InteractiveInterpreter_GetValue,
						node.Name))

		def CreateSetValue(node as BinaryExpression):
			return CastIfNeeded(
					node,
					CreateInterpreterInvocation(
						InteractiveInterpreter_SetValue,
						cast(ReferenceExpression, node.Left).Name,
						node.Right))

		def CastIfNeeded(srcNode as Expression, expression as Expression):
			if NodeType.ExpressionStatement == srcNode.ParentNode.NodeType:
				return expression

			return CodeBuilder.CreateCast(srcNode.ExpressionType, expression)

def WriteRepr(writer as System.IO.TextWriter, value):
	code = Type.GetTypeCode(value.GetType())
	if TypeCode.String == code:
		Visitors.BooPrinterVisitor.WriteStringLiteral(value, writer)
	elif TypeCode.Object == code:
		a = value as Array
		if a is not null:
			writer.Write("(")
			for i in range(len(a)):
				writer.Write(", ") if i > 0
				WriteRepr(writer, a.GetValue(i))
			writer.Write(", ") if 1 == len(a)
			writer.Write(")")
		else:
			c = value as Delegate
			if c is not null:
				method = c.Method
				writer.Write(method.DeclaringType.FullName)
				writer.Write(".")
				writer.Write(method.Name)
			else:
				writer.Write(value)
	else:
		writer.Write(value)

def ReadBlock(line as string):
	newLine = System.Environment.NewLine
	buffer = System.Text.StringBuilder()
	buffer.Append(line)
	buffer.Append(newLine)
	while line=prompt("... "):
		break if 0 == len(line)
		buffer.Append(line)
		buffer.Append(newLine)
	return buffer.ToString()

def DisplayErrors(errors as CompilerErrorCollection):
	for error in errors:
		pos = error.LexicalInfo.StartColumn
		print("---" + "-" * pos + "^") if pos > 0
		print("ERROR: ${error.Message}")

interpreter = InteractiveInterpreter(RememberLastValue: true)

if "--print-modules" in argv:
	interpreter.Pipeline.Add(Steps.PrintBoo())

while line=prompt(">>> "):
	try:
		line = ReadBlock(line) if line[-1:] in ":", "\\"
		result = interpreter.Eval(line)
		if len(result.Errors):
			DisplayErrors(result.Errors)
		else:
			_ = interpreter.LastValue
			if _ is not null:
				WriteRepr(Console.Out, _)
				Console.WriteLine()
				interpreter.SetValue("_", _)
	except x as System.Reflection.TargetInvocationException:
		print(x.InnerException)
	except x:
		print(x)

#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 booish

import System
import Boo.Lang
import Boo.Lang.Compiler
import Boo.Lang.Compiler.Ast
import Boo.Lang.Compiler.TypeSystem
import Boo.Lang.Compiler.IO

enum InteractiveInterpreterOptions:

	None = 0

	PrintInterpretedModules = 1

class InteractiveInterpreter:

	_compiler = BooCompiler()

	_parser = BooCompiler()

	_values = {}

	_declarations = {}

	_imports = ImportCollection()

	_referenceProcessor = ProcessInterpreterReferences(self)

	[property(RememberLastValue)]
	_rememberLastValue = false

	def constructor():
		Initialize(InteractiveInterpreterOptions.None)

	def constructor(options as InteractiveInterpreterOptions):
		Initialize(options)

	private def Initialize(options as InteractiveInterpreterOptions):

		pipeline = Pipelines.CompileToMemory()
		pipeline.RemoveAt(0)

		index = pipeline.Find(Steps.ProcessMethodBodiesWithDuckTyping)
		pipeline[index] = ProcessVariableDeclarations(self)
		pipeline.InsertBefore(Steps.EmitAssembly, _referenceProcessor)

		index = pipeline.Find(Steps.IntroduceModuleClasses)
		cast(Steps.IntroduceModuleClasses, pipeline[index]).ForceModuleClass = true

		if InteractiveInterpreterOptions.PrintInterpretedModules == (
				options & InteractiveInterpreterOptions.PrintInterpretedModules):
			pipeline.Add(Steps.PrintBoo())

		_compiler.Parameters.Pipeline = pipeline

		_parser.Parameters.Pipeline = Pipelines.Parse()

	LastValue:
		get:
			return GetValue("@value")

	def Eval(code as string):

		result = Parse(code)
		return result if len(result.Errors)

		cu = result.CompileUnit
		module = cu.Modules[0]

		# remember the state of the module as of after parsing
		hasStatements = module.Globals.Statements.Count > 0
		hasMembers = module.Members.Count > 0

		module.Imports.Reject() do (item as Import):
			for existing in _imports:
				return true if existing.Namespace == item.Namespace

		if ((not hasStatements) and
			(not hasMembers) and
			0 == len(module.Imports)):
			return result

		savedImports = module.Imports.Clone()
		module.Imports.ExtendWithClones(_imports)

		if hasStatements:
			if IsSimpleReference(code):
				# simple references will be parsed as macros
				# but we want them to be evaluated
				# as references...
				ms as MacroStatement = module.Globals.Statements[0]
				module.Globals.Statements.ReplaceAt(0,
					ExpressionStatement(
							ReferenceExpression(ms.LexicalInfo, ms.Name)))
			_compiler.Parameters.OutputType = CompilerOutputType.ConsoleApplication
		else:
			_compiler.Parameters.OutputType = CompilerOutputType.Library

		SetValue("@value", null)
		result = _compiler.Run(cu)
		return result if len(result.Errors)

		RecordImports(savedImports)

		asm = result.GeneratedAssembly
		_compiler.Parameters.References.Add(asm) if hasMembers

		InitializeModuleInterpreter(asm, module)

		result.GeneratedAssemblyEntryPoint.Invoke(null, (null,)) if hasStatements
		return result

	def Parse(code as string):
		_parser.Parameters.Input.Clear()
		_parser.Parameters.Input.Add(StringInput("interactive", code))
		return _parser.Run()

	def Declare([required] name as string,
				[required] type as System.Type):
		_declarations.Add(name, type)

	def Lookup([required] name as string):
		type as System.Type = _declarations[name]
		return type if type is not null

		value = GetValue(name)
		return value.GetType() if value is not null

	def SetValue(name as string, value):
		_values[name] = value
		return value

	def GetValue(name as string):
		return _values[name]

	private def InitializeModuleInterpreter(asm as System.Reflection.Assembly,
										module as Module):
		moduleType = asm.GetType(cast(ModuleEntity, module.Entity).ModuleClass.FullName)
		moduleType.GetField("ParentInterpreter").SetValue(null, self)

	private def RecordImports(imports as ImportCollection):
		for imp in imports:
			imp.AssemblyReference = null
			_imports.Add(imp)

	private def IsSimpleReference(s as string):
		return /^\s*[_a-zA-Z][_a-zA-Z\d]*\s*$/.IsMatch(s)

	class InterpreterEntity(ITypedEntity):

		[getter(Name)]
		[getter(FullName)]
		_name as string

		[getter(Type)]
		_type as IType

		def constructor(name, type):
			_name = name
			_type = type

		EntityType:
			get:
				return TypeSystem.EntityType.Custom

		static def IsInterpreterEntity(node as Node):
			return node.Entity is not null and TypeSystem.EntityType.Custom == node.Entity.EntityType

	class InterpreterNamespace(INamespace):

		[getter(ParentNamespace)]
		_parent as INamespace

		_tss as TypeSystemServices

		_interpreter as InteractiveInterpreter

		_declarations = {}

		def constructor(interpreter, tss, parent):
			_interpreter = interpreter
			_tss = tss
			_parent = parent

		def Declare(name as string, type as IType):
			entity = InterpreterEntity(name, type)
			_declarations.Add(name, entity)
			return entity

		def Resolve(targetList as List, name as string, flags as EntityType) as bool:
			return false unless flags == EntityType.Any

			entity as IEntity = _declarations[name]
			if entity is null:
				type = _interpreter.Lookup(name)
				if type is not null:
					if type is object:
						entity = Declare(name, _tss.DuckType)
					else:
						entity = Declare(name, _tss.Map(type))

			if entity is not null:
				targetList.Add(entity)
				return true

			return false

		def GetMembers():
			return array(IEntity, 0)

	class ProcessVariableDeclarations(Steps.ProcessMethodBodiesWithDuckTyping):

		_namespace as InterpreterNamespace
		_interpreter as InteractiveInterpreter
		_isEntryPoint = false

		def constructor(interpreter):
			_interpreter = interpreter

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

			_namespace = InterpreterNamespace(
								_interpreter,
								TypeSystemServices,
								NameResolutionService.GlobalNamespace)
			NameResolutionService.GlobalNamespace = _namespace

		override def Dispose():
			_namespace = null

		override def OnMethod(node as Method):
			_isEntryPoint = node is Steps.ContextAnnotations.GetEntryPoint(Context);
			super(node)

		override def LeaveExpressionStatement(node as ExpressionStatement):
			GetConcreteExpressionType(node.Expression)
			super(node)

		override def HasSideEffect(node as Expression):
			return true if _interpreter.RememberLastValue
			return super(node)

		override def CheckLValue(node as Node):
			# prevent 'Expression can't be assigned to' error
			return true if InterpreterEntity.IsInterpreterEntity(node)
			return super(node)

		override def DeclareLocal(name as string, type as IType, privateScope as bool):
			return super(name, type, privateScope) if privateScope or not _isEntryPoint

			external = type as ExternalType
			_interpreter.Declare(name, external.ActualType) if external

			return _namespace.Declare(name, type)

	class ProcessInterpreterReferences(Steps.AbstractTransformerCompilerStep):

		static InteractiveInterpreter_GetValue = typeof(InteractiveInterpreter).GetMethod("GetValue")
		static InteractiveInterpreter_SetValue = typeof(InteractiveInterpreter).GetMethod("SetValue")

		_interpreterField as Field
		_interpreter as InteractiveInterpreter
		_isEntryPoint = false

		def constructor(interpreter):
			_interpreter = interpreter

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

		override def EnterModule(node as Module):

			module = cast(ModuleEntity, node.Entity).ModuleClass
			return false unless module

			_interpreterField = CodeBuilder.CreateField("ParentInterpreter", TypeSystemServices.Map(InteractiveInterpreter))
			_interpreterField.Modifiers = TypeMemberModifiers.Public | TypeMemberModifiers.Static
			module.Members.Add(_interpreterField)

			return true

		override def OnConstructor(node as Constructor):
			OnMethod(node)

		override def OnMethod(node as Method):
			_isEntryPoint = node is Steps.ContextAnnotations.GetEntryPoint(Context);
			super(node)

		override def OnReferenceExpression(node as ReferenceExpression):

			if (InterpreterEntity.IsInterpreterEntity(node) and
					not AstUtil.IsLhsOfAssignment(node)):
				ReplaceCurrentNode(CreateGetValue(node))

		override def LeaveBinaryExpression(node as BinaryExpression):
			if InterpreterEntity.IsInterpreterEntity(node.Left):
				ReplaceCurrentNode(CreateSetValue(node))

		override def LeaveExpressionStatement(node as ExpressionStatement):

			return unless _interpreter.RememberLastValue and _isEntryPoint

			if node.Expression.ExpressionType is not TypeSystemServices.VoidType:
				node.Expression = CreateInterpreterInvocation(
								InteractiveInterpreter_SetValue,
								"@value",
								node.Expression)
			else:
				eval = CodeBuilder.CreateEvalInvocation(node.LexicalInfo)
				eval.Arguments.Add(node.Expression)
				eval.Arguments.Add(
						CreateInterpreterInvocation(
							InteractiveInterpreter_SetValue,
							"@value",
							CodeBuilder.CreateNullLiteral()))
				node.Expression = eval

		def CreateInterpreterInvocation(method as System.Reflection.MethodInfo,
										name as string,
										value as Expression):
			mie = CreateInterpreterInvocation(method, name)
			mie.Arguments.Add(value)
			return mie

		def CreateInterpreterReference():
			return CodeBuilder.CreateReference(_interpreterField)

		def CreateInterpreterInvocation(method as System.Reflection.MethodInfo,
										name as string):
			return CodeBuilder.CreateMethodInvocation(
						CreateInterpreterReference(),
						TypeSystemServices.Map(method),
						CodeBuilder.CreateStringLiteral(name))

		def CreateGetValue(node as ReferenceExpression):
			return CastIfNeeded(
					node,
					CreateInterpreterInvocation(
						InteractiveInterpreter_GetValue,
						node.Name))

		def CreateSetValue(node as BinaryExpression):
			return CastIfNeeded(
					node,
					CreateInterpreterInvocation(
						InteractiveInterpreter_SetValue,
						cast(ReferenceExpression, node.Left).Name,
						node.Right))

		def CastIfNeeded(srcNode as Expression, expression as Expression):
			if NodeType.ExpressionStatement == srcNode.ParentNode.NodeType:
				return expression

			return CodeBuilder.CreateCast(srcNode.ExpressionType, expression)

def WriteRepr(writer as System.IO.TextWriter, value):
	code = Type.GetTypeCode(value.GetType())
	if TypeCode.String == code:
		Visitors.BooPrinterVisitor.WriteStringLiteral(value, writer)
	elif TypeCode.Object == code:
		a = value as Array
		if a is not null:
			writer.Write("(")
			for i in range(len(a)):
				writer.Write(", ") if i > 0
				WriteRepr(writer, a.GetValue(i))
			writer.Write(", ") if 1 == len(a)
			writer.Write(")")
		else:
			c = value as Delegate
			if c is not null:
				method = c.Method
				writer.Write(method.DeclaringType.FullName)
				writer.Write(".")
				writer.Write(method.Name)
			else:
				writer.Write(value)
	else:
		writer.Write(value)

def ReadBlock(line as string):
	newLine = System.Environment.NewLine
	buffer = System.Text.StringBuilder()
	buffer.Append(line)
	buffer.Append(newLine)
	while line=prompt("... "):
		break if 0 == len(line)
		buffer.Append(line)
		buffer.Append(newLine)
	return buffer.ToString()

def DisplayErrors(errors as CompilerErrorCollection):
	for error in errors:
		pos = error.LexicalInfo.StartColumn
		print("---" + "-" * pos + "^") if pos > 0
		print("ERROR: ${error.Message}")

if "--print-modules" in argv:
	options = InteractiveInterpreterOptions.PrintInterpretedModules
else:
	options = InteractiveInterpreterOptions.None

interpreter = InteractiveInterpreter(options, RememberLastValue: true)
while line=prompt(">>> "):
	try:
		line = ReadBlock(line) if line[-1:] in ":", "\\"
		result = interpreter.Eval(line)
		if len(result.Errors):
			DisplayErrors(result.Errors)
		else:
			_ = interpreter.LastValue
			if _ is not null:
				WriteRepr(Console.Out, _)
				Console.WriteLine()
				interpreter.SetValue("_", _)
	except x as System.Reflection.TargetInvocationException:
		print(x.InnerException)
	except x:
		print(x)


Allow to enable ducky mode for fixtures

Allow to enable ducky mode for fixtures

import NUnit.Framework

import System.IO(StreamReader)

import Boo.Lang.Parser
import Boo.Lang.Compiler.Ast
import Boo.Lang.Compiler.IO
import BooJs.Compiler

# The JavaScript interpretter
import Jurassic
import Jurassic.Library

class ConsoleMock(ObjectInstance):
    _output = []

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

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

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

    def reset():
        _output.Clear()

class FixtureRunner:

    static _comp as BooJsCompiler
    static _engine as ScriptEngine

    static def run(file as string):

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

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

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

      result = comp.Run()
      assert 0 == len(result.Errors), result.Errors.ToString(true) + result.CompileUnit.ToCodeString()

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

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

    static def setupCompiler():
        if not _comp:
            pipeline = Pipelines.ProduceBooJs()
            _comp = newBooJsCompiler(pipeline)
            _comp.Parameters.Debug = true

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

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

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

            # Configure the global environment
            console = ConsoleMock(self._engine)
            self._engine.SetGlobalValue('console', console)

            # Load runtime
            self._engine.ExecuteFile('/Users/drslump/www/boojs/src/Boo.js')

        return self._engine

    static def runTest(code as string, expected as string):
        print '---------------------------------------------[code]-'
        print code

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

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

          # Wrap code is a function to avoid leaking many globals
          #code = "(function(){\n\n" + code + "\n\n}).call(this)\n"

          try:
            engine.Execute(code)
          except e as JavaScriptException:
            assert false, e.Message + "\n----------------------\n" + code
          except e as System.Exception:
            assert false, e.ToString() + "\n---------------------\n" + code
          ensure:
            print '--------------------------------------------[output]-'
            print console.output()
            print '-----------------------------------------------------'

          Assert.AreEqual(expected, console.output())

import NUnit.Framework

import System.IO(StreamReader)

import Boo.Lang.Parser
import Boo.Lang.Compiler.Ast
import Boo.Lang.Compiler.IO
import BooJs.Compiler

# The JavaScript interpretter
import Jurassic
import Jurassic.Library

class ConsoleMock(ObjectInstance):
    _output = []

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

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

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

    def reset():
        _output.Clear()

class FixtureRunner:

    static _comp as BooJsCompiler
    static _engine as ScriptEngine

    static def run(file as string):

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

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

      result = comp.Run()
      assert 0 == len(result.Errors), result.Errors.ToString(true) + result.CompileUnit.ToCodeString()

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

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

    static def setupCompiler():
        if not _comp:
            pipeline = Pipelines.ProduceBooJs()
            _comp = newBooJsCompiler(pipeline)
            _comp.Parameters.Debug = true

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

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

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

            # Configure the global environment
            console = ConsoleMock(self._engine)
            self._engine.SetGlobalValue('console', console)

            # Load runtime
            self._engine.ExecuteFile('/Users/drslump/www/boojs/src/Boo.js')

        return self._engine

    static def runTest(code as string, expected as string):
        print '---------------------------------------------[code]-'
        print code

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

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

          # Wrap code is a function to avoid leaking many globals
          #code = "(function(){\n\n" + code + "\n\n}).call(this)\n"

          try:
            engine.Execute(code)
          except e as JavaScriptException:
            assert false, e.Message + "\n----------------------\n" + code
          except e as System.Exception:
            assert false, e.ToString() + "\n---------------------\n" + code
          ensure:
            print '--------------------------------------------[output]-'
            print console.output()
            print '-----------------------------------------------------'

          Assert.AreEqual(expected, console.output())


import console builtins

import console builtins

#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.IO
import System.Text
import Boo.Lang.Useful.CommandLine
import Boo.Lang.Interpreter
import Boo.Lang.PatternMatching

class Program:

	_cmdLine = BooCommandLine()

	def main(argv as (string)):

		error as CommandLineException
		try:
			_cmdLine.Parse(argv)
		except x as CommandLineException:
			error = x

		processing = do:
			if (error is not null
				or _cmdLine.Help
				or not _cmdLine.IsValid):
				print error.Message if error is not null
				usage()
				return -1
			process()

		if _cmdLine.UTF8:
			using writer = StreamWriter(Console.OpenStandardOutput(), Encoding.UTF8):
				Console.SetOut(writer)
				Console.WriteLine()
				return processing()
		else:
			return processing()

	def banner():
		print ResourceManager.GetString("boo.CommandLine.header"), BooVersion
		print

	def usage():
		banner()
		print ResourceManager.GetString("boo.CommandLine.usage")
		_cmdLine.PrintOptions()

	def process():
		banner()
		match _cmdLine.Style:
			case BooCommandLineStyle.InteractiveInterpreter:
				booish()
			case BooCommandLineStyle.Interpreter:
				booi()
			case BooCommandLineStyle.Compiler:
				booc()

	def booc():
		pass

	def booi():
		pass

	def booish():
		print """The following builtin functions are available:
	dir(Type): lists the members of a type
	help(Type): prints detailed information about a type
	load(string): evals an external boo file
	globals(): returns the names of all variables known to the interpreter

Enter boo code in the prompt below."""
		interpreter = InteractiveInterpreterConsole()
		interpreter.Eval("import Boo.Lang.Interpreter.Builtins")
		interpreter.ReadEvalPrintLoop()

[STAThread]
def Main(argv as (string)):
	return Program().main(argv)

#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.IO
import System.Text
import Boo.Lang.Useful.CommandLine
import Boo.Lang.Interpreter
import Boo.Lang.PatternMatching

class Program:

	_cmdLine = BooCommandLine()

	def main(argv as (string)):

		error as CommandLineException
		try:
			_cmdLine.Parse(argv)
		except x as CommandLineException:
			error = x

		processing = do:
			if (error is not null
				or _cmdLine.Help
				or not _cmdLine.IsValid):
				print error.Message if error is not null
				usage()
				return -1
			process()

		if _cmdLine.UTF8:
			using writer = StreamWriter(Console.OpenStandardOutput(), Encoding.UTF8):
				Console.SetOut(writer)
				Console.WriteLine()
				return processing()
		else:
			return processing()

	def banner():
		print ResourceManager.GetString("boo.CommandLine.header"), BooVersion
		print

	def usage():
		banner()
		print ResourceManager.GetString("boo.CommandLine.usage")
		_cmdLine.PrintOptions()

	def process():
		banner()
		match _cmdLine.Style:
			case BooCommandLineStyle.InteractiveInterpreter:
				booish()
			case BooCommandLineStyle.Interpreter:
				booi()
			case BooCommandLineStyle.Compiler:
				booc()

	def booc():
		pass

	def booi():
		pass

	def booish():
		print """The following builtin functions are available:
	dir(Type): lists the members of a type
	help(Type): prints detailed information about a type
	load(string): evals an external boo file
	globals(): returns the names of all variables known to the interpreter

Enter boo code in the prompt below."""
		interpreter = InteractiveInterpreterConsole()
		interpreter.ReadEvalPrintLoop()

[STAThread]
def Main(argv as (string)):
	return Program().main(argv)


print input state on failure too

print input state on failure too

namespace OMetaParserConsole

import System
import Boo.Lang.Compiler.Ast
import Boo.OMeta
import Boo.OMeta.Parser
import Boo.Lang.PatternMatching

def ReadBlock(line as string):
	newLine = System.Environment.NewLine
	buffer = System.Text.StringBuilder()
	buffer.Append(line)
	buffer.Append(newLine)
	while line=prompt("... "):
		break if 0 == len(line)
		buffer.Append(line)
		buffer.Append(newLine)
	return buffer.ToString()

macro WithColor:
	case [| WithColor $color |]:
		yield [|
			Console.ForegroundColor = $color
			$(WithColor.Body)
		|]

def PrintInput(input as OMetaInput):
	print "{0}(IsEmpty: {1}, Position: {2})" % (input.GetType(), input.IsEmpty, input.Position)

promptColor = ConsoleColor.DarkBlue
inputColor = ConsoleColor.Blue
resultColor = ConsoleColor.White
detailsColor = ConsoleColor.DarkGray
failureColor = ConsoleColor.DarkRed
while true:

	WithColor promptColor:
		Console.Write(">>> ")

	WithColor inputColor:
		line = Console.ReadLine()
		if line.EndsWith(":"):
			line = ReadBlock(line)
		if line == "/q":
			break
		line += "\n"

	match BooParser().stmt(line):
		case SuccessfulMatch(Input, Value):
			WithColor resultColor:
				print Value
			WithColor detailsColor:
				match Value:
					case ExpressionStatement(Expression: e):
						print "ExpressionStatement(${e.GetType()})"
					otherwise:
						print Value.GetType()
				PrintInput Input
		case FailedMatch(Input, Failure):
			WithColor failureColor:
				print Failure
				PrintInput Input

namespace OMetaParserConsole

import System
import Boo.Lang.Compiler.Ast
import Boo.OMeta
import Boo.OMeta.Parser
import Boo.Lang.PatternMatching

def ReadBlock(line as string):
	newLine = System.Environment.NewLine
	buffer = System.Text.StringBuilder()
	buffer.Append(line)
	buffer.Append(newLine)
	while line=prompt("... "):
		break if 0 == len(line)
		buffer.Append(line)
		buffer.Append(newLine)
	return buffer.ToString()

macro WithColor:
	case [| WithColor $color |]:
		yield [|
			Console.ForegroundColor = $color
			$(WithColor.Body)
		|]

promptColor = ConsoleColor.DarkBlue
inputColor = ConsoleColor.Blue
resultColor = ConsoleColor.White
detailsColor = ConsoleColor.DarkGray
failureColor = ConsoleColor.DarkRed
while true:

	WithColor promptColor:
		Console.Write(">>> ")

	WithColor inputColor:
		line = Console.ReadLine()
		if line.EndsWith(":"):
			line = ReadBlock(line)
		if line == "/q":
			break
		line += "\n"

	match BooParser().stmt(line):
		case SuccessfulMatch(Input, Value):
			WithColor resultColor:
				print Value
			WithColor detailsColor:
				match Value:
					case ExpressionStatement(Expression: e):
						print "ExpressionStatement(${e.GetType()})"
					otherwise:
						print Value.GetType()
				print "{0}(IsEmpty: {1}, Position: {2})" % (Input.GetType(), Input.IsEmpty, Input.Position)
		case FailedMatch(Failure):
			WithColor failureColor:
				print Failure


minor refactoring in build script

minor refactoring in build script

import System.Environment
import System.IO

import GitSharp

projectName = "NTemplate"

repo as Repository
version as string
slnFile = ".\\src\\${projectName}.sln"
configuration = 'Release'
rootDir = Directory.GetCurrentDirectory()
outDir = Path.Combine(rootDir, "out")
artifactsDir = Path.Combine(outDir, "artifacts")

def getRepo:
	if repo == null:
		repo = Repository(Directory.GetCurrentDirectory())
	return repo

def setVersion:
	majorVersion = File.ReadAllText('version').Trim()
	buildNumber = env('BUILD_NUMBER')
	buildNumber = "0" if (string.IsNullOrEmpty(buildNumber))
	version = majorVersion + "." + buildNumber
	print 'version: ' + version

def reportVersionToTeamCity:
	print "##teamcity[buildNumber '${version}']"

def writeBuildInfo:
	sourceOutFilename = Path.Combine(outDir, 'BuildInfo.txt')
	lines = ["Build info:","===================="]
	lines.Add(" at: " + System.DateTime.UtcNow)
	lines.Add(" branch: " + repo.CurrentBranch.Name)
	lines.Add(" commit: " + repo.Head.CurrentCommit.Hash)
	lines.Add(" source: " + env("BUILD_VCS_URL"))

	File.WriteAllLines(sourceOutFilename, array(string, lines))

def revertAssemblyInfo:
	index = Index(repo)
	fullpath = Path.GetFullPath(Path.Combine(Directory.GetCurrentDirectory(), "src/SolutionAssemblyInfo.cs"))
	index.Checkout(fullpath)

setVersion()
reportVersionToTeamCity()
getRepo()

desc "set build configuration to Debug"
target debug:
	configuration = 'Debug'

desc "initialization"
target init, (generate_assembly_info):
	rmdir(outDir)
	mkdir(outDir)
	print 'initialized'

desc "Generating the shared assemblyInfo with version info"
target generate_assembly_info:
	with generate_assembly_info():
		.file = 'src\\SolutionAssemblyInfo.cs'
		.version = version

desc "entry point for the teamcity build"
target default, (init, build, test, package):
	print 'default target completed'

desc "Build the sources"
target build:
	msbuild(file: slnFile, configuration: configuration)

desc "run unit tests"
target test:
	test_assemblies = (".\\src\\${projectName}.Tests\\bin\\${configuration}\\${projectName}.Tests.dll",)
	nunit(assemblies: test_assemblies, toolPath: ".\\src\\packages\\NUnit.2.5.7.10213\\Tools\\nunit-console.exe")

desc "copy artifacts to output directory"
target package:
	writeBuildInfo()

	for file in Directory.GetFiles(".\\src\\${projectName}\\bin\\${configuration}\\"):
		cp(file, Path.Combine(outDir, Path.GetFileName(file)))

	cp(".\\license.txt", Path.Combine(outDir, "license.txt"))

	zipFile = "${projectName}-"+version+".zip"
	zip(outDir, Path.Combine(outDir, zipFile))
	mkdir(artifactsDir)
	File.Move(Path.Combine(outDir, zipFile), Path.Combine(artifactsDir, zipFile))

	# nuget
	#for file in Directory.GetFiles(outDir):
	#

	for file in Directory.GetFiles(outDir):
		File.Delete(file)

	File.Move(Path.Combine(".\\", "TestResult.xml"), Path.Combine(artifactsDir, "TestResult.xml"))

import System.Environment
import System.IO

import GitSharp

repo as Repository
version as string
slnFile = ".\\src\\ntemplate.sln"
configuration = 'Release'
rootDir = Directory.GetCurrentDirectory()
outDir = Path.Combine(rootDir, "out")
artifactsDir = Path.Combine(outDir, "artifacts")

def getRepo:
	if repo == null:
		repo = Repository(Directory.GetCurrentDirectory())
	return repo

def setVersion:
	majorVersion = File.ReadAllText('version').Trim()
	buildNumber = env('BUILD_NUMBER')
	buildNumber = "0" if (string.IsNullOrEmpty(buildNumber))
	version = majorVersion + "." + buildNumber
	print 'version: ' + version

def reportVersionToTeamCity:
	print "##teamcity[buildNumber '${version}']"

def writeBuildInfo:
	sourceOutFilename = Path.Combine(outDir, 'BuildInfo.txt')
	lines = ["Build info:","===================="]
	lines.Add(" at: " + System.DateTime.UtcNow)
	lines.Add(" branch: " + repo.CurrentBranch.Name)
	lines.Add(" commit: " + repo.Head.CurrentCommit.Hash)
	lines.Add(" source: " + env("BUILD_VCS_URL"))

	File.WriteAllLines(sourceOutFilename, array(string, lines))

def revertAssemblyInfo:
	index = Index(repo)
	fullpath = Path.GetFullPath(Path.Combine(Directory.GetCurrentDirectory(), "src/SolutionAssemblyInfo.cs"))
	index.Checkout(fullpath)

setVersion()
reportVersionToTeamCity()
getRepo()

desc "set build configuration to Debug"
target debug:
	configuration = 'Debug'

desc "initialization"
target init, (generate_assembly_info):
	rmdir(outDir)
	mkdir(outDir)
	print 'initialized'

desc "Generating the shared assemblyInfo with version info"
target generate_assembly_info:
	with generate_assembly_info():
		.file = 'src\\SolutionAssemblyInfo.cs'
		.version = version

desc "entry point for the teamcity build"
target default, (init, build, test, package):
	print 'default target completed'

desc "Build the sources"
target build:
	msbuild(file: slnFile, configuration: configuration)

desc "run unit tests"
target test:
	test_assemblies = (".\\src\\NTemplate.Tests\\bin\\${configuration}\\NTemplate.Tests.dll",)
	nunit(assemblies: test_assemblies, toolPath: ".\\src\\packages\\NUnit.2.5.7.10213\\Tools\\nunit-console.exe")

desc "copy artifacts to output directory"
target package:
	writeBuildInfo()
	cp(".\\src\\NTemplate\\bin\\${configuration}\\NTemplate.dll", Path.Combine(outDir, "NTemplate.dll"))
	cp(".\\src\\NTemplate\\bin\\${configuration}\\NTemplate.pdb", Path.Combine(outDir, "NTemplate.pdb"))
	cp(".\\license.txt", Path.Combine(outDir, "license.txt"))

	zipFile = "NTemplate-"+version+".zip"
	zip(outDir, Path.Combine(outDir, zipFile))
	mkdir(artifactsDir)
	File.Move(Path.Combine(outDir, zipFile), Path.Combine(artifactsDir, zipFile))
	File.Delete(Path.Combine(outDir, "NTemplate.dll"))
	File.Delete(Path.Combine(outDir, "NTemplate.pdb"))
	File.Delete(Path.Combine(outDir, "license.txt"))
	File.Delete(Path.Combine(outDir, "BuildInfo.txt"))
	File.Move(Path.Combine(".\\", "TestResult.xml"), Path.Combine(artifactsDir, "TestResult.xml"))