import only the minimal necessary from NUnit

namespace Boo.Lang.PatternMatching.Tests

import NUnit.Framework(TestFixtureAttribute, TestAttribute, ExpectedExceptionAttribute, Assert)
import Boo.Lang.PatternMatching

class Container[of T]:
	public value as T

class Collection:
	public Items as List

[TestFixture]
class MatchMacroTest:

	[Test]
	def MemberReferenceTarget():
		expected = 42
		actual = singleItemOfContainedList(Container of Collection(value: Collection(Items: [expected])))
		Assert.AreEqual(expected, actual)

	def singleItemOfContainedList(container):
		match container:
			case Container of Collection(value.Items: (item,)):
				return item

	[Test]
	def DoubleMatch():
		Assert.AreEqual("int int", doubleMatch(1, 2))
		Assert.AreEqual("int string", doubleMatch(1, '2'))
		Assert.AreEqual("_ string", doubleMatch(1.0, '2'))

	[Test]
	def DoubleMatchErrorIncludesBothArguments():
		try:
			doubleMatch('2', 42)
			Assert.Fail("MatchError expected")
		except e as MatchError:
			Assert.AreEqual("`a` failed to match `2` or `b` failed to match `42`", e.Message)

	def doubleMatch(a, b):
		match a, b:
			case int(), int():
				return "int int"
			case int(), string():
				return "int string"
			case _, string():
				return "_ string"

	[Test]
	def TestFixedSizeCollection():
		Assert.AreEqual(1, lastItem(Collection(Items: [1])))
		Assert.AreEqual(2, lastItem(Collection(Items: [1, 2])))

	[Test]
	def TestNestedFixedSize():
		c = Collection(Items: [Collection(Items: [1, 2])])
		Assert.AreEqual(2, nestedLastItem(c))

	[Test]
	def TestFixedSizeCollectionTyped():
		Assert.AreEqual(1, lastIntItem(Collection(Items: [1])))
		Assert.AreEqual(2, lastIntItem(Collection(Items: [1, 2])))

	[Test]
	def TestFixedSizeCollectionLastItemBeforeCatchAll():
		Assert.AreEqual(1, lastIntItemBeforeCatchAll(Collection(Items: [1, "_", "__"])))
		Assert.AreEqual(2, lastIntItemBeforeCatchAll(Collection(Items: [1, 2, "_", "__"])))

	[Test]
	[ExpectedException(MatchError)]
	def TestMatchErrorOnFixedSize():
		lastItem(Collection(Items: [1, 2, 3]))

	[Test]
	def TestGenericMatch():
		Assert.AreEqual(42, genericMatch(Container of int(value: 21)))
		Assert.AreEqual("BOO", genericMatch(Container of string(value: 'boo')))

	[Test]
	def TestPropertyPattern():
		Assert.AreEqual("item foo", itemByName(Item(Name: "foo")))
		Assert.AreEqual("not foo", itemByName(Item(Name: "not foo")))

	[Test]
	def TestNestedPropertyPattern():
		Assert.AreEqual("foo:bar", nestedByName(
								Item(Name: "foo",
									Child: Item(Name: "bar"))))

	[Test]
	def TestStringValue():
		for s in ("foo", "bar"):
			Assert.AreEqual("*${s}*", matchStringValue(s))

	[Test]
	def TestQualifiedReference():
		Assert.AreEqual("default item", itemByQualifiedReference(Item.Default))
		Assert.AreEqual("foo", itemByQualifiedReference(Item(Name: "foo")))

	[Test]
	def TestImplicitPropertyPattern():
		Assert.AreEqual("FOO", itemByImplicitNameReference(Item(Name: "foo")))

	[Test]
	[ExpectedException(MatchError)]
	def TestMatchErrorOnPropertyPattern():
		itemByName(42)

	[Test]
	def TestMatchErrorMessageIncludesValue():
		try:
			itemByName(42)
		except e as MatchError:
			Assert.AreEqual("`o` failed to match `42`", e.Message)

	[Test]
	[ExpectedException(MatchError)]
	def TestMatchErrorOnNestedPropertyPattern():
		nestedByName(42)

	[Test]
	def TestCaseCapture():
		Assert.AreEqual("foo", caseCapture(Item(Name: "foo")))
		Assert.AreEqual(42, caseCapture(21))

	[Test]
	def TestOtherwise():
		Assert.AreEqual("int", matchIntOtherwise(42))
		Assert.AreEqual("otherwise", matchIntOtherwise("42"))

	[Test]
	def TestEitherPattern():
		Assert.AreEqual("yes", either(42))
		Assert.AreEqual("yes", either(-1))
		Assert.AreEqual("no", either(0))

	def either(value as int):
		match value:
			case 42 | -1:
				return "yes"
			otherwise:
				return "no"

	[Test]
	def TestConstrainedOrPattern():
		Assert.AreEqual("yes", constrainedOr(42, 2))
		Assert.AreEqual("yes", constrainedOr(-1, 2))
		Assert.AreEqual("yes", constrainedOr(42, 42))
		Assert.AreEqual("no", constrainedOr(-1, 42))

	def constrainedOr(value1 as int, value2 as int):
		match value1:
			case 42 or value2 < 23:
				return "yes"
			otherwise:
				return "no"

	[Test]
	def TestConstrainedAndPattern():
		Assert.AreEqual("yes", constrainedAnd(42, 2))
		Assert.AreEqual("no", constrainedAnd(-1, 2))
		Assert.AreEqual("no", constrainedAnd(42, 42))
		Assert.AreEqual("no", constrainedAnd(-1, 42))

	def constrainedAnd(value1 as int, value2 as int):
		match value1:
			case 42 and value2 < 23:
				return "yes"
			otherwise:
				return "no"

	[Test]
	def TestBothPattern():
		Assert.AreEqual("yes", both(Item(Name: "foo")))
		Assert.AreEqual("yes", both(Item(Name: "foo", Child: null)))
		Assert.AreEqual("no", both(Item(Name: "foo", Child: Item(Name: "bar"))))
		Assert.AreEqual("no", both(Item(Name: "bar", Child: null)))

	def both(value as Item):
		match value:
			case Item(Child: null) & Item(Name: "foo"):
				return "yes"
			otherwise:
				return "no"

	enum Foo:
		None
		Bar
		Baz

	[Test]
	def TestMemberReferenceIsTreatedAsValueComparison():
		Assert.AreEqual("Bar", matchMemberRef(Foo.Bar))
		Assert.AreEqual("Baz", matchMemberRef(Foo.Baz))

	[Test]
	[ExpectedException(MatchError)]
	def TestMatchErrorOnMemberReferencePattern():
		matchMemberRef(Foo.None)

	def itemByName(o):
		match o:
			case Item(Name: "foo"):
				return "item foo"
			case Item(Name: name):
				return name

	def nestedByName(o):
		match o:
			case Item(Name: outer, Child: Item(Name: inner)):
				return "${outer}:${inner}"

	def itemByQualifiedReference(o):
		match o:
			case Item(Name: Item.Default.Name):
				return "default item"
			case Item(Name: name):
				return name

	def itemByImplicitNameReference(o):
		match o:
			case Item(Name):
				return Name.ToUpper()

	def caseCapture(o):
		match o:
			case i = int():
				return i*2
			case item = Item():
				return item.Name

	def matchMemberRef(o as Foo):
		match o:
			case Foo.Bar:
				return "Bar"
			case Foo.Baz:
				return "Baz"

	def matchIntOtherwise(o):
		match o:
			case int():
				return "int"
			otherwise:
				return "otherwise"

	def matchStringValue(o):
		match o:
			case "foo":
				return "*foo*"
			case "bar":
				return "*bar*"

	def genericMatch(o):
		match o:
			case Container[of int](value: i):
				return i*2
			case Container[of string](value: s):
				return s.ToUpper()

	def lastItem(o):
		match o:
			case Collection(Items: (last,)):
				return last
			case Collection(Items: (_, last)):
				return last

	def nestedLastItem(o):
		match o:
			case Collection(Items: (Collection(Items: (_, last)),)):
				return last

	def lastIntItem(o):
		match o:
			case Collection(Items: (last = int(),)):
				return last
			case Collection(Items: (_ = int(), last = int(),)):
				return last

	def lastIntItemBeforeCatchAll(o):
		match o:
			case Collection(Items: (_ = int(), last = int(), *_)):
				return last
			case Collection(Items: (last = int(), *_)):
				return last
		return -1

namespace Boo.Lang.PatternMatching.Tests

import NUnit.Framework
import Boo.Lang.PatternMatching

class Container[of T]:
	public value as T

class Collection:
	public Items as List

