remove part of FixIndent, could alter string literals

git-svn-id: fcce7d6ce0ac15bf24f68871b5145d46fc068279@2049 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

// authors:
// Arron Washington
// Ian MacLean (original C# version)

//See also:
//http://svn.myrealbox.com/viewcvs/trunk/mcs/class/System/Microsoft.CSharp/CSharpCodeGenerator.cs?view=auto
//http://svn.myrealbox.com/viewcvs/trunk/mcs/class/System/Microsoft.VisualBasic/VBCodeGenerator.cs?view=auto

namespace Boo.Lang.CodeDom

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

class BooCodeGenerator(CodeGenerator):
	static newlinePattern = Regex("\\n",RegexOptions.Compiled)

	static whiteSpacePattern = Regex("(\\s*)",RegexOptions.Compiled)

	static primitives = { "System.Double" : "double",
						"System.Single" : "single",
						"System.Int32" : "int",
						"System.Int64" : "long",
						"System.Int16" : "short",
						"System.UInt16" : "ushort",
						"System.Boolean" : "bool",
						"System.Char" : "char",
						"System.String" : "string",
						"System.Object" : "object",
						"System.Void" : "void"}

	protected NullToken as string:
		get:
			return "null"

	protected override def OutputType(typeRef as CodeTypeReference):
		Output.Write(GetTypeOutput(typeRef))

	protected override def GenerateArrayCreateExpression(exp as CodeArrayCreateExpression) :
		if exp.Initializers.Count:
			Output.Write("(")
			OutputExpressionList(exp.Initializers)
			Output.Write(")")
		else:
			Output.Write("array(")
			OutputType(exp.CreateType)
			Output.Write(",")
			if exp.SizeExpression:
				GenerateExpression(exp)
			else:
				Output.Write(exp.Size)
			Output.Write(")")

	protected override def GenerateBaseReferenceExpression(e as CodeBaseReferenceExpression) :
		Output.Write("super")

	protected override def GenerateCastExpression(e as CodeCastExpression) :
		Output.Write("cast(")
		OutputType(e.TargetType)
		Output.Write(",")
		GenerateExpression(e.Expression)
		Output.Write(")")

	protected override def GenerateDelegateCreateExpression(e as CodeDelegateCreateExpression) :
		GenerateExpression(e.TargetObject)
		Output.Write(".${e.MethodName} as ")
		OutputType(e.DelegateType)

	protected override def GenerateFieldReferenceExpression(e as CodeFieldReferenceExpression):
		MemberReference(e.TargetObject, e.FieldName)

	protected override def GenerateArgumentReferenceExpression(e as CodeArgumentReferenceExpression) :
		Output.Write(e.ParameterName)

	protected override def GenerateVariableReferenceExpression(e as CodeVariableReferenceExpression) :
		Output.Write(e.VariableName)

	protected override def GenerateIndexerExpression(e as CodeIndexerExpression) :
		Indexer(e.TargetObject, e.Indices)

	protected override def GenerateArrayIndexerExpression(e as CodeArrayIndexerExpression) :
		Indexer(e.TargetObject, e.Indices)

	protected override def GenerateMethodInvokeExpression(e as CodeMethodInvokeExpression) :
		Invocation(e.Method, e.Parameters)

	protected override def GenerateMethodReferenceExpression(e as CodeMethodReferenceExpression) :
		MemberReference(e.TargetObject, e.MethodName)
	protected override def GenerateEventReferenceExpression(e as CodeEventReferenceExpression) :
		MemberReference(e.TargetObject, e.EventName)

	protected override def GenerateDelegateInvokeExpression(e as CodeDelegateInvokeExpression) :
		Invocation(e.TargetObject, e.Parameters)

	protected override def GenerateObjectCreateExpression(e as CodeObjectCreateExpression) :
		OutputType(e.CreateType)
		Output.Write("(")
		OutputExpressionList(e.Parameters)
		Output.Write(")")

	protected override def GeneratePropertyReferenceExpression(e as CodePropertyReferenceExpression) :
		MemberReference(e.TargetObject, e.PropertyName)

	protected override def GeneratePropertySetValueReferenceExpression(e as CodePropertySetValueReferenceExpression) :
		Output.Write("value")

	protected override def GenerateThisReferenceExpression(e as CodeThisReferenceExpression) :
		Output.Write("self")

	protected override def GenerateExpressionStatement(e as CodeExpressionStatement) :
		GenerateExpression(e.Expression)
		Output.WriteLine();

	protected override def GenerateIterationStatement(e as CodeIterationStatement) :
		GenerateStatement(e.InitStatement)
		Output.Write("while ")
		GenerateExpression(e.TestExpression)
		Output.Write(":")
		Indent++
		GenerateStatements(e.Statements)
		GenerateStatement(e.IncrementStatement)
		Indent--

	protected override def GenerateThrowExceptionStatement(e as CodeThrowExceptionStatement) :
		Output.Write("raise ")
		GenerateExpression(e.ToThrow)

	protected override def GenerateComment(comment as CodeComment) :
		//TODO: check comment.DocComment when boo doc format is available
		commentSymbol = "#"
		txt = comment.Text

		Output.Write(commentSymbol + " ")

		i = 0
		while i<len(txt):
			rawArrayIndexing:
				Output.Write(txt[i])
				if txt[i] == char('\r'):
					if i<len(txt)-1 and txt[i+1]==char('\n'):
						continue
					Output.Write(commentSymbol)
				elif txt[i] == char('\n'):
					Output.Write(commentSymbol)
			++i
		Output.WriteLine()

	protected override def GenerateMethodReturnStatement(e as CodeMethodReturnStatement) :
		Output.Write("return ")
		GenerateExpression(e.Expression)
		Output.WriteLine()

	protected override def GenerateConditionStatement(e as CodeConditionStatement) :
		Output.Write("if ")
		GenerateExpression(e.Condition)
		Output.WriteLine(":")
		Indent++
		GenerateStatements(e.TrueStatements)
		Output.WriteLine ("pass") if len(e.TrueStatements) == 0
		Indent--
		return unless e.FalseStatements.IsValid()
		Output.WriteLine("else:")
		Indent++
		GenerateStatements(e.FalseStatements)
		Output.WriteLine ("pass") if len(e.TrueStatements) == 0
		Indent--

	protected override def GenerateTryCatchFinallyStatement(e as CodeTryCatchFinallyStatement) :
		Output.Write("try:")
		Indent++
		GenerateStatements(e.TryStatements)
		Indent--
		for exp as CodeCatchClause in e.CatchClauses:
			Output.Write("except ")
			Output.Write("${exp.LocalName} as ")
			OutputType(exp.CatchExceptionType)
			Output.Write(":")
			Indent++
			GenerateStatements(exp.Statements)
			Indent--
		GenerateStatements(e.FinallyStatements)

	protected override def GenerateAssignStatement(e as CodeAssignStatement) :
		GenerateExpression(e.Left)
		Output.Write(" = ")
		GenerateExpression(e.Right)
		Output.WriteLine()
	protected override def GenerateAttachEventStatement(e as CodeAttachEventStatement) :
		GenerateExpression(e.Event)
		Output.Write(" += ")
		GenerateExpression(e.Listener)
		Output.WriteLine()
	protected override def GenerateRemoveEventStatement(e as CodeRemoveEventStatement) :
		GenerateExpression(e.Event)
		Output.Write(" -= ")
		GenerateExpression(e.Listener)
		Output.WriteLine()
	protected override def GenerateGotoStatement(e as CodeGotoStatement) :
		Output.WriteLine("goto ${e.Label}")
	protected override def GenerateLabeledStatement(e as CodeLabeledStatement) :
		Output.WriteLine(":${e.Label}")
		GenerateStatement(e.Statement) if e.Statement
	protected override def GenerateVariableDeclarationStatement(e as CodeVariableDeclarationStatement) :
		Output.Write("${e.Name} as ")
		OutputType(e.Type)
		if e.InitExpression:
			Output.Write(" = ")
			GenerateExpression(e.InitExpression)
		Output.WriteLine()
	protected override def GenerateLinePragmaStart(e as CodeLinePragma) :
		pass

	protected override def GenerateLinePragmaEnd(e as CodeLinePragma) :
		pass

	protected override def GenerateEvent(e as CodeMemberEvent, c as CodeTypeDeclaration) :

		if e.PrivateImplementationType:
			raise "Does not support private implementation type."
		if e.ImplementationTypes.IsValid():
			raise "Does not implement these types: ${join(e.ImplementationTypes)}"
		e.Attributes |= MemberAttributes.Final

		ModifiersAndAttributes(e)

		Output.Write("event ${e.Name} as ")
		OutputType(e.Type)
		Output.WriteLine()
	protected override def GenerateField(e as CodeMemberField) :

		ModifiersAndAttributes(e)
		Output.Write("${e.Name} as ")
		OutputType(e.Type)
		if e.InitExpression:
			Output.Write(" = ")
			GenerateExpression(e.InitExpression)
		Output.WriteLine()

	protected override def GenerateSnippetMember(e as CodeSnippetTypeMember) :
		OutputAttributeDeclarations(e.CustomAttributes) if e.CustomAttributes
		Output.Write(FixIndent(e.Text))

	protected override def GenerateSnippetExpression(e as CodeSnippetExpression) :
		//Output.Write("("+e.Value+")")
		Output.Write(e.Value)

	protected override def GenerateSnippetCompileUnit(e as CodeSnippetCompileUnit) :
		Output.Write(FixIndent(e.Value))

	protected override def GenerateSnippetStatement(e as CodeSnippetStatement) :
		Output.Write(FixIndent(e.Value))

	protected def FixIndent(code as string) as string:
		return string.Empty if code is null or code==string.Empty

		//find first line that has non-whitespace and isn't a comment
		lines = newlinePattern.Split(code.Replace("\r\n", "\n"))
		foundfirst = false  //1st non-whitespace, non-commented line
		insidecomment = 0
		for line in lines:
			trimmed = line.Trim()
			if trimmed == string.Empty:
				continue

			startmultiline = trimmed.StartsWith("/*")

			//if not whitespace and not a comment...
			if (insidecomment <= 0 and
					not startmultiline and
					not trimmed.StartsWith("#") and
					not trimmed.StartsWith("//")):
				foundfirst = true
				first = line
				break //stop for loop

			//special stuff for handling multiline comments
			if startmultiline or insidecomment > 0:
				j = 0
				linelen = len(trimmed) - 1
				while j < linelen:
					rawArrayIndexing:
						c = trimmed[j]
					if c==char('/'):
						rawArrayIndexing:
							nextc = trimmed[j+1]
						if nextc==char('*'):
							++insidecomment
							++j
					elif c==char('*') and insidecomment > 0:
						rawArrayIndexing:
							nextc = trimmed[j+1]
						if nextc==char('/'):
							--insidecomment
							++j
					++j

		if not foundfirst:  //all whitespace or comments
			return code

		indentprefix = string.Empty
		//how much the code should be indented:
		indentprefix = Options.IndentString * Indent if Indent > 0

		//how much the code actually is indented:
		indent = whiteSpacePattern.Match(first).Groups[0].Value

		//if code is properly indented, return now
		return code if indent == indentprefix

		buffer = System.Text.StringBuilder()
		sz = len(indent)
		firstline = true
		for line in lines:
			if not firstline: //indentedtextwriter will handle indenting first line
				buffer.Append(indentprefix)
			else:
				firstline = false
			if sz==0 or not line.StartsWith(indent):
				buffer.Append(line)
			else:
				buffer.Append(line[sz:])
			buffer.Append("\n")
		return buffer.ToString()

	protected override def GenerateEntryPointMethod(e as CodeEntryPointMethod, c as CodeTypeDeclaration) :
		Method(e, "Main")

	protected override def GenerateMethod(e as CodeMemberMethod, c as CodeTypeDeclaration) :
		Method(e, e.Name)

	protected override def GenerateProperty(e as CodeMemberProperty, c as CodeTypeDeclaration) :
		ModifiersAndAttributes(e)

		Output.Write(" ${e.Name} ")
		if len(e.Parameters) > 1:
			Output.Write("(")
			OutputParameters(e.Parameters)
			Output.Write(")")
		Output.Write(" as ")
		OutputType(e.Type)
		Output.WriteLine(":")
		Indent++
		if e.HasGet:
			Output.WriteLine("get:")
			Indent++
			GenerateStatements(e.GetStatements)
			passcheck(e.GetStatements)
			Indent--
			Output.WriteLine()
		if e.HasSet:
			Output.WriteLine("set:")
			Indent++
			GenerateStatements(e.SetStatements)
			passcheck(e.SetStatements)
			Indent--
			Output.WriteLine()
		Indent--
		Output.WriteLine()
	protected override def GenerateConstructor(e as CodeConstructor, c as CodeTypeDeclaration) :
		Method(e, "constructor")

	protected override def GenerateTypeConstructor(e as CodeTypeConstructor) :
		e.Attributes |= MemberAttributes.Static
		e.Parameters.Clear()
		Method(e, "constructor")

	protected override def GenerateTypeStart(e as CodeTypeDeclaration):
		Output.WriteLine()
		OutputAttributeDeclarations(e.CustomAttributes)
		Output.WriteLine()
		if e.TypeAttributes & TypeAttributes.Sealed:
			e.TypeAttributes = (e.TypeAttributes.ToInt() & ~TypeAttributes.Sealed.ToInt()).ToEnum(TypeAttributes)
			Output.Write("final ")
		if e isa CodeTypeDelegate:
			GenerateDelegate(e)
			return
		OutputTypeAttributes(e.TypeAttributes, e.IsStruct, e.IsEnum)
		Output.Write(" ${e.Name}(")
		for index in range(len(e.BaseTypes)):
			var as CodeTypeReference = e.BaseTypes[index]
			OutputType(var)
			continue unless index + 1 != e.BaseTypes.Count
			Output.Write(",")
		Output.Write("):")
		Indent++
		#Ah hah hah, yeah, no. Empty class.
		passcheck(e.Members)

	private def GenerateDelegate(e as CodeTypeDelegate):
		if e.TypeAttributes & TypeAttributes.NestedFamORAssem == TypeAttributes.Public:
			Output.Write("public ")
		Output.Write("callable ${e.Name}(")
		OutputParameters(e.Parameters)
		Output.Write(") as ")
		OutputType(e.ReturnType)

	protected override def GenerateTypeEnd(e as CodeTypeDeclaration) :
		Indent--

	protected override def GenerateNamespaceStart(e as CodeNamespace) :
		if e and e.Name and e.Name != string.Empty:
			Output.WriteLine("namespace ${e.Name}")

	protected override def GenerateNamespaceEnd(e as CodeNamespace) :
		pass

	protected override def GenerateNamespaceImport(e as CodeNamespaceImport) :
		Output.WriteLine("import ${e.Namespace}")

	protected override def GenerateAttributeDeclarationsStart(attributes as CodeAttributeDeclarationCollection) :
		Output.Write("[")

	protected override def GenerateAttributeDeclarationsEnd(attributes as CodeAttributeDeclarationCollection) :
		Output.Write("]")

	protected override def Supports(support as GeneratorSupport) as bool:
		return true

	protected override def IsValidIdentifier(value as string) as bool:
		return true

	protected override def CreateEscapedIdentifier(value as string) as string:
		return value

	protected override def CreateValidIdentifier(value as string) as string:
		return value

	protected override def GetTypeOutput(typeRef as CodeTypeReference):
		if typeRef.ArrayElementType:
			out = GetTypeOutput(typeRef.ArrayElementType)
		elif typeRef.BaseType in primitives:
			out = primitives[typeRef.BaseType]
		else:
			out = typeRef.BaseType

		if typeRef.ArrayRank > 0:
			if typeRef.ArrayRank > 1:
				return "(${out}, ${typeRef.ArrayRank})"
			return "(${out})"
		return out

	protected override def QuoteSnippetString(snippet as string) as string:
		s = snippet.Replace("\\", "\\\\")
		s = s.Replace("\"", "\\\"")
		s = s.Replace("\t", "\\t")
		s = s.Replace("\r", "\\r")
		s = s.Replace("\n", "\\n")
		return "\"" + s + "\""

	def Method(method as duck, name as string):
		ModifiersAndAttributes(method)
		Output.Write("def ${name} (")
		OutputParameters(method.Parameters)
		Output.Write(")")
		unless GetTypeOutput(method.ReturnType) == "void":
			Output.Write(" as ")
			OutputType(method.ReturnType)
			OutputAttributeDeclarations(method.ReturnTypeCustomAttributes) if method.ReturnTypeCustomAttributes
		Output.WriteLine(":")
		Indent++
		if bugged = method as CodeConstructor:
			if bugged.BaseConstructorArgs.IsValid():
				Output.Write("super(")
				OutputExpressionList(bugged.BaseConstructorArgs)
				Output.WriteLine(")")
			if bugged.ChainedConstructorArgs.IsValid():
				Output.Write("self(")
				OutputExpressionList(bugged.ChainedConstructorArgs)
				Output.WriteLine(")")
		GenerateStatements(method.Statements)
		passcheck(method.Statements)
		Indent--

	def MemberReference(target as CodeExpression, member as string):
		GenerateExpression(target)
		Output.Write(".${member}")

	def Indexer(target as CodeExpression, indices as CodeExpressionCollection):
		GenerateExpression(target)
		Output.Write("[")
		OutputExpressionList(indices)
		Output.Write("]")

	def Invocation(target as CodeExpression, parameters as CodeExpressionCollection):
		GenerateExpression(target)
		Output.Write("(")
		OutputExpressionList(parameters)
		Output.Write(")")

	def passcheck(stuff as ICollection):
		return if stuff.IsValid()
		Output.Write("pass")

	override def OutputTypeNamePair(type as CodeTypeReference, name as string):
		Output.Write("${name} as ")
		OutputType(type)

	protected override def GenerateParameterDeclarationExpression(e as CodeParameterDeclarationExpression):
		OutputAttributes(e.CustomAttributes, null, true);
		OutputDirection(e.Direction)
		OutputTypeNamePair(e.Type, e.Name)

	private def OutputAttributes(attributes as CodeAttributeDeclarationCollection, prefix as string, inline as bool):
		for att as CodeAttributeDeclaration in attributes:
			GenerateAttributeDeclarationsStart(attributes)
			Output.Write(prefix) if prefix
			OutputAttributeDeclaration(att)
			GenerateAttributeDeclarationsEnd(attributes)
			if inline:
				Output.Write(" ")
			else:
				Output.WriteLine()

	private def OutputAttributeDeclaration(attribute as CodeAttributeDeclaration):
		Output.Write(attribute.Name.Replace ('+', '.'))
		Output.Write('(')
		first = true
		for argument as CodeAttributeArgument in attribute.Arguments:
			if first:
				OutputAttributeArgument(argument)
				first = false
			else:
				Output.Write(", ")
				OutputAttributeArgument(argument)
		Output.Write(')')

	def ModifiersAndAttributes(typeMember as CodeTypeMember):
		OutputAttributeDeclarations(typeMember.CustomAttributes) if typeMember.CustomAttributes
		Output.WriteLine()
		to = { ziggy| return cast(int, ziggy) }
		fro = { ziggy| return cast(MemberAttributes, ziggy) }
		if typeMember.Attributes & MemberAttributes.VTableMask == MemberAttributes.New:
			typeMember.Attributes = fro(to(typeMember.Attributes) & ~ to(MemberAttributes.New))
		OutputMemberAccessModifier(typeMember.Attributes)
		OutputMemberScopeModifier(typeMember.Attributes)

	def OutputAttributeArgument(arg as CodeAttributeArgument):
		if arg.Name and arg.Name != "":
			Output.Write(arg.Name)
			Output.Write(" : ")
		GenerateExpression(arg.Value)

	protected override def OutputDirection(direction as FieldDirection):
		if direction == FieldDirection.Ref or direction == FieldDirection.Out:
			Output.Write("ref ")

	protected override def OutputFieldScopeModifier(attributes as MemberAttributes):
		scope = attributes & MemberAttributes.ScopeMask
		if scope == MemberAttributes.Static:
			Output.Write("static ")
		elif scope == MemberAttributes.Const:
			Output.Write("static final ")

	private def AsBool(data):
		return data is not null and data isa bool and cast(bool,data)

	protected override def GenerateCompileUnitStart(compileUnit as CodeCompileUnit):
		GenerateComment(CodeComment("------------------------------------------------------------------------------"))
		GenerateComment(CodeComment("This code was automatically generated by Boo.Lang.CodeDom v.${BooVersion}."))
		GenerateComment(CodeComment(""))
		GenerateComment(CodeComment("     Changes to this file may cause incorrect behavior and will be lost if "))
		GenerateComment(CodeComment("     the code is regenerated."))
		GenerateComment(CodeComment("------------------------------------------------------------------------------"))
		Output.WriteLine()

		//for future:
		//if AsBool(compileUnit.UserData["WhiteSpaceAgnostic"]):
		//	Output.WriteLine("Option WhiteSpaceAgnostic")

		super.GenerateCompileUnitStart(compileUnit)

	protected override def GenerateCompileUnit(compileUnit as CodeCompileUnit):
		GenerateCompileUnitStart(compileUnit)

		for ns as CodeNamespace in compileUnit.Namespaces:
			GenerateNamespace(ns)

		if compileUnit.AssemblyCustomAttributes.Count > 0:
			OutputAttributes(compileUnit.AssemblyCustomAttributes,
					"assembly:", false)
			Output.WriteLine("")

		GenerateCompileUnitEnd(compileUnit)

