better support for char comparisons

better support for char comparisons

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

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

namespace BooBinding

import System
import System.Reflection
import System.CodeDom
import System.Text
import System.Collections
import ICSharpCode.SharpRefactory.Parser
import ICSharpCode.SharpRefactory.Parser.AST

class BooRefactory:
	// This class can refactor the c# parse tree before converting to boo
	// to simplify some things

	// NOTE: While writing this, I discovered many bugs in SharpRefactory I worked around
	// by overriding the buggy methods.
	// Since SharpRefactory will be replaced by NRefactory soon, the bugs won't be fixed
	// in SharpRefactory but only in NRefactory (but many bugs don't exist in NRefactory anyhow)

	def Refactor(compilationUnit as CompilationUnit):
		RefactorNamespaces(compilationUnit)
		compilationUnit.AcceptChildren(RefactoryVisitor(), null);

	def RefactorNamespaces(compilationUnit as CompilationUnit):
		namespacecount = CountNamespaces(compilationUnit.Children)
		if namespacecount == 1:
			// put the namespace definition to the beginning
			for o in compilationUnit.Children:
				n = o as NamespaceDeclaration
				if n != null:
					// get using definitions into the namespace
					num = 0
					i = 0
					while i < compilationUnit.Children.Count:
						child = compilationUnit.Children[i]
						print("i = " + i.ToString() + "; child =" + child.ToString())
						if child isa UsingDeclaration or child isa UsingAliasDeclaration:
							compilationUnit.Children.RemoveAt(i)
							i -= 1
							n.Children.Insert(num, child)
							num += 1
						i += 1
					return

	def CountNamespaces(l as ArrayList) as int:
		count as int = 0
		for o in l:
			n = o as NamespaceDeclaration
			if n != null:
				count += 1 + CountNamespaces(n.Children)
		return count

class RefactoryVisitor(AbstractASTVisitor):
	override def Visit(typeDeclaration as TypeDeclaration, data):
		// create DefaultMemberAttribute for indexers
		if typeDeclaration.Type == Types.Class and HasIndexer(typeDeclaration.Children):
			expression = PrimitiveExpression('Indexer', 'Indexer')
			attribute = ICSharpCode.SharpRefactory.Parser.AST.Attribute("DefaultMember", MakeArray(expression), ArrayList())
			typeDeclaration.Attributes.Add(AttributeSection(null, MakeArray(attribute)))

		field as FieldDeclaration = null
		removed = ArrayList() // list of all elements that should be removed from typeDeclaration

		// prefix fields with underscore
		fields = ArrayList()
		for o in typeDeclaration.Children:
			field = o as FieldDeclaration
			if field != null:
				for var as VariableDeclaration in field.Fields:
					if var.Name.Length < 2 or not var.Name[1:2] == "_":
						if Char.IsLower(var.Name, 0) and IsPrivate(field.Modifier):
							fields.Add(var)
		typeDeclaration.AcceptChildren(RenameFieldVisitor(fields), data)
		for var as VariableDeclaration in fields:
			var.Name = "_" + var.Name

		// convert Getter/Setter into attributes if possible
		// -> therefore create a new field list (containing the fields, not VarDecs)
		fieldHash = Hashtable()
		for o in typeDeclaration.Children:
			field = o as FieldDeclaration
			if field != null:
				if field.Fields.Count == 1:
					fieldHash[cast(VariableDeclaration, field.Fields[0]).Name] = field
		// now look for properties
		for o in typeDeclaration.Children:
			property = o as PropertyDeclaration
			if property != null and property.Modifier == Modifier.Public:
				ok = true
				field = null
				// check if the property has correct getters/setters
				if property.HasGetRegion:
					block = property.GetRegion.Block
					if block != null and block.Children.Count == 1:
						child = block.Children[0] as ReturnStatement
						if child != null:
							retExpr = child.ReturnExpression as IdentifierExpression
							if retExpr != null:
								field = fieldHash[retExpr.Identifier]
					ok = false if field == null
				if ok and property.HasSetRegion:
					ok = false
					setterField as FieldDeclaration = null
					block = property.SetRegion.Block
					if block != null and block.Children.Count == 1:
						childStatement = block.Children[0] as StatementExpression
						if child != null:
							expr = childStatement.Expression as AssignmentExpression
							if expr != null and expr.Op == AssignmentOperatorType.Assign:
								leftExpr = expr.Left as IdentifierExpression
								rightExpr = expr.Right as IdentifierExpression
								if leftExpr != null and rightExpr != null and rightExpr.Identifier == "value":
									setterField = fieldHash[leftExpr.Identifier]
					ok = false if setterField == null or setterField != field

				if ok and property.HasGetRegion:
					removed.Add(o)
					print("Replacing property ${property.Name}")
					identifier = IdentifierExpression(property.Name)
					attributes = ArrayList()
					if property.HasSetRegion:
						attributes.Add(ICSharpCode.SharpRefactory.Parser.AST.Attribute("Property", MakeArray(identifier), ArrayList()))
					else:
						attributes.Add(ICSharpCode.SharpRefactory.Parser.AST.Attribute("Getter", MakeArray(identifier), ArrayList()))
					field.Attributes.Add(AttributeSection(null, attributes))

		for o in removed:
			typeDeclaration.Children.Remove(o)

		return super(typeDeclaration, data)

	def IsPrivate(m as Modifier) as bool:
		// fields without any modifier are also private
		return false if (m & Modifier.Public) == Modifier.Public
		return false if (m & Modifier.Protected) == Modifier.Protected
		return false if (m & Modifier.Internal) == Modifier.Internal
		return true

	override def Visit(statementExpression as StatementExpression, data):
		expr = statementExpression.Expression as UnaryOperatorExpression
		if expr != null:
			// found unary operator as single statement
			expr.Op = UnaryOperatorType.Increment if expr.Op == UnaryOperatorType.PostIncrement
			expr.Op = UnaryOperatorType.Decrement if expr.Op == UnaryOperatorType.PostDecrement

		return super(statementExpression, data)

	override def Visit(localVariableDeclaration as LocalVariableDeclaration, data):
		/* Check for this structure:
		[LocalVariableDeclaration: Type=[TypeReference: Type=EmptyClass, PointerNestingLevel=0, RankSpecifier=System.Int32[]], Modifier =None
		Variables={[VariableDeclaration: Name=c, Initializer=[CastExpression:
			CastTo=[TypeReference: Type=EmptyClass, PointerNestingLevel=0, RankSpecifier=System.Int32[]],
			Expression=***]]}]
		*/
		if localVariableDeclaration.Variables.Count == 1:
			var as VariableDeclaration = localVariableDeclaration.Variables[0]
			castExpr = var.Initializer as CastExpression
			if castExpr != null:
				if castExpr.CastTo.Type == localVariableDeclaration.Type.Type:
					// remove redundant cast
					var.Initializer = castExpr.Expression

		return super(localVariableDeclaration, data)

	override def Visit(blockStatement as BlockStatement, data):
		// work around SharpRefactory bug (that won't be fixed before NRefactory)
		switchSection = blockStatement as SwitchSection
		return Visit(switchSection, data) if switchSection != null
		return super(blockStatement, data)

	override def Visit(switchSection as SwitchSection, data):
		if switchSection.Children != null and switchSection.Children.Count > 0:
			lastNum = switchSection.Children.Count - 1
			lastChild = switchSection.Children[lastNum]
			if lastChild isa BreakStatement:
				switchSection.Children.RemoveAt(lastNum)

		// can't use super because of another SharpRefactory bug
		for label as Expression in switchSection.SwitchLabels:
			label.AcceptVisitor(self, data) if label != null
		return switchSection.AcceptChildren(self, data)

	def HasIndexer(l as ArrayList) as bool:
		for o in l:
			return true if o isa IndexerDeclaration
		return false

	def MakeArray(val as object) as ArrayList:
		a = ArrayList()
		a.Add(val)
		return a

class RenameFieldVisitor(AbstractASTVisitor):
	_fields as ArrayList
	_curBlock = ArrayList()
	_blocks = Stack()

	def constructor(fields as ArrayList):
		_fields = fields

	override def Visit(typeDeclaration as TypeDeclaration, data):
		// ignore sub-types
		return null

	override def Visit(blockStatement as BlockStatement, data):
		Push()
		result = super(blockStatement, data)
		Pop()
		return result

	override def Visit(methodDeclaration as MethodDeclaration, data):
		Push()
		result = super(methodDeclaration, data)
		Pop()
		return result

	override def Visit(constructorDeclaration as ConstructorDeclaration, data):
		Push()
		result = super(constructorDeclaration, data)
		Pop()
		return result

	private def Push():
		//print("PUSH block")
		_blocks.Push(_curBlock)
		_curBlock = ArrayList()

	private def Pop():
		_curBlock = _blocks.Pop()
		//print("POP block")

	override def Visit(localVariableDeclaration as LocalVariableDeclaration, data):
		for decl as VariableDeclaration in localVariableDeclaration.Variables:
			//print("add variable ${decl.Name} to block")
			_curBlock.Add(decl.Name)
		return super(localVariableDeclaration, data)

	override def Visit(parameterDeclarationExpression as ParameterDeclarationExpression, data):
		_curBlock.Add(parameterDeclarationExpression.ParameterName)
		//print("add parameter ${parameterDeclarationExpression.ParameterName} to block")
		return super(parameterDeclarationExpression, data)

	override def Visit(identifierExpression as IdentifierExpression, data):
		name = identifierExpression.Identifier
		for var as VariableDeclaration in _fields:
			if var.Name == name and not IsLocal(name):
				identifierExpression.Identifier = "_" + name
				return null
		return super(identifierExpression, data)

	override def Visit(fieldReferenceExpression as FieldReferenceExpression, data):
		if fieldReferenceExpression.TargetObject isa ThisReferenceExpression:
			name = fieldReferenceExpression.FieldName
			for var as VariableDeclaration in _fields:
				if var.Name == fieldReferenceExpression.FieldName:
					fieldReferenceExpression.FieldName = "_" + name
					return null
		return super(fieldReferenceExpression, data)

	def IsLocal(name as string) as bool:
		for block as ArrayList in _blocks:
			for n as string in block:
				return true if name == n
		for n as string in _curBlock:
			return true if name == n
		return false

	override def Visit(invocationExpression as InvocationExpression, data):
		// this method is a workaround for a bug in SharpRefactory
		result = data
		if invocationExpression.TargetObject != null:
			result = invocationExpression.TargetObject.AcceptVisitor(self, data)
		if invocationExpression.Parameters != null:
			for n as INode in invocationExpression.Parameters:
				n.AcceptVisitor(self, data)
		return result

	override def Visit(indexerExpression as IndexerExpression, data):
		// this method is a workaround for a bug in SharpRefactory
		result = indexerExpression.TargetObject.AcceptVisitor(self, data)
		for n as INode in indexerExpression.Indices:
			n.AcceptVisitor(self, data)
		return result

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

