use comma to separate parameters

use comma to separate parameters

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

"""
Interactive Forms-based Console
"""

namespace booish.gui

import System
import System.Text
import System.IO
import System.Reflection
import booish
import System.Windows.Forms
import System.Drawing
import System.Runtime.InteropServices

class KeyChar:

	public static Enter = chr(Keys.Enter)

	public static Dot = chr('.')

	public static Back = chr(Keys.Back)

	public static Esc = chr(Keys.Escape)

class CompletionBox(ListBox):

	_interpreter as InteractiveInterpreter

	def constructor(interpreter):
		self._interpreter = interpreter
		self.Visible = false

	def Fill(type as System.Type):

		members = []
		for member in type.GetMembers():
			if IsValidSuggestion(member):
				if member isa FieldInfo:
					field = member as FieldInfo
					members.Add("${field.Name} as ${getBooTypeName(field.FieldType)}")
				elif member isa PropertyInfo:
					property = member as PropertyInfo
					getField = ("", "getter,")[property.CanRead]
					setField = ("", "setter")[property.CanWrite]
					members.Add("${member.Name} as ${getBooTypeName(property.PropertyType)} (${getField} ${setField})")
				elif member isa MethodInfo:
					method = member as MethodInfo
					params = join("${param.Name} as ${getBooTypeName(param.ParameterType)}"
										for param in method.GetParameters(), ", ")
					returnValue = ""
					returnValue = " as ${getBooTypeName(method.ReturnType)}" if method.ReturnType is not void
					members.Add("${member.Name}(${params})${returnValue}")

		Items.Clear()
		for item in members.Sort():
			Items.Add(item)

	def Show(pos as Point):
		self.Location = pos
		self.Visible = true
		self.Focus()

	private def IsValidSuggestion(member as MemberInfo):
		return false if not IsPublic(member)
		method = member as MethodBase
		if method is not null:
			return false if method.IsSpecialName
		return true

	private def IsPublic(member as MemberInfo):
		if MemberTypes.Method == member.MemberType:
			return cast(MethodBase, member).IsPublic
		if MemberTypes.Property == member.MemberType:
			p as PropertyInfo = member
			return (p.GetGetMethod() or p.GetSetMethod()).IsPublic
		if MemberTypes.Field == member.MemberType:
			return cast(FieldInfo, member).IsPublic
		return true

	override def OnLostFocus(args as EventArgs):
		self.Visible = false
		super(args)

	override def OnKeyPress(args as KeyPressEventArgs):
		finish = def ():
			Parent.Focus()
			args.Handled = true

		if args.KeyChar in KeyChar.Esc, KeyChar.Back:
			finish()
		elif KeyChar.Enter == args.KeyChar:
			cast(TextBox, Parent).SelectedText = /\w+/.Match(SelectedItem).Groups[0].Value
			finish()
		else:
			super(args)

	private def getBooTypeName(type as System.Type):
		return _interpreter.getBooTypeName(type)

class PromptBox(TextBox):

	enum InputState:
		SingleLine = 0
		Block = 1

	_state = InputState.SingleLine

	_block = System.IO.StringWriter()

	_console = StringWriter()

	[getter(Interpreter)]
	_interpreter = InteractiveInterpreter(
								RememberLastValue: true,
								Print: print)

	_completionBox = CompletionBox(_interpreter)

	def constructor():
		self.Dock = DockStyle.Fill
		self.Multiline = true
		self.AcceptsTab = true
		self.ScrollBars = ScrollBars.Vertical
		_interpreter.References.Add(typeof(TextBox).Assembly)
		_interpreter.References.Add(typeof(Font).Assembly)
		_interpreter.SetValue("inspect", inspect)

		Controls.Add(_completionBox)

		prompt()

	CaretPos:
		get:
			p as Point
			assert booish.gui.externs.User32.GetCaretPos(p)
			return p

	def GetCurrentLine():
		line = Lines[-1][4:]
		print("")
		return line

	def SingleLineInputState():
		code = GetCurrentLine()

		if code[-1:] in ":", "\\":
			_state = InputState.Block
			_block.GetStringBuilder().Length = 0
			_block.WriteLine(code)
		else:
			Eval(code)

	def BlockInputState():
		code = GetCurrentLine()
		if 0 == len(code):
			Eval(_block.ToString())
			_state = InputState.SingleLine
		else:
			_block.WriteLine(code)

	override def OnKeyPress(args as KeyPressEventArgs):
		if KeyChar.Enter == args.KeyChar:
			try:
				(SingleLineInputState, BlockInputState)[_state]()
			except x:
				print(x)
			prompt()
			args.Handled = true
		elif KeyChar.Dot == args.KeyChar:
			self.SelectedText = "."
			DotComplete()
			args.Handled = true

		super(args)

	override def OnResize(args as EventArgs):
		_completionBox.Size = System.Drawing.Size(Width*.65, Height*.4)
		super(args)

	def DotComplete():
		m = /((\w|\.)+)\.$/.Match(self.Lines[-1])
		if m.Success:
			// only simple identifiers right now
			expression = m.Groups[1].Value
			if /^\w+$/.IsMatch(expression):
				type = _interpreter.Lookup(expression)
				if type is not null:
					_completionBox.Fill(type)
					_completionBox.Show(CaretPos)

	def Eval(code as string):
		saved = Console.Out
		Console.SetOut(_console)
		try:
			_interpreter.LoopEval(code)
		ensure:
			FlushConsole()
			Console.SetOut(saved)

	def FlushConsole():
		AppendText(_console.ToString())
		_console.GetStringBuilder().Length = 0

	def print(msg):
		AppendText("${msg}\r\n")

	def inspect([required] obj):
		f = Form(Text: "Object Inspector [${obj}]")
		f.Controls.Add(PropertyGrid(
							Dock: DockStyle.Fill,
							SelectedObject: obj,
							Font: Font))
		f.Show()
		return f

	def prompt():
		AppendText((">>> ", "... ")[_state])

def chr(key as Keys):
	return cast(IConvertible, cast(int, key)).ToChar(null)

def chr(s as string):
	assert len(s) == 1
	return s[0]

#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

"""
Interactive Forms-based Console
"""

namespace booish.gui

import System
import System.Text
import System.IO
import System.Reflection
import booish
import System.Windows.Forms
import System.Drawing
import System.Runtime.InteropServices

class KeyChar:

	public static Enter = chr(Keys.Enter)

	public static Dot = chr('.')

	public static Back = chr(Keys.Back)

	public static Esc = chr(Keys.Escape)

class CompletionBox(ListBox):

	_interpreter as InteractiveInterpreter

	def constructor(interpreter):
		self._interpreter = interpreter
		self.Visible = false

	def Fill(type as System.Type):

		members = []
		for member in type.GetMembers():
			if IsValidSuggestion(member):
				if member isa FieldInfo:
					field = member as FieldInfo
					members.Add("${field.Name} as ${getBooTypeName(field.FieldType)}")
				elif member isa PropertyInfo:
					property = member as PropertyInfo
					getField = ("", "getter,")[property.CanRead]
					setField = ("", "setter")[property.CanWrite]
					members.Add("${member.Name} as ${getBooTypeName(property.PropertyType)} (${getField} ${setField})")
				elif member isa MethodInfo:
					method = member as MethodInfo
					params = join("${param.Name} as ${getBooTypeName(param.ParameterType)}"
										for param in method.GetParameters())
					returnValue = ""
					returnValue = " as ${getBooTypeName(method.ReturnType)}" if method.ReturnType is not void
					members.Add("${member.Name}(${params})${returnValue}")

		Items.Clear()
		for item in members.Sort():
			Items.Add(item)

	def Show(pos as Point):
		self.Location = pos
		self.Visible = true
		self.Focus()

	private def IsValidSuggestion(member as MemberInfo):
		return false if not IsPublic(member)
		method = member as MethodBase
		if method is not null:
			return false if method.IsSpecialName
		return true

	private def IsPublic(member as MemberInfo):
		if MemberTypes.Method == member.MemberType:
			return cast(MethodBase, member).IsPublic
		if MemberTypes.Property == member.MemberType:
			p as PropertyInfo = member
			return (p.GetGetMethod() or p.GetSetMethod()).IsPublic
		if MemberTypes.Field == member.MemberType:
			return cast(FieldInfo, member).IsPublic
		return true

	override def OnLostFocus(args as EventArgs):
		self.Visible = false
		super(args)

	override def OnKeyPress(args as KeyPressEventArgs):
		finish = def ():
			Parent.Focus()
			args.Handled = true

		if args.KeyChar in KeyChar.Esc, KeyChar.Back:
			finish()
		elif KeyChar.Enter == args.KeyChar:
			cast(TextBox, Parent).SelectedText = /\w+/.Match(SelectedItem).Groups[0].Value
			finish()
		else:
			super(args)

	private def getBooTypeName(type as System.Type):
		return _interpreter.getBooTypeName(type)

