[] as now mandatory

[] as now mandatory

namespace Boo.PatternMatching.Tests

import NUnit.Framework
import Boo.PatternMatching

class Item:
	public static final Default = Item(Name: "default")

	[property(Name)] _name = ""
	[property(Child)] _child as Item

class Container[of T]:
	public value as T

class Collection:
	public Items as List

[TestFixture]
class MatchMacroTest:

	[Test]
	def TestFixedSizeCollection():
		Assert.AreEqual(1, lastItem(Collection(Items: [1])))
		Assert.AreEqual(2, lastItem(Collection(Items: [1, 2])))

	[Test]
	def TestNestedFixedSize():
		c = Collection(Items: [Collection(Items: [1, 2])])
		Assert.AreEqual(2, nestedLastItem(c))

	[Test]
	[ExpectedException(MatchError)]
	def TestMatchErrorOnFixedSize():
		lastItem(Collection(Items: [1, 2, 3]))

	[Test]
	def TestGenericMatch():
		Assert.AreEqual(42, genericMatch(Container of int(value: 21)))
		Assert.AreEqual("BOO", genericMatch(Container of string(value: 'boo')))

	[Test]
	def TestPropertyPattern():
		Assert.AreEqual("item foo", itemByName(Item(Name: "foo")))
		Assert.AreEqual("not foo", itemByName(Item(Name: "not foo")))

	[Test]
	def TestNestedPropertyPattern():
		Assert.AreEqual("foo:bar", nestedByName(
								Item(Name: "foo",
									Child: Item(Name: "bar"))))

	[Test]
	def TestStringValue():
		for s in ("foo", "bar"):
			Assert.AreEqual("*${s}*", matchStringValue(s))

	[Test]
	def TestQualifiedReference():
		Assert.AreEqual("default item", itemByQualifiedReference(Item.Default))
		Assert.AreEqual("foo", itemByQualifiedReference(Item(Name: "foo")))

	[Test]
	def TestImplicitPropertyPattern():
		Assert.AreEqual("FOO", itemByImplicitNameReference(Item(Name: "foo")))

	[Test]
	[ExpectedException(MatchError)]
	def TestMatchErrorOnPropertyPattern():
		itemByName(42)

	[Test]
	def TestMatchErrorMessageIncludesValue():
		try:
			itemByName(42)
		except e as MatchError:
			Assert.AreEqual("'o' failed to match '42'", e.Message)

	[Test]
	[ExpectedException(MatchError)]
	def TestMatchErrorOnNestedPropertyPattern():
		nestedByName(42)

	[Test]
	def TestCaseCapture():
		Assert.AreEqual("foo", caseCapture(Item(Name: "foo")))
		Assert.AreEqual(42, caseCapture(21))

	[Test]
	def TestOtherwise():
		Assert.AreEqual("int", matchIntOtherwise(42))
		Assert.AreEqual("otherwise", matchIntOtherwise("42"))

	enum Foo:
		None
		Bar
		Baz

	[Test]
	def TestMemberReferenceIsTreatedAsValueComparison():
		Assert.AreEqual("Bar", matchMemberRef(Foo.Bar))
		Assert.AreEqual("Baz", matchMemberRef(Foo.Baz))

	[Test]
	[ExpectedException(MatchError)]
	def TestMatchErrorOnMemberReferencePattern():
		matchMemberRef(Foo.None)

	def itemByName(o):
		match o:
			case Item(Name: "foo"):
				return "item foo"
			case Item(Name: name):
				return name

	def nestedByName(o):
		match o:
			case Item(Name: outer, Child: Item(Name: inner)):
				return "${outer}:${inner}"

	def itemByQualifiedReference(o):
		match o:
			case Item(Name: Item.Default.Name):
				return "default item"
			case Item(Name: name):
				return name

	def itemByImplicitNameReference(o):
		match o:
			case Item(Name):
				return Name.ToUpper()

	def caseCapture(o):
		match o:
			case i = int():
				return i*2
			case item = Item():
				return item.Name

	def matchMemberRef(o as Foo):
		match o:
			case Foo.Bar:
				return "Bar"
			case Foo.Baz:
				return "Baz"

	def matchIntOtherwise(o):
		match o:
			case int():
				return "int"
			otherwise:
				return "otherwise"

	def matchStringValue(o):
		match o:
			case "foo":
				return "*foo*"
			case "bar":
				return "*bar*"

	def genericMatch(o):
		match o:
			case Container[of int](value: i):
				return i*2
			case Container[of string](value: s):
				return s.ToUpper()

	def lastItem(o):
		match o:
			case Collection(Items: (last,)):
				return last
			case Collection(Items: (_, last)):
				return last

	def nestedLastItem(o):
		match o:
			case Collection(Items: (Collection(Items: (_, last)),)):
				return last

namespace Boo.PatternMatching.Tests

import NUnit.Framework
import Boo.PatternMatching

class Item:
	public static final Default = Item(Name: "default")

	[property(Name)] _name = ""
	[property(Child)] _child as Item

class Container of T:
	public value as T

class Collection:
	public Items as List

[TestFixture]
class MatchMacroTest:

	[Test]
	def TestFixedSizeCollection():
		Assert.AreEqual(1, lastItem(Collection(Items: [1])))
		Assert.AreEqual(2, lastItem(Collection(Items: [1, 2])))

	[Test]
	def TestNestedFixedSize():
		c = Collection(Items: [Collection(Items: [1, 2])])
		Assert.AreEqual(2, nestedLastItem(c))

	[Test]
	[ExpectedException(MatchError)]
	def TestMatchErrorOnFixedSize():
		lastItem(Collection(Items: [1, 2, 3]))

	[Test]
	def TestGenericMatch():
		Assert.AreEqual(42, genericMatch(Container of int(value: 21)))
		Assert.AreEqual("BOO", genericMatch(Container of string(value: 'boo')))

	[Test]
	def TestPropertyPattern():
		Assert.AreEqual("item foo", itemByName(Item(Name: "foo")))
		Assert.AreEqual("not foo", itemByName(Item(Name: "not foo")))

	[Test]
	def TestNestedPropertyPattern():
		Assert.AreEqual("foo:bar", nestedByName(
								Item(Name: "foo",
									Child: Item(Name: "bar"))))

	[Test]
	def TestStringValue():
		for s in ("foo", "bar"):
			Assert.AreEqual("*${s}*", matchStringValue(s))

	[Test]
	def TestQualifiedReference():
		Assert.AreEqual("default item", itemByQualifiedReference(Item.Default))
		Assert.AreEqual("foo", itemByQualifiedReference(Item(Name: "foo")))

	[Test]
	def TestImplicitPropertyPattern():
		Assert.AreEqual("FOO", itemByImplicitNameReference(Item(Name: "foo")))

	[Test]
	[ExpectedException(MatchError)]
	def TestMatchErrorOnPropertyPattern():
		itemByName(42)

	[Test]
	def TestMatchErrorMessageIncludesValue():
		try:
			itemByName(42)
		except e as MatchError:
			Assert.AreEqual("'o' failed to match '42'", e.Message)

	[Test]
	[ExpectedException(MatchError)]
	def TestMatchErrorOnNestedPropertyPattern():
		nestedByName(42)

	[Test]
	def TestCaseCapture():
		Assert.AreEqual("foo", caseCapture(Item(Name: "foo")))
		Assert.AreEqual(42, caseCapture(21))

	[Test]
	def TestOtherwise():
		Assert.AreEqual("int", matchIntOtherwise(42))
		Assert.AreEqual("otherwise", matchIntOtherwise("42"))

	enum Foo:
		None
		Bar
		Baz

	[Test]
	def TestMemberReferenceIsTreatedAsValueComparison():
		Assert.AreEqual("Bar", matchMemberRef(Foo.Bar))
		Assert.AreEqual("Baz", matchMemberRef(Foo.Baz))

	[Test]
	[ExpectedException(MatchError)]
	def TestMatchErrorOnMemberReferencePattern():
		matchMemberRef(Foo.None)

	def itemByName(o):
		match o:
			case Item(Name: "foo"):
				return "item foo"
			case Item(Name: name):
				return name

	def nestedByName(o):
		match o:
			case Item(Name: outer, Child: Item(Name: inner)):
				return "${outer}:${inner}"

	def itemByQualifiedReference(o):
		match o:
			case Item(Name: Item.Default.Name):
				return "default item"
			case Item(Name: name):
				return name

	def itemByImplicitNameReference(o):
		match o:
			case Item(Name):
				return Name.ToUpper()

	def caseCapture(o):
		match o:
			case i = int():
				return i*2
			case item = Item():
				return item.Name

	def matchMemberRef(o as Foo):
		match o:
			case Foo.Bar:
				return "Bar"
			case Foo.Baz:
				return "Baz"

	def matchIntOtherwise(o):
		match o:
			case int():
				return "int"
			otherwise:
				return "otherwise"

	def matchStringValue(o):
		match o:
			case "foo":
				return "*foo*"
			case "bar":
				return "*bar*"

	def genericMatch(o):
		match o:
			case Container[of int](value: i):
				return i*2
			case Container[of string](value: s):
				return s.ToUpper()

	def lastItem(o):
		match o:
			case Collection(Items: (last,)):
				return last
			case Collection(Items: (_, last)):
				return last

	def nestedLastItem(o):
		match o:
			case Collection(Items: (Collection(Items: (_, last)),)):
				return last


fix to pass on .net 2

fix to pass on .net 2

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

import System
import NUnit.Framework

def foo():
	return "foo"

if Environment.Version < Version(2, 0):
	d1 as Delegate = foo
	Assert.AreEqual("foo", d1.DynamicInvoke(null))

	d2 as MulticastDelegate = foo
	Assert.AreEqual("foo", d2.DynamicInvoke(null))

d3 as ICallable = foo
Assert.AreEqual("foo", d3())

import System
import NUnit.Framework

def foo():
	return "foo"

d1 as Delegate = foo
Assert.AreEqual("foo", d1.DynamicInvoke(null))

d2 as MulticastDelegate = foo
Assert.AreEqual("foo", d2.DynamicInvoke(null))

d3 as ICallable = foo
Assert.AreEqual("foo", d3())


super method invocation

super method invocation

namespace Boojay.Compilation

import System.IO

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

import Boo.Lang.PatternMatching

import org.objectweb.asm
import org.objectweb.asm.Type