namespace BooBinding

import System
import System.Reflection
import System.CodeDom
import System.Text
import System.Collections
import ICSharpCode.SharpRefactory.Parser
import ICSharpCode.SharpRefactory.Parser.AST

class BooRefactory:
	// This class can refactor the c# parse tree before converting to boo
	// to simplify some things

	// NOTE: While writing this, I discovered many bugs in SharpRefactory I worked around
	// by overriding the buggy methods.
	// Since SharpRefactory will be replaced by NRefactory soon, the bugs won't be fixed
	// in SharpRefactory but only in NRefactory (but many bugs don't exist in NRefactory anyhow)

	def Refactor(compilationUnit as CompilationUnit):
		RefactorNamespaces(compilationUnit)
		compilationUnit.AcceptChildren(RefactoryVisitor(), null);

	def RefactorNamespaces(compilationUnit as CompilationUnit):
		namespacecount = CountNamespaces(compilationUnit.Children)
		if namespacecount == 1:
			// put the namespace definition to the beginning
			for o in compilationUnit.Children:
				n = o as NamespaceDeclaration
				if n != null:
					// get using definitions into the namespace
					num = 0
					i = 0
					while i < compilationUnit.Children.Count:
						child = compilationUnit.Children[i]
						print("i = " + i.ToString() + "; child =" + child.ToString())
						if child isa UsingDeclaration or child isa UsingAliasDeclaration:
							compilationUnit.Children.RemoveAt(i)
							i -= 1
							n.Children.Insert(num, child)
							num += 1
						i += 1
					return

	def CountNamespaces(l as ArrayList) as int:
		count as int = 0
		for o in l:
			n = o as NamespaceDeclaration
			if n != null:
				count += 1 + CountNamespaces(n.Children)
		return count

class RefactoryVisitor(AbstractASTVisitor):
	override def Visit(typeDeclaration as TypeDeclaration, data):
		// create DefaultMemberAttribute for indexers
		if typeDeclaration.Type == Types.Class and HasIndexer(typeDeclaration.Children):
			expression = PrimitiveExpression('Indexer', 'Indexer')
			attribute = ICSharpCode.SharpRefactory.Parser.AST.Attribute("DefaultMember", MakeArray(expression), ArrayList())
			typeDeclaration.Attributes.Add(AttributeSection(null, MakeArray(attribute)))

		field as FieldDeclaration = null
		removed = ArrayList() // list of all elements that should be removed from typeDeclaration

		// prefix fields with underscore
		fields = ArrayList()
		for o in typeDeclaration.Children:
			field = o as FieldDeclaration
			if field != null:
				for var as VariableDeclaration in field.Fields:
					if var.Name.Length < 2 or not var.Name[1] == "_":
						if Char.IsLower(var.Name, 0) and IsPrivate(field.Modifier):
							fields.Add(var)
		typeDeclaration.AcceptChildren(RenameFieldVisitor(fields), data)
		for var as VariableDeclaration in fields:
			var.Name = "_" + var.Name

		// convert Getter/Setter into attributes if possible
		// -> therefore create a new field list (containing the fields, not VarDecs)
		fieldHash = Hashtable()
		for o in typeDeclaration.Children:
			field = o as FieldDeclaration
			if field != null:
				if field.Fields.Count == 1:
					fieldHash[cast(VariableDeclaration, field.Fields[0]).Name] = field
		// now look for properties
		for o in typeDeclaration.Children:
			property = o as PropertyDeclaration
			if property != null and property.Modifier == Modifier.Public:
				ok = true
				field = null
				// check if the property has correct getters/setters
				if property.HasGetRegion:
					block = property.GetRegion.Block
					if block != null and block.Children.Count == 1:
						child = block.Children[0] as ReturnStatement
						if child != null:
							retExpr = child.ReturnExpression as IdentifierExpression
							if retExpr != null:
								field = fieldHash[retExpr.Identifier]
					ok = false if field == null
				if ok and property.HasSetRegion:
					ok = false
					setterField as FieldDeclaration = null
					block = property.SetRegion.Block
					if block != null and block.Children.Count == 1:
						childStatement = block.Children[0] as StatementExpression
						if child != null:
							expr = childStatement.Expression as AssignmentExpression
							if expr != null and expr.Op == AssignmentOperatorType.Assign:
								leftExpr = expr.Left as IdentifierExpression
								rightExpr = expr.Right as IdentifierExpression
								if leftExpr != null and rightExpr != null and rightExpr.Identifier == "value":
									setterField = fieldHash[leftExpr.Identifier]
					ok = false if setterField == null or setterField != field

				if ok and property.HasGetRegion:
					removed.Add(o)
					print("Replacing property ${property.Name}")
					identifier = IdentifierExpression(property.Name)
					attributes = ArrayList()
					if property.HasSetRegion:
						attributes.Add(ICSharpCode.SharpRefactory.Parser.AST.Attribute("Property", MakeArray(identifier), ArrayList()))
					else:
						attributes.Add(ICSharpCode.SharpRefactory.Parser.AST.Attribute("Getter", MakeArray(identifier), ArrayList()))
					field.Attributes.Add(AttributeSection(null, attributes))

		for o in removed:
			typeDeclaration.Children.Remove(o)

		return super(typeDeclaration, data)

	def IsPrivate(m as Modifier) as bool:
		// fields without any modifier are also private
		return false if (m & Modifier.Public) == Modifier.Public
		return false if (m & Modifier.Protected) == Modifier.Protected
		return false if (m & Modifier.Internal) == Modifier.Internal
		return true

	override def Visit(statementExpression as StatementExpression, data):
		expr = statementExpression.Expression as UnaryOperatorExpression
		if expr != null:
			// found unary operator as single statement
			expr.Op = UnaryOperatorType.Increment if expr.Op == UnaryOperatorType.PostIncrement
			expr.Op = UnaryOperatorType.Decrement if expr.Op == UnaryOperatorType.PostDecrement

		return super(statementExpression, data)

	override def Visit(localVariableDeclaration as LocalVariableDeclaration, data):
		/* Check for this structure:
		[LocalVariableDeclaration: Type=[TypeReference: Type=EmptyClass, PointerNestingLevel=0, RankSpecifier=System.Int32[]], Modifier =None
		Variables={[VariableDeclaration: Name=c, Initializer=[CastExpression:
			CastTo=[TypeReference: Type=EmptyClass, PointerNestingLevel=0, RankSpecifier=System.Int32[]],
			Expression=***]]}]
		*/
		if localVariableDeclaration.Variables.Count == 1:
			var as VariableDeclaration = localVariableDeclaration.Variables[0]
			castExpr = var.Initializer as CastExpression
			if castExpr != null:
				if castExpr.CastTo.Type == localVariableDeclaration.Type.Type:
					// remove redundant cast
					var.Initializer = castExpr.Expression

		return super(localVariableDeclaration, data)

	override def Visit(blockStatement as BlockStatement, data):
		// work around SharpRefactory bug (that won't be fixed before NRefactory)
		switchSection = blockStatement as SwitchSection
		return Visit(switchSection, data) if switchSection != null
		return super(blockStatement, data)

	override def Visit(switchSection as SwitchSection, data):
		if switchSection.Children != null and switchSection.Children.Count > 0:
			lastNum = switchSection.Children.Count - 1
			lastChild = switchSection.Children[lastNum]
			if lastChild isa BreakStatement:
				switchSection.Children.RemoveAt(lastNum)

		// can't use super because of another SharpRefactory bug
		for label as Expression in switchSection.SwitchLabels:
			label.AcceptVisitor(self, data) if label != null
		return switchSection.AcceptChildren(self, data)

	def HasIndexer(l as ArrayList) as bool:
		for o in l:
			return true if o isa IndexerDeclaration
		return false

	def MakeArray(val as object) as ArrayList:
		a = ArrayList()
		a.Add(val)
		return a

class RenameFieldVisitor(AbstractASTVisitor):
	_fields as ArrayList
	_curBlock = ArrayList()
	_blocks = Stack()

	def constructor(fields as ArrayList):
		_fields = fields

	override def Visit(typeDeclaration as TypeDeclaration, data):
		// ignore sub-types
		return null

	override def Visit(blockStatement as BlockStatement, data):
		Push()
		result = super(blockStatement, data)
		Pop()
		return result

	override def Visit(methodDeclaration as MethodDeclaration, data):
		Push()
		result = super(methodDeclaration, data)
		Pop()
		return result

	override def Visit(constructorDeclaration as ConstructorDeclaration, data):
		Push()
		result = super(constructorDeclaration, data)
		Pop()
		return result

	private def Push():
		//print("PUSH block")
		_blocks.Push(_curBlock)
		_curBlock = ArrayList()

	private def Pop():
		_curBlock = _blocks.Pop()
		//print("POP block")

	override def Visit(localVariableDeclaration as LocalVariableDeclaration, data):
		for decl as VariableDeclaration in localVariableDeclaration.Variables:
			//print("add variable ${decl.Name} to block")
			_curBlock.Add(decl.Name)
		return super(localVariableDeclaration, data)

	override def Visit(parameterDeclarationExpression as ParameterDeclarationExpression, data):
		_curBlock.Add(parameterDeclarationExpression.ParameterName)
		//print("add parameter ${parameterDeclarationExpression.ParameterName} to block")
		return super(parameterDeclarationExpression, data)

	override def Visit(identifierExpression as IdentifierExpression, data):
		name = identifierExpression.Identifier
		for var as VariableDeclaration in _fields:
			if var.Name == name and not IsLocal(name):
				identifierExpression.Identifier = "_" + name
				return null
		return super(identifierExpression, data)

	override def Visit(fieldReferenceExpression as FieldReferenceExpression, data):
		if fieldReferenceExpression.TargetObject isa ThisReferenceExpression:
			name = fieldReferenceExpression.FieldName
			for var as VariableDeclaration in _fields:
				if var.Name == fieldReferenceExpression.FieldName:
					fieldReferenceExpression.FieldName = "_" + name
					return null
		return super(fieldReferenceExpression, data)

	def IsLocal(name as string) as bool:
		for block as ArrayList in _blocks:
			for n as string in block:
				return true if name == n
		for n as string in _curBlock:
			return true if name == n
		return false

	override def Visit(invocationExpression as InvocationExpression, data):
		// this method is a workaround for a bug in SharpRefactory
		result = data
		if invocationExpression.TargetObject != null:
			result = invocationExpression.TargetObject.AcceptVisitor(self, data)
		if invocationExpression.Parameters != null:
			for n as INode in invocationExpression.Parameters:
				n.AcceptVisitor(self, data)
		return result

	override def Visit(indexerExpression as IndexerExpression, data):
		// this method is a workaround for a bug in SharpRefactory
		result = indexerExpression.TargetObject.AcceptVisitor(self, data)
		for n as INode in indexerExpression.Indices:
			n.AcceptVisitor(self, data)
		return result


