Updated Build.boo to parse version numbers if they are 0 Increased version to 0.2.0

import System
import System.IO

# Project Information
title            = "Stitch"
company          = ""
version_major    = 0
version_minor    = 2
version_build    = 0
version_revision = 0
version          = "0.2.0.0"
repository_url   = ""
repository_type  = "git"

# Build Information
solution_folder  = "Source"
solution_file    = "Stitch.sln"
configuration    = "Release"
dotnet_version   = "4.0"
build_directory  = DirectoryInfo("Build/${configuration}")

# Targets
desc "Default target"
target default:
  try:
    call clean
    call binaries
    call assemblyInfo
    call compile
    call package
    call nuget
  except e:
    print e

target clean:
  rmdir("Build")

target binaries:
  with FileList(solution_folder):
    .Include("*")
    .ForEach def(file):
        packages_config = Path.Combine(file.FullName,"packages.config")
        if Directory.Exists(file.FullName) and File.Exists(packages_config):
            exec("Tools\\NuGet\\NuGet.exe install ${packages_config} -o Libraries")

target compile:
  print "Compiling ${solution_file}"
  with msbuild():
    .file = Path.Combine(solution_folder, solution_file)
    .configuration = configuration
    .version = dotnet_version
    .properties = { 'OutDir': build_directory.FullName+"/" }

target package:
  package_dir = "Build/Package"
  mkdir package_dir
  cp("Build/${configuration}/Stitch.Core.dll", package_dir + "/Stitch.Core.dll")
  cp("Build/${configuration}/Stitch.Web.dll", package_dir + "/Stitch.Web.dll")
  cp("Build/${configuration}/Stitch.exe", package_dir + "/Stitch.exe")
  zip(package_dir, String.Format("Build/{4}.{0}.{1}.{2}.{3}.zip", version_major, version_minor, version_build, version_revision, title))
  rmdir package_dir

target getRevision:
  int.TryParse(env("build.vcs.number"), version_revision)
  if (version_revision > 0):
    return

  if (repository_type == "git"):
    exec("git describe --tags --long", { 'Output': '_summary.txt' })

    file = StreamReader("_summary.txt")
    line = file.ReadLine()
    file.Close()
    file.Dispose()

    rm("_summary.txt")

    parts = line.Split(char('-'))

    print parts[1]

    revision = -1
    if (int.TryParse(parts[1].Trim(),revision)):
      version_revision = revision

  if (version_revision >= 0):
    return
  else:
    raise "Unable to determine repository revision"

target assemblyInfo, (getRevision):
  print "Generating SolutionVersion.cs"
  version = String.Format("{0}.{1}.{2}.{3}", version_major, version_minor, version_build, version_revision)
  with generate_assembly_info():
    .file = "${solution_folder}/SolutionVersion.cs"
    .version = version
    .fileVersion = version
    .title = title
    .description  = String.Format("{0} is a product of {1}", title, company)
    .copyright = String.Format("Copyright {0} {1}", DateTime.Now.Year, company)
    .comVisible = false
    .companyName = company
    .productName = title

def createNuget(name as string, nuspec as string):
  rm(name) if File.Exists(name)
  file = StreamWriter(name)
  file.Write(nuspec)
  file.Close()
  exec(".\\Tools\\NuGet\\NuGet.exe pack .\\${name} -OutputDirectory Build")
  rm(name)

target nuget, (assemblyInfo):
  createNuget("Stitch.nuspec", """<?xml version="1.0" encoding="utf-8"?>
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
    <metadata>
        <id>Stitch</id>
        <version>${version}</version>
        <authors>Nathan Palmer</authors>
        <description>Develop and test your JavaScript applications as CommonJS modules in Node.js. Then __Stitch__ them together to run in the browser. Port of Sam Stephenson's Stitch.</description>
        <language>en-GB</language>
        <projectUrl>https://github.com/nathanpalmer/stitch-aspnet</projectUrl>
        <licenseUrl>https://github.com/nathanpalmer/stitch-aspnet/blob/master/LICENSE.txt</licenseUrl>
    </metadata>
    <files>
        <file src="Build\Release\Stitch.Core.dll"
              target="lib" />
        <file src="Build\Release\Stitch.exe"
              target="bin" />
        <file src="LICENSE.txt"
              target="" />
    </files>
</package>
  """)

  createNuget("Stitch.AspNet.nuspec", """<?xml version="1.0" encoding="utf-8"?>
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
    <metadata>
        <id>Stitch.AspNet</id>
        <version>${version}</version>
        <authors>Nathan Palmer</authors>
        <description>Develop and test your JavaScript applications as CommonJS modules in Node.js. Then __Stitch__ them together to run in the browser. Port of Sam Stephenson's Stitch.</description>
        <language>en-GB</language>
        <projectUrl>https://github.com/nathanpalmer/stitch-aspnet</projectUrl>
        <licenseUrl>https://github.com/nathanpalmer/stitch-aspnet/blob/master/LICENSE.txt</licenseUrl>
        <dependencies>
          <dependency id="Stitch" version="${version_major}.${version_minor}" />
        </dependencies>
    </metadata>
    <files>
        <file src="Build\Release\Stitch.Web.dll"
              target="lib" />
        <file src="LICENSE.txt"
              target="" />
        <file src="Web.config.transform"
              target="content" />
    </files>
</package>
  """)

  createNuget("Stitch.Compilers.CoffeeScript.nuspec", """<?xml version="1.0" encoding="utf-8"?>
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
    <metadata>
        <id>Stitch.Compilers.CoffeeScript</id>
        <version>${version}</version>
        <authors>Nathan Palmer</authors>
        <description>CoffeeScript compiler for use with Stitch.</description>
        <language>en-GB</language>
        <projectUrl>https://github.com/nathanpalmer/stitch-aspnet</projectUrl>
        <licenseUrl>https://github.com/nathanpalmer/stitch-aspnet/blob/master/LICENSE.txt</licenseUrl>
        <dependencies>
            <dependency id="SassAndCoffee.Core" version="1.0" />
            <dependency id="Stitch" version="${version_major}.${version_minor}" />
        </dependencies>
    </metadata>
    <files>
        <file src="Build\Release\Stitch.Compilers.CoffeeScript.dll"
              target="lib" />
        <file src="LICENSE.txt"
              target="" />
    </files>
</package>
  """)

import System
import System.IO

# Project Information
title            = "Stitch"
company          = ""
version_major    = 0
version_minor    = 1
version_build    = 0
version_revision = 0
version          = "0.1.0.0"
repository_url   = ""
repository_type  = "git"

# Build Information
solution_folder  = "Source"
solution_file    = "Stitch.sln"
configuration    = "Release"
dotnet_version   = "4.0"
build_directory  = DirectoryInfo("Build/${configuration}")

# Targets
desc "Default target"
target default:
  try:
    call clean
    call binaries
    call assemblyInfo
    call compile
    call package
    call nuget
  except e:
    print e

target clean:
  rmdir("Build")

target binaries:
  with FileList(solution_folder):
    .Include("*")
    .ForEach def(file):
        packages_config = Path.Combine(file.FullName,"packages.config")
        if Directory.Exists(file.FullName) and File.Exists(packages_config):
            exec("Tools\\NuGet\\NuGet.exe install ${packages_config} -o Libraries")

target compile:
  print "Compiling ${solution_file}"
  with msbuild():
    .file = Path.Combine(solution_folder, solution_file)
    .configuration = configuration
    .version = dotnet_version
    .properties = { 'OutDir': build_directory.FullName+"/" }

target package:
  package_dir = "Build/Package"
  mkdir package_dir
  cp("Build/${configuration}/Stitch.Core.dll", package_dir + "/Stitch.Core.dll")
  cp("Build/${configuration}/Stitch.Web.dll", package_dir + "/Stitch.Web.dll")
  cp("Build/${configuration}/Stitch.exe", package_dir + "/Stitch.exe")
  zip(package_dir, String.Format("Build/{4}.{0}.{1}.{2}.{3}.zip", version_major, version_minor, version_build, version_revision, title))
  rmdir package_dir

target getRevision:
  int.TryParse(env("build.vcs.number"), version_revision)
  if (version_revision > 0):
    return

  if (repository_type == "git"):
    exec("git describe --tags --long", { 'Output': '_summary.txt' })

    file = StreamReader("_summary.txt")
    line = file.ReadLine()
    file.Close()
    file.Dispose()

    rm("_summary.txt")

    parts = line.Split(char('-'))

    revision = 0
    if (int.TryParse(parts[1].Trim(),revision)):
      version_revision = revision

  if (version_revision > 0):
    return
  else:
    raise "Unable to determine repository revision"

