distro-version was renamed to boo.version

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

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

import System

version = Project.Properties["boo.version"]

trunk = getTrunkUri()
tag = Uri(trunk, "tags/${version}")

print(svn("cp ${trunk} ${tag} -m '${version} release'"))

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

import System

version = Project.Properties["distro-version"]

trunk = getTrunkUri()
tag = Uri(trunk, "tags/${version}")

print(svn("cp ${trunk} ${tag} -m '${version} release'"))


Fixed Home & End keys behavior

Fixed Home & End keys behavior

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

namespace Boo.Lang.Interpreter

import System
import System.Collections
import System.Collections.Generic
import System.IO
import System.Linq
import System.Text
import System.Text.RegularExpressions
import Boo.Lang.Compiler
import Boo.Lang.Compiler.TypeSystem
import PatternMatching
import Environments

class InteractiveInterpreterConsole:

	public final static HISTORY_FILENAME = "booish_history"
	public final static HISTORY_CAPACITY = 100

	_history = System.Collections.Generic.Queue of string(HISTORY_CAPACITY)
	_historyFile = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), HISTORY_FILENAME)
	_historyIndex = 0
	_session = System.Collections.Generic.List of string()

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

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

	[property(ShowWarnings)]
	_showWarnings = false

	QQBegin = "[|"
	QQEnd= "|]"

	_interpreter as InteractiveInterpreter

	def constructor():
		self(InteractiveInterpreter())

	def constructor(interpreter as InteractiveInterpreter):
		_interpreter = interpreter
		_interpreter.RememberLastValue = true

		DisableColors = not string.IsNullOrEmpty(Environment.GetEnvironmentVariable("BOOISH_DISABLE_COLORS"))
		if not DisableColors: #make sure setting color does not throw an exception
			try:
				Console.ForegroundColor = ConsoleColor.DarkGray
			except:
				DisableColors = true
		DisableAutocompletion = not string.IsNullOrEmpty(Environment.GetEnvironmentVariable("BOOISH_DISABLE_AUTOCOMPLETION"))

		_interpreter.SetValue("load", Load)
		_interpreter.SetValue("save", Save)
		_interpreter.SetValue("quit", Quit)

		LoadHistory()

	def SetValue(name as string, value):
		_interpreter.SetValue(name, value)

	Line:
		get: return _line.ToString()

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

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

	LineIndentLen:
		get: return IndentChars.Length * _indent

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

	PrintModules:
		get: return _interpreter.Pipeline.Find(Boo.Lang.Compiler.Steps.PrintBoo) != -1
		set:
			if value:
				if not PrintModules:
					_interpreter.Pipeline.Add(Boo.Lang.Compiler.Steps.PrintBoo())
			else:
				_interpreter.Pipeline.Remove(Boo.Lang.Compiler.Steps.PrintBoo)

	property DefaultPrompt = ">>> "

	property BlockPrompt = "... "

	property IndentChars = "    "

	property DisableColors = false

	property DisableAutocompletion = false

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

	property PromptColor = ConsoleColor.DarkGreen

	property ExceptionColor = ConsoleColor.DarkRed

	property WarningColor = ConsoleColor.Yellow

	property ErrorColor = ConsoleColor.Red

	property SuggestionsColor = ConsoleColor.DarkYellow

	property SelectedSuggestionColor = ConsoleColor.DarkMagenta

	_selectedSuggestionIndex as int?

	_suggestions as EnvironmentBoundValue[of (object)]

	CanAutoComplete as bool:
		get: return _selectedSuggestionIndex is not null

	private _builtins as (IEntity)
	private _filter as string

	def Eval(code as string):
		try:
			result = _interpreter.Eval(code)
			DisplayResults(result)
			return result
		except x as System.Reflection.TargetInvocationException:
			ConsolePrintException(x.InnerException)
		except x:
			ConsolePrintException(x)

	private def DisplayResults(results as CompilerContext):
		if ShowWarnings:
			DisplayProblems(results.Warnings, WarningColor)
		if not DisplayProblems(results.Errors, ErrorColor):
			ProcessLastValue()

	private def ConsolePrintPrompt():
		ConsolePrintPrompt(true)

	private def ConsolePrintPrompt(autoIndent as bool):
		return if _quit
		WithColor PromptColor:
			Console.Write(CurrentPrompt)
		if autoIndent and CurrentPrompt == BlockPrompt:
			for i in range(_indent):
				WriteIndent()
		originalYPosition = Console.CursorTop

	private def WithColor(color as ConsoleColor, block as System.Action):
		if DisableColors:
			block()
		else:
			Console.ForegroundColor = color
			try:
				block()
			ensure:
				Console.ResetColor()

	private def ConsolePrintMessage(msg as string):
		WithColor InterpreterColor:
			print msg

	private def ConsolePrintException(e as Exception):
		WithColor ExceptionColor:
			print e

	private def ConsolePrintError(msg as string):
		WithColor ExceptionColor:
			print msg

	private def NewLine():
		Console.Write(Environment.NewLine)

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

		i = 0

		for s in SuggestionDescriptions():
			Console.ForegroundColor = SuggestionsColor if not DisableColors
			Console.Write(", ") if i > 0
			if i > 20: #TODO: maxcandidates pref + paging?
				Console.Write("... (too many candidates)")
				break
			if i == _selectedSuggestionIndex:
				Console.ForegroundColor = SelectedSuggestionColor if not DisableColors
			Console.Write(s)
			i++

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

	def SuggestionDescriptions():
		return _suggestions.Select(DescriptionsFor).Value

	def DescriptionsFor(suggestions as (object)):
		return array((DescriptionFor(s) for s in suggestions).Distinct())

	def DescriptionFor(s):
		match s:
			case e = IEntity():
				return Boo.Lang.Interpreter.Builtins.DescribeEntity(e)
			otherwise:
				return s.ToString()

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

	protected def WriteIndent():
		Write(IndentChars)

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

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

	protected def Delete(count as int): #if count is 0, forward-delete
		cx = Console.CursorLeft+(Console.CursorTop-originalYPosition)*Console.WindowWidth-len(CurrentPrompt)-count
		return if cx < len(CurrentPrompt) and count == 0
		dcount = (count if count != 0 else 1)
		_line.Remove(cx, dcount)
		curX = Console.CursorLeft - dcount
		curY = Console.CursorTop
		if curX < 0:
			curX = Console.WindowWidth-1
			curY = Console.CursorTop-1

		Console.CursorTop = curY
		Console.CursorLeft = curX
		Console.Write("${_line.ToString(cx,LineLen-cx)} ")
		Console.CursorTop = curY
		Console.CursorLeft = curX

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

	def DisplaySuggestions():
		DisplaySuggestions(Line)

	def DisplaySuggestions(query as string):
		return if DisableAutocompletion

		#TODO: FIXME: refactor to one regex?
		p_open = re_open.Matches(query).Count
		p_close = re_close.Matches(query).Count
		if p_open > p_close:
			query = query.Split(" ,(\t".ToCharArray(), 100)[-1]
		else:
			query = query.Split(" ,\t".ToCharArray(), 100)[-1]
		if query.LastIndexOf('.') > 0:
			codeToComplete = query[0:query.LastIndexOf('.')+1]
			_filter = query[query.LastIndexOf('.')+1:]
			_suggestions = (
				_interpreter
				.SuggestCompletionsFor(codeToComplete+"__codecomplete__")
				.Select[of (object)](
					{ es | array(e as object for e in es if e.Name.StartsWith(_filter)) }))

		if _suggestions.Value is null or 0 == len(_suggestions.Value): #suggest a  var
			_filter = query
			_suggestions = EnvironmentBoundValue.Create(
							null,
							array(
								var.Key.ToString() as object
								for var in _interpreter.Values
								if var.ToString().StartsWith(_filter)))

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

	def AutoComplete():
		raise InvalidOperationException("no suggestions") if _suggestions.Value is null or _selectedSuggestionIndex is null

		Write(_suggestions.Select[of string]({ ss | AutoCompletionFor(ss[_selectedSuggestionIndex.Value]) }).Value)

		_selectedSuggestionIndex = null
		_suggestions = EnvironmentBoundValue[of (object)](null, null)

	def AutoCompletionFor(s):
		match s:
			case m = IMethod(Name: name):
				if len(m.GetParameters()) == 0:
					return "$(TrimFilter(name))()"
				return "$(TrimFilter(name))("
			case IEntity(Name: name):
				return TrimFilter(name)
			otherwise:
				return TrimFilter(s.ToString())

	def TrimFilter(s as string):
		return s[len(_filter):]

	def Top(d as decimal, i as decimal):
		return System.Math.Ceiling(d/i);

	private _beforeHistory = string.Empty

	private originalYPosition = 0

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

	def ReadEvalPrintLoop():
		Console.CursorVisible = true
		ConsolePrintPrompt()
		while not _quit:
			ReadEvalPrintLoopStep()
		SaveHistory()
		DisplayGoodbye()

	private def ReadEvalPrintLoopStep():
		cki = Console.ReadKey(true)
		key = cki.Key
		keyChar = cki.KeyChar
		control = false

		newLine = keyChar in Environment.NewLine

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

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

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

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

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

		if not newLine:
			#line-editing support
			if cx < LineLen and not _multiline:
				_line.Insert(cx, keyChar) if not control
				CurX = Console.CursorLeft
				CurY = Console.CursorTop
				Console.Write(_line.ToString(cx, LineLen-cx))
				Console.CursorTop = CurY
				if CurX + 1 >= Console.WindowWidth:
					Console.CursorTop++
					Console.CursorLeft = 0
				else:
					Console.CursorLeft = CurX+1
			else:
				_line.Append(keyChar) if not control
				Console.Write(keyChar)

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

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

				_indent++ if LineLastChar in _blockStarters or Line.EndsWith(QQBegin)
				_indent-- if Line.EndsWith(IndentChars+"pass")
				if Line.EndsWith(QQEnd):
					CheckBooLangCompilerReferenced()
					_indent--

				if _indent <= 0:
					_indent = 0
					try:
						Eval(_buffer.ToString())
					ensure:
						_buffer.Length = 0 #truncate buffer

			LineLen = 0 #truncate line
			ConsolePrintPrompt()

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

		cmd = line.Split()

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

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

		else:
			return false
		return true

	private _indent as int = 0

	def DisplayLogo():
		WithColor InterpreterColor:
			print """Welcome to booish, an interactive interpreter for the boo programming language.
Running boo ${BooVersion} on ${Boo.Lang.Runtime.RuntimeServices.RuntimeDisplayName}.

Enter boo code in the prompt below (or type /help)."""

	def DisplayHelp():
		WithColor InterpreterColor:
			print """The following builtin functions are available :
    /? or /h or /help : display this help
    dir(type) : returns the members of a type
    describe(type) : describe a type as boo code
    globals() or /g : returns names of all variables known to interpreter
    load(file) or /l file : evals an external boo file
    save(file) or /s file : writes your current booish session into file
    quit() or /q : exits the interpreter"""

	def DisplayGoodbye():	// booish is friendly
		WithColor InterpreterColor:
			print ""
			print "All your boo are belong to us!"

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

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

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

	def Load([required] path as string):
		if path.EndsWith(".boo"):
			ConsolePrintMessage("Evaluating '${path}' ...")
			DisplayResults(_interpreter.EvalCompilerInput(Boo.Lang.Compiler.IO.FileInput(path)))
		else:
			ConsolePrintMessage("Adding reference to '${path}'")
			try:
				_interpreter.References.Add(System.Reflection.Assembly.LoadFrom(path))
			except e:
				ConsolePrintException(e)

	private def ProcessLastValue():
		_ = _interpreter.LastValue
		if _ is not null:
			Console.WriteLine(Boo.Lang.Interpreter.Builtins.repr(_))
			_interpreter.SetValue("_", _)

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

	private _quit = false

	def Quit():
		_quit = true

	def DisplayProblems(problems as ICollection, color as ConsoleColor):
		return if problems is null or problems.Count == 0
		WithColor color:
			for problem as duck in problems:
				markLocation(problem.LexicalInfo)
				type = ("ERROR" if problem isa CompilerError else "WARNING")
				Console.WriteLine("${type}: ${problem.Message}")
		return true

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

	private def CheckBooLangCompilerReferenced():
		return if _blcReferenced
		_interpreter.References.Add(typeof(Boo.Lang.Compiler.CompilerContext).Assembly)
		_blcReferenced = true

	_blcReferenced = false

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

