Also query MD dom.

Also query MD dom.

namespace UnityScript.MonoDevelop.Completion

import System
import System.Collections.Generic

import UnityScript
import UnityScript.MonoDevelop
import UnityScript.MonoDevelop.ProjectModel

import MonoDevelop.Core
import MonoDevelop.Projects
import MonoDevelop.Projects.Dom.Parser
import MonoDevelop.Ide.Gui
import MonoDevelop.Ide.Gui.Content
import MonoDevelop.Ide.CodeCompletion

import Boo.Lang.Compiler
import Boo.Lang.Compiler.Steps
import Boo.Lang.Compiler.Ast
import Boo.Lang.Compiler.IO
import Boo.Lang.Compiler.TypeSystem
import Boo.Lang.PatternMatching

class UnityScriptEditorCompletion(CompletionTextEditorExtension):

	_dom as ProjectDom
	_resolver as UnityScriptTypeResolver
	_project as DotNetProject

	override def Initialize():
		super()
		_dom = ProjectDomService.GetProjectDom(Document.Project) or ProjectDomService.GetFileDom(Document.FileName)
		InstallUnityScriptSyntaxModeIfNeeded()
		_resolver = UnityScriptTypeResolver()
		_project = Document.Project as DotNetProject
		InitializeProjectReferences()

	def InitializeProjectReferences():
		if _project is null:
			return

		for reference in _project.References:
			if ReferenceType.Project != reference.ReferenceType:
				_resolver.AddReference(reference.Reference)

	def InstallUnityScriptSyntaxModeIfNeeded():
		view = Document.GetContent[of MonoDevelop.SourceEditor.SourceEditorView]()
		return if view is null

		mimeType = UnityScriptParser.MimeType
		return if view.Document.SyntaxMode.MimeType == mimeType

		mode = Mono.TextEditor.Highlighting.SyntaxModeService.GetSyntaxMode(mimeType)
		if mode is not null:
			view.Document.SyntaxMode = mode
		else:
			LoggingService.LogWarning(GetType() + " could not get SyntaxMode for mimetype '" + mimeType + "'.")

	override def ExtendsEditor(doc as MonoDevelop.Ide.Gui.Document, editor as IEditableTextBuffer):
		return IsUnityScriptFile(doc.Name)

	override def HandleCodeCompletion(context as CodeCompletionContext, completionChar as char):
#		print "HandleCodeCompletion(${context.ToString()}, ${completionChar.ToString()})"

		match completionChar.ToString():
			case ' ':
				lineText = GetLineText(context.TriggerLine).TrimStart()
				if not lineText.StartsWith("import "):
					return null

				return ImportCompletionDataFor('')

			case '.':
				lineText = GetLineText(context.TriggerLine)
				lineLength = lineText.Length
				lineText = lineText.TrimStart()
				trimmedLength = lineLength - lineText.Length
				if lineText.StartsWith("import "):
					nameSpace = lineText[len("import "):context.TriggerLineOffset-(2+trimmedLength)].Trim()
					return ImportCompletionDataFor(nameSpace)

				result = null as CompletionDataList
				text = string.Format ("{0}{1} {2}", Document.TextEditor.GetText (0, context.TriggerOffset),
				                                    CompletionFinder.CompletionToken,
				                                    Document.TextEditor.GetText (context.TriggerOffset, Document.TextEditor.TextLength))
				# print text
				_resolver.Input.Clear()
				_resolver.Input.Add(StringInput("completion text", text))

				result = CompletionDataList()
				resultHash = Dictionary[of string,string]()

				_resolver.ResolveAnd() do (type as IType):
					# print type
					domType = _dom.GetType(type.FullName)
					if (null != domType):
						for member in domType.Members:
							resultHash[SanitizeMemberName(type,member.Name)] = member.StockIcon
					else:
						for member in type.GetMembers():
							# print member
							resultHash[SanitizeMemberName(type,member.Name)] = GetIconForMember(member)

					for pair in resultHash:
						valid = not string.IsNullOrEmpty(pair.Key)
						for prefix as string in ["get_","set_","add_","remove_"]:
							if (pair.Key.StartsWith(prefix, StringComparison.Ordinal) and \
							    resultHash.ContainsKey(pair.Key[prefix.Length:])):
								valid = false
						if (valid):
							result.Add(pair.Key, pair.Value)

				return result

			otherwise:
				return null

	def ImportCompletionDataFor(nameSpace as string):
#		print "ImportCompletionDataFor(${nameSpace})"

		result = CompletionDataList()
		for member in _dom.GetNamespaceContents(nameSpace, true, true):
			result.Add(member.Name, member.StockIcon)
		return result

	def GetLineText(line as int):
		return Document.TextEditor.GetLineText(line)

	def GetIconForMember(member as IEntity):
		match member.EntityType:
			case EntityType.BuiltinFunction:
				return Stock.Method
			case EntityType.Constructor:
				return Stock.Method
			case EntityType.Method:
				return Stock.Method
			case EntityType.Local:
				return Stock.Field
			case EntityType.Field:
				return Stock.Field
			case EntityType.Property:
				return Stock.Property
			case EntityType.Event:
				return Stock.Event
			otherwise:
				return Stock.Literal

	def SanitizeMemberName(type as IType,memberName as string) as string:
		name = memberName
		if (0 <= (lastDot = name.LastIndexOf('.'))):
			name = name[lastDot+1:]
		if ("constructor" == name or "ctor" == name):
			name = type.Name
		if (name.StartsWith("internal_", StringComparison.OrdinalIgnoreCase) or name.StartsWith("op_", StringComparison.Ordinal)):
			name = string.Empty
		return name

namespace UnityScript.MonoDevelop.Completion

import System
import System.Collections.Generic

import UnityScript
import UnityScript.MonoDevelop
import UnityScript.MonoDevelop.ProjectModel

import MonoDevelop.Core
import MonoDevelop.Projects
import MonoDevelop.Projects.Dom.Parser
import MonoDevelop.Ide.Gui
import MonoDevelop.Ide.Gui.Content
import MonoDevelop.Ide.CodeCompletion

import Boo.Lang.Compiler
import Boo.Lang.Compiler.Steps
import Boo.Lang.Compiler.Ast
import Boo.Lang.Compiler.IO
import Boo.Lang.Compiler.TypeSystem
import Boo.Lang.PatternMatching

class UnityScriptEditorCompletion(CompletionTextEditorExtension):

	_dom as ProjectDom
	_resolver as UnityScriptTypeResolver
	_project as DotNetProject

	override def Initialize():
		super()
		_dom = ProjectDomService.GetProjectDom(Document.Project) or ProjectDomService.GetFileDom(Document.FileName)
		InstallUnityScriptSyntaxModeIfNeeded()
		_resolver = UnityScriptTypeResolver()
		_project = Document.Project as DotNetProject
		InitializeProjectReferences()

	def InitializeProjectReferences():
		if _project is null:
			return

		for reference in _project.References:
			if ReferenceType.Project != reference.ReferenceType:
				_resolver.AddReference(reference.Reference)

	def InstallUnityScriptSyntaxModeIfNeeded():
		view = Document.GetContent[of MonoDevelop.SourceEditor.SourceEditorView]()
		return if view is null

		mimeType = UnityScriptParser.MimeType
		return if view.Document.SyntaxMode.MimeType == mimeType

		mode = Mono.TextEditor.Highlighting.SyntaxModeService.GetSyntaxMode(mimeType)
		if mode is not null:
			view.Document.SyntaxMode = mode
		else:
			LoggingService.LogWarning(GetType() + " could not get SyntaxMode for mimetype '" + mimeType + "'.")

	override def ExtendsEditor(doc as MonoDevelop.Ide.Gui.Document, editor as IEditableTextBuffer):
		return IsUnityScriptFile(doc.Name)

	override def HandleCodeCompletion(context as CodeCompletionContext, completionChar as char):
#		print "HandleCodeCompletion(${context.ToString()}, ${completionChar.ToString()})"

		match completionChar.ToString():
			case ' ':
				lineText = GetLineText(context.TriggerLine).TrimStart()
				if not lineText.StartsWith("import "):
					return null

				return ImportCompletionDataFor('')

			case '.':
				lineText = GetLineText(context.TriggerLine)
				lineLength = lineText.Length
				lineText = lineText.TrimStart()
				trimmedLength = lineLength - lineText.Length
				if lineText.StartsWith("import "):
					nameSpace = lineText[len("import "):context.TriggerLineOffset-(2+trimmedLength)].Trim()
					return ImportCompletionDataFor(nameSpace)

				result = null as CompletionDataList
				text = string.Format ("{0}{1} {2}", Document.TextEditor.GetText (0, context.TriggerOffset),
				                                    CompletionFinder.CompletionToken,
				                                    Document.TextEditor.GetText (context.TriggerOffset, Document.TextEditor.TextLength))
				# print text
				_resolver.Input.Clear()
				_resolver.Input.Add(StringInput("completion text", text))

				result = CompletionDataList()

				_resolver.ResolveAnd() do (type as IType):
					resultHash = Dictionary[of string,string]()
					for member in type.GetMembers():
						# print member
						resultHash[SanitizeMemberName(type,member)] = GetIconForMember(member)
					for pair in resultHash:
						unless string.IsNullOrEmpty(pair.Key) or (4 < pair.Key.Length and /^[gs]et_/.Matches(pair.Key) and resultHash.ContainsKey(pair.Key[4:])):
							result.Add(pair.Key, pair.Value)

				return result

			otherwise:
				return null

	def ImportCompletionDataFor(nameSpace as string):
#		print "ImportCompletionDataFor(${nameSpace})"

		result = CompletionDataList()
		for member in _dom.GetNamespaceContents(nameSpace, true, true):
			result.Add(member.Name, member.StockIcon)
		return result

	def GetLineText(line as int):
		return Document.TextEditor.GetLineText(line)

	def GetIconForMember(member as IEntity):
		match member.EntityType:
			case EntityType.BuiltinFunction:
				return Stock.Method
			case EntityType.Constructor:
				return Stock.Method
			case EntityType.Method:
				return Stock.Method
			case EntityType.Local:
				return Stock.Field
			case EntityType.Field:
				return Stock.Field
			case EntityType.Property:
				return Stock.Property
			case EntityType.Event:
				return Stock.Event
			otherwise:
				return Stock.Literal

	def SanitizeMemberName(type as IType,member as IEntity) as string:
		name = member.Name
		if (0 <= (lastDot = name.LastIndexOf('.'))):
			name = name[lastDot+1:]
		if ("constructor" == name):
			name = type.Name
		if (name.StartsWith("internal_", StringComparison.OrdinalIgnoreCase) or name.StartsWith("op_", StringComparison.Ordinal)):
			name = string.Empty
		return name


Minor cosmetic tweak. This is finally working!

""" Simple script to upload a new release asset to GitHub.

    Authentication is done using oAuth2 tokens. The token to use should
    be available as an environment variable named GITHUB_TOKEN.

    usage: booi github-release.boo -- <release> <path/to/build.zip>

    Note that It will remove any prior assets with the same name before
    uploading the new one.

"""