#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

// authors:
// Arron Washington
// Ian MacLean (original C# version)

//See also:
//http://svn.myrealbox.com/viewcvs/trunk/mcs/class/System/Microsoft.CSharp/CSharpCodeGenerator.cs?view=auto
//http://svn.myrealbox.com/viewcvs/trunk/mcs/class/System/Microsoft.VisualBasic/VBCodeGenerator.cs?view=auto

namespace Boo.Lang.CodeDom

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

class BooCodeGenerator(CodeGenerator):
	static newlinePattern = Regex("\\n",RegexOptions.Compiled)

	static whiteSpacePattern = Regex("(\\s*)",RegexOptions.Compiled)

	static primitives = { "System.Double" : "double",
						"System.Single" : "single",
						"System.Int32" : "int",
						"System.Int64" : "long",
						"System.Int16" : "short",
						"System.UInt16" : "ushort",
						"System.Boolean" : "bool",
						"System.Char" : "char",
						"System.String" : "string",
						"System.Object" : "object",
						"System.Void" : "void"}

	protected NullToken as string:
		get:
			return "null"

	protected override def OutputType(typeRef as CodeTypeReference):
		Output.Write(GetTypeOutput(typeRef))

	protected override def GenerateArrayCreateExpression(exp as CodeArrayCreateExpression) :
		if exp.Initializers.Count:
			Output.Write("(")
			OutputExpressionList(exp.Initializers)
			Output.Write(")")
		else:
			Output.Write("array(")
			OutputType(exp.CreateType)
			Output.Write(",")
			if exp.SizeExpression:
				GenerateExpression(exp)
			else:
				Output.Write(exp.Size)
			Output.Write(")")

	protected override def GenerateBaseReferenceExpression(e as CodeBaseReferenceExpression) :
		Output.Write("super")

	protected override def GenerateCastExpression(e as CodeCastExpression) :
		Output.Write("cast(")
		OutputType(e.TargetType)
		Output.Write(",")
		GenerateExpression(e.Expression)
		Output.Write(")")

	protected override def GenerateDelegateCreateExpression(e as CodeDelegateCreateExpression) :
		GenerateExpression(e.TargetObject)
		Output.Write(".${e.MethodName} as ")
		OutputType(e.DelegateType)

	protected override def GenerateFieldReferenceExpression(e as CodeFieldReferenceExpression):
		MemberReference(e.TargetObject, e.FieldName)

	protected override def GenerateArgumentReferenceExpression(e as CodeArgumentReferenceExpression) :
		Output.Write(e.ParameterName)

	protected override def GenerateVariableReferenceExpression(e as CodeVariableReferenceExpression) :
		Output.Write(e.VariableName)

	protected override def GenerateIndexerExpression(e as CodeIndexerExpression) :
		Indexer(e.TargetObject, e.Indices)

	protected override def GenerateArrayIndexerExpression(e as CodeArrayIndexerExpression) :
		Indexer(e.TargetObject, e.Indices)

	protected override def GenerateMethodInvokeExpression(e as CodeMethodInvokeExpression) :
		Invocation(e.Method, e.Parameters)

	protected override def GenerateMethodReferenceExpression(e as CodeMethodReferenceExpression) :
		MemberReference(e.TargetObject, e.MethodName)
	protected override def GenerateEventReferenceExpression(e as CodeEventReferenceExpression) :
		MemberReference(e.TargetObject, e.EventName)

	protected override def GenerateDelegateInvokeExpression(e as CodeDelegateInvokeExpression) :
		Invocation(e.TargetObject, e.Parameters)

	protected override def GenerateObjectCreateExpression(e as CodeObjectCreateExpression) :
		OutputType(e.CreateType)
		Output.Write("(")
		OutputExpressionList(e.Parameters)
		Output.Write(")")

	protected override def GeneratePropertyReferenceExpression(e as CodePropertyReferenceExpression) :
		MemberReference(e.TargetObject, e.PropertyName)

	protected override def GeneratePropertySetValueReferenceExpression(e as CodePropertySetValueReferenceExpression) :
		Output.Write("value")

	protected override def GenerateThisReferenceExpression(e as CodeThisReferenceExpression) :
		Output.Write("self")

	protected override def GenerateExpressionStatement(e as CodeExpressionStatement) :
		GenerateExpression(e.Expression)
		Output.WriteLine();

	protected override def GenerateIterationStatement(e as CodeIterationStatement) :
		GenerateStatement(e.InitStatement)
		Output.Write("while ")
		GenerateExpression(e.TestExpression)
		Output.Write(":")
		Indent++
		GenerateStatements(e.Statements)
		GenerateStatement(e.IncrementStatement)
		Indent--

	protected override def GenerateThrowExceptionStatement(e as CodeThrowExceptionStatement) :
		Output.Write("raise ")
		GenerateExpression(e.ToThrow)

	protected override def GenerateComment(comment as CodeComment) :
		//TODO: check comment.DocComment when boo doc format is available
		commentSymbol = "#"
		txt = comment.Text

		Output.Write(commentSymbol + " ")

		i = 0
		while i<len(txt):
			rawArrayIndexing:
				Output.Write(txt[i])
				if txt[i] == char('\r'):
					if i<len(txt)-1 and txt[i+1]==char('\n'):
						continue
					Output.Write(commentSymbol)
				elif txt[i] == char('\n'):
					Output.Write(commentSymbol)
			++i
		Output.WriteLine()

	protected override def GenerateMethodReturnStatement(e as CodeMethodReturnStatement) :
		Output.Write("return ")
		GenerateExpression(e.Expression)
		Output.WriteLine()

	protected override def GenerateConditionStatement(e as CodeConditionStatement) :
		Output.Write("if ")
		GenerateExpression(e.Condition)
		Output.WriteLine(":")
		Indent++
		GenerateStatements(e.TrueStatements)
		Output.WriteLine ("pass") if len(e.TrueStatements) == 0
		Indent--
		return unless e.FalseStatements.IsValid()
		Output.WriteLine("else:")
		Indent++
		GenerateStatements(e.FalseStatements)
		Output.WriteLine ("pass") if len(e.TrueStatements) == 0
		Indent--

	protected override def GenerateTryCatchFinallyStatement(e as CodeTryCatchFinallyStatement) :
		Output.Write("try:")
		Indent++
		GenerateStatements(e.TryStatements)
		Indent--
		for exp as CodeCatchClause in e.CatchClauses:
			Output.Write("except ")
			Output.Write("${exp.LocalName} as ")
			OutputType(exp.CatchExceptionType)
			Output.Write(":")
			Indent++
			GenerateStatements(exp.Statements)
			Indent--
		GenerateStatements(e.FinallyStatements)

	protected override def GenerateAssignStatement(e as CodeAssignStatement) :
		GenerateExpression(e.Left)
		Output.Write(" = ")
		GenerateExpression(e.Right)
		Output.WriteLine()
	protected override def GenerateAttachEventStatement(e as CodeAttachEventStatement) :
		GenerateExpression(e.Event)
		Output.Write(" += ")
		GenerateExpression(e.Listener)
		Output.WriteLine()
	protected override def GenerateRemoveEventStatement(e as CodeRemoveEventStatement) :
		GenerateExpression(e.Event)
		Output.Write(" -= ")
		GenerateExpression(e.Listener)
		Output.WriteLine()
	protected override def GenerateGotoStatement(e as CodeGotoStatement) :
		Output.WriteLine("goto ${e.Label}")
	protected override def GenerateLabeledStatement(e as CodeLabeledStatement) :
		Output.WriteLine(":${e.Label}")
		GenerateStatement(e.Statement) if e.Statement
	protected override def GenerateVariableDeclarationStatement(e as CodeVariableDeclarationStatement) :
		Output.Write("${e.Name} as ")
		OutputType(e.Type)
		if e.InitExpression:
			Output.Write(" = ")
			GenerateExpression(e.InitExpression)
		Output.WriteLine()
	protected override def GenerateLinePragmaStart(e as CodeLinePragma) :
		pass

	protected override def GenerateLinePragmaEnd(e as CodeLinePragma) :
		pass

	protected override def GenerateEvent(e as CodeMemberEvent, c as CodeTypeDeclaration) :

		if e.PrivateImplementationType:
			raise "Does not support private implementation type."
		if e.ImplementationTypes.IsValid():
			raise "Does not implement these types: ${join(e.ImplementationTypes)}"
		e.Attributes |= MemberAttributes.Final

		ModifiersAndAttributes(e)

		Output.Write("event ${e.Name} as ")
		OutputType(e.Type)
		Output.WriteLine()
	protected override def GenerateField(e as CodeMemberField) :

		ModifiersAndAttributes(e)
		Output.Write("${e.Name} as ")
		OutputType(e.Type)
		if e.InitExpression:
			Output.Write(" = ")
			GenerateExpression(e.InitExpression)
		Output.WriteLine()

	protected override def GenerateSnippetMember(e as CodeSnippetTypeMember) :
		OutputAttributeDeclarations(e.CustomAttributes) if e.CustomAttributes
		Output.Write(FixIndent(e.Text))

	protected override def GenerateSnippetExpression(e as CodeSnippetExpression) :
		//Output.Write("("+e.Value+")")
		Output.Write(e.Value)

	protected override def GenerateSnippetCompileUnit(e as CodeSnippetCompileUnit) :
		Output.Write(FixIndent(e.Value))

	protected override def GenerateSnippetStatement(e as CodeSnippetStatement) :
		Output.Write(FixIndent(e.Value))

	protected def FixIndent(code as string) as string:
		return string.Empty if code is null or code==string.Empty

		if Options.IndentString.StartsWith(" "):
			code = code.Replace("\t",Options.IndentString)
		elif Options.IndentString.StartsWith("\t"):
			code = code.Replace("    ","\t")

		//find first line that has non-whitespace and isn't a comment
		lines = newlinePattern.Split(code.Replace("\r\n", "\n"))
		foundfirst = false  //1st non-whitespace, non-commented line
		insidecomment = 0
		for line in lines:
			trimmed = line.Trim()
			if trimmed == string.Empty:
				continue

			startmultiline = trimmed.StartsWith("/*")

			//if not whitespace and not a comment...
			if (insidecomment <= 0 and
					not startmultiline and
					not trimmed.StartsWith("#") and
					not trimmed.StartsWith("//")):
				foundfirst = true
				first = line
				break //stop for loop

			//special stuff for handling multiline comments
			if startmultiline or insidecomment > 0:
				j = 0
				linelen = len(trimmed) - 1
				while j < linelen:
					rawArrayIndexing:
						c = trimmed[j]
					if c==char('/'):
						rawArrayIndexing:
							nextc = trimmed[j+1]
						if nextc==char('*'):
							++insidecomment
							++j
					elif c==char('*') and insidecomment > 0:
						rawArrayIndexing:
							nextc = trimmed[j+1]
						if nextc==char('/'):
							--insidecomment
							++j
					++j

		if not foundfirst:  //all whitespace or comments
			return code

		indentprefix = string.Empty
		//how much the code should be indented:
		indentprefix = Options.IndentString * Indent if Indent > 0

		//how much the code actually is indented:
		indent = whiteSpacePattern.Match(first).Groups[0].Value

		//if code is properly indented, return now
		return code if indent == indentprefix

		buffer = System.Text.StringBuilder()
		sz = len(indent)
		firstline = true
		for line in lines:
			if not firstline: //indentedtextwriter will handle indenting first line
				buffer.Append(indentprefix)
			else:
				firstline = false
			if sz==0 or not line.StartsWith(indent):
				buffer.Append(line)
			else:
				buffer.Append(line[sz:])
			buffer.Append("\n")
		return buffer.ToString()

	protected override def GenerateEntryPointMethod(e as CodeEntryPointMethod, c as CodeTypeDeclaration) :
		Method(e, "Main")

	protected override def GenerateMethod(e as CodeMemberMethod, c as CodeTypeDeclaration) :
		Method(e, e.Name)

	protected override def GenerateProperty(e as CodeMemberProperty, c as CodeTypeDeclaration) :
		ModifiersAndAttributes(e)

		Output.Write(" ${e.Name} ")
		if len(e.Parameters) > 1:
			Output.Write("(")
			OutputParameters(e.Parameters)
			Output.Write(")")
		Output.Write(" as ")
		OutputType(e.Type)
		Output.WriteLine(":")
		Indent++
		if e.HasGet:
			Output.WriteLine("get:")
			Indent++
			GenerateStatements(e.GetStatements)
			passcheck(e.GetStatements)
			Indent--
			Output.WriteLine()
		if e.HasSet:
			Output.WriteLine("set:")
			Indent++
			GenerateStatements(e.SetStatements)
			passcheck(e.SetStatements)
			Indent--
			Output.WriteLine()
		Indent--
		Output.WriteLine()
	protected override def GenerateConstructor(e as CodeConstructor, c as CodeTypeDeclaration) :
		Method(e, "constructor")

	protected override def GenerateTypeConstructor(e as CodeTypeConstructor) :
		e.Attributes |= MemberAttributes.Static
		e.Parameters.Clear()
		Method(e, "constructor")

	protected override def GenerateTypeStart(e as CodeTypeDeclaration):
		Output.WriteLine()
		OutputAttributeDeclarations(e.CustomAttributes)
		Output.WriteLine()
		if e.TypeAttributes & TypeAttributes.Sealed:
			e.TypeAttributes = (e.TypeAttributes.ToInt() & ~TypeAttributes.Sealed.ToInt()).ToEnum(TypeAttributes)
			Output.Write("final ")
		if e isa CodeTypeDelegate:
			GenerateDelegate(e)
			return
		OutputTypeAttributes(e.TypeAttributes, e.IsStruct, e.IsEnum)
		Output.Write(" ${e.Name}(")
		for index in range(len(e.BaseTypes)):
			var as CodeTypeReference = e.BaseTypes[index]
			OutputType(var)
			continue unless index + 1 != e.BaseTypes.Count
			Output.Write(",")
		Output.Write("):")
		Indent++
		#Ah hah hah, yeah, no. Empty class.
		passcheck(e.Members)

	private def GenerateDelegate(e as CodeTypeDelegate):
		if e.TypeAttributes & TypeAttributes.NestedFamORAssem == TypeAttributes.Public:
			Output.Write("public ")
		Output.Write("callable ${e.Name}(")
		OutputParameters(e.Parameters)
		Output.Write(") as ")
		OutputType(e.ReturnType)

	protected override def GenerateTypeEnd(e as CodeTypeDeclaration) :
		Indent--

	protected override def GenerateNamespaceStart(e as CodeNamespace) :
		if e and e.Name and e.Name != string.Empty:
			Output.WriteLine("namespace ${e.Name}")

	protected override def GenerateNamespaceEnd(e as CodeNamespace) :
		pass

	protected override def GenerateNamespaceImport(e as CodeNamespaceImport) :
		Output.WriteLine("import ${e.Namespace}")

	protected override def GenerateAttributeDeclarationsStart(attributes as CodeAttributeDeclarationCollection) :
		Output.Write("[")

	protected override def GenerateAttributeDeclarationsEnd(attributes as CodeAttributeDeclarationCollection) :
		Output.Write("]")

	protected override def Supports(support as GeneratorSupport) as bool:
		return true

	protected override def IsValidIdentifier(value as string) as bool:
		return true

	protected override def CreateEscapedIdentifier(value as string) as string:
		return value

	protected override def CreateValidIdentifier(value as string) as string:
		return value

	protected override def GetTypeOutput(typeRef as CodeTypeReference):
		if typeRef.ArrayElementType:
			out = GetTypeOutput(typeRef.ArrayElementType)
		elif typeRef.BaseType in primitives:
			out = primitives[typeRef.BaseType]
		else:
			out = typeRef.BaseType

		if typeRef.ArrayRank > 0:
			if typeRef.ArrayRank > 1:
				return "(${out}, ${typeRef.ArrayRank})"
			return "(${out})"
		return out

	protected override def QuoteSnippetString(snippet as string) as string:
		s = snippet.Replace("\\", "\\\\")
		s = s.Replace("\"", "\\\"")
		s = s.Replace("\t", "\\t")
		s = s.Replace("\r", "\\r")
		s = s.Replace("\n", "\\n")
		return "\"" + s + "\""

	def Method(method as duck, name as string):
		ModifiersAndAttributes(method)
		Output.Write("def ${name} (")
		OutputParameters(method.Parameters)
		Output.Write(")")
		unless GetTypeOutput(method.ReturnType) == "void":
			Output.Write(" as ")
			OutputType(method.ReturnType)
			OutputAttributeDeclarations(method.ReturnTypeCustomAttributes) if method.ReturnTypeCustomAttributes
		Output.WriteLine(":")
		Indent++
		if bugged = method as CodeConstructor:
			if bugged.BaseConstructorArgs.IsValid():
				Output.Write("super(")
				OutputExpressionList(bugged.BaseConstructorArgs)
				Output.WriteLine(")")
			if bugged.ChainedConstructorArgs.IsValid():
				Output.Write("self(")
				OutputExpressionList(bugged.ChainedConstructorArgs)
				Output.WriteLine(")")
		GenerateStatements(method.Statements)
		passcheck(method.Statements)
		Indent--

	def MemberReference(target as CodeExpression, member as string):
		GenerateExpression(target)
		Output.Write(".${member}")

	def Indexer(target as CodeExpression, indices as CodeExpressionCollection):
		GenerateExpression(target)
		Output.Write("[")
		OutputExpressionList(indices)
		Output.Write("]")

	def Invocation(target as CodeExpression, parameters as CodeExpressionCollection):
		GenerateExpression(target)
		Output.Write("(")
		OutputExpressionList(parameters)
		Output.Write(")")

	def passcheck(stuff as ICollection):
		return if stuff.IsValid()
		Output.Write("pass")

	override def OutputTypeNamePair(type as CodeTypeReference, name as string):
		Output.Write("${name} as ")
		OutputType(type)

	protected override def GenerateParameterDeclarationExpression(e as CodeParameterDeclarationExpression):
		OutputAttributes(e.CustomAttributes, null, true);
		OutputDirection(e.Direction)
		OutputTypeNamePair(e.Type, e.Name)

	private def OutputAttributes(attributes as CodeAttributeDeclarationCollection, prefix as string, inline as bool):
		for att as CodeAttributeDeclaration in attributes:
			GenerateAttributeDeclarationsStart(attributes)
			Output.Write(prefix) if prefix
			OutputAttributeDeclaration(att)
			GenerateAttributeDeclarationsEnd(attributes)
			if inline:
				Output.Write(" ")
			else:
				Output.WriteLine()

	private def OutputAttributeDeclaration(attribute as CodeAttributeDeclaration):
		Output.Write(attribute.Name.Replace ('+', '.'))
		Output.Write('(')
		first = true
		for argument as CodeAttributeArgument in attribute.Arguments:
			if first:
				OutputAttributeArgument(argument)
				first = false
			else:
				Output.Write(", ")
				OutputAttributeArgument(argument)
		Output.Write(')')

	def ModifiersAndAttributes(typeMember as CodeTypeMember):
		OutputAttributeDeclarations(typeMember.CustomAttributes) if typeMember.CustomAttributes
		Output.WriteLine()
		to = { ziggy| return cast(int, ziggy) }
		fro = { ziggy| return cast(MemberAttributes, ziggy) }
		if typeMember.Attributes & MemberAttributes.VTableMask == MemberAttributes.New:
			typeMember.Attributes = fro(to(typeMember.Attributes) & ~ to(MemberAttributes.New))
		OutputMemberAccessModifier(typeMember.Attributes)
		OutputMemberScopeModifier(typeMember.Attributes)

	def OutputAttributeArgument(arg as CodeAttributeArgument):
		if arg.Name and arg.Name != "":
			Output.Write(arg.Name)
			Output.Write(" : ")
		GenerateExpression(arg.Value)

	protected override def OutputDirection(direction as FieldDirection):
		if direction == FieldDirection.Ref or direction == FieldDirection.Out:
			Output.Write("ref ")

	protected override def OutputFieldScopeModifier(attributes as MemberAttributes):
		scope = attributes & MemberAttributes.ScopeMask
		if scope == MemberAttributes.Static:
			Output.Write("static ")
		elif scope == MemberAttributes.Const:
			Output.Write("static final ")

	private def AsBool(data):
		return data is not null and data isa bool and cast(bool,data)

	protected override def GenerateCompileUnitStart(compileUnit as CodeCompileUnit):
		GenerateComment(CodeComment("------------------------------------------------------------------------------"))
		GenerateComment(CodeComment("This code was automatically generated by Boo.Lang.CodeDom v.${BooVersion}."))
		GenerateComment(CodeComment(""))
		GenerateComment(CodeComment("     Changes to this file may cause incorrect behavior and will be lost if "))
		GenerateComment(CodeComment("     the code is regenerated."))
		GenerateComment(CodeComment("------------------------------------------------------------------------------"))
		Output.WriteLine()

		//for future:
		//if AsBool(compileUnit.UserData["WhiteSpaceAgnostic"]):
		//	Output.WriteLine("Option WhiteSpaceAgnostic")

		super.GenerateCompileUnitStart(compileUnit)

	protected override def GenerateCompileUnit(compileUnit as CodeCompileUnit):
		GenerateCompileUnitStart(compileUnit)

		for ns as CodeNamespace in compileUnit.Namespaces:
			GenerateNamespace(ns)

		if compileUnit.AssemblyCustomAttributes.Count > 0:
			OutputAttributes(compileUnit.AssemblyCustomAttributes,
					"assembly:", false)
			Output.WriteLine("")

		GenerateCompileUnitEnd(compileUnit)