namespace Boo.Lang.Interpreter

import System
import System.Collections
import System.Collections.Generic
import System.IO
import System.Linq
import System.Text
import System.Text.RegularExpressions
import Boo.Lang.Compiler
import Boo.Lang.Compiler.TypeSystem
import PatternMatching
import Environments

class InteractiveInterpreterConsole:

	public final static HISTORY_FILENAME = "booish_history"
	public final static HISTORY_CAPACITY = 100

	_history = System.Collections.Generic.Queue of string(HISTORY_CAPACITY)
	_historyFile = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), HISTORY_FILENAME)
	_historyIndex = 0
	_session = System.Collections.Generic.List of string()

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

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

	[property(ShowWarnings)]
	_showWarnings = false

	QQBegin = "[|"
	QQEnd= "|]"

	_interpreter as InteractiveInterpreter

	def constructor():
		self(InteractiveInterpreter())

	def constructor(interpreter as InteractiveInterpreter):
		_interpreter = interpreter
		_interpreter.RememberLastValue = true

		DisableColors = not string.IsNullOrEmpty(Environment.GetEnvironmentVariable("BOOISH_DISABLE_COLORS"))
		if not DisableColors: #make sure setting color does not throw an exception
			try:
				Console.ForegroundColor = ConsoleColor.DarkGray
			except:
				DisableColors = true
		DisableAutocompletion = not string.IsNullOrEmpty(Environment.GetEnvironmentVariable("BOOISH_DISABLE_AUTOCOMPLETION"))

		_interpreter.SetValue("load", Load)
		_interpreter.SetValue("save", Save)
		_interpreter.SetValue("quit", Quit)

		LoadHistory()

	def SetValue(name as string, value):
		_interpreter.SetValue(name, value)

	Line:
		get: return _line.ToString()

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

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

	LineIndentLen:
		get: return IndentChars.Length * _indent

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

	PrintModules:
		get: return _interpreter.Pipeline.Find(Boo.Lang.Compiler.Steps.PrintBoo) != -1
		set:
			if value:
				if not PrintModules:
					_interpreter.Pipeline.Add(Boo.Lang.Compiler.Steps.PrintBoo())
			else:
				_interpreter.Pipeline.Remove(Boo.Lang.Compiler.Steps.PrintBoo)

	property DefaultPrompt = ">>> "

	property BlockPrompt = "... "

	property IndentChars = "    "

	property DisableColors = false

	property DisableAutocompletion = false

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

	property PromptColor = ConsoleColor.DarkGreen

	property ExceptionColor = ConsoleColor.DarkRed

	property WarningColor = ConsoleColor.Yellow

	property ErrorColor = ConsoleColor.Red

	property SuggestionsColor = ConsoleColor.DarkYellow

	property SelectedSuggestionColor = ConsoleColor.DarkMagenta

	_selectedSuggestionIndex as int?

	_suggestions as EnvironmentBoundValue[of (object)]

	CanAutoComplete as bool:
		get: return _selectedSuggestionIndex is not null

	private _builtins as (IEntity)
	private _filter as string

	def Eval(code as string):
		try:
			result = _interpreter.Eval(code)
			DisplayResults(result)
			return result
		except x as System.Reflection.TargetInvocationException:
			ConsolePrintException(x.InnerException)
		except x:
			ConsolePrintException(x)

	private def DisplayResults(results as CompilerContext):
		if ShowWarnings:
			DisplayProblems(results.Warnings, WarningColor)
		if not DisplayProblems(results.Errors, ErrorColor):
			ProcessLastValue()

	private def ConsolePrintPrompt():
		ConsolePrintPrompt(true)

	private def ConsolePrintPrompt(autoIndent as bool):
		return if _quit
		WithColor PromptColor:
			Console.Write(CurrentPrompt)
		if autoIndent and CurrentPrompt == BlockPrompt:
			for i in range(_indent):
				WriteIndent()
		originalYPosition = Console.CursorTop

	private def WithColor(color as ConsoleColor, block as System.Action):
		if DisableColors:
			block()
		else:
			Console.ForegroundColor = color
			try:
				block()
			ensure:
				Console.ResetColor()

	private def ConsolePrintMessage(msg as string):
		WithColor InterpreterColor:
			print msg

	private def ConsolePrintException(e as Exception):
		WithColor ExceptionColor:
			print e

	private def ConsolePrintError(msg as string):
		WithColor ExceptionColor:
			print msg

	private def NewLine():
		Console.Write(Environment.NewLine)

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

		i = 0

		for s in SuggestionDescriptions():
			Console.ForegroundColor = SuggestionsColor if not DisableColors
			Console.Write(", ") if i > 0
			if i > 20: #TODO: maxcandidates pref + paging?
				Console.Write("... (too many candidates)")
				break
			if i == _selectedSuggestionIndex:
				Console.ForegroundColor = SelectedSuggestionColor if not DisableColors
			Console.Write(s)
			i++

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

	def SuggestionDescriptions():
		return _suggestions.Select(DescriptionsFor).Value

	def DescriptionsFor(suggestions as (object)):
		return array((DescriptionFor(s) for s in suggestions).Distinct())

	def DescriptionFor(s):
		match s:
			case e = IEntity():
				return Boo.Lang.Interpreter.Builtins.DescribeEntity(e)
			otherwise:
				return s.ToString()

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

	protected def WriteIndent():
		Write(IndentChars)

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

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

	protected def Delete(count as int): #if count is 0, forward-delete
		cx = Console.CursorLeft+(Console.CursorTop-originalYPosition)*Console.WindowWidth-len(CurrentPrompt)-count
		return if cx < len(CurrentPrompt) and count == 0
		dcount = (count if count != 0 else 1)
		_line.Remove(cx, dcount)
		curX = Console.CursorLeft - dcount
		curY = Console.CursorTop
		if curX < 0:
			curX = Console.WindowWidth-1
			curY = Console.CursorTop-1

		Console.CursorTop = curY
		Console.CursorLeft = curX
		Console.Write("${_line.ToString(cx,LineLen-cx)} ")
		Console.CursorTop = curY
		Console.CursorLeft = curX

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

	def DisplaySuggestions():
		DisplaySuggestions(Line)

	def DisplaySuggestions(query as string):
		return if DisableAutocompletion

		#TODO: FIXME: refactor to one regex?
		p_open = re_open.Matches(query).Count
		p_close = re_close.Matches(query).Count
		if p_open > p_close:
			query = query.Split(" ,(\t".ToCharArray(), 100)[-1]
		else:
			query = query.Split(" ,\t".ToCharArray(), 100)[-1]
		if query.LastIndexOf('.') > 0:
			codeToComplete = query[0:query.LastIndexOf('.')+1]
			_filter = query[query.LastIndexOf('.')+1:]
			_suggestions = (
				_interpreter
				.SuggestCompletionsFor(codeToComplete+"__codecomplete__")
				.Select[of (object)](
					{ es | array(e as object for e in es if e.Name.StartsWith(_filter)) }))

		if _suggestions.Value is null or 0 == len(_suggestions.Value): #suggest a  var
			_filter = query
			_suggestions = EnvironmentBoundValue.Create(
							null,
							array(
								var.Key.ToString() as object
								for var in _interpreter.Values
								if var.ToString().StartsWith(_filter)))

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

	def AutoComplete():
		raise InvalidOperationException("no suggestions") if _suggestions.Value is null or _selectedSuggestionIndex is null

		Write(_suggestions.Select[of string]({ ss | AutoCompletionFor(ss[_selectedSuggestionIndex.Value]) }).Value)

		_selectedSuggestionIndex = null
		_suggestions = EnvironmentBoundValue[of (object)](null, null)

	def AutoCompletionFor(s):
		match s:
			case m = IMethod(Name: name):
				if len(m.GetParameters()) == 0:
					return "$(TrimFilter(name))()"
				return "$(TrimFilter(name))("
			case IEntity(Name: name):
				return TrimFilter(name)
			otherwise:
				return TrimFilter(s.ToString())

	def TrimFilter(s as string):
		return s[len(_filter):]

	private _beforeHistory = string.Empty

	private originalYPosition = 0

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

	def ReadEvalPrintLoop():
		Console.CursorVisible = true
		ConsolePrintPrompt()
		while not _quit:
			ReadEvalPrintLoopStep()
		SaveHistory()
		DisplayGoodbye()

	private def ReadEvalPrintLoopStep():
		cki = Console.ReadKey(true)
		key = cki.Key
		keyChar = cki.KeyChar
		control = false

		newLine = keyChar in Environment.NewLine

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

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

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

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

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

		if not newLine:
			#line-editing support
			if cx < LineLen and not _multiline:
				_line.Insert(cx, keyChar) if not control
				CurX = Console.CursorLeft
				CurY = Console.CursorTop
				Console.Write(_line.ToString(cx, LineLen-cx))
				Console.CursorTop = CurY
				if CurX + 1 >= Console.WindowWidth:
					Console.CursorTop++
					Console.CursorLeft = 0
				else:
					Console.CursorLeft = CurX+1
			else:
				_line.Append(keyChar) if not control
				Console.Write(keyChar)

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

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

				_indent++ if LineLastChar in _blockStarters or Line.EndsWith(QQBegin)
				_indent-- if Line.EndsWith(IndentChars+"pass")
				if Line.EndsWith(QQEnd):
					CheckBooLangCompilerReferenced()
					_indent--

				if _indent <= 0:
					_indent = 0
					try:
						Eval(_buffer.ToString())
					ensure:
						_buffer.Length = 0 #truncate buffer

			LineLen = 0 #truncate line
			ConsolePrintPrompt()

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

		cmd = line.Split()

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

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

		else:
			return false
		return true

	private _indent as int = 0

	def DisplayLogo():
		WithColor InterpreterColor:
			print """Welcome to booish, an interactive interpreter for the boo programming language.
Running boo ${BooVersion} on ${Boo.Lang.Runtime.RuntimeServices.RuntimeDisplayName}.

Enter boo code in the prompt below (or type /help)."""

	def DisplayHelp():
		WithColor InterpreterColor:
			print """The following builtin functions are available :
    /? or /h or /help : display this help
    dir(type) : returns the members of a type
    describe(type) : describe a type as boo code
    globals() or /g : returns names of all variables known to interpreter
    load(file) or /l file : evals an external boo file
    save(file) or /s file : writes your current booish session into file
    quit() or /q : exits the interpreter"""

	def DisplayGoodbye():	// booish is friendly
		WithColor InterpreterColor:
			print ""
			print "All your boo are belong to us!"

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

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

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

	def Load([required] path as string):
		if path.EndsWith(".boo"):
			ConsolePrintMessage("Evaluating '${path}' ...")
			DisplayResults(_interpreter.EvalCompilerInput(Boo.Lang.Compiler.IO.FileInput(path)))
		else:
			ConsolePrintMessage("Adding reference to '${path}'")
			try:
				_interpreter.References.Add(System.Reflection.Assembly.LoadFrom(path))
			except e:
				ConsolePrintException(e)

	private def ProcessLastValue():
		_ = _interpreter.LastValue
		if _ is not null:
			Console.WriteLine(Boo.Lang.Interpreter.Builtins.repr(_))
			_interpreter.SetValue("_", _)

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

	private _quit = false

	def Quit():
		_quit = true

	def DisplayProblems(problems as ICollection, color as ConsoleColor):
		return if problems is null or problems.Count == 0
		WithColor color:
			for problem as duck in problems:
				markLocation(problem.LexicalInfo)
				type = ("ERROR" if problem isa CompilerError else "WARNING")
				Console.WriteLine("${type}: ${problem.Message}")
		return true

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

	private def CheckBooLangCompilerReferenced():
		return if _blcReferenced
		_interpreter.References.Add(typeof(Boo.Lang.Compiler.CompilerContext).Assembly)
		_blcReferenced = true

	_blcReferenced = false