from System import Environment, Uri
from System.IO import Path, File
from System.Net import WebClient, WebException, HttpWebResponse
from System.Collections.Generic import List
import System.Web.Script.Serialization from System.Web.Extensions

class Release:
""" Models the Json responses from GitHub http://developer.github.com/v3/repos/releases/
    Using dynamic types is buggy under some Mono versions, so this is the
    only solution to make it work.
"""
    class Asset:
        property url as string
        property id as int
        property name as string
        property label as string
        property state as string
        property content_type as string
        property size as int
        property download_count as int
        property created_at as string
        property updated_at as string

    property assets as List[of Asset]
    property url as string
    property html_url as string
    property assets_url as string
    property upload_url as string
    property id as int
    property tag_name as string
    property target_commitish as string
    property name as string
    property body as string
    property draft as bool
    property prerelease as bool
    property created_at as string
    property published_at as string

def client():
""" WebClient doesn't keep the headers between requests, so we instantiate
    a new one every time we want to use it.
"""
    wc = WebClient()
    wc.Headers['Authorization'] = 'token ' + Environment.GetEnvironmentVariable('GITHUB_TOKEN')
    # The API requires this custom Accept header while in beta
    wc.Headers.Add('Accept', 'application/vnd.github.manifold-preview')
    wc.Headers.Add('user-agent', 'boo-lang')
    return wc

if len(argv) < 2:
    print 'Usage: github-release <release> <path/to/build.zip>'
    return

if not Environment.GetEnvironmentVariable('GITHUB_TOKEN'):
    raise 'GITHUB_TOKEN environment variable not set'

MAX_UPLOAD_RETRIES = 5
USERNAME = 'boo-lang'
REPO = 'boo'
API_URL = "https://api.github.com/repos/$USERNAME/$REPO/releases"
RELEASE_NAME = argv[0]
ASSET_FILE = argv[1]
ASSET_NAME = Path.GetFileName(ASSET_FILE)

json = JavaScriptSerializer()

print "Looking for release $RELEASE_NAME"
try:
    result = client().DownloadString(API_URL)
except wx as WebException:
    var e = wx.Response cast HttpWebResponse
    using sr = System.IO.StreamReader(e.GetResponseStream()):
        print sr.ReadToEnd()
    raise
releases = json.Deserialize[of List[of Release]](result)
release = releases.Find({rel | rel.tag_name == RELEASE_NAME})

if not release:
    raise "Release $RELEASE_NAME not found"

for asset in release.assets:
    if asset.name == ASSET_NAME:
        print "Removing asset $(asset.id)"
        client().UploadData(asset.url as string, "DELETE", array(byte, 0))
        break

upload_url = (release.upload_url as string).Replace('{?name,label}', "?name=$ASSET_NAME")
print "Uploading $ASSET_FILE to $upload_url"
retries = 0
:retry
try:
    using uploadClient = client():
        uploadClient.Headers.Add("Content-Type", "application/zip")
        using fileStream = File.OpenRead(ASSET_FILE), requestStream = uploadClient.OpenWrite(Uri(upload_url), "POST"):
            fileStream.CopyTo(requestStream)
except ex as WebException:
    var e2 = ex.Response cast HttpWebResponse
    using sr = System.IO.StreamReader(e2.GetResponseStream()):
        print sr.ReadToEnd()
    retries += 1
    if retries >= MAX_UPLOAD_RETRIES:
        print "Too many retries, giving up..."
        raise ex
    print "Upload failed ($(ex.Message)), retrying..."
    goto retry

print 'Upload successful.'

""" Simple script to upload a new release asset to GitHub.

    Authentication is done using oAuth2 tokens. The token to use should
    be available as an environment variable named GITHUB_TOKEN.

    usage: booi github-release.boo -- <release> <path/to/build.zip>

    Note that It will remove any prior assets with the same name before
    uploading the new one.

"""

from System import Environment, Uri
from System.IO import Path, File
from System.Net import WebClient, WebException, HttpWebResponse
from System.Collections.Generic import List
import System.Web.Script.Serialization from System.Web.Extensions

class Release:
""" Models the Json responses from GitHub http://developer.github.com/v3/repos/releases/
    Using dynamic types is buggy under some Mono versions, so this is the
    only solution to make it work.
"""
    class Asset:
        property url as string
        property id as int
        property name as string
        property label as string
        property state as string
        property content_type as string
        property size as int
        property download_count as int
        property created_at as string
        property updated_at as string

    property assets as List[of Asset]
    property url as string
    property html_url as string
    property assets_url as string
    property upload_url as string
    property id as int
    property tag_name as string
    property target_commitish as string
    property name as string
    property body as string
    property draft as bool
    property prerelease as bool
    property created_at as string
    property published_at as string

def client():
""" WebClient doesn't keep the headers between requests, so we instantiate
    a new one every time we want to use it.
"""
    wc = WebClient()
    wc.Headers['Authorization'] = 'token ' + Environment.GetEnvironmentVariable('GITHUB_TOKEN')
    # The API requires this custom Accept header while in beta
    wc.Headers.Add('Accept', 'application/vnd.github.manifold-preview')
    wc.Headers.Add('user-agent', 'boo-lang')
    return wc

if len(argv) < 2:
    print 'Usage: github-release <release> <path/to/build.zip>'
    return

if not Environment.GetEnvironmentVariable('GITHUB_TOKEN'):
    raise 'GITHUB_TOKEN environment variable not set'

MAX_UPLOAD_RETRIES = 5
USERNAME = 'boo-lang'
REPO = 'boo'
API_URL = "https://api.github.com/repos/$USERNAME/$REPO/releases"
RELEASE_NAME = argv[0]
ASSET_FILE = argv[1]
ASSET_NAME = Path.GetFileName(ASSET_FILE)

json = JavaScriptSerializer()

print "Looking for release $RELEASE_NAME"
try:
    result = client().DownloadString(API_URL)
except wx as WebException:
    var e = wx.Response cast HttpWebResponse
    using sr = System.IO.StreamReader(e.GetResponseStream()):
        print sr.ReadToEnd()
    raise
releases = json.Deserialize[of List[of Release]](result)
release = releases.Find({rel | rel.tag_name == RELEASE_NAME})

if not release:
    raise "Release $RELEASE_NAME not found"

for asset in release.assets:
    if asset.name == ASSET_NAME:
        print "Removing asset $(asset.id)"
        client().UploadData(asset.url as string, "DELETE", array(byte, 0))
        break

upload_url = (release.upload_url as string).Replace('{?name,label}', "?name=$ASSET_NAME&label=$RELEASE_NAME")
print "Uploading $ASSET_FILE to $upload_url"
retries = 0
:retry
try:
    using uploadClient = client():
        uploadClient.Headers.Add("Content-Type", "application/zip")
        using fileStream = File.OpenRead(ASSET_FILE), requestStream = uploadClient.OpenWrite(Uri(upload_url), "POST"):
            fileStream.CopyTo(requestStream)
except ex as WebException:
    var e2 = ex.Response cast HttpWebResponse
    using sr = System.IO.StreamReader(e2.GetResponseStream()):
        print sr.ReadToEnd()
    retries += 1
    if retries >= MAX_UPLOAD_RETRIES:
        print "Too many retries, giving up..."
        raise ex
    print "Upload failed ($(ex.Message)), retrying..."
    goto retry

print 'Upload successful.'


* implementing support to directory as classpaths

namespace boojay

import System
import System.Reflection
import System.Diagnostics
import System.IO as SysIO
import Boo.Lang.Compiler
import Boo.Lang.Compiler.IO
import Boojay.Compilation from Boojay.Compilation
import Boojay.Compilation.TypeSystem from Boojay.Compilation

import java.io
import java.util.zip

def loadAssembly(name as string):
	if SysIO.File.Exists(name):
		return Assembly.LoadFrom(name)
	return Assembly.Load(name)

def loadJar(name as string):
	return JarTypeSystemProvider().ForJar(name) if SysIO.File.Exists(name)
	raise Exception("Can' load library ${name}")

def parseCommandLine(argv as (string)):
	try:
		cmdLine = CommandLine(argv)
		if (not cmdLine.IsValid) or cmdLine.DoHelp:
			cmdLine.PrintOptions()
			return null
		return cmdLine
	except x:
		print "BCE000: ", x.Message
		return null

def selectPipeline(cmdLine as CommandLine):
	return (BoojayPipelines.ProduceBoo() if cmdLine.Boo else BoojayPipelines.ProduceBytecode())

def configureParams(cmdLine as CommandLine, params as CompilerParameters):
	for fname in cmdLine.SourceFiles():
		if cmdLine.Verbose: print fname
		params.Input.Add(FileInput(fname))

	for reference in cmdLine.References:
		params.References.Add(loadAssembly(reference))

	for classpath in retrieveJars(cmdLine.Classpaths):
		params.References.Add(loadJar(classpath))

	params.OutputAssembly = getOutputDirectory(cmdLine)
	if cmdLine.DebugCompiler:
		params.EnableTraceSwitch()
		params.TraceLevel = System.Diagnostics.TraceLevel.Verbose
		Trace.Listeners.Add(TextWriterTraceListener(System.Console.Error))

def retrieveJars(classpaths as List[of string]):
	for classpath in classpaths:
		if classpath.EndsWith(".jar"):
			yield classpath
		else:
			if SysIO.Directory.Exists(classpath):
				for jar in SysIO.Directory.GetFiles(classpath, "*.jar"):
					yield SysIO.Path.Combine(classpath, jar)
			else:
				print "Can' locate classpath: ${classpath}"

def showErrorsWarnings(cmdLine as CommandLine, result as CompilerContext):
	for error in result.Errors:
		print error.ToString(cmdLine.Verbose)
	for warning in result.Warnings:
		print warning

def outputJarFile(cmdLine as CommandLine, temporaryDirectory as string, result as CompilerContext):
	if result.Errors.Count == 0 and isJar(cmdLine.OutputDirectory):
		generateJar(cmdLine.OutputDirectory, temporaryDirectory, result["GeneratedClassFiles"])

def getOutputDirectory(cmdLine as CommandLine):
	return (SysIO.Path.GetTempPath() if isJar(cmdLine.OutputDirectory) else cmdLine.OutputDirectory)

def isJar(path as string):
	return path.EndsWith(".jar")

def generateJar(outputJar as string, temporaryDir as string, files as List):
	return unless files
	manifest = createManifest(temporaryDir)

	zos = ZipOutputStream(fos = FileOutputStream(outputJar))

	for name as string in files + [manifest]:
		zos.putNextEntry(ZipEntry(name.RemoveStart(temporaryDir)))
		zos.write(System.IO.File.ReadAllBytes(name))
		zos.closeEntry()

	zos.close()
	fos.close()

def createManifest(baseDir as string):
	filename = SysIO.Path.Combine(baseDir, "META-INF/MANIFEST.MF")
	SysIO.Directory.CreateDirectory(SysIO.Path.GetDirectoryName(filename))
	using file = SysIO.File.CreateText(filename):
		file.WriteLine("Manifest-Version: 1.0")
	return filename

[extension]
def RemoveStart(value as string, start as string):
	return value unless value.StartsWith(start)
	return value[start.Length:]

print "boojay .0a"

cmdLine = parseCommandLine(argv)
if cmdLine is null: return