target assemblyInfo, (getRevision):
  print "Generating SolutionVersion.cs"
  version = String.Format("{0}.{1}.{2}.{3}", version_major, version_minor, version_build, version_revision)
  with generate_assembly_info():
    .file = "${solution_folder}/SolutionVersion.cs"
    .version = version
    .fileVersion = version
    .title = title
    .description  = String.Format("{0} is a product of {1}", title, company)
    .copyright = String.Format("Copyright {0} {1}", DateTime.Now.Year, company)
    .comVisible = false
    .companyName = company
    .productName = title

def createNuget(name as string, nuspec as string):
  rm(name) if File.Exists(name)
  file = StreamWriter(name)
  file.Write(nuspec)
  file.Close()
  exec(".\\Tools\\NuGet\\NuGet.exe pack .\\${name} -OutputDirectory Build")
  rm(name)

target nuget, (assemblyInfo):
  createNuget("Stitch.nuspec", """<?xml version="1.0" encoding="utf-8"?>
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
    <metadata>
        <id>Stitch</id>
        <version>${version}</version>
        <authors>Nathan Palmer</authors>
        <description>Develop and test your JavaScript applications as CommonJS modules in Node.js. Then __Stitch__ them together to run in the browser. Port of Sam Stephenson's Stitch.</description>
        <language>en-GB</language>
        <projectUrl>https://github.com/nathanpalmer/stitch-aspnet</projectUrl>
        <licenseUrl>https://github.com/nathanpalmer/stitch-aspnet/blob/master/LICENSE.txt</licenseUrl>
    </metadata>
    <files>
        <file src="Build\Release\Stitch.Core.dll"
              target="lib" />
        <file src="Build\Release\Stitch.exe"
              target="bin" />
        <file src="LICENSE.txt"
              target="" />
    </files>
</package>
  """)

  createNuget("Stitch.AspNet.nuspec", """<?xml version="1.0" encoding="utf-8"?>
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
    <metadata>
        <id>Stitch.AspNet</id>
        <version>${version}</version>
        <authors>Nathan Palmer</authors>
        <description>Develop and test your JavaScript applications as CommonJS modules in Node.js. Then __Stitch__ them together to run in the browser. Port of Sam Stephenson's Stitch.</description>
        <language>en-GB</language>
        <projectUrl>https://github.com/nathanpalmer/stitch-aspnet</projectUrl>
        <licenseUrl>https://github.com/nathanpalmer/stitch-aspnet/blob/master/LICENSE.txt</licenseUrl>
        <dependencies>
          <dependency id="Stitch" version="${version_major}.${version_minor}" />
        </dependencies>
    </metadata>
    <files>
        <file src="Build\Release\Stitch.Web.dll"
              target="lib" />
        <file src="LICENSE.txt"
              target="" />
        <file src="Web.config.transform"
              target="content" />
    </files>
</package>
  """)

  createNuget("Stitch.Compilers.CoffeeScript.nuspec", """<?xml version="1.0" encoding="utf-8"?>
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
    <metadata>
        <id>Stitch.Compilers.CoffeeScript</id>
        <version>${version}</version>
        <authors>Nathan Palmer</authors>
        <description>CoffeeScript compiler for use with Stitch.</description>
        <language>en-GB</language>
        <projectUrl>https://github.com/nathanpalmer/stitch-aspnet</projectUrl>
        <licenseUrl>https://github.com/nathanpalmer/stitch-aspnet/blob/master/LICENSE.txt</licenseUrl>
        <dependencies>
            <dependency id="SassAndCoffee.Core" version="1.0" />
            <dependency id="Stitch" version="${version_major}.${version_minor}" />
        </dependencies>
    </metadata>
    <files>
        <file src="Build\Release\Stitch.Compilers.CoffeeScript.dll"
              target="lib" />
        <file src="LICENSE.txt"
              target="" />
    </files>
</package>
  """)


better error messages for unsupported case patterns

namespace Boo.PatternMatching

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

class MatchError(System.Exception):

	def constructor(msg as string):
		super(msg)

class MatchMacro(AbstractAstMacro):
	override def Expand(node as MacroStatement):
		return MatchExpander(Context).expand(node)

class MatchExpander:

	matchValue as ReferenceExpression
	context as CompilerContext

	def constructor(context as CompilerContext):
		self.context = context

	def expand(node as MacroStatement):

		expression = node.Arguments[0]
		matchValue = newTemp(expression)

		topLevel = expanded = expandCase(caseList(node)[0])
		for case in caseList(node)[1:]:
			expansion = expandCase(case)
			continue if expansion is null
			expanded.FalseBlock = expansion.ToBlock()
			expanded = expansion

		return null if topLevel is null

		matchError = [| raise MatchError("'" + $(expression.ToCodeString()) + "' failed to match '" + $matchValue + "'") |]
		matchError.LexicalInfo = node.LexicalInfo

		expanded.FalseBlock = matchError.ToBlock()
		return [|
			block:
				$matchValue = $expression
				$topLevel
		|].Block

	def expandCase(node as MacroStatement):
		assert 1 == len(node.Arguments)
		pattern = node.Arguments[0]
		mie = pattern as MethodInvocationExpression
		if mie is not null:
			return expandObjectPattern(mie, node.Block)
		reference = pattern as ReferenceExpression
		if reference is not null:
			return expandIrrefutablePattern(reference, node.Block)
		context.Errors.Add(CompilerError(pattern.LexicalInfo, "Unsupported pattern: '${pattern}'"))

	def expandObjectPattern(node as MethodInvocationExpression, block as Block):
		condition = expandObjectPattern(matchValue, node)
		return [|
			if $condition:
				$block
		|]

	def expandObjectPattern(matchValue as Expression, node as MethodInvocationExpression) as Expression:

		typeName = cast(ReferenceExpression, node.Target).Name

		if len(node.NamedArguments) == 0 and len(node.Arguments) == 0:
			return [| $matchValue isa $(typeName) |]

		temp = newTemp(node)

		condition = [| ($temp = $matchValue as $(typeName)) is not null |]

		for member in node.Arguments:
			assert member isa ReferenceExpression, member.ToCodeString()
			memberRef = MemberReferenceExpression(member.LexicalInfo, temp.CloneNode(), member.ToString())
			condition = [| $condition and __eval__($member = $memberRef, true) |]

		for member in node.NamedArguments:
			memberRef = MemberReferenceExpression(member.First.LexicalInfo, temp.CloneNode(), member.First.ToString())
			variable = member.Second as ReferenceExpression
			if variable is not null and variable.NodeType == NodeType.ReferenceExpression:
				condition = [| $condition and __eval__($variable = $memberRef, true) |]
				continue

			nestedPattern = member.Second as MethodInvocationExpression
			if nestedPattern is not null:
				nestedCondition = expandObjectPattern(memberRef, nestedPattern)
				condition = [| $condition and $nestedCondition |]
				continue

			condition = [| $condition and ($memberRef == $(member.Second)) |]
		return condition

	def expandIrrefutablePattern(node as ReferenceExpression, block as Block):
		return [|
			if true:
				$node = $matchValue
				$block
		|]

	def newTemp(e as Expression):
		return ReferenceExpression(
				LexicalInfo: e.LexicalInfo,
				Name: "$match$${context.AllocIndex()}")

class CaseMacro(AbstractAstMacro):
	override def Expand(node as MacroStatement):

		match = node.ParentNode.ParentNode as MacroStatement
		assert match.Name == "match"

		caseList(match).Add(node)

		return null

def caseList(node as MacroStatement) as List:
	list as List = node["caseList"]
	if list is null:
		node["caseList"] = list = []
	return list

namespace Boo.PatternMatching

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

class MatchError(System.Exception):

	def constructor(msg as string):
		super(msg)

class MatchMacro(AbstractAstMacro):
	override def Expand(node as MacroStatement):
		return MatchExpander(Context).expand(node)