support for slicing and exponentiation

support for slicing and exponentiation

namespace Boo.OMeta.Parser

import Boo.OMeta
import Boo.PatternMatching

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

macro infix:

	l, op, r = infix.Arguments

	return ExpressionStatement([| $l = ((($l >> l, $op >> op, $r >> r) ^ newInfixExpression(op, l, r)) | $r) |])

macro infixr:

	l, op, r = infixr.Arguments

	return ExpressionStatement([| $l = ((($r >> l, $op >> op, $l >> r) ^ newInfixExpression(op, l, r)) | $r) |])

macro prefix:

	rule, op, next = prefix.Arguments

	return ExpressionStatement([| $rule = (($op >> op, $rule >> e) ^ newPrefixExpression(op, e)) | $next |])

macro list_of:
"""
Generates rules for lists of the given expression.

	list_of expression

Expands to something that matches:

	expression (COMMA expression)+
"""

	rule, = list_of.Arguments

	block as Block = list_of.ParentNode

	listRuleName = ReferenceExpression(Name: "${rule}_list")
	listRule = [| $listRuleName = ((($rule >> first), ++((COMMA, $rule >> e) ^ e) >> rest) ^ prepend(first, rest)) | ($rule >> v ^ [v]) |]
	block.Add(listRule)

	optionalRuleName = ReferenceExpression(Name: "optional_${rule}_list")
	optionalListRule = [| $optionalRuleName = $listRuleName | ("" ^ []) |]
	block.Add(optionalListRule)