compiler = newBoojayCompiler(selectPipeline(cmdLine))
configureParams(cmdLine, compiler.Parameters)

result = compiler.Run()

showErrorsWarnings(cmdLine, result)
outputJarFile(cmdLine, compiler.Parameters.OutputAssembly, result)

namespace boojay

import System
import System.Reflection
import System.Diagnostics
import System.IO as SysIO
import Boo.Lang.Compiler
import Boo.Lang.Compiler.IO
import Boojay.Compilation from Boojay.Compilation
import Boojay.Compilation.TypeSystem from Boojay.Compilation

import java.io
import java.util.zip

def loadAssembly(name as string):
	if SysIO.File.Exists(name):
		return Assembly.LoadFrom(name)
	return Assembly.Load(name)

def loadJar(name as string):
	return JarTypeSystemProvider().ForJar(name) if SysIO.File.Exists(name)
	raise Exception("Can' load library ${name}")

def parseCommandLine(argv as (string)):
	try:
		cmdLine = CommandLine(argv)
		if (not cmdLine.IsValid) or cmdLine.DoHelp:
			cmdLine.PrintOptions()
			return null
		return cmdLine
	except x:
		print "BCE000: ", x.Message
		return null

def selectPipeline(cmdLine as CommandLine):
	return (BoojayPipelines.ProduceBoo() if cmdLine.Boo else BoojayPipelines.ProduceBytecode())

def configureParams(cmdLine as CommandLine, params as CompilerParameters):
	for fname in cmdLine.SourceFiles():
		if cmdLine.Verbose: print fname
		params.Input.Add(FileInput(fname))

	for reference in cmdLine.References:
		params.References.Add(loadAssembly(reference))

	for classpath in cmdLine.Classpaths:
		params.References.Add(loadJar(classpath))

	params.OutputAssembly = getOutputDirectory(cmdLine)
	if cmdLine.DebugCompiler:
		params.EnableTraceSwitch()
		params.TraceLevel = System.Diagnostics.TraceLevel.Verbose
		Trace.Listeners.Add(TextWriterTraceListener(System.Console.Error))

def showErrorsWarnings(cmdLine as CommandLine, result as CompilerContext):
	for error in result.Errors:
		print error.ToString(cmdLine.Verbose)
	for warning in result.Warnings:
		print warning

def outputJarFile(cmdLine as CommandLine, temporaryDirectory as string, result as CompilerContext):
	if result.Errors.Count == 0 and isJar(cmdLine.OutputDirectory):
		generateJar(cmdLine.OutputDirectory, temporaryDirectory, result["GeneratedClassFiles"])

def getOutputDirectory(cmdLine as CommandLine):
	return (SysIO.Path.GetTempPath() if isJar(cmdLine.OutputDirectory) else cmdLine.OutputDirectory)

def isJar(path as string):
	return path.EndsWith(".jar")

def generateJar(outputJar as string, temporaryDir as string, files as List):
	return unless files
	manifest = createManifest(temporaryDir)

	zos = ZipOutputStream(fos = FileOutputStream(outputJar))

	for name as string in files + [manifest]:
		zos.putNextEntry(ZipEntry(name.RemoveStart(temporaryDir)))
		zos.write(System.IO.File.ReadAllBytes(name))
		zos.closeEntry()

	zos.close()
	fos.close()

def createManifest(baseDir as string):
	filename = SysIO.Path.Combine(baseDir, "META-INF/MANIFEST.MF")
	SysIO.Directory.CreateDirectory(SysIO.Path.GetDirectoryName(filename))
	using file = SysIO.File.CreateText(filename):
		file.WriteLine("Manifest-Version: 1.0")
	return filename

[extension]
def RemoveStart(value as string, start as string):
	return value unless value.StartsWith(start)
	return value[start.Length:]

print "boojay .0a"

cmdLine = parseCommandLine(argv)
if cmdLine is null: return

compiler = newBoojayCompiler(selectPipeline(cmdLine))
configureParams(cmdLine, compiler.Parameters)

result = compiler.Run()

showErrorsWarnings(cmdLine, result)
outputJarFile(cmdLine, compiler.Parameters.OutputAssembly, result)


better output for property interception

better output for property interception

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

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

namespace Boo.Lang.Useful.Attributes

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

class NoTrace(AbstractAstAttribute):
	public static final Annotation = object()

	override def Apply(node as Node):
		node.Annotate(NoTrace.Annotation)

class TraceMethodCallsAttribute(AbstractAstAttribute):
"""
	Visits every method adding a trace statement at both its very
	beginning and end.
"""

	[property(TraceMethod)]
	_traceMethod as ReferenceExpression

	def constructor():
		self([| System.Console.WriteLine |]);

	def constructor(traceMethod as ReferenceExpression):
		_traceMethod = traceMethod

	override def Apply(node as Node):
		node.Accept(TraceVisitor(_traceMethod))

	class IsGeneratorVisitor(DepthFirstVisitor):

		[getter(IsGenerator)]
		_isGenerator = false

		override def OnBlockExpression(node as BlockExpression):
			pass

		override def OnYieldStatement(node as YieldStatement):
			_isGenerator = true

	class TraceVisitor(DepthFirstVisitor):

		_traceMethod as ReferenceExpression

		def constructor(traceMethod as ReferenceExpression):
			_traceMethod = traceMethod

		def GetFullName(method as Method):
			if IsModuleClass(method.DeclaringType):
				module as Module = method.EnclosingModule
				if module.Namespace is null: return method.Name
				return "${module.Namespace.Name}.{method.Name}"

			prop = method.ParentNode as Property
			if prop is not null:
				return prop.FullName + "." + method.Name

			return method.FullName

		def IsModuleClass(type as TypeDefinition):
			return Boo.Lang.Compiler.Steps.IntroduceModuleClasses.IsModuleClass(type)

		override def LeaveMethod(method as Method):

			//print method, method.ContainsAnnotation(NoTrace.Annotation)
			if method.ContainsAnnotation(NoTrace.Annotation):
				return

			if not HasBody(method):
				return

			fullName = GetFullName(method)

			method.Body.Insert(0, TraceCall("TRACE: Entering ${fullName}"))
			if IsGenerator(method):
				return

			method.Body = [|
				try:
					$(method.Body)
				ensure:
					$(TraceCall("TRACE: Leaving ${fullName}"))
			|].ToBlock()

		def IsGenerator(method as Method):
			visitor = IsGeneratorVisitor()
			method.Body.Accept(visitor)
			return visitor.IsGenerator

		def HasBody(method as Method):
			if method.DeclaringType isa InterfaceDefinition: return false
			if method.Body is null or len(method.Body.Statements) == 0: return false
			return true

		def TraceCall(msg as string):
			return [| $_traceMethod($msg) |]

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

namespace Boo.Lang.Useful.Attributes

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

class NoTrace(AbstractAstAttribute):
	public static final Annotation = object()

	override def Apply(node as Node):
		node.Annotate(NoTrace.Annotation)

class TraceMethodCallsAttribute(AbstractAstAttribute):
"""
	Visits every method adding a trace statement at both its very
	beginning and end.
"""

	[property(TraceMethod)]
	_traceMethod as ReferenceExpression

	def constructor():
		self([| System.Console.WriteLine |]);

	def constructor(traceMethod as ReferenceExpression):
		_traceMethod = traceMethod

	override def Apply(node as Node):
		node.Accept(TraceVisitor(_traceMethod))

	class IsGeneratorVisitor(DepthFirstVisitor):

		[getter(IsGenerator)]
		_isGenerator = false

		override def OnBlockExpression(node as BlockExpression):
			pass

		override def OnYieldStatement(node as YieldStatement):
			_isGenerator = true

	class TraceVisitor(DepthFirstVisitor):

		_traceMethod as ReferenceExpression

		def constructor(traceMethod as ReferenceExpression):
			_traceMethod = traceMethod

		def GetFullName(method as Method):
			if not IsModuleClass(method.DeclaringType):
				return method.FullName

			module as Module = method.EnclosingModule
			if module.Namespace is null: return method.Name
			return "${module.Namespace.Name}.{method.Name}"

		def IsModuleClass(type as TypeDefinition):
			return Boo.Lang.Compiler.Steps.IntroduceModuleClasses.IsModuleClass(type)

		override def LeaveMethod(method as Method):

			//print method, method.ContainsAnnotation(NoTrace.Annotation)
			if method.ContainsAnnotation(NoTrace.Annotation):
				return

			if not HasBody(method):
				return

			fullName = GetFullName(method)

			method.Body.Insert(0, TraceCall("TRACE: Entering ${fullName}"))
			if IsGenerator(method):
				return

			method.Body = [|
				try:
					$(method.Body)
				ensure:
					$(TraceCall("TRACE: Leaving ${fullName}"))
			|].ToBlock()

		def IsGenerator(method as Method):
			visitor = IsGeneratorVisitor()
			method.Body.Accept(visitor)
			return visitor.IsGenerator

		def HasBody(method as Method):
			if method.DeclaringType isa InterfaceDefinition: return false
			if method.Body is null or len(method.Body.Statements) == 0: return false
			return true

		def TraceCall(msg as string):
			return [| $_traceMethod($msg) |]


unused import

unused import

namespace Boojay.Compilation.Steps

import Boo.Lang.Compiler.Ast
import Boo.Lang.Compiler.TypeSystem
import Boo.Lang.Compiler.Steps
import Boo.Lang.PatternMatching

import Boojay.Compilation.TypeSystem

class InjectCasts(AbstractTransformerCompilerStep):

	_currentReturnType as IType

	override def Run():
		Visit CompileUnit

	override def EnterMethod(node as Method):
		_currentReturnType = bindingFor(node).ReturnType
		return true

	override def LeaveBinaryExpression(node as BinaryExpression):
		match node.Operator:

			case BinaryOperatorType.Assign:
				node.Right = checkCast(typeOf(node.Left), node.Right)

			case BinaryOperatorType.Or:
				checkOperands node

			case BinaryOperatorType.And:
				checkOperands node

			case BinaryOperatorType.TypeTest:
				node.Right = mapBoxedType(node.Right)

			otherwise:
				pass

	def checkOperands(node as BinaryExpression):
		node.Right = checkCast(typeOf(node), node.Right)
		node.Left = checkCast(typeOf(node), node.Left)

	def mapBoxedType(e as TypeofExpression):
		boxedType = boxedTypeFor(bindingFor(e.Type))
		if boxedType is null: return e
		return CodeBuilder.CreateTypeofExpression(boxedType)

	override def LeaveMethodInvocationExpression(node as MethodInvocationExpression):
		m = optionalBindingFor(node.Target) as IMethodBase
		if m is null:
			return

		definition = definitionFor(m)
		parameterTypes = parameterTypesFor(definition)
		for i in range(len(parameterTypes)):
			node.Arguments[i] = checkCast(parameterTypes[i], node.Arguments[i])

		returnType = returnTypeOf(m)
		if returnType is null:
			return

		resultingNode = checkCast(returnType, returnTypeOf(definition), node)
		ReplaceCurrentNode(resultingNode)

	def returnTypeOf(m as IMethodBase):
		return m.CallableType.GetSignature().ReturnType

	def parameterTypesFor(m as IMethodBase):
		return array(p.Type for p in m.GetParameters())

	override def LeaveReturnStatement(node as ReturnStatement):
		if node.Expression is null:
			return
		node.Expression = checkCast(_currentReturnType, node.Expression)

	def optionalBindingFor(node as Node):
		return typeSystem().GetOptionalEntity(node)

	def checkCast(expected as IType, e as Expression):
		actual = typeOf(e)
		return checkCast(expected, actual, e)

	def checkCast(expected as IType, actual as IType, e as Expression):
		return checkCastAfterErasure(erasureFor(expected), erasureFor(actual), e)

	def checkCastAfterErasure(expected as IType, actual as IType, e as Expression):

		if expected is actual:
			return e

		if isUnbox(expected, actual):
			return unbox(expected, e)

		if isBox(expected, actual):
			return box(actual, e)

		# TODO: must take generic erasure in consideration for all the subclasses
		# and implemented interfaces
		if actual.IsSubclassOf(expected):
			return e

		if isJavaLangObject(expected):
			return e