Protected the array literal pattern by checking its type before slicing its members

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

namespace Boo.Lang.PatternMatching.Impl

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

class PatternExpander:

	private static final Dummy = '_'

	def Expand(matchValue as Expression, pattern as Expression) as Expression:
		if mie = pattern as MethodInvocationExpression:
			if mie.Target.ToString() == 'array':
				lst = ArrayLiteralExpression()
				for arg in mie.Arguments:
					lst.Items.Add(arg)

				temp = NewTemp(matchValue)
				condition = [| $temp = $matchValue as (object) |] #System.Collections.IList |]
				return [| $condition and $(ExpandFixedSizePattern(temp, lst)) |]

			return ExpandObjectPattern(matchValue, pattern)

		if pattern isa MemberReferenceExpression:
			return ExpandValuePattern(matchValue, pattern)

		if pattern isa ReferenceExpression:
			return ExpandBindPattern(matchValue, pattern)

		if pattern isa QuasiquoteExpression:
			return ExpandQuasiquotePattern(matchValue, pattern)

		if pattern isa RELiteralExpression:
			return ExpandRegexPattern(matchValue, pattern)

		binary = pattern as BinaryExpression
		if binary is not null:
			if BinaryOperatorType.Assign == binary.Operator:
				return ExpandCapturePattern(matchValue, pattern)

			if BinaryOperatorType.BitwiseOr == binary.Operator:
				return ExpandEitherPattern(matchValue, pattern)

			if BinaryOperatorType.BitwiseAnd == binary.Operator:
				return ExpandBothPattern(matchValue, pattern)

			if BinaryOperatorType.And == binary.Operator:
				return ExpandConstrainedAndPattern(matchValue, pattern)

			if BinaryOperatorType.Or == binary.Operator:
				return ExpandConstrainedOrPattern(matchValue, pattern)

		if pattern isa ArrayLiteralExpression:
			return ExpandFixedSizePattern(matchValue, pattern)

		return ExpandValuePattern(matchValue, pattern)

	def ExpandBothPattern(matchValue as Expression, node as BinaryExpression) as Expression:
		l = Expand(matchValue, node.Left)
		r = Expand(matchValue, node.Right)
		return [| $l and $r |]

	def ExpandConstrainedAndPattern(matchValue as Expression, node as BinaryExpression) as Expression:
		l = Expand(matchValue, node.Left)
		return [| $l and $(node.Right) |]

	def ExpandConstrainedOrPattern(matchValue as Expression, node as BinaryExpression) as Expression:
		l = Expand(matchValue, node.Left)
		return [| $l or $(node.Right) |]

	def ExpandRegexPattern(matchValue as Expression, node as RELiteralExpression):
		tempBinding = NewTemp(node)
		return ExpandRegexPatternWithBinding(matchValue, node, tempBinding)

	def ExpandRegexPatternWithBinding(matchValue as Expression, pattern as RELiteralExpression, binding as ReferenceExpression):

		groupNames = array(groupName for groupName in pattern.Regex.GetGroupNames() if not IsInteger(groupName))

		expansion = [| @($binding = $pattern.Match($matchValue)) |]
		if len(groupNames) == 0:
			expansion.Arguments.Add([| $binding.Success |])
		else:
			bindingAction = [| @() |]
			for groupName in groupNames:
				groupBinding = ReferenceExpression(LexicalInfo: pattern.LexicalInfo, Name: groupName)
				bindingAction.Arguments.Add([| $groupBinding = $binding.Groups[$groupName].Captures |])
			bindingAction.Arguments.Add([| true |])

			expansion.Arguments.Add([| ($bindingAction if $binding.Success else false) |])

		return expansion

	def IsInteger(s as string):
		_ as int
		return int.TryParse(s, _)

	def ExpandEitherPattern(matchValue as Expression, node as BinaryExpression) as Expression:
		l = Expand(matchValue, node.Left)
		r = Expand(matchValue, node.Right)
		return [| $l or $r |]

	def ExpandBindPattern(matchValue as Expression, node as ReferenceExpression):
		return [| @($node = $matchValue, true) |]

	def ExpandValuePattern(matchValue as Expression, node as Expression):
		return [| $matchValue == $node |]

	def ExpandCapturePattern(matchValue as Expression, node as BinaryExpression):
		name = node.Left
		pattern = node.Right
		if pattern isa RELiteralExpression:
			return ExpandRegexPatternWithBinding(matchValue, pattern, name)

		return ExpandObjectPattern(matchValue, name, pattern)

	def ExpandObjectPattern(matchValue as Expression, node as MethodInvocationExpression) as Expression:
		if len(node.NamedArguments) == 0 and len(node.Arguments) == 0:
			return [| $matchValue isa $(TypeRefFrom(node)) |]
		return ExpandObjectPattern(matchValue, NewTemp(node), node)

	def ExpandObjectPattern(matchValue as Expression, temp as ReferenceExpression, node as MethodInvocationExpression) as Expression:

		condition = [| ($matchValue isa $(TypeRefFrom(node))) and @($temp = cast($(TypeRefFrom(node)), $matchValue), true) |]
		condition.LexicalInfo = node.LexicalInfo

		for member in node.Arguments:
			assert member isa ReferenceExpression, "Invalid argument '${member}' in pattern '${node}'."
			memberRef = MemberReferenceExpression(member.LexicalInfo, temp.CloneNode(), member.ToString())
			condition = [| $condition and @($member = $memberRef, true) |]

		for member in node.NamedArguments:
			namedArgCondition = ExpandMemberPattern(temp.CloneNode(), member)
			condition = [| $condition and $namedArgCondition |]

		return condition

	class QuasiquotePatternBuilder(DepthFirstVisitor):

		static final Ast = [| Boo.Lang.Compiler.Ast |]

		_parent as PatternExpander
		_pattern as Expression

		def constructor(parent as PatternExpander):
			_parent = parent

		def Build(node as QuasiquoteExpression):
			return Expand(node.Node)

		def Expand(node as Node):
			node.Accept(self)
			expansion = _pattern
			_pattern = null
			assert expansion is not null, "Unsupported pattern '${node}'"
			return expansion

		def Push(srcNode as Node, e as Expression):
			assert _pattern is null
			e.LexicalInfo = srcNode.LexicalInfo
			_pattern = e

		override def OnSpliceExpression(node as SpliceExpression):
			_pattern = node.Expression

		override def OnSpliceTypeReference(node as SpliceTypeReference):
			_pattern = node.Expression

		def ExpandFixedSize(items):
			a = [| (,) |]
			for item in items:
				a.Items.Add(Expand(item))
			return a

		override def OnOmittedExpression(node as OmittedExpression):
			_pattern = [| $Ast.OmittedExpression.Default |]

		override def OnSlice(node as Slice):
			ctor = [| $Ast.Slice() |]
			ExpandProperty ctor, "Begin", node.Begin
			ExpandProperty ctor, "End", node.End
			ExpandProperty ctor, "Step", node.Step
			Push node, ctor

		def ExpandProperty(ctor as MethodInvocationExpression, name as string, value as Expression):
			if value is null: return
			ctor.NamedArguments.Add(ExpressionPair(First: ReferenceExpression(name), Second: Expand(value)))

		override def OnMacroStatement(node as MacroStatement):
			if len(node.Arguments) > 0:
				Push node, [| $Ast.MacroStatement(Name: $(node.Name), Arguments: $(ExpandFixedSize(node.Arguments))) |]
			else:
				Push node, [| $Ast.MacroStatement(Name: $(node.Name)) |]

		override def OnSlicingExpression(node as SlicingExpression):
			Push node, [| $Ast.SlicingExpression(Target: $(Expand(node.Target)), Indices: $(ExpandFixedSize(node.Indices))) |]

		override def OnTryCastExpression(node as TryCastExpression):
			Push node, [| $Ast.TryCastExpression(Target: $(Expand(node.Target)), Type: $(Expand(node.Type))) |]

		override def OnCastExpression(node as CastExpression):
			Push node, [| $Ast.CastExpression(Target: $(Expand(node.Target)), Type: $(Expand(node.Type))) |]

		override def OnMethodInvocationExpression(node as MethodInvocationExpression):
			if len(node.Arguments) > 0:
				pattern = [| $Ast.MethodInvocationExpression(Target: $(Expand(node.Target)), Arguments: $(ExpandFixedSize(node.Arguments))) |]
			else:
				pattern = [| $Ast.MethodInvocationExpression(Target: $(Expand(node.Target))) |]
			Push node, pattern

		override def OnTypeofExpression(node as TypeofExpression):
			Push node, [| $Ast.TypeofExpression(Type: $(Expand(node.Type))) |]

		override def OnListLiteralExpression(node as ListLiteralExpression):
			Push node, [| $Ast.ListLiteralExpression(Items: $(ExpandFixedSize(node.Items))) |]

		override def OnBoolLiteralExpression(node as BoolLiteralExpression):
			Push node, [| $Ast.BoolLiteralExpression(Value: $node) |]

		override def OnIntegerLiteralExpression(node as IntegerLiteralExpression):
			Push node, [| $Ast.IntegerLiteralExpression(Value: $node) |]

		override def OnStringLiteralExpression(node as StringLiteralExpression):
			Push node, [| $Ast.StringLiteralExpression(Value: $node) |]

		override def OnNullLiteralExpression(node as NullLiteralExpression):
			Push node, [| $Ast.NullLiteralExpression() |]

		override def OnUnaryExpression(node as UnaryExpression):
			Push node, [| $Ast.UnaryExpression(Operator: $Ast.UnaryOperatorType.$(node.Operator.ToString()), Operand: $(Expand(node.Operand))) |]

		override def OnBinaryExpression(node as BinaryExpression):
			Push node, [| $Ast.BinaryExpression(Operator: $Ast.BinaryOperatorType.$(node.Operator.ToString()), Left: $(Expand(node.Left)), Right: $(Expand(node.Right))) |]

		override def OnReferenceExpression(node as ReferenceExpression):
			Push node, [| $Ast.ReferenceExpression(Name: $(node.Name)) |]

		override def OnSuperLiteralExpression(node as SuperLiteralExpression):
			Push node, [| $Ast.SuperLiteralExpression() |]

		override def OnSelfLiteralExpression(node as SelfLiteralExpression):
			Push node, [| $Ast.SelfLiteralExpression() |]

		override def OnMemberReferenceExpression(node as MemberReferenceExpression):
			Push node, [| $Ast.MemberReferenceExpression(Target: $(Expand(node.Target)), Name: $(node.Name)) |]

		override def OnGenericReferenceExpression(node as GenericReferenceExpression):
			Push node, [| $Ast.GenericReferenceExpression(Target: $(Expand(node.Target)), GenericArguments: $(ExpandFixedSize(node.GenericArguments))) |]

		override def OnSimpleTypeReference(node as SimpleTypeReference):
			Push node, [| $Ast.SimpleTypeReference(Name: $(node.Name)) |]

		override def OnGenericTypeReference(node as GenericTypeReference):
			Push node, [| $Ast.GenericTypeReference(Name: $(node.Name), GenericArguments: $(ExpandFixedSize(node.GenericArguments))) |]

	def ObjectPatternFor(node as QuasiquoteExpression):
		return QuasiquotePatternBuilder(self).Build(node)

	def ExpandQuasiquotePattern(matchValue as Expression, node as QuasiquoteExpression) as Expression:
		return ExpandObjectPattern(matchValue, ObjectPatternFor(node))

	def ExpandMemberPattern(matchValue as Expression, member as ExpressionPair):
		memberRef = ExpandMemberReference(member.First, matchValue)
		return Expand(memberRef, member.Second)

	def ExpandMemberReference(member as Expression, target as Expression) as Expression:
		memberRef = member as MemberReferenceExpression
		if memberRef is not null:
			return MemberReferenceExpression(member.LexicalInfo, ExpandMemberReference(memberRef.Target, target), memberRef.Name)
		return MemberReferenceExpression(member.LexicalInfo, target, member.ToString())

	def ExpandFixedSizePattern(matchValue as Expression, pattern as ArrayLiteralExpression):
		patternLen = len(pattern.Items)
		condition = [| $(patternLen) == len($matchValue) |]

		if patternLen == 0: return condition

		if IsCatchAllPattern(last = pattern.Items[patternLen-1]):
			pattern.Items.Remove(last)
			condition = [| $(patternLen-1) <= len($matchValue) |]

		i = 0
		for item in pattern.Items:
			itemValue = [| $matchValue[$i] |]
			itemPattern = Expand(itemValue, item)
			condition = [| $condition and $itemPattern |]
			++i
		return condition

	def TypeRefFrom(node as MethodInvocationExpression):
		return node.Target

	def IsCatchAllPattern(pattern as Expression) as bool:
		last = pattern as UnaryExpression
		if AstUtil.IsExplodeExpression(last):
			last_re = last.Operand as ReferenceExpression
			return last_re and last_re.Name == Dummy
		return false