[TestFixture]
class MatchMacroTest:

	[Test]
	def MemberReferenceTarget():
		expected = 42
		actual = singleItemOfContainedList(Container of Collection(value: Collection(Items: [expected])))
		Assert.AreEqual(expected, actual)

	def singleItemOfContainedList(container):
		match container:
			case Container of Collection(value.Items: (item,)):
				return item

	[Test]
	def DoubleMatch():
		Assert.AreEqual("int int", doubleMatch(1, 2))
		Assert.AreEqual("int string", doubleMatch(1, '2'))
		Assert.AreEqual("_ string", doubleMatch(1.0, '2'))

	[Test]
	def DoubleMatchErrorIncludesBothArguments():
		try:
			doubleMatch('2', 42)
			Assert.Fail("MatchError expected")
		except e as MatchError:
			Assert.AreEqual("`a` failed to match `2` or `b` failed to match `42`", e.Message)

	def doubleMatch(a, b):
		match a, b:
			case int(), int():
				return "int int"
			case int(), string():
				return "int string"
			case _, string():
				return "_ string"

	[Test]
	def TestFixedSizeCollection():
		Assert.AreEqual(1, lastItem(Collection(Items: [1])))
		Assert.AreEqual(2, lastItem(Collection(Items: [1, 2])))

	[Test]
	def TestNestedFixedSize():
		c = Collection(Items: [Collection(Items: [1, 2])])
		Assert.AreEqual(2, nestedLastItem(c))

	[Test]
	def TestFixedSizeCollectionTyped():
		Assert.AreEqual(1, lastIntItem(Collection(Items: [1])))
		Assert.AreEqual(2, lastIntItem(Collection(Items: [1, 2])))

	[Test]
	def TestFixedSizeCollectionLastItemBeforeCatchAll():
		Assert.AreEqual(1, lastIntItemBeforeCatchAll(Collection(Items: [1, "_", "__"])))
		Assert.AreEqual(2, lastIntItemBeforeCatchAll(Collection(Items: [1, 2, "_", "__"])))

	[Test]
	[ExpectedException(MatchError)]
	def TestMatchErrorOnFixedSize():
		lastItem(Collection(Items: [1, 2, 3]))

	[Test]
	def TestGenericMatch():
		Assert.AreEqual(42, genericMatch(Container of int(value: 21)))
		Assert.AreEqual("BOO", genericMatch(Container of string(value: 'boo')))

	[Test]
	def TestPropertyPattern():
		Assert.AreEqual("item foo", itemByName(Item(Name: "foo")))
		Assert.AreEqual("not foo", itemByName(Item(Name: "not foo")))

	[Test]
	def TestNestedPropertyPattern():
		Assert.AreEqual("foo:bar", nestedByName(
								Item(Name: "foo",
									Child: Item(Name: "bar"))))

	[Test]
	def TestStringValue():
		for s in ("foo", "bar"):
			Assert.AreEqual("*${s}*", matchStringValue(s))

	[Test]
	def TestQualifiedReference():
		Assert.AreEqual("default item", itemByQualifiedReference(Item.Default))
		Assert.AreEqual("foo", itemByQualifiedReference(Item(Name: "foo")))

	[Test]
	def TestImplicitPropertyPattern():
		Assert.AreEqual("FOO", itemByImplicitNameReference(Item(Name: "foo")))

	[Test]
	[ExpectedException(MatchError)]
	def TestMatchErrorOnPropertyPattern():
		itemByName(42)

	[Test]
	def TestMatchErrorMessageIncludesValue():
		try:
			itemByName(42)
		except e as MatchError:
			Assert.AreEqual("`o` failed to match `42`", e.Message)

	[Test]
	[ExpectedException(MatchError)]
	def TestMatchErrorOnNestedPropertyPattern():
		nestedByName(42)

	[Test]
	def TestCaseCapture():
		Assert.AreEqual("foo", caseCapture(Item(Name: "foo")))
		Assert.AreEqual(42, caseCapture(21))

	[Test]
	def TestOtherwise():
		Assert.AreEqual("int", matchIntOtherwise(42))
		Assert.AreEqual("otherwise", matchIntOtherwise("42"))

	[Test]
	def TestEitherPattern():
		Assert.AreEqual("yes", either(42))
		Assert.AreEqual("yes", either(-1))
		Assert.AreEqual("no", either(0))

	def either(value as int):
		match value:
			case 42 | -1:
				return "yes"
			otherwise:
				return "no"

	[Test]
	def TestConstrainedOrPattern():
		Assert.AreEqual("yes", constrainedOr(42, 2))
		Assert.AreEqual("yes", constrainedOr(-1, 2))
		Assert.AreEqual("yes", constrainedOr(42, 42))
		Assert.AreEqual("no", constrainedOr(-1, 42))

	def constrainedOr(value1 as int, value2 as int):
		match value1:
			case 42 or value2 < 23:
				return "yes"
			otherwise:
				return "no"

	[Test]
	def TestConstrainedAndPattern():
		Assert.AreEqual("yes", constrainedAnd(42, 2))
		Assert.AreEqual("no", constrainedAnd(-1, 2))
		Assert.AreEqual("no", constrainedAnd(42, 42))
		Assert.AreEqual("no", constrainedAnd(-1, 42))

	def constrainedAnd(value1 as int, value2 as int):
		match value1:
			case 42 and value2 < 23:
				return "yes"
			otherwise:
				return "no"

	[Test]
	def TestBothPattern():
		Assert.AreEqual("yes", both(Item(Name: "foo")))
		Assert.AreEqual("yes", both(Item(Name: "foo", Child: null)))
		Assert.AreEqual("no", both(Item(Name: "foo", Child: Item(Name: "bar"))))
		Assert.AreEqual("no", both(Item(Name: "bar", Child: null)))

	def both(value as Item):
		match value:
			case Item(Child: null) & Item(Name: "foo"):
				return "yes"
			otherwise:
				return "no"

	enum Foo:
		None
		Bar
		Baz

	[Test]
	def TestMemberReferenceIsTreatedAsValueComparison():
		Assert.AreEqual("Bar", matchMemberRef(Foo.Bar))
		Assert.AreEqual("Baz", matchMemberRef(Foo.Baz))

	[Test]
	[ExpectedException(MatchError)]
	def TestMatchErrorOnMemberReferencePattern():
		matchMemberRef(Foo.None)

	def itemByName(o):
		match o:
			case Item(Name: "foo"):
				return "item foo"
			case Item(Name: name):
				return name

	def nestedByName(o):
		match o:
			case Item(Name: outer, Child: Item(Name: inner)):
				return "${outer}:${inner}"

	def itemByQualifiedReference(o):
		match o:
			case Item(Name: Item.Default.Name):
				return "default item"
			case Item(Name: name):
				return name

	def itemByImplicitNameReference(o):
		match o:
			case Item(Name):
				return Name.ToUpper()

	def caseCapture(o):
		match o:
			case i = int():
				return i*2
			case item = Item():
				return item.Name

	def matchMemberRef(o as Foo):
		match o:
			case Foo.Bar:
				return "Bar"
			case Foo.Baz:
				return "Baz"

	def matchIntOtherwise(o):
		match o:
			case int():
				return "int"
			otherwise:
				return "otherwise"

	def matchStringValue(o):
		match o:
			case "foo":
				return "*foo*"
			case "bar":
				return "*bar*"

	def genericMatch(o):
		match o:
			case Container[of int](value: i):
				return i*2
			case Container[of string](value: s):
				return s.ToUpper()

	def lastItem(o):
		match o:
			case Collection(Items: (last,)):
				return last
			case Collection(Items: (_, last)):
				return last

	def nestedLastItem(o):
		match o:
			case Collection(Items: (Collection(Items: (_, last)),)):
				return last

	def lastIntItem(o):
		match o:
			case Collection(Items: (last = int(),)):
				return last
			case Collection(Items: (_ = int(), last = int(),)):
				return last

	def lastIntItemBeforeCatchAll(o):
		match o:
			case Collection(Items: (_ = int(), last = int(), *_)):
				return last
			case Collection(Items: (last = int(), *_)):
				return last
		return -1


remove comma from codedom array test

remove comma from codedom array test

git-svn-id: fcce7d6ce0ac15bf24f68871b5145d46fc068279@2070 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.CodeDom.Tests

import System
import System.CodeDom
import System.CodeDom.Compiler
import System.IO
import NUnit.Framework
import Boo.Lang.CodeDom