class BoojayEmitter(AbstractVisitorCompilerStep):

	_classWriter as ClassWriter
	_code as MethodVisitor
	_currentMethod as Method
	_typeMappings as Hash
	_primitiveMappings as Hash

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

	def Run():
		Visit(CompileUnit)

	def initializeTypeMappings():
		_typeMappings = {
			typeSystem.ObjectType: "java/lang/Object",
			typeSystem.StringType: "java/lang/String",
			typeSystem.ICallableType: "Boojay/Runtime/Callable",
			typeSystem.TypeType: "java/lang/Class",
			typeSystem.IEnumerableType: "Boojay/Runtime/Enumerable",
			typeSystem.IEnumeratorType: "Boojay/Runtime/Enumerator",
			typeSystem.RuntimeServicesType: "Boojay/Runtime/RuntimeServices",
			typeSystem.Map(typeof(System.IDisposable)): "Boojay/Runtime/Disposable",
		}

		_primitiveMappings = {
			typeSystem.BoolType: BOOLEAN_TYPE.getDescriptor(),
			typeSystem.IntType: INT_TYPE.getDescriptor(),
			typeSystem.VoidType: VOID_TYPE.getDescriptor(),
			typeSystem.CharType: CHAR_TYPE.getDescriptor(),
		}

	override def OnInterfaceDefinition(node as InterfaceDefinition):
		emitTypeDefinition node

	override def OnClassDefinition(node as ClassDefinition):
		emitTypeDefinition node

	def emitTypeDefinition(node as TypeDefinition):
		_classWriter = ClassWriter(ClassWriter.COMPUTE_MAXS)
		_classWriter.visit(
			Opcodes.V1_5,
			typeAttributes(node),
			javaType(node.FullName),
			null,
			javaType(baseType(node)),
			implementedInterfaces(node))

		emit node.Members

		_classWriter.visitEnd()

		writeClassFile node

	def implementedInterfaces(node as TypeDefinition):
		interfaces = array(
				javaType(itf)
				for itf in node.BaseTypes
				if isInterface(itf))
		if len(interfaces) == 0: return null
		return interfaces

	def isInterface(typeRef as TypeReference):
		return typeBinding(typeRef).IsInterface

	def typeBinding(node as Node) as IType:
		return entity(node)

	def expressionType(e as Expression) as IType:
		return self.TypeSystemServices.GetExpressionType(e)

	def typeAttributes(node as TypeDefinition):
		attrs = 0
		match node.NodeType:
			case NodeType.ClassDefinition:
				attrs += Opcodes.ACC_SUPER
			case NodeType.InterfaceDefinition:
				attrs += (Opcodes.ACC_INTERFACE + Opcodes.ACC_ABSTRACT)
		if node.IsPublic:
			attrs += Opcodes.ACC_PUBLIC
		return attrs

	def writeClassFile(node as TypeDefinition):
		fname = classFullFileName(node)
		ensurePath(fname)
		File.WriteAllBytes(Path.Combine(outputDirectory(), fname), _classWriter.toByteArray())

	def outputDirectory():
		return Parameters.OutputAssembly

	override def OnIfStatement(node as IfStatement):

		elseLabel = Label()
		afterElseLabel = Label()

		emitBranchFalse node.Condition, elseLabel
		emit node.TrueBlock
		if node.FalseBlock is not null:
			GOTO afterElseLabel
		mark elseLabel
		if node.FalseBlock is not null:
			emit node.FalseBlock
			mark afterElseLabel

	def emitBranchFalse(e as Expression, label as Label):
		match e:
			case UnaryExpression(
					Operator: UnaryOperatorType.LogicalNot,
					Operand: condition):
				emitBranchTrue condition, label
			otherwise:
				emit e
				IFEQ label

	def emitBranchTrue(e as Expression, label as Label):
		match e:
			case UnaryExpression(
					Operator: UnaryOperatorType.LogicalNot,
					Operand: condition):
				emitBranchFalse condition, label

			case BinaryExpression(
					Operator: BinaryOperatorType.LessThan,
					Left: l,
					Right: r):
				emit l
				emit r
				IF_ICMPLT label
			otherwise:
				emit e
				IFNE label

	override def OnWhileStatement(node as WhileStatement):

		testLabel = Label()
		bodyLabel = Label()
		GOTO testLabel
		mark bodyLabel
		emit node.Block
		mark testLabel
		emitBranchTrue node.Condition, bodyLabel

	override def OnField(node as Field):
		field = _classWriter.visitField(
					memberAttributes(node),
					node.Name,
					typeDescriptor(entity(node.Type)),
					null,
					null)
		field.visitEnd()

	override def OnConstructor(node as Constructor):
		emitMethod "<init>", node

	_methodMappings = {
		".ctor": "<init>",
		"constructor": "<init>",
		"Main": "main",
		"ToString": "toString",
		"Equals": "equals",
		"GetType": "getClass"
	}

	override def OnMethod(node as Method):
		emitMethod methodName(node.Name), node

	def methodName(name as string):
		return _methodMappings[name] or name

	def emitMethod(methodName as string, node as Method):
		_code = _classWriter.visitMethod(
					memberAttributes(node),
					methodName,
					javaSignature(node),
					null,
					null)

		if not node.IsAbstract:
			_currentMethod = node
			emitMethodBody node
		_code.visitEnd()

	def emitMethodBody(node as Method):
		prepareLocalVariables node

		_code.visitCode()

		emit node.Body
		RETURN

		_code.visitMaxs(0, 0)

	def memberAttributes(node as TypeMember):
		attributes = Opcodes.ACC_PUBLIC
		if node.IsStatic: attributes += Opcodes.ACC_STATIC
		if node.IsAbstract: attributes += Opcodes.ACC_ABSTRACT
		return attributes

	def prepareLocalVariables(node as Method):
		i = firstLocalIndex(node)
		for local in node.Locals:
			index(local, i)
			++i

	def firstLocalIndex(node as Method):
		if 0 == len(node.Parameters):
			if node.IsStatic: return 0
			return 1
		return lastParameterIndex(node) + 1

	def lastParameterIndex(node as Method):
		param as InternalParameter = entity(node.Parameters[-1])
		return param.Index

	def newTemp(type as IType):
		i = nextLocalIndex()
		local = CodeBuilder.DeclareTempLocal(self._currentMethod, type)
		index local.Local, i
		return local

	def nextLocalIndex():
		locals = _currentMethod.Locals
		if len(locals) == 0: return firstLocalIndex(_currentMethod)
		return index(entity(locals[-1])) + 1

	def index(node as Local, index as int):
		node["index"] = index

	def index(entity as InternalLocal) as int:
		return entity.Local["index"]

	override def LeaveExpressionStatement(node as ExpressionStatement):
		discardValueOnStack node.Expression

	def discardValueOnStack(node as Expression):
		mie = node as MethodInvocationExpression
		if mie is null: return

		m = entity(mie.Target) as IMethod
		if m is null: return

		if hasReturnValue(m): POP

	def hasReturnValue(m as IMethod):
		return m.ReturnType is not TypeSystemServices.VoidType

	override def OnNullLiteralExpression(node as NullLiteralExpression):
		ACONST_NULL

	override def OnReturnStatement(node as ReturnStatement):
		if node.Expression is null:
			RETURN
		else:
			emit node.Expression
			if isReferenceType(node.Expression):
				ARETURN
			else:
				IRETURN

	def isReferenceType(e as Expression):
		return not expressionType(e).IsValueType

	override def OnMethodInvocationExpression(node as MethodInvocationExpression):
		match entity(node.Target):
			case ctor = IConstructor():
				emitConstructorInvocation ctor, node
			case method = IMethod():
				emitMethodInvocation method, node
			case builtin = BuiltinFunction():
				emitBuiltinInvocation builtin, node

	def emitSuperMethodInvocation(method as InternalMethod, node as MethodInvocationExpression):
		ALOAD 0
		emit node.Arguments
		INVOKESPECIAL method.Overriden

	def emitConstructorInvocation(ctor as IConstructor, node as MethodInvocationExpression):
		match node.Target:
			case SuperLiteralExpression():
				ALOAD 0
				emit node.Arguments
				INVOKESPECIAL ctor
			otherwise:
				emitObjectCreation ctor, node

	def emitBuiltinInvocation(builtin as BuiltinFunction, node as MethodInvocationExpression):
		match builtin.FunctionType:
			case BuiltinFunctionType.Eval:
				emitEval node

	def emitEval(node as MethodInvocationExpression):
		for e in node.Arguments.ToArray()[:-1]:
			emit e
			discardValueOnStack e
		emit node.Arguments[-1]

	def emitMethodInvocation(method as IMethod, node as MethodInvocationExpression):
		match node.Target:
			case SuperLiteralExpression():
				emitSuperMethodInvocation method, node
			otherwise:
				emitMethodInvocationHandlingSpecialCases method, node

	def emitMethodInvocationHandlingSpecialCases(method as IMethod, node as MethodInvocationExpression):
		if isSpecialIkvmGetter(method):
			emitSpecialIkvmGetter(method)
			return

		if isArrayLength(method):
			emit node.Target
			ARRAYLENGTH
			return

		if handleSpecialStringMethod(method, node):
			return

		emitRegularMethodInvocation method, node

	def emitRegularMethodInvocation(method as IMethod, node as MethodInvocationExpression):
		emit node.Target
		emit node.Arguments

		if method.IsStatic:
			INVOKESTATIC method
		elif method.DeclaringType.IsInterface:
			INVOKEINTERFACE method
		else:
			INVOKEVIRTUAL method

	def handleSpecialStringMethod(method as IMethod, node as MethodInvocationExpression):
		if method.DeclaringType is not typeSystem.StringType:
			return false

		match method.Name:
			case "get_Item":
				emit node.Target
				emit node.Arguments
				invokeWithName Opcodes.INVOKEVIRTUAL, method, "charAt"
			case "get_Length":
				emit node.Target
				invokeWithName Opcodes.INVOKEVIRTUAL, method, "length"
			otherwise:
				return false

		return true

	def isArrayLength(method as IMethod):
		return method.FullName == "System.Array.get_Length"

	def emitObjectCreation(ctor as IConstructor, node as MethodInvocationExpression):
		NEW ctor.DeclaringType
		DUP
		emit node.Arguments
		INVOKESPECIAL ctor

	def emitSpecialIkvmGetter(method as IMethod):
	"""
	in the ikvm version of GNU.ClassPath the
	console streams are mapped to properties
	where in java they should be treated as static fields
	"""
		emitLoadStaticField(
			method.DeclaringType,
			stripGetterPrefix(method.Name),
			method.ReturnType)

	def isSpecialIkvmGetter(method as IMethod):
		return (method.IsStatic
			and method.DeclaringType.FullName == "java.lang.System"
			and method.Name in ("get_out", "get_in", "get_err"))

	override def OnRaiseStatement(node as RaiseStatement):
		if node.Exception is not null:
			emit node.Exception
			ATHROW
		else:
			ALOAD index(entity(enclosingHandler(node).Declaration))
			ATHROW

	def enclosingHandler(node as Node) as ExceptionHandler:
		return node.GetAncestor(NodeType.ExceptionHandler)

	override def OnTryStatement(node as TryStatement):
		L1 = Label()
		L2 = Label()
		L3 = Label()
		mark L1
		emit node.ProtectedBlock
		GOTO L3
		mark L2

		for handler in node.ExceptionHandlers:
			L4 = Label()
			mark L4
			decl = handler.Declaration
			TRYCATCHBLOCK L1, L2, L4, javaType(decl.Type)
			ASTORE index(entity(decl))
			emit handler.Block
			GOTO L3

		if node.EnsureBlock is null:

			mark L3

		else:

			L4 = Label()
			mark L4
			TRYCATCHBLOCK L1, L4, L4, null
			temp = newTemp(typeSystem.ObjectType)
			ASTORE index(temp)
			emit node.EnsureBlock
			ALOAD index(temp)
			ATHROW

			mark L3
			emit node.EnsureBlock

	override def OnCastExpression(node as CastExpression):
		emit node.Target
		CHECKCAST javaType(node.Type)

	override def OnTryCastExpression(node as TryCastExpression):
		emit node.Target
		DUP
		INSTANCEOF javaType(node.Type)
		L1 = Label()
		L2 = Label()
		IFNE L1
		POP
		ACONST_NULL
		GOTO L2
		mark L1
		CHECKCAST javaType(node.Type)
		mark L2

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

			case BinaryOperatorType.Assign:
				emitAssignment node

			case BinaryOperatorType.Subtraction:
				emitSubtraction node
			case BinaryOperatorType.Addition:
				emitAddition node
			case BinaryOperatorType.Multiply:
				emitMultiply node

			case BinaryOperatorType.TypeTest:
				emitTypeTest node

			case BinaryOperatorType.Equality:
				emitEquality node
			case BinaryOperatorType.Inequality:
				emitInequality node

			case BinaryOperatorType.ReferenceEquality:
				emitReferenceEquality node
			case BinaryOperatorType.ReferenceInequality:
				emitReferenceInequality node

			case BinaryOperatorType.GreaterThanOrEqual:
				emitComparison node, Opcodes.IF_ICMPLT

	def emitComparison(node as BinaryExpression, instruction as int):
		L1 = Label()
		L2 = Label()

		emit node.Left
		emit node.Right
		emitJumpInsn instruction, L1
		ICONST_1
		GOTO L2
		mark L1
		ICONST_0
		mark L2

	def emitReferenceInequality(node as BinaryExpression):
		emitComparison node, Opcodes.IF_ACMPEQ

	def emitReferenceEquality(node as BinaryExpression):
		emitComparison node, Opcodes.IF_ACMPNE

	def emitInequality(node as BinaryExpression):
		assert isInteger(node.Left) and isInteger(node.Right)
		emitComparison node, Opcodes.IF_ICMPEQ

	def emitEquality(node as BinaryExpression):
		assert isInteger(node.Left) and isInteger(node.Right)
		emitComparison node, Opcodes.IF_ICMPNE

	def isInteger(e as Expression):
		return typeSystem.IsIntegerOrBool(expressionType(e))

	def emitTypeTest(node as BinaryExpression):
		match node.Right:
			case TypeofExpression(Type: t):
				emit node.Left
				INSTANCEOF javaType(t)

	def emitMultiply(node as BinaryExpression):
		emit node.Left
		emit node.Right
		IMUL

	def emitAddition(node as BinaryExpression):
		emit node.Left
		emit node.Right
		IADD

	def emitSubtraction(node as BinaryExpression):
		emit node.Left
		emit node.Right
		ISUB

	def emitAssignment(node as BinaryExpression):
		match node.Left:
			case memberRef = MemberReferenceExpression():
				match entity(memberRef):
                    case field = IField(IsStatic: false):
                    	emit memberRef.Target
                    	emit node.Right
                    	PUTFIELD field
			case reference = ReferenceExpression():
				emit node.Right
				match entity(reference):
					case local = ILocalEntity():
						if not local.Type.IsValueType:
							ASTORE index(local)
						else:
							ISTORE index(local)

	override def OnMemberReferenceExpression(node as MemberReferenceExpression):
		match entity(node):
			case field = IField(IsStatic: true):
				GETSTATIC field
			case field = IField(IsStatic: false):
				emit node.Target
				GETFIELD field
			case IMethod(IsStatic: false):
				emit node.Target
			case IMethod(IsStatic: true):
				pass

	override def OnSelfLiteralExpression(node as SelfLiteralExpression):
		ALOAD 0

	override def OnReferenceExpression(node as ReferenceExpression):
		match entity(node):
			case param = InternalParameter():
				emitLoad param.Type, param.Index
			case local = ILocalEntity():
				emitLoad local.Type, index(local)
			case type = IType():
				LDC type

	def emitLoad(type as IType, index as int):
		if not type.IsValueType:
			ALOAD index
		else:
			ILOAD index

	def isChar(type as IType):
		return type == typeSystem.CharType

	def isIntegerOrBool(type as IType):
		return typeSystem.IsIntegerOrBool(type)

	def stripGetterPrefix(name as string):
		return name[len("get_"):]

	override def OnArrayLiteralExpression(node as ArrayLiteralExpression):
		ICONST len(node.Items)
		ANEWARRAY javaType(expressionType(node).GetElementType())
		i = 0
		for item in node.Items:
			DUP
			ICONST i++
			emit item
			AASTORE

	override def OnSlicingExpression(node as SlicingExpression):
		assert 1 == len(node.Indices)
		match node.Indices[0].Begin:
			case IntegerLiteralExpression(Value: value):
				if value < 0:
					emitNormalizedArraySlicing node
				else:
					emitRawArraySlicing node

			otherwise:
				emitNormalizedArraySlicing node

	def emitRawArraySlicing(node as SlicingExpression):
		emit node.Target
		emit node.Indices[0].Begin
		AALOAD

	def emitNormalizedArraySlicing(node as SlicingExpression):
		L1 = Label()
		local = ensureLocal(node.Target)
		ALOAD index(local)
		emit node.Indices[0].Begin
		DUP
		ICONST_0
		IF_ICMPGE L1
		ALOAD index(local)
		ARRAYLENGTH
		IADD
		mark L1
		AALOAD

	def ensureLocal(e as Expression):
		local = optionalEntity(e) as InternalLocal
		if local is not null: return local

		local = newTemp(expressionType(e))
		emit e
		ASTORE index(local)
		return local

	override def OnStringLiteralExpression(node as StringLiteralExpression):
		LDC node.Value

	override def OnIntegerLiteralExpression(node as IntegerLiteralExpression):
		ICONST node.Value

	override def OnBoolLiteralExpression(node as BoolLiteralExpression):
		if node.Value:
			ICONST_1
		else:
			ICONST_0

	def baseType(node as TypeDefinition):
		if node isa InterfaceDefinition: return TypeSystemServices.ObjectType
		return self.GetType(node).BaseType

	typeSystem as JavaTypeSystem:
		get: return self.TypeSystemServices

	def javaSignature(method as IMethod):
		return ("("
			+ join(typeDescriptor(p.Type) for p in method.GetParameters(), "")
			+ ")"
			+ typeDescriptor(method.ReturnType))

	def javaSignature(node as Method):
		return javaSignature(entity(node) as IMethod)

	def ensurePath(fname as string):
		path = Path.GetDirectoryName(fname)
		if not string.IsNullOrEmpty(path):
			Directory.CreateDirectory(path)

	def classFullFileName(node as TypeDefinition):
		return javaType(entity(node)) + ".class"

	def emit(node as Node):
		Visit(node)

	def emit(nodes as System.Collections.IEnumerable):
		VisitCollection(nodes)

	def LDC(value):
		emitLdcInsn value

	def LDC(type as IType):
		emitLdcInsn org.objectweb.asm.Type.getType(typeDescriptor(type))

	def IFEQ(label as Label):
		emitJumpInsn Opcodes.IFEQ, label

	def IF_ACMPEQ(label as Label):
		emitJumpInsn Opcodes.IF_ACMPEQ, label

	def IF_ICMPGE(label as Label):
		emitJumpInsn Opcodes.IF_ICMPGE, label

	def IF_ICMPGT(label as Label):
		emitJumpInsn Opcodes.IF_ICMPGT, label

	def IF_ICMPLE(label as Label):
		emitJumpInsn Opcodes.IF_ICMPLE, label

	def IF_ICMPLT(label as Label):
		emitJumpInsn Opcodes.IF_ICMPLT, label

	def IFNE(label as Label):
		emitJumpInsn Opcodes.IFNE, label

	def IF_ACMPNE(label as Label):
		emitJumpInsn Opcodes.IF_ACMPNE, label

	def GOTO(label as Label):
		emitJumpInsn Opcodes.GOTO, label

	def INSTANCEOF(typeName as string):
		emitTypeInsn Opcodes.INSTANCEOF, typeName

	def CHECKCAST(typeName as string):
		emitTypeInsn Opcodes.CHECKCAST, typeName

	def ILOAD(index as int):
		emitVarInsn Opcodes.ILOAD, index

	def ICONST(value as int):
		if value >= -1 and value <= 5:
			emitInsn iconstOpcodeFor(value)
		elif value >= -127 and value <= 127:
			emitIntInsn Opcodes.BIPUSH, value
		else:
			emitIntInsn Opcodes.SIPUSH, value

	def ICONST_0():
		emitInsn Opcodes.ICONST_0

	def ICONST_1():
		emitInsn Opcodes.ICONST_1

	def iconstOpcodeFor(value as int):
		if value == 0: return Opcodes.ICONST_0
		if value == 1: return Opcodes.ICONST_1
		if value == 2: return Opcodes.ICONST_2
		if value == 3: return Opcodes.ICONST_3
		if value == 4: return Opcodes.ICONST_4
		if value == 5: return Opcodes.ICONST_5
		if value == -1: return Opcodes.ICONST_M1
		raise System.ArgumentException("value")

	def ISTORE(index as int):
		emitVarInsn(Opcodes.ISTORE, index)

	def ACONST_NULL():
		emitInsn Opcodes.ACONST_NULL

	def ALOAD(index as int):
		emitVarInsn(Opcodes.ALOAD, index)

	def ANEWARRAY(type as string):
		emitTypeInsn Opcodes.ANEWARRAY, type

	def ARRAYLENGTH():
		emitInsn Opcodes.ARRAYLENGTH

	def AASTORE():
		emitInsn Opcodes.AASTORE

	def AALOAD():
		emitInsn Opcodes.AALOAD

	def ASTORE(index as int):
		emitVarInsn(Opcodes.ASTORE, index)

	def GETSTATIC(field as IField):
		emitLoadStaticField(field.DeclaringType, field.Name, field.Type)

	def GETFIELD(field as IField):
		emitField Opcodes.GETFIELD, field

	def PUTFIELD(field as IField):
		emitField Opcodes.PUTFIELD, field

	def INVOKESTATIC(method as IMethod):
		invoke(Opcodes.INVOKESTATIC, method)

	def INVOKEVIRTUAL(method as IMethod):
		invoke(Opcodes.INVOKEVIRTUAL, method)

	def INVOKESPECIAL(method as IMethod):
		invoke(Opcodes.INVOKESPECIAL, method)

	def INVOKEINTERFACE(method as IMethod):
		invoke(Opcodes.INVOKEINTERFACE, method)

	def TRYCATCHBLOCK(begin as Label, end as Label, target as Label, type as string):
		_code.visitTryCatchBlock(begin, end, target, type)

	def ATHROW():
		emitInsn(Opcodes.ATHROW)

	def RETURN():
	   emitInsn(Opcodes.RETURN)

	def ARETURN():
	   emitInsn(Opcodes.ARETURN)

	def IRETURN():
	   emitInsn(Opcodes.IRETURN)

	def POP():
		emitInsn(Opcodes.POP)

	def NEW(type as IType):
		emitTypeInsn(Opcodes.NEW, javaType(type))

	def DUP():
		emitInsn(Opcodes.DUP)

	def ISUB():
		emitInsn(Opcodes.ISUB)

	def IADD():
		emitInsn(Opcodes.IADD)

	def IMUL():
		emitInsn(Opcodes.IMUL)

	def emitVarInsn(opcode as int, index as int):
		_code.visitVarInsn(opcode, index)

	def emitInsn(i as int):
		_code.visitInsn(i)

	def emitIntInsn(opcode as int, value as int):
		_code.visitIntInsn(opcode, value)

	def emitJumpInsn(i as int, label as Label):
		_code.visitJumpInsn(i, label)

	def emitLdcInsn(value):
		_code.visitLdcInsn(value)

	def emitTypeInsn(opcode as int, name as string):
		_code.visitTypeInsn(opcode, name)

	def mark(label as Label):
		_code.visitLabel(label)

	def invoke(opcode as int, method as IMethod):
		invokeWithName opcode, method, methodName(method.Name)

	def invokeWithName(opcode as int, method as IMethod, methodName as string):
		_code.visitMethodInsn(
				opcode,
				javaType(method.DeclaringType),
				methodName,
				javaSignature(method))

	def emitLoadStaticField(declaringType as IType, fieldName as string, fieldType as IType):
		emitField Opcodes.GETSTATIC, declaringType, fieldName, fieldType

	def emitField(opcode as int, field as IField):
		emitField opcode, field.DeclaringType, field.Name, field.Type

	def emitField(opcode as int, declaringType as IType, fieldName as string, fieldType as IType):
		_code.visitFieldInsn(
				opcode,
				javaType(declaringType),
				fieldName,
				typeDescriptor(fieldType))

	def entity(node as Node):
		return GetEntity(node)

	def optionalEntity(node as Node):
		return self.TypeSystemServices.GetOptionalEntity(node)

	def javaType(typeRef as TypeReference):
		return javaType(entity(typeRef) as IType)

	def typeDescriptor(type as IType) as string:
		if type in _primitiveMappings: return _primitiveMappings[type]
		if type.IsArray: return "[" + typeDescriptor(type.GetElementType())
		return "L" + javaType(type) + ";"

	def javaType(type as IType) as string:
		if type in _typeMappings: return _typeMappings[type]
		return javaType(type.FullName)

	def javaType(typeName as string):
		return typeName.Replace('.', '/')