internal def NewTemp(e as Expression):
	return ReferenceExpression(
			LexicalInfo: e.LexicalInfo,
			Name: CompilerContext.Current.GetUniqueName("match"))

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

namespace Boo.Lang.PatternMatching.Impl

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

class PatternExpander:

	private static final Dummy = '_'

	def Expand(matchValue as Expression, pattern as Expression) as Expression:
		if mie = pattern as MethodInvocationExpression:
			if mie.Target.ToString() == 'array':
				lst = ArrayLiteralExpression()
				for arg in mie.Arguments:
					lst.Items.Add(arg)
				return ExpandFixedSizePattern(matchValue, lst)

			return ExpandObjectPattern(matchValue, pattern)

		if pattern isa MemberReferenceExpression:
			return ExpandValuePattern(matchValue, pattern)

		if pattern isa ReferenceExpression:
			return ExpandBindPattern(matchValue, pattern)

		if pattern isa QuasiquoteExpression:
			return ExpandQuasiquotePattern(matchValue, pattern)

		if pattern isa RELiteralExpression:
			return ExpandRegexPattern(matchValue, pattern)

		binary = pattern as BinaryExpression
		if binary is not null:
			if BinaryOperatorType.Assign == binary.Operator:
				return ExpandCapturePattern(matchValue, pattern)

			if BinaryOperatorType.BitwiseOr == binary.Operator:
				return ExpandEitherPattern(matchValue, pattern)

			if BinaryOperatorType.BitwiseAnd == binary.Operator:
				return ExpandBothPattern(matchValue, pattern)

			if BinaryOperatorType.And == binary.Operator:
				return ExpandConstrainedAndPattern(matchValue, pattern)

			if BinaryOperatorType.Or == binary.Operator:
				return ExpandConstrainedOrPattern(matchValue, pattern)

		if pattern isa ArrayLiteralExpression:
			return ExpandFixedSizePattern(matchValue, pattern)

		return ExpandValuePattern(matchValue, pattern)

	def ExpandBothPattern(matchValue as Expression, node as BinaryExpression) as Expression:
		l = Expand(matchValue, node.Left)
		r = Expand(matchValue, node.Right)
		return [| $l and $r |]

	def ExpandConstrainedAndPattern(matchValue as Expression, node as BinaryExpression) as Expression:
		l = Expand(matchValue, node.Left)
		return [| $l and $(node.Right) |]

	def ExpandConstrainedOrPattern(matchValue as Expression, node as BinaryExpression) as Expression:
		l = Expand(matchValue, node.Left)
		return [| $l or $(node.Right) |]

	def ExpandRegexPattern(matchValue as Expression, node as RELiteralExpression):
		tempBinding = NewTemp(node)
		return ExpandRegexPatternWithBinding(matchValue, node, tempBinding)

	def ExpandRegexPatternWithBinding(matchValue as Expression, pattern as RELiteralExpression, binding as ReferenceExpression):

		groupNames = array(groupName for groupName in pattern.Regex.GetGroupNames() if not IsInteger(groupName))

		expansion = [| @($binding = $pattern.Match($matchValue)) |]
		if len(groupNames) == 0:
			expansion.Arguments.Add([| $binding.Success |])
		else:
			bindingAction = [| @() |]
			for groupName in groupNames:
				groupBinding = ReferenceExpression(LexicalInfo: pattern.LexicalInfo, Name: groupName)
				bindingAction.Arguments.Add([| $groupBinding = $binding.Groups[$groupName].Captures |])
			bindingAction.Arguments.Add([| true |])

			expansion.Arguments.Add([| ($bindingAction if $binding.Success else false) |])

		return expansion

	def IsInteger(s as string):
		_ as int
		return int.TryParse(s, _)

	def ExpandEitherPattern(matchValue as Expression, node as BinaryExpression) as Expression:
		l = Expand(matchValue, node.Left)
		r = Expand(matchValue, node.Right)
		return [| $l or $r |]

	def ExpandBindPattern(matchValue as Expression, node as ReferenceExpression):
		return [| @($node = $matchValue, true) |]

	def ExpandValuePattern(matchValue as Expression, node as Expression):
		return [| $matchValue == $node |]

	def ExpandCapturePattern(matchValue as Expression, node as BinaryExpression):
		name = node.Left
		pattern = node.Right
		if pattern isa RELiteralExpression:
			return ExpandRegexPatternWithBinding(matchValue, pattern, name)

		return ExpandObjectPattern(matchValue, name, pattern)

	def ExpandObjectPattern(matchValue as Expression, node as MethodInvocationExpression) as Expression:
		if len(node.NamedArguments) == 0 and len(node.Arguments) == 0:
			return [| $matchValue isa $(TypeRefFrom(node)) |]
		return ExpandObjectPattern(matchValue, NewTemp(node), node)

	def ExpandObjectPattern(matchValue as Expression, temp as ReferenceExpression, node as MethodInvocationExpression) as Expression:

		condition = [| ($matchValue isa $(TypeRefFrom(node))) and @($temp = cast($(TypeRefFrom(node)), $matchValue), true) |]
		condition.LexicalInfo = node.LexicalInfo

		for member in node.Arguments:
			assert member isa ReferenceExpression, "Invalid argument '${member}' in pattern '${node}'."
			memberRef = MemberReferenceExpression(member.LexicalInfo, temp.CloneNode(), member.ToString())
			condition = [| $condition and @($member = $memberRef, true) |]

		for member in node.NamedArguments:
			namedArgCondition = ExpandMemberPattern(temp.CloneNode(), member)
			condition = [| $condition and $namedArgCondition |]

		return condition

	class QuasiquotePatternBuilder(DepthFirstVisitor):

		static final Ast = [| Boo.Lang.Compiler.Ast |]

		_parent as PatternExpander
		_pattern as Expression

		def constructor(parent as PatternExpander):
			_parent = parent

		def Build(node as QuasiquoteExpression):
			return Expand(node.Node)

		def Expand(node as Node):
			node.Accept(self)
			expansion = _pattern
			_pattern = null
			assert expansion is not null, "Unsupported pattern '${node}'"
			return expansion

		def Push(srcNode as Node, e as Expression):
			assert _pattern is null
			e.LexicalInfo = srcNode.LexicalInfo
			_pattern = e

		override def OnSpliceExpression(node as SpliceExpression):
			_pattern = node.Expression

		override def OnSpliceTypeReference(node as SpliceTypeReference):
			_pattern = node.Expression

		def ExpandFixedSize(items):
			a = [| (,) |]
			for item in items:
				a.Items.Add(Expand(item))
			return a

		override def OnOmittedExpression(node as OmittedExpression):
			_pattern = [| $Ast.OmittedExpression.Default |]

		override def OnSlice(node as Slice):
			ctor = [| $Ast.Slice() |]
			ExpandProperty ctor, "Begin", node.Begin
			ExpandProperty ctor, "End", node.End
			ExpandProperty ctor, "Step", node.Step
			Push node, ctor

		def ExpandProperty(ctor as MethodInvocationExpression, name as string, value as Expression):
			if value is null: return
			ctor.NamedArguments.Add(ExpressionPair(First: ReferenceExpression(name), Second: Expand(value)))

		override def OnMacroStatement(node as MacroStatement):
			if len(node.Arguments) > 0:
				Push node, [| $Ast.MacroStatement(Name: $(node.Name), Arguments: $(ExpandFixedSize(node.Arguments))) |]
			else:
				Push node, [| $Ast.MacroStatement(Name: $(node.Name)) |]

		override def OnSlicingExpression(node as SlicingExpression):
			Push node, [| $Ast.SlicingExpression(Target: $(Expand(node.Target)), Indices: $(ExpandFixedSize(node.Indices))) |]

		override def OnTryCastExpression(node as TryCastExpression):
			Push node, [| $Ast.TryCastExpression(Target: $(Expand(node.Target)), Type: $(Expand(node.Type))) |]

		override def OnCastExpression(node as CastExpression):
			Push node, [| $Ast.CastExpression(Target: $(Expand(node.Target)), Type: $(Expand(node.Type))) |]

		override def OnMethodInvocationExpression(node as MethodInvocationExpression):
			if len(node.Arguments) > 0:
				pattern = [| $Ast.MethodInvocationExpression(Target: $(Expand(node.Target)), Arguments: $(ExpandFixedSize(node.Arguments))) |]
			else:
				pattern = [| $Ast.MethodInvocationExpression(Target: $(Expand(node.Target))) |]
			Push node, pattern

		override def OnTypeofExpression(node as TypeofExpression):
			Push node, [| $Ast.TypeofExpression(Type: $(Expand(node.Type))) |]

		override def OnListLiteralExpression(node as ListLiteralExpression):
			Push node, [| $Ast.ListLiteralExpression(Items: $(ExpandFixedSize(node.Items))) |]

		override def OnBoolLiteralExpression(node as BoolLiteralExpression):
			Push node, [| $Ast.BoolLiteralExpression(Value: $node) |]

		override def OnIntegerLiteralExpression(node as IntegerLiteralExpression):
			Push node, [| $Ast.IntegerLiteralExpression(Value: $node) |]

		override def OnStringLiteralExpression(node as StringLiteralExpression):
			Push node, [| $Ast.StringLiteralExpression(Value: $node) |]

		override def OnNullLiteralExpression(node as NullLiteralExpression):
			Push node, [| $Ast.NullLiteralExpression() |]

		override def OnUnaryExpression(node as UnaryExpression):
			Push node, [| $Ast.UnaryExpression(Operator: $Ast.UnaryOperatorType.$(node.Operator.ToString()), Operand: $(Expand(node.Operand))) |]

		override def OnBinaryExpression(node as BinaryExpression):
			Push node, [| $Ast.BinaryExpression(Operator: $Ast.BinaryOperatorType.$(node.Operator.ToString()), Left: $(Expand(node.Left)), Right: $(Expand(node.Right))) |]

		override def OnReferenceExpression(node as ReferenceExpression):
			Push node, [| $Ast.ReferenceExpression(Name: $(node.Name)) |]

		override def OnSuperLiteralExpression(node as SuperLiteralExpression):
			Push node, [| $Ast.SuperLiteralExpression() |]

		override def OnSelfLiteralExpression(node as SelfLiteralExpression):
			Push node, [| $Ast.SelfLiteralExpression() |]

		override def OnMemberReferenceExpression(node as MemberReferenceExpression):
			Push node, [| $Ast.MemberReferenceExpression(Target: $(Expand(node.Target)), Name: $(node.Name)) |]

		override def OnGenericReferenceExpression(node as GenericReferenceExpression):
			Push node, [| $Ast.GenericReferenceExpression(Target: $(Expand(node.Target)), GenericArguments: $(ExpandFixedSize(node.GenericArguments))) |]

		override def OnSimpleTypeReference(node as SimpleTypeReference):
			Push node, [| $Ast.SimpleTypeReference(Name: $(node.Name)) |]

		override def OnGenericTypeReference(node as GenericTypeReference):
			Push node, [| $Ast.GenericTypeReference(Name: $(node.Name), GenericArguments: $(ExpandFixedSize(node.GenericArguments))) |]

	def ObjectPatternFor(node as QuasiquoteExpression):
		return QuasiquotePatternBuilder(self).Build(node)

	def ExpandQuasiquotePattern(matchValue as Expression, node as QuasiquoteExpression) as Expression:
		return ExpandObjectPattern(matchValue, ObjectPatternFor(node))

	def ExpandMemberPattern(matchValue as Expression, member as ExpressionPair):
		memberRef = ExpandMemberReference(member.First, matchValue)
		return Expand(memberRef, member.Second)

	def ExpandMemberReference(member as Expression, target as Expression) as Expression:
		memberRef = member as MemberReferenceExpression
		if memberRef is not null:
			return MemberReferenceExpression(member.LexicalInfo, ExpandMemberReference(memberRef.Target, target), memberRef.Name)
		return MemberReferenceExpression(member.LexicalInfo, target, member.ToString())

	def ExpandFixedSizePattern(matchValue as Expression, pattern as ArrayLiteralExpression):
		patternLen = len(pattern.Items)
		condition = [| $(patternLen) == len($matchValue) |]

		if patternLen == 0: return condition

		if IsCatchAllPattern(last = pattern.Items[patternLen-1]):
			pattern.Items.Remove(last)
			condition = [| $(patternLen-1) <= len($matchValue) |]

		i = 0
		for item in pattern.Items:
			itemValue = [| $matchValue[$i] |]
			itemPattern = Expand(itemValue, item)
			condition = [| $condition and $itemPattern |]
			++i
		return condition

	def TypeRefFrom(node as MethodInvocationExpression):
		return node.Target

	def IsCatchAllPattern(pattern as Expression) as bool:
		last = pattern as UnaryExpression
		if AstUtil.IsExplodeExpression(last):
			last_re = last.Operand as ReferenceExpression
			return last_re and last_re.Name == Dummy
		return false