arrayperformance update to reflect language advances

git-svn-id: c3b8989232654601f1acea7403f771adaaa44a81@1107 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

def test():
	items = 2000000

	a = array(object, range(items))

	collect = []

	start = date.Now
	for i in range(items):
		collect.Add(a[i])

	print("${(date.Now-start).TotalMilliseconds} elapsed.")

test()
test()
test()

#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

def test():
	items = 2000000

	a = array(object, range(items))

	collect = []

	start = date.Now
	for i as int in range(items):
		collect.Add(a[i])

	elapsed = date.Now.Subtract(start)

	print("${elapsed.TotalMilliseconds} elapsed.")

test()
test()
test()


cached return value field must be private

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

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

namespace Boo.Lang.Useful.Attributes

import System.Threading
import Boo.Lang.Compiler
import Boo.Lang.Compiler.Ast
import Boo.Lang.Compiler.Steps
import Boo.Lang.Compiler.TypeSystem

class OnceAttribute(AbstractAstAttribute):
"""
Caches the return value of a method.

Usage
	[once]
	def foo():
		return Math.Sin(Math.PI*3)

@author Sorin Ionescu (sorin.ionescu@gmail.com)
"""
	private class ReturnValueStorageVisitor(DepthFirstVisitor):
	"""
	Replaces returns statements with binary expressions that
	store the values of the return statements.
	"""
		_returnValue as ReferenceExpression

		def constructor(returnValue as ReferenceExpression):
			_returnValue = returnValue

		override def OnReturnStatement(returnStatement as ReturnStatement):
		"""
		Replaces returns statements with binary expressions that
		store the values of the return statements.
		"""
			returnValueAssignment = BinaryExpression(
				LexicalInfo: returnStatement.LexicalInfo,
				Operator: BinaryOperatorType.Assign,
				Left: _returnValue,
				Right: returnStatement.Expression)

			parentNode = returnStatement.ParentNode
			parentNode.Replace(
				returnStatement,
				ExpressionStatement(returnValueAssignment))

	_method as Method
	_methodLock as Field
	_cached as Field
	_returnValue as Field

	override def Dispose():
	"""
	Dont get rid of the context because we'll need it later.
	"""
		pass

	override def Apply(node as Node):
	"""
	Applies the <OnceAttribute> to [node].

	Parameters:
		node
			The node to apply the <OnceAttribute> to.
	"""
		if not node isa Method:
			InvalidNodeForAttribute("Method")
			return

		_method = node

		CreateCachedField()
		CreateMethodLockField()
		PrepareMethodBody()

		Context.Parameters.Pipeline.AfterStep += def(
			sender,
			e as CompilerStepEventArgs):

			return if not e.Step isa ProcessMethodBodies

			# void methods dont need to be cached.
			returnType = TypeSystemServices.GetEntity(_method.ReturnType)
			return if returnType is self.TypeSystemServices.VoidType

			CreateReturnValueField()
			PostProcessMethod()

	def CreateReturnValueField():
	"""
	Creates the field that stores the return value of the cached method.
	"""
		template = self.CodeBuilder.CreateField('field', TypeSystemServices.GetEntity(_method.ReturnType))
		template.Modifiers = TypeMemberModifiers.Private
		_returnValue = AddField(template, "___${_method.Name}_returnValue")

	def CreateCachedField():
	"""
	Creates the cached flag.

	Remarks:
		The flag is used to check whether the method has been cached.
	"""
		template = [|
			private field as bool
		|]
		_cached = AddField(template, "___${_method.Name}_cached")

	def CreateMethodLockField():
	"""
	Creates the lock field.

	Remarks:
		The field is used to lock on when the operatation is thread safe.
	"""
		template = [|
			private field as object = object()
		|]
		_methodLock = AddField(template, "___${_method.Name}_lock")

	def AddField(template as Field, name as string):
		template.LexicalInfo = self.LexicalInfo
		template.Name = name
		template.Modifiers |= TypeMemberModifiers.Static if IsStaticMethod()
		_method.DeclaringType.Members.Add(template)
		return template

	def IsStaticMethod():
		return _method.IsStatic or _method.ParentNode isa Module

	def PrepareMethodBody():

		cached = ReferenceExpression(_cached.Name)
		methodLock = ReferenceExpression(_methodLock.Name)

		_method.Body = [|
			if not $cached:
				System.Threading.Monitor.Enter($methodLock)
				try:
					if not $cached:
						$(_method.Body)
						$cached = true
				ensure:
					System.Threading.Monitor.Exit($methodLock)
		|].ToBlock()

	def PostProcessMethod():
	"""
	Visit the node and replace return statements
	with binary expressions that store the return
	statement values.
	Add a single return statement at the end
	to return the cached value.
	"""
		_method.Accept(
				ReturnValueStorageVisitor(
					self.CodeBuilder.CreateReference(_returnValue)))
		_method.Body.Add(
			ReturnStatement(
				LexicalInfo: LexicalInfo,
				Expression: self.CodeBuilder.CreateReference(_returnValue)))

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

namespace Boo.Lang.Useful.Attributes

import System.Threading
import Boo.Lang.Compiler
import Boo.Lang.Compiler.Ast
import Boo.Lang.Compiler.Steps
import Boo.Lang.Compiler.TypeSystem

class OnceAttribute(AbstractAstAttribute):
"""
Caches the return value of a method.

Usage
	[once]
	def foo():
		return Math.Sin(Math.PI*3)

@author Sorin Ionescu (sorin.ionescu@gmail.com)
"""
	private class ReturnValueStorageVisitor(DepthFirstVisitor):
	"""
	Replaces returns statements with binary expressions that
	store the values of the return statements.
	"""
		_returnValue as ReferenceExpression

		def constructor(returnValue as ReferenceExpression):
			_returnValue = returnValue

		override def OnReturnStatement(returnStatement as ReturnStatement):
		"""
		Replaces returns statements with binary expressions that
		store the values of the return statements.
		"""
			returnValueAssignment = BinaryExpression(
				LexicalInfo: returnStatement.LexicalInfo,
				Operator: BinaryOperatorType.Assign,
				Left: _returnValue,
				Right: returnStatement.Expression)

			parentNode = returnStatement.ParentNode
			parentNode.Replace(
				returnStatement,
				ExpressionStatement(returnValueAssignment))

	_method as Method
	_methodLock as Field
	_cached as Field
	_returnValue as Field

	override def Dispose():
	"""
	Dont get rid of the context because we'll need it later.
	"""
		pass

	override def Apply(node as Node):
	"""
	Applies the <OnceAttribute> to [node].

	Parameters:
		node
			The node to apply the <OnceAttribute> to.
	"""
		if not node isa Method:
			InvalidNodeForAttribute("Method")
			return

		_method = node

		CreateCachedField()
		CreateMethodLockField()
		PrepareMethodBody()

		Context.Parameters.Pipeline.AfterStep += def(
			sender,
			e as CompilerStepEventArgs):

			return if not e.Step isa ProcessMethodBodies

			# void methods dont need to be cached.
			returnType = TypeSystemServices.GetEntity(_method.ReturnType)
			return if returnType is self.TypeSystemServices.VoidType

			CreateReturnValueField()
			PostProcessMethod()

	def CreateReturnValueField():
	"""
	Creates the field that stores the return value of the cached method.
	"""
		template = self.CodeBuilder.CreateField('field', TypeSystemServices.GetEntity(_method.ReturnType))
		_returnValue = AddField(template, "___${_method.Name}_returnValue")

	def CreateCachedField():
	"""
	Creates the cached flag.

	Remarks:
		The flag is used to check whether the method has been cached.
	"""
		template = [|
			private field as bool
		|]
		_cached = AddField(template, "___${_method.Name}_cached")

	def CreateMethodLockField():
	"""
	Creates the lock field.

	Remarks:
		The field is used to lock on when the operatation is thread safe.
	"""
		template = [|
			private field as object = object()
		|]
		_methodLock = AddField(template, "___${_method.Name}_lock")

	def AddField(template as Field, name as string):
		template.LexicalInfo = self.LexicalInfo
		template.Name = name
		template.Modifiers |= TypeMemberModifiers.Static if IsStaticMethod()
		_method.DeclaringType.Members.Add(template)
		return template

	def IsStaticMethod():
		return _method.IsStatic or _method.ParentNode isa Module

	def PrepareMethodBody():

		cached = ReferenceExpression(_cached.Name)
		methodLock = ReferenceExpression(_methodLock.Name)

		_method.Body = [|
			if not $cached:
				System.Threading.Monitor.Enter($methodLock)
				try:
					if not $cached:
						$(_method.Body)
						$cached = true
				ensure:
					System.Threading.Monitor.Exit($methodLock)
		|].ToBlock()

	def PostProcessMethod():
	"""
	Visit the node and replace return statements
	with binary expressions that store the return
	statement values.
	Add a single return statement at the end
	to return the cached value.
	"""
		_method.Accept(
				ReturnValueStorageVisitor(
					self.CodeBuilder.CreateReference(_returnValue)))
		_method.Body.Add(
			ReturnStatement(
				LexicalInfo: LexicalInfo,
				Expression: self.CodeBuilder.CreateReference(_returnValue)))


DRY build.boo

DRY build.boo

solution = "MarkEmbling.Utils.sln"

configuration = "release"

test_assembly = "MarkEmbling.Utils.Tests/bin/${configuration}/MarkEmbling.Utils.Tests.dll"

bin_path = "build/bin"

package_path = "build/nuget"

target default, (compile, test, prep):

  pass

desc "Compiles the solution"

target compile:

  msbuild(file: solution, configuration: configuration, version: "4.0")

desc "Executes the tests"

target test, (compile):

  with nunit(assembly: test_assembly, toolPath: "packages/NUnit.Runners.2.6.2/tools/nunit-console.exe")

desc "Copies the binaries to the 'build/bin' directory"

target prep, (compile, test):

  rmdir(bin_path)

  with FileList("MarkEmbling.Utils/bin/${configuration}"):

    .Include("*.{dll,exe,config}")

    .ForEach def(file):

      file.CopyToDirectory(bin_path)

  with FileList("MarkEmbling.Utils.Forms/bin/${configuration}"):

    .Include("*.{dll,exe,config}")

    .ForEach def(file):

      file.CopyToDirectory(bin_path)

desc "Creates the NuGet packages"