class MatchExpander:

	matchValue as ReferenceExpression
	context as CompilerContext

	def constructor(context as CompilerContext):
		self.context = context

	def expand(node as MacroStatement):

		expression = node.Arguments[0]
		matchValue = newTemp(expression)

		topLevel = expanded = expandCase(caseList(node)[0])
		for case in caseList(node)[1:]:
			expansion = expandCase(case)
			expanded.FalseBlock = expansion.ToBlock()
			expanded = expansion

		matchError = [| raise MatchError("'" + $(expression.ToCodeString()) + "' failed to match '" + $matchValue + "'") |]
		matchError.LexicalInfo = node.LexicalInfo

		expanded.FalseBlock = matchError.ToBlock()
		return [|
			block:
				$matchValue = $expression
				$topLevel
		|].Block

	def expandCase(node as MacroStatement):
		assert 1 == len(node.Arguments)
		pattern = node.Arguments[0]
		mie = pattern as MethodInvocationExpression
		if mie is not null:
			return expandObjectPattern(mie, node.Block)
		reference = pattern as ReferenceExpression
		if reference is not null:
			return expandIrrefutablePattern(reference, node.Block)
		raise CompilerError(pattern.LexicalInfo, "Unsupported pattern: '${pattern}'")

	def expandObjectPattern(node as MethodInvocationExpression, block as Block):
		condition = expandObjectPattern(matchValue, node)
		return [|
			if $condition:
				$block
		|]

	def expandObjectPattern(matchValue as Expression, node as MethodInvocationExpression) as Expression:

		typeName = cast(ReferenceExpression, node.Target).Name

		if len(node.NamedArguments) == 0 and len(node.Arguments) == 0:
			return [| $matchValue isa $(typeName) |]

		temp = newTemp(node)

		condition = [| ($temp = $matchValue as $(typeName)) is not null |]

		for member in node.Arguments:
			assert member isa ReferenceExpression, member.ToCodeString()
			memberRef = MemberReferenceExpression(member.LexicalInfo, temp.CloneNode(), member.ToString())
			condition = [| $condition and __eval__($member = $memberRef, true) |]

		for member in node.NamedArguments:
			memberRef = MemberReferenceExpression(member.First.LexicalInfo, temp.CloneNode(), member.First.ToString())
			variable = member.Second as ReferenceExpression
			if variable is not null and variable.NodeType == NodeType.ReferenceExpression:
				condition = [| $condition and __eval__($variable = $memberRef, true) |]
				continue

			nestedPattern = member.Second as MethodInvocationExpression
			if nestedPattern is not null:
				nestedCondition = expandObjectPattern(memberRef, nestedPattern)
				condition = [| $condition and $nestedCondition |]
				continue

			condition = [| $condition and ($memberRef == $(member.Second)) |]
		return condition

	def expandIrrefutablePattern(node as ReferenceExpression, block as Block):
		return [|
			if true:
				$node = $matchValue
				$block
		|]

	def newTemp(e as Expression):
		return ReferenceExpression(
				LexicalInfo: e.LexicalInfo,
				Name: "$match$${context.AllocIndex()}")

class CaseMacro(AbstractAstMacro):
	override def Expand(node as MacroStatement):

		match = node.ParentNode.ParentNode as MacroStatement
		assert match.Name == "match"

		caseList(match).Add(node)

		return null

def caseList(node as MacroStatement) as List:
	list as List = node["caseList"]
	if list is null:
		node["caseList"] = list = []
	return list


always keep the insert point visible

always keep the insert point visible

git-svn-id: c3b8989232654601f1acea7403f771adaaa44a81@1442 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 Gtk from "gtk-sharp"
import Gdk from "gdk-sharp" as Gdk
import Pango from "pango-sharp" as Pango
import Boo.Lang.Interpreter from Boo.Lang.Interpreter

class PromptView(TextView):

	_interpreter = InteractiveInterpreter(RememberLastValue: true, Print: print)

	def constructor():

		self.WrapMode = Gtk.WrapMode.Word

		//if Environment.OSVersion.Platform in PlatformID.Win32NT, PlatformID.Win32Windows:
		self.ModifyFont(Pango.FontDescription(Family: "Lucida Console"))

		_interpreter.References.Add(typeof(TextView).Assembly)
		_interpreter.References.Add(typeof(Gdk.Key).Assembly)

		_interpreter.SetValue("cls", { Buffer.Text = "" })
		_interpreter.SetValue("view", self)

		prompt()

	override def OnKeyPressEvent(ev as Gdk.EventKey):
		if Gdk.Key.Return == ev.Key:
			try:
				EvalCurrentLine()
			except x:
				print(x)
			prompt()
			return true
		elif ev.Key in Gdk.Key.BackSpace, Gdk.Key.Left:
			if Buffer.GetIterAtMark(Buffer.InsertMark).LineOffset < 5:
				return true

		return super(ev)

	def print(obj):
		Buffer.InsertAtCursor("${obj}\n")

	def prompt():
		Buffer.MoveMark(Buffer.InsertMark, Buffer.EndIter)
		Buffer.InsertAtCursor(">>> ")
		ScrollMarkOnscreen(Buffer.InsertMark)

	def EvalCurrentLine():
		start = Buffer.GetIterAtLine(Buffer.LineCount)
		line = Buffer.GetText(start, Buffer.EndIter, false)

		print("")
		_interpreter.LoopEval(line[4:])

class MainWindow(Window):

	def constructor():
		super("booish")

		window = ScrolledWindow()
		window.Add(PromptView())

		self.Add(window)

		self.DeleteEvent += Application.Quit

Application.Init()

window = MainWindow(DefaultWidth:  400,
				DefaultHeight: 250)

window.ShowAll()

Application.Run()

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

import System
import Gtk from "gtk-sharp"
import Gdk from "gdk-sharp" as Gdk
import Pango from "pango-sharp" as Pango
import Boo.Lang.Interpreter from Boo.Lang.Interpreter

class PromptView(TextView):

	_interpreter = InteractiveInterpreter(RememberLastValue: true, Print: print)

	def constructor():

		self.WrapMode = Gtk.WrapMode.Word

		if Environment.OSVersion.Platform in PlatformID.Win32NT, PlatformID.Win32Windows:
			self.ModifyFont(
				Pango.FontDescription(Family: "Lucida Console", Size: 12))
		else:
			self.ModifyFont(
				Pango.FontDescription(Family: "Courier New"))

		_interpreter.References.Add(typeof(TextView).Assembly)
		_interpreter.References.Add(typeof(Gdk.Key).Assembly)

		_interpreter.SetValue("cls", { Buffer.Text = "" })

		prompt()

	override def OnKeyPressEvent(ev as Gdk.EventKey):
		if Gdk.Key.Return == ev.Key:
			try:
				EvalCurrentLine()
			except x:
				print(x)
			prompt()
			return true
		elif ev.Key in Gdk.Key.BackSpace, Gdk.Key.Left:
			if Buffer.GetIterAtMark(Buffer.InsertMark).LineOffset < 5:
				return true

		return super(ev)

	def print(obj):
		Buffer.InsertAtCursor("${obj}\n")

	def prompt():
		Buffer.MoveMark(Buffer.InsertMark, Buffer.EndIter)
		Buffer.InsertAtCursor(">>> ")

	def EvalCurrentLine():
		start = Buffer.GetIterAtLine(Buffer.LineCount)
		line = Buffer.GetText(start, Buffer.EndIter, false)

		print("")
		_interpreter.LoopEval(line[4:])

class MainWindow(Window):

	def constructor():
		super("booish")

		window = ScrolledWindow()
		window.Add(PromptView())

		self.Add(window)

		self.DeleteEvent += Application.Quit

Application.Init()

window = MainWindow(DefaultWidth:  400,
				DefaultHeight: 250)

window.ShowAll()

Application.Run()


fix the test case to make sure it also runs on .net

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

"""
False
True
"""

import System
import System.Collections
import System.Collections.Generic

c1 = Comparer.Default
c2 = Comparer[of string].Default

print c1.GetType().IsGenericType
print c2.GetType().IsGenericType

"""
System.Collections.Comparer
System.Collections.Generic.IComparableOfTComparer`1[System.String]
"""

import System
import System.Collections
import System.Collections.Generic

c1 = Comparer.Default
c2 = Comparer[of string].Default

print c1.GetType()
print c2.GetType()


Fixing dedent bug occurring when there is no newline after the last statement. Dedents is not generated in this case.

namespace Boo.OMeta.Parser

import System.Text
import Boo.OMeta
import Boo.Lang.PatternMatching
import Boo.Adt

data Token(kind as string, value as string)

ometa WhitespaceSensitiveTokenizer(stack = [0]):