class PromptBox(TextBox):

	enum InputState:
		SingleLine = 0
		Block = 1

	_state = InputState.SingleLine

	_block = System.IO.StringWriter()

	_console = StringWriter()

	[getter(Interpreter)]
	_interpreter = InteractiveInterpreter(
								RememberLastValue: true,
								Print: print)

	_completionBox = CompletionBox(_interpreter)

	def constructor():
		self.Dock = DockStyle.Fill
		self.Multiline = true
		self.AcceptsTab = true
		self.ScrollBars = ScrollBars.Vertical
		_interpreter.References.Add(typeof(TextBox).Assembly)
		_interpreter.References.Add(typeof(Font).Assembly)
		_interpreter.SetValue("inspect", inspect)

		Controls.Add(_completionBox)

		prompt()

	CaretPos:
		get:
			p as Point
			assert booish.gui.externs.User32.GetCaretPos(p)
			return p

	def GetCurrentLine():
		line = Lines[-1][4:]
		print("")
		return line

	def SingleLineInputState():
		code = GetCurrentLine()

		if code[-1:] in ":", "\\":
			_state = InputState.Block
			_block.GetStringBuilder().Length = 0
			_block.WriteLine(code)
		else:
			Eval(code)

	def BlockInputState():
		code = GetCurrentLine()
		if 0 == len(code):
			Eval(_block.ToString())
			_state = InputState.SingleLine
		else:
			_block.WriteLine(code)

	override def OnKeyPress(args as KeyPressEventArgs):
		if KeyChar.Enter == args.KeyChar:
			try:
				(SingleLineInputState, BlockInputState)[_state]()
			except x:
				print(x)
			prompt()
			args.Handled = true
		elif KeyChar.Dot == args.KeyChar:
			self.SelectedText = "."
			DotComplete()
			args.Handled = true

		super(args)

	override def OnResize(args as EventArgs):
		_completionBox.Size = System.Drawing.Size(Width*.65, Height*.4)
		super(args)

	def DotComplete():
		m = /((\w|\.)+)\.$/.Match(self.Lines[-1])
		if m.Success:
			// only simple identifiers right now
			expression = m.Groups[1].Value
			if /^\w+$/.IsMatch(expression):
				type = _interpreter.Lookup(expression)
				if type is not null:
					_completionBox.Fill(type)
					_completionBox.Show(CaretPos)

	def Eval(code as string):
		saved = Console.Out
		Console.SetOut(_console)
		try:
			_interpreter.LoopEval(code)
		ensure:
			FlushConsole()
			Console.SetOut(saved)

	def FlushConsole():
		AppendText(_console.ToString())
		_console.GetStringBuilder().Length = 0

	def print(msg):
		AppendText("${msg}\r\n")

	def inspect([required] obj):
		f = Form(Text: "Object Inspector [${obj}]")
		f.Controls.Add(PropertyGrid(
							Dock: DockStyle.Fill,
							SelectedObject: obj,
							Font: Font))
		f.Show()
		return f

	def prompt():
		AppendText((">>> ", "... ")[_state])

def chr(key as Keys):
	return cast(IConvertible, cast(int, key)).ToChar(null)

def chr(s as string):
	assert len(s) == 1
	return s[0]


treat mono-1.0==net-1.1 and mono-##==net-## when searching for booc.exe in nant task

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

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

namespace Boo.NAnt

import System
import System.Globalization
import System.IO
import System.Text.RegularExpressions
import NAnt.Core
import NAnt.Core.Attributes
import NAnt.Core.Types
import NAnt.Core.Util
import NAnt.DotNet.Types

import NAnt.DotNet.Tasks
import System.Reflection

[TaskName('booc')]
public class BoocTask(CompilerBase):
	#region Private Instance Fields
	private _debugOutput as DebugOutput = DebugOutput.Enable
	private _exe as string

	private _useruntime = true //keep true for mono compatibility (don't call booc.exe directly)
	private _noconfig = false
	private _nostdlib = false

	#endregion Private Instance Fields
	#region Private Static Fields
	private static _classNameRegex = Regex('^((?<comment>/\\*.*?(\\*/|$))|[\\s\\.\\{]+|class\\s+(?<class>\\w+)|(?<keyword>\\w+))*')

	private static _namespaceRegex = Regex('^((?<comment>/\\*.*?(\\*/|$))|[\\s\\.\\{]+|namespace\\s+(?<namespace>(\\w+(\\.\\w+)*)+)|(?<keyword>\\w+))*')

	#endregion Private Static Fields

	def constructor():
		SupportsKeyFile = true
		SupportsKeyContainer = true

	[FrameworkConfigurable("exename")]
	[TaskAttribute('exename')]
	public override ExeName as string:
		get:
			return _exe
		set:
			_exe = value

	[FrameworkConfigurable("useruntimeengine")]
	[TaskAttribute('useruntimeengine')]
	override UseRuntimeEngine as bool:
		get:
			return _useruntime
		set:
			_useruntime = value

	#region Public Instance Properties

	[TaskAttribute('debug')]
	public DebugOutput as DebugOutput:
		get:
			return _debugOutput
		set:
			_debugOutput = value

	public Debug as bool:
		get:
			return (DebugOutput != DebugOutput.None)
		set:
			if value:
				DebugOutput = DebugOutput.Enable
			else:
				DebugOutput = DebugOutput.None

	[FrameworkConfigurable('noconfig')]
	[TaskAttribute('noconfig')]
	[BooleanValidator]
	public NoConfig as bool:
		get:
			return _noconfig
		set:
			_noconfig = value

	[FrameworkConfigurable('nostdlib')]
	[TaskAttribute('nostdlib')]
	[BooleanValidator]
	public NoStdLib as bool:
		get:
			return _nostdlib
		set:
			_nostdlib = value

	private def FindBooc() as string:
		path as string
		dir = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)
		if Project.TargetFramework:
			frameworkname = Project.TargetFramework.Name
			path = Path.Combine(Path.Combine(dir, frameworkname), "booc.exe")
			if File.Exists(path):
				return path
			//treat mono-1.0==net-1.1 and mono-##==net-##
			frameworkname = frameworkname.Replace("mono-1.0","net-1.1").Replace("mono-","net-")
			path = Path.Combine(Path.Combine(dir, frameworkname), "booc.exe")
			if File.Exists(path):
				return path
		path = Path.Combine(dir, "booc.exe")
		if File.Exists(path):
			return path
		_useruntime = false
		return "booc" //try booc in PATH

	protected override def ExecuteTask():
		if not ExeName or ExeName == string.Empty:
			ExeName = FindBooc()
		super()

	#endregion Public Instance Properties
	#region Override implementation of CompilerBase
	protected override def WriteOptions(writer as TextWriter):
		if DebugOutput != DebugOutput.None:
			WriteOption(writer, 'debug')
		else:
			WriteOption(writer, 'debug-')
		if NoConfig and (not Arguments.Contains('-noconfig')):
			Arguments.Add(Argument('-noconfig'))
		if NoStdLib:
			WriteOption(writer, "nostdlib")
		if Verbose:
			WriteOption(writer, "vv")

	protected override def WriteOption(writer as TextWriter, name as string):
		writer.WriteLine("-{0}", name)

	protected override def WriteOption(writer as TextWriter, name as string, value as string):
		if value.IndexOf(" ") > 0 and (not value.StartsWith("\"")
						or not value.EndsWith("\"")):
			writer.WriteLine("-{0}:\"{1}\"", name, value)
		else:
			writer.WriteLine("-{0}:{1}", name, value)

	public Extension as string:
		get:
			return 'boo'

	protected ClassNameRegex as Regex:
		get:
			return _classNameRegex

	protected NamespaceRegex as Regex:
		get:
			return _namespaceRegex
	#endregion Override implementation of CompilerBase

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

namespace Boo.NAnt

import System
import System.Globalization
import System.IO
import System.Text.RegularExpressions
import NAnt.Core
import NAnt.Core.Attributes
import NAnt.Core.Types
import NAnt.Core.Util
import NAnt.DotNet.Types

import NAnt.DotNet.Tasks
import System.Reflection

[TaskName('booc')]
public class BoocTask(CompilerBase):
	#region Private Instance Fields
	private _debugOutput as DebugOutput = DebugOutput.Enable
	private _exe as string

	private _useruntime = true //keep true for mono compatibility (don't call booc.exe directly)
	private _noconfig = false
	private _nostdlib = false

	#endregion Private Instance Fields
	#region Private Static Fields
	private static _classNameRegex = Regex('^((?<comment>/\\*.*?(\\*/|$))|[\\s\\.\\{]+|class\\s+(?<class>\\w+)|(?<keyword>\\w+))*')

	private static _namespaceRegex = Regex('^((?<comment>/\\*.*?(\\*/|$))|[\\s\\.\\{]+|namespace\\s+(?<namespace>(\\w+(\\.\\w+)*)+)|(?<keyword>\\w+))*')

	#endregion Private Static Fields

	def constructor():
		SupportsKeyFile = true
		SupportsKeyContainer = true

	[FrameworkConfigurable("exename")]
	[TaskAttribute('exename')]
	public override ExeName as string:
		get:
			return _exe
		set:
			_exe = value

	[FrameworkConfigurable("useruntimeengine")]
	[TaskAttribute('useruntimeengine')]
	override UseRuntimeEngine as bool:
		get:
			return _useruntime
		set:
			_useruntime = value

	#region Public Instance Properties

	[TaskAttribute('debug')]
	public DebugOutput as DebugOutput:
		get:
			return _debugOutput
		set:
			_debugOutput = value

	public Debug as bool:
		get:
			return (DebugOutput != DebugOutput.None)
		set:
			if value:
				DebugOutput = DebugOutput.Enable
			else:
				DebugOutput = DebugOutput.None

	[FrameworkConfigurable('noconfig')]
	[TaskAttribute('noconfig')]
	[BooleanValidator]
	public NoConfig as bool:
		get:
			return _noconfig
		set:
			_noconfig = value

	[FrameworkConfigurable('nostdlib')]
	[TaskAttribute('nostdlib')]
	[BooleanValidator]
	public NoStdLib as bool:
		get:
			return _nostdlib
		set:
			_nostdlib = value

	private def FindBooc() as string:
		path as string
		dir = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)
		if Project.TargetFramework:
			path = Path.Combine(dir, Project.TargetFramework.Name)
			path = Path.Combine(path, "booc.exe")
			if File.Exists(path):
				return path
		path = Path.Combine(dir, "booc.exe")
		if File.Exists(path):
			return path
		_useruntime = false
		return "booc" //try booc in PATH

	protected override def ExecuteTask():
		if not ExeName or ExeName == string.Empty:
			ExeName = FindBooc()
		super()

	#endregion Public Instance Properties
	#region Override implementation of CompilerBase
	protected override def WriteOptions(writer as TextWriter):
		if DebugOutput != DebugOutput.None:
			WriteOption(writer, 'debug')
		else:
			WriteOption(writer, 'debug-')
		if NoConfig and (not Arguments.Contains('-noconfig')):
			Arguments.Add(Argument('-noconfig'))
		if NoStdLib:
			WriteOption(writer, "nostdlib")
		if Verbose:
			WriteOption(writer, "vv")

	protected override def WriteOption(writer as TextWriter, name as string):
		writer.WriteLine("-{0}", name)

	protected override def WriteOption(writer as TextWriter, name as string, value as string):
		if value.IndexOf(" ") > 0 and (not value.StartsWith("\"")
						or not value.EndsWith("\"")):
			writer.WriteLine("-{0}:\"{1}\"", name, value)
		else:
			writer.WriteLine("-{0}:{1}", name, value)

	public Extension as string:
		get:
			return 'boo'

	protected ClassNameRegex as Regex:
		get:
			return _classNameRegex

	protected NamespaceRegex as Regex:
		get:
			return _namespaceRegex
	#endregion Override implementation of CompilerBase