#		watch e, e.GetType(), expected, actual
		resultingCast = CastExpression(
							Type: CodeBuilder.CreateTypeReference(expected),
							Target: e)
		BindExpressionType(resultingCast, expected)
		return resultingCast

	def isBox(expected as IType, actual as IType):
		return actual.IsValueType and not expected.IsValueType

	def box(type as IType, e as Expression):
		boxedType = boxedTypeFor(type)
		assert boxedType is not null, type.ToString()
		return boxTo(boxedType, e)

	_boxedTypes as Hash

	def boxedTypeFor(type as IType) as System.Type:
		if _boxedTypes is null:
			_boxedTypes = {
				typeSystem().CharType: java.lang.Character,
				typeSystem().BoolType: java.lang.Boolean,
				typeSystem().IntType: java.lang.Integer,
			}
		return _boxedTypes[type]

	def boxTo(type as System.Type, e as Expression):
		return CodeBuilder.CreateConstructorInvocation(firstConstructorFor(type), e)

	def firstConstructorFor(type as System.Type):
		return typeSystem().Map(type.GetConstructors()[0])

	def unbox(expected as IType, e as Expression):
		return CodeBuilder.CreateMethodInvocation(unboxMethodFor(expected), e)

	def unboxMethodFor(type as IType):
		return resolveRuntimeMethod("Unbox" + title(type.Name))

	def title(s as string):
		return s[:1].ToUpper() + s[1:]

	def isUnbox(expected as IType, actual as IType):
		return expected.IsValueType and not actual.IsValueType

	def typeSystem() as JavaTypeSystem:
		return self.TypeSystemServices

namespace Boojay.Compilation.Steps

import Boo.Lang.Compiler.Ast
import Boo.Lang.Compiler.TypeSystem
import Boo.Lang.Compiler.Steps
import Boo.Lang.PatternMatching

import Boojay.Compilation.TypeSystem
import Boojay.Compilation.Steps.Macros

class InjectCasts(AbstractTransformerCompilerStep):

	_currentReturnType as IType

	override def Run():
		Visit CompileUnit

	override def EnterMethod(node as Method):
		_currentReturnType = bindingFor(node).ReturnType
		return true

	override def LeaveBinaryExpression(node as BinaryExpression):
		match node.Operator:

			case BinaryOperatorType.Assign:
				node.Right = checkCast(typeOf(node.Left), node.Right)

			case BinaryOperatorType.Or:
				checkOperands node

			case BinaryOperatorType.And:
				checkOperands node

			case BinaryOperatorType.TypeTest:
				node.Right = mapBoxedType(node.Right)

			otherwise:
				pass

	def checkOperands(node as BinaryExpression):
		node.Right = checkCast(typeOf(node), node.Right)
		node.Left = checkCast(typeOf(node), node.Left)

	def mapBoxedType(e as TypeofExpression):
		boxedType = boxedTypeFor(bindingFor(e.Type))
		if boxedType is null: return e
		return CodeBuilder.CreateTypeofExpression(boxedType)

	override def LeaveMethodInvocationExpression(node as MethodInvocationExpression):
		m = optionalBindingFor(node.Target) as IMethodBase
		if m is null:
			return

		definition = definitionFor(m)
		parameterTypes = parameterTypesFor(definition)
		for i in range(len(parameterTypes)):
			node.Arguments[i] = checkCast(parameterTypes[i], node.Arguments[i])

		returnType = returnTypeOf(m)
		if returnType is null:
			return

		resultingNode = checkCast(returnType, returnTypeOf(definition), node)
		ReplaceCurrentNode(resultingNode)

	def returnTypeOf(m as IMethodBase):
		return m.CallableType.GetSignature().ReturnType

	def parameterTypesFor(m as IMethodBase):
		return array(p.Type for p in m.GetParameters())

	override def LeaveReturnStatement(node as ReturnStatement):
		if node.Expression is null:
			return
		node.Expression = checkCast(_currentReturnType, node.Expression)

	def optionalBindingFor(node as Node):
		return typeSystem().GetOptionalEntity(node)

	def checkCast(expected as IType, e as Expression):
		actual = typeOf(e)
		return checkCast(expected, actual, e)

	def checkCast(expected as IType, actual as IType, e as Expression):
		return checkCastAfterErasure(erasureFor(expected), erasureFor(actual), e)

	def checkCastAfterErasure(expected as IType, actual as IType, e as Expression):

		if expected is actual:
			return e

		if isUnbox(expected, actual):
			return unbox(expected, e)

		if isBox(expected, actual):
			return box(actual, e)

		# TODO: must take generic erasure in consideration for all the subclasses
		# and implemented interfaces
		if actual.IsSubclassOf(expected):
			return e

		if isJavaLangObject(expected):
			return e

#		watch e, e.GetType(), expected, actual
		resultingCast = CastExpression(
							Type: CodeBuilder.CreateTypeReference(expected),
							Target: e)
		BindExpressionType(resultingCast, expected)
		return resultingCast

	def isBox(expected as IType, actual as IType):
		return actual.IsValueType and not expected.IsValueType

	def box(type as IType, e as Expression):
		boxedType = boxedTypeFor(type)
		assert boxedType is not null, type.ToString()
		return boxTo(boxedType, e)

	_boxedTypes as Hash

	def boxedTypeFor(type as IType) as System.Type:
		if _boxedTypes is null:
			_boxedTypes = {
				typeSystem().CharType: java.lang.Character,
				typeSystem().BoolType: java.lang.Boolean,
				typeSystem().IntType: java.lang.Integer,
			}
		return _boxedTypes[type]

	def boxTo(type as System.Type, e as Expression):
		return CodeBuilder.CreateConstructorInvocation(firstConstructorFor(type), e)

	def firstConstructorFor(type as System.Type):
		return typeSystem().Map(type.GetConstructors()[0])

	def unbox(expected as IType, e as Expression):
		return CodeBuilder.CreateMethodInvocation(unboxMethodFor(expected), e)

	def unboxMethodFor(type as IType):
		return resolveRuntimeMethod("Unbox" + title(type.Name))

	def title(s as string):
		return s[:1].ToUpper() + s[1:]

	def isUnbox(expected as IType, actual as IType):
		return expected.IsValueType and not actual.IsValueType

	def typeSystem() as JavaTypeSystem:
		return self.TypeSystemServices


support for test scripts containing dots in their names

#!/usr/bin/env booi
"""
Generates test fixtures from files under tests/

First line of test case can specify a nunit attribute to go with it:
	* "// ignore reason" for [Ignore("reason")]
	* "// category name" for [Category("name")]
"""
import System
import System.IO
import Boo.Lang.PatternMatching

//def MapPath(path):
//	return Path.Combine(Project.BaseDirectory, path)

def GetTestCaseName(fname as string):
	return Path.GetFileNameWithoutExtension(fname).Replace("-", "_").Replace(".", "_")

def WriteTestCases(writer as TextWriter, baseDirs as string*):
	for baseDir in baseDirs:
		count = 0
		for fname in Directory.GetFiles(baseDir):
			continue unless fname.EndsWith(".js")
			++count
			categoryAttribute = CategoryAttributeFor(fname)
			writer.Write("""
	${categoryAttribute}
	[Test] def ${GetTestCaseName(fname)}():
		RunTestCase("${fname.Replace('\\', '/')}")
		""")
		print("${count} test cases found in ${baseDir}.")

def CategoryAttributeFor(testFile as string):
"""
If the first line of the test case file starts with // category CategoryName
then return a suitable [CategoryName()] attribute.
"""
	match FirstLineOf(testFile):
		case /\/\/\s*ignore\s+(?<reason>.*)/:
			return "[Ignore(\"${reason[0].Value.Trim()}\")]"
		case /\/\/\s*category\s+(?<name>.*)/:
			return "[Category(\"${name[0].Value.Trim()}\")]"
		otherwise:
			return ""

def FirstLineOf(fname as string):
	using reader=File.OpenText(fname):
		return reader.ReadLine()

def GenerateTestFixture(targetFile as string, header as string, *srcDirs as (string)):
	contents = GenerateTestFixtureSource(header, srcDirs)
	if ShouldReplaceContent(targetFile, contents):
		WriteAllText(targetFile, contents)

def ShouldReplaceContent(fname as string, contents as string):
	if not File.Exists(fname): return true
	return ns(ReadAllText(fname)) != ns(contents)

def ReadAllText(fname as string):
	using reader=File.OpenText(fname):
		return reader.ReadToEnd()

def WriteAllText(fname as string, contents as string):
	using writer=StreamWriter(fname):
		writer.Write(contents)

def ns(s as string):
"""
Normalize string.
"""
	return s.Trim().Replace("\r\n", Environment.NewLine)

def GenerateTestFixtureSource(header as string, srcDirs as (string)):
	writer=StringWriter()
	writer.Write(header)
	WriteTestCases(writer, srcDirs)
	return writer.ToString()

GenerateTestFixture("src/UnityScript.Tests/ParserTestFixture.Generated.boo", """
namespace UnityScript.Tests

import NUnit.Framework

partial class ParserTestFixture:
""", "tests/parser")

GenerateTestFixture("src/UnityScript.Tests/SemanticsTestFixture.boo", """
namespace UnityScript.Tests

import NUnit.Framework

[TestFixture]
class SemanticsTestFixture(AbstractSemanticsTestFixture):
""", "tests/semantics")

GenerateTestFixture("src/UnityScript.Tests/StrictIntegrationTestFixture.boo", """
namespace UnityScript.Tests

import NUnit.Framework

[TestFixture]
class StrictIntegrationTestFixture(AbstractIntegrationTestFixture):

	override def SetCompilationOptions():
		super()
		Parameters.Ducky = false
		Parameters.Strict = true

""", "tests/integration")

GenerateTestFixture("src/UnityScript.Tests/DuckyIntegrationTestFixture.boo", """
namespace UnityScript.Tests

import NUnit.Framework

[TestFixture]
class DuckyIntegrationTestFixture(AbstractIntegrationTestFixture):
	override def SetCompilationOptions():
		super()
		Parameters.Ducky = true
		Parameters.Strict = false
""", "tests/integration", "tests/ducky")