ometa BooParser < WhitespaceSensitiveTokenizer:

	tokens:
		equality = "=="
		inequality = "!="
		assign = "="
		assign_inplace = "+=" | "-=" | "*=" | "/=" | "%=" | "^="
		xor = "^"
		plus = "+"
		minus = "-"
		exponentiation = "**"
		star = "*"
		division = "/"
		modulus = "%"
		bitwise_not = "~"
		bitwise_shift_left = "<<"
		bitwise_shift_right = ">>"
		bitwise_and = "&"
		bitwise_or = "|"
		num = ++digit
		colon = ":"
		dot = "."
		comma = ","
		lparen = "(", enterWhitespaceAgnosticRegion
		rparen = ")", leaveWhitespaceAgnosticRegion
		lbrack = "[", enterWhitespaceAgnosticRegion
		rbrack = "]", leaveWhitespaceAgnosticRegion
		kw = (keywords >> value, ~(letter | digit)) ^ value
		tdqs = ('"""', ++(~'"""', _) >> s, '"""') ^ s
		sdqs = ('"', ++(~'"', _) >> s, '"') ^ s
		sqs = ("'", ++(~"'", _) >> s, "'") ^ s
		id = ((letter | '_') >> p, --(letter | digit | '_') >> s) ^ makeString(p, s)

	keywords "class", "def", "import", "pass", "return", "true", \
		"false", "and", "or", "as", "not", "if", "is", "null", \
		"for", "in", "yield"

	keyword[expected] = ((KW >> t) and (expected is tokenValue(t))) ^ t

	module = (
		--EOL,
		(docstring >> s | ""),
		--import_declaration >> ids,
		--module_member >> members,
		--stmt >> stmts,
		--whitespace
	) ^ newModule(s, ids, members, stmts)

	docstring = (TDQS >> t, eol) ^ tokenValue(t)

	import_declaration = (IMPORT, qualified_name >> qn, eol) ^ newImport(qn)

	qualified_name = (ID >> qualifier, --((DOT, ID >> n) ^ n) >> suffix)^ buildQName(qualifier, suffix)

	module_member = class_def | method

	class_def = (
		CLASS, ID >> className, begin_block, class_body >> body, end_block
	) ^ newClass(className, body)

	begin_block = COLON, INDENT

	end_block = DEDENT

	class_body = ((PASS, eol) ^ null) | (++class_member >> members ^ members)

	class_member = method | class_def

	method = (
		DEF, ID >> name, LPAREN, optional_parameter_list >> parameters, RPAREN, block >> body
	) ^ newMethod(name, parameters, body)

	list_of parameter

	parameter = ((ID >> name, AS, type_reference >> type) | (ID >> name)) ^ ParameterDeclaration(Name: tokenValue(name), Type: type)

	block = empty_block | non_empty_block

	empty_block = (begin_block, (PASS, eol), end_block) ^ Block()

	non_empty_block = (begin_block, ++stmt >> stmts, end_block)  ^ newBlock(stmts)

	stmt = ((stmt_block >> s) | (stmt_line >> s, eol)) ^ s

	stmt_line = (~~(ID, AS), stmt_declaration) \
		| stmt_expression \
		| stmt_return \
		| stmt_yield

	stmt_yield = (YIELD, assignment >> e, stmt_modifier >> m) ^ YieldStatement(Expression: e, Modifier: m)

	stmt_modifier = ((IF, assignment >> e) ^ StatementModifier(Type: StatementModifierType.If, Condition: e)) | ""

	stmt_declaration = (declaration >> d, ASSIGN, expression >> e) ^ newDeclarationStatement(d, e)

	declaration = (ID >> name, ((AS, type_reference >> typeRef) | "")) ^ newDeclaration(name, typeRef)

	stmt_block = stmt_if | stmt_for

	stmt_for = (FOR, declaration_list >> dl, IN, expression >> e, block >> body) ^ newForStatement(dl, e, body)

	stmt_if = (IF, expression >> e, block >> trueBlock) ^ newIfStatement(e, trueBlock)

	stmt_return = (RETURN, optional_expression >> e, stmt_modifier >> m) ^ ReturnStatement(Expression: e, Modifier: m)

	optional_expression = expression | ""

	stmt_expression = (multi_assignment | assignment) >> e ^ ExpressionStatement(Expression: e)

	multi_assignment = (expression >> l, ASSIGN >> op, assignment_list >> items) ^ newInfixExpression(op, l, newRValue(items))

	list_of assignment

	infixr assignment, (ASSIGN | ASSIGN_INPLACE), expression

	expression = or_expression

	infix or_expression, OR, and_expression

	infix and_expression, AND, not_expression

	not_expression = ((NOT >> op, expression >> e) ^ newPrefixExpression(op, e)) | membership_expression

	infix membership_expression, (IN | (NOT, IN)), identity_test_expression

	infix identity_test_expression, ((IS, NOT) | IS), comparison

	infix comparison, (EQUALITY | INEQUALITY | IS), bitwise_or_expression

	infix bitwise_or_expression, BITWISE_OR, bitwise_xor_expression

	infix bitwise_xor_expression, XOR, bitwise_and_expression

	infix bitwise_and_expression, BITWISE_AND, bitwise_shift_expression

	infix bitwise_shift_expression, bitwise_shift_op, term

	bitwise_shift_op = (BITWISE_SHIFT_LEFT | BITWISE_SHIFT_RIGHT)

	infix term, (PLUS | MINUS), factor

	infix factor, (STAR | DIVISION | MODULUS), signalled_expression

	prefix signalled_expression, MINUS, bitwise_not_expression

	prefix bitwise_not_expression, BITWISE_NOT, exponentiation_expression

	infix exponentiation_expression, EXPONENTIATION, try_cast

	try_cast = ((try_cast >> e, AS, type_reference >> typeRef) ^ TryCastExpression(Target: e, Type: typeRef)) | member_reference

	member_reference = ((expression >> e, DOT, ID >> name) ^ newMemberReference(e, name)) | slicing

	slicing = ((expression >> e, LBRACK, slice_list >> indices, RBRACK) ^ newSlicing(e, indices)) | invocation

	slice = (expression >> begin,
				((COLON, omitted_expression >> end,
					((COLON, omitted_expression >> step) | ""))
					| "")) ^ newSlice(begin, end, step)

	list_of slice

	omitted_expression = expression | ("" ^ OmittedExpression.Default)

	invocation = ((expression >> target, LPAREN, optional_expression_list >> args, RPAREN) ^ newInvocation(target, args)) \
		| atom

	type_reference = type_reference_simple

	type_reference_simple = (qualified_name >> qname) ^ SimpleTypeReference(Name: qname)

	atom = integer | boolean | reference | array_literal | list_literal \
		| string_literal | prefix | null_literal | parenthesized_expression

	parenthesized_expression = (LPAREN, expression >> e, RPAREN) ^ e

	null_literal = NULL ^ [| null |]

	prefix = ((BITWISE_NOT | MINUS) >> op, expression >> e) ^ newPrefixExpression(op, e)

	string_literal = ((SDQS | TDQS | SQS) >> s) ^ newStringLiteral(s)

	array_literal = (LPAREN, (COMMA ^ (items=[])) | (expression_list >> items, (COMMA | "")), RPAREN) ^ newArrayLiteral(items)

	list_literal = (LBRACK, optional_expression_list >> items, RBRACK) ^ newListLiteral(items)

	list_of expression

	list_of declaration

	reference = ID >> r ^ newReference(r)

	integer = NUM >> n ^ newInteger(n)

	boolean = true_literal | false_literal

	true_literal = TRUE ^ [| true |]

	false_literal = FALSE ^ [| false |]

	eol = ++EOL | ~_

	def newSlicing(target as Expression, slices):
		node = SlicingExpression(Target: target)
		for slice in slices: node.Indices.Add(slice)
		return node

	def newSlice(begin as Expression, end as Expression, step as Expression):
		return Slice(begin, end, step)

	def newRValue(items as List):
		if len(items) > 1: return newArrayLiteral(items)
		return items[0]

	def newForStatement(declarations, e as Expression, body as Block):
		node = ForStatement(Iterator: e, Block: body)
		for d in declarations: node.Declarations.Add(d)
		return node

	def newDeclaration(name, type as TypeReference):
		return Declaration(Name: tokenValue(name), Type: type)

	def newDeclarationStatement(d as Declaration,  initializer as Expression):
		return DeclarationStatement(Declaration: d, Initializer: initializer)

	def newIfStatement(condition as Expression, trueBlock as Block):
		return IfStatement(Condition: condition, TrueBlock: trueBlock)

	def newModule(doc, imports, members, stmts):
		m = Module(Documentation: doc)
		for item in imports: m.Imports.Add(item)
		for member in members: m.Members.Add(member)
		for stmt as Statement in stmts: m.Globals.Add(stmt)
		return m

	def newImport(qname as string):
		return Import(Namespace: qname)

	def newInteger(t):
		value = int.Parse(tokenValue(t))
		return IntegerLiteralExpression(Value: value)

	def newMethod(name, parameters, body as Block):
		node = Method(Name: tokenValue(name), Body: body)
		for p in parameters: node.Parameters.Add(p)
		return node

	def newClass(name, members):
		klass = ClassDefinition(Name: tokenValue(name))
		if members is not null:
			for member in members: klass.Members.Add(member)
		return klass

	def newInvocation(target as Expression, args as List):
		mie = MethodInvocationExpression(Target: target)
		for arg in args: mie.Arguments.Add(arg)
		return mie

	def newReference(t):
		return ReferenceExpression(Name: tokenValue(t))

	def newMemberReference(target as Expression, name):
		return MemberReferenceExpression(Target: target, Name: tokenValue(name))

	def newArrayLiteral(items):
		literal = ArrayLiteralExpression()
		for item in items:
			literal.Items.Add(item)
		return literal

	def newListLiteral(items):
		literal = ListLiteralExpression()
		for item in items: literal.Items.Add(item)
		return literal

	def newStringLiteral(s):
		return StringLiteralExpression(Value: tokenValue(s))

	def newInfixExpression(op, l as Expression, r as Expression):
		return BinaryExpression(Operator: binaryOperatorFor(op), Left: l, Right: r)

	def newPrefixExpression(op, e as Expression):
		return UnaryExpression(Operator: unaryOperatorFor(op), Operand: e)

	def unaryOperatorFor(op):
		match tokenValue(op):
			case "not": return UnaryOperatorType.LogicalNot
			case "-": return UnaryOperatorType.UnaryNegation
			case "~": return UnaryOperatorType.OnesComplement

	def binaryOperatorFor(op):
		match tokenValue(op):
			case "is": return BinaryOperatorType.ReferenceEquality
			case "and": return BinaryOperatorType.And
			case "or": return BinaryOperatorType.Or
			case "^": return BinaryOperatorType.ExclusiveOr
			case "+": return BinaryOperatorType.Addition
			case "-": return BinaryOperatorType.Subtraction
			case "*": return BinaryOperatorType.Multiply
			case "**": return BinaryOperatorType.Exponentiation
			case "/": return BinaryOperatorType.Division
			case "%": return BinaryOperatorType.Modulus
			case "=": return BinaryOperatorType.Assign
			case "==": return BinaryOperatorType.Equality
			case "!=": return BinaryOperatorType.Inequality
			case "+=": return BinaryOperatorType.InPlaceAddition
			case "-=": return BinaryOperatorType.InPlaceSubtraction
			case "/=": return BinaryOperatorType.InPlaceDivision
			case "*=": return BinaryOperatorType.InPlaceMultiply
			case "^=": return BinaryOperatorType.InPlaceExclusiveOr

	def newAssignment(l as Expression, r as Expression):
		return [| $l = $r |]

	def newBlock(stmts):
		b = Block()
		for item in stmts:
			b.Statements.Add(item)
		return b

	def prepend(first, tail as List):
		if first is null: return tail
		return [first] + tail

	def buildQName(q, rest):
		return join(tokenValue(t) for t in prepend(q, rest), '.')