[TestFixture]
class CodeGeneratorTestFixture:

	_generator as ICodeGenerator

	[SetUp]
	def SetUp():
		_generator = BooCodeProvider().CreateGenerator()
		Assert.IsNotNull(_generator)

	[Test]
	def TestArrayType():
		stmt = CodeVariableDeclarationStatement()
		stmt.Name = "anArray"
		stmt.Type = CodeTypeReference(typeof((int)))

		expected = "anArray as (int)"

		buffer = StringWriter()
		_generator.GenerateCodeFromStatement(stmt, buffer, CodeGeneratorOptions())
		Assert.AreEqual(expected, buffer.ToString().Trim())

	[Test]
	def TestArrayCreateSingle():
		e = CodeArrayCreateExpression(CodeTypeReference(int), *(CodePrimitiveExpression(2),))

		expected = "(of int: 2)"

		buffer = StringWriter()
		_generator.GenerateCodeFromExpression(e, buffer, CodeGeneratorOptions())
		Assert.AreEqual(expected, buffer.ToString().Trim())

	[Test]
	def TestArrayCreateMultiple():
		e = CodeArrayCreateExpression(CodeTypeReference(int), CodePrimitiveExpression(2),
			CodePrimitiveExpression(3), CodePrimitiveExpression(4))

		expected = "(of int: 2, 3, 4)"

		buffer = StringWriter()
		_generator.GenerateCodeFromExpression(e, buffer, CodeGeneratorOptions())
		Assert.AreEqual(expected, buffer.ToString().Trim())

	[Test]
	def TestFixIndent1():
		//1. code is indented one tab, but needs to be indented 8 spaces.
		//2. also, there are comments before the code that should be ignored
		//3. also, stuff inside triple quoted strings should not be altered
		//3. extra leading whitespace converted to spaces as well,
		//   so tabs and spaces aren't mixed on the same line
		code = """
	//asdf
	   #asgdawg
/*
	 Inline boo code
	 	/*is
		supported:
		*/
	 */
	def Calendar1Selected():
		Label1.Text = ("""
		code+='"""'
		code+="""Boo for .NET
	says
		you picked """
		code+='"""'
		code+=""" + Calendar1.SelectedDate.ToString('D'))

	def Button1Click():
		Calendar1.VisibleDate = System.Convert.ToDateTime(Edit1.Text)
		Label1.Text = 'Boo for .NET says you set ' + Calendar1.VisibleDate.ToString('D')

"""

		expected = """
        	//asdf
        	   #asgdawg
        /*
        	 Inline boo code
        	 	/*is
        		supported:
        		*/
        	 */
        def Calendar1Selected():
            Label1.Text = ("""
		expected+='"""'
		expected+="""Boo for .NET
	says
		you picked """
		expected+='"""'
		expected+=""" + Calendar1.SelectedDate.ToString('D'))

        def Button1Click():
            Calendar1.VisibleDate = System.Convert.ToDateTime(Edit1.Text)
            Label1.Text = 'Boo for .NET says you set ' + Calendar1.VisibleDate.ToString('D')

"""

		result = Boo.Lang.CodeDom.BooCodeGenerator.FixIndent(code, "    ", 2, false)
		result = result.Replace("\r\n","\n").Trim()
		expected = expected.Replace("\r\n","\n").Trim()
		Assert.AreEqual(expected, result)

#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.CodeDom.Tests

import System
import System.CodeDom
import System.CodeDom.Compiler
import System.IO
import NUnit.Framework
import Boo.Lang.CodeDom

[TestFixture]
class CodeGeneratorTestFixture:

	_generator as ICodeGenerator

	[SetUp]
	def SetUp():
		_generator = BooCodeProvider().CreateGenerator()
		Assert.IsNotNull(_generator)

	[Test]
	def TestArrayType():
		stmt = CodeVariableDeclarationStatement()
		stmt.Name = "anArray"
		stmt.Type = CodeTypeReference(typeof((int)))

		expected = "anArray as (int)"

		buffer = StringWriter()
		_generator.GenerateCodeFromStatement(stmt, buffer, CodeGeneratorOptions())
		Assert.AreEqual(expected, buffer.ToString().Trim())

	[Test]
	def TestArrayCreateSingle():
		e = CodeArrayCreateExpression(CodeTypeReference(int), *(CodePrimitiveExpression(2),))

		expected = "(of int: 2,)"

		buffer = StringWriter()
		_generator.GenerateCodeFromExpression(e, buffer, CodeGeneratorOptions())
		Assert.AreEqual(expected, buffer.ToString().Trim())

	[Test]
	def TestArrayCreateMultiple():
		e = CodeArrayCreateExpression(CodeTypeReference(int), CodePrimitiveExpression(2),
			CodePrimitiveExpression(3), CodePrimitiveExpression(4))

		expected = "(of int: 2, 3, 4)"

		buffer = StringWriter()
		_generator.GenerateCodeFromExpression(e, buffer, CodeGeneratorOptions())
		Assert.AreEqual(expected, buffer.ToString().Trim())

	[Test]
	def TestFixIndent1():
		//1. code is indented one tab, but needs to be indented 8 spaces.
		//2. also, there are comments before the code that should be ignored
		//3. also, stuff inside triple quoted strings should not be altered
		//3. extra leading whitespace converted to spaces as well,
		//   so tabs and spaces aren't mixed on the same line
		code = """
	//asdf
	   #asgdawg
/*
	 Inline boo code
	 	/*is
		supported:
		*/
	 */
	def Calendar1Selected():
		Label1.Text = ("""
		code+='"""'
		code+="""Boo for .NET
	says
		you picked """
		code+='"""'
		code+=""" + Calendar1.SelectedDate.ToString('D'))

	def Button1Click():
		Calendar1.VisibleDate = System.Convert.ToDateTime(Edit1.Text)
		Label1.Text = 'Boo for .NET says you set ' + Calendar1.VisibleDate.ToString('D')

"""

		expected = """
        	//asdf
        	   #asgdawg
        /*
        	 Inline boo code
        	 	/*is
        		supported:
        		*/
        	 */
        def Calendar1Selected():
            Label1.Text = ("""
		expected+='"""'
		expected+="""Boo for .NET
	says
		you picked """
		expected+='"""'
		expected+=""" + Calendar1.SelectedDate.ToString('D'))

        def Button1Click():
            Calendar1.VisibleDate = System.Convert.ToDateTime(Edit1.Text)
            Label1.Text = 'Boo for .NET says you set ' + Calendar1.VisibleDate.ToString('D')

"""

		result = Boo.Lang.CodeDom.BooCodeGenerator.FixIndent(code, "    ", 2, false)
		result = result.Replace("\r\n","\n").Trim()
		expected = expected.Replace("\r\n","\n").Trim()
		Assert.AreEqual(expected, result)


operator overloading for Array

operator overloading for Array

namespace BooJs.Lang.Globals

import BooJs.Lang.Extensions

#import System.Collections(IList, IEnumerable, ICollection)

import System.Collections(IList, IEnumerable, IEnumerator)
import System.Collections.Generic(IList, IEnumerable, IEnumerator)
import System(Func)

