camelCase => PascalCase

camelCase => PascalCase

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

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

namespace Boo.Lang.Useful.Attributes

import System.Threading
import Boo.Lang.Compiler
import Boo.Lang.Compiler.Ast
import Boo.Lang.Compiler.Steps
import Boo.Lang.Compiler.TypeSystem

class OnceAttribute(AbstractAstAttribute):
"""
Caches the return value of a method.

@author Sorin Ionescu (sorin.ionescu@gmail.com)
"""
	private class ReturnValueStorageVisitor(DepthFirstVisitor):
	"""
	Replaces returns statements with binary expressions that
	store the values of the return statements.
	"""
		_returnValue as ReferenceExpression

		def constructor(returnValue as ReferenceExpression):
			_returnValue = returnValue

		override def OnReturnStatement(returnStatement as ReturnStatement):
		"""
		Replaces returns statements with binary expressions that
		store the values of the return statements.
		"""
			returnValueAssignment = BinaryExpression(
				LexicalInfo: returnStatement.LexicalInfo,
				Operator: BinaryOperatorType.Assign,
				Left: _returnValue,
				Right: returnStatement.Expression)

			parentNode = returnStatement.ParentNode
			parentNode.Replace(
				returnStatement,
				ExpressionStatement(returnValueAssignment))

	_method as Method
	_methodLock as Field
	_cached as Field
	_returnValue as Field

	override def Dispose():
	"""
	Dont get rid of the context because we'll need it later.
	"""
		pass

	override def Apply(node as Node):
	"""
	Applies the <OnceAttribute> to [node].

	Parameters:
		node
			The node to apply the <OnceAttribute> to.
	"""
		assert node isa Method

		_method = node

		CreateCachedField()
		CreateMethodLockField()
		PrepareMethodBody()

		Context.Parameters.Pipeline.AfterStep += def(
			sender,
			e as CompilerStepEventArgs):

			return if not e.Step isa ProcessMethodBodies

			# Void methods cannot be cached.
			assert _method.ReturnType.Entity is not \
				self.TypeSystemServices.VoidType, "once attribute cannot be applied to void methods"

			CreateReturnValueField()
			PostProcessMethod()

	def CreateReturnValueField():
	"""
	Creates the field that stores the return value of the cached method.
	"""
		template = self.CodeBuilder.CreateField('field', _method.ReturnType.Entity)
		_returnValue = AddField(template, "___${_method.Name}_returnValue")

	def CreateCachedField():
	"""
	Creates the cached flag.

	Remarks:
		The flag is used to check whether the method has been cached.
	"""
		template = ast:
			private field as bool
		_cached = AddField(template, "___${_method.Name}_cached")

	def CreateMethodLockField():
	"""
	Creates the lock field.

	Remarks:
		The field is used to lock on when the operatation is thread safe.
	"""
		template = ast:
			private field as object = object()
		_methodLock = AddField(template, "___${_method.Name}_lock")

	def AddField(template as Field, name as string):
		template.LexicalInfo = self.LexicalInfo
		template.Name = name
		template.Modifiers = TypeMemberModifiers.Private
		template.Modifiers |= TypeMemberModifiers.Static if IsStaticMethod()
		_method.DeclaringType.Members.Add(template)
		return template

	def IsStaticMethod():
		return _method.IsStatic or _method.ParentNode isa Module

	def PrepareMethodBody():
		newMethodBodyTemplate = ast:
			if not cached:
				System.Threading.Monitor.Enter(methodLock)
				try:
					if not cached:
						oldMethodBody
						cached = true
				ensure:
					System.Threading.Monitor.Exit(methodLock)

		ReplaceReferences(newMethodBodyTemplate, 'cached', _cached.Name)
		ReplaceReferences(newMethodBodyTemplate, 'methodLock', _methodLock.Name)
		newMethodBodyTemplate.ReplaceNodes(
			MacroStatement(Name: 'oldMethodBody'),
			_method.Body)

		_method.Body = Block()
		_method.Body.Add(newMethodBodyTemplate)

	def ReplaceReferences(node as Node, what as string, value as string):
		node.ReplaceNodes(
			ReferenceExpression(what),
			ReferenceExpression(value))

	def PostProcessMethod():
	"""
	Visit the node and replace return statements
	with binary expressions that store the return
	statement values.
	Add a single return statement at the end
	to return the cached value.
	"""
		_method.Accept(
				ReturnValueStorageVisitor(
					self.CodeBuilder.CreateReference(_returnValue)))
		_method.Body.Add(
			ReturnStatement(
				LexicalInfo: LexicalInfo,
				Expression: self.CodeBuilder.CreateReference(_returnValue)))

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

namespace Boo.Lang.Useful.Attributes

import System.Threading
import Boo.Lang.Compiler
import Boo.Lang.Compiler.Ast
import Boo.Lang.Compiler.Steps
import Boo.Lang.Compiler.TypeSystem