namespace Boo.OMeta.Parser

import Boo.OMeta
import Boo.PatternMatching

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

macro infix:

	l, op, r = infix.Arguments

	return ExpressionStatement([| $l = ((($l >> l, $op >> op, $r >> r) ^ newInfixExpression(op, l, r)) | $r) |])

macro infixr:

	l, op, r = infixr.Arguments

	return ExpressionStatement([| $l = ((($r >> l, $op >> op, $l >> r) ^ newInfixExpression(op, l, r)) | $r) |])

macro list_of:
"""
Generates rules for lists of the given expression.

	list_of expression

Expands to something that matches:

	expression (COMMA expression)+
"""

	rule, = list_of.Arguments

	block as Block = list_of.ParentNode

	listRuleName = ReferenceExpression(Name: "${rule}_list")
	listRule = [| $listRuleName = ((($rule >> first), ++((COMMA, $rule >> e) ^ e) >> rest) ^ prepend(first, rest)) | ($rule >> v ^ [v]) |]
	block.Add(listRule)

	optionalRuleName = ReferenceExpression(Name: "optional_${rule}_list")
	optionalListRule = [| $optionalRuleName = $listRuleName | ("" ^ []) |]
	block.Add(optionalListRule)

ometa BooParser < WhitespaceSensitiveTokenizer:

	tokens:
		equality = "=="
		inequality = "!="
		assign = "="
		assign_inplace = "+=" | "-=" | "*=" | "/=" | "%=" | "^="
		xor = "^"
		plus = "+"
		minus = "-"
		exponentiation = "**"
		star = "*"
		division = "/"
		modulus = "%"
		bitwise_not = "~"
		bitwise_shift_left = "<<"
		bitwise_shift_right = ">>"
		bitwise_and = "&"
		bitwise_or = "|"
		num = ++digit
		colon = ":"
		dot = "."
		comma = ","
		lparen = "(", enterWhitespaceAgnosticRegion
		rparen = ")", leaveWhitespaceAgnosticRegion
		lbrack = "[", enterWhitespaceAgnosticRegion
		rbrack = "]", leaveWhitespaceAgnosticRegion
		kw = (keywords >> value, ~(letter | digit)) ^ value
		tdqs = ('"""', ++(~'"""', _) >> s, '"""') ^ s
		sdqs = ('"', ++(~'"', _) >> s, '"') ^ s
		sqs = ("'", ++(~"'", _) >> s, "'") ^ s
		id = ((letter | '_') >> p, --(letter | digit | '_') >> s) ^ makeString(p, s)

	keywords "class", "def", "import", "pass", "return", "true", \
		"false", "and", "or", "as", "not", "if", "is", "null", \
		"for", "in", "yield"

	keyword[expected] = ((KW >> t) and (expected is tokenValue(t))) ^ t

	module = (
		--EOL,
		(docstring >> s | ""),
		--import_declaration >> ids,
		--module_member >> members,
		--stmt >> stmts,
		--whitespace
	) ^ newModule(s, ids, members, stmts)

	docstring = (TDQS >> t, eol) ^ tokenValue(t)

	import_declaration = (IMPORT, qualified_name >> qn, eol) ^ newImport(qn)

	qualified_name = (ID >> qualifier, --((DOT, ID >> n) ^ n) >> suffix)^ buildQName(qualifier, suffix)

	module_member = class_def | method

	class_def = (
		CLASS, ID >> className, beginBlock, class_body >> body, endBlock
	) ^ newClass(className, body)

	beginBlock = COLON, INDENT

	endBlock = DEDENT

	class_body = ((PASS, eol) ^ null) | (++class_member >> members ^ members)

	class_member = method | class_def

	method = (
		DEF, ID >> name, LPAREN, optional_parameter_list >> parameters, RPAREN, block >> body
	) ^ newMethod(name, parameters, body)

	list_of parameter

	parameter = ((ID >> name, AS, type_reference >> type) | (ID >> name)) ^ ParameterDeclaration(Name: tokenValue(name), Type: type)

	block = empty_block | non_empty_block

	empty_block = (beginBlock, (PASS, eol), endBlock) ^ Block()

	non_empty_block = (beginBlock, ++stmt >> stmts, endBlock)  ^ newBlock(stmts)

	stmt = ((stmt_block >> s) | (stmt_line >> s, eol)) ^ s

	stmt_line = (~~(ID, AS), stmt_declaration) | stmt_expression | stmt_return \
		| stmt_yield

	stmt_yield = (YIELD, assignment >> e, stmt_modifier >> m) ^ YieldStatement(Expression: e, Modifier: m)

	stmt_modifier = ((IF, assignment >> e) ^ StatementModifier(Type: StatementModifierType.If, Condition: e)) | ""

	stmt_declaration = (declaration >> d, ASSIGN, expression >> e) ^ newDeclarationStatement(d, e)

	declaration = (ID >> name, ((AS, type_reference >> typeRef) | "")) ^ newDeclaration(name, typeRef)

	stmt_block = stmt_if | stmt_for

	stmt_for = (FOR, declaration_list >> dl, IN, expression >> e, block >> body) ^ newForStatement(dl, e, body)

	stmt_if = (IF, expression >> e, block >> trueBlock) ^ newIfStatement(e, trueBlock)

	stmt_return = (RETURN, optional_expression >> e, stmt_modifier >> m) ^ ReturnStatement(Expression: e, Modifier: m)

	optional_expression = expression | ""

	stmt_expression = (multi_assignment | assignment) >> e ^ ExpressionStatement(Expression: e)

	multi_assignment = (expression >> l, ASSIGN >> op, assignment_list >> items) ^ newInfixExpression(op, l, newRValue(items))

	list_of assignment

	infixr assignment, (ASSIGN | ASSIGN_INPLACE), expression

	expression = or_expression

	infix or_expression, OR, and_expression

	infix and_expression, AND, not_expression

	not_expression = ((NOT >> op, expression >> e) ^ newPrefixExpression(op, e)) | membership_expression

	infix membership_expression, (IN, (NOT, IN)), identity_test_expression

	infix identity_test_expression, ((IS, NOT) ^ IS), comparison

	infix comparison, (EQUALITY | INEQUALITY | IS), bitwise_or_expression

	infix bitwise_or_expression, BITWISE_OR, bitwise_xor_expression

	infix bitwise_xor_expression, XOR, bitwise_and_expression

	infix bitwise_and_expression, BITWISE_AND, bitwise_shift_expression

	infix bitwise_shift_expression, bitwise_shift_op, term

	bitwise_shift_op = (BITWISE_SHIFT_LEFT | BITWISE_SHIFT_RIGHT)

	infix term, (PLUS | MINUS), factor

	infix factor, (STAR | DIVISION | MODULUS), signalled_expression

	signalled_expression = (MINUS, signalled_expression >> e) | bitwise_not_expression

	bitwise_not_expression = (BITWISE_NOT, bitwise_not_expression >> e) | exponentiation_expression

	exponentiation_expression = (EXPONENTIATION, exponentiation_expression >> e) | try_cast

	try_cast = ((try_cast >> e, AS, type_reference >> typeRef) ^ TryCastExpression(Target: e, Type: typeRef)) | member_reference

	member_reference = (member_reference, DOT, ID >> name) | slicing

	slicing = (slicing >> e, LBRACK, expression_list, RBRACK) | invocation

	invocation = ((invocation >> target, LPAREN, optional_expression_list >> args, RPAREN) ^ newInvocation(target, args)) | atom

	type_reference = type_reference_simple

	type_reference_simple = (qualified_name >> qname) ^ SimpleTypeReference(Name: qname)

	atom = integer | boolean | reference | array_literal | list_literal \
		| string_literal | prefix | null_literal | parenthesized_expression

	parenthesized_expression = (LPAREN, expression >> e, RPAREN) ^ e

	null_literal = NULL ^ [| null |]

	prefix = ((BITWISE_NOT | MINUS) >> op, expression >> e) ^ newPrefixExpression(op, e)

	string_literal = ((SDQS | TDQS | SQS) >> s) ^ newStringLiteral(s)

	array_literal = (LPAREN, (COMMA ^ (items=[])) | (expression_list >> items, (COMMA | "")), RPAREN) ^ newArrayLiteral(items)

	list_literal = (LBRACK, optional_expression_list >> items, RBRACK) ^ newListLiteral(items)

	list_of expression

	list_of declaration

	reference = ID >> r ^ newReference(r)

	integer = NUM >> n ^ newInteger(n)

	boolean = true_literal | false_literal

	true_literal = TRUE ^ [| true |]

	false_literal = FALSE ^ [| false |]

	eol = ++EOL | ~_

	def newRValue(items as List):
		if len(items) > 1: return newArrayLiteral(items)
		return items[0]

	def newForStatement(declarations, e as Expression, body as Block):
		node = ForStatement(Iterator: e, Block: body)
		for d in declarations: node.Declarations.Add(d)
		return node

	def newDeclaration(name, type as TypeReference):
		return Declaration(Name: tokenValue(name), Type: type)

	def newDeclarationStatement(d as Declaration,  initializer as Expression):
		return DeclarationStatement(Declaration: d, Initializer: initializer)

	def newIfStatement(condition as Expression, trueBlock as Block):
		return IfStatement(Condition: condition, TrueBlock: trueBlock)

	def newModule(doc, imports, members, stmts):
		m = Module(Documentation: doc)
		for item in imports: m.Imports.Add(item)
		for member in members: m.Members.Add(member)
		for stmt as Statement in stmts: m.Globals.Add(stmt)
		return m

	def newImport(qname as string):
		return Import(Namespace: qname)

	def newInteger(t):
		value = int.Parse(tokenValue(t))
		return IntegerLiteralExpression(Value: value)

	def newMethod(name, parameters, body as Block):
		node = Method(Name: tokenValue(name), Body: body)
		for p in parameters: node.Parameters.Add(p)
		return node

	def newClass(name, members):
		klass = ClassDefinition(Name: tokenValue(name))
		if members is not null:
			for member in members: klass.Members.Add(member)
		return klass

	def newInvocation(target as Expression, args as List):
		mie = MethodInvocationExpression(Target: target)
		for arg in args: mie.Arguments.Add(arg)
		return mie

	def newReference(t):
		return ReferenceExpression(Name: tokenValue(t))

	def newArrayLiteral(items):
		literal = ArrayLiteralExpression()
		for item in items:
			literal.Items.Add(item)
		return literal

	def newListLiteral(items):
		literal = ListLiteralExpression()
		for item in items: literal.Items.Add(item)
		return literal

	def newStringLiteral(s):
		return StringLiteralExpression(Value: tokenValue(s))

	def newInfixExpression(op, l as Expression, r as Expression):
		return BinaryExpression(Operator: binaryOperatorFor(op), Left: l, Right: r)

	def newPrefixExpression(op, e as Expression):
		return UnaryExpression(Operator: unaryOperatorFor(op), Operand: e)

	def unaryOperatorFor(op):
		match tokenValue(op):
			case "not": return UnaryOperatorType.LogicalNot
			case "-": return UnaryOperatorType.UnaryNegation

	def binaryOperatorFor(op):
		match tokenValue(op):
			case "is": return BinaryOperatorType.ReferenceEquality
			case "and": return BinaryOperatorType.And
			case "or": return BinaryOperatorType.Or
			case "^": return BinaryOperatorType.ExclusiveOr
			case "+": return BinaryOperatorType.Addition
			case "-": return BinaryOperatorType.Subtraction
			case "*": return BinaryOperatorType.Multiply
			case "/": return BinaryOperatorType.Division
			case "%": return BinaryOperatorType.Modulus
			case "=": return BinaryOperatorType.Assign
			case "==": return BinaryOperatorType.Equality
			case "!=": return BinaryOperatorType.Inequality
			case "+=": return BinaryOperatorType.InPlaceAddition
			case "-=": return BinaryOperatorType.InPlaceSubtraction
			case "/=": return BinaryOperatorType.InPlaceDivision
			case "*=": return BinaryOperatorType.InPlaceMultiply
			case "^=": return BinaryOperatorType.InPlaceExclusiveOr

	def newAssignment(l as Expression, r as Expression):
		return [| $l = $r |]

	def newBlock(stmts):
		b = Block()
		for item in stmts:
			b.Statements.Add(item)
		return b

	def prepend(first, tail as List):
		if first is null: return tail
		return [first] + tail

	def buildQName(q, rest):
		return join(tokenValue(t) for t in prepend(q, rest), '.')