/*
	Before the first line of the file is read, a single zero
	is pushed on the stack; this will never be popped off again.
	The numbers pushed on the stack will always be strictly
	increasing from bottom to top. At the beginning of each
	logical line, the line's indentation level is compared
	to the top of the stack.

	If it is equal, nothing happens. If it is larger,
	it is pushed on the stack, and one INDENT token is generated.
	If it is smaller, it must be one of the numbers occurring
	on the stack; all numbers on the stack that are larger
	are popped off, and for each number popped off
	a DEDENT token is generated.

	At the end of the file, a DEDENT token is generated
	for each number remaining on the stack that is
	larger than zero.

	http://docs.python.org/ref/indentation.html
*/

	scanner = (
		(
			  (((_ >> t) and (t isa Token)) ^ t) // token introduced by processDedent
			| (((indentation >> i) and sameIndent(i)) ^ makeToken("eol"))
			| (((indentation >> i) and largerIndent(i)) ^ makeToken("indent"))
			| (((indentation >> i) and smallerIndent(i), $(processDedent(input, i)) >> value) ^ value)
			| ((--space, tokens >> t) ^ t)
		) >> value
	) ^ value

	indentation = ((empty_lines, spaces, ~_) ^ []) | (empty_lines, spaces)
	empty_lines = ~~empty_line, ++empty_line
	empty_line = spaces, newline
	spaces = --space >> value ^ value
	space = ' ' | '\t' | (newline and inWSA(input))
	newline = '\n' | "\r\n" | "\r"
	token[expected] = (scanner >> t and tokenMatches(t, expected)) ^ t

	wsa = ~~_ and inWSA(input)

	tokens = ++(~newline, _) >> t ^ makeToken("line", t)

	enterWhitespaceAgnosticRegion = $(enterWSA(input))

	leaveWhitespaceAgnosticRegion = $(leaveWSA(input))

	INDENT = token["indent"]
	DEDENT = token["dedent"] | ~_
	EOL = token["eol"]

	def inWSA(input as OMetaInput):
		return wsaLevel(input) > 0

	def wsaLevel(input as OMetaInput) as int:
		return input.GetMemo("wsaLevel") or 0

	def wsaLevel(input as OMetaInput, value as int):
		return success(input.SetMemo("wsaLevel", value))

	def enterWSA(input as OMetaInput):
		return wsaLevel(input, wsaLevel(input) + 1)

	def leaveWSA(input as OMetaInput):
		return wsaLevel(input, wsaLevel(input) - 1)

	def success(input as OMetaInput):
		return SuccessfulMatch(input, null)

	def sameIndent(i):
		return currentIndent() == len(i)

	def largerIndent(i):
		if len(i) > currentIndent():
			stack.Push(len(i))
			return true

	def processDedent(input as OMetaInput, i):
		while smallerIndent(i):
			input = OMetaInput.Prepend(makeToken("dedent"), input)
			stack.Pop()
		assert sameIndent(i)
		return SuccessfulMatch(input, makeToken("eol"))

	def smallerIndent(i):
		return len(i) < currentIndent()

	def currentIndent() as int:
		return stack[-1]

def tokenMatches(token as Token, expected):
	return expected is token.kind

def tokenValue(token as Token):
	return null if token is null
	return token.value

def makeToken(kind):
	return Token(kind, kind)

def makeToken(kind, value):
	return Token(kind, flatString(value))

def makeString(*values):
	buffer = StringBuilder()
	for value in values:
		flatString buffer, value
	return buffer.ToString()

def flatString(value) as string:
	if value isa string: return value
	buffer = StringBuilder()
	flatString buffer, value
	return buffer.ToString()

def flatString(buffer as StringBuilder, value):
	match value:
		case null:
			return
		case string():
			buffer.Append(value)
		case char():
			buffer.Append(value)
		otherwise:
			for item in value:
				flatString buffer, item

namespace Boo.OMeta.Parser

import System.Text
import Boo.OMeta
import Boo.Lang.PatternMatching
import Boo.Adt

data Token(kind as string, value as string)

ometa WhitespaceSensitiveTokenizer(stack = [0]):

/*
	Before the first line of the file is read, a single zero
	is pushed on the stack; this will never be popped off again.
	The numbers pushed on the stack will always be strictly
	increasing from bottom to top. At the beginning of each
	logical line, the line's indentation level is compared
	to the top of the stack.

	If it is equal, nothing happens. If it is larger,
	it is pushed on the stack, and one INDENT token is generated.
	If it is smaller, it must be one of the numbers occurring
	on the stack; all numbers on the stack that are larger
	are popped off, and for each number popped off
	a DEDENT token is generated.

	At the end of the file, a DEDENT token is generated
	for each number remaining on the stack that is
	larger than zero.

	http://docs.python.org/ref/indentation.html
*/

	scanner = (
		(
			  (((_ >> t) and (t isa Token)) ^ t) // token introduced by processDedent
			| (((indentation >> i) and sameIndent(i)) ^ makeToken("eol"))
			| (((indentation >> i) and largerIndent(i)) ^ makeToken("indent"))
			| (((indentation >> i) and smallerIndent(i), $(processDedent(input, i)) >> value) ^ value)
			| ((--space, tokens >> t) ^ t)
		) >> value
	) ^ value

	indentation = ((empty_lines, spaces, ~_) ^ []) | (empty_lines, spaces)
	empty_lines = ~~empty_line, ++empty_line
	empty_line = spaces, newline
	spaces = --space >> value ^ value
	space = ' ' | '\t' | (newline and inWSA(input))
	newline = '\n' | "\r\n" | "\r"
	token[expected] = (scanner >> t and tokenMatches(t, expected)) ^ t

	wsa = ~~_ and inWSA(input)

	tokens = ++(~newline, _) >> t ^ makeToken("line", t)

	enterWhitespaceAgnosticRegion = $(enterWSA(input))

	leaveWhitespaceAgnosticRegion = $(leaveWSA(input))

	INDENT = token["indent"]
	DEDENT = token["dedent"]
	EOL = token["eol"]

	def inWSA(input as OMetaInput):
		return wsaLevel(input) > 0

	def wsaLevel(input as OMetaInput) as int:
		return input.GetMemo("wsaLevel") or 0

	def wsaLevel(input as OMetaInput, value as int):
		return success(input.SetMemo("wsaLevel", value))

	def enterWSA(input as OMetaInput):
		return wsaLevel(input, wsaLevel(input) + 1)

	def leaveWSA(input as OMetaInput):
		return wsaLevel(input, wsaLevel(input) - 1)

	def success(input as OMetaInput):
		return SuccessfulMatch(input, null)

	def sameIndent(i):
		return currentIndent() == len(i)

	def largerIndent(i):
		if len(i) > currentIndent():
			stack.Push(len(i))
			return true

	def processDedent(input as OMetaInput, i):
		while smallerIndent(i):
			input = OMetaInput.Prepend(makeToken("dedent"), input)
			stack.Pop()
		assert sameIndent(i)
		return SuccessfulMatch(input, makeToken("eol"))

	def smallerIndent(i):
		return len(i) < currentIndent()

	def currentIndent() as int:
		return stack[-1]

def tokenMatches(token as Token, expected):
	return expected is token.kind

def tokenValue(token as Token):
	return null if token is null
	return token.value

def makeToken(kind):
	return Token(kind, kind)

def makeToken(kind, value):
	return Token(kind, flatString(value))

def makeString(*values):
	buffer = StringBuilder()
	for value in values:
		flatString buffer, value
	return buffer.ToString()

def flatString(value) as string:
	if value isa string: return value
	buffer = StringBuilder()
	flatString buffer, value
	return buffer.ToString()

def flatString(buffer as StringBuilder, value):
	match value:
		case null:
			return
		case string():
			buffer.Append(value)
		case char():
			buffer.Append(value)
		otherwise:
			for item in value:
				flatString buffer, item


Updating build script again...

Updating build script again...

configuration = "release"
test_assembly = "src/FluentLinqToSql.Tests/bin/${configuration}/FluentLinqToSql.Tests.dll"
dbtest_assembly = "src/FluentLinqToSql.DatabaseTests/bin/${configuration}/FluentLinqToSql.DatabaseTests.dll"
dbtest_enabled = true

target default, (init, compile, test, createDb, dbTest, deploy, package):
  pass

target ci, (init, compile, createDb, coverage, deploy, package):
  pass

target init:
  rmdir("build")

target noDatabase:
  dbtest_enabled = false

target compile:
  msbuild(file: "FluentLinqToSql.sln", configuration: "release")

target test:
  nunit(assembly: test_assembly)

target createDb:
  if not dbtest_enabled:
    print "Skipping database creation"
    return
  print "Trying to create test db"
  nunit(assembly: dbtest_assembly, include: "CreateDatabase")
  print "Test database created"