internal def NewTemp(e as Expression):
	return ReferenceExpression(
			LexicalInfo: e.LexicalInfo,
			Name: CompilerContext.Current.GetUniqueName("match"))


BooBinding: Fixed NullReferenceException when compiler returned warnings.

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

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

namespace BooBinding

import System
import System.Collections
import System.IO
import System.Diagnostics
import System.Text
import System.Reflection
import System.CodeDom.Compiler

import Boo.Lang.Compiler.CompilerWarning as BooWarning
import Boo.Lang.Compiler.CompilerError as BooError

import ICSharpCode.Core.Services

import ICSharpCode.SharpDevelop.Internal.Project
import ICSharpCode.SharpDevelop.Gui
import ICSharpCode.SharpDevelop.Services

class BooBindingCompilerManager:
	_booLibNotFound = "Boo.Dll was not found in the boo addin directory"

	def GetCompiledOutputName(fileName as string) as string:
		return Path.ChangeExtension(fileName, ".exe")

	def GetCompiledOutputName(p as BooProject) as string:
		compilerparameters as BooCompilerParameters = p.ActiveConfiguration

		fileUtilityService as FileUtilityService = ServiceManager.Services.GetService(typeof(FileUtilityService))
		exe = fileUtilityService.GetDirectoryNameWithSeparator(compilerparameters.OutputDirectory) + compilerparameters.OutputAssembly
		if (compilerparameters.CompileTarget == CompileTarget.Library):
			return exe + ".dll"
		else:
			return exe + ".exe"

	def CanCompile(fileName as string) as bool:
		return Path.GetExtension(fileName).ToLower() == ".boo"

	private def MakeError(text as string):
		cr = CompilerResults(TempFileCollection())
		cr.Errors.Add(CompilerError(ErrorText: text))
		return DefaultCompilerResult(cr, text)

	private def Compile(compilerparameters as BooCompilerParameters, fileNames as (string), outputFile as string, p as IProject) as ICompilerResult:
		messageService as IMessageService = ServiceManager.Services.GetService(typeof(IMessageService))
		cr as CompilerResults = null

		booDir = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)
		booLib = Path.Combine(booDir, "Boo.dll")
		if not File.Exists(booLib):
			messageService.ShowError(_booLibNotFound)
			return MakeError(_booLibNotFound)
		outputDirectory = Path.GetDirectoryName(outputFile)
		try:
			File.Copy(booLib, Path.Combine(outputDirectory, Path.GetFileName(booLib)))
		except:
			pass

		if compilerparameters.CompileTarget == CompileTarget.WinExe:
			WriteManifestFile(outputFile)

		compilerOutput = StringBuilder()
		setup = AppDomainSetup()
		setup.ApplicationBase = booDir
		setup.ApplicationName = "BooCompiler"
		appDomain as AppDomain = null
		try:
			/*
			// TODO: Use app domain for invoking compiler
			Console.WriteLine("Create AppDomain")
			appDomain = AppDomain.CreateDomain("BooCompiler", null, setup)

			appDomain.AssemblyResolve += MyResolveEventHandler
			Console.WriteLine("Create Instance")
			compilero = appDomain.CreateInstanceAndUnwrap("BooBinding", "BooBinding.BooCompilerWrapper")
			Console.WriteLine(compilero) // prints BooBinding.BooCompilerWrapper
			Console.WriteLine(compilero.GetType().FullName) // prints MarshalByRefObject
			// -> casting to BooCompilerWrapper is not possible
			// the same code works in a console application.
			*/
			compiler as BooCompilerWrapper = BooCompilerWrapper()
			compiler.SetOptions(compilerparameters.CurrentCompilerOptions)
			compiler.OutputFile = outputFile

			for fileName as string in fileNames:
				compiler.AddInputFile(Path.GetFullPath(fileName))

			if p != null:
				// write references
				for lib as ProjectReference in p.ProjectReferences:
					compiler.AddReference(lib.GetReferencedFileName(p))
				// write embedded resources
				for finfo as ProjectFile in p.ProjectFiles:
					if finfo.Subtype != Subtype.Directory and finfo.BuildAction == BuildAction.EmbedAsResource:
						compiler.AddResource(finfo.Name)
				p.CopyReferencesToOutputPath(false)

			result = compiler.Run()
			cr = CompilerResults(TempFileCollection())
			info as Boo.Lang.Compiler.Ast.LexicalInfo = null
			for o in result:
				Console.WriteLine(o.ToString())
				compilerOutput.Append(o.ToString())
				compilerOutput.Append(Environment.NewLine)
				error = CompilerError()
				if o isa BooError:
					booError as BooError = o
					info = booError.LexicalInfo
					error.IsWarning   = false
					error.ErrorNumber = booError.Code
					error.ErrorText   = booError.Message
				else:
					warning as BooWarning = o
					info = warning.LexicalInfo
					error.IsWarning   = true
					error.ErrorNumber = warning.Code
					if warning.Message != null and warning.Message.StartsWith("WARNING: "):
						error.ErrorText = warning.Message[9:]
					else:
						error.ErrorText = warning.Message

				if info != null and info.Line >= 0:
					error.Column   = info.Column
					error.Line     = info.Line
				if info != null and info.FileName != null:
					error.FileName = Path.GetFullPath(info.FileName)
				cr.Errors.Add(error)

			compiler = null
		ensure:
			//AppDomain.Unload(appDomain)
			//Console.WriteLine("AppDomain unloaded")
			pass

		return DefaultCompilerResult(cr, compilerOutput.ToString())

	static def MyResolveEventHandler(sender, e as ResolveEventArgs) as Assembly:
		Console.WriteLine("Resolving ${e.Name}")
		return null

	def CompileFile(fileName as string, compilerparameters as BooCompilerParameters) as ICompilerResult:
		compilerparameters.OutputDirectory = Path.GetDirectoryName(fileName)
		compilerparameters.OutputAssembly  = Path.GetFileNameWithoutExtension(fileName)

		return Compile(compilerparameters, (fileName,), GetCompiledOutputName(fileName), null)

	def CompileProject(project as BooProject) as ICompilerResult:
		compilerparameters as BooCompilerParameters = project.ActiveConfiguration

		fileNames = ArrayList()

		for	finfo as ProjectFile in project.ProjectFiles:
			if finfo.Subtype != Subtype.Directory:
				if finfo.BuildAction == BuildAction.Compile:
						fileNames.Add(finfo.Name)

		exe = GetCompiledOutputName(project)
		if compilerparameters.CompileTarget == CompileTarget.WinExe:
			WriteManifestFile(exe)

		return Compile(compilerparameters, fileNames.ToArray(typeof(string)), exe, project)

	def WriteManifestFile(fileName as string):
		manifestFile = fileName + ".manifest"
		return if File.Exists(manifestFile)
		using sw = StreamWriter(manifestFile):
			sw.WriteLine("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>")
			sw.WriteLine("")
			sw.WriteLine("<assembly xmlns=\"urn:schemas-microsoft-com:asm.v1\" manifestVersion=\"1.0\">")
			sw.WriteLine("	<dependency>")
			sw.WriteLine("		<dependentAssembly>")
			sw.WriteLine("			<assemblyIdentity")
			sw.WriteLine("				type=\"win32\"")
			sw.WriteLine("				name=\"Microsoft.Windows.Common-Controls\"")
			sw.WriteLine("				version=\"6.0.0.0\"")
			sw.WriteLine("				processorArchitecture=\"X86\"")
			sw.WriteLine("				publicKeyToken=\"6595b64144ccf1df\"")
			sw.WriteLine("				language=\"*\"")
			sw.WriteLine("			/>")
			sw.WriteLine("		</dependentAssembly>")
			sw.WriteLine("	</dependency>")
			sw.WriteLine("</assembly>")

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