class Array[of T] (Object, IList[of T]): #, IList):

    [Transform( Boo.Array.op_Equality($1, $2) )]
    static def op_Equality(lhs as Array[of T], rhs as Array[of T]) as bool:
        pass
    [Transform( Boo.Array.op_Member($1, $2) )]
    static def op_Member(lhs as Array[of T], rhs as object) as bool:
        pass
    [Transform( not Boo.Array.op_Member($1, $2) )]
    static def op_NotMember(lhs as Array[of T], rhs as object) as bool:
        pass
    [Transform( Boo.Array.op_Addition($1, $2) )]
    static def op_Addition(lhs as Array[of T], rhs as Array[of T]) as Array[of T]:
        pass
    [Transform( Boo.Array.op_Multiply($1, $2) )]
    static def op_Multiply(lhs as Array[of T], rhs as int) as Array[of T]:
        pass

    # Allow assignment from arrays
    static def op_Implicit(rhs as (T)) as Array[of T]:
        pass

    # Implement enumerable interfaces that conflicts
    def IEnumerable.GetEnumerator() as IEnumerator:
        pass
    def GetEnumerator() as IEnumerator[of T]:
        pass

    # Indexer
    self[index as int] as T:
        [Transform( $0[$1] )]
        get: pass
        [Transform( $0[$1] = $2 )]
        set: pass

    /*
    self[index as int] as object:
        get: pass
        set: pass
    */

    public length as uint

    def constructor():
        pass
    def constructor(n as int):
        pass

    # HACK: Emulate multiple params in a Javascript compatible way (up to 3 elements)
    def push(itm as T) as uint:
        pass
    def push(itm1 as T, itm2 as T) as uint:
        pass
    def push(itm1 as T, itm2 as T, itm3 as T) as uint:
        pass

    def pop() as T:
        pass

    def reverse() as Array[of T]:
        pass

    def shift() as T:
        pass

    def sort() as Array[of T]:
        pass
    def sort(comp as callable) as Array[of T]:
        pass

    def splice(index as int, cnt as int, *elems as (T)) as Array[of T]:
        pass
    def splice(index as int, cnt as int) as Array[of T]:
        pass
    def splice(index as int) as Array[of T]:
        pass

    # HACK: Emulate multiple params in a Javascript compatible way (up to 3 elements)
    def unshift(itm1 as T) as uint:
        pass
    def unshift(itm1 as T, itm2 as T) as uint:
        pass
    def unshift(itm1 as T, itm2 as T, itm3 as T) as uint:
        pass

    # HACK: Emulate multiple params in a Javascript compatible way (up to 3 elements)
    def concat(itm1 as Array[of T]) as Array[of T]:
        pass
    def concat(itm1 as Array[of T], itm2 as Array[of T]) as Array[of T]:
        pass
    def concat(itm1 as Array[of T], itm2 as Array[of T], itm3 as Array[of T]) as Array[of T]:
        pass

    def join(sep as string) as string:
        pass

    def slice(start as int, stop as int) as Array[of T]:
        pass

    def slice(start as int) as Array[of T]:
        pass

    def indexOf(itm as T, start as int) as int:
        pass

    def indexOf(itm as T) as int:
        pass

    def lastIndexOf(itm as T, start as int) as int:
        pass

    def lastIndexOf(itm as T) as int:
        pass

    def filter(callback as callable, context as object) as Array[of T]:
        pass
    def filter(callback as callable) as Array[of T]:
        pass

    #def forEach(callback as callable(T), context as object) as void:
    def forEach(callback as Func[of T], context as object) as void:

        pass
    def forEach(callback as callable) as void:
        pass

    def every(callback as callable, context as object) as bool:
        pass
    def every(callback as callable) as bool:
        pass

    def map(callback as callable, context as object) as Array[of T]:
        pass
    def map(callback as callable) as Array[of T]:
        pass

    def some(callback as callable, context as object) as bool:
        pass
    def some(callback as callable) as bool:
        pass

    def reduce(callback as callable, initialValue as object) as object:
        pass
    def reduce(callback as callable) as object:
        pass

    def reduceRight(callback as callable, initialValue as object) as object:
        pass
    def reduceRight(callback as callable) as object:
        pass

    # Ecma 5th edition

    static def isArray(arg as object) as bool:
        pass

class Array(Array[of object]):
    [Transform( Boo.Array.op_Equality($1, $2) )]
    static def op_Equality(lhs as Array, rhs as Array) as bool:
        pass
    [Transform( Boo.Array.op_Member($1, $2) )]
    static def op_Member(lhs as object, rhs as Array) as bool:
        pass
    [Transform( not Boo.Array.op_Member($1, $2) )]
    static def op_NotMember(lhs as object, rhs as Array) as bool:
        pass
    [Transform( Boo.Array.op_Addition($1, $2) )]
    static def op_Addition(lhs as Array, rhs as Array) as Array:
        pass
    [Transform( Boo.Array.op_Multiply($1, $2) )]
    static def op_Multiply(lhs as Array, rhs as int) as Array:
        pass

    def constructor():
        pass
    def constructor(n as int):
        pass

namespace BooJs.Lang.Globals

import BooJs.Lang.Extensions

#import System.Collections(IList, IEnumerable, ICollection)

import System.Collections(IList, IEnumerable, IEnumerator)
import System.Collections.Generic(IList, IEnumerable, IEnumerator)
import System(Func)

class Array[of T] (Object, IList[of T]): #, IList):

    [Transform( Boo.Array.op_Equality($1, $2) )]
    static def op_Equality(lhs as Array[of T], rhs as Array[of T]) as bool:
        pass
    [Transform( Boo.Array.op_Member($1, $2) )]
    static def op_Member(lhs as Array[of T], rhs as object) as bool:
        pass
    [Transform( not Boo.Array.op_Member($1, $2) )]
    static def op_NotMember(lhs as Array[of T], rhs as object) as bool:
        pass
    [Transform( Boo.Array.op_Addition($1, $2) )]
    static def op_Addition(lhs as Array[of T], rhs as Array[of T]) as Array[of T]:
        pass
    [Transform( Boo.Array.op_Multiply($1, $2) )]
    static def op_Multiply(lhs as Array[of T], rhs as int) as Array[of T]:
        pass

    # Allow assignment from arrays
    static def op_Implicit(rhs as (T)) as Array[of T]:
        pass

    # Implement enumerable interfaces that conflicts
    def IEnumerable.GetEnumerator() as IEnumerator:
        pass
    def GetEnumerator() as IEnumerator[of T]:
        pass

    # Indexer
    self[index as int] as T:
        [Transform( $0[$1] )]
        get: pass
        [Transform( $0[$1] = $2 )]
        set: pass

    /*
    self[index as int] as object:
        get: pass
        set: pass
    */

    public length as uint

    def constructor():
        pass
    def constructor(n as int):
        pass

    # HACK: Emulate multiple params in a Javascript compatible way (up to 3 elements)
    def push(itm as T) as uint:
        pass
    def push(itm1 as T, itm2 as T) as uint:
        pass
    def push(itm1 as T, itm2 as T, itm3 as T) as uint:
        pass

    def pop() as T:
        pass

    def reverse() as Array[of T]:
        pass

    def shift() as T:
        pass

    def sort() as Array[of T]:
        pass
    def sort(comp as callable) as Array[of T]:
        pass

    def splice(index as int, cnt as int, *elems as (T)) as Array[of T]:
        pass
    def splice(index as int, cnt as int) as Array[of T]:
        pass
    def splice(index as int) as Array[of T]:
        pass

    # HACK: Emulate multiple params in a Javascript compatible way (up to 3 elements)
    def unshift(itm1 as T) as uint:
        pass
    def unshift(itm1 as T, itm2 as T) as uint:
        pass
    def unshift(itm1 as T, itm2 as T, itm3 as T) as uint:
        pass

    # HACK: Emulate multiple params in a Javascript compatible way (up to 3 elements)
    def concat(itm1 as Array[of T]) as Array[of T]:
        pass
    def concat(itm1 as Array[of T], itm2 as Array[of T]) as Array[of T]:
        pass
    def concat(itm1 as Array[of T], itm2 as Array[of T], itm3 as Array[of T]) as Array[of T]:
        pass

    def join(sep as string) as string:
        pass

    def slice(start as int, stop as int) as Array[of T]:
        pass

    def slice(start as int) as Array[of T]:
        pass

    def indexOf(itm as T, start as int) as int:
        pass

    def indexOf(itm as T) as int:
        pass

    def lastIndexOf(itm as T, start as int) as int:
        pass

    def lastIndexOf(itm as T) as int:
        pass

    def filter(callback as callable, context as object) as Array[of T]:
        pass
    def filter(callback as callable) as Array[of T]:
        pass

    #def forEach(callback as callable(T), context as object) as void:
    def forEach(callback as Func[of T], context as object) as void:

        pass
    def forEach(callback as callable) as void:
        pass

    def every(callback as callable, context as object) as bool:
        pass
    def every(callback as callable) as bool:
        pass

    def map(callback as callable, context as object) as Array[of T]:
        pass
    def map(callback as callable) as Array[of T]:
        pass

    def some(callback as callable, context as object) as bool:
        pass
    def some(callback as callable) as bool:
        pass

    def reduce(callback as callable, initialValue as object) as object:
        pass
    def reduce(callback as callable) as object:
        pass

    def reduceRight(callback as callable, initialValue as object) as object:
        pass
    def reduceRight(callback as callable) as object:
        pass

    # Ecma 5th edition

    static def isArray(arg as object) as bool:
        pass

class Array(Array[of object]):
    def constructor():
        pass
    def constructor(n as int):
        pass


Hash now implements IQuackFu to make it more flexible

namespace BooJs.Lang

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

class Builtins:

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

    static public final STOP = 'STOP'

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

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

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

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

    [EnumeratorItemType(typeof(Object))]
    class Hash(Object, Iterable, IQuackFu):  # The IQuackFoo allows to access items as members (dot notation)
    """ Simple hash/dictionary based on Javascript's object
    """
        [Transform( $2.hasOwnProperty($1) )]
        static def op_Member(lhs as object, rhs as Hash) as bool:
            pass
        [Transform( not $2.hasOwnProperty($1) )]
        static def op_NotMember(lhs as object, rhs as Hash) as bool:
            pass

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

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

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

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

    class ReturnValue:
        public value as object

        def constructor(val as object):
            value = val

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

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

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

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

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

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

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

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

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

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

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