simplify custom rule application by moving it to the grammar definition

namespace Boo.OMeta.Tests

import Boo.OMeta
import Boo.Lang.PatternMatching
import Boo.Adt
import NUnit.Framework

data Exp = Const(value as int) | Sum(left as Exp, right as Exp)

[TestFixture]
class ObjectMatchingTest:

	[Test]
	def PropertyPatternMatching():
		ometa ConstList:
			option ParseTree
			parse = ++(Const(value) ^ value)

		match ConstList().parse(OMetaInput.For([Const(1), Const(42)])):
			case SuccessfulMatch(Input, Value):
				assert Input.IsEmpty
				Assert.AreEqual([1, 42], Value)

	[Test]
	def PropertyParsing():
		ometa Evaluator:
			eval = const | sum
			const = Const(value) ^ value
			sum = Sum(left: eval >> l as int, right: eval >> r as int) ^ (l + r)

		match Evaluator().eval(OMetaInput.Singleton(Sum(Const(21), Sum(Const(11), Const(10))))):
			case SuccessfulMatch(Input, Value):
				assert Input.IsEmpty
				Assert.AreEqual(42, Value)

	[Test]
	def StringFieldMatchingWithCustomRule():

		data Person(name)

		ometa JohnOrPaulMatcher:

			match = Person(name: string_matching[john_or_paul] >> _)
			john = "John "
			paul = "Paul "
			john_or_paul = john | paul

			string_matching[rule] = $(string_matching(rule, input))

			def string_matching(rule as string, input as OMetaInput):
				match input.Head:
					case s = string():
						match Apply(rule, OMetaInput.For(s)):
							case SuccessfulMatch():
								return SuccessfulMatch(input.Tail, input.Head)
							case FailedMatch(Failure):
								return FailedMatch(input, Failure)
					otherwise:
						return FailedMatch(input, ObjectPatternFailure("'$(input.Head)' is not a string"))

		def john_or_paul(person):
			return JohnOrPaulMatcher().match(OMetaInput.Singleton(person))

		match john_or_paul(Person("John Stewart")):
			case SuccessfulMatch(Input):
				assert Input.IsEmpty

		match john_or_paul(Person("Paul Jones")):
			case SuccessfulMatch(Input):
				assert Input.IsEmpty

		match john_or_paul(Person("Eric")):
			case FailedMatch(Input):
				assert Input.Head == "Eric"

		match john_or_paul(Person(42)):
			case FailedMatch(Input, Failure: ObjectPatternFailure(Pattern)):
				assert Input.Head == 42
				assert Pattern == "'42' is not a string"