ignore exception related to temp directory removal

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

from System import AppDomain, Console, Environment, Guid, STAThreadAttribute
import System.Linq.Enumerable
from System.Diagnostics import Trace, TraceLevel, TextWriterTraceListener, Stopwatch
from System.IO import TextReader, StreamWriter, Directory, File, Path
from System.Reflection import Assembly
from Boo.Lang.Compiler import CompilerContext, CompilerOutputType
from Boo.Lang.Compiler.IO import StringInput, FileInput
from Boo.Lang.Compiler.Pipelines import CompileToMemory, CompileToFile
from Boo.Lang.Compiler.TypeSystem.Reflection import IAssemblyReference
from Boo.Lang.Useful.CommandLine import CommandLineException

class Program:

    public static final Version = 'Booi ' + BooVersion
    public static final DefaultErrorCode = 127
    public static final DefaultSuccessCode = 0

    _args as (string)
    _cmdline as CommandLine

    _assemblyResolver = AssemblyResolver()
    _params = CompilerParameters()

    def constructor(cmdline, args as (string)):
        _cmdline = cmdline
        _args = args

        # Register a custom resolver to provide compiler assemblies at runtime
        AppDomain.CurrentDomain.AssemblyResolve += _assemblyResolver.AssemblyResolve

    protected def LoadDlls(path as string):
        dlls = Directory.GetFiles(path, '*.dll')
        for dll in dlls:
            Trace.TraceInformation('Loading assembly {0}', dll)
            asm = _params.LoadAssembly(dll)
            _params.References.Add(asm)
            _assemblyResolver.AddAssembly(asm.Assembly)

    protected def LoadPackages(path as string):
        # Load all DLLs in the root directory
        LoadDlls(path)

        # Process NuGet style repositories by looking into package directories,
        # only accessing their latests versions if more than one is present.
        dirs = Directory.GetDirectories(path) \
              .GroupBy({ x | /\.\d.*$/.Replace(x, '') }) \
              .Select({ g | g.OrderBy({ f | f.ToLower() }).Last() }) \
              .ToList()

        # Get executing framework version to discard newer assemblies
        maxversion = typeof(System.Type).Assembly.GetName().Version
        for dir in dirs:
            dir = Path.Combine(dir, 'lib')
            continue unless Directory.Exists(dir)

            # Load any generic assemblies in the lib folder
            LoadDlls(dir)

            # Assume we always target the full framework (netXX)
            path = Directory.GetDirectories(dir, 'net*') \
                 .OrderBy({ f | f[-2:] }) \
                 .Where({ f |
                    maxversion >= System.Version(
                        join(f[-2:].ToCharArray(), '.')
                    )
                 }) \
                 .LastOrDefault()

            LoadDlls(path) if path

    def Run() as int:

        # TODO: Instead of always creating a temp directory to copy
        #       all the references, it should be possible to inject code
        #       in the generated assembly that registers to the AppDomain
        #       assembly load event and loads them based on the configuration
        #       used when compiling. This will allow to load NuGet references
        #       on demand too and should work when using nunit.

        _params.OutputType = CompilerOutputType.Auto
        _params.Debug = _cmdline.Debug
        _params.Ducky = _cmdline.Ducky
        _params.Strict = _cmdline.Strict
        _params.WhiteSpaceAgnostic = _cmdline.Wsa
        _params.Cache = _cmdline.Cache

        if _cmdline.Verbose:
            _params.TraceLevel = TraceLevel.Verbose
            Trace.Listeners.Add(TextWriterTraceListener(Console.Error))

        if (_params.Debug and IsMono()) or _cmdline.Runner or _cmdline.Output:
            # Save to disk to ensure we get symbols loaded on runtime errors
            _params.GenerateInMemory = false
            _params.Pipeline = CompileToFile()

            if not _cmdline.Output:
                # Find a temporary directory name for the output
                while true:
                    path = Path.Combine(Path.GetTempPath(), 'booi-' + Path.GetRandomFileName())
                    path = path.Replace('.', '-')
                    break unless Directory.Exists(path)
                Trace.TraceInformation("Creating temporary directory '{0}'", path)
                Directory.CreateDirectory(path)
                _params.OutputAssembly = Path.Combine(path, 'booi-main.exe')
            else:
                if Directory.Exists(_cmdline.Output):
                    path = _cmdline.Output
                    _params.OutputAssembly = Path.Combine(path, 'booi-main.exe')
                else:
                    path = Path.GetDirectoryName(_cmdline.Output)
                    _params.OutputAssembly = _cmdline.Output

        else:
            _params.GenerateInMemory = true
            _params.Pipeline = CompileToMemory()

        for d in _cmdline.Defines:
            _params.Defines.Add(d.Key, d.Value)

        for libpath in _cmdline.LibPaths:
            try:
                _params.LibPaths.AddUnique(Path.GetFullPath(libpath))
            except ex as System.ArgumentException:
                Trace.TraceError("LibPath '{0}' not found", libpath)

        sw = System.Diagnostics.Stopwatch()
        sw.Start()

        for reference in _cmdline.References:
            try:
                asmref = _params.LoadAssembly(reference)
                if asmref is null:
                    printerr string.Format(Boo.Lang.Resources.StringResources.BooC_UnableToLoadAssembly, reference)
                    return DefaultErrorCode

                _params.References.Add(asmref)
                _assemblyResolver.AddAssembly(asmref.Assembly)
            except ex:
                printerr "Error loading reference '{0}': {1}", reference, ex
                return DefaultErrorCode

        # Load packages
        for package in _cmdline.Packages:
            LoadPackages(package) if Directory.Exists(package)

        sw.Stop()
        Trace.TraceInformation("Loading references took {0}ms", sw.ElapsedMilliseconds)

        for file in _cmdline.Files:
            continue if file == '-'
            if Directory.Exists(file):
                for f in Directory.GetFiles(file, '*.boo'):
                    _params.Input.Add(FileInput(f))
            else:
                _params.Input.Add(FileInput(file))

        if not len(_cmdline.Files) or _cmdline.Files.Contains('-'):
            _params.Input.Add(StringInput('<stdin>', consume(Console.In)))

        # Set the MAIN define if we are compiling a single script
        if 1 == len(_params.Input) and not _params.Defines.ContainsKey('MAIN'):
            _params.Defines.Add('MAIN', '')

        sw.Restart()
        generated = Compile()
        return DefaultErrorCode if generated is null
        sw.Stop()
        Trace.TraceInformation('Compilation took {0}ms', sw.ElapsedMilliseconds)

        if _cmdline.Runner or _cmdline.Output:
            # Collect all dependencies in a temporary dir
            for asmref in _params.References:
                asm = asmref.Assembly
                # Skip dynamically generated assemblies
                continue if asm.IsDynamic

                try:
                    asmfile = Path.GetFileName(asm.Location)
                    if asmfile in ('System.dll', 'System.Core.dll', 'mscorlib.dll'):
                        continue

                    # HACK: The assembly containing the parser is imported dynamically by
                    #       the compiler, looking for it to be placed next to it.
                    #       Here we ensure that when the compiler is referenced the parser
                    #       is also copied next to it.
                    if asmfile == 'Boo.Lang.Compiler.dll':
                        File.Copy(
                            asm.Location.Replace('Boo.Lang.Compiler.dll', 'Boo.Lang.Parser.dll'),
                            Path.Combine(path, 'Boo.Lang.Parser.dll')
                        )

                    File.Copy(asm.Location, Path.Combine(path, asmfile))
                except ex:
                    Trace.TraceError("Error copying assembly '{0}' to '{1}'", asmref.Name, path)

            DumpSolutionFile()

        if _cmdline.Runner:
            # Replace place holders in script arguments
            asmpath = generated.Location
            args = List of string()
            if not len(_args):
                args.Add('"' + asmpath + '"')
            else:
                for arg in _args:
                    arg = arg.Replace('%filename%', Path.GetFileName(asmpath))
                    arg = arg.Replace('%pathname%', Path.GetDirectoryName(asmpath))
                    arg = arg.Replace('%assembly%', asmpath)
                    arg = arg.Replace('%solution%', asmpath[:-3] + 'sln')
                    if arg == '%':
                        arg = asmpath
                    args.Add('"' + arg + '"')

            # Try to execute the runner
            Trace.TraceInformation('Running command: {0} {1}', _cmdline.Runner, join(args, ' '))
            retval = runcommand(_cmdline.Runner, join(args, ' '))
        else:
            retval = Execute(generated, _args)

        # Clean up generated files
        if path and not _cmdline.Output:
            Trace.TraceInformation("Removing temporary directory '{0}'", path)
            try:
                Directory.Delete(path, true)
            except x:
                Trace.TraceError("Failed to remove temporary directory: {0}", x)

        return retval

    def Compile():
        context = CompilerContext(_params)
        _params.Pipeline.Run(context)

        if _cmdline.Warnings and len(context.Warnings):
            printerr(context.Warnings.ToString())

        if len(context.Errors):
            printerr(context.Errors.ToString(_params.TraceVerbose))
            return null

        if not context.GeneratedAssembly:
            return Assembly.LoadFrom(context.GeneratedAssemblyFileName)

        return context.GeneratedAssembly

    def Execute(asm as Assembly, argv as (string)):
        exitCode = DefaultSuccessCode
        try:
            _assemblyResolver.AddAssembly(asm)

            main = asm.EntryPoint
            if not main:
                printerr 'Error: Entry point not found'
                return DefaultErrorCode

            if len(main.GetParameters()) > 0:
                returnValue = main.Invoke(null, (argv,))
            else:
                returnValue = main.Invoke(null, null)

            exitCode = returnValue if returnValue is not null
        except x as TargetInvocationException:
            printerr x.InnerException
            exitCode = DefaultErrorCode
        ensure:
            Environment.ExitCode = exitCode

        return exitCode

    protected def DumpSolutionFile():
    """ Create a MonoDevelop solution file to ease debugging. MonoDevelop allows defining
        managed executables as projects.
    """
        asmpath = Path.GetDirectoryName(_params.OutputAssembly)
        asmfile = Path.GetFileName(_params.OutputAssembly)
        asmname = asmfile[:-4]

        # Get assemblies automatically compiled from sources
        assemblies = [asmfile]
        for asmref as IAssemblyReference in _params.References:
            asm = asmref.Assembly
            if asm.IsDynamic:
                assemblies.Add(asmref.Name + '.dll')
                continue

            attrs = asm.GetCustomAttributes(CompilationTimeAttribute, false)
            if len(attrs):
                assemblies.Add(asmref.Name + '.dll')

        fpath = Path.Combine(asmpath, asmname + '.sln')
        using fs = StreamWriter(fpath):
            fs.WriteLine()
            fs.WriteLine('Microsoft Visual Studio Solution File, Format Version 11.00')
            fs.WriteLine('# Visual Studio 2010')
            fs.WriteLine('Global')
            fs.WriteLine('\tGlobalSection(MonoDevelopProperties) = preSolution')
            fs.WriteLine('\t\tStartupItem = {0}', asmname)
            fs.WriteLine('\tEndGlobalSection')
            fs.WriteLine('EndGlobal')

            for asmfile in assemblies:
                # Generate a deterministic Guid based on the file name
                using md5 = System.Security.Cryptography.MD5.Create():
                    bytes = System.Text.Encoding.Default.GetBytes(asmfile)
                    guid = Guid(md5.ComputeHash(bytes))

                fs.WriteLine('Project("{{8BC9CEB9-8B4A-11D0-8D11-00A0C91BC942}}") = "{0}", "{1}", "{{{2}}}"', asmfile[:-4], asmfile, guid)
                fs.WriteLine('\tProjectSection(MonoDevelopProperties) = preProject')
                fs.WriteLine('\t\tConfigurations = $0')
                fs.WriteLine('\t\t$0.Configuration = $1')
                fs.WriteLine('\t\t$1.name = Debug')
                fs.WriteLine('\t\t$1.OutputPath = ./')
                fs.WriteLine('\t\t$1.ctype = ProjectConfiguration')
                fs.WriteLine('\tEndProjectSection')
                fs.WriteLine('EndProject')

            # Include an NUnit assembly test collection project
            fs.WriteLine('Project("{9344bdbb-3e7f-41fc-a0dd-8665d75ee146}") = "nunit", "nunit.mdproj", "{4B9B3417-6A64-4D19-912A-9A7C50828CEA}"')
            fs.WriteLine('EndProject')

        # Generate an mdproj file for the NUnit assembly test collection
        fpath = Path.Combine(asmpath, 'nunit.mdproj')
        using fs = StreamWriter(fpath):
            fs.WriteLine('<?xml version="1.0" encoding="utf-8"?>')
            fs.WriteLine('<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">')
            fs.WriteLine('<PropertyGroup>')
            fs.WriteLine(' <Configuration Condition=" \'$(Configuration)\' == \'\' ">Default</Configuration>')
            fs.WriteLine(' <Platform Condition=" \'$(Platform)\' == \'\' ">AnyCPU</Platform>')
            fs.WriteLine(' <ItemType>NUnitAssemblyGroupProject</ItemType>')
            fs.WriteLine(' <ProductVersion>10.0.0</ProductVersion>')
            fs.WriteLine(' <SchemaVersion>2.0</SchemaVersion>')
            fs.WriteLine(' <ProjectGuid>{{4B9B3417-6A64-4D19-912A-9A7C50828CEA}}</ProjectGuid>')
            fs.WriteLine('</PropertyGroup>')
            fs.WriteLine('<PropertyGroup Condition=" \'$(Configuration)|$(Platform)\' == \'Default|AnyCPU\' ">')
            fs.WriteLine(' <Assemblies>')
            fs.WriteLine('  <Assemblies>')
            for asmfile in assemblies:
                fs.WriteLine('   <Assembly Path="{0}" />', Path.GetFullPath(Path.Combine(asmpath, asmfile)))
            fs.WriteLine('  </Assemblies>')
            fs.WriteLine(' </Assemblies>')
            fs.WriteLine('</PropertyGroup>')
            fs.WriteLine('</Project>')