namespace BooJs.Lang

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

class Builtins:

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

    static public final STOP = 'STOP'

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

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

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

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

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

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

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

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

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

    class ReturnValue:
        public value as object

        def constructor(val as object):
            value = val

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

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

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

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

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

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

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

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

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

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

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


That logo atop Booish at startup can be annoying.  Added the --nologo option to disable it.

git-svn-id: fcce7d6ce0ac15bf24f68871b5145d46fc068279@2945 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.Diagnostics
import Boo.Lang.Compiler.Steps
import Boo.Lang.Interpreter

interpreter = InteractiveInterpreter2(RememberLastValue: true)

loadRequests = System.Collections.Generic.List[of string]()

for arg in argv:
	if arg == "--print-modules" or arg == "-print-modules":
		interpreter.Pipeline.Add(PrintBoo())
	if arg == "--debug" or arg == "-debug":
		Debug.Listeners.Add(TextWriterTraceListener(Console.Out))
	if arg == "-w":
		interpreter.ShowWarnings = true
	if arg.StartsWith("-r:"):
		loadRequests.Add(arg.Substring(3))
	if arg == "--nologo" or arg == "-nologo":
	    nologo = true
	if not arg.StartsWith("-"):
		loadRequests.Add(arg)

interpreter.DisplayHelp() unless nologo

for req in loadRequests:
	interpreter.load(req)

interpreter.ConsoleLoopEval()

#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.Diagnostics
import Boo.Lang.Compiler.Steps
import Boo.Lang.Interpreter

interpreter = InteractiveInterpreter2(RememberLastValue: true)

loadRequests = System.Collections.Generic.List[of string]()

for arg in argv:
	if arg == "--print-modules" or arg == "-print-modules":
		interpreter.Pipeline.Add(PrintBoo())
	if arg == "--debug" or arg == "-debug":
		Debug.Listeners.Add(TextWriterTraceListener(Console.Out))
	if arg == "-w":
		interpreter.ShowWarnings = true
	if arg.StartsWith("-r:"):
		loadRequests.Add(arg.Substring(3))
	if not arg.StartsWith("-"):
		loadRequests.Add(arg)

interpreter.DisplayHelp()

for req in loadRequests:
	interpreter.load(req)

interpreter.ConsoleLoopEval()


better support for command line args

better support for command line args

git-svn-id: fcce7d6ce0ac15bf24f68871b5145d46fc068279@1942 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.IO
import System.Reflection
import System.Security.Permissions
import System.Threading
import Boo.Lang.Compiler
import Boo.Lang.Compiler.IO
import Boo.Lang.Compiler.Pipelines
import Boo.Lang.Compiler.Steps

class AssemblyResolver:

	_cache = {}

	def AddAssembly([required] asm as Assembly):
		_cache[GetSimpleName(asm.FullName)] = asm

	def LoadAssembly([required] name as string):
		asm = ProbeFile(name)
		if asm is not null:
			_cache[asm.GetName().Name] = asm
		return asm

	def AssemblyResolve(sender, args as ResolveEventArgs) as Assembly:
		simpleName = GetSimpleName(args.Name)

		asm as Assembly = _cache[simpleName]
		if asm is null:
			basePath = Path.GetFullPath(simpleName)
			asm = ProbeFile(basePath + ".dll")
			if asm is null:
				asm = ProbeFile(basePath + ".exe")
			_cache[simpleName] = asm

		return asm

	private def GetSimpleName(name as string):
		return /,\s*/.Split(name)[0]

	private def ProbeFile(fname as string):
		return Assembly.LoadFrom(fname) if File.Exists(fname)

def consume(reader as TextReader):
	return join(line for line in reader, "\n")

[STAThread]
def Main(argv as (string)):

	if len(argv) < 1:
		print("booi <script.boo>")
		return -1

	compiler = BooCompiler()
	compiler.Parameters.Pipeline = CompileToMemory()

	resolver = AssemblyResolver()
	AppDomain.CurrentDomain.AssemblyResolve += resolver.AssemblyResolve

	consumedArgs = 1
	for arg in argv:
		if "-" == arg:
			compiler.Parameters.Input.Add(StringInput("<stdin>", consume(Console.In)))
			break
		elif "-ducky" == arg:
			compiler.Parameters.Ducky = true
		elif "-w" == arg:
			printWarnings = true
		elif arg.StartsWith("-r:"):
			compiler.Parameters.References.Add(resolver.LoadAssembly(arg[3:]))
		else:
			compiler.Parameters.Input.Add(FileInput(arg))
			break
		++consumedArgs

	result = compiler.Run()
	if printWarnings and len(result.Warnings):
		print(result.Warnings.ToString())
	if len(result.Errors):
		print(result.Errors.ToString(true))
		return -1
	else:
		try:
			resolver.AddAssembly(result.GeneratedAssembly)
			result.GeneratedAssembly.EntryPoint.Invoke(null, (argv[consumedArgs:],))
		except x as TargetInvocationException:
			print(x.InnerException)
			return -1
	return 0

[assembly: SecurityPermission(
						SecurityAction.RequestMinimum,
						ControlAppDomain: true)]

#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.Reflection
import System.Security.Permissions
import System.Threading
import Boo.Lang.Compiler
import Boo.Lang.Compiler.IO
import Boo.Lang.Compiler.Pipelines
import Boo.Lang.Compiler.Steps

class AssemblyResolver:

	_cache = {}

	def AddAssembly([required] asm as Assembly):
		_cache[GetSimpleName(asm.FullName)] = asm

	def AssemblyResolve(sender, args as ResolveEventArgs) as Assembly:
		simpleName = GetSimpleName(args.Name)

		asm as Assembly = _cache[simpleName]
		if asm is null:
			basePath = Path.GetFullPath(simpleName)
			asm = ProbeFile(basePath + ".dll")
			if asm is null:
				asm = ProbeFile(basePath + ".exe")
			_cache[simpleName] = asm

		return asm

	private def GetSimpleName(name as string):
		return /,\s*/.Split(name)[0]

	private def ProbeFile(fname as string):
		return Assembly.LoadFrom(fname) if File.Exists(fname)

def consume(reader as TextReader):
	return join(line for line in reader, "\n")

[STAThread]
def Main(argv as (string)):

	if len(argv) < 1:
		print("booi <script.boo>")
		return -1

	compiler = BooCompiler()
	compiler.Parameters.Pipeline = CompileToMemory()

	for arg in argv:
		if "-" == arg:
			compiler.Parameters.Input.Add(StringInput("<stdin>", consume(Console.In)))
			break
		elif "-ducky" == arg:
			compiler.Parameters.Ducky = true
		elif "-w" == arg:
			printWarnings = true
		else:
			compiler.Parameters.Input.Add(FileInput(arg))
			break

	resolver = AssemblyResolver()
	AppDomain.CurrentDomain.AssemblyResolve += resolver.AssemblyResolve
	result = compiler.Run()
	if printWarnings and len(result.Warnings):
		print(result.Warnings.ToString())
	if len(result.Errors):
		print(result.Errors.ToString(true))
		return -1
	else:
		try:
			resolver.AddAssembly(result.GeneratedAssembly)
			result.GeneratedAssembly.EntryPoint.Invoke(null, (argv[1:],))
		except x as TargetInvocationException:
			print(x.InnerException)
			return -1
	return 0

[assembly: SecurityPermission(
						SecurityAction.RequestMinimum,
						ControlAppDomain: true)]


refactor logic in GenerateFullPathToTool()

git-svn-id: fcce7d6ce0ac15bf24f68871b5145d46fc068279@2156 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.Microsoft.Build.Tasks

import Microsoft.Build.Framework
import Microsoft.Build.Tasks
import Microsoft.Build.Utilities
import System
import System.Diagnostics
import System.IO
import System.Globalization
import System.Text.RegularExpressions
import System.Threading