GenerateTestFixture("src/UnityScript.Tests/PragmaTestFixture.boo", """
namespace UnityScript.Tests

import NUnit.Framework

[TestFixture]
class PragmaTestFixture(AbstractIntegrationTestFixture):
""", "tests/pragma")

GenerateTestFixture("src/UnityScript.Tests/GenericsTestFixture.Generated.boo", """
namespace UnityScript.Tests

import NUnit.Framework

[TestFixture]
class GenericsTestFixture(AbstractIntegrationTestFixture):
""", "tests/generics")

GenerateTestFixture("src/UnityScript.Tests/EvalTestFixture.boo", """
namespace UnityScript.Tests

import NUnit.Framework

[TestFixture]
class EvalTestFixture(AbstractIntegrationTestFixture):
""", "tests/eval")

GenerateTestFixture("src/UnityScript.Tests/ExpandoTestFixture.boo", """
namespace UnityScript.Tests

import NUnit.Framework

[TestFixture]
class ExpandoTestFixture(AbstractIntegrationTestFixture):

	override def SetCompilationOptions():
		super()
		Parameters.Expando = true
""", "tests/expando")

GenerateTestFixture("src/UnityScript.Tests/ErrorMessagesTestFixture.Generated.boo", """
namespace UnityScript.Tests

import NUnit.Framework

partial class ErrorMessagesTestFixture:
""", "tests/error-messages")

GenerateTestFixture("src/UnityScript.Tests/StackTraceTestFixture.Generated.boo", """
namespace UnityScript.Tests

import NUnit.Framework

partial class StackTraceTestFixture:
""", "tests/stacktrace")

#!/usr/bin/env booi
"""
Generates test fixtures from files under tests/

First line of test case can specify a nunit attribute to go with it:
	* "// ignore reason" for [Ignore("reason")]
	* "// category name" for [Category("name")]
"""
import System
import System.IO
import Boo.Lang.PatternMatching

//def MapPath(path):
//	return Path.Combine(Project.BaseDirectory, path)

def GetTestCaseName(fname as string):
	return Path.GetFileNameWithoutExtension(fname).Replace("-", "_")

def WriteTestCases(writer as TextWriter, baseDirs as string*):
	for baseDir in baseDirs:
		count = 0
		for fname in Directory.GetFiles(baseDir):
			continue unless fname.EndsWith(".js")
			++count
			categoryAttribute = CategoryAttributeFor(fname)
			writer.Write("""
	${categoryAttribute}
	[Test] def ${GetTestCaseName(fname)}():
		RunTestCase("${fname.Replace('\\', '/')}")
		""")
		print("${count} test cases found in ${baseDir}.")

def CategoryAttributeFor(testFile as string):
"""
If the first line of the test case file starts with // category CategoryName
then return a suitable [CategoryName()] attribute.
"""
	match FirstLineOf(testFile):
		case /\/\/\s*ignore\s+(?<reason>.*)/:
			return "[Ignore(\"${reason[0].Value.Trim()}\")]"
		case /\/\/\s*category\s+(?<name>.*)/:
			return "[Category(\"${name[0].Value.Trim()}\")]"
		otherwise:
			return ""

def FirstLineOf(fname as string):
	using reader=File.OpenText(fname):
		return reader.ReadLine()

def GenerateTestFixture(targetFile as string, header as string, *srcDirs as (string)):
	contents = GenerateTestFixtureSource(header, srcDirs)
	if ShouldReplaceContent(targetFile, contents):
		WriteAllText(targetFile, contents)

def ShouldReplaceContent(fname as string, contents as string):
	if not File.Exists(fname): return true
	return ns(ReadAllText(fname)) != ns(contents)

def ReadAllText(fname as string):
	using reader=File.OpenText(fname):
		return reader.ReadToEnd()

def WriteAllText(fname as string, contents as string):
	using writer=StreamWriter(fname):
		writer.Write(contents)

def ns(s as string):
"""
Normalize string.
"""
	return s.Trim().Replace("\r\n", Environment.NewLine)

def GenerateTestFixtureSource(header as string, srcDirs as (string)):
	writer=StringWriter()
	writer.Write(header)
	WriteTestCases(writer, srcDirs)
	return writer.ToString()

GenerateTestFixture("src/UnityScript.Tests/ParserTestFixture.Generated.boo", """
namespace UnityScript.Tests

import NUnit.Framework

partial class ParserTestFixture:
""", "tests/parser")

GenerateTestFixture("src/UnityScript.Tests/SemanticsTestFixture.boo", """
namespace UnityScript.Tests

import NUnit.Framework

[TestFixture]
class SemanticsTestFixture(AbstractSemanticsTestFixture):
""", "tests/semantics")

GenerateTestFixture("src/UnityScript.Tests/StrictIntegrationTestFixture.boo", """
namespace UnityScript.Tests

import NUnit.Framework

[TestFixture]
class StrictIntegrationTestFixture(AbstractIntegrationTestFixture):

	override def SetCompilationOptions():
		super()
		Parameters.Ducky = false
		Parameters.Strict = true

""", "tests/integration")

GenerateTestFixture("src/UnityScript.Tests/DuckyIntegrationTestFixture.boo", """
namespace UnityScript.Tests

import NUnit.Framework

[TestFixture]
class DuckyIntegrationTestFixture(AbstractIntegrationTestFixture):
	override def SetCompilationOptions():
		super()
		Parameters.Ducky = true
		Parameters.Strict = false
""", "tests/integration", "tests/ducky")

GenerateTestFixture("src/UnityScript.Tests/PragmaTestFixture.boo", """
namespace UnityScript.Tests

import NUnit.Framework

[TestFixture]
class PragmaTestFixture(AbstractIntegrationTestFixture):
""", "tests/pragma")

GenerateTestFixture("src/UnityScript.Tests/GenericsTestFixture.Generated.boo", """
namespace UnityScript.Tests

import NUnit.Framework

[TestFixture]
class GenericsTestFixture(AbstractIntegrationTestFixture):
""", "tests/generics")

GenerateTestFixture("src/UnityScript.Tests/EvalTestFixture.boo", """
namespace UnityScript.Tests

import NUnit.Framework

[TestFixture]
class EvalTestFixture(AbstractIntegrationTestFixture):
""", "tests/eval")

GenerateTestFixture("src/UnityScript.Tests/ExpandoTestFixture.boo", """
namespace UnityScript.Tests

import NUnit.Framework

[TestFixture]
class ExpandoTestFixture(AbstractIntegrationTestFixture):

	override def SetCompilationOptions():
		super()
		Parameters.Expando = true
""", "tests/expando")

GenerateTestFixture("src/UnityScript.Tests/ErrorMessagesTestFixture.Generated.boo", """
namespace UnityScript.Tests

import NUnit.Framework

partial class ErrorMessagesTestFixture:
""", "tests/error-messages")

GenerateTestFixture("src/UnityScript.Tests/StackTraceTestFixture.Generated.boo", """
namespace UnityScript.Tests

import NUnit.Framework

partial class StackTraceTestFixture:
""", "tests/stacktrace")


-w command line option: print warnings

-w command line option: print warnings

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

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

import System
import System.IO
import System.Reflection
import System.Security.Permissions
import System.Threading
import Boo.Lang.Compiler
import Boo.Lang.Compiler.IO
import Boo.Lang.Compiler.Pipelines
import Boo.Lang.Compiler.Steps

class AssemblyResolver:

	_cache = {}

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

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

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

		return asm

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

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

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

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

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

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

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

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

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

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

import System
import System.IO
import System.Reflection
import System.Security.Permissions
import System.Threading
import Boo.Lang.Compiler
import Boo.Lang.Compiler.IO
import Boo.Lang.Compiler.Pipelines
import Boo.Lang.Compiler.Steps

class AssemblyResolver:

	_cache = {}

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

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

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

		return asm

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

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

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

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

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

	compiler = BooCompiler()

	compiler.Parameters.Pipeline = CompileToMemory()

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

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

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


from clause no longer necessary

from clause no longer necessary

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

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

import System
import System.IO
import Boo.IO
import Boo.Lang.Compiler
import Boo.Lang.Compiler.IO
import Boo.Lang.Compiler.Ast
import Gdk as Gdk
import Gtk
import GtkSourceView
import BooExplorer.Common

class BooEditor(ScrolledWindow):

	static _booSourceLanguage = SourceLanguagesManager().GetLanguageFromMimeType("text/x-boo")

	[getter(Buffer)]
	_buffer = SourceBuffer(_booSourceLanguage,
							Highlight: true)

	_view = SourceView(_buffer,
						ShowLineNumbers: true,
						AutoIndent: true,
						TabsWidth: 4)

	[getter(FileName)]
	_fname as string

	Label:
		get:
			return System.IO.Path.GetFileName(_fname) if _fname
			return "unnamed.boo"

	def constructor():
		self.SetPolicy(PolicyType.Automatic, PolicyType.Automatic)
		self.Add(_view)

	def Open([required] fname as string):
		_buffer.Text = TextFile.ReadFile(fname)
		_buffer.Modified = false
		_fname = fname

	def SaveAs([required] fname as string):
		TextFile.WriteFile(fname, _buffer.Text)
		_fname = fname
		_buffer.Modified = false

	def Redo():
		_buffer.Redo()

	def Undo():
		_buffer.Undo()