namespace BooBinding

import System
import System.Collections
import System.IO
import System.Diagnostics
import System.Text
import System.Reflection
import System.CodeDom.Compiler

import Boo.Lang.Compiler.CompilerWarning as BooWarning
import Boo.Lang.Compiler.CompilerError as BooError

import ICSharpCode.Core.Services

import ICSharpCode.SharpDevelop.Internal.Project
import ICSharpCode.SharpDevelop.Gui
import ICSharpCode.SharpDevelop.Services

class BooBindingCompilerManager:
	_booLibNotFound = "Boo.Dll was not found in the boo addin directory"

	def GetCompiledOutputName(fileName as string) as string:
		return Path.ChangeExtension(fileName, ".exe")

	def GetCompiledOutputName(p as BooProject) as string:
		compilerparameters as BooCompilerParameters = p.ActiveConfiguration

		fileUtilityService as FileUtilityService = ServiceManager.Services.GetService(typeof(FileUtilityService))
		exe = fileUtilityService.GetDirectoryNameWithSeparator(compilerparameters.OutputDirectory) + compilerparameters.OutputAssembly
		if (compilerparameters.CompileTarget == CompileTarget.Library):
			return exe + ".dll"
		else:
			return exe + ".exe"

	def CanCompile(fileName as string) as bool:
		return Path.GetExtension(fileName).ToLower() == ".boo"

	private def MakeError(text as string):
		cr = CompilerResults(TempFileCollection())
		cr.Errors.Add(CompilerError(ErrorText: text))
		return DefaultCompilerResult(cr, text)

	private def Compile(compilerparameters as BooCompilerParameters, fileNames as (string), outputFile as string, p as IProject) as ICompilerResult:
		messageService as IMessageService = ServiceManager.Services.GetService(typeof(IMessageService))
		cr as CompilerResults = null

		booDir = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)
		booLib = Path.Combine(booDir, "Boo.dll")
		if not File.Exists(booLib):
			messageService.ShowError(_booLibNotFound)
			return MakeError(_booLibNotFound)
		outputDirectory = Path.GetDirectoryName(outputFile)
		try:
			File.Copy(booLib, Path.Combine(outputDirectory, Path.GetFileName(booLib)))
		except:
			pass

		if compilerparameters.CompileTarget == CompileTarget.WinExe:
			WriteManifestFile(outputFile)

		compilerOutput = StringBuilder()
		setup = AppDomainSetup()
		setup.ApplicationBase = booDir
		setup.ApplicationName = "BooCompiler"
		appDomain as AppDomain = null
		try:
			/*
			// TODO: Use app domain for invoking compiler
			Console.WriteLine("Create AppDomain")
			appDomain = AppDomain.CreateDomain("BooCompiler", null, setup)

			appDomain.AssemblyResolve += MyResolveEventHandler
			Console.WriteLine("Create Instance")
			compilero = appDomain.CreateInstanceAndUnwrap("BooBinding", "BooBinding.BooCompilerWrapper")
			Console.WriteLine(compilero) // prints BooBinding.BooCompilerWrapper
			Console.WriteLine(compilero.GetType().FullName) // prints MarshalByRefObject
			// -> casting to BooCompilerWrapper is not possible
			// the same code works in a console application.
			*/
			compiler as BooCompilerWrapper = BooCompilerWrapper()
			compiler.SetOptions(compilerparameters.CurrentCompilerOptions)
			compiler.OutputFile = outputFile

			for fileName as string in fileNames:
				compiler.AddInputFile(Path.GetFullPath(fileName))

			if p != null:
				// write references
				for lib as ProjectReference in p.ProjectReferences:
					compiler.AddReference(lib.GetReferencedFileName(p))
				// write embedded resources
				for finfo as ProjectFile in p.ProjectFiles:
					if finfo.Subtype != Subtype.Directory and finfo.BuildAction == BuildAction.EmbedAsResource:
						compiler.AddResource(finfo.Name)
				p.CopyReferencesToOutputPath(false)

			result = compiler.Run()
			cr = CompilerResults(TempFileCollection())
			info as Boo.Lang.Compiler.Ast.LexicalInfo = null
			for o in result:
				Console.WriteLine(o.ToString())
				compilerOutput.Append(o.ToString())
				compilerOutput.Append(Environment.NewLine)
				error = CompilerError()
				if o isa BooError:
					booError as BooError = o
					info = booError.LexicalInfo
					error.IsWarning   = false
					error.ErrorNumber = booError.Code
					error.ErrorText   = booError.Message
				else:
					warning as BooWarning = o
					error.IsWarning   = true
					error.ErrorNumber = warning.Code
					error.ErrorText   = warning.Message
				error.Column   = info.Column
				error.Line     = info.Line
				error.FileName = Path.GetFullPath(info.FileName) unless info.FileName == null
				cr.Errors.Add(error)

			compiler = null
		ensure:
			//AppDomain.Unload(appDomain)
			//Console.WriteLine("AppDomain unloaded")
			pass

		return DefaultCompilerResult(cr, compilerOutput.ToString())

	static def MyResolveEventHandler(sender, e as ResolveEventArgs) as Assembly:
		Console.WriteLine("Resolving ${e.Name}")
		return null

	def CompileFile(fileName as string, compilerparameters as BooCompilerParameters) as ICompilerResult:
		compilerparameters.OutputDirectory = Path.GetDirectoryName(fileName)
		compilerparameters.OutputAssembly  = Path.GetFileNameWithoutExtension(fileName)

		return Compile(compilerparameters, (fileName,), GetCompiledOutputName(fileName), null)

	def CompileProject(project as BooProject) as ICompilerResult:
		compilerparameters as BooCompilerParameters = project.ActiveConfiguration

		fileNames = ArrayList()

		for	finfo as ProjectFile in project.ProjectFiles:
			if finfo.Subtype != Subtype.Directory:
				if finfo.BuildAction == BuildAction.Compile:
						fileNames.Add(finfo.Name)

		exe = GetCompiledOutputName(project)
		if compilerparameters.CompileTarget == CompileTarget.WinExe:
			WriteManifestFile(exe)

		return Compile(compilerparameters, fileNames.ToArray(typeof(string)), exe, project)

	def WriteManifestFile(fileName as string):
		manifestFile = fileName + ".manifest"
		return if File.Exists(manifestFile)
		using sw = StreamWriter(manifestFile):
			sw.WriteLine("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>")
			sw.WriteLine("")
			sw.WriteLine("<assembly xmlns=\"urn:schemas-microsoft-com:asm.v1\" manifestVersion=\"1.0\">")
			sw.WriteLine("	<dependency>")
			sw.WriteLine("		<dependentAssembly>")
			sw.WriteLine("			<assemblyIdentity")
			sw.WriteLine("				type=\"win32\"")
			sw.WriteLine("				name=\"Microsoft.Windows.Common-Controls\"")
			sw.WriteLine("				version=\"6.0.0.0\"")
			sw.WriteLine("				processorArchitecture=\"X86\"")
			sw.WriteLine("				publicKeyToken=\"6595b64144ccf1df\"")
			sw.WriteLine("				language=\"*\"")
			sw.WriteLine("			/>")
			sw.WriteLine("		</dependentAssembly>")
			sw.WriteLine("	</dependency>")
			sw.WriteLine("</assembly>")