class OnceAttribute(AbstractAstAttribute):
"""
Caches the return value of a method.

@author Sorin Ionescu (sorin.ionescu@gmail.com)
"""
	private class ReturnValueStorageVisitor(DepthFirstVisitor):
	"""
	Replaces returns statements with binary expressions that
	store the values of the return statements.
	"""
		_returnValue as ReferenceExpression

		def constructor(returnValue as ReferenceExpression):
			_returnValue = returnValue

		override def OnReturnStatement(returnStatement as ReturnStatement):
		"""
		Replaces returns statements with binary expressions that
		store the values of the return statements.
		"""
			returnValueAssignment = BinaryExpression(
				LexicalInfo: returnStatement.LexicalInfo,
				Operator: BinaryOperatorType.Assign,
				Left: _returnValue,
				Right: returnStatement.Expression)

			parentNode = returnStatement.ParentNode
			parentNode.Replace(
				returnStatement,
				ExpressionStatement(returnValueAssignment))

	_method as Method
	_methodLock as Field
	_cached as Field
	_returnValue as Field

	override def Dispose():
	"""
	Dont get rid of the context because we'll need it later.
	"""
		pass

	override def Apply(node as Node):
	"""
	Applies the <OnceAttribute> to [node].

	Parameters:
		node
			The node to apply the <OnceAttribute> to.
	"""
		assert node isa Method

		_method = node

		CreateCachedField()
		CreateMethodLockField()
		PrepareMethodBody()

		Context.Parameters.Pipeline.AfterStep += def(
			sender,
			e as CompilerStepEventArgs):

			return if not e.Step isa ProcessMethodBodies

			# Void methods cannot be cached.
			assert _method.ReturnType.Entity is not \
				self.TypeSystemServices.VoidType, "once attribute cannot be applied to void methods"

			CreateReturnValueField()
			PostProcessMethod()

	def CreateReturnValueField():
	"""
	Creates the field that stores the return value of the cached method.
	"""
		template = self.CodeBuilder.CreateField('field', _method.ReturnType.Entity)
		_returnValue = AddField(template, "___${_method.Name}_returnValue")

	def CreateCachedField():
	"""
	Creates the cached flag.

	Remarks:
		The flag is used to check whether the method has been cached.
	"""
		template = ast:
			private field as bool
		_cached = AddField(template, "___${_method.Name}_cached")

	def CreateMethodLockField():
	"""
	Creates the lock field.

	Remarks:
		The field is used to lock on when the operatation is thread safe.
	"""
		template = ast:
			private field as object = object()
		_methodLock = AddField(template, "___${_method.Name}_lock")

	def AddField(template as Field, name as string):
		template.LexicalInfo = self.LexicalInfo
		template.Name = name
		template.Modifiers = TypeMemberModifiers.Private
		template.Modifiers |= TypeMemberModifiers.Static if IsStaticMethod()
		_method.DeclaringType.Members.Add(template)
		return template

	def IsStaticMethod():
		return _method.IsStatic or _method.ParentNode isa Module

	def PrepareMethodBody():
		newMethodBodyTemplate = ast:
			if not cached:
				System.Threading.Monitor.Enter(methodLock)
				try:
					if not cached:
						oldMethodBody
						cached = true
				ensure:
					System.Threading.Monitor.Exit(methodLock)

		replaceReferences(newMethodBodyTemplate, 'cached', _cached.Name)
		replaceReferences(newMethodBodyTemplate, 'methodLock', _methodLock.Name)
		newMethodBodyTemplate.ReplaceNodes(
			MacroStatement(Name: 'oldMethodBody'),
			_method.Body)

		_method.Body = Block()
		_method.Body.Add(newMethodBodyTemplate)

	def replaceReferences(node as Node, what as string, value as string):
		node.ReplaceNodes(
			ReferenceExpression(what),
			ReferenceExpression(value))

	def PostProcessMethod():
	"""
	Visit the node and replace return statements
	with binary expressions that store the return
	statement values.
	Add a single return statement at the end
	to return the cached value.
	"""
		_method.Accept(
				ReturnValueStorageVisitor(
					self.CodeBuilder.CreateReference(_returnValue)))
		_method.Body.Add(
			ReturnStatement(
				LexicalInfo: LexicalInfo,
				Expression: self.CodeBuilder.CreateReference(_returnValue)))


always declare the rule variables

always declare the rule variables

namespace Boo.Pegs

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

def expand(e as Expression) as Expression:
	match e:
		case ArrayLiteralExpression(Items: items):
			return expandArguments([| sequence() |], items)

		case ListLiteralExpression(Items: items):
			return expandArguments([| choice() |], items)

		case UnaryExpression(Operator: UnaryOperatorType.Increment, Operand: expression):
			return [| one_or_many($(expand(expression))) |]

		case UnaryExpression(Operator: UnaryOperatorType.Decrement, Operand: expression):
			return [| zero_or_many($(expand(expression))) |]

		case UnaryExpression(Operator: UnaryOperatorType.OnesComplement, Operand: expression):
			return [| choice($(expand(expression)), empty()) |]

		case UnaryExpression(Operator: UnaryOperatorType.LogicalNot, Operand: expression):
			return [| not_predicate($(expand(expression))) |]

		case s=StringLiteralExpression():
			return [| terminal($s) |]

		case BinaryExpression(
				Operator: BinaryOperatorType.BitwiseOr,
				Left: l,
				Right: r):

			return [| choice($(expand(l)), $(expand(r))) |]

		case BinaryExpression(
				Operator: BinaryOperatorType.Subtraction,
				Left: l=ReferenceExpression(),
				Right: r=ReferenceExpression()):
			return [| char_range($(charFor(l)), $(charFor(r))) |]

		case block=BlockExpression():
			template = [| { context as PegContext | _ } |]
			block.Parameters = template.Parameters
			return SpliceExpander().Expand([| action($block) |])

		otherwise:
			return e

class SpliceExpander(DepthFirstTransformer):
	def Expand(node as Expression):
		return self.VisitNode(node)

	override def LeaveSpliceExpression(node as SpliceExpression):
		match node.Expression:
			case function=ReferenceExpression():
				mie = node.ParentNode as MethodInvocationExpression
				if mie is not null and mie.Target is node:
					mie.Arguments.Insert(0, [| context |])
					ReplaceCurrentNode(node.Expression)
				else:
					ReplaceCurrentNode([| $function(context) |])

def charFor(e as ReferenceExpression):
	return CharLiteralExpression(e.LexicalInfo, e.Name)

def expandArguments(invocation as MethodInvocationExpression, args):
	for arg in args:
		invocation.Arguments.Add(expand(arg))
	return invocation