target package, (prep):

  with nuget_pack():

    .toolPath = "packages/NuGet.CommandLine.2.6.1/tools/NuGet.exe"

    .nuspecFile = "build/nuget/MarkEmbling.Utils.nuspec"

    .outputDirectory = package_path

  with nuget_pack():

    .toolPath = "packages/NuGet.CommandLine.2.6.1/tools/NuGet.exe"

    .nuspecFile = "build/nuget/MarkEmbling.Utils.Forms.nuspec"

    .outputDirectory = package_path

solution = "MarkEmbling.Utils.sln"

configuration = "release"

test_assembly = "MarkEmbling.Utils.Tests/bin/${configuration}/MarkEmbling.Utils.Tests.dll"

target default, (compile, test, prep):

  pass

desc "Compiles the solution"

target compile:

  msbuild(file: solution, configuration: configuration, version: "4.0")

desc "Executes the tests"

target test, (compile):

  with nunit(assembly: test_assembly, toolPath: "packages/NUnit.Runners.2.6.2/tools/nunit-console.exe")

desc "Copies the binaries to the 'build/bin' directory"

target prep, (compile, test):

  rmdir("build/bin")

  with FileList("MarkEmbling.Utils/bin/${configuration}"):

    .Include("*.{dll,exe,config}")

    .ForEach def(file):

      file.CopyToDirectory("build/bin")

  with FileList("MarkEmbling.Utils.Forms/bin/${configuration}"):

    .Include("*.{dll,exe,config}")

    .ForEach def(file):

      file.CopyToDirectory("build/bin")

desc "Creates the NuGet packages"

target package, (prep):

  with nuget_pack():

    .toolPath = "packages/NuGet.CommandLine.2.6.1/tools/NuGet.exe"

    .nuspecFile = "build/nuget/MarkEmbling.Utils.nuspec"

    .outputDirectory = "build/nuget"

  with nuget_pack():

    .toolPath = "packages/NuGet.CommandLine.2.6.1/tools/NuGet.exe"

    .nuspecFile = "build/nuget/MarkEmbling.Utils.Forms.nuspec"

    .outputDirectory = "build/nuget"


BOO-1030 : smart indentation in booish
+ make auto-completion work within block:
+ misc code cleanups

git-svn-id: fcce7d6ce0ac15bf24f68871b5145d46fc068279@2973 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.Lang.Interpreter

import System
import System.Collections
import System.Collections.Generic
import System.IO
import System.Text
import System.Text.RegularExpressions
import Boo.Lang
import Boo.Lang.Compiler
import Boo.Lang.Compiler.Ast
import Boo.Lang.Compiler.TypeSystem
import Boo.Lang.Compiler.IO