class Booc(ManagedCompiler):
"""
Represents the Boo compiler MSBuild task.

Authors:
	Sorin Ionescu (sorin.ionescu@gmail.com)
"""
	def constructor():
		NoLogo = true

	Pipelines:
	"""
	Gets/sets the aditional pipelines to add to the compiler process.
	"""
		get:
			return Bag['Pipelines'] as (string)
		set:
			Bag['Pipelines'] = value

	Verbosity:
	"""
	Gets/sets the verbosity level.
	"""
		get:
			return Bag['Verbosity'] as string
		set:
			Bag['Verbosity'] = value

	Culture:
	"""
	Gets/sets the culture.
	"""
		get:
			return Bag['Culture'] as string
		set:
			Bag['Culture'] = value

	SourceDirectory:
	"""
	Gets/sets the source directory.
	"""
		get:
			return Bag['Source Directory'] as string
		set:
			Bag['Source Directory'] = value

	ToolName:
	"""
	Gets the tool name.
	"""
		get:
			return "booc.exe"

	override def Execute():
	"""
	Executes the task.

	Returns:
		true if the task completed successfully; otherwise, false.
	"""
		boocCommandLine = CommandLineBuilderExtension()
		AddResponseFileCommands(boocCommandLine)

		warningPattern = regex(
			'^(?<file>.*?)\\((?<line>\\d+),(?<column>\\d+)\\):' +
				' (?BCW\\d{4}): WARNING: (?<message>.*)$',
			RegexOptions.Compiled)
		# Captures the file, line, column, code, and message from a BOO warning
		# in the form of: Program.boo(1,1): BCW0000: WARNING: This is a warning.

		errorPattern = regex(
			'^(((?<file>.*?)\\((?<line>\\d+),(?<column>\\d+)\\): )?' +
				'(?BCE\\d{4})|(?<errorType>Fatal) error):' +
				'( Boo.Lang.Compiler.CompilerError:)?' +
				' (?<message>.*?)($| --->)',
			RegexOptions.Compiled |
				RegexOptions.ExplicitCapture |
				RegexOptions.Multiline)
		/*
		 * Captures the file, line, column, code, error type, and message from a
		 * BOO error of the form of:
		 * 1. Program.boo(1,1): BCE0000: This is an error.
		 * 2. Program.boo(1,1): BCE0000: Boo.Lang.Compiler.CompilerError:
		 *    	This is an error. ---> Program.boo:4:19: This is an error
		 * 3. BCE0000: This is an error.
		 * 4. Fatal error: This is an error.
		 *
		 * The second line of the following error format is not cought because
		 * .NET does not support if|then|else in regular expressions,
		 * and the regex will be horrible complicated.
		 * The second line is as worthless as the first line.
		 * Therefore, it is not worth implementing it.
		 *
		 * 	Fatal error: This is an error.
		 * 	Parameter name: format.
		 */

		buildSuccess = true
		outputLine = String.Empty
		errorLine = String.Empty
		readingDoneEvents = (ManualResetEvent(false), ManualResetEvent(false))

		boocProcessStartInfo = ProcessStartInfo(
			FileName: GenerateFullPathToTool(),
			Arguments: boocCommandLine.ToString(),
			ErrorDialog: false,
			CreateNoWindow: true,
			RedirectStandardError: true,
			RedirectStandardInput: false,
			RedirectStandardOutput: true,
			UseShellExecute: false)

		boocProcess = Process(StartInfo: boocProcessStartInfo)

		parseOutput = def(line as string):
			warningPatternMatch = warningPattern.Match(line)
			errorPatternMatch = errorPattern.Match(line)

			if warningPatternMatch.Success:
				Log.LogWarning(
					null,
					warningPatternMatch.Groups['code'].Value,
					null,
					warningPatternMatch.Groups['file'].Value,
					int.Parse(warningPatternMatch.Groups['line'].Value),
					int.Parse(warningPatternMatch.Groups['column'].Value),
					0,
					0,
					warningPatternMatch.Groups['message'].Value)

			elif errorPatternMatch.Success:
				code = errorPatternMatch.Groups['code'].Value
				code = 'BCE0000' if string.IsNullOrEmpty(code)
				file = errorPatternMatch.Groups['file'].Value
				file = 'BOOC' if string.IsNullOrEmpty(file)

				try:
					lineNumber = int.Parse(
						errorPatternMatch.Groups['line'].Value,
						NumberStyles.Integer)

				except FormatException:
					lineNumber = 0

				try:
					columnNumber = int.Parse(
						errorPatternMatch.Groups['column'].Value,
						NumberStyles.Integer)

				except FormatException:
					columnNumber = 0

				Log.LogError(
					errorPatternMatch.Groups['errorType'].Value.ToLower(),
					code,
					null,
					file,
					lineNumber,
					columnNumber,
					0,
					0,
					errorPatternMatch.Groups['message'].Value)

				buildSuccess = false

			else:
				Log.LogMessage(MessageImportance.Low, line)

		readStandardOutput = def():
			while true:
				outputLine = boocProcess.StandardOutput.ReadLine()

				if outputLine:
					parseOutput(outputLine)

				else:
					readingDoneEvents[0].Set()
					break

		readStandardError = def():
			while true:
				errorLine = boocProcess.StandardError.ReadLine()

				if errorLine:
					parseOutput(errorLine)

				else:
					readingDoneEvents[1].Set()
					break

		standardOutputReadingThread = Thread(readStandardOutput as ThreadStart)
		standardErrorReadingThread = Thread(readStandardError as ThreadStart)
		# Two threads are required (MSDN); otherwise, a deadlock WILL occur.

		try:
			boocProcess.Start()

			Log.LogMessage(
				MessageImportance.High,
				"${ToolName} ${boocProcess.StartInfo.Arguments}",
				null)

			standardOutputReadingThread.Start()
			standardErrorReadingThread.Start()

			WaitHandle.WaitAny((readingDoneEvents[0],))
			WaitHandle.WaitAny((readingDoneEvents[1],))
			# MSBuild runs on an STA thread, and WaitHandle.WaitAll()
			# is not supported.

		except e as Exception:
			Log.LogErrorFromException(e)
			buildSuccess = false

		ensure:
			boocProcess.Close()

		return buildSuccess

	protected override def AddCommandLineCommands(
		commandLine as CommandLineBuilderExtension):
	"""
	Adds command line commands.

	Remarks:
		It prevents <ManagedCompiler> from adding the standard commands.
	"""
			pass

	protected override def AddResponseFileCommands(
		commandLine as CommandLineBuilderExtension):
	"""
	Generates the Boo compiler command line.

	Returns:
		The Boo compiler command line.
	"""
		commandLine.AppendSwitchIfNotNull('-t:', TargetType)
		commandLine.AppendSwitchIfNotNull('-o:', OutputAssembly)
		commandLine.AppendSwitchIfNotNull('-c:', Culture)
		commandLine.AppendSwitchIfNotNull('-srcdir:', SourceDirectory)
		commandLine.AppendSwitchIfNotNull('-keyfile:', KeyFile)
		commandLine.AppendSwitchIfNotNull('-keycontainer:', KeyContainer)

		commandLine.AppendSwitchIfNotNull("-lib:", AdditionalLibPaths, ",")

		if NoLogo:
			commandLine.AppendSwitch('-nologo')
		if NoConfig:
			commandLine.AppendSwitch('-noconfig')
		if DelaySign:
			commandLine.AppendSwitch('-delaysign')

		if EmitDebugInformation:
			commandLine.AppendSwitch('-debug')
		else:
			commandLine.AppendSwitch('-debug-')

		if ResponseFiles:
			for rsp in ResponseFiles:
				commandLine.AppendSwitchIfNotNull("@", rsp.ItemSpec)

		if Pipelines:
			for pipeline in Pipelines:
				commandLine.AppendSwitchIfNotNull('-p:', pipeline)

		if References:
			for reference in References:
				commandLine.AppendSwitchIfNotNull('-r:', reference.ItemSpec)

		if Resources:
			for resource in Resources:
				commandLine.AppendSwitchIfNotNull('-resource:', resource.ItemSpec)

		if Verbosity:
			if string.Compare(
					Verbosity,
					'Normal',
					StringComparison.InvariantCultureIgnoreCase) == 0:
				pass

			elif string.Compare(
					Verbosity,
					'Warning',
					StringComparison.InvariantCultureIgnoreCase) == 0:

				commandLine.AppendSwitch('-v')

			elif string.Compare(
					Verbosity,
					'Info',
					StringComparison.InvariantCultureIgnoreCase) == 0:

				commandLine.AppendSwitch('-vv')

			elif string.Compare(
					Verbosity,
					'Verbose',
					StringComparison.InvariantCultureIgnoreCase) == 0:

				commandLine.AppendSwitch('-vvv')

			else:
				Log.LogErrorWithCodeFromResources(
					'Vbc.EnumParameterHasInvalidValue',
					'Verbosity',
					Verbosity,
					'Normal, Warning, Info, Verbose')

		commandLine.AppendFileNamesIfNotNull(Sources, ' ')

	protected override def GenerateFullPathToTool():
	"""
	Generats the full path to booc.exe.
	"""
		path = ""

		if ToolPath:
			path = Path.Combine(ToolPath, ToolName)

		return path if File.Exists(path)

		path = Path.Combine(
			Path.GetDirectoryName(typeof(Booc).Assembly.Location),
			ToolName)

		return path if File.Exists(path)

		path = ToolLocationHelper.GetPathToDotNetFrameworkFile(
			ToolName,
			TargetDotNetFrameworkVersion.VersionLatest)

		return path if File.Exists(path)

		/* //removed this error message for mono compatibility
		Log.LogErrorWithCodeFromResources(
			"General.FrameworksFileNotFound",
			ToolName,
			ToolLocationHelper.GetDotNetFrameworkVersionFolderPrefix(
				TargetDotNetFrameworkVersion.Version20))
		*/
		path = "booc"

		return path