macro peg:
/*"""
Usage:

	peg MyGrammar:

		// sequence
		MyRule1 = E1, E2, EN

		// choice
		MyRule2 = [Choice1, Choice2, ChoiceN]
		MyRule3 = Choice1 | Choice2

		// repetition (one or many)
		MyRule4 = ++E

		// repetition (zero or many)
		MyRule5 = --E

		// optional
		MyRule6 = ~OptionalPrefix, Suffix

		// semantic action
		MyRule7 = { print $text }

Example:

	peg miniboo:
		Module = Spacing, ++Class, EndOfFile
		Class = CLASS, Identifier, Begin, ++Member, End
		Member = DEF, Identifier, LPAREN, RPAREN, Block
		Block = Begin, ++Statement, End
		Statement = Invocation
		Invocation = ++Expression
		Expression = Identifier | String
		String = "'", ++(not "'"), "'", Spacing
		Identifier = ++[a-z, A-Z], OptionalSpacing
		Begin = ":", Spacing
		End = empty()
		Spacing = ++[' ', '\t', '\r', '\n']
		OptionalSpacing = ~Spacing
		CLASS = "class", Spacing
		DEF = "def", Spacing
		LPAREN = "(", OptionalSpacing
		RPAREN = ")", OptionalSpacing
		EndOfFile = not any()
"""*/

	rules = []
	for node in peg.Block.Statements:
		match node:
			case ExpressionStatement(
					Expression: BinaryExpression(
						Operator: BinaryOperatorType.Assign,
						Left: rule = ReferenceExpression(),
						Right: expression)):

				rules.Add((rule, expression))

	result = Block()

	# declare all rules
	for rule as ReferenceExpression, _ in rules:
		decl = DeclarationStatement(
			Declaration(Name: rule.Name, Type: SimpleTypeReference("PegRule")),
			[| PegRule() |])
		result.Add(decl)

	# expand all the expressions
	for rule as ReferenceExpression, expression as Expression in rules:
		try:
			result.Add([| $rule.Expression = $(expand(expression)) |])
		except x:
			Context.Errors.Add(CompilerErrorFactory.MacroExpansionError(rule, x))

	return result

namespace Boo.Pegs

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

def expand(e as Expression) as Expression:
	match e:
		case ArrayLiteralExpression(Items: items):
			return expandArguments([| sequence() |], items)

		case ListLiteralExpression(Items: items):
			return expandArguments([| choice() |], items)

		case UnaryExpression(Operator: UnaryOperatorType.Increment, Operand: expression):
			return [| one_or_many($(expand(expression))) |]

		case UnaryExpression(Operator: UnaryOperatorType.Decrement, Operand: expression):
			return [| zero_or_many($(expand(expression))) |]

		case UnaryExpression(Operator: UnaryOperatorType.OnesComplement, Operand: expression):
			return [| choice($(expand(expression)), empty()) |]

		case UnaryExpression(Operator: UnaryOperatorType.LogicalNot, Operand: expression):
			return [| not_predicate($(expand(expression))) |]

		case s=StringLiteralExpression():
			return [| terminal($s) |]

		case BinaryExpression(
				Operator: BinaryOperatorType.BitwiseOr,
				Left: l,
				Right: r):

			return [| choice($(expand(l)), $(expand(r))) |]

		case BinaryExpression(
				Operator: BinaryOperatorType.Subtraction,
				Left: l=ReferenceExpression(),
				Right: r=ReferenceExpression()):
			return [| char_range($(charFor(l)), $(charFor(r))) |]

		case block=BlockExpression():
			template = [| { context as PegContext | _ } |]
			block.Parameters = template.Parameters
			return SpliceExpander().Expand([| action($block) |])

		otherwise:
			return e

class SpliceExpander(DepthFirstTransformer):
	def Expand(node as Expression):
		return self.VisitNode(node)

	override def LeaveSpliceExpression(node as SpliceExpression):
		match node.Expression:
			case function=ReferenceExpression():
				mie = node.ParentNode as MethodInvocationExpression
				if mie is not null and mie.Target is node:
					mie.Arguments.Insert(0, [| context |])
					ReplaceCurrentNode(node.Expression)
				else:
					ReplaceCurrentNode([| $function(context) |])

def charFor(e as ReferenceExpression):
	return CharLiteralExpression(e.LexicalInfo, e.Name)

def expandArguments(invocation as MethodInvocationExpression, args):
	for arg in args:
		invocation.Arguments.Add(expand(arg))
	return invocation

macro peg:
/*"""
Usage:

	peg MyGrammar:

		// sequence
		MyRule1 = E1, E2, EN

		// choice
		MyRule2 = [Choice1, Choice2, ChoiceN]
		MyRule3 = Choice1 | Choice2

		// repetition (one or many)
		MyRule4 = ++E

		// repetition (zero or many)
		MyRule5 = --E

		// optional
		MyRule6 = ~OptionalPrefix, Suffix

		// semantic action
		MyRule7 = { print $text }

Example:

	peg miniboo:
		Module = Spacing, ++Class, EndOfFile
		Class = CLASS, Identifier, Begin, ++Member, End
		Member = DEF, Identifier, LPAREN, RPAREN, Block
		Block = Begin, ++Statement, End
		Statement = Invocation
		Invocation = ++Expression
		Expression = Identifier | String
		String = "'", ++(not "'"), "'", Spacing
		Identifier = ++[a-z, A-Z], OptionalSpacing
		Begin = ":", Spacing
		End = empty()
		Spacing = ++[' ', '\t', '\r', '\n']
		OptionalSpacing = ~Spacing
		CLASS = "class", Spacing
		DEF = "def", Spacing
		LPAREN = "(", OptionalSpacing
		RPAREN = ")", OptionalSpacing
		EndOfFile = not any()
"""*/

	rules = []
	for node in peg.Block.Statements:
		match node:
			case ExpressionStatement(
					Expression: BinaryExpression(
						Operator: BinaryOperatorType.Assign,
						Left: rule = ReferenceExpression(),
						Right: expression)):

				rules.Add((rule, expression))

	result = Block()

	# declare all rules
	for rule as ReferenceExpression, _ in rules:
		result.Add([| $rule = PegRule() |])

	# expand all the expressions
	for rule as ReferenceExpression, expression as Expression in rules:
		try:
			result.Add([| $rule.Expression = $(expand(expression)) |])
		except x:
			Context.Errors.Add(CompilerErrorFactory.MacroExpansionError(rule, x))

	return result


oops