class InteractiveInterpreter2(AbstractInterpreter):

	_values = {}

	_declarations = {}

	_representers = []

	[property(ShowWarnings)]
	_showWarnings = false

	[property(BlockStarters, value is not null)]
	_blockStarters = (char(':'), char('\\'),)

	[getter(LastValue)]
	_lastValue

	[property(Print, value is not null)]
	_print as callable(object) = print

	_entityNameComparer = EntityNameComparer()

	def constructor():
		super()
		Initialize()

	def constructor(parser as ICompilerStep):
		super(parser)
		Initialize()

	def Initialize():
		_disableColors = true if Environment.GetEnvironmentVariable("BOOISH_DISABLE_COLORS") is not null
		if not _disableColors: #make sure setting color does not throw an exception
			try:
				Console.ForegroundColor = ConsoleColor.DarkGray
			except:
				_disableColors = true
		_disableAutocompletion = true if Environment.GetEnvironmentVariable("BOOISH_DISABLE_AUTOCOMPLETION") is not null
		InitializeStandardReferences()
		LoadHistory()

	def Reset():
		_values.Clear()
		_declarations.Clear()
		_lastValue = null
		_indent = 0
		InitializeStandardReferences()

	public final static HISTORY_FILENAME = "booish_history"
	public final static HISTORY_CAPACITY = 100
	protected _history = System.Collections.Generic.Queue of string(HISTORY_CAPACITY)
	protected _historyFile = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), HISTORY_FILENAME)
	protected _historyIndex = 0
	protected _session = System.Collections.Generic.List of string()

	private _buffer = StringBuilder()	#buffer to be executed
	private _line = StringBuilder()		#line being edited
	private _multiline = false			#is the current line a multi-line?

	Line as string:
		get:
			return _line.ToString()

	LineLastChar as char:
		get:
			return _line.Chars[LineLen-1] if LineLen != 0
			return char('\0')

	LineLen as int:
		get:
			return _line.Length
		set:
			_line.Length = value

	LineIndentLen as int:
		get:
			return IndentChars.Length * _indent

	CurrentPrompt as string:
		get:
			if _indent > 0:
				return BlockPrompt
			return DefaultPrompt

	[property(DefaultPrompt)]
	_defaultPrompt = ">>>"

	[property(BlockPrompt)]
	_blockPrompt = "..."

	[property(IndentChars)]
	_indentChars = "    "

	[property(DisableColors)]
	_disableColors = false

	[property(DisableAutocompletion)]
	_disableAutocompletion = false

	[property(InterpreterColor)] # messages from the interpreter (not from user code)
	_interpreterColor = ConsoleColor.DarkGray

	[property(PromptColor)]
	_promptColor = ConsoleColor.DarkGreen

	[property(ExceptionColor)]
	_exceptionColor = ConsoleColor.DarkRed

	[property(SuggestionsColor)]
	_suggestionsColor = ConsoleColor.DarkYellow

	[property(SelectedSuggestionColor)]
	_selectedSuggestionColor = ConsoleColor.DarkMagenta

	[property(SelectedSuggestionIndex)]
	_selectedSuggIdx as int?

	[property(Suggestions)]
	_suggestions as (object)

	CanAutoComplete as bool:
		get:
			return _selectedSuggIdx is not null

	private _builtins as (IEntity)
	private _filter as string

	private def ConsolePrintPrompt():
		ConsolePrintPrompt(true)

	private def ConsolePrintPrompt(autoIndent as bool):
		return if _quit
		Console.ForegroundColor = _promptColor if not _disableColors
		Console.Write(CurrentPrompt)
		Console.ResetColor() if not _disableColors
		if autoIndent and CurrentPrompt == BlockPrompt:
			for i in range(_indent):
				WriteIndent()

	private def ConsolePrintMessage(msg as string):
		Console.ForegroundColor = _interpreterColor if not _disableColors
		print msg
		Console.ResetColor() if not _disableColors

	private def ConsolePrintException(e as Exception):
		Console.ForegroundColor = _exceptionColor if not _disableColors
		print e
		Console.ResetColor() if not _disableColors

	private def ConsolePrintError(msg as string):
		Console.ForegroundColor = _exceptionColor if not _disableColors
		print msg
		Console.ResetColor() if not _disableColors

	protected def ConsolePrintSuggestions():
		cursorLeft = Console.CursorLeft
		#cursorTop = Console.CursorTop
		Console.Write(Environment.NewLine)

		i = 0

		Array.Sort(_suggestions) if _suggestions isa (string)
		Array.Sort[of IEntity](_suggestions, _entityNameComparer) if _suggestions isa (IEntity)

		for s in _suggestions as (object):
			Console.ForegroundColor = _suggestionsColor if not _disableColors
			Console.Write(", ") if i > 0
			if i > 20: #TODO: maxcandidates pref + paging?
				Console.Write("... (too many candidates)")
				break
			if i == _selectedSuggIdx:
				Console.ForegroundColor = _selectedSuggestionColor if not _disableColors
			if s isa IEntity:
				Console.Write(DescribeEntity(s as IEntity))
			else:
				Console.Write(s)
			i++

		Console.ResetColor() if not _disableColors
		#Console.CursorTop = cursorTop
		Console.Write(Environment.NewLine)
		ConsolePrintPrompt(false)
		Console.Write(Line)
		Console.CursorLeft = cursorLeft

	protected def Write(s as string):
		Console.Write(s)
		_line.Append(s)

	protected def WriteIndent():
		Write(IndentChars)

	protected def Indent():
		WriteIndent()
		_indent++

	protected def Unindent():
		return if _indent == 0
		Delete(IndentChars.Length)
		_indent--

	protected def Delete(count as int): #if count is 0, forward-delete
		cx = Console.CursorLeft-len(CurrentPrompt)-count
		return if cx < LineLen and count == 0
		dcount = (count if count != 0 else 1)
		_line.Remove(cx, dcount)
		curX = Console.CursorLeft - dcount
		Console.CursorLeft = curX
		Console.Write("${_line.ToString(cx, LineLen-cx)} ")
		Console.CursorLeft = curX

	private static re_open = Regex("\\(", RegexOptions.Singleline)
	private static re_close = Regex("\\)", RegexOptions.Singleline)

	def DisplaySuggestions():
		DisplaySuggestions(Line)

	def DisplaySuggestions(query as string):
		return if DisableAutocompletion

		#TODO: FIXME: refactor to one regex?
		p_open = re_open.Matches(query).Count
		p_close = re_close.Matches(query).Count
		if p_open > p_close:
			query = query.Split(" ,(\t".ToCharArray(), 100)[-1]
		else:
			query = query.Split(" ,\t".ToCharArray(), 100)[-1]
		if query.LastIndexOf('.') > 0:
			codeToComplete = query[0:query.LastIndexOf('.')+1]
			_suggestions = SuggestCodeCompletion(codeToComplete+"__codecomplete__")
			_filter = query[query.LastIndexOf('.')+1:]
			_suggestions = array(e for e in _suggestions as (IEntity)
								unless not e.Name.StartsWith(_filter)) as (IEntity)

		if not _suggestions or 0 == len(_suggestions): #suggest a  var
			_filter = query
			_suggestions = array(var.ToString() for var in _values.Keys
									unless not var.ToString().StartsWith(_filter))

		if not _suggestions or 0 == len(_suggestions):
			_selectedSuggIdx = null
			#Console.Beep() #TODO: flash background?
		elif 1 == len(_suggestions):
			AutoComplete()
		else:
			ConsolePrintSuggestions()

	def AutoComplete():
		raise InvalidOperationException("no suggestions") if _suggestions is null or _selectedSuggIdx is null

		s = _suggestions[_selectedSuggIdx.Value] as IEntity
		completion = (s.Name[len(_filter):] if s is not null else (_suggestions[_selectedSuggIdx.Value] as string)[len(_filter):])
		Write(completion)
		if s and s.EntityType == EntityType.Method:
			Write("(")
			m = s as IMethod
			Write(')') if m.GetParameters().Length == 0

		_selectedSuggIdx = null
		_suggestions = null

	private _beforeHistory = string.Empty

	def DisplayHistory():
		if _history.Count == 0 or _historyIndex < 0 or _historyIndex > _history.Count:
			return
		Console.CursorLeft = len(CurrentPrompt)
		Console.Write(string.Empty.PadLeft(LineLen, char(' ')))
		line = _history.ToArray()[_historyIndex]
		LineLen = 0
		Console.CursorLeft = len(CurrentPrompt)
		Write(line)

	def ConsoleLoopEval():
		Console.CursorVisible = true
		ConsolePrintPrompt()

		lastChar = char('0')
		key = ConsoleKey.LeftArrow
		while not _quit:
			cki = Console.ReadKey(true)
			key = cki.Key
			keyChar = cki.KeyChar
			control = false

			newLine = keyChar in Environment.NewLine

			if char.IsControl(keyChar):
				control = true
				if keyChar == char('\t'):
					if LineLen > 0 and (char.IsLetterOrDigit(LineLastChar) or LineLastChar == char('.')):
						_selectedSuggIdx = 0
						DisplaySuggestions()
					else:
						Indent()

				#line-editing support
				if not _multiline and LineLen > 0:
					if Console.CursorLeft > len(CurrentPrompt):
						if key == ConsoleKey.Backspace:
							if _indent > 0 and LineLen == LineIndentLen:
								Unindent()
							else:
								Delete(1)
						elif key == ConsoleKey.LeftArrow:
							Console.CursorLeft--
					if key == ConsoleKey.Delete:
						Delete(0)
					elif key == ConsoleKey.RightArrow:
						if Console.CursorLeft < (len(CurrentPrompt)+LineLen):
							Console.CursorLeft++
					elif key == ConsoleKey.Home:
						Console.CursorLeft = len(CurrentPrompt)
					elif key == ConsoleKey.End:
						Console.CursorLeft = len(CurrentPrompt) + LineLen

				#history support
				if key == ConsoleKey.UpArrow:
					if _historyIndex > 0:
						_historyIndex--
						DisplayHistory()
				elif key == ConsoleKey.DownArrow:
					if _historyIndex < _history.Count-1:
						_historyIndex++
						DisplayHistory()

				#auto-completion support
				if CanAutoComplete:
					if key == ConsoleKey.LeftArrow:
						if _selectedSuggIdx > 0:
							_selectedSuggIdx--
						else:
							_selectedSuggIdx = len(_suggestions)
						DisplaySuggestions()
					elif key == ConsoleKey.RightArrow:
						if _selectedSuggIdx <= len(_suggestions):
							_selectedSuggIdx++
						else:
							_selectedSuggIdx = 0
						DisplaySuggestions()
					if newLine:
						AutoComplete()
						continue
				if not newLine:
					continue

			_selectedSuggIdx = null

			cx = Console.CursorLeft-len(CurrentPrompt)
			#multi-line?
			if cx < 0 or LineLen >= Console.WindowWidth-len(CurrentPrompt):
				cx = LineLen
				_multiline = true

			if not newLine:
				#line-editing support
				if cx < LineLen and not _multiline:
					_line.Insert(cx, keyChar) if not control
					Console.Write(_line.ToString(cx, LineLen-cx))
					Console.CursorLeft = len(CurrentPrompt)+cx+1
				else:
					_line.Append(keyChar) if not control
					Console.Write(keyChar)

			if newLine:
				_multiline = false
				Console.Write(Environment.NewLine)

				if not TryRunCommand(Line):
					_buffer.Append(Line)
					_buffer.Append(Environment.NewLine)
					AddToHistory(Line)

					_indent++ if LineLastChar in _blockStarters
					_indent-- if Line.EndsWith(IndentChars+"pass")

					if _indent == 0:
						try:
							InternalLoopEval(_buffer.ToString())
							_buffer.Length = 0 #truncate buffer
						except x as System.Reflection.TargetInvocationException:
							ConsolePrintException(x.InnerException)
						except x:
							ConsolePrintException(x)

				LineLen = 0 #truncate line
				ConsolePrintPrompt()

			lastChar = keyChar

		SaveHistory()
		DisplayGoodbye()

	/* returns false if no command has been processed, true otherwise */
	def TryRunCommand(line as string):
		if not line.StartsWith("/"):
			return false

		cmd = line.Split()

		if len(cmd) == 1:
			if cmd[0] == "/q" or cmd[0] == "/quit":
				quit()
			elif cmd[0] == "/?" or cmd[0] == "/h" or cmd[0] == "/help":
				DisplayHelp()
				ConsolePrintPrompt()
			elif cmd[0] == "/g" or cmd[0] == "/globals":
				InternalLoopEval("globals()")
				ConsolePrintPrompt()
			else:
				return false

		elif len(cmd) == 2:
			if cmd[0] == "/l" or cmd[0] == "/load":
				load(cmd[1])
				ConsolePrintPrompt()
			elif cmd[0] == "/s" or cmd[0] == "/save":
				save(cmd[1])
				ConsolePrintPrompt()
			else:
				return false

		else:
			return false
		return true

	private _indent as int = 0

	def LoopEval(code as string):
		using console = ConsoleCapture():
			result = InternalLoopEval(code)
			for line in System.IO.StringReader(console.ToString()):
				_print(line)
		return result

	private def InternalLoopEval(code as string):
		result = self.Eval(code)
		if ShowWarnings:
			self.DisplayProblems(result.Warnings)
		if not self.DisplayProblems(result.Errors):
			ProcessLastValue()
			_session.Add(code)
		return result

	private def ProcessLastValue():
		_ = self.LastValue
		if _ is not null:
			_print(repr(_))
			SetValue("_", _)

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

	override def SetLastValue(value):
		_lastValue = value

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

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

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

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

	def DisplayProblems(problems as ICollection):
		return if problems is null or problems.Count == 0
		Console.ForegroundColor = _exceptionColor if not _disableColors
		for problem as duck in problems:
			markLocation(problem.LexicalInfo)
			type = ("WARNING", "ERROR")[problem isa CompilerError]
			_print("${type}: ${problem.Message}")
		Console.ResetColor() if not _disableColors
		if problems.Count > 0:
			return true
		return false

	private def markLocation(location as LexicalInfo):
		pos = location.Column
		_print("---" + "-" * pos + "^") if pos > 0

	private def InitializeStandardReferences():
		SetValue("interpreter", self)
		SetValue("dir", dir)
		SetValue("describe", describe)
		SetValue("print", { value | _print(value) })
		SetValue("load", load)
		SetValue("globals", globals)
		SetValue("quit", quit)
		SetValue("getRootNamespace", Namespace.GetRootNamespace)

	def DisplayLogo():
		Console.ForegroundColor = _interpreterColor	if not _disableColors
		print """Welcome to booish, an interpreter for the boo programming language.
Running boo ${BooVersion} in CLR v${Environment.Version}.

Enter boo code in the prompt below (or type /help)."""
		Console.ResetColor() if not _disableColors

	def DisplayHelp():
		Console.ForegroundColor = _interpreterColor	if not _disableColors
		print """The following builtin functions are available :
    dir(type) : returns the members of a type
    describe(type) : describe a type as boo code
    globals() or /g : returns names of all variables known to interpreter
    load(file) or /l file : evals an external boo file
    save(file) or /s file : writes your current booish session into file
    quit() or /q : exits the interpreter"""
		Console.ResetColor() if not _disableColors

	def DisplayGoodbye():	// booish is friendly
		Console.ForegroundColor = _interpreterColor if not _disableColors
		print ""
		print "All your boo are belong to us!"
		Console.ResetColor() if not _disableColors

	def LoadHistory():
		try:
			using history = File.OpenText(_historyFile):
				while line = history.ReadLine():
					AddToHistory(line)
		except:
			ConsolePrintError("Cannot load history from '${_historyFile}'")

	def AddToHistory(line as string):
		return if 0 == (len(line) - len(Environment.NewLine))
		_history.Dequeue() if _history.Count >= HISTORY_CAPACITY
		line = line.Replace(Environment.NewLine, "")
		_history.Enqueue(line)
		_historyIndex = _history.Count

	def SaveHistory():
		try:
			using sw = System.IO.File.CreateText(_historyFile):
				for line in _history:
					sw.WriteLine(line)
		except:
			ConsolePrintError("Cannot save history to '${_historyFile}'.")

	def globals():
		return array(string, _values.Keys)

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

	def load([required] path as string):
		if path.EndsWith(".boo"):
			ConsolePrintMessage("Evaluating '${path}' ...")
			result = EvalCompilerInput(FileInput(path))
			if ShowWarnings:
				DisplayProblems(result.Warnings)
			if not DisplayProblems(result.Errors):
				ProcessLastValue()
		else:
			try:
				ConsolePrintMessage("Adding reference to '${path}'")
				References.Add(System.Reflection.Assembly.LoadFrom(path))
			except e:
				ConsolePrintError(e.Message)

	def save([required] path as string):
		if path is string.Empty:
			path = "booish_session.boo"
		elif not path.EndsWith(".boo"):
			path = "${path}.boo"
		try:
			using sw = System.IO.File.CreateText(path):
				for line in _session:
					sw.Write(line)
			ConsolePrintMessage("Session saved to '${path}'.")
		except:
			ConsolePrintError("Cannot save to '${path}'. Check if path is valid and has correct permissions.")

	def describe(obj):
		type = (obj as Type) or obj.GetType()
		DescribeType("    ", type)

	private _quit = false
	def quit():
		_quit = true

	def DescribeType(indent as string, type as Type):
		if type.IsInterface:
			typeDef = "interface"
			baseTypes = array(GetBooTypeName(t) for t in type.GetInterfaces())
		else:
			typeDef = "class"
			baseTypes = (GetBooTypeName(type.BaseType),) + array(GetBooTypeName(t) for t in type.GetInterfaces())

		_print("${typeDef} ${type.Name}(${join(baseTypes, ', ')}):")
		_print("")

		for ctor in type.GetConstructors():
			_print("${indent}def constructor(${DescribeParameters(ctor.GetParameters())})")
			_print("")

		sortByName = def (lhs as Reflection.MemberInfo, rhs as Reflection.MemberInfo):
			return lhs.Name.CompareTo(rhs.Name)

		for f as Reflection.FieldInfo in List(type.GetFields()).Sort(sortByName):
			_print("${indent}public ${DescribeField(f)}")
			_print("")

		for p as Reflection.PropertyInfo in List(type.GetProperties()).Sort(sortByName):
			_print("${indent}${DescribeProperty(p)}:")
			_print("${indent}${indent}get") if p.GetGetMethod() is not null
			_print("${indent}${indent}set") if p.GetSetMethod() is not null
			_print("")

		for m as Reflection.MethodInfo in List(type.GetMethods()).Sort(sortByName):
			continue if m.IsSpecialName
			_print("${indent}${DescribeMethod(m)}")
			_print("")

		for e as Reflection.EventInfo in List(type.GetEvents()).Sort(sortByName):
			_print("${indent}${DescribeEvent(e)}")
			_print("")

	static def DescribeEntity(entity as IEntity):
		method = entity as ExternalMethod
		if method is not null:
			return InteractiveInterpreter.DescribeMethod(method.MethodInfo)
		field = entity as ExternalField
		if field is not null:
			return InteractiveInterpreter.DescribeField(field.FieldInfo)
		property = entity as ExternalProperty
		if property is not null:
			return InteractiveInterpreter.DescribeProperty(property.PropertyInfo)
		e = entity as ExternalEvent
		if e is not null:
			return InteractiveInterpreter.DescribeEvent(e.EventInfo)
		return entity.ToString()

	static def DescribeEvent(e as Reflection.EventInfo):
		return "${DescribeModifiers(e)}event ${e.Name} as ${e.EventHandlerType}"

	static def DescribeProperty(p as Reflection.PropertyInfo):
		modifiers = DescribeModifiers(p)
		params = DescribePropertyParameters(p.GetIndexParameters())
		return "${modifiers}${p.Name}${params} as ${GetBooTypeName(p.PropertyType)}"

	static def DescribeField(f as Reflection.FieldInfo):
		return "${DescribeModifiers(f)}${f.Name} as ${GetBooTypeName(f.FieldType)}"

	static def DescribeMethod(m as Reflection.MethodInfo):
		returnType = GetBooTypeName(m.ReturnType)
		modifiers = DescribeModifiers(m)
		return "${modifiers}def ${m.Name}(${DescribeParameters(m.GetParameters())}) as ${returnType}"

	static def DescribeModifiers(f as Reflection.FieldInfo):
		return "static " if f.IsStatic
		return ""

	static def DescribeModifiers(m as Reflection.MethodBase):
		return "static " if m.IsStatic
		return ""

	static def DescribeModifiers(e as Reflection.EventInfo):
		return DescribeModifiers(e.GetAddMethod(true) or e.GetRemoveMethod(true))

	static def DescribeModifiers(p as Reflection.PropertyInfo):
		accessor = p.GetGetMethod(true) or p.GetSetMethod(true)
		return DescribeModifiers(accessor)

	static def DescribePropertyParameters(parameters as (Reflection.ParameterInfo)):
		return "" if 0 == len(parameters)
		return "(${DescribeParameters(parameters)})"

	static def DescribeParameters(parameters as (Reflection.ParameterInfo)):
		return join(DescribeParameter(p) for p in parameters, ", ")

	static def DescribeParameter(p as Reflection.ParameterInfo):
		return "${p.Name} as ${GetBooTypeName(p.ParameterType)}"

	static def GetBooTypeName(type as System.Type) as string:
		return "(${GetBooTypeName(type.GetElementType())})" if type.IsArray
		return "object" if object is type
		return "string" if string is type
		return "void" if void is type
		return "bool" if bool is type
		return "byte" if byte is type
		return "char" if char is type
		return "sbyte" if sbyte is type
		return "short" if short is type
		return "ushort" if ushort is type
		return "int" if int is type
		return "uint" if uint is type
		return "long" if long is type
		return "ulong" if ulong is type
		return "single" if single is type
		return "double" if double is type
		return "date" if date is type
		return "timespan" if timespan is type
		return "regex" if regex is type
		return type.FullName

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

	def repr(value, writer as System.IO.TextWriter):
		return unless value is not null
		InitializeRepresenters() if 0 == len(_representers)
		GetBestRepresenter(value.GetType())(value, writer)

	private def InitializeRepresenters():
		AddRepresenter(string) do (value as string, writer as TextWriter):
			Visitors.BooPrinterVisitor.WriteStringLiteral(value, writer)

		AddRepresenter(bool) do (value as bool, writer as TextWriter):
			writer.Write(("false", "true")[value])

		AddRepresenter(Array) do (a as Array, writer as TextWriter):
			writer.Write("(")
			RepresentItems(a, writer)
			writer.Write(")")

		AddRepresenter(Delegate) do (d as Delegate, writer as TextWriter):
			method = d.Method
			writer.Write(method.DeclaringType.FullName)
			writer.Write(".")
			writer.Write(method.Name)

		AddRepresenter(IDictionary) do (value as IDictionary, writer as TextWriter):
			writer.Write("{")
			i = 0
			for key in value.Keys:
				writer.Write(", ") if i
				repr(key, writer)
				writer.Write(": ")
				repr(value[key], writer)
				++i
			writer.Write("}")

		AddRepresenter(IList) do (value as IList, writer as TextWriter):
			writer.Write("[")
			RepresentItems(value, writer)
			writer.Write("]")

		AddRepresenter(object) do (value, writer as TextWriter):
			writer.Write(value)

	private def RepresentItems(items, writer as TextWriter):
		i = 0
		for item in items:
			writer.Write(", ") if i > 0
			repr(item, writer)
			++i

	callable Representer(value, writer as TextWriter)

	private def AddRepresenter(type as Type, value as Representer):
		_representers.Add((type, value))

	def GetBestRepresenter(type as Type) as Representer:
		for key as Type, value in _representers:
			return value if key.IsAssignableFrom(type)
		assert false, "An appropriate representer could not be found!"