class MainWindow(Window):

	_status = Statusbar(HasResizeGrip: false)
	_notebookEditors = Notebook(TabPos: PositionType.Top, Scrollable: true)
	_notebookHelpers = Notebook(TabPos: PositionType.Bottom, Scrollable: true)
	_notebookOutline = Notebook(TabPos: PositionType.Bottom, Scrollable: true)

	_documentOutline = TreeView()

	_output = TextView(Editable: false)
	_outputBuffer = _output.Buffer

	_accelGroup = AccelGroup()
	_editors = [] # workaround for gtk# bug #61703

	def constructor():
		super("Boo Explorer")

		self.AddAccelGroup(_accelGroup)
		self.Maximize()
		self.DeleteEvent += OnDelete

		_documentOutline.AppendColumn("Name", CellRendererText (), ("text", 0))
		_notebookOutline.AppendPage(CreateScrolled(_documentOutline), Label("Document Outline"))
		_notebookHelpers.AppendPage(CreateScrolled(_output), Label("Output"))

		vbox = VBox(false, 2)
		vbox.PackStart(CreateMenuBar(), false, false, 0)

		editPanel = VPaned()
		editPanel.Pack1(_notebookEditors, true, true)
		editPanel.Pack2(_notebookHelpers, false, true)

		mainPanel = HPaned()
		mainPanel.Pack2(_notebookOutline, false, false)
		mainPanel.Pack1(editPanel, true, true)

		vbox.PackStart(mainPanel, true, true, 0)
		vbox.PackStart(_status, false, false, 0)

		self.Add(vbox)

		self.NewDocument()

		Timeout.Add(3s.TotalMilliseconds, self.UpdateDocumentOutline)

	private def CreateScrolled(widget):
		sw = ScrolledWindow()
		sw.Add(widget)
		return sw

	private def AppendEditor(editor as BooEditor):
		_notebookEditors.AppendPage(editor, Label(editor.Label))
		_editors.Add(editor)
		editor.ShowAll()
		_notebookEditors.CurrentPage = _notebookEditors.NPages-1

	def NewDocument():
		self.AppendEditor(editor=BooEditor())
		return editor

	def OpenDocument(fname as string):
		editor = BooEditor()
		editor.Open(fname)
		self.AppendEditor(editor)
		return editor

	private def CreateMenuBar():
		mb = MenuBar()

		file = Menu()
		file.Append(ImageMenuItem(Stock.New, _accelGroup, Activated: _menuItemNew_Activated))
		file.Append(ImageMenuItem(Stock.Open, _accelGroup, Activated: _menuItemOpen_Activated))
		file.Append(ImageMenuItem(Stock.Save, _accelGroup, Activated: _menuItemSave_Activated))
		file.Append(SeparatorMenuItem())
		file.Append(ImageMenuItem(Stock.Quit, _accelGroup, Activated: _menuItemExit_Activated))

		edit = Menu()
		edit.Append(ImageMenuItem(Stock.Undo, _accelGroup, Activated: _menuItemUndo_Activated))
		edit.Append(ImageMenuItem(Stock.Redo, _accelGroup, Activated: _menuItemRedo_Activated))
		edit.Append(SeparatorMenuItem())
		edit.Append(ImageMenuItem(Stock.Cut, _accelGroup, Activated: _menuItemCut_Activated))
		edit.Append(ImageMenuItem(Stock.Copy, _accelGroup, Activated: _menuItemCopy_Activated))
		edit.Append(ImageMenuItem(Stock.Paste, _accelGroup, Activated: _menuItemPaste_Activated))
		edit.Append(ImageMenuItem(Stock.Delete, _accelGroup, Activated: _menuItemDelete_Activated))
		edit.Append(SeparatorMenuItem())
		edit.Append(ImageMenuItem(Stock.Preferences, _accelGroup))

		tools = Menu()
		tools.Append(mi=ImageMenuItem(Stock.Execute, _accelGroup, Activated: _menuItemExecute_Activated))
		mi.AddAccelerator("activate", _accelGroup, AccelKey(Gdk.Key.F5, Enum.ToObject(Gdk.ModifierType, 0), AccelFlags.Visible))
		tools.Append(miExpand=MenuItem("Expand", Activated: _menuItemExpand_Activated))
		miExpand.AddAccelerator("activate", _accelGroup, AccelKey(Gdk.Key.E, Gdk.ModifierType.ControlMask, AccelFlags.Visible))

		documents = Menu()
		documents.Append(ImageMenuItem(Stock.Close, _accelGroup, Activated: _menuItemClose_Activated))

		mb.Append(MenuItem("_File", Submenu: file))
		mb.Append(MenuItem("_Edit", Submenu: edit))
		mb.Append(MenuItem("_Tools", Submenu: tools))
		mb.Append(MenuItem("_Documents", Submenu: documents))
		return mb

	CurrentEditor as BooEditor:
		get:
			// can't do the simpler:
			// editor as BooEditor = _notebookEditors.CurrentPageWidget
			// because of gtk# bug #61703
			return _editors[_notebookEditors.CurrentPage]

	def AppendOutput(text as string):
		target = _outputBuffer.EndIter
		_outputBuffer.Insert(target, text)

	def DisplayErrors(errors as CompilerErrorCollection):
		self.AppendOutput(errors.ToString(true)) if (len(errors))

	private def GetClipboard():
		return Clipboard.Get(Gdk.Selection.Clipboard)

	CurrentBuffer:
		get:
			return CurrentEditor.Buffer

	private def _menuItemClose_Activated():
		_editors.RemoveAt(_notebookEditors.CurrentPage)
		_notebookEditors.RemovePage(_notebookEditors.CurrentPage)

	private def _menuItemCut_Activated():
		CurrentBuffer.CutClipboard(GetClipboard(), true)

	private def _menuItemCopy_Activated():
		CurrentBuffer.CopyClipboard(GetClipboard())

	private def _menuItemPaste_Activated():
		CurrentBuffer.PasteClipboard(GetClipboard())

	private def _menuItemDelete_Activated():
		pass

	private def _menuItemExecute_Activated():

		_outputBuffer.Clear()
		self.AppendOutput("${_outputBuffer.Text}****** Compiling ${CurrentEditor.Label} *******\n")
		compiler = CreateCompiler(Boo.Lang.Compiler.Pipelines.Run())
		compiler.Parameters.Input.Add(StringInput(CurrentEditor.Label, CurrentEditor.Buffer.Text))

		start = date.Now
		try:
			using console=Boo.Lang.Interpreter.ConsoleCapture():
				result = compiler.Run()
			self.DisplayErrors(result.Errors)
			self.AppendOutput(console.ToString())
		except x:
			self.AppendOutput(x.ToString())
		self.AppendOutput("Complete in ${date.Now-start}.")

	private def _menuItemNew_Activated():
		self.NewDocument()

	private def _menuItemOpen_Activated():
		fs = FileSelection("Open file", SelectMultiple: true)
		fs.Complete("*.boo")
		try:
			if cast(int, ResponseType.Ok) == fs.Run():
				for fname in fs.Selections:
					self.OpenDocument(fname)
		ensure:
			fs.Hide()

		self.UpdateDocumentOutline()

	private def UpdateDocumentOutline():
		try:
			DocumentOutlineProcessor(_documentOutline, CurrentEditor).Update()
		except ignored:
			pass
		return true // to match Gdk.Function signature

	private def _menuItemExpand_Activated():
		editor = CurrentEditor

		compiler = CreateCompiler(Boo.Lang.Compiler.Pipelines.CompileToBoo())
		compiler.Parameters.OutputWriter = StringWriter()
		compiler.Parameters.Input.Add(StringInput(editor.Label, editor.Buffer.Text))
		result = compiler.Run()
		self.DisplayErrors(result.Errors)
		unless len(result.Errors):
			NewDocument().Buffer.Text = compiler.Parameters.OutputWriter.ToString()

	private def CreateCompiler(pipeline):
		compiler = BooCompiler()
		compiler.Parameters.Pipeline = pipeline
		compiler.Parameters.References.Add(System.Reflection.Assembly.LoadWithPartialName("nunit.framework"))
		return compiler

	private def _menuItemSave_Activated():
		editor = CurrentEditor

		fname = editor.FileName
		if fname is null:
			fs = FileSelection("Save As", SelectMultiple: false)
			if cast(int, ResponseType.Ok) != fs.Run():
				return
			fs.Hide()
			fname = fs.Selections[0]
		editor.SaveAs(fname)
		_notebookEditors.SetTabLabelText(editor, editor.Label)

	private def _menuItemUndo_Activated():
		CurrentEditor.Undo()

	private def _menuItemRedo_Activated():
		CurrentEditor.Redo()

	private def _menuItemExit_Activated():
		Application.Quit()

	def OnDelete(sender, args as DeleteEventArgs):
		Application.Quit()
		args.RetVal = true

class DocumentOutlineProcessor:

	_store = TreeStore((string,))
	_documentOutline as TreeView
	_module as Boo.Lang.Compiler.Ast.Module

	def constructor(documentOutline, editor as BooEditor):
		_module = Parse(editor.Label, editor.Buffer.Text)
		_documentOutline = documentOutline

	def Parse(fname, text):
		compiler = BooCompiler()
		compiler.Parameters.Input.Add(StringInput(fname, text))
		compiler.Parameters.Pipeline = Pipelines.Parse()
		return compiler.Run().CompileUnit.Modules[0]

	def Update():
		for type in _module.Members:
			iter = _store.AppendValues((type.Name,))
			if type isa TypeDefinition:
				UpdateType(iter, type)
		_documentOutline.Model = _store
		_documentOutline.ExpandAll()

	def UpdateType(parent, type as TypeDefinition):
		for member in type.Members:
			_store.AppendValues(parent, (member.Name,))

Application.Init()

MainWindow().ShowAll()

Application.Run()

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

import System
import System.IO
import Boo.IO
import Boo.Lang.Compiler
import Boo.Lang.Compiler.IO
import Boo.Lang.Compiler.Ast
import Gdk from "gdk-sharp" as Gdk
import Gtk from "gtk-sharp"
import GtkSourceView from "gtksourceview-sharp"
import BooExplorer.Common from BooExplorer.Common

class BooEditor(ScrolledWindow):

	static _booSourceLanguage = SourceLanguagesManager().GetLanguageFromMimeType("text/x-boo")

	[getter(Buffer)]
	_buffer = SourceBuffer(_booSourceLanguage,
							Highlight: true)

	_view = SourceView(_buffer,
						ShowLineNumbers: true,
						AutoIndent: true,
						TabsWidth: 4)

	[getter(FileName)]
	_fname as string

	Label:
		get:
			return System.IO.Path.GetFileName(_fname) if _fname
			return "unnamed.boo"

	def constructor():
		self.SetPolicy(PolicyType.Automatic, PolicyType.Automatic)
		self.Add(_view)

	def Open([required] fname as string):
		_buffer.Text = TextFile.ReadFile(fname)
		_buffer.Modified = false
		_fname = fname

	def SaveAs([required] fname as string):
		TextFile.WriteFile(fname, _buffer.Text)
		_fname = fname
		_buffer.Modified = false

	def Redo():
		_buffer.Redo()

	def Undo():
		_buffer.Undo()

