help also describes events now

help also describes events now

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

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

namespace booish

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

class InteractiveInterpreter:

	_compiler = BooCompiler()

	_parser = BooCompiler()

	_values = {}

	_declarations = {}

	_imports = ImportCollection()

	_referenceProcessor = ProcessInterpreterReferences(self)

	_representers = []

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

	[property(RememberLastValue)]
	_rememberLastValue = false

	_inputId = 0

	def constructor():

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

		pipeline.Replace(Steps.ProcessMethodBodiesWithDuckTyping,
						ProcessVariableDeclarations(self))
		pipeline.InsertBefore(Steps.EmitAssembly, _referenceProcessor)

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

		// avoid InvalidCastExceptions by always
		// defining callable types only once per run
		pipeline.Replace(Steps.InitializeTypeSystemServices, InitializeTypeSystemServices())
		pipeline.Add(CacheCallableTypes())

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

		InitializeStandardReferences()

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

	Pipeline:
		get:
			return _compiler.Parameters.Pipeline

	References:
		get:
			return _compiler.Parameters.References

	def LoopEval(code as string):
		result = self.Eval(code)
		if len(result.Errors):
			self.DisplayErrors(result.Errors)
		else:
			_ = self.LastValue
			if _ is not null:
				_print(repr(_))
				SetValue("_", _)

	def Eval(code as string):

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

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

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

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

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

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

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

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

		RecordImports(savedImports)

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

		InitializeModuleInterpreter(asm, module)

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

	def Parse(code as string):
		_parser.Parameters.Input.Clear()
		_parser.Parameters.Input.Add(StringInput("input${++_inputId}", code))
		return _parser.Run()

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

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

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

	def Reset():
		_declarations.Clear()
		_values.Clear()
		InitializeStandardReferences()

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

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

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

	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 help(obj):
		type = (obj as Type) or obj.GetType()
		describeType("    ", type)

	def describeType(indent as string, type as Type):

		baseTypes = (getBooTypeName(type.BaseType),) + array(getBooTypeName(t) for t in type.GetInterfaces())
		_print("class ${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 ${describeModifiers(f)}${f.Name} as ${getBooTypeName(f.FieldType)}")
			_print("")

		for p as Reflection.PropertyInfo in List(type.GetProperties()).Sort(sortByName):
			modifiers = describeModifiers(p)
			params = describePropertyParameters(p.GetIndexParameters())
			_print("${indent}${modifiers}${p.Name}${params} as ${getBooTypeName(p.PropertyType)}:")
			_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
			modifiers = describeModifiers(m)
			returnType = getBooTypeName(m.ReturnType)
			_print("${indent}${modifiers}def ${m.Name}(${describeParameters(m.GetParameters())}) as ${returnType}")
			_print("")

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

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

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

	def describeModifiers(e as Reflection.EventInfo):
		return describeModifiers(e.GetAddMethod() or e.GetRemoveMethod())

	def describeModifiers(p as Reflection.PropertyInfo):
		accessor = p.GetGetMethod() or p.GetSetMethod()
		return describeModifiers(accessor)

	def describePropertyParameters(parameters as (Reflection.ParameterInfo)):
		return "" if 0 == len(parameters)
		return "(${describeParameters(parameters)})"

	def describeParameters(parameters as (Reflection.ParameterInfo)):
		return join(describeParameter(p) for p in parameters, ", ")

	def describeParameter(p as Reflection.ParameterInfo):
		return "${p.Name} as ${getBooTypeName(p.ParameterType)}"

	def getBooTypeName(type as System.Type):
		return "(${getBooTypeName(type.GetElementType())})" if type.IsArray
		return "object" if type is object
		return "string" if type is string
		return "void" if type is void
		return "bool" if type is bool
		return "byte" if type is byte
		return "short" if type is short
		return "int" if type is int
		return "long" if type is long
		return "single" if type is single
		return "double" if type is double
		return "date" if type is date
		return type.FullName

	private def InitializeStandardReferences():
		SetValue("interpreter", self)
		SetValue("dir", dir)
		SetValue("help", help)
		SetValue("print", { value | _print(value) })

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

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

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

	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 item as DictionaryEntry in value:
				writer.Write(", ") if i
				repr(item.Key, writer)
				writer.Write(": ")
				repr(item.Value, 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 InterpreterEntity(ITypedEntity):

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

		[getter(Type)]
		_type as IType

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

		EntityType:
			get:
				return TypeSystem.EntityType.Custom

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

	class InterpreterNamespace(INamespace):

		[getter(ParentNamespace)]
		_parent as INamespace

		_tss as TypeSystemServices

		_interpreter as InteractiveInterpreter

		_declarations = {}

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

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

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

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

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

			return false

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

	class InterpreterTypeSystemServices(TypeSystemServices):

		[getter(CachedCallableTypes)]
		_cachedCallableTypes as List

		[getter(GeneratedCallableTypes)]
		_generatedCallableTypes = []

		def constructor(context, cache):
			super(context)
			_cachedCallableTypes = cache

		override def CreateConcreteCallableType(sourceNode as Node,
											anonymousType as AnonymousCallableType):

			for type as System.Type in _cachedCallableTypes:
				cached = Map(type) as ExternalCallableType
				if anonymousType.GetSignature() == cached.GetSignature():
					return cached

			new = super(sourceNode, anonymousType)
			_generatedCallableTypes.Add(new)
			return new

	class InitializeTypeSystemServices(Steps.AbstractCompilerStep):

		_cachedCallableTypes = []

		override def Run():
			Context.TypeSystemServices = InterpreterTypeSystemServices(Context, _cachedCallableTypes)

	class CacheCallableTypes(Steps.AbstractCompilerStep):

		override def Run():
			return if len(Errors)

			services as InterpreterTypeSystemServices = self.TypeSystemServices
			types = services.GeneratedCallableTypes
			return unless len(types)

			debug "caching", len(types), "callable types"
			for type as InternalCallableType in types:
				debug type
				services.CachedCallableTypes.Add(type.GeneratedType)

	class ProcessVariableDeclarations(Steps.ProcessMethodBodiesWithDuckTyping):

		_namespace as InterpreterNamespace
		_interpreter as InteractiveInterpreter
		_entryPoint as Method

		def constructor(interpreter):
			_interpreter = interpreter

		InEntryPoint:
			get:
				return _currentMethod.Method is _entryPoint

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

			_namespace = InterpreterNamespace(
								_interpreter,
								TypeSystemServices,
								NameResolutionService.GlobalNamespace)
			NameResolutionService.GlobalNamespace = _namespace
			_entryPoint = Steps.ContextAnnotations.GetEntryPoint(Context)

		override def Dispose():
			_namespace = null

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

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

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

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

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

			return _namespace.Declare(name, type)

	class ProcessInterpreterReferences(Steps.AbstractTransformerCompilerStep):

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

		_interpreterField as Field
		_interpreter as InteractiveInterpreter
		_isEntryPoint = false

		def constructor(interpreter):
			_interpreter = interpreter

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

		override def EnterModule(node as Module):

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

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

			return true

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

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

		override def OnReferenceExpression(node as ReferenceExpression):

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

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

		override def LeaveExpressionStatement(node as ExpressionStatement):

			return unless _interpreter.RememberLastValue and _isEntryPoint

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

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

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

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

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

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

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

			return CodeBuilder.CreateCast(srcNode.ExpressionType, expression)

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

interpreter = InteractiveInterpreter(RememberLastValue: true)

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

if "--debug" in argv:
	Debug.Listeners.Add(TextWriterTraceListener(Console.Out))

while line=prompt(">>> "):
	try:
		line = ReadBlock(line) if line[-1:] in ":", "\\"
		interpreter.LoopEval(line)
	except x as System.Reflection.TargetInvocationException:
		print(x.InnerException)
	except x:
		print(x)

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

namespace booish

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

class InteractiveInterpreter:

	_compiler = BooCompiler()

	_parser = BooCompiler()

	_values = {}

	_declarations = {}

	_imports = ImportCollection()

	_referenceProcessor = ProcessInterpreterReferences(self)

	_representers = []

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

	[property(RememberLastValue)]
	_rememberLastValue = false

	_inputId = 0

	def constructor():

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

		pipeline.Replace(Steps.ProcessMethodBodiesWithDuckTyping,
						ProcessVariableDeclarations(self))
		pipeline.InsertBefore(Steps.EmitAssembly, _referenceProcessor)

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

		// avoid InvalidCastExceptions by always
		// defining callable types only once per run
		pipeline.Replace(Steps.InitializeTypeSystemServices, InitializeTypeSystemServices())
		pipeline.Add(CacheCallableTypes())

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

		InitializeStandardReferences()

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

	Pipeline:
		get:
			return _compiler.Parameters.Pipeline

	References:
		get:
			return _compiler.Parameters.References

	def LoopEval(code as string):
		result = self.Eval(code)
		if len(result.Errors):
			self.DisplayErrors(result.Errors)
		else:
			_ = self.LastValue
			if _ is not null:
				_print(repr(_))
				SetValue("_", _)

	def Eval(code as string):

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

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

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

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

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

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

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

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

		RecordImports(savedImports)

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

		InitializeModuleInterpreter(asm, module)

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

	def Parse(code as string):
		_parser.Parameters.Input.Clear()
		_parser.Parameters.Input.Add(StringInput("input${++_inputId}", code))
		return _parser.Run()

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

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

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

	def Reset():
		_declarations.Clear()
		_values.Clear()
		InitializeStandardReferences()

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

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

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

	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 help(obj):
		type = (obj as Type) or obj.GetType()
		describeType("    ", type)

	def describeType(indent as string, type as Type):

		baseTypes = (getBooTypeName(type.BaseType),) + array(getBooTypeName(t) for t in type.GetInterfaces())
		_print("class ${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 ${describeModifiers(f)}${f.Name} as ${getBooTypeName(f.FieldType)}")
			_print("")

		for p as Reflection.PropertyInfo in List(type.GetProperties()).Sort(sortByName):
			modifiers = describeModifiers(p)
			params = describePropertyParameters(p.GetIndexParameters())
			_print("${indent}${modifiers}${p.Name}${params} as ${getBooTypeName(p.PropertyType)}:")
			_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
			modifiers = describeModifiers(m)
			returnType = getBooTypeName(m.ReturnType)
			_print("${indent}${modifiers}def ${m.Name}(${describeParameters(m.GetParameters())}) as ${returnType}")
			_print("")

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

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

	def describeModifiers(p as Reflection.PropertyInfo):
		accessor = p.GetGetMethod() or p.GetSetMethod()
		return describeModifiers(accessor)

	def describePropertyParameters(parameters as (Reflection.ParameterInfo)):
		return "" if 0 == len(parameters)
		return "(${describeParameters(parameters)})"

	def describeParameters(parameters as (Reflection.ParameterInfo)):
		return join(describeParameter(p) for p in parameters, ", ")

	def describeParameter(p as Reflection.ParameterInfo):
		return "${p.Name} as ${getBooTypeName(p.ParameterType)}"

	def getBooTypeName(type as System.Type):
		return "(${getBooTypeName(type.GetElementType())})" if type.IsArray
		return "object" if type is object
		return "string" if type is string
		return "void" if type is void
		return "bool" if type is bool
		return "byte" if type is byte
		return "short" if type is short
		return "int" if type is int
		return "long" if type is long
		return "single" if type is single
		return "double" if type is double
		return "date" if type is date
		return type.FullName

	private def InitializeStandardReferences():
		SetValue("interpreter", self)
		SetValue("dir", dir)
		SetValue("help", help)
		SetValue("print", { value | _print(value) })

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

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

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

	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 item as DictionaryEntry in value:
				writer.Write(", ") if i
				repr(item.Key, writer)
				writer.Write(": ")
				repr(item.Value, 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 InterpreterEntity(ITypedEntity):

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

		[getter(Type)]
		_type as IType

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

		EntityType:
			get:
				return TypeSystem.EntityType.Custom

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

	class InterpreterNamespace(INamespace):

		[getter(ParentNamespace)]
		_parent as INamespace

		_tss as TypeSystemServices

		_interpreter as InteractiveInterpreter

		_declarations = {}

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

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

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

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

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

			return false

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

	class InterpreterTypeSystemServices(TypeSystemServices):

		[getter(CachedCallableTypes)]
		_cachedCallableTypes as List

		[getter(GeneratedCallableTypes)]
		_generatedCallableTypes = []

		def constructor(context, cache):
			super(context)
			_cachedCallableTypes = cache

		override def CreateConcreteCallableType(sourceNode as Node,
											anonymousType as AnonymousCallableType):

			for type as System.Type in _cachedCallableTypes:
				cached = Map(type) as ExternalCallableType
				if anonymousType.GetSignature() == cached.GetSignature():
					return cached

			new = super(sourceNode, anonymousType)
			_generatedCallableTypes.Add(new)
			return new

	class InitializeTypeSystemServices(Steps.AbstractCompilerStep):

		_cachedCallableTypes = []

		override def Run():
			Context.TypeSystemServices = InterpreterTypeSystemServices(Context, _cachedCallableTypes)

	class CacheCallableTypes(Steps.AbstractCompilerStep):

		override def Run():
			return if len(Errors)

			services as InterpreterTypeSystemServices = self.TypeSystemServices
			types = services.GeneratedCallableTypes
			return unless len(types)

			debug "caching", len(types), "callable types"
			for type as InternalCallableType in types:
				debug type
				services.CachedCallableTypes.Add(type.GeneratedType)

	class ProcessVariableDeclarations(Steps.ProcessMethodBodiesWithDuckTyping):

		_namespace as InterpreterNamespace
		_interpreter as InteractiveInterpreter
		_entryPoint as Method

		def constructor(interpreter):
			_interpreter = interpreter

		InEntryPoint:
			get:
				return _currentMethod.Method is _entryPoint

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

			_namespace = InterpreterNamespace(
								_interpreter,
								TypeSystemServices,
								NameResolutionService.GlobalNamespace)
			NameResolutionService.GlobalNamespace = _namespace
			_entryPoint = Steps.ContextAnnotations.GetEntryPoint(Context)

		override def Dispose():
			_namespace = null

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

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

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

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

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

			return _namespace.Declare(name, type)

	class ProcessInterpreterReferences(Steps.AbstractTransformerCompilerStep):

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

		_interpreterField as Field
		_interpreter as InteractiveInterpreter
		_isEntryPoint = false

		def constructor(interpreter):
			_interpreter = interpreter

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

		override def EnterModule(node as Module):

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

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

			return true

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

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

		override def OnReferenceExpression(node as ReferenceExpression):

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

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

		override def LeaveExpressionStatement(node as ExpressionStatement):

			return unless _interpreter.RememberLastValue and _isEntryPoint

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

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

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

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

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

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

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

			return CodeBuilder.CreateCast(srcNode.ExpressionType, expression)

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

interpreter = InteractiveInterpreter(RememberLastValue: true)

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

if "--debug" in argv:
	Debug.Listeners.Add(TextWriterTraceListener(Console.Out))

while line=prompt(">>> "):
	try:
		line = ReadBlock(line) if line[-1:] in ":", "\\"
		interpreter.LoopEval(line)
	except x as System.Reflection.TargetInvocationException:
		print(x.InnerException)
	except x:
		print(x)


add L suffix when printing long values

add L suffix when printing long values

#region license
// Copyright (c) 2009 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.Builtins

import System
import System.Collections
import System.IO

def repr(value):
	return Repr.repr(value)

static class Repr:

	_representers = []

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

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

	private def InitializeRepresenters():
		AddRepresenter(string) do (value as string, writer as TextWriter):
			Boo.Lang.Compiler.Ast.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
			if method.DeclaringType is not null:
				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(long) do (value, writer as TextWriter):
			writer.Write(value)
			writer.Write("L")

		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

	private def AddRepresenter(type as Type, value as Action[of object, TextWriter]):
		_representers.Add((type, value))

	def GetBestRepresenter(type as Type) as Action[of object, TextWriter]:
		for key as Type, value in _representers:
			return value if key.IsAssignableFrom(type)
		raise ArgumentException("An appropriate representer could not be found!")

#region license
// Copyright (c) 2009 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.Builtins

import System
import System.Collections
import System.IO

def repr(value):
	return Repr.repr(value)

static class Repr:

	_representers = []

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

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

	private def InitializeRepresenters():
		AddRepresenter(string) do (value as string, writer as TextWriter):
			Boo.Lang.Compiler.Ast.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
			if method.DeclaringType is not null:
				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

	private def AddRepresenter(type as Type, value as Action[of object, TextWriter]):
		_representers.Add((type, value))

	def GetBestRepresenter(type as Type) as Action[of object, TextWriter]:
		for key as Type, value in _representers:
			return value if key.IsAssignableFrom(type)
		raise ArgumentException("An appropriate representer could not be found!")


support for multiple arguments to 'print' macro

namespace Boojay.Macros

import Boo.Lang.Compiler
import Boo.Lang.Compiler.Ast

macro print:

	if 0 == len(print.Arguments):
		return ExpressionStatement(print.LexicalInfo, [| java.lang.System.out.println("") |])

	b = Block(print.LexicalInfo)
	last = print.Arguments[-1]
	for arg in print.Arguments:
		if arg is last: break
		b.Add([| java.lang.System.out.print($arg) |])
		b.Add([| java.lang.System.out.print(' ') |])
	b.Add([| java.lang.System.out.println($last) |])
	return b

namespace Boojay.Macros

import Boo.Lang.Compiler
import Boo.Lang.Compiler.Ast

macro print:
	assert 1 == len(print.Arguments)
	arg, = print.Arguments
	return ExpressionStatement(print.LexicalInfo, [| java.lang.System.out.println($arg) |])


Fixed trailing comma in js printer

Fixed trailing comma in js printer

namespace BooJs.Compiler.Mozilla

import System.IO(TextWriter)
import System.Web.Script.Serialization(JavaScriptSerializer) from 'System.Web.Extensions'

import Boo.Lang.Compiler.Ast(Module)

import BooJs.Compiler(CompilerContext)
import BooJs.Compiler.SourceMap(MapBuilder)

class JsPrinter(Printer):
"""
    Generates Javascript source code from the Mozilla AST
"""
    [property(SourceMap)]
    _srcmap as MapBuilder = null

    def constructor(writer as TextWriter):
        super(writer)

    override def Visit(node as Node):
        if SourceMap and node and node.loc:
            identifier = node as Identifier
            ident = (identifier.name if identifier else null)
            pos as Position = node.loc.start
            SourceMap.Map(node.loc.source, pos.line-1, pos.column, Line, Column, ident)

        super(node)

    protected def WrapProgram(node as Program):
        # TODO: Move to custom step
        module = node['module'] as Module

        deps = List of IExpression() { Literal('Boo') }
        refs = List of IPattern() { Identifier('Boo') }

        # Get namespace mapping annotations
        mapping as Hash = module['nsmapping']
        asmrefs as Hash = module['nsasmrefs']

        for ns in mapping.Keys:
            if ns in asmrefs:
                print '{0} => {1}' % (ns, mapping[ns] + ':' + asmrefs[ns])
                deps.Add( Literal(ns + ':' + asmrefs[ns]) )
            else:
                print '{0} => {1}' % (ns, mapping[ns])
                deps.Add( Literal(ns) )
            refs.Add( Identifier(mapping[ns]) )

        # Use Boo.define to bootstrap the module contents
        call = CallExpression()
        call.callee = MemberExpression(
            object: Identifier(name: 'Boo'),
            property: Identifier(name: 'define')
        )
        call.arguments.Add(Literal(value: (module.Namespace.ToString() if module.Namespace else '')))
        call.arguments.Add(ArrayExpression(elements: deps))
        fn = FunctionExpression(params: refs, body: BlockStatement(body: node.body))
        call.arguments.Add(fn)

        return ExpressionStatement(call)

    virtual def OnProgram(node as Program):
        Visit WrapProgram(node)

        # Add source map to the runtime
        # TODO: Sourcemaps should be per assembly not module
        if SourceMap and CompilerContext.Current.Parameters.Debug:
            srcmap = SourceMap.ToHash()
            Write 'Boo.sourcemap({0});', JavaScriptSerializer().Serialize(srcmap)
            WriteLine

    virtual def OnWhileStatement(node as WhileStatement):
        WriteIndented 'while ('
        Visit node.test
        Write ') '
        Visit node.body

    virtual def OnDoWhileStatement(node as DoWhileStatement):
        WriteIndented 'do '
        Visit node.body
        WriteIndented ' while ('
        Visit node.test
        Write ')'

    virtual def OnForStatement(node as ForStatement):
        WriteIndented 'for ('
        Visit node.init
        Write '; '
        Visit node.test
        Write '; '
        Visit node.update
        Write ') '
        Visit node.body

    virtual def OnSwitchStatement(node as SwitchStatement):
        WriteIndented 'switch ('
        Visit node.discriminant
        WriteLine ') {'
        for case in node.cases:
            WriteIndented 'case '
            Visit case.test
            WriteLine ':'
            Indent
            for st in case.consequent:
                WriteIndented
                Visit st
            Dedent
        WriteIndented
        WriteLine '}'

    virtual def OnThisExpression(node as ThisExpression):
        Write 'this'

    virtual def OnArrayExpression(node as ArrayExpression):
        Write '['
        WriteCommaSeparatedList node.elements
        Write ']'

    virtual def OnSequenceExpression(node as SequenceExpression):
        Write '('
        WriteCommaSeparatedList node.expressions
        Write ')'

    virtual def OnObjectExpression(node as ObjectExpression):
        if not len(node.properties):
            Write '{}'
            return

        WriteLine '{'
        Indent
        l = len(node.properties)-1
        for idx as int, prop as ObjectExpressionProp in enumerate(node.properties):
            WriteIndented
            Visit prop.key
            Write ': '
            Visit prop.value
            Write ', ' if idx < l
            WriteLine
        Dedent
        WriteIndented '}'
        WriteLine

    virtual def OnFunctionExpression(node as FunctionExpression):
        Write 'function '
        Visit node.id
        Write '('
        WriteCommaSeparatedList node.params
        Write ') '
        Visit node.body
        Trim

    virtual def OnBlockStatement(node as BlockStatement):
        PrecedenceStack.Add(Precedence.None)
        WriteLine '{'
        Indent
        Visit(node.body)
        Trim
        WriteLine
        Dedent
        WriteIndented '}'
        WriteLine
        PrecedenceStack.Pop()

    virtual def OnFunctionDeclaration(node as FunctionDeclaration):
        WriteIndented 'function '
        Visit node.id
        Write ' ('
        WriteCommaSeparatedList node.params
        Write ') '
        Visit node.body
        WriteLine

    virtual def OnReturnStatement(node as ReturnStatement):
        WriteIndented 'return '
        Visit node.argument
        WriteLine ';'

    virtual def OnWithStatement(node as WithStatement):
        WriteIndented 'with ('
        Visit node.object
        Write ') '
        Visit node.body

    virtual def OnContinueStatement(node as ContinueStatement):
        WriteIndented 'continue '
        Visit node.label
        WriteLine ';'

    virtual def OnBreakStatement(node as BreakStatement):
        WriteIndented 'break '
        Visit node.label
        WriteLine ';'

    virtual def OnLabeledStatement(node as LabeledStatement):
        WriteIndented
        Visit node.label
        Write ': '
        Visit node.body

    virtual def OnIfStatement(node as IfStatement):
        WriteIndented 'if ('
        Visit node.test
        Write ') '
        Visit node.consequent
        if node.alternate:
            Trim
            Write ' else '
            Visit node.alternate
        WriteLine

    virtual def OnExpressionStatement(node as ExpressionStatement):
        line = Line
        WriteIndented
        Visit node.expression
        Trim
        WriteLine ';'
        # Include additional new line if the expressions spanned multiple lines
        if Line-line > 2:
            WriteLine

    virtual def OnVariableDeclaration(node as VariableDeclaration):
        WriteIndented '{0} ', node.kind
        Indent
        for idx as int, decl as VariableDeclarator in enumerate(node.declarations):
            if idx > 0:
                WriteLine ','
                WriteIndented

            Visit decl.id
            if decl.init:
                Write ' = '
                Visit decl.init

            Trim

        WriteLine ';'
        WriteLine if len(node.declarations) > 1
        Dedent

    virtual def OnUnaryExpression(node as UnaryExpression):
        precedence = (Precedence.Unary if node.prefix else Precedence.Postfix)
        Parens precedence:
            if node.prefix:
                Write node.operator
                Write ' ' if node.operator == 'typeof'
            Visit node.argument
            if not node.prefix:
                Write node.operator

    virtual def OnUpdateExpression(node as UpdateExpression):
        OnUnaryExpression(node as UnaryExpression)

    virtual def OnBinaryExpression(node as BinaryExpression):
        Parens _binaryPrecedence[node.operator]:
            Visit node.left
            Write ' {0} ', node.operator
            Visit node.right

    virtual def OnAssignmentExpression(node as AssignmentExpression):
        OnBinaryExpression(node as BinaryExpression)

    virtual def OnLogicalExpression(node as LogicalExpression):
        OnBinaryExpression(node as BinaryExpression)

    virtual def OnConditionalExpression(node as ConditionalExpression):
        Parens Precedence.Conditional:
            Visit node.test
            Write ' ? '
            Visit node.consequent
            Write ' : '
            Visit node.alternate

    virtual def OnMemberExpression(node as MemberExpression):
        Parens Precedence.Member:
            Visit node.object
        if node.computed:
            Write '['
            Visit node.property
            Write ']'
        else:
            Write '.'
            Visit node.property

    virtual def OnCallExpression(node as CallExpression):
        Parens Precedence.Call:
            # Make sure self-callable functions are defined as expressions
            if node.callee isa FunctionExpression:
                Write '('
                Visit node.callee
                Write ')'
            else:
                Visit node.callee
            Write '('
            WriteCommaSeparatedList(node.arguments)
            Write ')'

    virtual def OnNewExpression(node as NewExpression):
        Parens Precedence.New:
            Write 'new '
            Visit node._constructor
            if len(node.arguments):
                Write '('
                WriteCommaSeparatedList(node.arguments)
                Write ')'

    virtual def OnThrowStatement(node as ThrowStatement):
        WriteIndented 'throw '
        Visit node.argument
        WriteLine ';'

    virtual def OnTryStatement(node as TryStatement):
        WriteIndented 'try '
        Visit node.block
        Trim
        Visit node.handlers
        Trim
        if node.finalizer:
            Write ' finally '
            Visit node.finalizer
        WriteLine

    virtual def OnCatchClause(node as CatchClause):
        Write ' catch ('
        Visit node.param
        Write ') '
        Visit node.body

    virtual def OnLiteral(node as Literal):
        if node.value is null:
            Write 'null'
        elif node.value == true or node.value == false:
            Write node.value.ToString().ToLower()
        elif node.regexp:
            Write node.value
        elif node.value isa string:
            quotes = 0
            result = ''
            for ch in (node.value as string):
                if ch == char('\''):
                    quotes++
                    result += ch
                elif ch == char('"'):
                    quotes--
                    result += ch
                elif ch == char('\n'): result += '\\n'
                elif ch == char('\r'): result += '\\r'
                elif ch == char('\u2028'): result += '\\u2028'
                elif ch == char('\u2029'): result += '\\u2029'
                else: result += ch

            quote = ('\'' if quotes <= 0 else '"')
            Write quote + result.Replace(quote, '\\' + quote) + quote
        else:
            # TODO: Properly write numbers
            Write node.value.ToString()

    virtual def OnIdentifier(node as Identifier):
        Write node.name

    virtual def OnXCodeExpression(node as XCodeExpression):
        Parens Precedence.Assignment:
            lines = /\r\n|\n/.Split(node.code)
            Write lines[0]
            for line in lines[1:]:
                WriteLine
                WriteIndented line

namespace BooJs.Compiler.Mozilla

import System.IO(TextWriter)
import System.Web.Script.Serialization(JavaScriptSerializer) from 'System.Web.Extensions'

import Boo.Lang.Compiler.Ast(Module)

import BooJs.Compiler(CompilerContext)
import BooJs.Compiler.SourceMap(MapBuilder)

class JsPrinter(Printer):
"""
    Generates Javascript source code from the Mozilla AST
"""
    [property(SourceMap)]
    _srcmap as MapBuilder = null

    def constructor(writer as TextWriter):
        super(writer)

    override def Visit(node as Node):
        if SourceMap and node and node.loc:
            identifier = node as Identifier
            ident = (identifier.name if identifier else null)
            pos as Position = node.loc.start
            SourceMap.Map(node.loc.source, pos.line-1, pos.column, Line, Column, ident)

        super(node)

    protected def WrapProgram(node as Program):
        # TODO: Move to custom step
        module = node['module'] as Module

        deps = List of IExpression() { Literal('Boo') }
        refs = List of IPattern() { Identifier('Boo') }

        # Get namespace mapping annotations
        mapping as Hash = module['nsmapping']
        asmrefs as Hash = module['nsasmrefs']

        for ns in mapping.Keys:
            if ns in asmrefs:
                print '{0} => {1}' % (ns, mapping[ns] + ':' + asmrefs[ns])
                deps.Add( Literal(ns + ':' + asmrefs[ns]) )
            else:
                print '{0} => {1}' % (ns, mapping[ns])
                deps.Add( Literal(ns) )
            refs.Add( Identifier(mapping[ns]) )

        # Use Boo.define to bootstrap the module contents
        call = CallExpression()
        call.callee = MemberExpression(
            object: Identifier(name: 'Boo'),
            property: Identifier(name: 'define')
        )
        call.arguments.Add(Literal(value: (module.Namespace.ToString() if module.Namespace else '')))
        call.arguments.Add(ArrayExpression(elements: deps))
        fn = FunctionExpression(params: refs, body: BlockStatement(body: node.body))
        call.arguments.Add(fn)

        return ExpressionStatement(call)

    virtual def OnProgram(node as Program):
        Visit WrapProgram(node)

        # Add source map to the runtime
        # TODO: Sourcemaps should be per assembly not module
        if SourceMap and CompilerContext.Current.Parameters.Debug:
            srcmap = SourceMap.ToHash()
            Write 'Boo.sourcemap({0});', JavaScriptSerializer().Serialize(srcmap)
            WriteLine

    virtual def OnWhileStatement(node as WhileStatement):
        WriteIndented 'while ('
        Visit node.test
        Write ') '
        Visit node.body

    virtual def OnDoWhileStatement(node as DoWhileStatement):
        WriteIndented 'do '
        Visit node.body
        WriteIndented ' while ('
        Visit node.test
        Write ')'

    virtual def OnForStatement(node as ForStatement):
        WriteIndented 'for ('
        Visit node.init
        Write '; '
        Visit node.test
        Write '; '
        Visit node.update
        Write ') '
        Visit node.body

    virtual def OnSwitchStatement(node as SwitchStatement):
        WriteIndented 'switch ('
        Visit node.discriminant
        WriteLine ') {'
        for case in node.cases:
            WriteIndented 'case '
            Visit case.test
            WriteLine ':'
            Indent
            for st in case.consequent:
                WriteIndented
                Visit st
            Dedent
        WriteIndented
        WriteLine '}'

    virtual def OnThisExpression(node as ThisExpression):
        Write 'this'

    virtual def OnArrayExpression(node as ArrayExpression):
        Write '['
        WriteCommaSeparatedList node.elements
        Write ']'

    virtual def OnSequenceExpression(node as SequenceExpression):
        Write '('
        WriteCommaSeparatedList node.expressions
        Write ')'

    virtual def OnObjectExpression(node as ObjectExpression):
        if not len(node.properties):
            Write '{}'
            return

        WriteLine '{'
        Indent
        for prop in node.properties:
            WriteIndented
            Visit prop.key
            Write ': '
            Visit prop.value
            WriteLine ', '
        Dedent
        WriteLine '}'

    virtual def OnFunctionExpression(node as FunctionExpression):
        Write 'function '
        Visit node.id
        Write '('
        WriteCommaSeparatedList node.params
        Write ') '
        Visit node.body
        Trim

    virtual def OnBlockStatement(node as BlockStatement):
        PrecedenceStack.Add(Precedence.None)
        WriteLine '{'
        Indent
        Visit(node.body)
        Trim
        WriteLine
        Dedent
        WriteIndented '}'
        WriteLine
        PrecedenceStack.Pop()

    virtual def OnFunctionDeclaration(node as FunctionDeclaration):
        WriteIndented 'function '
        Visit node.id
        Write ' ('
        WriteCommaSeparatedList node.params
        Write ') '
        Visit node.body
        WriteLine

    virtual def OnReturnStatement(node as ReturnStatement):
        WriteIndented 'return '
        Visit node.argument
        WriteLine ';'

    virtual def OnWithStatement(node as WithStatement):
        WriteIndented 'with ('
        Visit node.object
        Write ') '
        Visit node.body

    virtual def OnContinueStatement(node as ContinueStatement):
        WriteIndented 'continue '
        Visit node.label
        WriteLine ';'

    virtual def OnBreakStatement(node as BreakStatement):
        WriteIndented 'break '
        Visit node.label
        WriteLine ';'

    virtual def OnLabeledStatement(node as LabeledStatement):
        WriteIndented
        Visit node.label
        Write ': '
        Visit node.body

    virtual def OnIfStatement(node as IfStatement):
        WriteIndented 'if ('
        Visit node.test
        Write ') '
        Visit node.consequent
        if node.alternate:
            Trim
            Write ' else '
            Visit node.alternate
        WriteLine

    virtual def OnExpressionStatement(node as ExpressionStatement):
        line = Line
        WriteIndented
        Visit node.expression
        Trim
        WriteLine ';'
        # Include additional new line if the expressions spanned multiple lines
        if Line-line > 2:
            WriteLine

    virtual def OnVariableDeclaration(node as VariableDeclaration):
        WriteIndented '{0} ', node.kind
        Indent
        for idx as int, decl as VariableDeclarator in enumerate(node.declarations):
            if idx > 0:
                WriteLine ','
                WriteIndented

            Visit decl.id
            if decl.init:
                Write ' = '
                Visit decl.init

            Trim

        WriteLine ';'
        WriteLine if len(node.declarations) > 1
        Dedent

    virtual def OnUnaryExpression(node as UnaryExpression):
        precedence = (Precedence.Unary if node.prefix else Precedence.Postfix)
        Parens precedence:
            if node.prefix:
                Write node.operator
                Write ' ' if node.operator == 'typeof'
            Visit node.argument
            if not node.prefix:
                Write node.operator

    virtual def OnUpdateExpression(node as UpdateExpression):
        OnUnaryExpression(node as UnaryExpression)

    virtual def OnBinaryExpression(node as BinaryExpression):
        Parens _binaryPrecedence[node.operator]:
            Visit node.left
            Write ' {0} ', node.operator
            Visit node.right

    virtual def OnAssignmentExpression(node as AssignmentExpression):
        OnBinaryExpression(node as BinaryExpression)

    virtual def OnLogicalExpression(node as LogicalExpression):
        OnBinaryExpression(node as BinaryExpression)

    virtual def OnConditionalExpression(node as ConditionalExpression):
        Parens Precedence.Conditional:
            Visit node.test
            Write ' ? '
            Visit node.consequent
            Write ' : '
            Visit node.alternate

    virtual def OnMemberExpression(node as MemberExpression):
        Parens Precedence.Member:
            Visit node.object
        if node.computed:
            Write '['
            Visit node.property
            Write ']'
        else:
            Write '.'
            Visit node.property

    virtual def OnCallExpression(node as CallExpression):
        Parens Precedence.Call:
            # Make sure self-callable functions are defined as expressions
            if node.callee isa FunctionExpression:
                Write '('
                Visit node.callee
                Write ')'
            else:
                Visit node.callee
            Write '('
            WriteCommaSeparatedList(node.arguments)
            Write ')'

    virtual def OnNewExpression(node as NewExpression):
        Parens Precedence.New:
            Write 'new '
            Visit node._constructor
            if len(node.arguments):
                Write '('
                WriteCommaSeparatedList(node.arguments)
                Write ')'

    virtual def OnThrowStatement(node as ThrowStatement):
        WriteIndented 'throw '
        Visit node.argument
        WriteLine ';'

    virtual def OnTryStatement(node as TryStatement):
        WriteIndented 'try '
        Visit node.block
        Trim
        Visit node.handlers
        Trim
        if node.finalizer:
            Write ' finally '
            Visit node.finalizer
        WriteLine

    virtual def OnCatchClause(node as CatchClause):
        Write ' catch ('
        Visit node.param
        Write ') '
        Visit node.body

    virtual def OnLiteral(node as Literal):
        if node.value is null:
            Write 'null'
        elif node.value == true or node.value == false:
            Write node.value.ToString().ToLower()
        elif node.regexp:
            Write node.value
        elif node.value isa string:
            quotes = 0
            result = ''
            for ch in (node.value as string):
                if ch == char('\''):
                    quotes++
                    result += ch
                elif ch == char('"'):
                    quotes--
                    result += ch
                elif ch == char('\n'): result += '\\n'
                elif ch == char('\r'): result += '\\r'
                elif ch == char('\u2028'): result += '\\u2028'
                elif ch == char('\u2029'): result += '\\u2029'
                else: result += ch

            quote = ('\'' if quotes <= 0 else '"')
            Write quote + result.Replace(quote, '\\' + quote) + quote
        else:
            # TODO: Properly write numbers
            Write node.value.ToString()

    virtual def OnIdentifier(node as Identifier):
        Write node.name

    virtual def OnXCodeExpression(node as XCodeExpression):
        Parens Precedence.Assignment:
            lines = /\r\n|\n/.Split(node.code)
            Write lines[0]
            for line in lines[1:]:
                WriteLine
                WriteIndented line


Flatten nested blocks

Flatten nested blocks

namespace BooJs.Compiler.Steps

import Boo.Lang.Compiler
import Boo.Lang.Compiler.Ast
import Boo.Lang.Compiler.Steps

import Boo.Lang.Environments
import Boo.Lang.Compiler.TypeSystem.Services.RuntimeMethodCache as BooRuntimeMethodCache
import BooJs.Compiler.TypeSystem(RuntimeMethodCache)
import BooJs.Lang.Extensions

import System.Runtime.CompilerServices(CompilerGeneratedAttribute)

class PrepareAst(AbstractTransformerCompilerStep):
"""
    Prepare the AST for its conversion to Javascript

        - Remove unneeded nodes
        - Convert builtins and runtime references

"""
    [getter(MethodCache)]
    private _methodCache as RuntimeMethodCache

    [getter(BooMethodCache)]
    private _booMethodCache as BooRuntimeMethodCache

    protected def GetAttribute[of T(System.Attribute)](node as Node) as T:
        entity = node.Entity as TypeSystem.IExternalEntity
        return (GetAttribute[of T](entity) if entity else null as T)

    protected def GetAttribute[of T(System.Attribute)](entity as TypeSystem.IExternalEntity) as T:
        return System.Attribute.GetCustomAttribute(entity.MemberInfo, typeof(T))

    protected def IsBuiltin(node as Node) as bool:
        entity = node.Entity as TypeSystem.Reflection.ExternalType
        if not entity and centity = node.Entity as TypeSystem.ExternalMethod:
            entity = centity.DeclaringType

        return false if not entity
        return true if entity.Type is TypeSystemServices.BuiltinsType
        return entity.DeclaringType == TypeSystemServices.BuiltinsType

    protected def IsGlobal(node as Node) as bool:
        if entity = node.Entity as TypeSystem.Reflection.ExternalType:
            return entity.ActualType.Namespace == 'BooJs.Lang.Globals'

        return false

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

        _methodCache = EnvironmentProvision[of RuntimeMethodCache]()
        _booMethodCache = EnvironmentProvision[of BooRuntimeMethodCache]()

    def EnterModule(node as Module):
        if node.Name == 'CompilerGenerated':
            RemoveCurrentNode()
            return false

        return true

    def EnterClassDefinition(node as ClassDefinition):
        if node.Name == 'CompilerGeneratedExtensions':
            RemoveCurrentNode()
            return false

        if GetAttribute[of CompilerGeneratedAttribute](node):
            RemoveCurrentNode()
            return false

        return true

    def OnExpressionStatement(node as ExpressionStatement):
        # Ignore the assignment of locals produced by closures instrumentation
        be = node.Expression as BinaryExpression
        if be and be.Operator == BinaryOperatorType.Assign and be.Left.ToString() == '$locals':
            RemoveCurrentNode
            return

        # Ignore initialization value calls (__initobj__). We do it elsewhere.
        mie = node.Expression as MethodInvocationExpression
        if mie and mie.Target.Entity == TypeSystem.BuiltinFunction.InitValueType:
            RemoveCurrentNode
            return

        super(node)

    protected def ProcessReference(node as ReferenceExpression) as Node:
        if TransformAttribute.HasAttribute(node):
            result = TransformAttribute.Resolve(node, null)
            Visit result
            return result

        # Primitive type references
        if entity = node.Entity as TypeSystem.Reflection.ExternalType:
            if TypeSystemServices.IsLiteralPrimitive(entity):
                return StringLiteralExpression(node.LexicalInfo, entity.FullName)

        # Check for builtins references
        if IsBuiltin(node):
            name = node.Name.Split(char('.'))[-1]
            refe = [| Boo.$(ReferenceExpression(Name: name)) |].withLexicalInfoFrom(node)
            refe.Entity = node.Entity
            return refe

        # Entities in the Global namespace are never prefixed
        if IsGlobal(node):
            node.Name = node.Name.Split(char('.'))[-1]
            return node

        # Members of the module are placed in the top scope
        ientity = node.Entity as TypeSystem.IMember
        if ientity and ientity.DeclaringType and ientity.DeclaringType.IsClass and ientity.DeclaringType.IsFinal:
            name = node.Name.Split(char('.'))[-1]
            return [| $(ReferenceExpression(Name: name)) |].withLexicalInfoFrom(node)

        return node

    def OnReferenceExpression(node as ReferenceExpression):
        ReplaceCurrentNode ProcessReference(node)

    def OnMemberReferenceExpression(node as MemberReferenceExpression):
        if TransformAttribute.HasAttribute(node):
            result = TransformAttribute.Resolve(node, null)
            Visit result
            ReplaceCurrentNode result
            return

        # Convert from `$locals.$variable` to `variable`
        if node.Target.NodeType == NodeType.ReferenceExpression:
            if (node.Target as ReferenceExpression).Name == '$locals':
                refexp = ReferenceExpression(node.Name[1:], LexicalInfo: node.LexicalInfo)
                ReplaceCurrentNode refexp
                return

        # Members of the module are placed in the top scope
        ientity = node.Target.Entity as TypeSystem.Internal.AbstractInternalType
        if node.Target.IsSynthetic and ientity and ientity.IsClass and ientity.IsFinal:
            refexp = ReferenceExpression(node.Name, LexicalInfo: node.LexicalInfo)
            ReplaceCurrentNode refexp
            return

        # Check for builtins references
        if IsBuiltin(node.Target):
            node.Target = [| Boo |].withLexicalInfoFrom(node.Target)

        super(node)

    def OnMethodInvocationExpression(node as MethodInvocationExpression):
        # Convert Transform attribute metadata to an AST annotation
        if TransformAttribute.HasAttribute(node.Target):
            result = TransformAttribute.Resolve(node.Target, node.Arguments)
            result = VisitNode(result)
            ReplaceCurrentNode result
            return

        /*
        rts = TypeSystemServices.RuntimeType
        rtsInvoke = ResolveMethod(rts, 'Invoke')
        rtsInvokeCallable = ResolveMethod(rts, 'InvokeCallable')
        rtsInvokeBinaryOperator = ResolveMethod(rts, 'InvokeBinaryOperator')
        rtsInvokeUnaryOperator = ResolveMethod(rts, 'InvokeUnaryOperator')
        rtsSetProperty = ResolveMethod(rts, 'SetProperty')
        rtsGetProperty = ResolveMethod(rts, 'GetProperty')
        rtsSetSlice = ResolveMethod(rts, 'SetSlice')
        rtsGetSlice = ResolveMethod(rts, 'GetSlice')
        */

        if node.Target.Entity == MethodCache.InvokeBinaryOperator:

            # Convert it back into a binary expression
            be = BinaryExpression(node.LexicalInfo)
            be.ExpressionType = node.ExpressionType
            be.Left = node.Arguments[1]
            be.Right = node.Arguments[2]

            # Remove the op_ to find the operator type
            operator = (node.Arguments[0] as StringLiteralExpression).Value[3:]
            be.Operator = System.Enum.Parse(BinaryOperatorType, operator)

            ReplaceCurrentNode Visit(be)
            return

        if node.Target.Entity == MethodCache.InvokeUnaryOperator:
            # Convert it back into a unary expression
            ue = UnaryExpression(node.LexicalInfo)
            ue.ExpressionType = node.ExpressionType
            ue.Operand = node.Arguments[1]

            # Remove the op_ to find the operator type
            operator = (node.Arguments[0] as StringLiteralExpression).Value[3:]
            ue.Operator = System.Enum.Parse(UnaryOperatorType, operator)

            ReplaceCurrentNode Visit(ue)
            return

        super(node)

    def OnMethod(node as Method):
    """ Process locals and detect the Main method to move its statements into the Module globals
    """
        # Skip compiler generated methods
        if node.IsSynthetic and node.IsInternal:
            RemoveCurrentNode
            return

        # Convert locals back to simple declaration statements
        found = ['$locals']
        for local in node.Locals:
            # Avoid duplicates
            if local.Name is null or local.Name in found:
                continue

            found.Push(local.Name)

            decl = Declaration(LexicalInfo: local.LexicalInfo)
            decl.Name = local.Name

            # Detect local type
            initializer as Expression = NullLiteralExpression()
            entity = local.Entity as TypeSystem.Internal.InternalLocal
            if entity:
                if entity.OriginalDeclaration:
                    # Skip declarations for those flagged as global
                    continue if entity.OriginalDeclaration.ContainsAnnotation('global')
                    decl.Type = entity.OriginalDeclaration.Type
                else:
                    # TODO: Can we generate proper type annotations with this?
                    decl.Type = SimpleTypeReference(Entity: entity)

                # Initialize with a default value
                if TypeSystemServices.IsNumber(entity.Type):
                    initializer = IntegerLiteralExpression(0)
                elif entity.Type == TypeSystemServices.TimeSpanType:
                    initializer = IntegerLiteralExpression(0)
                elif entity.Type == TypeSystemServices.BoolType:
                    initializer = BoolLiteralExpression(false)
                elif entity.Type == TypeSystemServices.StringType:
                    initializer = StringLiteralExpression('')

            st = DeclarationStatement(LexicalInfo: local.LexicalInfo, Declaration: decl, Initializer: initializer)
            node.Body.Insert(0, st)

        # Detect the Main method and move its statements to the Module globals
        if IsEntryPoint(node):
            Visit node.Body
            node.EnclosingModule.Globals = node.Body
            RemoveCurrentNode
            return

        super(node)

    def IsEntryPoint(node as Method):
        # Note: We cannot use ContextAnnotations.GetEntryPoint(Context) to detect
        # the entry point since when emitting the assembly we have to clone the AST
        # and then the node instance is different.
        return node.Name == IntroduceModuleClasses.EntryPointMethodName \
               and node.IsSynthetic and node.IsStatic and node.IsPrivate

    def OnCastExpression(node as CastExpression):
        mie = [| Boo.$(ReferenceExpression('cast'))() |]
        mie.Arguments.Add(node.Target)
        ReplaceCurrentNode( ProcessCast(mie, node.Type) )

    def OnTryCastExpression(node as TryCastExpression):
        mie = [| Boo.trycast() |]
        mie.Arguments.Add(node.Target)
        ReplaceCurrentNode( ProcessCast(mie, node.Type) )

    protected def ProcessCast(mie as MethodInvocationExpression, type as Node) as MethodInvocationExpression:
        if not type isa Expression:
            type = TypeofExpression(type.LexicalInfo, type)

        mie.Arguments.Add(type)
        return Visit(mie)

    def OnCharLiteralExpression(node as CharLiteralExpression):
    """ There is no char type in BooJs. char('c') and char(int) are converted to a string of length 1
    """
        ReplaceCurrentNode StringLiteralExpression(LexicalInfo: node.LexicalInfo, Value: node.Value.ToString())

    def OnRELiteralExpression(node as RELiteralExpression):
    """ Almost a direct translation to Javascript regexp literals. The only modification
        is that we ignore whitespace to allow the regexps to be somehow more readable while
        Boo's handling is to escape it as part of the match.

            / foo | bar /i  ->  /foo|bar/i
            @/ foo | bar /  ->  /foo|bar/i

        TODO: The ones prefixed with @ should be preprocessed to support new lines
    """
        re = node.Value
        start = re.IndexOf('/')
        stop = re.LastIndexOf('/')

        modifier = re[stop:]
        re = re[start:stop]

        # Ignore white space
        re = /\s+/.Replace(re, '')

        # Javascript does not support the single-line modifier (dot matches new lines).
        # We emulate it by converting dots to the [\s\S] expression in the regex.
        # NOTE: The algorithm will break when a dot is preceeded by an escaped back slash (\\.)
        if modifier.Contains('s'):
            re = /(?<!\\)\./.Replace(re, '[\\s\\S]')
            modifier = modifier.Replace('s', '')

        node.Value = re + modifier

    def OnArrayLiteralExpression(node as ArrayLiteralExpression):
    """ In BooJs arrays are muttable (Boo's Lists)
    """
        list = ListLiteralExpression(LexicalInfo: node.LexicalInfo, Items: node.Items)
        Visit list
        ReplaceCurrentNode list

    def OnUnlessStatement(node as UnlessStatement):
    """ Negate the condition to convert it into a simple if statement
    """
        ifst = IfStatement(LexicalInfo: node.LexicalInfo)
        ifst.Condition = UnaryExpression(
            Operator: UnaryOperatorType.LogicalNot,
            Operand: node.Condition,
            LexicalInfo: node.LexicalInfo
        )
        ifst.TrueBlock = node.Block

        ReplaceCurrentNode ifst

    def OnBinaryExpression(node as BinaryExpression):
        if node.Operator == BinaryOperatorType.TypeTest:
            mie = MethodInvocationExpression(node.LexicalInfo)
            mie.Target = ReferenceExpression(node.LexicalInfo, 'Boo.isa')
            mie.Arguments.Add(node.Left)
            mie.Arguments.Add(node.Right)
            Visit mie.Arguments
            ReplaceCurrentNode mie
            return

        super(node)

    def OnTypeofExpression(node as TypeofExpression):
        if st = node.Type as SimpleTypeReference:

            # Primitives are just enclosed as strings
            if TypeSystemServices.IsPrimitive(st.Name):
                ReplaceCurrentNode StringLiteralExpression(node.LexicalInfo, st.Name)
                return

            # If we have an entity use it
            if exent = st.Entity as TypeSystem.Reflection.ExternalType:
                refe = CodeBuilder.CreateReference(node.LexicalInfo, exent.ActualType)
                ReplaceCurrentNode ProcessReference(refe)
                return

            # Just rely on the type name
            refe = ReferenceExpression(node.LexicalInfo, st.Name)
            ReplaceCurrentNode ProcessReference(refe)

        elif node.Type isa ArrayTypeReference:

            refe = CodeBuilder.CreateReference(node.LexicalInfo, TypeSystemServices.ArrayType)
            ReplaceCurrentNode ProcessReference(refe)
            return

        else:
            raise 'Unsupported TypeReference: ' + node.Type + ' (' + node.Type.NodeType + ')'

    def OnUnaryExpression(node as UnaryExpression):
        # Logical Not folding
        if node.Operator == UnaryOperatorType.LogicalNot:
            # not(not(expr)) -> expr
            if ue = node.Operand as UnaryExpression and ue.Operator == UnaryOperatorType.LogicalNot:
                ReplaceCurrentNode Visit(ue.Operand)
                return

            if be = node.Operand as BinaryExpression:
                inverses = {
                    BinaryOperatorType.Equality: BinaryOperatorType.Inequality,
                    BinaryOperatorType.Inequality: BinaryOperatorType.Equality,
                    BinaryOperatorType.GreaterThan: BinaryOperatorType.LessThanOrEqual,
                    BinaryOperatorType.LessThanOrEqual: BinaryOperatorType.GreaterThan,
                    BinaryOperatorType.LessThan: BinaryOperatorType.GreaterThanOrEqual,
                    BinaryOperatorType.GreaterThanOrEqual: BinaryOperatorType.LessThan,
                }

                if be.Operator in inverses:
                    be.Operator = inverses[be.Operator]
                    ReplaceCurrentNode Visit(be)
                    return

        super(node)

    def OnBlock(node as Block):

        Visit node.Statements

        # Flatten nested blocks
        ofs = 0
        while ofs < len(node.Statements):
            st = node.Statements[ofs]
            if st.NodeType == NodeType.Block:
                node.Statements.Remove(st)
                for st2 in (st as Block).Statements:
                    node.Statements.Insert(ofs, st2)
                    ofs++
            else:
                ofs++


suggest only public members

suggest only public members

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

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

namespace booish.gui

import System
import ICSharpCode.TextEditor
import ICSharpCode.TextEditor.Document
import ICSharpCode.TextEditor.Actions
import ICSharpCode.TextEditor.Gui.CompletionWindow
import Boo.Lang.Compiler.TypeSystem

internal class CodeCompletionData(ICompletionData, IComparable):

	_name as string
	_imageProvider as ICompletionWindowImageProvider
	_entities as List = []

	def constructor(imageProvider, name):
		_imageProvider = imageProvider
		_name = name

	Text as (string):
		get:
			return (_name,)

	Description:
		get:
			return "${cast(IEntity, _entities[0]).FullName} (${len(_entities)} overloads)"

	ImageIndex as int:
		get:
			return _imageProvider.GetImageIndex(_entities[0])

	def AddEntity(entity as IEntity):
		_entities.Add(entity)

	def InsertAction(control as TextEditorControl):
		control.ActiveTextAreaControl.TextArea.InsertString(Text[0])

	public def CompareTo(obj) as int:
		if obj is null or not obj isa CodeCompletionData:
			return -1

		other = obj as CodeCompletionData
		return _name.CompareTo(other._name)

internal class CodeCompletionDataProvider(ICompletionDataProvider):

	_imageProvider as ICompletionWindowImageProvider
	_codeCompletion as (IEntity)

	ImageList as System.Windows.Forms.ImageList:
		get:
			return _imageProvider.ImageList

	PreSelection as string:
		get:
			return null

	def constructor(imageProvider, codeCompletion):
		_imageProvider = imageProvider
		_codeCompletion = codeCompletion

	def GenerateCompletionData(fileName as string, textArea as TextArea, charTyped as System.Char) as (ICompletionData):
		values = {}
		for item in _codeCompletion:
			continue if IsSpecial(item)
			continue if not IsPublic(item)

			data as CodeCompletionData
			data = values[item.Name]
			if data is null:
				name = item.Name
				if "." in name:
					name = /\./.Split(name)[-1]
				data = CodeCompletionData(_imageProvider, name)
				values[item.Name] = data
			data.AddEntity(item)
		return array(ICompletionData, values.Values)

	def IsSpecial(entity as IEntity):
		for prefix in ".", "___", "add_", "remove_", "get_", "set_":
			return true if entity.Name.StartsWith(prefix)

	def IsPublic(entity as IEntity):
		member = entity as IMember
		return member is null or member.IsPublic

	def GetImageIndex(entity as IEntity):
		return 0

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

namespace booish.gui

import System
import ICSharpCode.TextEditor
import ICSharpCode.TextEditor.Document
import ICSharpCode.TextEditor.Actions
import ICSharpCode.TextEditor.Gui.CompletionWindow
import Boo.Lang.Compiler.TypeSystem

internal class CodeCompletionData(ICompletionData, IComparable):

	_name as string
	_imageProvider as ICompletionWindowImageProvider
	_entities as List = []

	def constructor(imageProvider, name):
		_imageProvider = imageProvider
		_name = name

	Text as (string):
		get:
			return (_name,)

	Description:
		get:
			return "${cast(IEntity, _entities[0]).FullName} (${len(_entities)} overloads)"

	ImageIndex as int:
		get:
			return _imageProvider.GetImageIndex(_entities[0])

	def AddEntity(entity as IEntity):
		_entities.Add(entity)

	def InsertAction(control as TextEditorControl):
		control.ActiveTextAreaControl.TextArea.InsertString(Text[0])

	public def CompareTo(obj) as int:
		if obj is null or not obj isa CodeCompletionData:
			return -1

		other = obj as CodeCompletionData
		return _name.CompareTo(other._name)

internal class CodeCompletionDataProvider(ICompletionDataProvider):

	_imageProvider as ICompletionWindowImageProvider
	_codeCompletion as (IEntity)

	ImageList as System.Windows.Forms.ImageList:
		get:
			return _imageProvider.ImageList

	PreSelection as string:
		get:
			return null

	def constructor(imageProvider, codeCompletion):
		_imageProvider = imageProvider
		_codeCompletion = codeCompletion

	def GenerateCompletionData(fileName as string, textArea as TextArea, charTyped as System.Char) as (ICompletionData):
		values = {}
		for item in _codeCompletion:
			continue if IsSpecial(item)

			data as CodeCompletionData
			data = values[item.Name]
			if data is null:
				name = item.Name
				if "." in name:
					name = /\./.Split(name)[-1]
				data = CodeCompletionData(_imageProvider, name)
				values[item.Name] = data
			data.AddEntity(item)
		return array(ICompletionData, values.Values)

	def IsSpecial(entity as IEntity):
		for prefix in ".", "___", "add_", "remove_", "get_", "set_":
			return true if entity.Name.StartsWith(prefix)

	def GetImageIndex(entity as IEntity):
		return 0