class EntityNameComparer(IComparer of IEntity):
	def Compare(a as IEntity, b as IEntity) as int:
		return string.Compare(a.Name, b.Name)

#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.Lang.Interpreter

import System
import System.Collections
import System.Collections.Generic
import System.IO
import System.Text
import System.Text.RegularExpressions
import Boo.Lang
import Boo.Lang.Compiler
import Boo.Lang.Compiler.Ast
import Boo.Lang.Compiler.TypeSystem
import Boo.Lang.Compiler.IO

class InteractiveInterpreter2(AbstractInterpreter):

	_values = {}

	_declarations = {}

	_representers = []

	[property(ShowWarnings)]
	_showWarnings = false

	[property(BlockStarters, value is not null)]
	_blockStarters = (char(':'), char('\\'),)

	[getter(LastValue)]
	_lastValue

	[property(Print, value is not null)]
	_print as callable(object) = print

	_entityNameComparer = EntityNameComparer()

	def constructor():
		super()
		Initialize()

	def constructor(parser as ICompilerStep):
		super(parser)
		Initialize()

	def Initialize():
		_disableColors = true if Environment.GetEnvironmentVariable("BOOISH_DISABLE_COLORS") is not null
		if not _disableColors: #make sure setting color does not throw an exception
			try:
				Console.ForegroundColor = ConsoleColor.DarkGray
			except:
				_disableColors = true
		_disableAutocompletion = true if Environment.GetEnvironmentVariable("BOOISH_DISABLE_AUTOCOMPLETION") is not null
		InitializeStandardReferences()
		LoadHistory()

	def Reset():
		_values.Clear()
		_declarations.Clear()
		_lastValue = null
		_indent = 0
		InitializeStandardReferences()

	public final static HISTORY_FILENAME = "booish_history"
	public final static HISTORY_CAPACITY = 100
	protected _history = System.Collections.Generic.Queue of string(HISTORY_CAPACITY)
	protected _historyFile = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), HISTORY_FILENAME)
	protected _historyIndex = 0
	protected _session = System.Collections.Generic.List of string()

	private _buffer = StringBuilder()	#buffer to be executed
	private _line = StringBuilder()		#line being edited
	private _multiline = false			#is the current line a multi-line?

	CurrentPrompt as string:
		get:
			if _indent > 0:
				return BlockPrompt
			return DefaultPrompt

	[property(DefaultPrompt)]
	_defaultPrompt = ">>>"

	[property(BlockPrompt)]
	_blockPrompt = "..."

	[property(DisableColors)]
	_disableColors = false

	[property(DisableAutocompletion)]
	_disableAutocompletion = false

	[property(InterpreterColor)] # messages from the interpreter (not from user code)
	_interpreterColor = ConsoleColor.DarkGray

	[property(PromptColor)]
	_promptColor = ConsoleColor.DarkGreen

	[property(ExceptionColor)]
	_exceptionColor = ConsoleColor.DarkRed

	[property(SuggestionsColor)]
	_suggestionsColor = ConsoleColor.DarkYellow

	[property(SelectedSuggestionColor)]
	_selectedSuggestionColor = ConsoleColor.DarkMagenta

	[property(SelectedSuggestionIndex)]
	_selectedSuggIdx as int?

	[property(Suggestions)]
	_suggestions as (object)

	CanAutoComplete as bool:
		get:
			return _selectedSuggIdx is not null

	private _builtins as (IEntity)
	private _filter as string

	private def ConsolePrintPrompt():
		return if _quit
		Console.ForegroundColor = _promptColor if not _disableColors
		Console.Write(CurrentPrompt)
		#TODO: automatic indentation option ?
		#for i in range(_indent):
		#	_line.Append("\t")
		#	Console.Write("\t")
		Console.ResetColor() if not _disableColors
		_selectedSuggIdx = null
		_line.Length = 0
		_buffer.Length = 0

	private def ConsolePrintMessage(msg as string):
		Console.ForegroundColor = _interpreterColor if not _disableColors
		print msg
		Console.ResetColor() if not _disableColors

	private def ConsolePrintException(e as Exception):
		Console.ForegroundColor = _exceptionColor if not _disableColors
		print e
		Console.ResetColor() if not _disableColors

	private def ConsolePrintError(msg as string):
		Console.ForegroundColor = _exceptionColor if not _disableColors
		print msg
		Console.ResetColor() if not _disableColors

	protected def ConsolePrintSuggestions():
		cursorLeft = Console.CursorLeft
		#cursorTop = Console.CursorTop
		Console.Write(Environment.NewLine)

		i = 0

		Array.Sort(_suggestions) if _suggestions isa (string)
		Array.Sort[of IEntity](_suggestions, _entityNameComparer) if _suggestions isa (IEntity)

		for s in _suggestions as (object):
			Console.ForegroundColor = _suggestionsColor if not _disableColors
			Console.Write(", ") if i > 0
			if i > 20: #TODO: maxcandidates pref + paging?
				Console.Write("... (too much candidates)")
				break
			if i == _selectedSuggIdx:
				Console.ForegroundColor = _selectedSuggestionColor if not _disableColors
			if s isa IEntity:
				Console.Write(DescribeEntity(s as IEntity))
			else:
				Console.Write(s)
			i++

		Console.ResetColor() if not _disableColors
		#Console.CursorTop = cursorTop
		Console.Write(Environment.NewLine)
		ConsolePrintPrompt()
		Console.Write(_line.ToString())
		Console.CursorLeft = cursorLeft

	protected def Write(s as string):
		Console.Write(s)
		_line.Append(s)

	private static re_open = Regex("\\(", RegexOptions.Singleline)
	private static re_close = Regex("\\)", RegexOptions.Singleline)

	def DisplaySuggestions(query as string):
		return if DisableAutocompletion

		#TODO: FIXME: refactor to one regex?
		p_open = re_open.Matches(query).Count
		p_close = re_close.Matches(query).Count
		if p_open > p_close:
			query = query.Split(" ,(\t".ToCharArray(), 100)[-1]
		else:
			query = query.Split(" ,\t".ToCharArray(), 100)[-1]
		if query.LastIndexOf('.') > 0:
			codeToComplete = query[0:query.LastIndexOf('.')+1]
			_suggestions = SuggestCodeCompletion(codeToComplete+"__codecomplete__")
			_filter = query[query.LastIndexOf('.')+1:]
			_suggestions = array(e for e in _suggestions as (IEntity)
								unless not e.Name.StartsWith(_filter)) as (IEntity)

		if not _suggestions or 0 == len(_suggestions): #suggest a  var
			_filter = query
			_suggestions = array(var.ToString() for var in _values.Keys
									unless not var.ToString().StartsWith(_filter))

		if not _suggestions or 0 == len(_suggestions):
			_selectedSuggIdx = null
			#Console.Beep() #TODO: flash background?
		elif 1 == len(_suggestions):
			AutoComplete()
		else:
			ConsolePrintSuggestions()

	def AutoComplete():
		raise InvalidOperationException("no suggestions") if _suggestions is null or _selectedSuggIdx is null

		s = _suggestions[_selectedSuggIdx.Value] as IEntity
		completion = (s.Name[len(_filter):] if s is not null else (_suggestions[_selectedSuggIdx.Value] as string)[len(_filter):])
		Write(completion)
		if s and s.EntityType == EntityType.Method:
			Write("(")
			m = s as IMethod
			Write(')') if m.GetParameters().Length == 0

		_selectedSuggIdx = null
		_suggestions = null

	private _beforeHistory = string.Empty

	def DisplayHistory():
		if _history.Count == 0 or _historyIndex < 0 or _historyIndex > _history.Count:
			return
		Console.CursorLeft = len(CurrentPrompt)
		Console.Write(string.Empty.PadLeft(_line.Length, char(' ')))
		line = _history.ToArray()[_historyIndex]
		_line.Length = 0
		Console.CursorLeft = len(CurrentPrompt)
		Write(line)

	def ConsoleLoopEval():
		Console.CursorVisible = true
		ConsolePrintPrompt()

		lastChar = char('0')
		key = ConsoleKey.LeftArrow
		while not _quit:
			cki = Console.ReadKey(true)
			key = cki.Key
			keyChar = cki.KeyChar
			control = false

			newLine = keyChar in Environment.NewLine

			if char.IsControl(keyChar):
				control = true
				if keyChar == char('\t'):
					test = char(' ')
					test = _line.ToString()[_line.Length-1] if _line.Length > 0
					if char.IsLetterOrDigit(test) or test == char('.'):
						_selectedSuggIdx = 0
						DisplaySuggestions(_line.ToString())
					else:
						Write("    ")

				#line-editing support
				if not _multiline:
					if key == ConsoleKey.Backspace:
						if Console.CursorLeft > len(CurrentPrompt) and _line.Length > 0:
							cx = Console.CursorLeft-len(CurrentPrompt)-1
							_line.Remove(cx, 1)
							cx2 = --Console.CursorLeft
							Console.Write("${_line.ToString(cx, _line.Length-cx)} ")
							Console.CursorLeft = cx2
					elif key == ConsoleKey.Delete:
						if Console.CursorLeft >= len(CurrentPrompt) and _line.Length > 0:
							cx = Console.CursorLeft-len(CurrentPrompt)
							if cx < _line.Length:
								_line.Remove(cx, 1)
								cx2 = Console.CursorLeft
								Console.Write("${_line.ToString(cx, _line.Length-cx)} ")
								Console.CursorLeft = cx2
					elif key == ConsoleKey.LeftArrow:
						if Console.CursorLeft > len(CurrentPrompt) and _line.Length > 0:
							Console.CursorLeft--
					elif key == ConsoleKey.RightArrow:
						if Console.CursorLeft < (len(CurrentPrompt)+_line.Length):
							Console.CursorLeft++
					elif key == ConsoleKey.Home:
						Console.CursorLeft = len(CurrentPrompt)
					elif key == ConsoleKey.End:
						Console.CursorLeft = len(CurrentPrompt) + _line.Length

				#history support
				if key == ConsoleKey.UpArrow:
					if _historyIndex > 0:
						_historyIndex--
						DisplayHistory()
				elif key == ConsoleKey.DownArrow:
					if _historyIndex < _history.Count-1:
						_historyIndex++
						DisplayHistory()

				#auto-completion support
				if CanAutoComplete:
					if key == ConsoleKey.LeftArrow:
						if _selectedSuggIdx > 0:
							_selectedSuggIdx--
						else:
							_selectedSuggIdx = len(_suggestions)
						DisplaySuggestions(_line.ToString())
					elif key == ConsoleKey.RightArrow:
						if _selectedSuggIdx <= len(_suggestions):
							_selectedSuggIdx++
						else:
							_selectedSuggIdx = 0
						DisplaySuggestions(_line.ToString())
					if newLine:
						AutoComplete()
						continue
				if not newLine:
					continue

			_selectedSuggIdx = null

			cx = Console.CursorLeft-len(CurrentPrompt)
			#multi-line?
			if cx < 0 or _line.Length >= Console.WindowWidth-len(CurrentPrompt):
				cx = _line.Length
				_multiline = true

			if not newLine:
				#line-editing support
				if cx < _line.Length and not _multiline:
					_line.Insert(cx, keyChar) if not control
					Console.Write(_line.ToString(cx, _line.Length-cx))
					Console.CursorLeft = len(CurrentPrompt)+cx+1
				else:
					_line.Append(keyChar) if not control
					Console.Write(keyChar)

			if newLine:
				_multiline = false
				Console.Write(Environment.NewLine)

				line = _line.ToString()

				if not TryRunCommand(line):
					_buffer.Append(line)
					_buffer.Append(Environment.NewLine)
					AddToHistory(line)

					try:
						if len(line) > 0:
							_indent++ if line[len(line)-1] in _blockStarters
							_indent-- if line.EndsWith(keyChar.ToString()) and _indent > 0
						elif _indent > 0:
							_indent--
						if _indent == 0:
							InternalLoopEval(_buffer.ToString())
							_buffer.Length = 0 #truncate buffer
					except x as System.Reflection.TargetInvocationException:
						ConsolePrintException(x.InnerException)
					except x:
						ConsolePrintException(x)

					_line.Length = 0 #truncate line
					ConsolePrintPrompt()

			lastChar = keyChar

		SaveHistory()
		DisplayGoodbye()

	/* returns false if no command has been processed, true otherwise */
	def TryRunCommand(line as string):
		if not line.StartsWith("/"):
			return false

		cmd = line.Split()

		if len(cmd) == 1:
			if cmd[0] == "/q" or cmd[0] == "/quit":
				quit()
			elif cmd[0] == "/?" or cmd[0] == "/h" or cmd[0] == "/help":
				DisplayHelp()
				ConsolePrintPrompt()
			elif cmd[0] == "/g" or cmd[0] == "/globals":
				InternalLoopEval("globals()")
				ConsolePrintPrompt()
			else:
				return false

		elif len(cmd) == 2:
			if cmd[0] == "/l" or cmd[0] == "/load":
				load(cmd[1])
				ConsolePrintPrompt()
			elif cmd[0] == "/s" or cmd[0] == "/save":
				save(cmd[1])
				ConsolePrintPrompt()
			else:
				return false

		else:
			return false
		return true

	private _indent as int = 0

	def LoopEval(code as string):
		using console = ConsoleCapture():
			result = InternalLoopEval(code)
			for line in System.IO.StringReader(console.ToString()):
				_print(line)
		return result

	private def InternalLoopEval(code as string):
		result = self.Eval(code)
		if ShowWarnings:
			self.DisplayProblems(result.Warnings)
		if not self.DisplayProblems(result.Errors):
			ProcessLastValue()
			_session.Add(code)
		return result

	private def ProcessLastValue():
		_ = self.LastValue
		if _ is not null:
			_print(repr(_))
			SetValue("_", _)

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

	override def SetLastValue(value):
		_lastValue = value

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

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

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

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

	def DisplayProblems(problems as ICollection):
		return if problems is null or problems.Count == 0
		Console.ForegroundColor = _exceptionColor if not _disableColors
		for problem as duck in problems:
			markLocation(problem.LexicalInfo)
			type = ("WARNING", "ERROR")[problem isa CompilerError]
			_print("${type}: ${problem.Message}")
		Console.ResetColor() if not _disableColors
		if problems.Count > 0:
			return true
		return false

	private def markLocation(location as LexicalInfo):
		pos = location.Column
		_print("---" + "-" * pos + "^") if pos > 0

	private def InitializeStandardReferences():
		SetValue("interpreter", self)
		SetValue("dir", dir)
		SetValue("describe", describe)
		SetValue("print", { value | _print(value) })
		SetValue("load", load)
		SetValue("globals", globals)
		SetValue("quit", quit)
		SetValue("getRootNamespace", Namespace.GetRootNamespace)

	def DisplayLogo():
		Console.ForegroundColor = _interpreterColor	if not _disableColors
		print """Welcome to booish, an interpreter for the boo programming language.
Running boo ${BooVersion} in CLR v${Environment.Version}.

Enter boo code in the prompt below (or type /help)."""
		Console.ResetColor() if not _disableColors

	def DisplayHelp():
		Console.ForegroundColor = _interpreterColor	if not _disableColors
		print """The following builtin functions are available :
    dir(type) : returns the members of a type
    describe(type) : describe a type as boo code
    globals() or /g : returns names of all variables known to interpreter
    load(file) or /l file : evals an external boo file
    save(file) or /s file : writes your current booish session into file
    quit() or /q : exits the interpreter"""
		Console.ResetColor() if not _disableColors

	def DisplayGoodbye():	// booish is friendly
		Console.ForegroundColor = _interpreterColor if not _disableColors
		print ""
		print "All your boo are belong to us!"
		Console.ResetColor() if not _disableColors

	def LoadHistory():
		try:
			using history = File.OpenText(_historyFile):
				while line = history.ReadLine():
					AddToHistory(line)
		except:
			ConsolePrintError("Cannot load history from '${_historyFile}'")

	def AddToHistory(line as string):
		return if 0 == (len(line) - len(Environment.NewLine))
		_history.Dequeue() if _history.Count >= HISTORY_CAPACITY
		line = line.Replace(Environment.NewLine, "")
		_history.Enqueue(line)
		_historyIndex = _history.Count

	def SaveHistory():
		try:
			using sw = System.IO.File.CreateText(_historyFile):
				for line in _history:
					sw.WriteLine(line)
		except:
			ConsolePrintError("Cannot save history to '${_historyFile}'.")

	def globals():
		return array(string, _values.Keys)

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

	def load([required] path as string):
		if path.EndsWith(".boo"):
			ConsolePrintMessage("Evaluating '${path}' ...")
			result = EvalCompilerInput(FileInput(path))
			if ShowWarnings:
				DisplayProblems(result.Warnings)
			if not DisplayProblems(result.Errors):
				ProcessLastValue()
		else:
			try:
				ConsolePrintMessage("Adding reference to '${path}'")
				References.Add(System.Reflection.Assembly.LoadFrom(path))
			except e:
				ConsolePrintError(e.Message)

	def save([required] path as string):
		if path is string.Empty:
			path = "booish_session.boo"
		elif not path.EndsWith(".boo"):
			path = "${path}.boo"
		try:
			using sw = System.IO.File.CreateText(path):
				for line in _session:
					sw.Write(line)
			ConsolePrintMessage("Session saved to '${path}'.")
		except:
			ConsolePrintError("Cannot save to '${path}'. Check if path is valid and has correct permissions.")

	def describe(obj):
		type = (obj as Type) or obj.GetType()
		DescribeType("    ", type)

	private _quit = false
	def quit():
		_quit = true

	def DescribeType(indent as string, type as Type):
		if type.IsInterface:
			typeDef = "interface"
			baseTypes = array(GetBooTypeName(t) for t in type.GetInterfaces())
		else:
			typeDef = "class"
			baseTypes = (GetBooTypeName(type.BaseType),) + array(GetBooTypeName(t) for t in type.GetInterfaces())

		_print("${typeDef} ${type.Name}(${join(baseTypes, ', ')}):")
		_print("")

		for ctor in type.GetConstructors():
			_print("${indent}def constructor(${DescribeParameters(ctor.GetParameters())})")
			_print("")

		sortByName = def (lhs as Reflection.MemberInfo, rhs as Reflection.MemberInfo):
			return lhs.Name.CompareTo(rhs.Name)

		for f as Reflection.FieldInfo in List(type.GetFields()).Sort(sortByName):
			_print("${indent}public ${DescribeField(f)}")
			_print("")

		for p as Reflection.PropertyInfo in List(type.GetProperties()).Sort(sortByName):
			_print("${indent}${DescribeProperty(p)}:")
			_print("${indent}${indent}get") if p.GetGetMethod() is not null
			_print("${indent}${indent}set") if p.GetSetMethod() is not null
			_print("")

		for m as Reflection.MethodInfo in List(type.GetMethods()).Sort(sortByName):
			continue if m.IsSpecialName
			_print("${indent}${DescribeMethod(m)}")
			_print("")

		for e as Reflection.EventInfo in List(type.GetEvents()).Sort(sortByName):
			_print("${indent}${DescribeEvent(e)}")
			_print("")

	static def DescribeEntity(entity as IEntity):
		method = entity as ExternalMethod
		if method is not null:
			return InteractiveInterpreter.DescribeMethod(method.MethodInfo)
		field = entity as ExternalField
		if field is not null:
			return InteractiveInterpreter.DescribeField(field.FieldInfo)
		property = entity as ExternalProperty
		if property is not null:
			return InteractiveInterpreter.DescribeProperty(property.PropertyInfo)
		e = entity as ExternalEvent
		if e is not null:
			return InteractiveInterpreter.DescribeEvent(e.EventInfo)
		return entity.ToString()

	static def DescribeEvent(e as Reflection.EventInfo):
		return "${DescribeModifiers(e)}event ${e.Name} as ${e.EventHandlerType}"

	static def DescribeProperty(p as Reflection.PropertyInfo):
		modifiers = DescribeModifiers(p)
		params = DescribePropertyParameters(p.GetIndexParameters())
		return "${modifiers}${p.Name}${params} as ${GetBooTypeName(p.PropertyType)}"

	static def DescribeField(f as Reflection.FieldInfo):
		return "${DescribeModifiers(f)}${f.Name} as ${GetBooTypeName(f.FieldType)}"

	static def DescribeMethod(m as Reflection.MethodInfo):
		returnType = GetBooTypeName(m.ReturnType)
		modifiers = DescribeModifiers(m)
		return "${modifiers}def ${m.Name}(${DescribeParameters(m.GetParameters())}) as ${returnType}"

	static def DescribeModifiers(f as Reflection.FieldInfo):
		return "static " if f.IsStatic
		return ""

	static def DescribeModifiers(m as Reflection.MethodBase):
		return "static " if m.IsStatic
		return ""

	static def DescribeModifiers(e as Reflection.EventInfo):
		return DescribeModifiers(e.GetAddMethod(true) or e.GetRemoveMethod(true))

	static def DescribeModifiers(p as Reflection.PropertyInfo):
		accessor = p.GetGetMethod(true) or p.GetSetMethod(true)
		return DescribeModifiers(accessor)

	static def DescribePropertyParameters(parameters as (Reflection.ParameterInfo)):
		return "" if 0 == len(parameters)
		return "(${DescribeParameters(parameters)})"

	static def DescribeParameters(parameters as (Reflection.ParameterInfo)):
		return join(DescribeParameter(p) for p in parameters, ", ")

	static def DescribeParameter(p as Reflection.ParameterInfo):
		return "${p.Name} as ${GetBooTypeName(p.ParameterType)}"

	static def GetBooTypeName(type as System.Type) as string:
		return "(${GetBooTypeName(type.GetElementType())})" if type.IsArray
		return "object" if object is type
		return "string" if string is type
		return "void" if void is type
		return "bool" if bool is type
		return "byte" if byte is type
		return "char" if char is type
		return "sbyte" if sbyte is type
		return "short" if short is type
		return "ushort" if ushort is type
		return "int" if int is type
		return "uint" if uint is type
		return "long" if long is type
		return "ulong" if ulong is type
		return "single" if single is type
		return "double" if double is type
		return "date" if date is type
		return "timespan" if timespan is type
		return "regex" if regex is type
		return type.FullName

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

	def repr(value, writer as System.IO.TextWriter):
		return unless value is not null
		InitializeRepresenters() if 0 == len(_representers)
		GetBestRepresenter(value.GetType())(value, writer)

	private def InitializeRepresenters():
		AddRepresenter(string) do (value as string, writer as TextWriter):
			Visitors.BooPrinterVisitor.WriteStringLiteral(value, writer)

		AddRepresenter(bool) do (value as bool, writer as TextWriter):
			writer.Write(("false", "true")[value])

		AddRepresenter(Array) do (a as Array, writer as TextWriter):
			writer.Write("(")
			RepresentItems(a, writer)
			writer.Write(")")

		AddRepresenter(Delegate) do (d as Delegate, writer as TextWriter):
			method = d.Method
			writer.Write(method.DeclaringType.FullName)
			writer.Write(".")
			writer.Write(method.Name)

		AddRepresenter(IDictionary) do (value as IDictionary, writer as TextWriter):
			writer.Write("{")
			i = 0
			for key in value.Keys:
				writer.Write(", ") if i
				repr(key, writer)
				writer.Write(": ")
				repr(value[key], writer)
				++i
			writer.Write("}")

		AddRepresenter(IList) do (value as IList, writer as TextWriter):
			writer.Write("[")
			RepresentItems(value, writer)
			writer.Write("]")

		AddRepresenter(object) do (value, writer as TextWriter):
			writer.Write(value)

	private def RepresentItems(items, writer as TextWriter):
		i = 0
		for item in items:
			writer.Write(", ") if i > 0
			repr(item, writer)
			++i

	callable Representer(value, writer as TextWriter)

	private def AddRepresenter(type as Type, value as Representer):
		_representers.Add((type, value))

	def GetBestRepresenter(type as Type) as Representer:
		for key as Type, value in _representers:
			return value if key.IsAssignableFrom(type)
		assert false, "An appropriate representer could not be found!"