#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.Microsoft.Build.Tasks

import Microsoft.Build.Framework
import Microsoft.Build.Tasks
import Microsoft.Build.Utilities
import System
import System.Diagnostics
import System.IO
import System.Globalization
import System.Text.RegularExpressions
import System.Threading

class Booc(ManagedCompiler):
"""
Represents the Boo compiler MSBuild task.

Authors:
	Sorin Ionescu (sorin.ionescu@gmail.com)
"""
	def constructor():
		NoLogo = true

	Pipelines:
	"""
	Gets/sets the aditional pipelines to add to the compiler process.
	"""
		get:
			return Bag['Pipelines'] as (string)
		set:
			Bag['Pipelines'] = value

	Verbosity:
	"""
	Gets/sets the verbosity level.
	"""
		get:
			return Bag['Verbosity'] as string
		set:
			Bag['Verbosity'] = value

	Culture:
	"""
	Gets/sets the culture.
	"""
		get:
			return Bag['Culture'] as string
		set:
			Bag['Culture'] = value

	SourceDirectory:
	"""
	Gets/sets the source directory.
	"""
		get:
			return Bag['Source Directory'] as string
		set:
			Bag['Source Directory'] = value

	ToolName:
	"""
	Gets the tool name.
	"""
		get:
			return "booc.exe"

	override def Execute():
	"""
	Executes the task.

	Returns:
		true if the task completed successfully; otherwise, false.
	"""
		boocCommandLine = CommandLineBuilderExtension()
		AddResponseFileCommands(boocCommandLine)

		warningPattern = regex(
			'^(?<file>.*?)\\((?<line>\\d+),(?<column>\\d+)\\):' +
				' (?BCW\\d{4}): WARNING: (?<message>.*)$',
			RegexOptions.Compiled)
		# Captures the file, line, column, code, and message from a BOO warning
		# in the form of: Program.boo(1,1): BCW0000: WARNING: This is a warning.

		errorPattern = regex(
			'^(((?<file>.*?)\\((?<line>\\d+),(?<column>\\d+)\\): )?' +
				'(?BCE\\d{4})|(?<errorType>Fatal) error):' +
				'( Boo.Lang.Compiler.CompilerError:)?' +
				' (?<message>.*?)($| --->)',
			RegexOptions.Compiled |
				RegexOptions.ExplicitCapture |
				RegexOptions.Multiline)
		/*
		 * Captures the file, line, column, code, error type, and message from a
		 * BOO error of the form of:
		 * 1. Program.boo(1,1): BCE0000: This is an error.
		 * 2. Program.boo(1,1): BCE0000: Boo.Lang.Compiler.CompilerError:
		 *    	This is an error. ---> Program.boo:4:19: This is an error
		 * 3. BCE0000: This is an error.
		 * 4. Fatal error: This is an error.
		 *
		 * The second line of the following error format is not cought because
		 * .NET does not support if|then|else in regular expressions,
		 * and the regex will be horrible complicated.
		 * The second line is as worthless as the first line.
		 * Therefore, it is not worth implementing it.
		 *
		 * 	Fatal error: This is an error.
		 * 	Parameter name: format.
		 */

		buildSuccess = true
		outputLine = String.Empty
		errorLine = String.Empty
		readingDoneEvents = (ManualResetEvent(false), ManualResetEvent(false))

		boocProcessStartInfo = ProcessStartInfo(
			FileName: GenerateFullPathToTool(),
			Arguments: boocCommandLine.ToString(),
			ErrorDialog: false,
			CreateNoWindow: true,
			RedirectStandardError: true,
			RedirectStandardInput: false,
			RedirectStandardOutput: true,
			UseShellExecute: false)

		boocProcess = Process(StartInfo: boocProcessStartInfo)

		parseOutput = def(line as string):
			warningPatternMatch = warningPattern.Match(line)
			errorPatternMatch = errorPattern.Match(line)

			if warningPatternMatch.Success:
				Log.LogWarning(
					null,
					warningPatternMatch.Groups['code'].Value,
					null,
					warningPatternMatch.Groups['file'].Value,
					int.Parse(warningPatternMatch.Groups['line'].Value),
					int.Parse(warningPatternMatch.Groups['column'].Value),
					0,
					0,
					warningPatternMatch.Groups['message'].Value)

			elif errorPatternMatch.Success:
				code = errorPatternMatch.Groups['code'].Value
				code = 'BCE0000' if string.IsNullOrEmpty(code)
				file = errorPatternMatch.Groups['file'].Value
				file = 'BOOC' if string.IsNullOrEmpty(file)

				try:
					lineNumber = int.Parse(
						errorPatternMatch.Groups['line'].Value,
						NumberStyles.Integer)

				except FormatException:
					lineNumber = 0

				try:
					columnNumber = int.Parse(
						errorPatternMatch.Groups['column'].Value,
						NumberStyles.Integer)

				except FormatException:
					columnNumber = 0

				Log.LogError(
					errorPatternMatch.Groups['errorType'].Value.ToLower(),
					code,
					null,
					file,
					lineNumber,
					columnNumber,
					0,
					0,
					errorPatternMatch.Groups['message'].Value)

				buildSuccess = false

			else:
				Log.LogMessage(MessageImportance.Low, line)

		readStandardOutput = def():
			while true:
				outputLine = boocProcess.StandardOutput.ReadLine()

				if outputLine:
					parseOutput(outputLine)

				else:
					readingDoneEvents[0].Set()
					break

		readStandardError = def():
			while true:
				errorLine = boocProcess.StandardError.ReadLine()

				if errorLine:
					parseOutput(errorLine)

				else:
					readingDoneEvents[1].Set()
					break

		standardOutputReadingThread = Thread(readStandardOutput as ThreadStart)
		standardErrorReadingThread = Thread(readStandardError as ThreadStart)
		# Two threads are required (MSDN); otherwise, a deadlock WILL occur.

		try:
			boocProcess.Start()

			Log.LogMessage(
				MessageImportance.High,
				"${ToolName} ${boocProcess.StartInfo.Arguments}",
				null)

			standardOutputReadingThread.Start()
			standardErrorReadingThread.Start()

			WaitHandle.WaitAny((readingDoneEvents[0],))
			WaitHandle.WaitAny((readingDoneEvents[1],))
			# MSBuild runs on an STA thread, and WaitHandle.WaitAll()
			# is not supported.

		except e as Exception:
			Log.LogErrorFromException(e)
			buildSuccess = false

		ensure:
			boocProcess.Close()

		return buildSuccess

	protected override def AddCommandLineCommands(
		commandLine as CommandLineBuilderExtension):
	"""
	Adds command line commands.

	Remarks:
		It prevents <ManagedCompiler> from adding the standard commands.
	"""
			pass

	protected override def AddResponseFileCommands(
		commandLine as CommandLineBuilderExtension):
	"""
	Generates the Boo compiler command line.

	Returns:
		The Boo compiler command line.
	"""
		commandLine.AppendSwitchIfNotNull('-t:', TargetType)
		commandLine.AppendSwitchIfNotNull('-o:', OutputAssembly)
		commandLine.AppendSwitchIfNotNull('-c:', Culture)
		commandLine.AppendSwitchIfNotNull('-srcdir:', SourceDirectory)
		commandLine.AppendSwitchIfNotNull('-keyfile:', KeyFile)
		commandLine.AppendSwitchIfNotNull('-keycontainer:', KeyContainer)

		commandLine.AppendSwitchIfNotNull("-lib:", AdditionalLibPaths, ",")

		if NoLogo:
			commandLine.AppendSwitch('-nologo')
		if NoConfig:
			commandLine.AppendSwitch('-noconfig')
		if DelaySign:
			commandLine.AppendSwitch('-delaysign')

		if EmitDebugInformation:
			commandLine.AppendSwitch('-debug')
		else:
			commandLine.AppendSwitch('-debug-')

		if ResponseFiles:
			for rsp in ResponseFiles:
				commandLine.AppendSwitchIfNotNull("@", rsp.ItemSpec)

		if Pipelines:
			for pipeline in Pipelines:
				commandLine.AppendSwitchIfNotNull('-p:', pipeline)

		if References:
			for reference in References:
				commandLine.AppendSwitchIfNotNull('-r:', reference.ItemSpec)

		if Resources:
			for resource in Resources:
				commandLine.AppendSwitchIfNotNull('-resource:', resource.ItemSpec)

		if Verbosity:
			if string.Compare(
					Verbosity,
					'Normal',
					StringComparison.InvariantCultureIgnoreCase) == 0:
				pass

			elif string.Compare(
					Verbosity,
					'Warning',
					StringComparison.InvariantCultureIgnoreCase) == 0:

				commandLine.AppendSwitch('-v')

			elif string.Compare(
					Verbosity,
					'Info',
					StringComparison.InvariantCultureIgnoreCase) == 0:

				commandLine.AppendSwitch('-vv')

			elif string.Compare(
					Verbosity,
					'Verbose',
					StringComparison.InvariantCultureIgnoreCase) == 0:

				commandLine.AppendSwitch('-vvv')

			else:
				Log.LogErrorWithCodeFromResources(
					'Vbc.EnumParameterHasInvalidValue',
					'Verbosity',
					Verbosity,
					'Normal, Warning, Info, Verbose')

		commandLine.AppendFileNamesIfNotNull(Sources, ' ')

	protected override def GenerateFullPathToTool():
	"""
	Generats the full path to booc.exe.
	"""
		path = ""

		if ToolPath:
			path = Path.Combine(ToolPath, ToolName)

		if not File.Exists(path):
			path = Path.Combine(
				Path.GetDirectoryName(typeof(Booc).Assembly.Location),
				ToolName)

		if not File.Exists(path):
			path = ToolLocationHelper.GetPathToDotNetFrameworkFile(
				ToolName,
				TargetDotNetFrameworkVersion.Version20)

		if not File.Exists(path):
			Log.LogErrorWithCodeFromResources(
				"General.FrameworksFileNotFound",
				ToolName,
				ToolLocationHelper.GetDotNetFrameworkVersionFolderPrefix(
					TargetDotNetFrameworkVersion.Version20))
			path = "booc"

		return path