oops

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

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

namespace Boo.Lang.Extensions

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

class MacroMacro(LexicalInfoPreservingGeneratorMacro):

	override protected def ExpandGeneratorImpl(macro as MacroStatement) as Node*:
		if len(macro.Arguments) != 1 or macro.Arguments[0].NodeType != NodeType.ReferenceExpression:
			raise System.ArgumentException("Usage: macro <reference>", "reference")
		yield CreateMacroType(macro)

	override protected def ExpandImpl(macro as MacroStatement):
		raise System.NotImplementedException()

	private static def PascalCase(name as string) as string:
		return char.ToUpper(name[0]) + name[1:]

	private static def CreateMacroType(macro as MacroStatement) as ClassDefinition:
		name = (macro.Arguments[0] as ReferenceExpression).Name
		if YieldFinder(macro).Found:
			macroType = CreateNewStyleMacroType(name, macro)
		else:
			macroType = CreateOldStyleMacroType(name, macro)

		#add parent macro(s) accessor(s)
		parent = macro.GetAncestor[of MacroStatement]()
		while parent is not null:
			continue if macro.Name != "macro"
			name = (parent.Arguments[0] as ReferenceExpression).Name
			if not macroType.Members[name]:
				for accessor in CreateParentMacroAccessor(parent, name):
					macroType.Members.Add(accessor)
			parent = parent.GetAncestor[of MacroStatement]()

		return macroType

	#BOO-1077 style
	private static def CreateNewStyleMacroType(name as string, macro as MacroStatement) as ClassDefinition:
		arg = ReferenceExpression(name)
		return [|
				final class $(PascalCase(name) + "Macro") (Boo.Lang.Compiler.LexicalInfoPreservingGeneratorMacro):
					private __macro as Boo.Lang.Compiler.Ast.MacroStatement
					def constructor():
						super()
					def constructor(context as Boo.Lang.Compiler.CompilerContext):
						raise System.ArgumentNullException("context") if not context
						super(context)
					override protected def ExpandGeneratorImpl($name as Boo.Lang.Compiler.Ast.MacroStatement) as Boo.Lang.Compiler.Ast.Node*:
						raise System.ArgumentNullException($name) if not $(macro.Arguments[0])
						self.__macro = $arg
						$(macro.Body)
					[System.Runtime.CompilerServices.CompilerGeneratedAttribute]
					override protected def ExpandImpl($name as Boo.Lang.Compiler.Ast.MacroStatement) as Boo.Lang.Compiler.Ast.Statement:
						raise System.NotImplementedException("Boo installed version is older than the new macro syntax '${$(name)}' uses. Read BOO-1077 for more info.")
			|]

	private static def CreateOldStyleMacroType(name as string, macro as MacroStatement) as ClassDefinition:
		arg = ReferenceExpression(name)
		return [|
				final class $(PascalCase(name) + "Macro") (Boo.Lang.Compiler.LexicalInfoPreservingMacro):
					private __macro as Boo.Lang.Compiler.Ast.MacroStatement
					def constructor():
						super()
					def constructor(context as Boo.Lang.Compiler.CompilerContext):
						raise System.ArgumentNullException("context") if not context
						super(context)
					override protected def ExpandImpl($name as Boo.Lang.Compiler.Ast.MacroStatement) as Boo.Lang.Compiler.Ast.Statement:
						raise System.ArgumentNullException($name) if not $(macro.Arguments[0])
						self.__macro = $arg
						$(macro.Body)
			|]

	private static def CreateParentMacroAccessor(macro as MacroStatement, name as string):
		cacheField = [|
			private $("__" + name) as Boo.Lang.Compiler.Ast.MacroStatement
		|]
		yield cacheField

		cacheFieldRef = ReferenceExpression(cacheField.Name)
		yield [|
			[System.Runtime.CompilerServices.CompilerGeneratedAttribute]
			private $name:
				get:
					$cacheFieldRef = __macro.GetParentMacroByName($name) unless $cacheFieldRef
					return $cacheFieldRef
		|]

	private final class YieldFinder(DepthFirstVisitor, ITypeMemberStatementVisitor):
		private _found = false
		private _macroDepth = 0

		def constructor(macro as MacroStatement):
			macro.Accept(self)

		Found:
			get: return _found

		def OnTypeMemberStatement(node as TypeMemberStatement):
			pass

		override def OnYieldStatement(node as YieldStatement):
			_found = true if _macroDepth == 1

		override def EnterMacroStatement(node as MacroStatement) as bool:
			_macroDepth++
			return true

		override def LeaveMacroStatement(node as MacroStatement):
			_macroDepth--

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

namespace Boo.Lang.Extensions

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