unused import

unused import

namespace us

import System
import System.Reflection
import Boo.Lang.Compiler

def Main([required] argv as (string)) as int:
	try:
		return run(argv)
	except x:
		print "Error:", x
	return 255

def getAssemblyTitle():
	return cast(AssemblyTitleAttribute, getAssemblyAttribute(AssemblyTitleAttribute)).Title

def getAssemblyVersion():
	return Assembly.GetExecutingAssembly().GetName().Version

def getAssemblyCopyright():
	return cast(AssemblyCopyrightAttribute, getAssemblyAttribute(AssemblyCopyrightAttribute)).Copyright

def getAssemblyAttribute(type as System.Type):
	return Attribute.GetCustomAttribute(Assembly.GetExecutingAssembly(), type)

def compile(options as CommandLineOptions):
	compiler = UnityScriptCompilerFactory.FromCommandLineOptions(options)
	results = compiler.Run()
	if len(results.Errors):
		print results.Errors.ToString(options.Verbose)
		return 255
	else:
		if options.Execute:
			execute(results, options.MainMethod)
		else:
			print results.Warnings.ToString() if len(results.Warnings)
			print "Successfully compiled '${len(compiler.Parameters.Input)}' file(s) to '${results.GeneratedAssemblyFileName}'."
	return 0

def execute(result as CompilerContext, mainMethod as string):
	for type in result.GeneratedAssembly.GetTypes():
		method = type.GetMethod(mainMethod)
		if method is not null:
			method.Invoke(type(), (,))

def banner():
	print "${getAssemblyTitle()} - ${getAssemblyVersion()} ${getAssemblyCopyright()}"
	print

def run(argv as (string)):
	options = parseCommandLineOptions(argv)
	if options.IsValid and not options.DoHelp:
		try:
			return compile(options)
		except x as ApplicationException:
			print "ERROR:", x.Message
		except x:
			print "ERROR:", x
	else:
		usage(options)

	return 255

def parseCommandLineOptions(argv as (string)):
	return CommandLineOptions(*argv)

def usage(options as CommandLineOptions):
	banner()
	options.PrintOptions()

namespace us

import System
import System.Reflection
import System.IO
import Boo.Lang.Compiler

def Main([required] argv as (string)) as int:
	try:
		return run(argv)
	except x:
		print "Error:", x
	return 255

def getAssemblyTitle():
	return cast(AssemblyTitleAttribute, getAssemblyAttribute(AssemblyTitleAttribute)).Title

def getAssemblyVersion():
	return Assembly.GetExecutingAssembly().GetName().Version

def getAssemblyCopyright():
	return cast(AssemblyCopyrightAttribute, getAssemblyAttribute(AssemblyCopyrightAttribute)).Copyright

def getAssemblyAttribute(type as System.Type):
	return Attribute.GetCustomAttribute(Assembly.GetExecutingAssembly(), type)

def compile(options as CommandLineOptions):
	compiler = UnityScriptCompilerFactory.FromCommandLineOptions(options)
	results = compiler.Run()
	if len(results.Errors):
		print results.Errors.ToString(options.Verbose)
		return 255
	else:
		if options.Execute:
			execute(results, options.MainMethod)
		else:
			print results.Warnings.ToString() if len(results.Warnings)
			print "Successfully compiled '${len(compiler.Parameters.Input)}' file(s) to '${results.GeneratedAssemblyFileName}'."
	return 0

def execute(result as CompilerContext, mainMethod as string):
	for type in result.GeneratedAssembly.GetTypes():
		method = type.GetMethod(mainMethod)
		if method is not null:
			method.Invoke(type(), (,))

def banner():
	print "${getAssemblyTitle()} - ${getAssemblyVersion()} ${getAssemblyCopyright()}"
	print

def run(argv as (string)):
	options = parseCommandLineOptions(argv)
	if options.IsValid and not options.DoHelp:
		try:
			return compile(options)
		except x as ApplicationException:
			print "ERROR:", x.Message
		except x:
			print "ERROR:", x
	else:
		usage(options)

	return 255

def parseCommandLineOptions(argv as (string)):
	return CommandLineOptions(*argv)

def usage(options as CommandLineOptions):
	banner()
	options.PrintOptions()


deterministic project file update

deterministic project file update

import System.Xml
import System.IO
import System.Resources from System.Windows.Forms
import Boo.Lang.Useful.IO from Boo.Lang.Useful

class XmlDocumentWrapper:

	_document as XmlDocument

	def constructor(document as XmlDocument):
		_document = document

	def constructor(fname as string):
		_document = XmlDocument()
		_document.Load(fname)

	NamespaceURI:
		get:
			xmlns = _document.DocumentElement.Attributes["xmlns"]
			if xmlns is null: return null
			return xmlns.Value

	BaseURI:
		get:
			return System.Uri(_document.BaseURI)

	AbsolutePath:
		get:
			return BaseURI.AbsolutePath

	def Save():
		_document.Save(AbsolutePath)

class Project(XmlDocumentWrapper):

	static def Load(fname as string):
		doc = XmlDocument()
		doc.Load(fname)
		if fname.EndsWith(".mdp"):
			return MDProject(doc)
		return VS2005Project(doc)

	_sourceFiles as XmlElement

	def constructor(document as XmlDocument, sourceFiles as XmlElement):
		super(document)
		_sourceFiles = sourceFiles

	def SetSourceFiles(files as string*):
		_sourceFiles.RemoveAll()
		for item in List of string(files).Sort():
			_sourceFiles.AppendChild(CreateSourceElement(item))

	protected abstract def CreateSourceElement(src as string) as XmlElement:
		pass

class MDProject(Project):
	def constructor(document as XmlDocument):
		super(document, document.SelectSingleNode("/Project/Contents"))

	override protected def CreateSourceElement(src as string):
		file = _document.CreateElement("File", NamespaceURI)
		file.SetAttribute("name", src)
		file.SetAttribute("buildaction", "Compile")
		file.SetAttribute("subtype", "Code")
		return file

class VS2005Project(Project):

	def constructor(document as XmlDocument):
		super(document, selectCompileItemGroup(document))

	private def selectCompileItemGroup(document as XmlDocument):
		compileItem = document.SelectSingleNode("//*[local-name()='ItemGroup']/*[local-name()='Compile']")
		return compileItem.ParentNode

	override protected def CreateSourceElement(src as string):
		compile = _document.CreateElement("Compile", NamespaceURI)
		compile.SetAttribute("Include", src.Replace('/', '\\'))
		return compile

def updateProjectFile(fname as string):

	project = Project.Load(fname)
	project.SetSourceFiles(sourceFilesFor(project))
	project.Save()

	print project.AbsolutePath