def IsMono():
    return System.Type.GetType("Mono.Runtime") is not null

def runcommand(filename, arguments):
    p = System.Diagnostics.Process()
    p.StartInfo.Arguments = arguments
    p.StartInfo.CreateNoWindow = true
    p.StartInfo.UseShellExecute = true
    p.StartInfo.FileName = filename
    p.Start()
    p.WaitForExit()
    return p.ExitCode

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

def printerr(*args):
    for arg in args:
        Console.Error.Write(arg)
        Console.Error.Write(' ')
    Console.Error.WriteLine()

[STAThread]
def Main(argv as (string)) as int:
    # Find the number of arguments that apply to booi
    argc = System.Array.IndexOf(argv, '--')
    if argc >= 0:
        argv = argv[:argc] + argv[argc+1:]
    else:
        argc = 0
        for arg in argv:
            argc++
            break unless arg.StartsWith('-')

    try:
        cmdline = CommandLine(argv[:argc])
    except ex as CommandLineException:
        printerr 'Error:', ex.Message
        return Program.DefaultErrorCode

    if cmdline.DoVersion:
        print Program.Version
        return Program.DefaultSuccessCode

    if not cmdline.IsValid or cmdline.DoHelp:
        print "Usage: booi [options] <script|-> [-- [script options]]"
        print "Options:"
        cmdline.PrintOptions(Console.Out)
        return Program.DefaultErrorCode

    return Program(cmdline, argv[argc:]).Run()

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

from System import AppDomain, Console, Environment, Guid, STAThreadAttribute
import System.Linq.Enumerable
from System.Diagnostics import Trace, TraceLevel, TextWriterTraceListener, Stopwatch
from System.IO import TextReader, StreamWriter, Directory, File, Path
from System.Reflection import Assembly
from Boo.Lang.Compiler import CompilerContext, CompilerOutputType
from Boo.Lang.Compiler.IO import StringInput, FileInput
from Boo.Lang.Compiler.Pipelines import CompileToMemory, CompileToFile
from Boo.Lang.Compiler.TypeSystem.Reflection import IAssemblyReference
from Boo.Lang.Useful.CommandLine import CommandLineException