class EntityNameComparer(IComparer of IEntity):
	def Compare(a as IEntity, b as IEntity) as int:
		return string.Compare(a.Name, b.Name)


avoid ArithmeticOverflowException (that will occur after fixing handling ulongs)

class QuackTest(IQuackFu):
	private i as int
	def constructor(j as int):
		i = j
	def QuackInvoke(name as string, args as (object)) as object:
		assert name == "op_UnaryNegation"
		assert 1 == len(args)
		return QuackTest(- cast(QuackTest, args[0]).i)
	def QuackSet(name as string, parameters as (object), value) as object:
		assert parameters is null
		i = value
	def QuackGet(name as string, parameters as (object)) as object:
		assert parameters is null
		return i

_quacker as duck = QuackTest(17)
q = -_quacker
assert -17 == q.i

_duck as duck = 10
_duck = -_duck
assert _duck == -10

_1ul as ulong = 1
_2ul as ulong = 2
_ulong as ulong = 1
for i in range(5):
	_ulong *= _2ul
_ulong -= _1ul

_decimal as decimal = 1
for i in range(5):
	_decimal *= 2
_decimal -= 1
_decimal *= -1

_duck = _ulong
_duck = -_duck
assert _decimal == _duck
assert long == _duck.GetType()

_uint as uint = (2.0**32 - 1)
_duck = _uint
_duck = -_duck
assert _duck == -(2.0**32 - 1)
assert long == _duck.GetType()