namespace Boo.OMeta.Tests

import Boo.OMeta
import Boo.Lang.PatternMatching
import Boo.Adt
import NUnit.Framework

data Exp = Const(value as int) | Sum(left as Exp, right as Exp)

[TestFixture]
class ObjectMatchingTest:

	[Test]
	def PropertyPatternMatching():
		ometa ConstList:
			option ParseTree
			parse = ++(Const(value) ^ value)

		match ConstList().parse(OMetaInput.For([Const(1), Const(42)])):
			case SuccessfulMatch(Input, Value):
				assert Input.IsEmpty
				Assert.AreEqual([1, 42], Value)

	[Test]
	def PropertyParsing():
		ometa Evaluator:
			eval = const | sum
			const = Const(value) ^ value
			sum = Sum(left: eval >> l as int, right: eval >> r as int) ^ (l + r)

		match Evaluator().eval(OMetaInput.Singleton(Sum(Const(21), Sum(Const(11), Const(10))))):
			case SuccessfulMatch(Input, Value):
				assert Input.IsEmpty
				Assert.AreEqual(42, Value)

	[Test]
	def StringFieldMatchingWithCustomRule():

		data Person(name)

		ometa JohnOrPaulMatcher:
			john = "John "
			paul = "Paul "
			john_or_paul = john | paul
			match = Person(name: string[john_or_paul] >> _)
			string[rule] = $(string_matching(rule, input, context))

		def john_or_paul(person):
			return JohnOrPaulMatcher().match(OMetaInput.Singleton(person))

		match john_or_paul(Person("John Stewart")):
			case SuccessfulMatch(Input):
				assert Input.IsEmpty

		match john_or_paul(Person("Paul Jones")):
			case SuccessfulMatch(Input):
				assert Input.IsEmpty

		match john_or_paul(Person("Eric")):
			case FailedMatch(Input):
				assert Input.Head == "Eric"

		match john_or_paul(Person(42)):
			case FailedMatch(Input, Failure: ObjectPatternFailure(Pattern)):
				assert Input.Head == 42
				assert Pattern == "'42' is not a string"

	static def string_matching(rule as string, input as OMetaInput, context as OMetaEvaluationContext):
		match input.Head:
			case s = string():
				match context.Eval(rule, OMetaInput.For(s)):
					case SuccessfulMatch():
						return SuccessfulMatch(input.Tail, input.Head)
					case FailedMatch(Failure):
						return FailedMatch(input, Failure)
			otherwise:
				return FailedMatch(input, ObjectPatternFailure("'$(input.Head)' is not a string"))