class MacroMacro(LexicalInfoPreservingGeneratorMacro):

	override protected def ExpandGeneratorImpl(macro as MacroStatement) as Node*:
		if len(macro.Arguments) != 1 or macro.Arguments[0].NodeType != NodeType.ReferenceExpression:
			raise System.ArgumentException("Usage: macro <reference>", "reference")
		yield CreateMacroType(macro)

	override protected def ExpandImpl(macro as MacroStatement):
		raise System.NotImplementedException()

	private static def PascalCase(name as string) as string:
		return char.ToUpper(name[0]) + name[1:]

	private static def CreateMacroType(macro as MacroStatement) as ClassDefinition:
		name = (macro.Arguments[0] as ReferenceExpression).Name
		if YieldFinder(macro).Found:
			macroType = CreateNewStyleMacroType(name, macro)
		else:
			macroType = CreateOldStyleMacroType(name, macro)

		#add parent macro(s) accessor(s)
		parent = macro.GetAncestor[of MacroStatement]()
		while parent is not null:
			continue if macro.Name != "macro"
			name = (parent.Arguments[0] as ReferenceExpression).Name
			if not macroType.Members[name]:
				for accessor in CreateParentMacroAccessor(parent, name):
					macroType.Members.Add(accessor)
			parent = parent.GetAncestor[of MacroStatement]()

		return macroType

	#BOO-1077 style
	private static def CreateNewStyleMacroType(name as string, macro as MacroStatement) as ClassDefinition:
		arg = ReferenceExpression(name)
		return [|
				final class $(PascalCase(name) + "Macro") (Boo.Lang.Compiler.LexicalInfoPreservingGeneratorMacro):
					private __macro as Boo.Lang.Compiler.Ast.MacroStatement
					def constructor():
						super()
					def constructor(context as Boo.Lang.Compiler.CompilerContext):
						raise System.ArgumentNullException("context") if not context
						super(context)
					override protected def ExpandGeneratorImpl($name as Boo.Lang.Compiler.Ast.MacroStatement) as Boo.Lang.Compiler.Ast.Node*:
						raise System.ArgumentNullException($name) if not $(macro.Arguments[0])
						self.__macro = $arg
						$(macro.Body)
					[System.Runtime.CompilerServices.CompilerGeneratedAttribute]
					override protected def ExpandImpl($name as Boo.Lang.Compiler.Ast.MacroStatement) as Boo.Lang.Compiler.Ast.Statement:
						raise System.NotImplementedException("Boo installed version is older than the new macro syntax '${$(name)}' uses. Read BOO-1077 for more info.")
			|]

	private static def CreateOldStyleMacroType(name as string, macro as MacroStatement) as ClassDefinition:
		arg = ReferenceExpression(name)
		return [|
				final class $(PascalCase(name) + "Macro") (Boo.Lang.Compiler.LexicalInfoPreservingMacro):
					private __macro as Boo.Lang.Compiler.Ast.MacroStatement
					def constructor():
						super()
					def constructor(context as Boo.Lang.Compiler.CompilerContext):
						raise System.ArgumentNullException("context") if not context
						super(context)
					override protected def ExpandImpl($name as Boo.Lang.Compiler.Ast.MacroStatement) as Boo.Lang.Compiler.Ast.Statement:
						raise System.ArgumentNullException($name) if not $(macro.Arguments[0])
						self.__macro = $arg
						$(macro.Body)
			|]

	private static def CreateParentMacroAccessor(macro as MacroStatement, name as string):
		cacheField = [|
			private $("__" + name) as Boo.Lang.Compiler.AstMacroStatement
		|]
		yield cacheField

		cacheFieldRef = ReferenceExpression(cacheField.Name)
		yield [|
			[System.Runtime.CompilerServices.CompilerGeneratedAttribute]
			private $name:
				get:
					$cacheFieldRef = __macro.GetParentMacroByName($name) unless $cacheFieldRef
					return $cacheFieldRef
		|]

	private final class YieldFinder(DepthFirstVisitor, ITypeMemberStatementVisitor):
		private _found = false
		private _macroDepth = 0

		def constructor(macro as MacroStatement):
			macro.Accept(self)

		Found:
			get: return _found

		def OnTypeMemberStatement(node as TypeMemberStatement):
			pass

		override def OnYieldStatement(node as YieldStatement):
			_found = true if _macroDepth == 1

		override def EnterMacroStatement(node as MacroStatement) as bool:
			_macroDepth++
			return true

		override def LeaveMacroStatement(node as MacroStatement):
			_macroDepth--


code gardening

code gardening

namespace Boo.OMeta

import Boo.Adt

let EndOfInput = EndOfInputFailure()

data OMetaFailure() = \
	EndOfInputFailure() \
	| PredicateFailure(Predicate as string) \
	| NegationFailure(Predicate as string) \
	| LeftRecursionFailure() \
	| ExpectedValueFailure(Expected as object) \
	| ObjectPatternFailure(Pattern as string) \
	| ChoiceFailure(Failures as Boo.Lang.List[of FailedMatch]) \
	| RuleFailure(Rule as string, Reason as OMetaFailure)


semantic predicate should backtrack too

semantic predicate should backtrack too

namespace Boo.OMeta

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