target dbTest:
  if not dbtest_enabled:
    print "Skipping database tests"
    return
  nunit(assembly: dbtest_assembly)

target deploy:
  with FileList():
    .Include("src/FluentLinqToSql/bin/${configuration}/*.{dll,pdb,xml}")
    .Include("readme.txt")
    .Include("License.txt")
    .Flatten(true)
    .ForEach def(file):
      file.CopyToDirectory("build/${configuration}")

target package:
  zip("build/${configuration}", "build/FluentLinqToSql.zip")

target coverage:
  ncover_path = "c:/program files (x86)/ncover"
  app_assemblies = ("FluentLinqToSql",)
  teamcity_launcher = env("teamcity.dotnet.nunitlauncher")

  #ensure the dbtest assembly is in the right place...
  cp(dbtest_assembly, "src/FluentLinqToSql.Tests/bin/${configuration}/FluentLinqToSql.DatabaseTests.dll")

  with ncover():
    .toolPath = "${ncover_path}/NCover.console.exe"
    .reportDirectory = "build/Coverage"
    .workingDirectory = "src/FluentLinqToSql.Tests/bin/${project_configuration}"
    .applicationAssemblies = app_assemblies
    .program = "${teamcity_launcher} v2.0 x86 NUnit-2.4.6"
    .testAssembly = "FluentLinqToSql.Tests.dll;FluentLinqToSql.DatabaseTests.dll"
    .excludeAttributes = "System.Runtime.CompilerServices.CompilerGeneratedAttribute"

  with ncover_explorer():
    .toolPath = "${ncover_path}/NCoverExplorer.console.exe"
    .project = "FluentLinqToSql"
    .reportDirectory = "build/coverage"

configuration = "release"
test_assembly = "src/FluentLinqToSql.Tests/bin/${configuration}/FluentLinqToSql.Tests.dll"
dbtest_assembly = "src/FluentLinqToSql.DatabaseTests/bin/${configuration}/FluentLinqToSql.DatabaseTests.dll"
dbtest_enabled = true

target default, (init, compile, test, createDb, dbTest, deploy, package):
  pass

target ci, (init, compile, createDb, coverage, deploy, package):
  pass

target init:
  rmdir("build")

target noDatabase:
  dbtest_enabled = false

target compile:
  msbuild(file: "FluentLinqToSql.sln", configuration: "release")

target test:
  nunit(assembly: test_assembly)

target createDb:
  if not dbtest_enabled:
    print "Skipping database creation"
    return
  print "Trying to create test db"
  nunit(assembly: dbtest_assembly, include: "CreateDatabase")
  print "Test database created"

target dbTest:
  if not dbtest_enabled:
    print "Skipping database tests"
    return
  nunit(assembly: dbtest_assembly)

target deploy:
  with FileList():
    .Include("src/FluentLinqToSql/bin/${configuration}/*.{dll,pdb,xml}")
    .Include("readme.txt")
    .Include("License.txt")
    .Flatten(true)
    .ForEach def(file):
      file.CopyToDirectory("build/${configuration}")

target package:
  zip("build/${configuration}", "build/FluentLinqToSql.zip")

target coverage:
  ncover_path = "c:/program files (x86)/ncover"
  app_assemblies = ("FluentLinqToSql",)
  teamcity_launcher = env("teamcity.dotnet.nunitlauncher")

  #ensure the dbtest assembly is in the right place...
  cp(dbtest_assembly, "src/FluentLinqToSql.Tests/bin/${configuration}/")

  with ncover():
    .toolPath = "${ncover_path}/NCover.console.exe"
    .reportDirectory = "build/Coverage"
    .workingDirectory = "src/FluentLinqToSql.Tests/bin/${configuration}"
    .applicationAssemblies = app_assemblies
    .program = "${teamcity_launcher} v2.0 x86 NUnit-2.4.6"
    .testAssembly = "FluentLinqToSql.Tests.dll;FluentLinqToSql.DatabaseTests.dll"
    .excludeAttributes = "System.Runtime.CompilerServices.CompilerGeneratedAttribute"

  with ncover_explorer():
    .toolPath = "${ncover_path}/NCoverExplorer.console.exe"
    .project = "FluentLinqToSql"
    .reportDirectory = "build/coverage"


use 'Lucida Console' on windows

use 'Lucida Console' on windows

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

import System
import Gtk from "gtk-sharp"
import Gdk from "gdk-sharp" as Gdk
import Pango from "pango-sharp" as Pango
import booish from booish

class PromptView(TextView):

	_interpreter = InteractiveInterpreter(RememberLastValue: true)

	def constructor():

		if Environment.OSVersion.Platform in PlatformID.Win32NT, PlatformID.Win32Windows:
			self.ModifyFont(
				Pango.FontDescription(Family: "Lucida Console", Size: 12))
		else:
			self.ModifyFont(
				Pango.FontDescription(Family: "Courier New"))

		_interpreter.References.Add(typeof(TextView).Assembly)
		_interpreter.References.Add(typeof(Gdk.Key).Assembly)
		_interpreter.SetValue("print", print)
		_interpreter.SetValue("dir", dir)
		_interpreter.SetValue("help", help)
		_interpreter.SetValue("cls", { Buffer.Text = "" })

		prompt()

	override def OnKeyPressEvent(ev as Gdk.EventKey):
		if Gdk.Key.Return == ev.Key:
			try:
				EvalCurrentLine()
			except x:
				print(x)
			prompt()
			return true
		elif ev.Key in Gdk.Key.BackSpace, Gdk.Key.Left:
			if Buffer.GetIterAtMark(Buffer.InsertMark).LineOffset < 5:
				return true

		return super(ev)

	def print(obj):
		Buffer.InsertAtCursor("${obj}\n")

	def prompt():
		Buffer.MoveMark(Buffer.InsertMark, Buffer.EndIter)
		Buffer.InsertAtCursor(">>> ")

	def dir([required] obj):
		type = (obj as Type) or obj.GetType()
		for member in type.GetMembers():
			method = member as System.Reflection.MethodInfo
			if method is not null:
				yield method if method.IsPublic and not method.IsSpecialName
			else:
				yield member

	def help(obj):
		print(join(dir(obj), "\n"))

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

	def EvalCurrentLine():
		start = Buffer.GetIterAtLine(Buffer.LineCount)
		line = Buffer.GetText(start, Buffer.EndIter, false)

		print("")
		result = _interpreter.Eval(line[4:])
		if len(result.Errors):

			for error in result.Errors:
				pos = error.LexicalInfo.StartColumn
				print("---" + "-"*pos + "^") if pos > 0
				print("ERROR: ${error.Message}")
		else:
			_ = _interpreter.LastValue
			if _ is not null:
				print(repr(_))
				_interpreter.SetValue("_", _)

class MainWindow(Window):

	def constructor():
		super("booish")

		window = ScrolledWindow()
		window.Add(PromptView())

		self.Add(window)

		self.DeleteEvent += Application.Quit

Application.Init()

window = MainWindow(DefaultWidth:  400,
				DefaultHeight: 250)

window.ShowAll()

Application.Run()

import System
import Gtk from "gtk-sharp"
import Gdk from "gdk-sharp" as Gdk
import Pango from "pango-sharp" as Pango
import booish from booish

class PromptView(TextView):

	_interpreter = InteractiveInterpreter(RememberLastValue: true)

	def constructor():

		self.ModifyFont(
			Pango.FontDescription(Family: "Courier New"))

		_interpreter.References.Add(typeof(TextView).Assembly)
		_interpreter.References.Add(typeof(Gdk.Key).Assembly)
		_interpreter.SetValue("print", print)
		_interpreter.SetValue("dir", dir)
		_interpreter.SetValue("help", help)
		_interpreter.SetValue("cls", { Buffer.Text = "" })

		prompt()

	override def OnKeyPressEvent(ev as Gdk.EventKey):
		if Gdk.Key.Return == ev.Key:
			try:
				EvalCurrentLine()
			except x:
				print(x)
			prompt()
			return true
		elif ev.Key in Gdk.Key.BackSpace, Gdk.Key.Left:
			if Buffer.GetIterAtMark(Buffer.InsertMark).LineOffset < 5:
				return true

		return super(ev)

	def print(obj):
		Buffer.InsertAtCursor("${obj}\n")

	def prompt():
		Buffer.MoveMark(Buffer.InsertMark, Buffer.EndIter)
		Buffer.InsertAtCursor(">>> ")

	def dir([required] obj):
		type = (obj as Type) or obj.GetType()
		for member in type.GetMembers():
			method = member as System.Reflection.MethodInfo
			if method is not null:
				yield method if method.IsPublic and not method.IsSpecialName
			else:
				yield member

	def help(obj):
		print(join(dir(obj), "\n"))

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

	def EvalCurrentLine():
		start = Buffer.GetIterAtLine(Buffer.LineCount)
		line = Buffer.GetText(start, Buffer.EndIter, false)

		print("")
		result = _interpreter.Eval(line[4:])
		if len(result.Errors):

			for error in result.Errors:
				pos = error.LexicalInfo.StartColumn
				print("---" + "-"*pos + "^") if pos > 0
				print("ERROR: ${error.Message}")
		else:
			_ = _interpreter.LastValue
			if _ is not null:
				print(repr(_))
				_interpreter.SetValue("_", _)