no longer needed

no longer needed

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

class Foo(ICharacter):
	[AutoDelegate(ICharacter)]
	Character as Character

[method_decorator(Character.dump, Customer)]
class Trace:
	include TraceMixin

mixin TraceMixin(Foo, ICollection):

	def before(name):
		print(name)

	def after(name):
		print(name)

mixin ListMixin(IList):

	include CollectionMixin

[class_invariant(Count > -1)]
[demands_attribute(Foo)]
class Base:
	Count as int

for line in TextFile("foo"):
	given line:
		when /$foo/:
			print("cool!")

		when /#def/:
			print("hello, master.")

		when cool_enough:
			print("cool!")

		when _prop1:
			print("prop1")

def cool_enough(s as string):
	pass


word wrap

word wrap

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

import System
import Gtk from "gtk-sharp"
import Gdk from "gdk-sharp" as Gdk
import Pango from "pango-sharp" as Pango
import booish from booish

class PromptView(TextView):

	_interpreter = InteractiveInterpreter(RememberLastValue: true)

	def constructor():

		self.WrapMode = Gtk.WrapMode.Word

		if Environment.OSVersion.Platform in PlatformID.Win32NT, PlatformID.Win32Windows:
			self.ModifyFont(
				Pango.FontDescription(Family: "Lucida Console", Size: 12))
		else:
			self.ModifyFont(
				Pango.FontDescription(Family: "Courier New"))

		_interpreter.References.Add(typeof(TextView).Assembly)
		_interpreter.References.Add(typeof(Gdk.Key).Assembly)
		_interpreter.SetValue("print", print)
		_interpreter.SetValue("dir", dir)
		_interpreter.SetValue("help", help)
		_interpreter.SetValue("cls", { Buffer.Text = "" })

		prompt()

	override def OnKeyPressEvent(ev as Gdk.EventKey):
		if Gdk.Key.Return == ev.Key:
			try:
				EvalCurrentLine()
			except x:
				print(x)
			prompt()
			return true
		elif ev.Key in Gdk.Key.BackSpace, Gdk.Key.Left:
			if Buffer.GetIterAtMark(Buffer.InsertMark).LineOffset < 5:
				return true

		return super(ev)

	def print(obj):
		Buffer.InsertAtCursor("${obj}\n")

	def prompt():
		Buffer.MoveMark(Buffer.InsertMark, Buffer.EndIter)
		Buffer.InsertAtCursor(">>> ")

	def dir([required] obj):
		type = (obj as Type) or obj.GetType()
		return [
				member for member in type.GetMembers()
				unless (method=(member as System.Reflection.MethodInfo))
				and method.IsSpecialName]

	def help(obj):
		print(join(dir(obj), "\n"))

	def repr(value):
		writer = System.IO.StringWriter()
		WriteRepr(writer, value)
		return writer.ToString()

	def EvalCurrentLine():
		start = Buffer.GetIterAtLine(Buffer.LineCount)
		line = Buffer.GetText(start, Buffer.EndIter, false)

		print("")
		result = _interpreter.Eval(line[4:])
		if len(result.Errors):

			for error in result.Errors:
				pos = error.LexicalInfo.StartColumn
				print("---" + "-"*pos + "^") if pos > 0
				print("ERROR: ${error.Message}")
		else:
			_ = _interpreter.LastValue
			if _ is not null:
				print(repr(_))
				_interpreter.SetValue("_", _)

class MainWindow(Window):

	def constructor():
		super("booish")

		window = ScrolledWindow()
		window.Add(PromptView())

		self.Add(window)

		self.DeleteEvent += Application.Quit

Application.Init()

window = MainWindow(DefaultWidth:  400,
				DefaultHeight: 250)

window.ShowAll()

Application.Run()

import System
import Gtk from "gtk-sharp"
import Gdk from "gdk-sharp" as Gdk
import Pango from "pango-sharp" as Pango
import booish from booish

class PromptView(TextView):

	_interpreter = InteractiveInterpreter(RememberLastValue: true)

	def constructor():

		if Environment.OSVersion.Platform in PlatformID.Win32NT, PlatformID.Win32Windows:
			self.ModifyFont(
				Pango.FontDescription(Family: "Lucida Console", Size: 12))
		else:
			self.ModifyFont(
				Pango.FontDescription(Family: "Courier New"))

		_interpreter.References.Add(typeof(TextView).Assembly)
		_interpreter.References.Add(typeof(Gdk.Key).Assembly)
		_interpreter.SetValue("print", print)
		_interpreter.SetValue("dir", dir)
		_interpreter.SetValue("help", help)
		_interpreter.SetValue("cls", { Buffer.Text = "" })

		prompt()

	override def OnKeyPressEvent(ev as Gdk.EventKey):
		if Gdk.Key.Return == ev.Key:
			try:
				EvalCurrentLine()
			except x:
				print(x)
			prompt()
			return true
		elif ev.Key in Gdk.Key.BackSpace, Gdk.Key.Left:
			if Buffer.GetIterAtMark(Buffer.InsertMark).LineOffset < 5:
				return true

		return super(ev)

	def print(obj):
		Buffer.InsertAtCursor("${obj}\n")

	def prompt():
		Buffer.MoveMark(Buffer.InsertMark, Buffer.EndIter)
		Buffer.InsertAtCursor(">>> ")

	def dir([required] obj):
		type = (obj as Type) or obj.GetType()
		for member in type.GetMembers():
			method = member as System.Reflection.MethodInfo
			if method is not null:
				yield method if method.IsPublic and not method.IsSpecialName
			else:
				yield member

	def help(obj):
		print(join(dir(obj), "\n"))

	def repr(value):
		writer = System.IO.StringWriter()
		WriteRepr(writer, value)
		return writer.ToString()

	def EvalCurrentLine():
		start = Buffer.GetIterAtLine(Buffer.LineCount)
		line = Buffer.GetText(start, Buffer.EndIter, false)

		print("")
		result = _interpreter.Eval(line[4:])
		if len(result.Errors):

			for error in result.Errors:
				pos = error.LexicalInfo.StartColumn
				print("---" + "-"*pos + "^") if pos > 0
				print("ERROR: ${error.Message}")
		else:
			_ = _interpreter.LastValue
			if _ is not null:
				print(repr(_))
				_interpreter.SetValue("_", _)

class MainWindow(Window):

	def constructor():
		super("booish")

		window = ScrolledWindow()
		window.Add(PromptView())

		self.Add(window)

		self.DeleteEvent += Application.Quit

Application.Init()

window = MainWindow(DefaultWidth:  400,
				DefaultHeight: 250)

window.ShowAll()

Application.Run()


Documented why we raise sent exceptions from the top of the generator function

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

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) |])
                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 = [|
            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)