class Program:

    public static final Version = 'Booi ' + BooVersion
    public static final DefaultErrorCode = 127
    public static final DefaultSuccessCode = 0

    _args as (string)
    _cmdline as CommandLine

    _assemblyResolver = AssemblyResolver()
    _params = CompilerParameters()

    def constructor(cmdline, args as (string)):
        _cmdline = cmdline
        _args = args

        # Register a custom resolver to provide compiler assemblies at runtime
        AppDomain.CurrentDomain.AssemblyResolve += _assemblyResolver.AssemblyResolve

    protected def LoadDlls(path as string):
        dlls = Directory.GetFiles(path, '*.dll')
        for dll in dlls:
            Trace.TraceInformation('Loading assembly {0}', dll)
            asm = _params.LoadAssembly(dll)
            _params.References.Add(asm)
            _assemblyResolver.AddAssembly(asm.Assembly)

    protected def LoadPackages(path as string):
        # Load all DLLs in the root directory
        LoadDlls(path)

        # Process NuGet style repositories by looking into package directories,
        # only accessing their latests versions if more than one is present.
        dirs = Directory.GetDirectories(path) \
              .GroupBy({ x | /\.\d.*$/.Replace(x, '') }) \
              .Select({ g | g.OrderBy({ f | f.ToLower() }).Last() }) \
              .ToList()

        # Get executing framework version to discard newer assemblies
        maxversion = typeof(System.Type).Assembly.GetName().Version
        for dir in dirs:
            dir = Path.Combine(dir, 'lib')
            continue unless Directory.Exists(dir)

            # Load any generic assemblies in the lib folder
            LoadDlls(dir)

            # Assume we always target the full framework (netXX)
            path = Directory.GetDirectories(dir, 'net*') \
                 .OrderBy({ f | f[-2:] }) \
                 .Where({ f |
                    maxversion >= System.Version(
                        join(f[-2:].ToCharArray(), '.')
                    )
                 }) \
                 .LastOrDefault()

            LoadDlls(path) if path

    def Run() as int:

        # TODO: Instead of always creating a temp directory to copy
        #       all the references, it should be possible to inject code
        #       in the generated assembly that registers to the AppDomain
        #       assembly load event and loads them based on the configuration
        #       used when compiling. This will allow to load NuGet references
        #       on demand too and should work when using nunit.

        _params.OutputType = CompilerOutputType.Auto
        _params.Debug = _cmdline.Debug
        _params.Ducky = _cmdline.Ducky
        _params.Strict = _cmdline.Strict
        _params.WhiteSpaceAgnostic = _cmdline.Wsa
        _params.Cache = _cmdline.Cache

        if _cmdline.Verbose:
            _params.TraceLevel = TraceLevel.Verbose
            Trace.Listeners.Add(TextWriterTraceListener(Console.Error))

        if (_params.Debug and IsMono()) or _cmdline.Runner or _cmdline.Output:
            # Save to disk to ensure we get symbols loaded on runtime errors
            _params.GenerateInMemory = false
            _params.Pipeline = CompileToFile()

            if not _cmdline.Output:
                # Find a temporary directory name for the output
                while true:
                    path = Path.Combine(Path.GetTempPath(), 'booi-' + Path.GetRandomFileName())
                    path = path.Replace('.', '-')
                    break unless Directory.Exists(path)
                Trace.TraceInformation("Creating temporary directory '{0}'", path)
                Directory.CreateDirectory(path)
                _params.OutputAssembly = Path.Combine(path, 'booi-main.exe')
            else:
                if Directory.Exists(_cmdline.Output):
                    path = _cmdline.Output
                    _params.OutputAssembly = Path.Combine(path, 'booi-main.exe')
                else:
                    path = Path.GetDirectoryName(_cmdline.Output)
                    _params.OutputAssembly = _cmdline.Output

        else:
            _params.GenerateInMemory = true
            _params.Pipeline = CompileToMemory()

        for d in _cmdline.Defines:
            _params.Defines.Add(d.Key, d.Value)

        for libpath in _cmdline.LibPaths:
            try:
                _params.LibPaths.AddUnique(Path.GetFullPath(libpath))
            except ex as System.ArgumentException:
                Trace.TraceError("LibPath '{0}' not found", libpath)

        sw = System.Diagnostics.Stopwatch()
        sw.Start()

        for reference in _cmdline.References:
            try:
                asmref = _params.LoadAssembly(reference)
                if asmref is null:
                    printerr string.Format(Boo.Lang.Resources.StringResources.BooC_UnableToLoadAssembly, reference)
                    return DefaultErrorCode

                _params.References.Add(asmref)
                _assemblyResolver.AddAssembly(asmref.Assembly)
            except ex:
                printerr "Error loading reference '{0}': {1}", reference, ex
                return DefaultErrorCode

        # Load packages
        for package in _cmdline.Packages:
            LoadPackages(package) if Directory.Exists(package)

        sw.Stop()
        Trace.TraceInformation("Loading references took {0}ms", sw.ElapsedMilliseconds)

        for file in _cmdline.Files:
            continue if file == '-'
            if Directory.Exists(file):
                for f in Directory.GetFiles(file, '*.boo'):
                    _params.Input.Add(FileInput(f))
            else:
                _params.Input.Add(FileInput(file))

        if not len(_cmdline.Files) or _cmdline.Files.Contains('-'):
            _params.Input.Add(StringInput('<stdin>', consume(Console.In)))

        # Set the MAIN define if we are compiling a single script
        if 1 == len(_params.Input) and not _params.Defines.ContainsKey('MAIN'):
            _params.Defines.Add('MAIN', '')

        sw.Restart()
        generated = Compile()
        return DefaultErrorCode if generated is null
        sw.Stop()
        Trace.TraceInformation('Compilation took {0}ms', sw.ElapsedMilliseconds)

        if _cmdline.Runner or _cmdline.Output:
            # Collect all dependencies in a temporary dir
            for asmref in _params.References:
                asm = asmref.Assembly
                # Skip dynamically generated assemblies
                continue if asm.IsDynamic

                try:
                    asmfile = Path.GetFileName(asm.Location)
                    if asmfile in ('System.dll', 'System.Core.dll', 'mscorlib.dll'):
                        continue

                    # HACK: The assembly containing the parser is imported dynamically by
                    #       the compiler, looking for it to be placed next to it.
                    #       Here we ensure that when the compiler is referenced the parser
                    #       is also copied next to it.
                    if asmfile == 'Boo.Lang.Compiler.dll':
                        File.Copy(
                            asm.Location.Replace('Boo.Lang.Compiler.dll', 'Boo.Lang.Parser.dll'),
                            Path.Combine(path, 'Boo.Lang.Parser.dll')
                        )

                    File.Copy(asm.Location, Path.Combine(path, asmfile))
                except ex:
                    Trace.TraceError("Error copying assembly '{0}' to '{1}'", asmref.Name, path)

            DumpSolutionFile()

        if _cmdline.Runner:
            # Replace place holders in script arguments
            asmpath = generated.Location
            args = List of string()
            if not len(_args):
                args.Add('"' + asmpath + '"')
            else:
                for arg in _args:
                    arg = arg.Replace('%filename%', Path.GetFileName(asmpath))
                    arg = arg.Replace('%pathname%', Path.GetDirectoryName(asmpath))
                    arg = arg.Replace('%assembly%', asmpath)
                    arg = arg.Replace('%solution%', asmpath[:-3] + 'sln')
                    if arg == '%':
                        arg = asmpath
                    args.Add('"' + arg + '"')

            # Try to execute the runner
            Trace.TraceInformation('Running command: {0} {1}', _cmdline.Runner, join(args, ' '))
            retval = runcommand(_cmdline.Runner, join(args, ' '))
        else:
            retval = Execute(generated, _args)

        # Clean up generated files
        if path and not _cmdline.Output:
            Trace.TraceInformation("Removing temporary directory '{0}'", path)
            Directory.Delete(path, true)

        return retval

    def Compile():
        context = CompilerContext(_params)
        _params.Pipeline.Run(context)

        if _cmdline.Warnings and len(context.Warnings):
            printerr(context.Warnings.ToString())

        if len(context.Errors):
            printerr(context.Errors.ToString(_params.TraceVerbose))
            return null

        if not context.GeneratedAssembly:
            return Assembly.LoadFrom(context.GeneratedAssemblyFileName)

        return context.GeneratedAssembly

    def Execute(asm as Assembly, argv as (string)):
        exitCode = DefaultSuccessCode
        try:
            _assemblyResolver.AddAssembly(asm)

            main = asm.EntryPoint
            if not main:
                printerr 'Error: Entry point not found'
                return DefaultErrorCode

            if len(main.GetParameters()) > 0:
                returnValue = main.Invoke(null, (argv,))
            else:
                returnValue = main.Invoke(null, null)

            exitCode = returnValue if returnValue is not null
        except x as TargetInvocationException:
            printerr x.InnerException
            exitCode = DefaultErrorCode
        ensure:
            Environment.ExitCode = exitCode

        return exitCode

    protected def DumpSolutionFile():
    """ Create a MonoDevelop solution file to ease debugging. MonoDevelop allows defining
        managed executables as projects.
    """
        asmpath = Path.GetDirectoryName(_params.OutputAssembly)
        asmfile = Path.GetFileName(_params.OutputAssembly)
        asmname = asmfile[:-4]

        # Get assemblies automatically compiled from sources
        assemblies = [asmfile]
        for asmref as IAssemblyReference in _params.References:
            asm = asmref.Assembly
            if asm.IsDynamic:
                assemblies.Add(asmref.Name + '.dll')
                continue

            attrs = asm.GetCustomAttributes(CompilationTimeAttribute, false)
            if len(attrs):
                assemblies.Add(asmref.Name + '.dll')

        fpath = Path.Combine(asmpath, asmname + '.sln')
        using fs = StreamWriter(fpath):
            fs.WriteLine()
            fs.WriteLine('Microsoft Visual Studio Solution File, Format Version 11.00')
            fs.WriteLine('# Visual Studio 2010')
            fs.WriteLine('Global')
            fs.WriteLine('\tGlobalSection(MonoDevelopProperties) = preSolution')
            fs.WriteLine('\t\tStartupItem = {0}', asmname)
            fs.WriteLine('\tEndGlobalSection')
            fs.WriteLine('EndGlobal')

            for asmfile in assemblies:
                # Generate a deterministic Guid based on the file name
                using md5 = System.Security.Cryptography.MD5.Create():
                    bytes = System.Text.Encoding.Default.GetBytes(asmfile)
                    guid = Guid(md5.ComputeHash(bytes))

                fs.WriteLine('Project("{{8BC9CEB9-8B4A-11D0-8D11-00A0C91BC942}}") = "{0}", "{1}", "{{{2}}}"', asmfile[:-4], asmfile, guid)
                fs.WriteLine('\tProjectSection(MonoDevelopProperties) = preProject')
                fs.WriteLine('\t\tConfigurations = $0')
                fs.WriteLine('\t\t$0.Configuration = $1')
                fs.WriteLine('\t\t$1.name = Debug')
                fs.WriteLine('\t\t$1.OutputPath = ./')
                fs.WriteLine('\t\t$1.ctype = ProjectConfiguration')
                fs.WriteLine('\tEndProjectSection')
                fs.WriteLine('EndProject')

            # Include an NUnit assembly test collection project
            fs.WriteLine('Project("{9344bdbb-3e7f-41fc-a0dd-8665d75ee146}") = "nunit", "nunit.mdproj", "{4B9B3417-6A64-4D19-912A-9A7C50828CEA}"')
            fs.WriteLine('EndProject')

        # Generate an mdproj file for the NUnit assembly test collection
        fpath = Path.Combine(asmpath, 'nunit.mdproj')
        using fs = StreamWriter(fpath):
            fs.WriteLine('<?xml version="1.0" encoding="utf-8"?>')
            fs.WriteLine('<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">')
            fs.WriteLine('<PropertyGroup>')
            fs.WriteLine(' <Configuration Condition=" \'$(Configuration)\' == \'\' ">Default</Configuration>')
            fs.WriteLine(' <Platform Condition=" \'$(Platform)\' == \'\' ">AnyCPU</Platform>')
            fs.WriteLine(' <ItemType>NUnitAssemblyGroupProject</ItemType>')
            fs.WriteLine(' <ProductVersion>10.0.0</ProductVersion>')
            fs.WriteLine(' <SchemaVersion>2.0</SchemaVersion>')
            fs.WriteLine(' <ProjectGuid>{{4B9B3417-6A64-4D19-912A-9A7C50828CEA}}</ProjectGuid>')
            fs.WriteLine('</PropertyGroup>')
            fs.WriteLine('<PropertyGroup Condition=" \'$(Configuration)|$(Platform)\' == \'Default|AnyCPU\' ">')
            fs.WriteLine(' <Assemblies>')
            fs.WriteLine('  <Assemblies>')
            for asmfile in assemblies:
                fs.WriteLine('   <Assembly Path="{0}" />', Path.GetFullPath(Path.Combine(asmpath, asmfile)))
            fs.WriteLine('  </Assemblies>')
            fs.WriteLine(' </Assemblies>')
            fs.WriteLine('</PropertyGroup>')
            fs.WriteLine('</Project>')