class MainWindow(Window):

	def constructor():
		super("booish")

		window = ScrolledWindow()
		window.Add(PromptView())

		self.Add(window)

		self.DeleteEvent += Application.Quit

Application.Init()

window = MainWindow(DefaultWidth:  400,
				DefaultHeight: 250)

window.ShowAll()

Application.Run()


always reference UnityScript.Lang

always reference UnityScript.Lang

namespace UnityScript

import Boo.Lang.Compiler

class UnityScriptCompilerParameters(CompilerParameters):

	[property(ScriptBaseType)]
	_baseType as System.Type

	[property(ScriptMainMethod)]
	_mainMethod as string

	[property(Imports)]
	_imports = []

	[property(Expando)]
	_expando = false

	[property(GlobalVariablesBecomeFields)]
	_globalVariablesBecomeFields = true

	def constructor():
		self.Ducky = true
		self.OutputType = CompilerOutputType.Library
		self.References.Add(typeof(UnityScript.Lang.Array).Assembly)
		self.References.Add(GetType().Assembly)

namespace UnityScript

import Boo.Lang.Compiler

class UnityScriptCompilerParameters(CompilerParameters):

	[property(ScriptBaseType)]
	_baseType as System.Type

	[property(ScriptMainMethod)]
	_mainMethod as string

	[property(Imports)]
	_imports = []

	[property(Expando)]
	_expando = false

	[property(GlobalVariablesBecomeFields)]
	_globalVariablesBecomeFields = true

	def constructor():
		self.Ducky = true
		self.OutputType = CompilerOutputType.Library
		self.References.Add(GetType().Assembly)


globals() for booish

globals() for booish

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

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

namespace booish

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

class InteractiveInterpreter:

	_compiler = BooCompiler()

	_parser = BooCompiler()

	_values = {}

	_declarations = {}

	_imports = ImportCollection()

	_referenceProcessor = ProcessInterpreterReferences(self)

	_representers = []

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

	[property(RememberLastValue)]
	_rememberLastValue = false

	_inputId = 0

	def constructor():

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

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

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

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

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

		InitializeStandardReferences()

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

	Pipeline:
		get:
			return _compiler.Parameters.Pipeline

	References:
		get:
			return _compiler.Parameters.References

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

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

	def Eval(code as string):

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

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

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

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

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

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

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

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

		RecordImports(savedImports)

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

		InitializeModuleInterpreter(asm, module)

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

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

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

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

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

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

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

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

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

	def globals():
		return array(
					key for key as string in _values.Keys
					unless key.StartsWith("@"))

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

	def load([required] path as string):
		References.Add(System.Reflection.Assembly.LoadFrom(path))

	def help(obj):
		type = (obj as Type) or obj.GetType()
		describeType("    ", type)

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

		baseTypes = (getBooTypeName(type.BaseType),) + array(getBooTypeName(t) for t in type.GetInterfaces())
		_print("class ${type.Name}(${join(baseTypes, ', ')}):")
		_print("")

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

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

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

		for p as Reflection.PropertyInfo in List(type.GetProperties()).Sort(sortByName):
			modifiers = describeModifiers(p)
			params = describePropertyParameters(p.GetIndexParameters())
			_print("${indent}${modifiers}${p.Name}${params} as ${getBooTypeName(p.PropertyType)}:")
			_print("${indent}${indent}get") if p.GetGetMethod() is not null
			_print("${indent}${indent}set") if p.GetSetMethod() is not null
			_print("")

		for m as Reflection.MethodInfo in List(type.GetMethods()).Sort(sortByName):
			continue if m.IsSpecialName
			modifiers = describeModifiers(m)
			returnType = getBooTypeName(m.ReturnType)
			_print("${indent}${modifiers}def ${m.Name}(${describeParameters(m.GetParameters())}) as ${returnType}")
			_print("")

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

		AddRepresenter(IDictionary) do (value as IDictionary, writer as TextWriter):
			writer.Write("{")
			i = 0
			for item as DictionaryEntry in value:
				writer.Write(", ") if i
				repr(item.Key, writer)
				writer.Write(": ")
				repr(item.Value, writer)
				++i
			writer.Write("}")

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

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

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

	callable Representer(value, writer as TextWriter)

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

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

	class InterpreterEntity(ITypedEntity):

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

		[getter(Type)]
		_type as IType

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

		EntityType:
			get:
				return TypeSystem.EntityType.Custom

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

	class InterpreterNamespace(INamespace):

		[getter(ParentNamespace)]
		_parent as INamespace

		_tss as TypeSystemServices

		_interpreter as InteractiveInterpreter

		_declarations = {}

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

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

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

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

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

			return false

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

	class InterpreterTypeSystemServices(TypeSystemServices):

		[getter(CachedCallableTypes)]
		_cachedCallableTypes as List

		[getter(GeneratedCallableTypes)]
		_generatedCallableTypes = []

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

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

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

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

	class InitializeTypeSystemServices(Steps.AbstractCompilerStep):

		_cachedCallableTypes = []

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

	class CacheCallableTypes(Steps.AbstractCompilerStep):

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

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

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

	class ProcessVariableDeclarations(Steps.ProcessMethodBodiesWithDuckTyping):

		_namespace as InterpreterNamespace
		_interpreter as InteractiveInterpreter
		_entryPoint as Method

		def constructor(interpreter):
			_interpreter = interpreter

		InEntryPoint:
			get:
				return _currentMethod.Method is _entryPoint

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

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

		override def Dispose():
			_namespace = null

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

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

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

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

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

			return _namespace.Declare(name, type)

	class ProcessInterpreterReferences(Steps.AbstractTransformerCompilerStep):

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

		_interpreterField as Field
		_interpreter as InteractiveInterpreter
		_isEntryPoint = false

		def constructor(interpreter):
			_interpreter = interpreter

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

		override def EnterModule(node as Module):

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

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

			return true

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

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

		override def OnReferenceExpression(node as ReferenceExpression):

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

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

		override def LeaveExpressionStatement(node as ExpressionStatement):

			return unless _interpreter.RememberLastValue and _isEntryPoint

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

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

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

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

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

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

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

			return CodeBuilder.CreateCast(srcNode.ExpressionType, expression)

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

interpreter = InteractiveInterpreter(RememberLastValue: true)

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

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

interpreter.ConsoleLoopEval()

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

namespace booish

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