namespace Boojay.Compilation

import System.IO

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

import Boo.Lang.PatternMatching

import org.objectweb.asm
import org.objectweb.asm.Type

class BoojayEmitter(AbstractVisitorCompilerStep):

	_classWriter as ClassWriter
	_code as MethodVisitor
	_currentMethod as Method
	_typeMappings as Hash
	_primitiveMappings as Hash

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

	def Run():
		Visit(CompileUnit)

	def initializeTypeMappings():
		_typeMappings = {
			typeSystem.ObjectType: "java/lang/Object",
			typeSystem.StringType: "java/lang/String",
			typeSystem.ICallableType: "Boojay/Runtime/Callable",
			typeSystem.TypeType: "java/lang/Class",
			typeSystem.IEnumerableType: "Boojay/Runtime/Enumerable",
			typeSystem.IEnumeratorType: "Boojay/Runtime/Enumerator",
			typeSystem.RuntimeServicesType: "Boojay/Runtime/RuntimeServices",
			typeSystem.Map(typeof(System.IDisposable)): "Boojay/Runtime/Disposable",
		}

		_primitiveMappings = {
			typeSystem.BoolType: BOOLEAN_TYPE.getDescriptor(),
			typeSystem.IntType: INT_TYPE.getDescriptor(),
			typeSystem.VoidType: VOID_TYPE.getDescriptor(),
			typeSystem.CharType: CHAR_TYPE.getDescriptor(),
		}

	override def OnInterfaceDefinition(node as InterfaceDefinition):
		emitTypeDefinition node

	override def OnClassDefinition(node as ClassDefinition):
		emitTypeDefinition node

	def emitTypeDefinition(node as TypeDefinition):
		_classWriter = ClassWriter(ClassWriter.COMPUTE_MAXS)
		_classWriter.visit(
			Opcodes.V1_5,
			typeAttributes(node),
			javaType(node.FullName),
			null,
			javaType(baseType(node)),
			implementedInterfaces(node))

		emit node.Members

		_classWriter.visitEnd()

		writeClassFile node

	def implementedInterfaces(node as TypeDefinition):
		interfaces = array(
				javaType(itf)
				for itf in node.BaseTypes
				if isInterface(itf))
		if len(interfaces) == 0: return null
		return interfaces

	def isInterface(typeRef as TypeReference):
		return typeBinding(typeRef).IsInterface

	def typeBinding(node as Node) as IType:
		return entity(node)

	def expressionType(e as Expression) as IType:
		return self.TypeSystemServices.GetExpressionType(e)

	def typeAttributes(node as TypeDefinition):
		attrs = 0
		match node.NodeType:
			case NodeType.ClassDefinition:
				attrs += Opcodes.ACC_SUPER
			case NodeType.InterfaceDefinition:
				attrs += (Opcodes.ACC_INTERFACE + Opcodes.ACC_ABSTRACT)
		if node.IsPublic:
			attrs += Opcodes.ACC_PUBLIC
		return attrs

	def writeClassFile(node as TypeDefinition):
		fname = classFullFileName(node)
		ensurePath(fname)
		File.WriteAllBytes(Path.Combine(outputDirectory(), fname), _classWriter.toByteArray())

	def outputDirectory():
		return Parameters.OutputAssembly

	override def OnIfStatement(node as IfStatement):

		elseLabel = Label()
		afterElseLabel = Label()

		emitBranchFalse node.Condition, elseLabel
		emit node.TrueBlock
		if node.FalseBlock is not null:
			GOTO afterElseLabel
		mark elseLabel
		if node.FalseBlock is not null:
			emit node.FalseBlock
			mark afterElseLabel

	def emitBranchFalse(e as Expression, label as Label):
		match e:
			case UnaryExpression(
					Operator: UnaryOperatorType.LogicalNot,
					Operand: condition):
				emitBranchTrue condition, label
			otherwise:
				emit e
				IFEQ label

	def emitBranchTrue(e as Expression, label as Label):
		match e:
			case UnaryExpression(
					Operator: UnaryOperatorType.LogicalNot,
					Operand: condition):
				emitBranchFalse condition, label

			case BinaryExpression(
					Operator: BinaryOperatorType.LessThan,
					Left: l,
					Right: r):
				emit l
				emit r
				IF_ICMPLT label
			otherwise:
				emit e
				IFNE label

	override def OnWhileStatement(node as WhileStatement):

		testLabel = Label()
		bodyLabel = Label()
		GOTO testLabel
		mark bodyLabel
		emit node.Block
		mark testLabel
		emitBranchTrue node.Condition, bodyLabel

	override def OnField(node as Field):
		field = _classWriter.visitField(
					memberAttributes(node),
					node.Name,
					typeDescriptor(entity(node.Type)),
					null,
					null)
		field.visitEnd()

	override def OnConstructor(node as Constructor):
		emitMethod "<init>", node

	_methodMappings = {
		".ctor": "<init>",
		"constructor": "<init>",
		"Main": "main",
		"ToString": "toString",
		"Equals": "equals",
		"GetType": "getClass"
	}

	override def OnMethod(node as Method):
		emitMethod methodName(node.Name), node

	def methodName(name as string):
		return _methodMappings[name] or name

	def emitMethod(methodName as string, node as Method):
		_code = _classWriter.visitMethod(
					memberAttributes(node),
					methodName,
					javaSignature(node),
					null,
					null)

		if not node.IsAbstract:
			_currentMethod = node
			emitMethodBody node
		_code.visitEnd()

	def emitMethodBody(node as Method):
		prepareLocalVariables node

		_code.visitCode()

		emit node.Body
		RETURN

		_code.visitMaxs(0, 0)

	def memberAttributes(node as TypeMember):
		attributes = Opcodes.ACC_PUBLIC
		if node.IsStatic: attributes += Opcodes.ACC_STATIC
		if node.IsAbstract: attributes += Opcodes.ACC_ABSTRACT
		return attributes

	def prepareLocalVariables(node as Method):
		i = firstLocalIndex(node)
		for local in node.Locals:
			index(local, i)
			++i

	def firstLocalIndex(node as Method):
		if 0 == len(node.Parameters):
			if node.IsStatic: return 0
			return 1
		return lastParameterIndex(node) + 1

	def lastParameterIndex(node as Method):
		param as InternalParameter = entity(node.Parameters[-1])
		return param.Index

	def newTemp(type as IType):
		i = nextLocalIndex()
		local = CodeBuilder.DeclareTempLocal(self._currentMethod, type)
		index local.Local, i
		return local

	def nextLocalIndex():
		locals = _currentMethod.Locals
		if len(locals) == 0: return firstLocalIndex(_currentMethod)
		return index(entity(locals[-1])) + 1

	def index(node as Local, index as int):
		node["index"] = index

	def index(entity as InternalLocal) as int:
		return entity.Local["index"]

	override def LeaveExpressionStatement(node as ExpressionStatement):
		discardValueOnStack node.Expression

	def discardValueOnStack(node as Expression):
		mie = node as MethodInvocationExpression
		if mie is null: return

		m = entity(mie.Target) as IMethod
		if m is null: return

		if hasReturnValue(m): POP

	def hasReturnValue(m as IMethod):
		return m.ReturnType is not TypeSystemServices.VoidType

	override def OnNullLiteralExpression(node as NullLiteralExpression):
		ACONST_NULL

	override def OnReturnStatement(node as ReturnStatement):
		if node.Expression is null:
			RETURN
		else:
			emit node.Expression
			if isReferenceType(node.Expression):
				ARETURN
			else:
				IRETURN

	def isReferenceType(e as Expression):
		return not expressionType(e).IsValueType

	override def OnMethodInvocationExpression(node as MethodInvocationExpression):

		match entity(node.Target):
			case ctor = IConstructor():
				match node.Target:
					case SuperLiteralExpression():
						ALOAD 0
						emit node.Arguments
						INVOKESPECIAL ctor
					otherwise:
						emitObjectCreation ctor, node
			case method = IMethod():
				match node.Target:
					case SuperLiteralExpression():
						ALOAD 0
						emit node.Arguments
						INVOKESPECIAL method
					otherwise:
						emitMethodInvocation method, node
			case builtin = BuiltinFunction():
				emitBuiltinInvocation builtin, node

	def emitBuiltinInvocation(builtin as BuiltinFunction, node as MethodInvocationExpression):
		match builtin.FunctionType:
			case BuiltinFunctionType.Eval:
				emitEval node

	def emitEval(node as MethodInvocationExpression):
		for e in node.Arguments.ToArray()[:-1]:
			emit e
			discardValueOnStack e
		emit node.Arguments[-1]

	def emitMethodInvocation(method as IMethod, node as MethodInvocationExpression):

		if isSpecialIkvmGetter(method):
			emitSpecialIkvmGetter(method)
			return

		if isArrayLength(method):
			emit node.Target
			ARRAYLENGTH
			return

		if handleSpecialStringMethod(method, node):
			return

		emit node.Target
		emit node.Arguments

		if method.IsStatic:
			INVOKESTATIC method
		elif method.DeclaringType.IsInterface:
			INVOKEINTERFACE method
		else:
			INVOKEVIRTUAL method

	def handleSpecialStringMethod(method as IMethod, node as MethodInvocationExpression):
		if method.DeclaringType is not typeSystem.StringType:
			return false

		match method.Name:
			case "get_Item":
				emit node.Target
				emit node.Arguments
				invokeWithName Opcodes.INVOKEVIRTUAL, method, "charAt"
			case "get_Length":
				emit node.Target
				invokeWithName Opcodes.INVOKEVIRTUAL, method, "length"
			otherwise:
				return false

		return true

	def isArrayLength(method as IMethod):
		return method.FullName == "System.Array.get_Length"

	def emitObjectCreation(ctor as IConstructor, node as MethodInvocationExpression):
		NEW ctor.DeclaringType
		DUP
		emit node.Arguments
		INVOKESPECIAL ctor

	def emitSpecialIkvmGetter(method as IMethod):
	"""
	in the ikvm version of GNU.ClassPath the
	console streams are mapped to properties
	where in java they should be treated as static fields
	"""
		emitLoadStaticField(
			method.DeclaringType,
			stripGetterPrefix(method.Name),
			method.ReturnType)

	def isSpecialIkvmGetter(method as IMethod):
		return (method.IsStatic
			and method.DeclaringType.FullName == "java.lang.System"
			and method.Name in ("get_out", "get_in", "get_err"))

	override def OnRaiseStatement(node as RaiseStatement):
		if node.Exception is not null:
			emit node.Exception
			ATHROW
		else:
			ALOAD index(entity(enclosingHandler(node).Declaration))
			ATHROW

	def enclosingHandler(node as Node) as ExceptionHandler:
		return node.GetAncestor(NodeType.ExceptionHandler)

	override def OnTryStatement(node as TryStatement):
		L1 = Label()
		L2 = Label()
		L3 = Label()
		mark L1
		emit node.ProtectedBlock
		GOTO L3
		mark L2

		for handler in node.ExceptionHandlers:
			L4 = Label()
			mark L4
			decl = handler.Declaration
			TRYCATCHBLOCK L1, L2, L4, javaType(decl.Type)
			ASTORE index(entity(decl))
			emit handler.Block
			GOTO L3

		if node.EnsureBlock is null:

			mark L3

		else:

			L4 = Label()
			mark L4
			TRYCATCHBLOCK L1, L4, L4, null
			temp = newTemp(typeSystem.ObjectType)
			ASTORE index(temp)
			emit node.EnsureBlock
			ALOAD index(temp)
			ATHROW

			mark L3
			emit node.EnsureBlock

	override def OnCastExpression(node as CastExpression):
		emit node.Target
		CHECKCAST javaType(node.Type)

	override def OnTryCastExpression(node as TryCastExpression):
		emit node.Target
		DUP
		INSTANCEOF javaType(node.Type)
		L1 = Label()
		L2 = Label()
		IFNE L1
		POP
		ACONST_NULL
		GOTO L2
		mark L1
		CHECKCAST javaType(node.Type)
		mark L2

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

			case BinaryOperatorType.Assign:
				emitAssignment node

			case BinaryOperatorType.Subtraction:
				emitSubtraction node
			case BinaryOperatorType.Addition:
				emitAddition node
			case BinaryOperatorType.Multiply:
				emitMultiply node

			case BinaryOperatorType.TypeTest:
				emitTypeTest node

			case BinaryOperatorType.Equality:
				emitEquality node
			case BinaryOperatorType.Inequality:
				emitInequality node

			case BinaryOperatorType.ReferenceEquality:
				emitReferenceEquality node
			case BinaryOperatorType.ReferenceInequality:
				emitReferenceInequality node

			case BinaryOperatorType.GreaterThanOrEqual:
				emitComparison node, Opcodes.IF_ICMPLT

	def emitComparison(node as BinaryExpression, instruction as int):
		L1 = Label()
		L2 = Label()

		emit node.Left
		emit node.Right
		emitJumpInsn instruction, L1
		ICONST_1
		GOTO L2
		mark L1
		ICONST_0
		mark L2

	def emitReferenceInequality(node as BinaryExpression):
		emitComparison node, Opcodes.IF_ACMPEQ

	def emitReferenceEquality(node as BinaryExpression):
		emitComparison node, Opcodes.IF_ACMPNE

	def emitInequality(node as BinaryExpression):
		assert isInteger(node.Left) and isInteger(node.Right)
		emitComparison node, Opcodes.IF_ICMPEQ

	def emitEquality(node as BinaryExpression):
		assert isInteger(node.Left) and isInteger(node.Right)
		emitComparison node, Opcodes.IF_ICMPNE

	def isInteger(e as Expression):
		return typeSystem.IsIntegerOrBool(expressionType(e))

	def emitTypeTest(node as BinaryExpression):
		match node.Right:
			case TypeofExpression(Type: t):
				emit node.Left
				INSTANCEOF javaType(t)

	def emitMultiply(node as BinaryExpression):
		emit node.Left
		emit node.Right
		IMUL

	def emitAddition(node as BinaryExpression):
		emit node.Left
		emit node.Right
		IADD

	def emitSubtraction(node as BinaryExpression):
		emit node.Left
		emit node.Right
		ISUB

	def emitAssignment(node as BinaryExpression):
		match node.Left:
			case memberRef = MemberReferenceExpression():
				match entity(memberRef):
                    case field = IField(IsStatic: false):
                    	emit memberRef.Target
                    	emit node.Right
                    	PUTFIELD field
			case reference = ReferenceExpression():
				emit node.Right
				match entity(reference):
					case local = ILocalEntity():
						if not local.Type.IsValueType:
							ASTORE index(local)
						else:
							ISTORE index(local)

	override def OnMemberReferenceExpression(node as MemberReferenceExpression):
		match entity(node):
			case field = IField(IsStatic: true):
				GETSTATIC field
			case field = IField(IsStatic: false):
				emit node.Target
				GETFIELD field
			case IMethod(IsStatic: false):
				emit node.Target
			case IMethod(IsStatic: true):
				pass

	override def OnSelfLiteralExpression(node as SelfLiteralExpression):
		ALOAD 0

	override def OnReferenceExpression(node as ReferenceExpression):
		match entity(node):
			case param = InternalParameter():
				emitLoad param.Type, param.Index
			case local = ILocalEntity():
				emitLoad local.Type, index(local)
			case type = IType():
				LDC type

	def emitLoad(type as IType, index as int):
		if not type.IsValueType:
			ALOAD index
		else:
			ILOAD index

	def isChar(type as IType):
		return type == typeSystem.CharType

	def isIntegerOrBool(type as IType):
		return typeSystem.IsIntegerOrBool(type)

	def stripGetterPrefix(name as string):
		return name[len("get_"):]

	override def OnArrayLiteralExpression(node as ArrayLiteralExpression):
		ICONST len(node.Items)
		ANEWARRAY javaType(expressionType(node).GetElementType())
		i = 0
		for item in node.Items:
			DUP
			ICONST i++
			emit item
			AASTORE

	override def OnSlicingExpression(node as SlicingExpression):
		assert 1 == len(node.Indices)
		match node.Indices[0].Begin:
			case IntegerLiteralExpression(Value: value):
				if value < 0:
					emitNormalizedArraySlicing node
				else:
					emitRawArraySlicing node

			otherwise:
				emitNormalizedArraySlicing node

	def emitRawArraySlicing(node as SlicingExpression):
		emit node.Target
		emit node.Indices[0].Begin
		AALOAD

	def emitNormalizedArraySlicing(node as SlicingExpression):
		L1 = Label()
		local = ensureLocal(node.Target)
		ALOAD index(local)
		emit node.Indices[0].Begin
		DUP
		ICONST_0
		IF_ICMPGE L1
		ALOAD index(local)
		ARRAYLENGTH
		IADD
		mark L1
		AALOAD

	def ensureLocal(e as Expression):
		local = optionalEntity(e) as InternalLocal
		if local is not null: return local

		local = newTemp(expressionType(e))
		emit e
		ASTORE index(local)
		return local

	override def OnStringLiteralExpression(node as StringLiteralExpression):
		LDC node.Value

	override def OnIntegerLiteralExpression(node as IntegerLiteralExpression):
		ICONST node.Value

	override def OnBoolLiteralExpression(node as BoolLiteralExpression):
		if node.Value:
			ICONST_1
		else:
			ICONST_0

	def baseType(node as TypeDefinition):
		if node isa InterfaceDefinition: return TypeSystemServices.ObjectType
		return self.GetType(node).BaseType

	typeSystem as JavaTypeSystem:
		get: return self.TypeSystemServices

	def javaSignature(method as IMethod):
		return ("("
			+ join(typeDescriptor(p.Type) for p in method.GetParameters(), "")
			+ ")"
			+ typeDescriptor(method.ReturnType))

	def javaSignature(node as Method):
		return javaSignature(entity(node) as IMethod)

	def ensurePath(fname as string):
		path = Path.GetDirectoryName(fname)
		if not string.IsNullOrEmpty(path):
			Directory.CreateDirectory(path)

	def classFullFileName(node as TypeDefinition):
		return javaType(entity(node)) + ".class"

	def emit(node as Node):
		Visit(node)

	def emit(nodes as System.Collections.IEnumerable):
		VisitCollection(nodes)

	def LDC(value):
		emitLdcInsn value

	def LDC(type as IType):
		emitLdcInsn org.objectweb.asm.Type.getType(typeDescriptor(type))

	def IFEQ(label as Label):
		emitJumpInsn Opcodes.IFEQ, label

	def IF_ACMPEQ(label as Label):
		emitJumpInsn Opcodes.IF_ACMPEQ, label

	def IF_ICMPGE(label as Label):
		emitJumpInsn Opcodes.IF_ICMPGE, label

	def IF_ICMPGT(label as Label):
		emitJumpInsn Opcodes.IF_ICMPGT, label

	def IF_ICMPLE(label as Label):
		emitJumpInsn Opcodes.IF_ICMPLE, label

	def IF_ICMPLT(label as Label):
		emitJumpInsn Opcodes.IF_ICMPLT, label

	def IFNE(label as Label):
		emitJumpInsn Opcodes.IFNE, label

	def IF_ACMPNE(label as Label):
		emitJumpInsn Opcodes.IF_ACMPNE, label

	def GOTO(label as Label):
		emitJumpInsn Opcodes.GOTO, label

	def INSTANCEOF(typeName as string):
		emitTypeInsn Opcodes.INSTANCEOF, typeName

	def CHECKCAST(typeName as string):
		emitTypeInsn Opcodes.CHECKCAST, typeName

	def ILOAD(index as int):
		emitVarInsn Opcodes.ILOAD, index

	def ICONST(value as int):
		if value >= -1 and value <= 5:
			emitInsn iconstOpcodeFor(value)
		elif value >= -127 and value <= 127:
			emitIntInsn Opcodes.BIPUSH, value
		else:
			emitIntInsn Opcodes.SIPUSH, value

	def ICONST_0():
		emitInsn Opcodes.ICONST_0

	def ICONST_1():
		emitInsn Opcodes.ICONST_1

	def iconstOpcodeFor(value as int):
		if value == 0: return Opcodes.ICONST_0
		if value == 1: return Opcodes.ICONST_1
		if value == 2: return Opcodes.ICONST_2
		if value == 3: return Opcodes.ICONST_3
		if value == 4: return Opcodes.ICONST_4
		if value == 5: return Opcodes.ICONST_5
		if value == -1: return Opcodes.ICONST_M1
		raise System.ArgumentException("value")

	def ISTORE(index as int):
		emitVarInsn(Opcodes.ISTORE, index)

	def ACONST_NULL():
		emitInsn Opcodes.ACONST_NULL

	def ALOAD(index as int):
		emitVarInsn(Opcodes.ALOAD, index)

	def ANEWARRAY(type as string):
		emitTypeInsn Opcodes.ANEWARRAY, type

	def ARRAYLENGTH():
		emitInsn Opcodes.ARRAYLENGTH

	def AASTORE():
		emitInsn Opcodes.AASTORE

	def AALOAD():
		emitInsn Opcodes.AALOAD

	def ASTORE(index as int):
		emitVarInsn(Opcodes.ASTORE, index)

	def GETSTATIC(field as IField):
		emitLoadStaticField(field.DeclaringType, field.Name, field.Type)

	def GETFIELD(field as IField):
		emitField Opcodes.GETFIELD, field

	def PUTFIELD(field as IField):
		emitField Opcodes.PUTFIELD, field

	def INVOKESTATIC(method as IMethod):
		invoke(Opcodes.INVOKESTATIC, method)

	def INVOKEVIRTUAL(method as IMethod):
		invoke(Opcodes.INVOKEVIRTUAL, method)

	def INVOKESPECIAL(method as IMethod):
		invoke(Opcodes.INVOKESPECIAL, method)

	def INVOKEINTERFACE(method as IMethod):
		invoke(Opcodes.INVOKEINTERFACE, method)

	def TRYCATCHBLOCK(begin as Label, end as Label, target as Label, type as string):
		_code.visitTryCatchBlock(begin, end, target, type)

	def ATHROW():
		emitInsn(Opcodes.ATHROW)

	def RETURN():
	   emitInsn(Opcodes.RETURN)

	def ARETURN():
	   emitInsn(Opcodes.ARETURN)

	def IRETURN():
	   emitInsn(Opcodes.IRETURN)

	def POP():
		emitInsn(Opcodes.POP)

	def NEW(type as IType):
		emitTypeInsn(Opcodes.NEW, javaType(type))

	def DUP():
		emitInsn(Opcodes.DUP)

	def ISUB():
		emitInsn(Opcodes.ISUB)

	def IADD():
		emitInsn(Opcodes.IADD)

	def IMUL():
		emitInsn(Opcodes.IMUL)

	def emitVarInsn(opcode as int, index as int):
		_code.visitVarInsn(opcode, index)

	def emitInsn(i as int):
		_code.visitInsn(i)

	def emitIntInsn(opcode as int, value as int):
		_code.visitIntInsn(opcode, value)

	def emitJumpInsn(i as int, label as Label):
		_code.visitJumpInsn(i, label)

	def emitLdcInsn(value):
		_code.visitLdcInsn(value)

	def emitTypeInsn(opcode as int, name as string):
		_code.visitTypeInsn(opcode, name)

	def mark(label as Label):
		_code.visitLabel(label)

	def invoke(opcode as int, method as IMethod):
		invokeWithName opcode, method, methodName(method.Name)

	def invokeWithName(opcode as int, method as IMethod, methodName as string):
		_code.visitMethodInsn(
				opcode,
				javaType(method.DeclaringType),
				methodName,
				javaSignature(method))

	def emitLoadStaticField(declaringType as IType, fieldName as string, fieldType as IType):
		emitField Opcodes.GETSTATIC, declaringType, fieldName, fieldType

	def emitField(opcode as int, field as IField):
		emitField opcode, field.DeclaringType, field.Name, field.Type

	def emitField(opcode as int, declaringType as IType, fieldName as string, fieldType as IType):
		_code.visitFieldInsn(
				opcode,
				javaType(declaringType),
				fieldName,
				typeDescriptor(fieldType))

	def entity(node as Node):
		return GetEntity(node)

	def optionalEntity(node as Node):
		return self.TypeSystemServices.GetOptionalEntity(node)

	def javaType(typeRef as TypeReference):
		return javaType(entity(typeRef) as IType)

	def typeDescriptor(type as IType) as string:
		if type in _primitiveMappings: return _primitiveMappings[type]
		if type.IsArray: return "[" + typeDescriptor(type.GetElementType())
		return "L" + javaType(type) + ";"

	def javaType(type as IType) as string:
		if type in _typeMappings: return _typeMappings[type]
		return javaType(type.FullName)

	def javaType(typeName as string):
		return typeName.Replace('.', '/')