def IsMono():
    return System.Type.GetType("Mono.Runtime") is not null

def runcommand(filename, arguments):
    p = System.Diagnostics.Process()
    p.StartInfo.Arguments = arguments
    p.StartInfo.CreateNoWindow = true
    p.StartInfo.UseShellExecute = true
    p.StartInfo.FileName = filename
    p.Start()
    p.WaitForExit()
    return p.ExitCode

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

def printerr(*args):
    for arg in args:
        Console.Error.Write(arg)
        Console.Error.Write(' ')
    Console.Error.WriteLine()

[STAThread]
def Main(argv as (string)) as int:
    # Find the number of arguments that apply to booi
    argc = System.Array.IndexOf(argv, '--')
    if argc >= 0:
        argv = argv[:argc] + argv[argc+1:]
    else:
        argc = 0
        for arg in argv:
            argc++
            break unless arg.StartsWith('-')

    try:
        cmdline = CommandLine(argv[:argc])
    except ex as CommandLineException:
        printerr 'Error:', ex.Message
        return Program.DefaultErrorCode

    if cmdline.DoVersion:
        print Program.Version
        return Program.DefaultSuccessCode

    if not cmdline.IsValid or cmdline.DoHelp:
        print "Usage: booi [options] <script|-> [-- [script options]]"
        print "Options:"
        cmdline.PrintOptions(Console.Out)
        return Program.DefaultErrorCode

    return Program(cmdline, argv[argc:]).Run()


keep it very similar to the UnityScript version to easy copying back'n'forth

namespace Boo.MonoDevelop.ProjectModel

import Boo.Lang.Compiler.Ast
import MonoDevelop.Projects.Dom as MD
import Boo.Lang.PatternMatching

class DomConversionVisitor(DepthFirstVisitor):

	_result as MD.CompilationUnit
	_currentType as MD.DomType
	_namespace as string

	def constructor(result as MD.CompilationUnit):
		_result = result

	override def OnModule(node as Module):
		_namespace = null
		Visit(node.Namespace)
		Visit(node.Members)

	override def OnNamespaceDeclaration(node as NamespaceDeclaration):
		_namespace = node.Name
		region = BodyRegionOf(node.ParentNode)
		domUsing = MD.DomUsing(IsFromNamespace: true, Region: region)
		domUsing.Add(_namespace)
		_result.Add(domUsing)

	override def OnImport(node as Import):
		region = BodyRegionOf(node)
		domUsing = MD.DomUsing(Region: region)
		domUsing.Add(node.Namespace)
		_result.Add(domUsing)

	override def OnClassDefinition(node as ClassDefinition):
		OnTypeDefinition(node, MD.ClassType.Class)

	override def OnInterfaceDefinition(node as InterfaceDefinition):
		OnTypeDefinition(node, MD.ClassType.Interface)

	override def OnStructDefinition(node as StructDefinition):
		OnTypeDefinition(node, MD.ClassType.Struct)

	override def OnEnumDefinition(node as EnumDefinition):
		OnTypeDefinition(node, MD.ClassType.Enum)

	def OnTypeDefinition(node as TypeDefinition, classType as MD.ClassType):
		converted = MD.DomType(
						Name: node.Name,
						ClassType: classType,
						Location: LocationOf(node),
						BodyRegion: BodyRegionOf(node),
						DeclaringType: _currentType,
						Modifiers: ModifiersFrom(node))

		WithCurrentType converted:
			Visit(node.Members)

		AddType(converted)

	override def OnCallableDefinition(node as CallableDefinition):
		parameters = System.Collections.Generic.List[of MD.IParameter]()
		for p in node.Parameters: parameters.Add(ParameterFrom(null, p))

		converted = MD.DomType.CreateDelegate(_result, node.Name, LocationOf(node), ReturnTypeFrom(node.ReturnType), parameters)
		converted.Modifiers = ModifiersFrom(node)
		converted.DeclaringType = _currentType
		converted.BodyRegion = BodyRegionOf(node)

		for p in parameters: p.DeclaringMember = converted

		AddType(converted)

	override def OnField(node as Field):
		if _currentType is null: return

		_currentType.Add(MD.DomField(
							Name: node.Name,
							ReturnType: ParameterTypeFrom(node.Type),
							Location: LocationOf(node),
							BodyRegion: BodyRegionOf(node),
							DeclaringType: _currentType,
							Modifiers: ModifiersFrom(node)))

	override def OnProperty(node as Property):
		if _currentType is null: return

		converted = MD.DomProperty(
							Name: node.Name,
							ReturnType: ParameterTypeFrom(node.Type),
							Location: LocationOf(node),
							BodyRegion: BodyRegionOf(node),
							DeclaringType: _currentType,
							Modifiers: ModifiersFrom(node))
		if node.Getter is not null:
			converted.PropertyModifier |= MD.PropertyModifier.HasGet
			converted.GetRegion = BodyRegionOf(node.Getter)
		if node.Setter is not null:
			converted.PropertyModifier |= MD.PropertyModifier.HasSet
			converted.SetRegion = BodyRegionOf(node.Setter)

		_currentType.Add(converted)

	override def OnEnumMember(node as EnumMember):
		if _currentType is null: return

		_currentType.Add(MD.DomField(
							Name: node.Name,
							ReturnType: MD.DomReturnType(_currentType),
							Location: LocationOf(node),
							DeclaringType: _currentType,
							Modifiers: MD.Modifiers.Public | MD.Modifiers.Static | MD.Modifiers.Final))

	override def OnConstructor(node as Constructor):
		OnMethod(node, MD.MethodModifier.IsConstructor)

	override def OnDestructor(node as Destructor):
		OnMethod(node, MD.MethodModifier.IsFinalizer)

	override def OnMethod(node as Method):
		OnMethod(node, MD.MethodModifier.None)

	def OnMethod(node as Method, methodModifier as MD.MethodModifier):
		if _currentType is null: return

		converted = MD.DomMethod(
							Name: node.Name,
							Location: LocationOf(node),
							BodyRegion: BodyRegionOf(node),
							DeclaringType: _currentType,
							ReturnType: (MethodReturnTypeFrom(node) if IsRegularMethod(methodModifier) else null),
							Modifiers: ModifiersFrom(node),
							MethodModifier: methodModifier)

		for parameter in node.Parameters:
			converted.Add(ParameterFrom(converted, parameter))

		_currentType.Add(converted)

	def IsRegularMethod(modifier as MD.MethodModifier):
		match modifier:
			case MD.MethodModifier.IsConstructor | MD.MethodModifier.IsFinalizer:
				return false
			otherwise:
				return true

	def ModifiersFrom(node as TypeMember):
		modifiers = MD.Modifiers.None
		modifiers |= MD.Modifiers.Public if node.IsPublic
		modifiers |= MD.Modifiers.Private if node.IsPrivate
		modifiers |= MD.Modifiers.Protected if node.IsProtected
		modifiers |= MD.Modifiers.Internal if node.IsInternal
		modifiers |= MD.Modifiers.Static if node.IsStatic
		modifiers |= MD.Modifiers.Virtual if node.IsVirtual
		modifiers |= MD.Modifiers.Abstract if node.IsAbstract
		modifiers |= MD.Modifiers.Override if node.IsOverride
		modifiers |= MD.Modifiers.Final if node.IsFinal
		return modifiers

	def ParameterFrom(declaringMember as MD.IMember, parameter as ParameterDeclaration):
		return MD.DomParameter(
					DeclaringMember: declaringMember,
					Name: parameter.Name,
					ReturnType: ParameterTypeFrom(parameter.Type),
					Location: LocationOf(parameter))

	virtual def MethodReturnTypeFrom(method as Method):
		if method.ReturnType is not null:
			return ReturnTypeFrom(method.ReturnType)

		match ReturnTypeDetector().Detect(method):
			case ReturnTypeDetector.Result.Yields:
				return MD.DomReturnType("System.Collections.IEnumerator")
			case ReturnTypeDetector.Result.Returns:
				return DefaultReturnType()
			otherwise:
				return MD.DomReturnType("void")

	class ReturnTypeDetector(DepthFirstVisitor):
		enum Result:
			Returns
			Yields
			None

		_result = Result.None

		def Detect(node as Method):
			VisitAllowingCancellation(node)
			return _result

		override def OnBlockExpression(node as BlockExpression):
			pass // skip over closures

		override def OnReturnStatement(node as ReturnStatement):
			if node.Expression is null: return
			_result = Result.Returns
			Cancel()

		override def OnYieldStatement(node as YieldStatement):
			_result = Result.Yields
			Cancel()

	virtual def ParameterTypeFrom(typeRef as TypeReference):
		if typeRef is null: return DefaultReturnType()
		return ReturnTypeFrom(typeRef)

	virtual def ReturnTypeFrom(typeRef as TypeReference) as MD.DomReturnType:
		match typeRef:
			case SimpleTypeReference(Name: name):
				return MD.DomReturnType(name)
			case ArrayTypeReference(ElementType: elementType):
				type = ReturnTypeFrom(elementType)
				type.ArrayDimensions = 1
				type.SetDimension(0, 1)
				return type
			otherwise:
				return MD.DomReturnType("void")

	def AddType(type as MD.DomType):
		if _currentType is not null:
			_currentType.Add(type)
		else:
			type.Namespace = _namespace
			_result.Add(type)

	def WithCurrentType(type as MD.DomType, block as callable()):
		saved = _currentType
		_currentType = type
		try:
			block()
		ensure:
			_currentType = saved

	def BodyRegionOf(node as Node):
		return MD.DomRegion(DomLocationFrom(node.LexicalInfo), DomLocationFrom(node.EndSourceLocation))

	def LocationOf(node as Node):
		location = node.LexicalInfo
		return DomLocationFrom(location)

	def DomLocationFrom(location as SourceLocation):
		return MD.DomLocation(location.Line, location.Column)

	def DefaultReturnType():
		return MD.DomReturnType("object")