class OMetaMacroRuleProcessor:

	_ruleName as string
	_collectingParseTree as DynamicVariable[of bool]
	_optionParseTree as bool

	def constructor(ruleName as string, options as List):
		_ruleName = ruleName
		_optionParseTree = "ParseTree" in options
		_collectingParseTree = DynamicVariable[of bool](_optionParseTree)

	def expand(e as Expression, *args as (Expression)) as Block:

		input = [| input_ |]
		block = Block(LexicalInfo: e.LexicalInfo)
		for arg in args:
			code = [|
				block:
					lastMatch = any($input)
					smatch = lastMatch as SuccessfulMatch
					if smatch is null: return lastMatch
					$input = smatch.Input
					$arg = smatch.Value
			|].Block
			block.Add(code)

		expand block, e, input, [| lastMatch |]
		block.Add([| return lastMatch |])

		return block

	def expand(e as Expression, input as Expression, lastMatch as ReferenceExpression) as Block:
		block = Block()
		expand block, e, input, lastMatch
		return block

	def expandChoices(block as Block, choices as List, input as Expression, lastMatch as ReferenceExpression):
		currentBlock = block

		oldInput = uniqueName()
		failureList = uniqueName()
		currentBlock.Add([| $failureList = Boo.Lang.List[of FailedMatch]($(len(choices))) |])
		currentBlock.Add([| $oldInput = $input |])
		for choice in choices:
			expand currentBlock, choice, oldInput, lastMatch
			code = [|
				if $lastMatch isa FailedMatch:
					$failureList.Add($lastMatch)
			|]
			currentBlock.Add(code)
			currentBlock = code.TrueBlock
		currentBlock.Add([| $lastMatch = FailedMatch($oldInput, ChoiceFailure($failureList)) |])

	def resultAppend(result as Expression):
		if collectingParseTree:
			return [| $result.Add(smatch.Value) if smatch.Value is not null |]
		return ExpressionStatement([| $result = smatch.Value |])

	def expandRepetition(block as Block, e as Expression, input as Expression, lastMatch as ReferenceExpression):

		temp = lastMatch #uniqueName()
		result = uniqueName()
		block.Add(expand(e, input, temp))
		block.Add([| smatch = $temp as SuccessfulMatch |])
		code = [|
			if smatch is not null:
				$(resultAppend(result))
				$(expandRepetitionLoop(e, [| $temp.Input |], temp, result))
		|]
		if collectingParseTree:
			code.TrueBlock.Insert(0, [| $result = [] |])
		block.Add(code)

	def expandRepetitionLoop(e as Expression, input as Expression, lastMatch as ReferenceExpression, result as Expression):
		tempInput = uniqueName()
		code = [|
			block:
				$tempInput = $input
				while true:
					$(expand(e, tempInput, lastMatch))
					smatch = $lastMatch as SuccessfulMatch
					break if smatch is null
					$tempInput = smatch.Input
					$(resultAppend(result))

				$lastMatch = SuccessfulMatch($tempInput, $result)
		|]
		return code.Block

	def collectChoices(choices as List, e as Expression):
		match e:
			case [| $l | $r |]:
				collectChoices choices, l
				collectChoices choices, r
			otherwise:
				choices.Add(e)

	collectingParseTree:
		get: return _collectingParseTree.Value

	def expandSequence(block as Block, sequence as ExpressionCollection, input as Expression, lastMatch as ReferenceExpression):

		if _optionParseTree and collectingParseTree:
			expandSequenceWithParseTree block, sequence, input, lastMatch
		else:
			expandSequenceWithoutParseTree block, sequence, input, lastMatch

	def expandSequenceWithoutParseTree(block as Block, sequence as ExpressionCollection, input as Expression, lastMatch as ReferenceExpression):

		currentBlock = block
		for item in sequence.ToArray()[:-1]:
			expand currentBlock, item, input, lastMatch
			input = [| $lastMatch.Input |]
			code = [|
				if $lastMatch isa SuccessfulMatch:
					pass
			|]
			currentBlock.Add(code)
			currentBlock = code.TrueBlock
		expand currentBlock, sequence[-1], input, lastMatch

	def expandSequenceWithParseTree(block as Block, sequence as ExpressionCollection, input as Expression, lastMatch as ReferenceExpression):

		result = uniqueName()
		currentBlock = block
		currentBlock.Add([| $result = [] |])

		for item in sequence:
			expand currentBlock, item, input, lastMatch
			currentBlock.Add([| smatch = $lastMatch as SuccessfulMatch |])
			code = [|
				if smatch is not null:
					$(resultAppend(result))
			|]
			currentBlock.Add(code)
			currentBlock = code.TrueBlock
			input = [| $lastMatch.Input |]

		currentBlock.Add([| $lastMatch = SuccessfulMatch(smatch.Input, $result) |])

	def expandNegation(block as Block, rule as Expression, input as Expression, lastMatch as ReferenceExpression):
		oldInput = uniqueName()
		block.Add([| $oldInput = $input |])

		_collectingParseTree.With(false):
			expand block, rule, input, lastMatch
		block.Add([| smatch = $lastMatch as SuccessfulMatch |])
		code = [|
			if smatch is null:
				$lastMatch = SuccessfulMatch($oldInput, null)
			else:
				$lastMatch = FailedMatch($oldInput, NegationFailure($(rule.ToCodeString())))
		|]
		block.Add(code)
		return code

	def processVariables(e as Expression, input as Expression):
		e.ReplaceNodes([| input |], [| $input |])
		return e

	def expand(block as Block, e as Expression, input as Expression, lastMatch as ReferenceExpression):
		match e:
			case SpliceExpression(Expression: rule):
				block.Add([| $lastMatch = $(processVariables(rule, input)) |])

			case [| $rule[$arg] |]:
				newInput = uniqueName()
				block.Add([| $newInput = OMetaInput.Prepend($arg, $input) |])
				expand block, rule, newInput, lastMatch

			case [| $pattern and $predicate |]:
				oldInput = uniqueName()
				block.Add([| $oldInput = $input |])
				expand block, pattern, input, lastMatch
				checkPredicate = [|
					if $lastMatch isa SuccessfulMatch and not $(processVariables(predicate, input)):
						$lastMatch = FailedMatch($oldInput, PredicateFailure($(predicate.ToCodeString())))
				|]
				block.Add(checkPredicate)

			case [| $pattern ^ $value |]:
				_collectingParseTree.With(false):
					expand block, pattern, input, lastMatch
					code = [|
						block:
							smatch = $lastMatch as SuccessfulMatch
							if smatch is not null:
								$lastMatch = SuccessfulMatch(smatch.Input, $(processVariables(value, input)))
					|].Block
					block.Add(code)

			case [| $pattern >> $variable |]:
				_collectingParseTree.With(true):
					expand block, pattern, input, lastMatch
					match variable:
						case [| $name as $typeref |]:
							code = [|
								block:
									smatch = $lastMatch as SuccessfulMatch
									if smatch is not null:
										$name = cast($typeref, smatch.Value)
							|].Block
						otherwise:
							code = [|
								block:
									smatch = $lastMatch as SuccessfulMatch
									if smatch is not null:
										$variable = smatch.Value
							|].Block
					block.Add(code)

			case [| $_ | $_ |]:
				choices = []
				collectChoices choices, e
				expandChoices block, choices, input, lastMatch

			case StringLiteralExpression(Value: v):
				if len(v) == 0:
					block.Add([| $lastMatch = SuccessfulMatch($input, null) |])
				elif len(v) == 1:
					block.Add([| $lastMatch = character($input, $(CharLiteralExpression(e.LexicalInfo, v[0]))) |])
				else:
					block.Add([| $lastMatch = characters($input, $e) |])

			case [| ++$rule |]:
				expandRepetition block, rule, input, lastMatch

			case [| --$rule |]:
				result = uniqueName()
				block.Add([| $result = [] |]) if collectingParseTree
				block.Add(expandRepetitionLoop(rule, input, lastMatch, result))

			case [| ~$rule |]:
				expandNegation block, rule, input, lastMatch

			case ReferenceExpression(Name: name):
				block.Add([| $lastMatch = context.Eval($name, $input) |])

			case [| super |]:
				block.Add([| $lastMatch = SuperApply(context, $_ruleName, $input) |])

			case [| $_() |]:
				rules = processObjectPatternRules(e)
				condition = PatternExpander().expand([| smatch.Value |], e)
				code = [|
					block:
						$lastMatch = any($input)
						smatch = $lastMatch as SuccessfulMatch
						if smatch is not null:
							if $condition:
								$(expandObjectPatternRules(rules, lastMatch))
							else:
								$lastMatch = FailedMatch($input, ObjectPatternFailure($(e.ToCodeString())))
				|].Block
				block.Add(code)

			case ArrayLiteralExpression(Items: items):
				match items[0]:
					case [| ~$rule |]:
						negation = expandNegation(block, rule, input, lastMatch)
						if len(items) > 2:
							expandSequence negation.TrueBlock, items.PopRange(1), input, lastMatch
						else:
							expand negation.TrueBlock, items[1], input, lastMatch
					otherwise:
						expandSequence block, items, input, lastMatch

	def processObjectPatternRules(pattern as Expression):
		rules = []
		processObjectPatternRules rules, pattern
		return rules

	def expandObjectPatternRules(rules, lastMatch as Expression) as Block:
		block = Block()
		input = uniqueName()

		currentBlock = block
		for temp as Expression, rule as Expression in rules:
			block.Add([| $input = OMetaInput.Singleton($temp) |])
			expand block, rule, input, lastMatch
			code = [|
				if $lastMatch isa SuccessfulMatch:
					pass
			|]
			currentBlock.Add(code)
			currentBlock = code.TrueBlock
		return block

	def processObjectPatternRules(rules as List, pattern as MethodInvocationExpression):
		for arg in pattern.NamedArguments:
			match arg.Second:
				case [| $_ >> $_ |]:
					temp = uniqueName()
					rules.Add((temp, arg.Second))
					arg.Second = temp
				otherwise:
					pass