def sourceFilesFor(project as Project):
	baseURI = project.BaseURI
	for item as string in listFiles(Path.GetDirectoryName(baseURI.AbsolutePath)):
		if item =~ /\.svn/: continue
		if not item.EndsWith(".cs"): continue
		yield baseURI.MakeRelativeUri(System.Uri(item)).ToString()

def updateStringResources(txtFile as string):
	fname = Path.ChangeExtension(txtFile, ".resx")
	File.Delete(fname)

	using resourceFile = ResXResourceWriter(fname):

		using file=File.OpenText(txtFile):
			for line in file:
				if line.StartsWith(";"): continue
				line = line.Trim()
				if len(line) == 0: continue
				index = line.IndexOf('=')
				key = line[:index]
				value = line[index+1:]

				resourceFile.AddResource(key, value)

	print fname

def rebase(fname as string):
	#return Path.Combine("c:/projects/boo/", fname)
	return fname

fnames = (
"src/Boo.Lang/Boo.Lang-VS2005.csproj",
"src/Boo.Lang/Boo.Lang-VS2008.csproj",
"src/Boo.Lang.Parser/Boo.Lang.Parser-VS2005.csproj",
"src/Boo.Lang.Parser/Boo.Lang.Parser-VS2008.csproj",
"src/Boo.Lang.Compiler/Boo.Lang.Compiler-VS2005.csproj",
"src/Boo.Lang.Compiler/Boo.Lang.Compiler-VS2008.csproj",
"tests/BooCompiler.Tests/BooCompiler.Tests-VS2005.csproj",
"tests/BooCompiler.Tests/BooCompiler.Tests-VS2008.csproj",
"tests/Boo.Lang.Runtime.Tests/Boo.Lang.Runtime.Tests-VS2005.csproj",
"tests/Boo.Lang.Runtime.Tests/Boo.Lang.Runtime.Tests-VS2008.csproj",

"src/Boo.Lang.Compiler/Boo.Lang.Compiler.mdp",
)
for fname in fnames:
	updateProjectFile(rebase(fname))

updateStringResources(rebase("src/Boo.Lang/Resources/strings.txt"))

import System.Xml
import System.IO
import System.Resources from System.Windows.Forms
import Boo.Lang.Useful.IO from Boo.Lang.Useful

class XmlDocumentWrapper:

	_document as XmlDocument

	def constructor(document as XmlDocument):
		_document = document

	def constructor(fname as string):
		_document = XmlDocument()
		_document.Load(fname)

	NamespaceURI:
		get:
			xmlns = _document.DocumentElement.Attributes["xmlns"]
			if xmlns is null: return null
			return xmlns.Value

	BaseURI:
		get:
			return System.Uri(_document.BaseURI)

	AbsolutePath:
		get:
			return BaseURI.AbsolutePath

	def Save():
		_document.Save(AbsolutePath)

class Project(XmlDocumentWrapper):

	static def Load(fname as string):
		doc = XmlDocument()
		doc.Load(fname)
		if fname.EndsWith(".mdp"):
			return MDProject(doc)
		return VS2005Project(doc)

	_sourceFiles as XmlElement

	def constructor(document as XmlDocument, sourceFiles as XmlElement):
		super(document)
		_sourceFiles = sourceFiles

	def ResetSourceFiles():
		_sourceFiles.RemoveAll()

	def AddSourceFile(item as string):
		_sourceFiles.AppendChild(CreateSourceElement(item))

	protected abstract def CreateSourceElement(src as string) as XmlElement:
		pass

class MDProject(Project):
	def constructor(document as XmlDocument):
		super(document, document.SelectSingleNode("/Project/Contents"))

	override protected def CreateSourceElement(src as string):
		file = _document.CreateElement("File", NamespaceURI)
		file.SetAttribute("name", src)
		file.SetAttribute("buildaction", "Compile")
		file.SetAttribute("subtype", "Code")
		return file

class VS2005Project(Project):

	def constructor(document as XmlDocument):
		super(document, selectCompileItemGroup(document))

	private def selectCompileItemGroup(document as XmlDocument):
		compileItem = document.SelectSingleNode("//*[local-name()='ItemGroup']/*[local-name()='Compile']")
		return compileItem.ParentNode

	override protected def CreateSourceElement(src as string):
		compile = _document.CreateElement("Compile", NamespaceURI)
		compile.SetAttribute("Include", src.Replace('/', '\\'))
		return compile

def updateProjectFile(fname as string):
	project = Project.Load(fname)
	project.ResetSourceFiles()

	baseURI = project.BaseURI
	for item as string in listFiles(Path.GetDirectoryName(baseURI.AbsolutePath)):
		if item =~ /\.svn/: continue
		if not item.EndsWith(".cs"): continue
		uri = baseURI.MakeRelative(System.Uri(item))
		project.AddSourceFile(uri)
	project.Save()

	print project.AbsolutePath

def updateStringResources(txtFile as string):
	fname = Path.ChangeExtension(txtFile, ".resx")
	File.Delete(fname)

	using resourceFile = ResXResourceWriter(fname):

		using file=File.OpenText(txtFile):
			for line in file:
				if line.StartsWith(";"): continue
				line = line.Trim()
				if len(line) == 0: continue
				index = line.IndexOf('=')
				key = line[:index]
				value = line[index+1:]

				resourceFile.AddResource(key, value)

	print fname

def rebase(fname as string):
	#return Path.Combine("c:/projects/boo/", fname)
	return fname

fnames = (
"src/Boo.Lang/Boo.Lang-VS2005.csproj",
"src/Boo.Lang/Boo.Lang-VS2008.csproj",
"src/Boo.Lang.Parser/Boo.Lang.Parser-VS2005.csproj",
"src/Boo.Lang.Parser/Boo.Lang.Parser-VS2008.csproj",
"src/Boo.Lang.Compiler/Boo.Lang.Compiler-VS2005.csproj",
"src/Boo.Lang.Compiler/Boo.Lang.Compiler-VS2008.csproj",
#"src/Boo.Lang.Ast/Boo.Lang.Ast-VS2005.csproj",
#"src/Boo.Lang.Ast/Boo.Lang.Ast-VS2008.csproj",
"tests/BooCompiler.Tests/BooCompiler.Tests-VS2005.csproj",
"tests/BooCompiler.Tests/BooCompiler.Tests-VS2008.csproj",
"tests/Boo.Lang.Runtime.Tests/Boo.Lang.Runtime.Tests-VS2005.csproj",
"tests/Boo.Lang.Runtime.Tests/Boo.Lang.Runtime.Tests-VS2008.csproj",

"src/Boo.Lang.Compiler/Boo.Lang.Compiler.mdp",
)
for fname in fnames:
	updateProjectFile(rebase(fname))

updateStringResources(rebase("src/Boo.Lang/Resources/strings.txt"))


include an non standard .parse method for int, uint and double

namespace BooJs.Lang.Globals

import BooJs.Lang.Extensions

class Number(Object):

    [Transform( Math.pow($1, $2) )]
    static def op_Exponentiation(lhs as double, rhs as double) as double:
        pass

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

    static final NaN as double
    static final MAX_VALUE as double
    static final MIN_VALUE as double
    static final NEGATIVE_INFINITY as double
    static final POSITIVE_INFINITY as double

    [Transform( Boo['cast']($1, 'double') )]
    static def parse(v) as double:
        pass

    [Transform( Number() )]
    def constructor():
        pass

    [Transform( Number($1) )]
    def constructor(n as object):
        pass

    def toExponential(digits as int) as string:
        pass
    def toExponential() as string:
        pass
    def toFixed(decimals as int) as string:
        pass
    def toFixed() as string:
        pass
    def toPrecission(decimals as int) as string:
        pass
    def toPrecission() as string:
        pass

    def toString(radix as int) as string:
        pass

class NumberInt(Number):
    # Handle integer divisions
    [Transform( parseInt($1 / $2) )]
    static def op_Division(lhs as int, rhs as int) as int:
        pass
    # Exponentiation
    [Transform( Math.pow($1, $2) )]
    static def op_Exponentiation(lhs as int, rhs as int) as int:
        pass

    [Transform( Boo.String.op_Multiply($2, $1) )]
    static def op_Multiply(lhs as int, rhs as string) as string:
        pass

    [Transform( Boo['cast']($1, 'int') )]
    static def parse(v) as int:
        pass

    [Transform( Number() )]
    def constructor():
        pass

    [Transform( Number($1) )]
    def constructor(n as object):
        pass

class NumberUInt(NumberInt):
    [Transform( Boo['cast']($1, 'uint') )]
    static def parse(v) as uint:
        pass

    [Transform( Number() )]
    def constructor():
        pass

    [Transform( Number($1) )]
    def constructor(n as object):
        pass

namespace BooJs.Lang.Globals

import BooJs.Lang.Extensions

class Number(Object):

    [Transform( Math.pow($1, $2) )]
    static def op_Exponentiation(lhs as double, rhs as double) as double:
        pass

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

    static final NaN as double
    static final MAX_VALUE as double
    static final MIN_VALUE as double
    static final NEGATIVE_INFINITY as double
    static final POSITIVE_INFINITY as double

    [Transform( Number() )]
    def constructor():
        pass

    [Transform( Number($1) )]
    def constructor(n as object):
        pass

    def toExponential(digits as int) as string:
        pass
    def toExponential() as string:
        pass
    def toFixed(decimals as int) as string:
        pass
    def toFixed() as string:
        pass
    def toPrecission(decimals as int) as string:
        pass
    def toPrecission() as string:
        pass

    def toString(radix as int) as string:
        pass

class NumberInt(Number):
    # Handle integer divisions
    [Transform( parseInt($1 / $2) )]
    static def op_Division(lhs as int, rhs as int) as int:
        pass
    # Exponentiation
    [Transform( Math.pow($1, $2) )]
    static def op_Exponentiation(lhs as int, rhs as int) as int:
        pass

    [Transform( Boo.String.op_Multiply($2, $1) )]
    static def op_Multiply(lhs as int, rhs as string) as string:
        pass

    [Transform( Number() )]
    def constructor():
        pass

    [Transform( Number($1) )]
    def constructor(n as object):
        pass

class NumberUInt(NumberInt):
    [Transform( Number() )]
    def constructor():
        pass

    [Transform( Number($1) )]
    def constructor(n as object):
        pass