line continuations

line continuations

namespace Boo.OMeta.Parser

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

import System.Globalization

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:
		qq_begin = "[|"
		qq_end = "|]"
		equality = "=="
		inequality = "!="
		assign = "="
		assign_inplace = "+=" | "-=" | "*=" | "/=" | "%=" | "^=" | "&=" | "|="
		xor = "^"
		increment = "++"
		decrement = "--"
		plus = "+"
		minus = "-"
		exponentiation = "**"
		star = "*"
		division = "/"
		modulus = "%"
		ones_complement = "~"
		bitwise_shift_left = "<<"
		bitwise_shift_right = ">>"
		greater_than_eq = ">="
		greater_than = ">"
		less_than_eq = "<="
		less_than = "<"
		bitwise_and = "&"
		bitwise_or = "|"
		hexnum = ("0x", ++(hex_digit | digit) >> ds) ^ makeString(ds)
		num = ++digit
		colon = ":"
		dot = "."
		comma = ","
		lparen = "(", enterWhitespaceAgnosticRegion
		rparen = ")", leaveWhitespaceAgnosticRegion
		lbrack = "[", enterWhitespaceAgnosticRegion
		rbrack = "]", leaveWhitespaceAgnosticRegion
		lbrace = "{", enterWhitespaceAgnosticRegion
		rbrace = "}", leaveWhitespaceAgnosticRegion

		kw = (keywords >> value, ~(letter | digit | '_')) ^ value
		tdq = '"""'
		dq = '"'
		sqs = ("'", ++(~"'", _) >> s, "'") ^ s
		id = ((letter | '_') >> p, --(letter | digit | '_') >> s) ^ makeString(p, s)

	space = line_continuation | multi_line_comment | line_comment | super

	line_continuation = "\\", newline

	multi_line_comment = "/*", --(~"*/", (multi_line_comment | _)), "*/"

	line_comment = ('#' | "//"), --(~newline, _)

	hex_digit = _ >> c as char and ((c >= char('a') and c <= char('f')) or (c >= char('A') and c <= char('Z')))

	keywords "class", "def", "import", "pass", "return", "true", \
		"false", "and", "or", "as", "not", "if", "is", "null", \
		"for", "interface", "in", "yield", "self", "super", "of", \
		"event", "private", "protected", "internal", "public", "enum", \
		"callable", "unless"

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

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

	docstring = (TDQ, ++(~tdq, string_char) >> s, TDQ, eol) ^ makeString(s)

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

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

	module_member = assembly_attribute | type_def | method

	type_def = class_def | interface_def | enum_def | callable_def

	callable_def = (CALLABLE, ID >> name, method_parameters >> parameters, optional_type >> type, eol) ^ newCallable(name, parameters, type)

	class_def = (
		attributes >> attrs,
		member_modifiers >> mod,
		CLASS, ID >> className, super_types >> superTypes, begin_block, class_body >> body, end_block
	) ^ newClass(attrs, mod, className, superTypes, body)

	interface_def = (
		attributes >> attrs,
		member_modifiers >> mod,
		INTERFACE, ID >> name, super_types >> superTypes, begin_block, interface_body >> body, end_block
	) ^ newInterface(attrs, mod, name, superTypes, body)

	enum_def = (
		attributes >> attrs,
		member_modifiers >> mod,
		ENUM, ID >> name, begin_block, enum_body >> body, end_block
	) ^ newEnum(attrs, mod, name, body)

	enum_body = ++enum_field >> fields ^ fields

	enum_field = (attributes >> attrs, ID >> name, ((ASSIGN, expression >> e) | ""), eol) ^ newEnumField(attrs, name, e)

	super_types = ((LPAREN, optional_type_reference_list >> types, RPAREN) ^ types) | ""

	begin_block = COLON, INDENT

	end_block = DEDENT

	class_body = no_member | (++class_member >> members ^ members)

	interface_body = no_member

	no_member = (PASS, eol) ^ null

	class_member = type_def | method | property_def | field | event_def

	event_def = (
		attributes >> attrs,
		member_modifiers >> mod,
		EVENT, ID >> name, optional_type >> type, eol
	) ^ newEvent(attrs, mod, name, type)

	property_def = (
		attributes >> attrs,
		member_modifiers >> mod,
		ID >> name, property_parameters >> parameters, optional_type >> type,
		begin_block,
		(
			(property_getter >> pg, property_setter >> ps)
			| (property_setter >> ps, property_getter >> pg)
			| (property_setter >> ps)
			| (property_getter >> pg)
		),
		end_block
	) ^ newProperty(attrs, mod, name, parameters, type, pg, ps)

	property_parameters = ((LBRACK, parameter_list >> parameters, RBRACK) | "") ^ parameters

	property_getter = accessor["get"]

	property_setter = accessor["set"]

	accessor[key] = (
		attributes >> attrs,
		member_modifiers >> mod,
		(ID >> name and (tokenValue(name) == key)),
		block >> body
	) ^ newMethod(attrs, mod, name, null, null, null, body)

	field = (
		attributes >> attrs,
		member_modifiers >> mod,
		ID >> name, optional_type >> type, field_initializer >> initializer, eol
	) ^ newField(attrs, mod, name, type, initializer)

	field_initializer = (ASSIGN, rvalue) | ""

	member_modifiers = --((PRIVATE ^ TypeMemberModifiers.Private) | (PUBLIC ^ TypeMemberModifiers.Public)) >> all ^ all

	method = (
		attributes >> attrs,
		member_modifiers >> mod, DEF, ID >> name, method_parameters >> parameters,
			attributes >> returnTypeAttributes, optional_type >> type,
			block >> body
	) ^ newMethod(attrs, mod, name, parameters, returnTypeAttributes, type, body)

	method_parameters = (LPAREN, optional_parameter_list >> parameters, RPAREN) ^ parameters

	list_of parameter

	assembly_attribute = (
		LBRACK, (ID >> name and (tokenValue(name) == "assembly")), COLON,
		attribute_list >> value,
		RBRACK, eol) ^ value

	attributes = --((LBRACK, attribute_list >> value, RBRACK, --EOL) ^ value) >> all ^ all

	attribute = (ID >> name, attribute_arguments >> args) ^ newAttribute(name, args)

	attribute_arguments = invocation_arguments | ""

	list_of attribute

	parameter = (attributes >> attrs, ID >> name, optional_type >> type) ^ newParameterDeclaration(attrs, name, type)

	optional_type = (AS, type_reference) | ""

	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 | stmt_line

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

	stmt_macro = (ID >> name, assignment_list >> args, ((block >> b) | (stmt_modifier >> m))) ^ newMacro(name, args, b, m)

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

	stmt_modifier = ((stmt_modifier_node >> value, eol) ^ value) | (eol ^ null)

	stmt_modifier_node = (
		stmt_modifier_type >> t,
		assignment >> e
	) ^ newStatementModifier(t, e)

	stmt_modifier_type = (IF ^ StatementModifierType.If) | (UNLESS ^ StatementModifierType.Unless)

	stmt_declaration = (declaration >> d, ((ASSIGN, expression >> e) | ""), eol) ^ 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, rvalue >> e, block >> body) ^ newForStatement(dl, e, body)

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

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

	optional_assignment = assignment | ""

	stmt_expression = ((multi_assignment | assignment) >> e, stmt_modifier >> m) ^ ExpressionStatement(Expression: e, Modifier: m)

	multi_assignment = (expression >> l, ASSIGN >> op, rvalue >> r) ^ newInfixExpression(op, l, r)

	rvalue = assignment_list >> items ^ newRValue(items)

	list_of assignment

	infixr assignment, (ASSIGN | ASSIGN_INPLACE), expression

	expression = generator_expression | or_expression

	generator_expression = (
		or_expression >> projection,
		++generator_expression_body >> body
	) ^ newGeneratorExpression(projection, body)

	generator_expression_body = (FOR, declaration_list >> dl, IN, or_expression >> e, optional_filter >> f) ^ newGeneratorExpressionBody(dl, e, f)

	optional_filter = ((stmt_modifier_type >> t, or_expression >> e) ^ newStatementModifier(t, e)) | ""

	infix or_expression, OR, and_expression

	infix and_expression, AND, not_expression

	prefix not_expression, NOT, membership_expression

	infix membership_expression, (IN | ((NOT, IN) ^ makeToken("not in"))), identity_test_expression

	infix identity_test_expression, (((IS, NOT) ^ makeToken("is not")) | IS), comparison

	infix comparison, (EQUALITY | INEQUALITY | GREATER_THAN | GREATER_THAN_EQ | LESS_THAN | LESS_THAN_EQ), 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_LEFT | BITWISE_SHIFT_RIGHT), term

	infix term, (PLUS | MINUS), factor

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

	prefix signalled_expression, (MINUS | INCREMENT | DECREMENT), ones_complement_expression

	prefix ones_complement_expression, ONES_COMPLEMENT, 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 = ((member_reference >> e, DOT, ID >> name) ^ newMemberReference(e, name)) | slicing

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

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

	list_of expression

	list_of declaration

	list_of type_reference

	list_of slice

	omitted_expression = (COLON, expression) | (COLON ^ OmittedExpression.Default)

	invocation = ((member_reference >> target, invocation_arguments >> args) ^ newInvocation(target, args)) \
		| atom

	invocation_arguments = (LPAREN, optional_invocation_argument_list >> args, RPAREN) ^ args

	invocation_argument = named_argument | assignment

	list_of invocation_argument

	named_argument = (ID >> name, COLON, assignment >> value) ^ newNamedArgument(name, value)

	type_reference = type_reference_simple | type_reference_array | type_reference_callable

	type_reference_callable = (
		CALLABLE, LPAREN, optional_type_reference_list >> params, RPAREN, optional_type >> type
	) ^ newCallableTypeReference(params, type)

	type_reference_array = (LPAREN, ranked_type_reference >> tr, RPAREN) ^ tr

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

	atom = integer | boolean | reference | array_literal | list_literal \
		| string_interpolation | string_literal | null_literal | parenthesized_expression  \
		| self_literal | super_literal | quasi_quote | hash_literal

	quasi_quote = quasi_quote_member | quasi_quote_module | quasi_quote_expression | quasi_quote_stmt

	quasi_quote_module = (QQ_BEGIN, INDENT, module >> m, DEDENT, QQ_END) ^ newQuasiquoteBlock(m)

	quasi_quote_member = (QQ_BEGIN, INDENT, class_member >> m, DEDENT, QQ_END) ^ newQuasiquoteBlock(m)

	quasi_quote_expression = (QQ_BEGIN, rvalue >> s, QQ_END) ^ newQuasiquoteExpression(s)

	quasi_quote_stmt = (QQ_BEGIN, (qq_return | qq_macro) >> s, QQ_END) ^ newQuasiquoteExpression(s)

	qq_return = (RETURN, optional_assignment >> e) ^ ReturnStatement(Expression: e)

	qq_macro = (ID >> name, assignment_list >> args) ^ newMacro(name, args, null, null)

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

	null_literal = NULL ^ [| null |]

	super_literal = SUPER ^ [| super |]

	self_literal = SELF ^ [| self |]

	string_literal = (SQS >> s) ^ newStringLiteral(s)

	string_interpolation = (
		DQ,
		++(
			((++(~('"' | '$'), string_char) >> s) ^ StringLiteralExpression(makeString(s)))
			| (('${', expression >> v, --space, '}') ^ v)
			| ('$', atom)
			) >> items,
		DQ) ^ newStringInterpolation(items)

	string_char = ('\\', ('\\' | '$')) | (~'\\', _)

	array_literal = array_literal_empty | array_literal_single | array_literal_multi

	array_literal_empty = (LPAREN, array_literal_type >> type, COMMA, RPAREN) ^ newArrayLiteral(type, [])

	array_literal_single = (LPAREN, array_literal_type >> type, assignment >> e, COMMA, RPAREN) ^ newArrayLiteral(type, [e])

	array_literal_multi = (LPAREN, array_literal_type >> type, assignment >> e, ++(COMMA, assignment) >> tail, (COMMA | ""), RPAREN) ^ newArrayLiteral(type, prepend(e, tail))

	array_literal_type = ((OF, ranked_type_reference >> type, COLON) | "") ^ type

	ranked_type_reference = ((type_reference >> type), ((COMMA,  integer >> rank) | "")) ^ ArrayTypeReference(ElementType: type, Rank: rank)

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

	hash_literal = (LBRACE, optional_expression_pair_list >> items, optional_comma, RBRACE) ^ newHashLiteral(items)

	optional_comma = COMMA | ""

	expression_pair = (assignment >> first, COLON, assignment >> second) ^ ExpressionPair(First: first, Second: second)

	list_of expression_pair

	reference = ID >> r ^ newReference(r)

	integer = (NUM >> n ^ newInteger(n, NumberStyles.None)) \
		| (HEXNUM >> n ^ newInteger(n, NumberStyles.HexNumber))

	boolean = true_literal | false_literal

	true_literal = TRUE ^ [| true |]

	false_literal = FALSE ^ [| false |]

	eol = ++EOL | ~_