class MainWindow(Window):

	_status = Statusbar(HasResizeGrip: false)
	_notebookEditors = Notebook(TabPos: PositionType.Top, Scrollable: true)
	_notebookHelpers = Notebook(TabPos: PositionType.Bottom, Scrollable: true)
	_notebookOutline = Notebook(TabPos: PositionType.Bottom, Scrollable: true)

	_documentOutline = TreeView()

	_output = TextView(Editable: false)
	_outputBuffer = _output.Buffer

	_accelGroup = AccelGroup()
	_editors = [] # workaround for gtk# bug #61703

	def constructor():
		super("Boo Explorer")

		self.AddAccelGroup(_accelGroup)
		self.Maximize()
		self.DeleteEvent += OnDelete

		_documentOutline.AppendColumn("Name", CellRendererText (), ("text", 0))
		_notebookOutline.AppendPage(CreateScrolled(_documentOutline), Label("Document Outline"))
		_notebookHelpers.AppendPage(CreateScrolled(_output), Label("Output"))

		vbox = VBox(false, 2)
		vbox.PackStart(CreateMenuBar(), false, false, 0)

		editPanel = VPaned()
		editPanel.Pack1(_notebookEditors, true, true)
		editPanel.Pack2(_notebookHelpers, false, true)

		mainPanel = HPaned()
		mainPanel.Pack2(_notebookOutline, false, false)
		mainPanel.Pack1(editPanel, true, true)

		vbox.PackStart(mainPanel, true, true, 0)
		vbox.PackStart(_status, false, false, 0)

		self.Add(vbox)

		self.NewDocument()

		Timeout.Add(3s.TotalMilliseconds, self.UpdateDocumentOutline)

	private def CreateScrolled(widget):
		sw = ScrolledWindow()
		sw.Add(widget)
		return sw

	private def AppendEditor(editor as BooEditor):
		_notebookEditors.AppendPage(editor, Label(editor.Label))
		_editors.Add(editor)
		editor.ShowAll()
		_notebookEditors.CurrentPage = _notebookEditors.NPages-1

	def NewDocument():
		self.AppendEditor(editor=BooEditor())
		return editor

	def OpenDocument(fname as string):
		editor = BooEditor()
		editor.Open(fname)
		self.AppendEditor(editor)
		return editor

	private def CreateMenuBar():
		mb = MenuBar()

		file = Menu()
		file.Append(ImageMenuItem(Stock.New, _accelGroup, Activated: _menuItemNew_Activated))
		file.Append(ImageMenuItem(Stock.Open, _accelGroup, Activated: _menuItemOpen_Activated))
		file.Append(ImageMenuItem(Stock.Save, _accelGroup, Activated: _menuItemSave_Activated))
		file.Append(SeparatorMenuItem())
		file.Append(ImageMenuItem(Stock.Quit, _accelGroup, Activated: _menuItemExit_Activated))

		edit = Menu()
		edit.Append(ImageMenuItem(Stock.Undo, _accelGroup, Activated: _menuItemUndo_Activated))
		edit.Append(ImageMenuItem(Stock.Redo, _accelGroup, Activated: _menuItemRedo_Activated))
		edit.Append(SeparatorMenuItem())
		edit.Append(ImageMenuItem(Stock.Cut, _accelGroup, Activated: _menuItemCut_Activated))
		edit.Append(ImageMenuItem(Stock.Copy, _accelGroup, Activated: _menuItemCopy_Activated))
		edit.Append(ImageMenuItem(Stock.Paste, _accelGroup, Activated: _menuItemPaste_Activated))
		edit.Append(ImageMenuItem(Stock.Delete, _accelGroup, Activated: _menuItemDelete_Activated))
		edit.Append(SeparatorMenuItem())
		edit.Append(ImageMenuItem(Stock.Preferences, _accelGroup))

		tools = Menu()
		tools.Append(mi=ImageMenuItem(Stock.Execute, _accelGroup, Activated: _menuItemExecute_Activated))
		mi.AddAccelerator("activate", _accelGroup, AccelKey(Gdk.Key.F5, Enum.ToObject(Gdk.ModifierType, 0), AccelFlags.Visible))
		tools.Append(miExpand=MenuItem("Expand", Activated: _menuItemExpand_Activated))
		miExpand.AddAccelerator("activate", _accelGroup, AccelKey(Gdk.Key.E, Gdk.ModifierType.ControlMask, AccelFlags.Visible))

		documents = Menu()
		documents.Append(ImageMenuItem(Stock.Close, _accelGroup, Activated: _menuItemClose_Activated))

		mb.Append(MenuItem("_File", Submenu: file))
		mb.Append(MenuItem("_Edit", Submenu: edit))
		mb.Append(MenuItem("_Tools", Submenu: tools))
		mb.Append(MenuItem("_Documents", Submenu: documents))
		return mb

	CurrentEditor as BooEditor:
		get:
			// can't do the simpler:
			// editor as BooEditor = _notebookEditors.CurrentPageWidget
			// because of gtk# bug #61703
			return _editors[_notebookEditors.CurrentPage]

	def AppendOutput(text as string):
		target = _outputBuffer.EndIter
		_outputBuffer.Insert(target, text)

	def DisplayErrors(errors as CompilerErrorCollection):
		self.AppendOutput(errors.ToString(true)) if (len(errors))

	private def GetClipboard():
		return Clipboard.Get(Gdk.Selection.Clipboard)

	CurrentBuffer:
		get:
			return CurrentEditor.Buffer

	private def _menuItemClose_Activated():
		_editors.RemoveAt(_notebookEditors.CurrentPage)
		_notebookEditors.RemovePage(_notebookEditors.CurrentPage)

	private def _menuItemCut_Activated():
		CurrentBuffer.CutClipboard(GetClipboard(), true)

	private def _menuItemCopy_Activated():
		CurrentBuffer.CopyClipboard(GetClipboard())

	private def _menuItemPaste_Activated():
		CurrentBuffer.PasteClipboard(GetClipboard())

	private def _menuItemDelete_Activated():
		pass

	private def _menuItemExecute_Activated():

		_outputBuffer.Clear()
		self.AppendOutput("${_outputBuffer.Text}****** Compiling ${CurrentEditor.Label} *******\n")
		compiler = CreateCompiler(Boo.Lang.Compiler.Pipelines.Run())
		compiler.Parameters.Input.Add(StringInput(CurrentEditor.Label, CurrentEditor.Buffer.Text))

		start = date.Now
		try:
			using console=Boo.Lang.Interpreter.ConsoleCapture():
				result = compiler.Run()
			self.DisplayErrors(result.Errors)
			self.AppendOutput(console.ToString())
		except x:
			self.AppendOutput(x.ToString())
		self.AppendOutput("Complete in ${date.Now-start}.")

	private def _menuItemNew_Activated():
		self.NewDocument()

	private def _menuItemOpen_Activated():
		fs = FileSelection("Open file", SelectMultiple: true)
		fs.Complete("*.boo")
		try:
			if cast(int, ResponseType.Ok) == fs.Run():
				for fname in fs.Selections:
					self.OpenDocument(fname)
		ensure:
			fs.Hide()

		self.UpdateDocumentOutline()

	private def UpdateDocumentOutline():
		try:
			DocumentOutlineProcessor(_documentOutline, CurrentEditor).Update()
		except ignored:
			pass
		return true // to match Gdk.Function signature

	private def _menuItemExpand_Activated():
		editor = CurrentEditor

		compiler = CreateCompiler(Boo.Lang.Compiler.Pipelines.CompileToBoo())
		compiler.Parameters.OutputWriter = StringWriter()
		compiler.Parameters.Input.Add(StringInput(editor.Label, editor.Buffer.Text))
		result = compiler.Run()
		self.DisplayErrors(result.Errors)
		unless len(result.Errors):
			NewDocument().Buffer.Text = compiler.Parameters.OutputWriter.ToString()

	private def CreateCompiler(pipeline):
		compiler = BooCompiler()
		compiler.Parameters.Pipeline = pipeline
		compiler.Parameters.References.Add(System.Reflection.Assembly.LoadWithPartialName("nunit.framework"))
		return compiler

	private def _menuItemSave_Activated():
		editor = CurrentEditor

		fname = editor.FileName
		if fname is null:
			fs = FileSelection("Save As", SelectMultiple: false)
			if cast(int, ResponseType.Ok) != fs.Run():
				return
			fs.Hide()
			fname = fs.Selections[0]
		editor.SaveAs(fname)
		_notebookEditors.SetTabLabelText(editor, editor.Label)

	private def _menuItemUndo_Activated():
		CurrentEditor.Undo()

	private def _menuItemRedo_Activated():
		CurrentEditor.Redo()

	private def _menuItemExit_Activated():
		Application.Quit()

	def OnDelete(sender, args as DeleteEventArgs):
		Application.Quit()
		args.RetVal = true

class DocumentOutlineProcessor:

	_store = TreeStore((string,))
	_documentOutline as TreeView
	_module as Boo.Lang.Compiler.Ast.Module

	def constructor(documentOutline, editor as BooEditor):
		_module = Parse(editor.Label, editor.Buffer.Text)
		_documentOutline = documentOutline

	def Parse(fname, text):
		compiler = BooCompiler()
		compiler.Parameters.Input.Add(StringInput(fname, text))
		compiler.Parameters.Pipeline = Pipelines.Parse()
		return compiler.Run().CompileUnit.Modules[0]

	def Update():
		for type in _module.Members:
			iter = _store.AppendValues((type.Name,))
			if type isa TypeDefinition:
				UpdateType(iter, type)
		_documentOutline.Model = _store
		_documentOutline.ExpandAll()

	def UpdateType(parent, type as TypeDefinition):
		for member in type.Members:
			_store.AppendValues(parent, (member.Name,))

Application.Init()

MainWindow().ShowAll()

Application.Run()


revert eval change

revert eval change

namespace UnityScript.Scripting.Pipeline

import System
import System.Collections.Generic
import Boo.Lang.Compiler.Steps
import Boo.Lang.Compiler.TypeSystem
import UnityScript.Scripting

class IntroduceScriptingNamespace(AbstractCompilerStep):
"""
Makes the compiler take the EvaluationContext namespace into
consideration.
"""

	_evaluationContext as EvaluationContext

	def constructor(evaluationContext as EvaluationContext):
		_evaluationContext = evaluationContext

	override def Run():
		NameResolutionService.GlobalNamespace = EvaluationContextNamespace(
													self.TypeSystemServices,
													NameResolutionService.GlobalNamespace,
													_evaluationContext)

class EvaluationContextEntity(ITypedEntity):

	[getter(Delegate)]
	_delegate as IMember

	def constructor(delegate as IMember):
		_delegate = delegate

	Name as string:
		get: return _delegate.Name

	FullName as string:
		get: return _delegate.FullName

	EntityType as EntityType:
		get: return _delegate.EntityType

	Type as IType:
		get: return _delegate.Type

class ActiveScriptEntity(EvaluationContextEntity):

	[getter(Script)]
	_scriptId as int

	def constructor(scriptId as int, delegate as IMember):
		super(delegate)
		_scriptId = scriptId

class EvaluationContextNamespace(INamespace):

	_tss as TypeSystemServices
	_parent as INamespace
	_context as EvaluationContext
	_contextNamespace as INamespace
	_scriptContainerNamespace as INamespace
	_entityMapping = {}
	_activeScripts as (object)

	def constructor([required] tss as TypeSystemServices, [required] parent as INamespace, [required] context as EvaluationContext):
		_tss = tss
		_parent = parent
		_context = context
		_contextNamespace = tss.Map(_context.GetType())
		_activeScripts = _context.GetActiveScripts()
		_scriptContainerNamespace = tss.Map(_context.GetType().DeclaringType)

	Name:
		get: return "EvaluationContext"

	FullName:
		get: return Name

	EntityType:
		get: return EntityType.Namespace

	ParentNamespace as INamespace:
		get: return _parent

	def Resolve(targetList as ICollection of IEntity, name as string, filter as EntityType) as bool:
		if Resolve(_contextNamespace, targetList, name, filter): return true
		if Resolve(_scriptContainerNamespace, targetList, name, filter): return true

		for script in _activeScripts:
			mapActiveScript as EntityMapper = { entity as IEntity | ActiveScriptEntity(_context.GetActiveScriptId(script), entity) }
			scriptType = _tss.Map(script.GetType())
			if Resolve(scriptType, targetList, name, filter, mapActiveScript): return true

		return false

	def Resolve(ns as INamespace, targetList as ICollection of IEntity, name as string, filter as EntityType) as bool:
		return Resolve(ns, targetList, name, filter, { entity as IEntity | EvaluationContextEntity(entity) })

	def Resolve(ns as INamespace, targetList as ICollection of IEntity, name as string, filter as EntityType, mapper as EntityMapper) as bool:
		found = List[of IEntity]()
		if not ns.Resolve(found, name, filter): return false
		for entity in found:
			targetList.Add(MapEntity(entity, mapper))
		return true

	callable EntityMapper(entity as IEntity) as EvaluationContextEntity

	def MapEntity(entity as IEntity, mapper as EntityMapper):
		found = _entityMapping[entity]
		if found is not null: return found
		_entityMapping[entity] = found = mapper(entity)
		return found

	def GetMembers() as IEntity*:
		pass