namespace Boo.MonoDevelop.ProjectModel

import Boo.Lang.Compiler.Ast
import MonoDevelop.Projects.Dom as MD
import Boo.Lang.PatternMatching

class DomConversionVisitor(DepthFirstVisitor):

	_result as MD.CompilationUnit
	_currentType as MD.DomType
	_namespace as string

	def constructor(result as MD.CompilationUnit):
		_result = result

	override def OnModule(node as Module):
		_namespace = null
		Visit(node.Namespace)
		Visit(node.Members)

	override def OnNamespaceDeclaration(node as NamespaceDeclaration):
		_namespace = node.Name
		region = BodyRegionOf(node.ParentNode)
		domUsing = MD.DomUsing(IsFromNamespace: true, Region: region)
		domUsing.Add(_namespace)
		_result.Add(domUsing)

	override def OnImport(node as Import):
		region = BodyRegionOf(node)
		domUsing = MD.DomUsing(Region: region)
		domUsing.Add(node.Namespace)
		_result.Add(domUsing)

	override def OnClassDefinition(node as ClassDefinition):
		OnTypeDefinition(node, MD.ClassType.Class)

	override def OnInterfaceDefinition(node as InterfaceDefinition):
		OnTypeDefinition(node, MD.ClassType.Interface)

	override def OnStructDefinition(node as StructDefinition):
		OnTypeDefinition(node, MD.ClassType.Struct)

	override def OnEnumDefinition(node as EnumDefinition):
		OnTypeDefinition(node, MD.ClassType.Enum)

	def OnTypeDefinition(node as TypeDefinition, classType as MD.ClassType):
		converted = MD.DomType(
						Name: node.Name,
						ClassType: classType,
						Location: LocationOf(node),
						BodyRegion: BodyRegionOf(node),
						DeclaringType: _currentType,
						Modifiers: ModifiersFrom(node))

		WithCurrentType converted:
			Visit(node.Members)

		AddType(converted)

	override def OnCallableDefinition(node as CallableDefinition):
		parameters = System.Collections.Generic.List[of MD.IParameter]()
		for p in node.Parameters: parameters.Add(ParameterFrom(null, p))

		converted = MD.DomType.CreateDelegate(_result, node.Name, LocationOf(node), ReturnTypeFrom(node.ReturnType), parameters)
		converted.Modifiers = ModifiersFrom(node)
		converted.DeclaringType = _currentType
		converted.BodyRegion = BodyRegionOf(node)

		for p in parameters: p.DeclaringMember = converted

		AddType(converted)

	override def OnField(node as Field):
		if _currentType is null: return

		_currentType.Add(MD.DomField(
							Name: node.Name,
							ReturnType: ParameterTypeFrom(node.Type),
							Location: LocationOf(node),
							BodyRegion: BodyRegionOf(node),
							DeclaringType: _currentType,
							Modifiers: ModifiersFrom(node)))

	override def OnProperty(node as Property):
		if _currentType is null: return

		converted = MD.DomProperty(
							Name: node.Name,
							ReturnType: ParameterTypeFrom(node.Type),
							Location: LocationOf(node),
							BodyRegion: BodyRegionOf(node),
							DeclaringType: _currentType,
							Modifiers: ModifiersFrom(node))
		if node.Getter is not null:
			converted.PropertyModifier |= MD.PropertyModifier.HasGet
			converted.GetRegion = BodyRegionOf(node.Getter)
		if node.Setter is not null:
			converted.PropertyModifier |= MD.PropertyModifier.HasSet
			converted.SetRegion = BodyRegionOf(node.Setter)

		_currentType.Add(converted)

	override def OnEnumMember(node as EnumMember):
		if _currentType is null: return

		_currentType.Add(MD.DomField(
							Name: node.Name,
							ReturnType: MD.DomReturnType(_currentType),
							Location: LocationOf(node),
							DeclaringType: _currentType,
							Modifiers: MD.Modifiers.Public | MD.Modifiers.Static | MD.Modifiers.Final))

	override def OnConstructor(node as Constructor):
		OnMethod(node, MD.MethodModifier.IsConstructor)

	override def OnDestructor(node as Destructor):
		OnMethod(node, MD.MethodModifier.IsFinalizer)

	override def OnMethod(node as Method):
		OnMethod(node, MD.MethodModifier.None)

	def OnMethod(node as Method, methodModifier as MD.MethodModifier):
		if _currentType is null: return

		converted = MD.DomMethod(
							Name: node.Name,
							Location: LocationOf(node),
							BodyRegion: BodyRegionOf(node),
							DeclaringType: _currentType,
							ReturnType: (MethodReturnTypeFrom(node) if IsRegularMethod(methodModifier) else null),
							Modifiers: ModifiersFrom(node),
							MethodModifier: methodModifier)

		for parameter in node.Parameters:
			converted.Add(ParameterFrom(converted, parameter))

		_currentType.Add(converted)

	def IsRegularMethod(modifier as MD.MethodModifier):
		match modifier:
			case MD.MethodModifier.IsConstructor | MD.MethodModifier.IsFinalizer:
				return false
			otherwise:
				return true

	def ModifiersFrom(node as TypeMember):
		modifiers = MD.Modifiers.None
		modifiers |= MD.Modifiers.Public if node.IsPublic
		modifiers |= MD.Modifiers.Private if node.IsPrivate
		modifiers |= MD.Modifiers.Protected if node.IsProtected
		modifiers |= MD.Modifiers.Internal if node.IsInternal
		modifiers |= MD.Modifiers.Static if node.IsStatic
		modifiers |= MD.Modifiers.Virtual if node.IsVirtual
		modifiers |= MD.Modifiers.Abstract if node.IsAbstract
		modifiers |= MD.Modifiers.Override if node.IsOverride
		modifiers |= MD.Modifiers.Final if node.IsFinal
		return modifiers

	def ParameterFrom(declaringMember as MD.IMember, parameter as ParameterDeclaration):
		return MD.DomParameter(
					DeclaringMember: declaringMember,
					Name: parameter.Name,
					ReturnType: ParameterTypeFrom(parameter.Type),
					Location: LocationOf(parameter))

	virtual def MethodReturnTypeFrom(method as Method):
		if method.ReturnType is not null:
			return ReturnTypeFrom(method.ReturnType)

		match ReturnTypeDetector().Detect(method):
			case ReturnTypeDetector.Result.Yields:
				return MD.DomReturnType("System.Collections.IEnumerator")
			case ReturnTypeDetector.Result.Returns:
				return MD.DomReturnType("object")
			otherwise:
				return MD.DomReturnType("void")

	class ReturnTypeDetector(DepthFirstVisitor):
		enum Result:
			Returns
			Yields
			None

		_result = Result.None

		def Detect(node as Method):
			VisitAllowingCancellation(node)
			return _result

		override def OnBlockExpression(node as BlockExpression):
			pass // skip over closures

		override def OnReturnStatement(node as ReturnStatement):
			if node.Expression is null: return
			_result = Result.Returns
			Cancel()

		override def OnYieldStatement(node as YieldStatement):
			_result = Result.Yields
			Cancel()

	virtual def ParameterTypeFrom(typeRef as TypeReference):
		if typeRef is null: return MD.DomReturnType("object")
		return ReturnTypeFrom(typeRef)

	virtual def ReturnTypeFrom(typeRef as TypeReference) as MD.DomReturnType:
		match typeRef:
			case SimpleTypeReference(Name: name):
				return MD.DomReturnType(name)
			case ArrayTypeReference(ElementType: elementType):
				type = ReturnTypeFrom(elementType)
				type.ArrayDimensions = 1
				type.SetDimension(0, 1)
				return type
			otherwise:
				return MD.DomReturnType("void")

	def AddType(type as MD.DomType):
		if _currentType is not null:
			_currentType.Add(type)
		else:
			type.Namespace = _namespace
			_result.Add(type)

	def WithCurrentType(type as MD.DomType, block as callable()):
		saved = _currentType
		_currentType = type
		try:
			block()
		ensure:
			_currentType = saved

	def BodyRegionOf(node as Node):
		return MD.DomRegion(DomLocationFrom(node.LexicalInfo), DomLocationFrom(node.EndSourceLocation))

	def LocationOf(node as Node):
		location = node.LexicalInfo
		return DomLocationFrom(location)

	def DomLocationFrom(location as SourceLocation):
		return MD.DomLocation(location.Line, location.Column)