_ushort as ushort = (2.0**16 - 1)
_duck = _ushort
_duck = -_duck
assert _duck == -(2.0**16 - 1)
assert int == _duck.GetType()

class QuackTest(IQuackFu):
	private i as int
	def constructor(j as int):
		i = j
	def QuackInvoke(name as string, args as (object)) as object:
		assert name == "op_UnaryNegation"
		assert 1 == len(args)
		return QuackTest(- cast(QuackTest, args[0]).i)
	def QuackSet(name as string, parameters as (object), value) as object:
		assert parameters is null
		i = value
	def QuackGet(name as string, parameters as (object)) as object:
		assert parameters is null
		return i

_quacker as duck = QuackTest(17)
q = -_quacker
assert -17 == q.i

_duck as duck = 10
_duck = -_duck
assert _duck == -10

_1ul as ulong = 1
_2ul as ulong = 2
_ulong as ulong = 1
for i in range(63):
	_ulong *= _2ul
_ulong -= _1ul

_decimal as decimal = 1
for i in range(63):
	_decimal *= 2
_decimal -= 1
_decimal *= -1

_duck = _ulong
_duck = -_duck
assert _decimal == _duck
assert long == _duck.GetType()

_uint as uint = (2.0**32 - 1)
_duck = _uint
_duck = -_duck
assert _duck == -(2.0**32 - 1)
assert long == _duck.GetType()

_ushort as ushort = (2.0**16 - 1)
_duck = _ushort
_duck = -_duck
assert _duck == -(2.0**16 - 1)
assert int == _duck.GetType()