class InteractiveInterpreter:

	_compiler = BooCompiler()

	_parser = BooCompiler()

	_values = {}

	_declarations = {}

	_imports = ImportCollection()

	_referenceProcessor = ProcessInterpreterReferences(self)

	_representers = []

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

	[property(RememberLastValue)]
	_rememberLastValue = false

	_inputId = 0

	def constructor():

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

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

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

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

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

		InitializeStandardReferences()

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

	Pipeline:
		get:
			return _compiler.Parameters.Pipeline

	References:
		get:
			return _compiler.Parameters.References

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

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

	def Eval(code as string):

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

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

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

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

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

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

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

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

		RecordImports(savedImports)

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

		InitializeModuleInterpreter(asm, module)

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

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

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

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

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

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

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

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

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

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

	def load([required] path as string):
		References.Add(System.Reflection.Assembly.LoadFrom(path))

	def help(obj):
		type = (obj as Type) or obj.GetType()
		describeType("    ", type)

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

		baseTypes = (getBooTypeName(type.BaseType),) + array(getBooTypeName(t) for t in type.GetInterfaces())
		_print("class ${type.Name}(${join(baseTypes, ', ')}):")
		_print("")

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

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

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

		for p as Reflection.PropertyInfo in List(type.GetProperties()).Sort(sortByName):
			modifiers = describeModifiers(p)
			params = describePropertyParameters(p.GetIndexParameters())
			_print("${indent}${modifiers}${p.Name}${params} as ${getBooTypeName(p.PropertyType)}:")
			_print("${indent}${indent}get") if p.GetGetMethod() is not null
			_print("${indent}${indent}set") if p.GetSetMethod() is not null
			_print("")

		for m as Reflection.MethodInfo in List(type.GetMethods()).Sort(sortByName):
			continue if m.IsSpecialName
			modifiers = describeModifiers(m)
			returnType = getBooTypeName(m.ReturnType)
			_print("${indent}${modifiers}def ${m.Name}(${describeParameters(m.GetParameters())}) as ${returnType}")
			_print("")

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

		AddRepresenter(IDictionary) do (value as IDictionary, writer as TextWriter):
			writer.Write("{")
			i = 0
			for item as DictionaryEntry in value:
				writer.Write(", ") if i
				repr(item.Key, writer)
				writer.Write(": ")
				repr(item.Value, writer)
				++i
			writer.Write("}")

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

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

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

	callable Representer(value, writer as TextWriter)

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

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

	class InterpreterEntity(ITypedEntity):

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

		[getter(Type)]
		_type as IType

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

		EntityType:
			get:
				return TypeSystem.EntityType.Custom

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

	class InterpreterNamespace(INamespace):

		[getter(ParentNamespace)]
		_parent as INamespace

		_tss as TypeSystemServices

		_interpreter as InteractiveInterpreter

		_declarations = {}

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

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

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

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

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

			return false

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

	class InterpreterTypeSystemServices(TypeSystemServices):

		[getter(CachedCallableTypes)]
		_cachedCallableTypes as List

		[getter(GeneratedCallableTypes)]
		_generatedCallableTypes = []

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

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

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

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

	class InitializeTypeSystemServices(Steps.AbstractCompilerStep):

		_cachedCallableTypes = []

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

	class CacheCallableTypes(Steps.AbstractCompilerStep):

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

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

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

	class ProcessVariableDeclarations(Steps.ProcessMethodBodiesWithDuckTyping):

		_namespace as InterpreterNamespace
		_interpreter as InteractiveInterpreter
		_entryPoint as Method

		def constructor(interpreter):
			_interpreter = interpreter

		InEntryPoint:
			get:
				return _currentMethod.Method is _entryPoint

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

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

		override def Dispose():
			_namespace = null

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

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

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

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

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

			return _namespace.Declare(name, type)

	class ProcessInterpreterReferences(Steps.AbstractTransformerCompilerStep):

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

		_interpreterField as Field
		_interpreter as InteractiveInterpreter
		_isEntryPoint = false

		def constructor(interpreter):
			_interpreter = interpreter

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

		override def EnterModule(node as Module):

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

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

			return true

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

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

		override def OnReferenceExpression(node as ReferenceExpression):

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

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

		override def LeaveExpressionStatement(node as ExpressionStatement):

			return unless _interpreter.RememberLastValue and _isEntryPoint

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

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

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

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

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

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

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

			return CodeBuilder.CreateCast(srcNode.ExpressionType, expression)

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

interpreter = InteractiveInterpreter(RememberLastValue: true)

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

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

interpreter.ConsoleLoopEval()


Build script tweaks

Build script tweaks

configuration = "release"
test_assembly = "src/FluentLinqToSql.Tests/bin/${configuration}/FluentLinqToSql.Tests.dll"
dbtest_assembly = "src/FluentLinqToSql.DatabaseTests/bin/${configuration}/FluentLinqToSql.DatabaseTests.dll"
artest_assembly = "src/FluentLinqToSql.Tests.ActiveRecord/bin/${configuration}/FluentLinqToSql.Tests.ActiveRecord.dll"
dbtest_enabled = true

target default, (init, compile, test, createDb, dbTest, deploy, package):
  pass

target ci, (init, compile, createDb, coverage, deploy, package):
  pass

target init:
  rmdir("build")

target noDatabase:
  dbtest_enabled = false

target compile:
  msbuild(file: "FluentLinqToSql.sln", configuration: "release")

target test:
  nunit(toolPath: "lib/nunit/nunit-console-x86.exe", assemblies: (test_assembly, artest_assembly))

target createDb:
  if not dbtest_enabled:
    print "Skipping database creation"
    return
  print "Trying to create test db"
  nunit(assembly: dbtest_assembly, include: "CreateDatabase")
  #exec("lib/Tarantino/DatabaseDeployer.exe", "Rebuild (local) FluentLinqToSql db")
  print "Test database created"

target dbTest:
  if not dbtest_enabled:
    print "Skipping database tests"
    return
  nunit(assembly: dbtest_assembly)

target deploy:
  with FileList():
    .Include("src/FluentLinqToSql/bin/${configuration}/*.{dll,pdb,xml}")
    .Include("readme.txt")
    .Include("License.txt")
    .Flatten(true)
    .ForEach def(file):
      file.CopyToDirectory("build/${configuration}")

target package:
  zip("build/${configuration}", "build/FluentLinqToSql.zip")

target coverage:
  ncover_path = "c:/program files (x86)/ncover"
  app_assemblies = ("FluentLinqToSql",)
  teamcity_launcher = env("teamcity.dotnet.nunitlauncher")

  #ensure the other test assemblies are in the right place...
  System.IO.File.Copy(dbtest_assembly, "src/FluentLinqToSql.Tests/bin/${configuration}/FluentLinqToSql.DatabaseTests.dll", true)

  with FileList("src/FluentLinqToSql.Tests.ActiveRecord/bin/${configuration}"):
    .Include("FluentLinqToSql.Tests.ActiveRecord.dll")
    .Include("*.config")
    .Include("Schema.sql")
    .Flatten(true)
    .ForEach def(file):
      file.CopyToDirectory("src/FluentLinqToSql.Tests/bin/${configuration}")

  with ncover():
    .toolPath = "${ncover_path}/NCover.console.exe"
    .reportDirectory = "build/Coverage"
    .workingDirectory = "src/FluentLinqToSql.Tests/bin/${configuration}"
    .applicationAssemblies = app_assemblies
    .program = "${teamcity_launcher} v2.0 x86 NUnit-2.4.6"
    .testAssembly = "FluentLinqToSql.Tests.dll;FluentLinqToSql.DatabaseTests.dll;FluentLinqToSql.Tests.ActiveRecord.dll"
    .excludeAttributes = "System.Runtime.CompilerServices.CompilerGeneratedAttribute"

  with ncover_explorer():
    .toolPath = "${ncover_path}/NCoverExplorer.console.exe"
    .project = "FluentLinqToSql"
    .reportDirectory = "build/coverage"

target foo:
  print System.IO.FileInfo(".").FullName

configuration = "release"
test_assembly = "src/FluentLinqToSql.Tests/bin/${configuration}/FluentLinqToSql.Tests.dll"
dbtest_assembly = "src/FluentLinqToSql.DatabaseTests/bin/${configuration}/FluentLinqToSql.DatabaseTests.dll"
artest_assembly = "src/FluentLinqToSql.Tests.ActiveRecord/bin/${configuration}/FluentLinqToSql.Tests.ActiveRecord.dll"
dbtest_enabled = true

target default, (init, compile, test, createDb, dbTest, deploy, package):
  pass

target ci, (init, compile, createDb, coverage, deploy, package):
  pass

target init:
  rmdir("build")

target noDatabase:
  dbtest_enabled = false

target compile:
  msbuild(file: "FluentLinqToSql.sln", configuration: "release")

target test:
  nunit(toolPath: "lib/nunit/nunit-console-x86.exe", assemblies: (test_assembly, artest_assembly))

target createDb:
  if not dbtest_enabled:
    print "Skipping database creation"
    return
  print "Trying to create test db"
  nunit(assembly: dbtest_assembly, include: "CreateDatabase")
  #exec("lib/Tarantino/DatabaseDeployer.exe", "Rebuild (local) FluentLinqToSql db")
  print "Test database created"

target dbTest, (compile):
  if not dbtest_enabled:
    print "Skipping database tests"
    return
  nunit(assembly: dbtest_assembly)

target deploy:
  with FileList():
    .Include("src/FluentLinqToSql/bin/${configuration}/*.{dll,pdb,xml}")
    .Include("readme.txt")
    .Include("License.txt")
    .Flatten(true)
    .ForEach def(file):
      file.CopyToDirectory("build/${configuration}")

target package:
  zip("build/${configuration}", "build/FluentLinqToSql.zip")