Adding a controller variable to Brail

Adding a controller variable to Brail

git-svn-id: bab0c2de90ee11a20376049c2cdee62584491f34@1455 73e77b4c-caa6-f847-a29a-24ab75ae54b6

// Copyright 2004-2005 Castle Project - http://www.castleproject.org/
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
namespace Castle.MonoRail.Views.Brail

import System
import System.IO
import System.Diagnostics
import System.Collections
import Castle.MonoRail.Framework

# Base class for all the view scripts, this is the class that is responsible for
# support all the behind the scenes magic such as variable to PropertyBag trasnlation,
# resources usage, etc.
abstract class BrailBase:

	[Property(OutputStream)]
	outputStream as TextWriter

	# This is used by layout scripts only, for outputing the
	# child's content
	[Property(ChildOutput)]
	childOutput as TextWriter

	properties as Hashtable

	# list of dictionaries where additional properties can be stored.
	extendedPropertiesList = []

	context as IRailsEngineContext

	controller as Controller

	viewEngine as BooViewEngine

	def constructor(viewEngine as BooViewEngine, output as TextWriter, context as IRailsEngineContext, controller as Controller):
		self.viewEngine = viewEngine
		self.outputStream = output
		self.context = context
		self.controller = controller
		InitProperties(context, controller)

	# This is the method that will contain all the global code in the script and
	# is run to get the output of the script
	abstract def Run():
		pass

	# The path of the script, this is filled by AddBrailBaseClassStep
	# and is used for sub views
	virtual ScriptDirectory as string:
		get:
			return viewEngine.ViewRootDir

	# Output the subview to the client, this is either a relative path "SubView" which
	# is relative to the current /script/ or an "absolute" path "/home/menu" which is
	# actually relative to ViewDirRoot
	def OutputSubView(subviewName as string):
		OutputSubView(subviewName, {})

	# Similiar to the above function, but with a bunch of parameters that are used
	# just for this subview. This parameters are /not/ inheritable.
	def OutputSubView(subviewName as string, parameters as IDictionary):
		subViewFileName = GetSubViewFilename(subviewName)
		subView = viewEngine.GetCompiledScriptInstance(subViewFileName,outputStream,context, controller)
		for param as DictionaryEntry in parameters:
			subView.properties[param.Key] = param.Value if param.Key != null
		subView.Run()

	# Get the sub view file name, if the subview starts with a '/'
	# then the filename is considered relative to ViewDirRoot
	# otherwise, it's relative to the current script directory
	def GetSubViewFilename(subviewName as string):
		#absolute path from Views directory
		if subviewName[0] == char('/'):
			return viewEngine.GetFileName(subviewName[1:])
		return Path.Combine(ScriptDirectory,subviewName)+".boo"

	# this is called by ReplaceUnknownWithParameters step to create a more dynamic experiance
	# any uknon identifier will be translate into a call for GetParameter('identifier name').
	# This mean that when an uknonwn identifier is in the script, it will only be found on runtime.
	def GetParameter(name as string) as object:
		if properties.Contains(name):
			return properties[name]
		for dic as IDictionary in self.extendedPropertiesList:
			if dic.Contains(name):
				return dic[name]
		raise RailsException("Parameter '${name}' was not found!")

	def AddProperties(properties as IDictionary):
		self.extendedPropertiesList.Add(properties)

	# Allows to check that a parameter was defined
	def IsDefined(name as string) as bool:
		return properties.Contains(name)

	# Initialize all the properties that a script may need
	# One thing to note here is that resources are wrapped in ResourceToDuck wrapper
	# to enable easy use by the script
	def InitProperties(context as IRailsEngineContext, controller as Controller):
		properties = Hashtable(CaseInsensitiveHashCodeProvider.Default,
			CaseInsensitiveComparer.Default)

		properties.Add("context", context)
		properties.Add("request", context.Request)
		properties.Add("response", context.Response)
		properties.Add("session", context.Session)
		properties.Add("controller", controller)

		if controller.Resources is not null:
			for key in controller.Resources.Keys:
				properties.Add(key, ResourceToDuck(controller.Resources[key]))

		for helper in controller.Helpers.Values:
			properties.Add(helper.GetType().Name, helper)

		for key in context.Params.AllKeys:
			continue if key is null
			properties[key] = context.Params[key]

		for entry as DictionaryEntry in context.Flash:
			properties[entry.Key] = entry.Value

		for entry as DictionaryEntry in controller.PropertyBag:
			properties[entry.Key] = entry.Value

		properties["siteRoot"] = context.ApplicationPath

// Copyright 2004-2005 Castle Project - http://www.castleproject.org/
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
namespace Castle.MonoRail.Views.Brail

import System
import System.IO
import System.Diagnostics
import System.Collections
import Castle.MonoRail.Framework

# Base class for all the view scripts, this is the class that is responsible for
# support all the behind the scenes magic such as variable to PropertyBag trasnlation,
# resources usage, etc.
abstract class BrailBase:

	[Property(OutputStream)]
	outputStream as TextWriter

	# This is used by layout scripts only, for outputing the
	# child's content
	[Property(ChildOutput)]
	childOutput as TextWriter

	properties as Hashtable

	# list of dictionaries where additional properties can be stored.
	extendedPropertiesList = []

	context as IRailsEngineContext

	controller as Controller

	viewEngine as BooViewEngine

	def constructor(viewEngine as BooViewEngine, output as TextWriter, context as IRailsEngineContext, controller as Controller):
		self.viewEngine = viewEngine
		self.outputStream = output
		self.context = context
		self.controller = controller
		InitProperties(context, controller)

	# This is the method that will contain all the global code in the script and
	# is run to get the output of the script
	abstract def Run():
		pass

	# The path of the script, this is filled by AddBrailBaseClassStep
	# and is used for sub views
	virtual ScriptDirectory as string:
		get:
			return viewEngine.ViewRootDir

	# Output the subview to the client, this is either a relative path "SubView" which
	# is relative to the current /script/ or an "absolute" path "/home/menu" which is
	# actually relative to ViewDirRoot
	def OutputSubView(subviewName as string):
		OutputSubView(subviewName, {})

	# Similiar to the above function, but with a bunch of parameters that are used
	# just for this subview. This parameters are /not/ inheritable.
	def OutputSubView(subviewName as string, parameters as IDictionary):
		subViewFileName = GetSubViewFilename(subviewName)
		subView = viewEngine.GetCompiledScriptInstance(subViewFileName,outputStream,context, controller)
		for param as DictionaryEntry in parameters:
			subView.properties[param.Key] = param.Value if param.Key != null
		subView.Run()

	# Get the sub view file name, if the subview starts with a '/'
	# then the filename is considered relative to ViewDirRoot
	# otherwise, it's relative to the current script directory
	def GetSubViewFilename(subviewName as string):
		#absolute path from Views directory
		if subviewName[0] == char('/'):
			return viewEngine.GetFileName(subviewName[1:])
		return Path.Combine(ScriptDirectory,subviewName)+".boo"

	# this is called by ReplaceUnknownWithParameters step to create a more dynamic experiance
	# any uknon identifier will be translate into a call for GetParameter('identifier name').
	# This mean that when an uknonwn identifier is in the script, it will only be found on runtime.
	def GetParameter(name as string) as object:
		if properties.Contains(name):
			return properties[name]
		for dic as IDictionary in self.extendedPropertiesList:
			if dic.Contains(name):
				return dic[name]
		raise RailsException("Parameter '${name}' was not found!")

	def AddProperties(properties as IDictionary):
		self.extendedPropertiesList.Add(properties)

	# Allows to check that a parameter was defined
	def IsDefined(name as string) as bool:
		return properties.Contains(name)

	# Initialize all the properties that a script may need
	# One thing to note here is that resources are wrapped in ResourceToDuck wrapper
	# to enable easy use by the script
	def InitProperties(context as IRailsEngineContext, controller as Controller):
		properties = Hashtable(CaseInsensitiveHashCodeProvider.Default,
			CaseInsensitiveComparer.Default)

		properties.Add("context", context)
		properties.Add("request", context.Request)
		properties.Add("response", context.Response)
		properties.Add("session", context.Session)

		if controller.Resources is not null:
			for key in controller.Resources.Keys:
				properties.Add(key, ResourceToDuck(controller.Resources[key]))

		for helper in controller.Helpers.Values:
			properties.Add(helper.GetType().Name, helper)

		for key in context.Params.AllKeys:
			continue if key is null
			properties[key] = context.Params[key]

		for entry as DictionaryEntry in context.Flash:
			properties[entry.Key] = entry.Value

		for entry as DictionaryEntry in controller.PropertyBag:
			properties[entry.Key] = entry.Value

		properties["siteRoot"] = context.ApplicationPath