namespace Boo.OMeta

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

class OMetaMacroRuleProcessor:

	_ruleName as string
	_collectingParseTree as DynamicVariable[of bool]
	_optionParseTree as bool

	def constructor(ruleName as string, options as List):
		_ruleName = ruleName
		_optionParseTree = "ParseTree" in options
		_collectingParseTree = DynamicVariable[of bool](_optionParseTree)

	def expand(e as Expression, *args as (Expression)) as Block:

		input = [| input_ |]
		block = Block(LexicalInfo: e.LexicalInfo)
		for arg in args:
			code = [|
				block:
					lastMatch = any($input)
					smatch = lastMatch as SuccessfulMatch
					if smatch is null: return lastMatch
					$input = smatch.Input
					$arg = smatch.Value
			|].Block
			block.Add(code)

		expand block, e, input, [| lastMatch |]
		block.Add([| return lastMatch |])

		return block

	def expand(e as Expression, input as Expression, lastMatch as ReferenceExpression) as Block:
		block = Block()
		expand block, e, input, lastMatch
		return block

	def expandChoices(block as Block, choices as List, input as Expression, lastMatch as ReferenceExpression):
		currentBlock = block

		oldInput = uniqueName()
		failureList = uniqueName()
		currentBlock.Add([| $failureList = Boo.Lang.List[of FailedMatch]($(len(choices))) |])
		currentBlock.Add([| $oldInput = $input |])
		for choice in choices:
			expand currentBlock, choice, oldInput, lastMatch
			code = [|
				if $lastMatch isa FailedMatch:
					$failureList.Add($lastMatch)
			|]
			currentBlock.Add(code)
			currentBlock = code.TrueBlock
		currentBlock.Add([| $lastMatch = FailedMatch($oldInput, ChoiceFailure($failureList)) |])

	def resultAppend(result as Expression):
		if collectingParseTree:
			return [| $result.Add(smatch.Value) if smatch.Value is not null |]
		return ExpressionStatement([| $result = smatch.Value |])

	def expandRepetition(block as Block, e as Expression, input as Expression, lastMatch as ReferenceExpression):

		temp = lastMatch #uniqueName()
		result = uniqueName()
		block.Add(expand(e, input, temp))
		block.Add([| smatch = $temp as SuccessfulMatch |])
		code = [|
			if smatch is not null:
				$(resultAppend(result))
				$(expandRepetitionLoop(e, [| $temp.Input |], temp, result))
		|]
		if collectingParseTree:
			code.TrueBlock.Insert(0, [| $result = [] |])
		block.Add(code)

	def expandRepetitionLoop(e as Expression, input as Expression, lastMatch as ReferenceExpression, result as Expression):
		tempInput = uniqueName()
		code = [|
			block:
				$tempInput = $input
				while true:
					$(expand(e, tempInput, lastMatch))
					smatch = $lastMatch as SuccessfulMatch
					break if smatch is null
					$tempInput = smatch.Input
					$(resultAppend(result))

				$lastMatch = SuccessfulMatch($tempInput, $result)
		|]
		return code.Block

	def collectChoices(choices as List, e as Expression):
		match e:
			case [| $l | $r |]:
				collectChoices choices, l
				collectChoices choices, r
			otherwise:
				choices.Add(e)

	collectingParseTree:
		get: return _collectingParseTree.Value

	def expandSequence(block as Block, sequence as ExpressionCollection, input as Expression, lastMatch as ReferenceExpression):

		if _optionParseTree and collectingParseTree:
			expandSequenceWithParseTree block, sequence, input, lastMatch
		else:
			expandSequenceWithoutParseTree block, sequence, input, lastMatch

	def expandSequenceWithoutParseTree(block as Block, sequence as ExpressionCollection, input as Expression, lastMatch as ReferenceExpression):

		currentBlock = block
		for item in sequence.ToArray()[:-1]:
			expand currentBlock, item, input, lastMatch
			input = [| $lastMatch.Input |]
			code = [|
				if $lastMatch isa SuccessfulMatch:
					pass
			|]
			currentBlock.Add(code)
			currentBlock = code.TrueBlock
		expand currentBlock, sequence[-1], input, lastMatch

	def expandSequenceWithParseTree(block as Block, sequence as ExpressionCollection, input as Expression, lastMatch as ReferenceExpression):

		result = uniqueName()
		currentBlock = block
		currentBlock.Add([| $result = [] |])

		for item in sequence:
			expand currentBlock, item, input, lastMatch
			currentBlock.Add([| smatch = $lastMatch as SuccessfulMatch |])
			code = [|
				if smatch is not null:
					$(resultAppend(result))
			|]
			currentBlock.Add(code)
			currentBlock = code.TrueBlock
			input = [| $lastMatch.Input |]

		currentBlock.Add([| $lastMatch = SuccessfulMatch(smatch.Input, $result) |])

	def expandNegation(block as Block, rule as Expression, input as Expression, lastMatch as ReferenceExpression):
		oldInput = uniqueName()
		block.Add([| $oldInput = $input |])

		_collectingParseTree.With(false):
			expand block, rule, input, lastMatch
		block.Add([| smatch = $lastMatch as SuccessfulMatch |])
		code = [|
			if smatch is null:
				$lastMatch = SuccessfulMatch($oldInput, null)
			else:
				$lastMatch = FailedMatch($oldInput, NegationFailure($(rule.ToCodeString())))
		|]
		block.Add(code)
		return code

	def processVariables(e as Expression, input as Expression):
		e.ReplaceNodes([| input |], [| $input |])
		return e

	def expand(block as Block, e as Expression, input as Expression, lastMatch as ReferenceExpression):
		match e:
			case SpliceExpression(Expression: rule):
				block.Add([| $lastMatch = $(processVariables(rule, input)) |])

			case [| $rule[$arg] |]:
				newInput = uniqueName()
				block.Add([| $newInput = OMetaInput.Prepend($arg, $input) |])
				expand block, rule, newInput, lastMatch

			case [| $pattern and $predicate |]:
				expand block, pattern, input, lastMatch
				checkPredicate = [|
					if $lastMatch isa SuccessfulMatch and not $(processVariables(predicate, input)):
						$lastMatch = FailedMatch($input, PredicateFailure($(predicate.ToCodeString())))
				|]
				block.Add(checkPredicate)

			case [| $pattern ^ $value |]:
				_collectingParseTree.With(false):
					expand block, pattern, input, lastMatch
					code = [|
						block:
							smatch = $lastMatch as SuccessfulMatch
							if smatch is not null:
								$lastMatch = SuccessfulMatch(smatch.Input, $(processVariables(value, input)))
					|].Block
					block.Add(code)

			case [| $pattern >> $variable |]:
				_collectingParseTree.With(true):
					expand block, pattern, input, lastMatch
					match variable:
						case [| $name as $typeref |]:
							code = [|
								block:
									smatch = $lastMatch as SuccessfulMatch
									if smatch is not null:
										$name = cast($typeref, smatch.Value)
							|].Block
						otherwise:
							code = [|
								block:
									smatch = $lastMatch as SuccessfulMatch
									if smatch is not null:
										$variable = smatch.Value
							|].Block
					block.Add(code)

			case [| $_ | $_ |]:
				choices = []
				collectChoices choices, e
				expandChoices block, choices, input, lastMatch

			case StringLiteralExpression(Value: v):
				if len(v) == 0:
					block.Add([| $lastMatch = SuccessfulMatch($input, null) |])
				elif len(v) == 1:
					block.Add([| $lastMatch = character($input, $(CharLiteralExpression(e.LexicalInfo, v[0]))) |])
				else:
					block.Add([| $lastMatch = characters($input, $e) |])

			case [| ++$rule |]:
				expandRepetition block, rule, input, lastMatch

			case [| --$rule |]:
				result = uniqueName()
				block.Add([| $result = [] |]) if collectingParseTree
				block.Add(expandRepetitionLoop(rule, input, lastMatch, result))

			case [| ~$rule |]:
				expandNegation block, rule, input, lastMatch

			case ReferenceExpression(Name: name):
				block.Add([| $lastMatch = context.Eval($name, $input) |])

			case [| super |]:
				block.Add([| $lastMatch = SuperApply(context, $_ruleName, $input) |])

			case [| $_() |]:
				rules = processObjectPatternRules(e)
				condition = PatternExpander().expand([| smatch.Value |], e)
				code = [|
					block:
						$lastMatch = any($input)
						smatch = $lastMatch as SuccessfulMatch
						if smatch is not null:
							if $condition:
								$(expandObjectPatternRules(rules, lastMatch))
							else:
								$lastMatch = FailedMatch($input, ObjectPatternFailure($(e.ToCodeString())))
				|].Block
				block.Add(code)

			case ArrayLiteralExpression(Items: items):
				match items[0]:
					case [| ~$rule |]:
						negation = expandNegation(block, rule, input, lastMatch)
						if len(items) > 2:
							expandSequence negation.TrueBlock, items.PopRange(1), input, lastMatch
						else:
							expand negation.TrueBlock, items[1], input, lastMatch
					otherwise:
						expandSequence block, items, input, lastMatch

	def processObjectPatternRules(pattern as Expression):
		rules = []
		processObjectPatternRules rules, pattern
		return rules

	def expandObjectPatternRules(rules, lastMatch as Expression) as Block:
		block = Block()
		input = uniqueName()

		currentBlock = block
		for temp as Expression, rule as Expression in rules:
			block.Add([| $input = OMetaInput.Singleton($temp) |])
			expand block, rule, input, lastMatch
			code = [|
				if $lastMatch isa SuccessfulMatch:
					pass
			|]
			currentBlock.Add(code)
			currentBlock = code.TrueBlock
		return block

	def processObjectPatternRules(rules as List, pattern as MethodInvocationExpression):
		for arg in pattern.NamedArguments:
			match arg.Second:
				case [| $_ >> $_ |]:
					temp = uniqueName()
					rules.Add((temp, arg.Second))
					arg.Second = temp
				otherwise:
					pass