target coverage:
  ncover_path = "c:/program files (x86)/ncover"
  app_assemblies = ("FluentLinqToSql",)
  teamcity_launcher = env("teamcity.dotnet.nunitlauncher")

  #ensure the other test assemblies are in the right place...
  System.IO.File.Copy(dbtest_assembly, "src/FluentLinqToSql.Tests/bin/${configuration}/FluentLinqToSql.DatabaseTests.dll", true)

  with FileList("src/FluentLinqToSql.Tests.ActiveRecord/bin/${configuration}"):
    .Include("FluentLinqToSql.Tests.ActiveRecord.dll")
    .Include("*.config")
    .Include("Schema.sql")
    .Flatten(true)
    .ForEach def(file):
      file.CopyToDirectory("src/FluentLinqToSql.Tests/bin/${configuration}")

  with ncover():
    .toolPath = "${ncover_path}/NCover.console.exe"
    .reportDirectory = "build/Coverage"
    .workingDirectory = "src/FluentLinqToSql.Tests/bin/${configuration}"
    .applicationAssemblies = app_assemblies
    .program = "${teamcity_launcher} v2.0 x86 NUnit-2.4.6"
    .testAssembly = "FluentLinqToSql.Tests.dll;FluentLinqToSql.DatabaseTests.dll;FluentLinqToSql.Tests.ActiveRecord.dll"
    .excludeAttributes = "System.Runtime.CompilerServices.CompilerGeneratedAttribute"

  with ncover_explorer():
    .toolPath = "${ncover_path}/NCoverExplorer.console.exe"
    .project = "FluentLinqToSql"
    .reportDirectory = "build/coverage"

target foo:
  print System.IO.FileInfo(".").FullName


Tweaking build script

Tweaking build script

configuration = "release"
test_assembly = "src/FluentLinqToSql.Tests/bin/${configuration}/FluentLinqToSql.Tests.dll"
dbtest_assembly = "src/FluentLinqToSql.DatabaseTests/bin/${configuration}/FluentLinqToSql.DatabaseTests.dll"
artest_assembly = "src/FluentLinqToSql.Tests.ActiveRecord/bin/${configuration}/FluentLinqToSql.Tests.ActiveRecord.dll"
dbtest_enabled = true

target default, (init, compile, test, createDb, dbTest, deploy, package):
  pass

target ci, (init, compile, createDb, coverage, deploy, package):
  pass

target init:
  rmdir("build")

target noDatabase:
  dbtest_enabled = false

target compile:
  msbuild(file: "FluentLinqToSql.sln", configuration: "release")

target test:
  nunit(toolPath: "lib/nunit/nunit-console-x86.exe", assemblies: (test_assembly, artest_assembly))

target createDb:
  if not dbtest_enabled:
    print "Skipping database creation"
    return
  print "Trying to create test db"
  nunit(assembly: dbtest_assembly, include: "CreateDatabase")
  #exec("lib/Tarantino/DatabaseDeployer.exe", "Rebuild (local) FluentLinqToSql db")
  print "Test database created"

target dbTest, (compile):
  if not dbtest_enabled:
    print "Skipping database tests"
    return
  nunit(assembly: dbtest_assembly)

target deploy:
  with FileList():
    .Include("src/FluentLinqToSql/bin/${configuration}/*.{dll,pdb,xml}")
    .Include("readme.txt")
    .Include("License.txt")
    .Flatten(true)
    .ForEach def(file):
      file.CopyToDirectory("build/${configuration}")

target package:
  zip("build/${configuration}", "build/FluentLinqToSql.zip")

target coverage:
  ncover_path = "c:/program files (x86)/ncover"
  app_assemblies = ("FluentLinqToSql",)
  teamcity_launcher = env("teamcity.dotnet.nunitlauncher")

  #ensure the other test assemblies are in the right place...
  System.IO.File.Copy(dbtest_assembly, "src/FluentLinqToSql.Tests/bin/${configuration}/FluentLinqToSql.DatabaseTests.dll", true)

  with FileList("src/FluentLinqToSql.Tests.ActiveRecord/bin/${configuration}"):
    .Include("FluentLinqToSql.Tests.ActiveRecord.dll")
    .Include("*.config")
    .Include("Schema.sql")
    .Flatten(true)
    .ForEach def(file):
      file.CopyToDirectory("src/FluentLinqToSql.Tests/bin/${configuration}")

  with ncover():
    .toolPath = "${ncover_path}/NCover.console.exe"
    .reportDirectory = "build/Coverage"
    .workingDirectory = "src/FluentLinqToSql.Tests/bin/${configuration}"
    .applicationAssemblies = app_assemblies
    .program = "${teamcity_launcher} v2.0 x86 NUnit-2.4.6"
    .testAssembly = "FluentLinqToSql.Tests.dll;FluentLinqToSql.DatabaseTests.dll;FluentLinqToSql.Tests.ActiveRecord.dll"
    .excludeAttributes = "System.Runtime.CompilerServices.CompilerGeneratedAttribute"

  with ncover_explorer():
    .toolPath = "${ncover_path}/NCoverExplorer.console.exe"
    .project = "FluentLinqToSql"
    .reportDirectory = "build/coverage"

configuration = "release"
test_assembly = "src/FluentLinqToSql.Tests/bin/${configuration}/FluentLinqToSql.Tests.dll"
dbtest_assembly = "src/FluentLinqToSql.DatabaseTests/bin/${configuration}/FluentLinqToSql.DatabaseTests.dll"
artest_assembly = "src/FluentLinqToSql.Tests.ActiveRecord/bin/${configuration}/FluentLinqToSql.Tests.ActiveRecord.dll"
dbtest_enabled = true

target default, (init, compile, test, createDb, dbTest, deploy, package):
  pass

target ci, (init, compile, createDb, coverage, deploy, package):
  pass

target init:
  rmdir("build")

target noDatabase:
  dbtest_enabled = false

target compile:
  msbuild(file: "FluentLinqToSql.sln", configuration: "release")

target test:
  nunit(toolPath: "lib/nunit/nunit-console-x86.exe", assemblies: (test_assembly, artest_assembly))

target createDb:
  if not dbtest_enabled:
    print "Skipping database creation"
    return
  print "Trying to create test db"
  nunit(assembly: dbtest_assembly, include: "CreateDatabase")
  #exec("lib/Tarantino/DatabaseDeployer.exe", "Rebuild (local) FluentLinqToSql db")
  print "Test database created"

target dbTest, (compile):
  if not dbtest_enabled:
    print "Skipping database tests"
    return
  nunit(assembly: dbtest_assembly)

target deploy:
  with FileList():
    .Include("src/FluentLinqToSql/bin/${configuration}/*.{dll,pdb,xml}")
    .Include("readme.txt")
    .Include("License.txt")
    .Flatten(true)
    .ForEach def(file):
      file.CopyToDirectory("build/${configuration}")

target package:
  zip("build/${configuration}", "build/FluentLinqToSql.zip")

target coverage:
  ncover_path = "c:/program files (x86)/ncover"
  app_assemblies = ("FluentLinqToSql",)
  teamcity_launcher = env("teamcity.dotnet.nunitlauncher")

  #ensure the other test assemblies are in the right place...
  System.IO.File.Copy(dbtest_assembly, "src/FluentLinqToSql.Tests/bin/${configuration}/FluentLinqToSql.DatabaseTests.dll", true)
  System.IO.File.Copy(artest_assembly, "src/FluentLinqToSql.Tests/bin/${configuration}/FluentLinqToSql.Tests.ActiveRecord.dll", true)
  System.IO.File.Copy(artest_assembly + ".config", "src/FluentLinqToSql.Tests/bin/${configuration}/FluentLinqToSql.Tests.ActiveRecord.dll.config", true)

  with ncover():
    .toolPath = "${ncover_path}/NCover.console.exe"
    .reportDirectory = "build/Coverage"
    .workingDirectory = "src/FluentLinqToSql.Tests/bin/${configuration}"
    .applicationAssemblies = app_assemblies
    .program = "${teamcity_launcher} v2.0 x86 NUnit-2.4.6"
    .testAssembly = "FluentLinqToSql.Tests.dll;FluentLinqToSql.DatabaseTests.dll;FluentLinqToSql.Tests.ActiveRecord.dll"
    .excludeAttributes = "System.Runtime.CompilerServices.CompilerGeneratedAttribute"

  with ncover_explorer():
    .toolPath = "${ncover_path}/NCoverExplorer.console.exe"
    .project = "FluentLinqToSql"
    .reportDirectory = "build/coverage"