namespace Boo.OMeta.Parser

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

import System.Globalization

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:
		qq_begin = "[|"
		qq_end = "|]"
		equality = "=="
		inequality = "!="
		assign = "="
		assign_inplace = "+=" | "-=" | "*=" | "/=" | "%=" | "^=" | "&=" | "|="
		xor = "^"
		increment = "++"
		decrement = "--"
		plus = "+"
		minus = "-"
		exponentiation = "**"
		star = "*"
		division = "/"
		modulus = "%"
		ones_complement = "~"
		bitwise_shift_left = "<<"
		bitwise_shift_right = ">>"
		greater_than_eq = ">="
		greater_than = ">"
		less_than_eq = "<="
		less_than = "<"
		bitwise_and = "&"
		bitwise_or = "|"
		hexnum = ("0x", ++(hex_digit | digit) >> ds) ^ makeString(ds)
		num = ++digit
		colon = ":"
		dot = "."
		comma = ","
		lparen = "(", enterWhitespaceAgnosticRegion
		rparen = ")", leaveWhitespaceAgnosticRegion
		lbrack = "[", enterWhitespaceAgnosticRegion
		rbrack = "]", leaveWhitespaceAgnosticRegion
		lbrace = "{", enterWhitespaceAgnosticRegion
		rbrace = "}", leaveWhitespaceAgnosticRegion

		kw = (keywords >> value, ~(letter | digit | '_')) ^ value
		tdq = '"""'
		dq = '"'
		sqs = ("'", ++(~"'", _) >> s, "'") ^ s
		id = ((letter | '_') >> p, --(letter | digit | '_') >> s) ^ makeString(p, s)

	space = multi_line_comment | line_comment | super

	multi_line_comment = "/*", --(~"*/", (multi_line_comment | _)), "*/"

	line_comment = ('#' | "//"), --(~newline, _)

	hex_digit = _ >> c as char and ((c >= char('a') and c <= char('f')) or (c >= char('A') and c <= char('Z')))

	keywords "class", "def", "import", "pass", "return", "true", \
		"false", "and", "or", "as", "not", "if", "is", "null", \
		"for", "interface", "in", "yield", "self", "super", "of", \
		"event", "private", "protected", "internal", "public", "enum", \
		"callable", "unless"

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

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

	docstring = (TDQ, ++(~tdq, string_char) >> s, TDQ, eol) ^ makeString(s)

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

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

	module_member = assembly_attribute | type_def | method

	type_def = class_def | interface_def | enum_def | callable_def

	callable_def = (CALLABLE, ID >> name, method_parameters >> parameters, optional_type >> type, eol) ^ newCallable(name, parameters, type)

	class_def = (
		attributes >> attrs,
		member_modifiers >> mod,
		CLASS, ID >> className, super_types >> superTypes, begin_block, class_body >> body, end_block
	) ^ newClass(attrs, mod, className, superTypes, body)

	interface_def = (
		attributes >> attrs,
		member_modifiers >> mod,
		INTERFACE, ID >> name, super_types >> superTypes, begin_block, interface_body >> body, end_block
	) ^ newInterface(attrs, mod, name, superTypes, body)

	enum_def = (
		attributes >> attrs,
		member_modifiers >> mod,
		ENUM, ID >> name, begin_block, enum_body >> body, end_block
	) ^ newEnum(attrs, mod, name, body)

	enum_body = ++enum_field >> fields ^ fields

	enum_field = (attributes >> attrs, ID >> name, ((ASSIGN, expression >> e) | ""), eol) ^ newEnumField(attrs, name, e)

	super_types = ((LPAREN, optional_type_reference_list >> types, RPAREN) ^ types) | ""

	begin_block = COLON, INDENT

	end_block = DEDENT

	class_body = no_member | (++class_member >> members ^ members)

	interface_body = no_member

	no_member = (PASS, eol) ^ null

	class_member = type_def | method | property_def | field | event_def

	event_def = (
		attributes >> attrs,
		member_modifiers >> mod,
		EVENT, ID >> name, optional_type >> type, eol
	) ^ newEvent(attrs, mod, name, type)

	property_def = (
		attributes >> attrs,
		member_modifiers >> mod,
		ID >> name, property_parameters >> parameters, optional_type >> type,
		begin_block,
		(
			(property_getter >> pg, property_setter >> ps)
			| (property_setter >> ps, property_getter >> pg)
			| (property_setter >> ps)
			| (property_getter >> pg)
		),
		end_block
	) ^ newProperty(attrs, mod, name, parameters, type, pg, ps)

	property_parameters = ((LBRACK, parameter_list >> parameters, RBRACK) | "") ^ parameters

	property_getter = accessor["get"]

	property_setter = accessor["set"]

	accessor[key] = (
		attributes >> attrs,
		member_modifiers >> mod,
		(ID >> name and (tokenValue(name) == key)),
		block >> body
	) ^ newMethod(attrs, mod, name, null, null, null, body)

	field = (
		attributes >> attrs,
		member_modifiers >> mod,
		ID >> name, optional_type >> type, field_initializer >> initializer, eol
	) ^ newField(attrs, mod, name, type, initializer)

	field_initializer = (ASSIGN, rvalue) | ""

	member_modifiers = --((PRIVATE ^ TypeMemberModifiers.Private) | (PUBLIC ^ TypeMemberModifiers.Public)) >> all ^ all

	method = (
		attributes >> attrs,
		member_modifiers >> mod, DEF, ID >> name, method_parameters >> parameters,
			attributes >> returnTypeAttributes, optional_type >> type,
			block >> body
	) ^ newMethod(attrs, mod, name, parameters, returnTypeAttributes, type, body)

	method_parameters = (LPAREN, optional_parameter_list >> parameters, RPAREN) ^ parameters

	list_of parameter

	assembly_attribute = (
		LBRACK, (ID >> name and (tokenValue(name) == "assembly")), COLON,
		attribute_list >> value,
		RBRACK, eol) ^ value

	attributes = --((LBRACK, attribute_list >> value, RBRACK, --EOL) ^ value) >> all ^ all

	attribute = (ID >> name, attribute_arguments >> args) ^ newAttribute(name, args)

	attribute_arguments = invocation_arguments | ""

	list_of attribute

	parameter = (attributes >> attrs, ID >> name, optional_type >> type) ^ newParameterDeclaration(attrs, name, type)

	optional_type = (AS, type_reference) | ""

	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 | stmt_line

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

	stmt_macro = (ID >> name, assignment_list >> args, ((block >> b) | (stmt_modifier >> m))) ^ newMacro(name, args, b, m)

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

	stmt_modifier = ((stmt_modifier_node >> value, eol) ^ value) | (eol ^ null)

	stmt_modifier_node = (
		stmt_modifier_type >> t,
		assignment >> e
	) ^ newStatementModifier(t, e)

	stmt_modifier_type = (IF ^ StatementModifierType.If) | (UNLESS ^ StatementModifierType.Unless)

	stmt_declaration = (declaration >> d, ((ASSIGN, expression >> e) | ""), eol) ^ 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, rvalue >> e, block >> body) ^ newForStatement(dl, e, body)

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

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

	optional_assignment = assignment | ""

	stmt_expression = ((multi_assignment | assignment) >> e, stmt_modifier >> m) ^ ExpressionStatement(Expression: e, Modifier: m)

	multi_assignment = (expression >> l, ASSIGN >> op, rvalue >> r) ^ newInfixExpression(op, l, r)

	rvalue = assignment_list >> items ^ newRValue(items)

	list_of assignment

	infixr assignment, (ASSIGN | ASSIGN_INPLACE), expression

	expression = generator_expression | or_expression

	generator_expression = (
		or_expression >> projection,
		++generator_expression_body >> body
	) ^ newGeneratorExpression(projection, body)

	generator_expression_body = (FOR, declaration_list >> dl, IN, or_expression >> e, optional_filter >> f) ^ newGeneratorExpressionBody(dl, e, f)

	optional_filter = ((stmt_modifier_type >> t, or_expression >> e) ^ newStatementModifier(t, e)) | ""

	infix or_expression, OR, and_expression

	infix and_expression, AND, not_expression

	prefix not_expression, NOT, membership_expression

	infix membership_expression, (IN | ((NOT, IN) ^ makeToken("not in"))), identity_test_expression

	infix identity_test_expression, (((IS, NOT) ^ makeToken("is not")) | IS), comparison

	infix comparison, (EQUALITY | INEQUALITY | GREATER_THAN | GREATER_THAN_EQ | LESS_THAN | LESS_THAN_EQ), 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_LEFT | BITWISE_SHIFT_RIGHT), term

	infix term, (PLUS | MINUS), factor

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

	prefix signalled_expression, (MINUS | INCREMENT | DECREMENT), ones_complement_expression

	prefix ones_complement_expression, ONES_COMPLEMENT, 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 = ((member_reference >> e, DOT, ID >> name) ^ newMemberReference(e, name)) | slicing

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

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

	list_of expression

	list_of declaration

	list_of type_reference

	list_of slice

	omitted_expression = (COLON, expression) | (COLON ^ OmittedExpression.Default)

	invocation = ((member_reference >> target, invocation_arguments >> args) ^ newInvocation(target, args)) \
		| atom

	invocation_arguments = (LPAREN, optional_invocation_argument_list >> args, RPAREN) ^ args

	invocation_argument = named_argument | assignment

	list_of invocation_argument

	named_argument = (ID >> name, COLON, assignment >> value) ^ newNamedArgument(name, value)

	type_reference = type_reference_simple | type_reference_array | type_reference_callable

	type_reference_callable = (
		CALLABLE, LPAREN, optional_type_reference_list >> params, RPAREN, optional_type >> type
	) ^ newCallableTypeReference(params, type)

	type_reference_array = (LPAREN, ranked_type_reference >> tr, RPAREN) ^ tr

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

	atom = integer | boolean | reference | array_literal | list_literal \
		| string_interpolation | string_literal | null_literal | parenthesized_expression  \
		| self_literal | super_literal | quasi_quote | hash_literal

	quasi_quote = quasi_quote_member | quasi_quote_module | quasi_quote_expression | quasi_quote_stmt

	quasi_quote_module = (QQ_BEGIN, INDENT, module >> m, DEDENT, QQ_END) ^ newQuasiquoteBlock(m)

	quasi_quote_member = (QQ_BEGIN, INDENT, class_member >> m, DEDENT, QQ_END) ^ newQuasiquoteBlock(m)

	quasi_quote_expression = (QQ_BEGIN, rvalue >> s, QQ_END) ^ newQuasiquoteExpression(s)

	quasi_quote_stmt = (QQ_BEGIN, (qq_return | qq_macro) >> s, QQ_END) ^ newQuasiquoteExpression(s)

	qq_return = (RETURN, optional_assignment >> e) ^ ReturnStatement(Expression: e)

	qq_macro = (ID >> name, assignment_list >> args) ^ newMacro(name, args, null, null)

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

	null_literal = NULL ^ [| null |]

	super_literal = SUPER ^ [| super |]

	self_literal = SELF ^ [| self |]

	string_literal = (SQS >> s) ^ newStringLiteral(s)

	string_interpolation = (
		DQ,
		++(
			((++(~('"' | '$'), string_char) >> s) ^ StringLiteralExpression(makeString(s)))
			| (('${', expression >> v, --space, '}') ^ v)
			| ('$', atom)
			) >> items,
		DQ) ^ newStringInterpolation(items)

	string_char = ('\\', ('\\' | '$')) | (~'\\', _)

	array_literal = array_literal_empty | array_literal_single | array_literal_multi

	array_literal_empty = (LPAREN, array_literal_type >> type, COMMA, RPAREN) ^ newArrayLiteral(type, [])

	array_literal_single = (LPAREN, array_literal_type >> type, assignment >> e, COMMA, RPAREN) ^ newArrayLiteral(type, [e])

	array_literal_multi = (LPAREN, array_literal_type >> type, assignment >> e, ++(COMMA, assignment) >> tail, (COMMA | ""), RPAREN) ^ newArrayLiteral(type, prepend(e, tail))

	array_literal_type = ((OF, ranked_type_reference >> type, COLON) | "") ^ type

	ranked_type_reference = ((type_reference >> type), ((COMMA,  integer >> rank) | "")) ^ ArrayTypeReference(ElementType: type, Rank: rank)

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

	hash_literal = (LBRACE, optional_expression_pair_list >> items, optional_comma, RBRACE) ^ newHashLiteral(items)

	optional_comma = COMMA | ""

	expression_pair = (assignment >> first, COLON, assignment >> second) ^ ExpressionPair(First: first, Second: second)

	list_of expression_pair

	reference = ID >> r ^ newReference(r)

	integer = (NUM >> n ^ newInteger(n, NumberStyles.None)) \
		| (HEXNUM >> n ^ newInteger(n, NumberStyles.HexNumber))

	boolean = true_literal | false_literal

	true_literal = TRUE ^ [| true |]

	false_literal = FALSE ^ [| false |]

	eol = ++EOL | ~_