namespace UnityScript.Scripting.Pipeline

import System
import System.Collections.Generic
import Boo.Lang.Compiler.Steps
import Boo.Lang.Compiler.TypeSystem
import UnityScript.Scripting

class IntroduceScriptingNamespace(AbstractCompilerStep):
"""
Makes the compiler take the EvaluationContext namespace into
consideration.
"""

	_evaluationContext as EvaluationContext

	def constructor(evaluationContext as EvaluationContext):
		_evaluationContext = evaluationContext

	override def Run():
		NameResolutionService.GlobalNamespace = EvaluationContextNamespace(
													self.TypeSystemServices,
													NameResolutionService.GlobalNamespace,
													_evaluationContext)

class EvaluationContextEntity(ITypedEntity):

	[getter(Delegate)]
	_delegate as IMember

	def constructor(delegate as IMember):
		_delegate = delegate

	Name as string:
		get: return _delegate.Name

	FullName as string:
		get: return _delegate.FullName

	EntityType as EntityType:
		get: return _delegate.EntityType

	Type as IType:
		get: return _delegate.Type

class ActiveScriptEntity(EvaluationContextEntity):

	[getter(Script)]
	_scriptId as int

	def constructor(scriptId as int, delegate as IMember):
		super(delegate)
		_scriptId = scriptId

class EvaluationContextNamespace(INamespace):

	_tss as TypeSystemServices
	_parent as INamespace
	_context as EvaluationContext
	_contextNamespace as INamespace
	_scriptContainerNamespace as INamespace
	_entityMapping = {}
	_activeScripts as (object)

	def constructor([required] tss as TypeSystemServices, [required] parent as INamespace, [required] context as EvaluationContext):
		_tss = tss
		_parent = parent
		_context = context
		_contextNamespace = tss.Map(_context.GetType())

		// the UnityScript compiler will nest EvaluationContext subclasses in the type
		// doing the eval so it can be discovered and have its members accessible to the
		// script
		contextContainerType = _context.GetType().DeclaringType
		_scriptContainerNamespace = (tss.Map(contextContainerType) if contextContainerType is not null else NullNamespace.Default)
		_activeScripts = _context.GetActiveScripts()

	Name:
		get: return "EvaluationContext"

	FullName:
		get: return Name

	EntityType:
		get: return EntityType.Namespace

	ParentNamespace as INamespace:
		get: return _parent

	def Resolve(targetList as ICollection of IEntity, name as string, filter as EntityType) as bool:
		if Resolve(_contextNamespace, targetList, name, filter): return true
		if Resolve(_scriptContainerNamespace, targetList, name, filter): return true

		for script in _activeScripts:
			mapActiveScript as EntityMapper = { entity as IEntity | ActiveScriptEntity(_context.GetActiveScriptId(script), entity) }
			scriptType = _tss.Map(script.GetType())
			if Resolve(scriptType, targetList, name, filter, mapActiveScript): return true

		return false

	def Resolve(ns as INamespace, targetList as ICollection of IEntity, name as string, filter as EntityType) as bool:
		return Resolve(ns, targetList, name, filter, { entity as IEntity | EvaluationContextEntity(entity) })

	def Resolve(ns as INamespace, targetList as ICollection of IEntity, name as string, filter as EntityType, mapper as EntityMapper) as bool:
		found = List[of IEntity]()
		if not ns.Resolve(found, name, filter): return false
		for entity in found:
			targetList.Add(MapEntity(entity, mapper))
		return true

	callable EntityMapper(entity as IEntity) as EvaluationContextEntity

	def MapEntity(entity as IEntity, mapper as EntityMapper):
		found = _entityMapping[entity]
		if found is not null: return found
		_entityMapping[entity] = found = mapper(entity)
		return found

	def GetMembers() as IEntity*:
		pass


ability to execute script with Main method using the boo nant task

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

namespace Boo.NAnt

import System
import System.IO
import System.Linq.Enumerable
import NAnt.Core
import NAnt.Core.Attributes
import NAnt.Core.Types
import Boo.Lang.Compiler
import Boo.Lang.Compiler.IO
import Boo.Lang.Compiler.Ast
import Boo.Lang.Compiler.Pipelines
import Boo.Lang.Compiler.Steps

class AbstractScript:

	_project as Project
	_task as BooTask

	Project:
		get: return _project
		set: _project = value

	Task:
		get: return _task
		set: _task = value

	def print(msg):
		_task.LogInfo(msg)

	abstract def Run(argv as (string)):
		pass

class PrepareScriptStep(AbstractCompilerStep):

	override def Run():
		module = CompileUnit.Modules[0]

		method = [|
			override def Run(argv as (string)):
				pass
		|]

		method.Body = module.Globals
		module.Globals = Block()

		if module.Members["Main"] isa Method:
			method.Body = [|
				$(method.Body)
				Main(argv)
			|]

		script = ClassDefinition(Name: "__Script__")
		script.BaseTypes.Add(SimpleTypeReference("Boo.NAnt.AbstractScript"))
		script.Members.Add(method)
		script.Members.Extend(module.Members)

		module.Members.Clear()
		module.Members.Add(script)

def WithWorkingDir(dir as string, block as callable()):
	_saved = Environment.CurrentDirectory
	Environment.CurrentDirectory = dir
	try:
		block()
	ensure:
		Environment.CurrentDirectory = _saved

[TaskName("boo")]
class BooTask(AbstractBooTask):

	_src as FileInfo
	_code as RawXml
	_arguments = ArgumentCollection()

	[TaskAttribute("src", Required: false)]
	Source:
		get: return _src
		set: _src = value

	[BuildElement("code")]
	Code as RawXml:
		get: return _code
		set: _code = value

	[BuildElementArray("arg")]
	Arguments as ArgumentCollection:
		get: return _arguments

	override def ExecuteTask():

		compiler = BooCompiler()
		parameters = compiler.Parameters
		parameters.OutputType = CompilerOutputType.Library
		if _src:
			parameters.Input.Add(FileInput(_src.ToString()))
		else:
			parameters.Input.Add(StringInput("code", ReIndent(SourceCode())))
		parameters.References.Add(GetType().Assembly)
		parameters.References.Add(typeof(NAnt.Core.Project).Assembly)

		result = RunCompiler(compiler)

		print("script successfully compiled.")
		try:
			scriptType = result.GeneratedAssembly.GetType("__Script__", true)
			script as AbstractScript = scriptType()
			script.Project = Project
			script.Task = self
			WithWorkingDir(Project.BaseDirectory) do:
				script.Run([arg.ToString() for arg in Arguments].ToArray(string))
		except x:
			raise BuildException(x.Message, Location, x)

	override def GetDefaultPipeline():
		pipeline = CompileToMemory()
		pipeline.Insert(1, PrepareScriptStep())
		return pipeline

	private def SourceCode():
		if Code is not null:
			return Code.Xml.InnerText
		return XmlNode.InnerText

def ReIndent(code as string):
	lines = code.Replace("\r\n", "\n").Split(char('\n'))
	nonEmptyLines = line for line in lines if len(line.Trim())

	indentation = /(\s*)/.Match(nonEmptyLines.First()).Groups[0].Value
	return code if len(indentation) == 0

	buffer = System.Text.StringBuilder()
	for line in lines:
		if line.StartsWith(indentation):
			buffer.AppendLine(line[len(indentation):])
		else:
			buffer.AppendLine(line)
	return buffer.ToString()

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

namespace Boo.NAnt

import System
import System.IO
import NAnt.Core
import NAnt.Core.Attributes
import NAnt.Core.Types
import Boo.Lang.Compiler
import Boo.Lang.Compiler.IO
import Boo.Lang.Compiler.Ast
import Boo.Lang.Compiler.Pipelines
import Boo.Lang.Compiler.Steps

class AbstractScript:

	_project as Project
	_task as BooTask

	Project:
		get: return _project
		set: _project = value

	Task:
		get: return _task
		set: _task = value

	def print(msg):
		_task.LogInfo(msg)

	abstract def Run(argv as (string)):
		pass

class PrepareScriptStep(AbstractCompilerStep):

	override def Run():
		module = CompileUnit.Modules[0]

		method = [|
			override def Run(argv as (string)):
				pass
		|]

		method.Body = module.Globals
		module.Globals = Block()

		script = ClassDefinition(Name: "__Script__")
		script.BaseTypes.Add(SimpleTypeReference("Boo.NAnt.AbstractScript"))
		script.Members.Add(method)
		script.Members.Extend(module.Members)

		module.Members.Clear()
		module.Members.Add(script)

def WithWorkingDir(dir as string, block as callable()):
	_saved = Environment.CurrentDirectory
	Environment.CurrentDirectory = dir
	try:
		block()
	ensure:
		Environment.CurrentDirectory = _saved

[TaskName("boo")]
class BooTask(AbstractBooTask):

	_src as FileInfo
	_code as RawXml
	_arguments = ArgumentCollection()

	[TaskAttribute("src", Required: false)]
	Source:
		get: return _src
		set: _src = value

	[BuildElement("code")]
	Code as RawXml:
		get: return _code
		set: _code = value

	[BuildElementArray("arg")]
	Arguments as ArgumentCollection:
		get: return _arguments

	override def ExecuteTask():

		compiler = BooCompiler()
		parameters = compiler.Parameters
		parameters.OutputType = CompilerOutputType.Library
		if _src:
			parameters.Input.Add(FileInput(_src.ToString()))
		else:
			parameters.Input.Add(StringInput("code", ReIndent(SourceCode())))
		parameters.References.Add(GetType().Assembly)
		parameters.References.Add(typeof(NAnt.Core.Project).Assembly)

		result = RunCompiler(compiler)

		print("script successfully compiled.")
		try:
			scriptType = result.GeneratedAssembly.GetType("__Script__", true)
			script as AbstractScript = scriptType()
			script.Project = Project
			script.Task = self
			WithWorkingDir(Project.BaseDirectory) do:
				script.Run([arg.ToString() for arg in Arguments].ToArray(string))
		except x:
			raise BuildException(x.Message, Location, x)

	override def GetDefaultPipeline():
		pipeline = CompileToMemory()
		pipeline.Insert(1, PrepareScriptStep())
		return pipeline

	private def SourceCode():
		if Code is not null:
			return Code.Xml.InnerText
		return XmlNode.InnerText

def ReIndent(code as string):
	lines = NonEmptyLines(code)

	firstLine = lines[0]
	indentation = /(\s*)/.Match(firstLine).Groups[0].Value
	return code if len(indentation) == 0

	buffer = System.Text.StringBuilder()
	for line in lines:
		if not line.StartsWith(indentation):
			return code // let the parser complain about it
		buffer.AppendLine(line[len(indentation):])
	return buffer.ToString()

def NonEmptyLines(s as string):
	lines = s.Replace("\r\n", "\n").Split(char('\n'))
	return array(line for line in lines if len(line.Trim()))