is, is not

is, is not

namespace Boo.OMeta.Parser

import Boo.OMeta
import Boo.PatternMatching

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:
		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)

	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", "in", "yield", "self", "super"

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

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

	docstring = (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 = class_def | method

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

	begin_block = COLON, INDENT

	end_block = DEDENT

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

	class_member = method | class_def

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

	list_of parameter

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

	block = empty_block | non_empty_block

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

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

	stmt = stmt_block | 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 = ((IF, assignment >> e, eol) ^ StatementModifier(Type: StatementModifierType.If, Condition: e)) | (eol ^ null)

	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, expression >> e, block >> body) ^ newForStatement(dl, e, body)

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

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

	optional_expression = expression | ""

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

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

	list_of assignment

	infixr assignment, (ASSIGN | ASSIGN_INPLACE), expression

	expression = or_expression

	infix or_expression, OR, and_expression

	infix and_expression, AND, not_expression

	prefix not_expression, NOT, membership_expression

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

	// need to fix memoization for rule with arguments
	infix identity_test_expression, (IS, ((NOT ^ makeToken("is not")) | ("" ^ makeToken("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 slice

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

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

	type_reference = type_reference_simple

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

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

	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, --whitespace, '}') ^ v)
			| ('$', atom)
			) >> items,
		DQ) ^ newStringInterpolation(items)

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

	array_literal = array_literal_empty | array_literal_single | array_literal_multi

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

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

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

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

	list_of expression

	list_of declaration

	reference = ID >> r ^ newReference(r)

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

	boolean = true_literal | false_literal

	true_literal = TRUE ^ [| true |]

	false_literal = FALSE ^ [| false |]

	eol = ++EOL | ~_

	def newMacro(name, args, body, m):
		node = MacroStatement(Name: tokenValue(name), Block: body, Modifier: m)
		for arg in args: node.Arguments.Add(arg)
		return node

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

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

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

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

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

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

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

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

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

	def newInteger(t, style as NumberStyles):
		value = int.Parse(tokenValue(t), style)
		return IntegerLiteralExpression(Value: value)

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

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

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

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

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

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

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

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

	def newStringInterpolation(items as List):
		if len(items) == 1 and items[0] isa StringLiteralExpression:
			return items[0]
		node = ExpressionInterpolationExpression()
		for item in items: node.Expressions.Add(item)
		return node

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

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

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

	def binaryOperatorFor(op):
		match tokenValue(op):
			case "is": return BinaryOperatorType.ReferenceEquality
			case "is not": return BinaryOperatorType.ReferenceInequality
			case "in": return BinaryOperatorType.Member
			case "not in": return BinaryOperatorType.NotMember
			case "and": return BinaryOperatorType.And
			case "or": return BinaryOperatorType.Or
			case "|": return BinaryOperatorType.BitwiseOr
			case "&": return BinaryOperatorType.BitwiseAnd
			case "^": return BinaryOperatorType.ExclusiveOr
			case "+": return BinaryOperatorType.Addition
			case "-": return BinaryOperatorType.Subtraction
			case "*": return BinaryOperatorType.Multiply
			case "**": return BinaryOperatorType.Exponentiation
			case "/": return BinaryOperatorType.Division
			case "%": return BinaryOperatorType.Modulus
			case "=": return BinaryOperatorType.Assign
			case "==": return BinaryOperatorType.Equality
			case "!=": return BinaryOperatorType.Inequality
			case "+=": return BinaryOperatorType.InPlaceAddition
			case "-=": return BinaryOperatorType.InPlaceSubtraction
			case "/=": return BinaryOperatorType.InPlaceDivision
			case "*=": return BinaryOperatorType.InPlaceMultiply
			case "^=": return BinaryOperatorType.InPlaceExclusiveOr
			case "&=": return BinaryOperatorType.InPlaceBitwiseAnd
			case "|=": return BinaryOperatorType.InPlaceBitwiseOr
			case ">>": return BinaryOperatorType.ShiftRight
			case "<<": return BinaryOperatorType.ShiftLeft
			case "<": return BinaryOperatorType.LessThan
			case "<=": return BinaryOperatorType.LessThanOrEqual
			case ">": return BinaryOperatorType.GreaterThan
			case ">=": return BinaryOperatorType.GreaterThanOrEqual

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

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

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

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

namespace Boo.OMeta.Parser

import Boo.OMeta
import Boo.PatternMatching

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

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:
		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)

	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", "in", "yield", "self", "super"

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

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

	docstring = (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 = class_def | method

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

	begin_block = COLON, INDENT

	end_block = DEDENT

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

	class_member = method | class_def

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

	list_of parameter

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

	block = empty_block | non_empty_block

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

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

	stmt = stmt_block | 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 = ((IF, assignment >> e, eol) ^ StatementModifier(Type: StatementModifierType.If, Condition: e)) | (eol ^ null)

	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, expression >> e, block >> body) ^ newForStatement(dl, e, body)

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

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

	optional_expression = expression | ""

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

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

	list_of assignment

	infixr assignment, (ASSIGN | ASSIGN_INPLACE), expression

	expression = or_expression

	infix or_expression, OR, and_expression

	infix and_expression, AND, not_expression

	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 slice

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

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

	type_reference = type_reference_simple

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

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

	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, --whitespace, '}') ^ v)
			| ('$', atom)
			) >> items,
		DQ) ^ newStringInterpolation(items)

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

	array_literal = array_literal_empty | array_literal_single | array_literal_multi

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

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

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

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

	list_of expression

	list_of declaration

	reference = ID >> r ^ newReference(r)

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

	boolean = true_literal | false_literal

	true_literal = TRUE ^ [| true |]

	false_literal = FALSE ^ [| false |]

	eol = ++EOL | ~_

	def newMacro(name, args, body, m):
		node = MacroStatement(Name: tokenValue(name), Block: body, Modifier: m)
		for arg in args: node.Arguments.Add(arg)
		return node

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

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

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

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

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

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

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

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

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

	def newInteger(t, style as NumberStyles):
		value = int.Parse(tokenValue(t), style)
		return IntegerLiteralExpression(Value: value)

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

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

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

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

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

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

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

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

	def newStringInterpolation(items as List):
		if len(items) == 1 and items[0] isa StringLiteralExpression:
			return items[0]
		node = ExpressionInterpolationExpression()
		for item in items: node.Expressions.Add(item)
		return node

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

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

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

	def binaryOperatorFor(op):
		match tokenValue(op):
			case "is": return BinaryOperatorType.ReferenceEquality
			case "is not": return BinaryOperatorType.ReferenceInequality
			case "in": return BinaryOperatorType.Member
			case "not in": return BinaryOperatorType.NotMember
			case "and": return BinaryOperatorType.And
			case "or": return BinaryOperatorType.Or
			case "|": return BinaryOperatorType.BitwiseOr
			case "&": return BinaryOperatorType.BitwiseAnd
			case "^": return BinaryOperatorType.ExclusiveOr
			case "+": return BinaryOperatorType.Addition
			case "-": return BinaryOperatorType.Subtraction
			case "*": return BinaryOperatorType.Multiply
			case "**": return BinaryOperatorType.Exponentiation
			case "/": return BinaryOperatorType.Division
			case "%": return BinaryOperatorType.Modulus
			case "=": return BinaryOperatorType.Assign
			case "==": return BinaryOperatorType.Equality
			case "!=": return BinaryOperatorType.Inequality
			case "+=": return BinaryOperatorType.InPlaceAddition
			case "-=": return BinaryOperatorType.InPlaceSubtraction
			case "/=": return BinaryOperatorType.InPlaceDivision
			case "*=": return BinaryOperatorType.InPlaceMultiply
			case "^=": return BinaryOperatorType.InPlaceExclusiveOr
			case "&=": return BinaryOperatorType.InPlaceBitwiseAnd
			case "|=": return BinaryOperatorType.InPlaceBitwiseOr
			case ">>": return BinaryOperatorType.ShiftRight
			case "<<": return BinaryOperatorType.ShiftLeft
			case "<": return BinaryOperatorType.LessThan
			case "<=": return BinaryOperatorType.LessThanOrEqual
			case ">": return BinaryOperatorType.GreaterThan
			case ">=": return BinaryOperatorType.GreaterThanOrEqual

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

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

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

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


add more tests to unary negation testcase

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

"""
-3
32767
SHORT OVERFLOW
-32768
-4
ULONG OVERFLOW
"""

i = 3
print(-i)

d as double = -1.0
d = -d
assert d == 1.0

s as short = -32767 #-short.MaxValue
s = -s
print s
s = short.MinValue #-32768
try:
	s = -s
except:
	print "SHORT OVERFLOW"

unchecked:
	s = -s
print s

l as long = 4
l = -l
print l

u as ulong = 424242
try:
	u = -u
except:
	print "ULONG OVERFLOW"


more useful queries

more useful queries

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

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

"""
This example shows how to use db4o 3.0 (http://www.db4o.com), a great
oodbm infrastructure, to implement a simple time tracking application.
"""

namespace TimeTracker

import System
import System.ComponentModel
import System.IO
import System.Drawing
import System.Windows.Forms
import TimeTracker.Model

class MainForm(Form):

	_system as TimeTrackerSystem

	_components = System.ComponentModel.Container()

	_current as Activity

	_tabs = TabControl(Dock: DockStyle.Fill)

	_view = ListView(View: View.Details, Dock: DockStyle.Fill)

	_timer = System.Windows.Forms.Timer(_components,
										Tick: UpdateNotifyText,
										Interval: 30000)

	_notify as NotifyIcon

	def constructor():
		self.Text = "Boo Time Tracker (powered by db4o)"
		self.ShowInTaskbar = false
		self.MinimizeBox = false
		Minimize()

		_notify = NotifyIcon(_components,
				Text: self.Text,
				Icon: self.Icon,
				Visible: true,
				ContextMenu: CreateContextMenu(),
				DoubleClick: { self.WindowState = WindowState.Normal })

		_system = TimeTrackerSystem(
						Path.Combine(
							Application.UserAppDataPath,
							"timetracker.yap"))

		activitiesPage = TabPage(Text: "Activities")
		activitiesPage.Controls.Add(_view)

		prompt = booish.gui.PromptBox(
						Font: System.Drawing.Font("Lucida Console", 11))
		prompt.Interpreter.SetValue("system", _system)
		prompt.Interpreter.SetValue("MainForm", self)
		prompt.Interpreter.References.Add(typeof(Project).Assembly)
		promptPage = TabPage(Text: "Console")
		promptPage.Controls.Add(prompt)
		_tabs.TabPages.AddRange((activitiesPage, promptPage))

		self.Controls.Add(_tabs)

		_timer.Start()

	def UpdateNotifyText():
		if _current is null:
			_notify.Text = self.Text
		else:
			_current.Finished = date.Now
			_notify.Text = _current.ToString()

	override def OnClosing(args as CancelEventArgs):
		Minimize()
		args.Cancel = true
		super(args)

	def Minimize():
		self.WindowState = WindowState.Minimized

	def _contextMenu_Popup(sender as ContextMenu):

		sender.MenuItems.Clear()

		for project in _system.Projects:
			sender.MenuItems.Add(CreateProjectMenuItem(project))

		sender.MenuItems.Add(MenuItem("-"))
		sender.MenuItems.Add(
			MenuItem(Text: "New Project",
					Click: NewProject))
		sender.MenuItems.Add(MenuItem("-"))
		sender.MenuItems.Add(
			MenuItem(Text: "Quit",
					Click: Application.Exit))

	def CreateProjectMenuItem(project as Project):
		menu = MenuItem(Text: project.Name)
		for t in _system.QueryTasks(project):
			menu.MenuItems.Add(
				MenuItem(Text: t.Name, Click: { StartTask(t) }))

		menu.MenuItems.Add(MenuItem("-"))
		menu.MenuItems.Add(
			MenuItem(Text: "New Task",
					Click: { NewTask(project) }))
		return menu

	def CreateContextMenu():
		return System.Windows.Forms.ContextMenu(Popup: _contextMenu_Popup)

	def FlushCurrentActivity():
		 return if _current is null

		 _current.Finished = date.Now
		 _system.AddActivity(_current)

		 _current = null

	def StartTask(task as Task):
		FlushCurrentActivity()
		_current = Activity(Task: task, Started: date.Now)
		UpdateNotifyText()

	def NewTask(project as Project):
		name = Prompt("New Task", "Task Name: ")
		if name is not null:
			_system.AddTask(Task(Project: project, Name: name))

	def NewProject():
		name = Prompt("New Project", "Project's Name: ")
		if name is not null:
			_system.AddProject(Project(Name: name))

	def Prompt(title as string, message as string):
		dlg = PromptDialog(Text: title,
						Message: message,
						Font: self.Font,
						StartPosition: FormStartPosition.CenterScreen)
		if DialogResult.OK == dlg.ShowDialog():
			return dlg.Value

	override def Dispose(disposing as bool):
		FlushCurrentActivity()
		_components.Dispose()
		_system.Dispose()
		super(disposing)

class PromptDialog(Form):

	_value as TextBox
	_message as Label

	def constructor():

		_message = Label(Location: Point(2, 2),
						Size: System.Drawing.Size(200, 18))
		_value = TextBox(
						Location: Point(2, 20),
						Size: System.Drawing.Size(290, 18))

		ok = Button(Text: "OK",
					Location: Point(50, 45),
					DialogResult: DialogResult.OK)

		cancel = Button(Text: "Cancel",
					Location: Point(150, 45),
					DialogResult: DialogResult.Cancel)

		SuspendLayout()
		self.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog
		self.StartPosition = FormStartPosition.CenterParent
		self.Size = System.Drawing.Size(300, 120)
		self.AcceptButton = ok
		self.CancelButton = cancel
		Controls.Add(_message)
		Controls.Add(_value)
		Controls.Add(ok)
		Controls.Add(cancel)
		ResumeLayout(false)

	Message as string:
		set:
			_message.Text = value

	Value:
		get:
			return _value.Text
		set:
			_value.Text = value

Application.Run(MainForm())

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

"""
This example shows how to use db4o 3.0 (http://www.db4o.com), a great
oodbm infrastructure, to implement a simple time tracking application.
"""

namespace TimeTracker

import System
import System.ComponentModel
import System.IO
import System.Drawing
import System.Windows.Forms
import TimeTracker.Model

class MainForm(Form):

	_system as TimeTrackerSystem

	_components = System.ComponentModel.Container()

	_current as Activity

	_tabs = TabControl(Dock: DockStyle.Fill)

	_view = ListView(View: View.Details, Dock: DockStyle.Fill)

	_timer = System.Windows.Forms.Timer(_components,
										Tick: UpdateNotifyText,
										Interval: 30000)

	_notify as NotifyIcon

	def constructor():
		self.Text = "Boo Time Tracker (powered by db4o)"
		self.ShowInTaskbar = false
		self.MinimizeBox = false
		Minimize()

		_notify = NotifyIcon(_components,
				Text: self.Text,
				Icon: self.Icon,
				Visible: true,
				ContextMenu: CreateContextMenu(),
				DoubleClick: { self.WindowState = WindowState.Normal })

		_system = TimeTrackerSystem(
						Path.Combine(
							Application.UserAppDataPath,
							"timetracker.yap"))

		activitiesPage = TabPage(Text: "Activities")
		activitiesPage.Controls.Add(_view)

		prompt = booish.gui.PromptBox(
						Font: System.Drawing.Font("Lucida Console", 11))
		prompt.Interpreter.SetValue("system", _system)
		prompt.Interpreter.SetValue("MainForm", self)
		prompt.Interpreter.References.Add(typeof(Project).Assembly)
		promptPage = TabPage(Text: "Console")
		promptPage.Controls.Add(prompt)
		_tabs.TabPages.AddRange((activitiesPage, promptPage))

		self.Controls.Add(_tabs)

		_timer.Start()

	def UpdateNotifyText():
		if _current is null:
			_notify.Text = self.Text
		else:
			_current.Finished = date.Now
			_notify.Text = _current.ToString()

	override def OnClosing(args as CancelEventArgs):
		Minimize()
		args.Cancel = true
		super(args)

	def Minimize():
		self.WindowState = WindowState.Minimized

	def _contextMenu_Popup(sender as ContextMenu):

		sender.MenuItems.Clear()

		for project in _system.Projects:
			sender.MenuItems.Add(CreateProjectMenuItem(project))

		sender.MenuItems.Add(MenuItem("-"))
		sender.MenuItems.Add(
			MenuItem(Text: "New Project",
					Click: NewProject))
		sender.MenuItems.Add(MenuItem("-"))
		sender.MenuItems.Add(
			MenuItem(Text: "Quit",
					Click: Application.Exit))

	def CreateProjectMenuItem(project as Project):
		menu = MenuItem(Text: project.Name)
		for t in _system.QueryTasksByProject(project):
			menu.MenuItems.Add(
				MenuItem(Text: t.Name, Click: { StartTask(t) }))

		menu.MenuItems.Add(MenuItem("-"))
		menu.MenuItems.Add(
			MenuItem(Text: "New Task",
					Click: { NewTask(project) }))
		return menu

	def CreateContextMenu():
		return System.Windows.Forms.ContextMenu(Popup: _contextMenu_Popup)

	def FlushCurrentActivity():
		 return if _current is null

		 _current.Finished = date.Now
		 _system.AddActivity(_current)

		 _current = null

	def StartTask(task as Task):
		FlushCurrentActivity()
		_current = Activity(Task: task, Started: date.Now)
		UpdateNotifyText()

	def NewTask(project as Project):
		name = Prompt("New Task", "Task Name: ")
		if name is not null:
			_system.AddTask(Task(Project: project, Name: name))

	def NewProject():
		name = Prompt("New Project", "Project's Name: ")
		if name is not null:
			_system.AddProject(Project(Name: name))

	def Prompt(title as string, message as string):
		dlg = PromptDialog(Text: title,
						Message: message,
						Font: self.Font,
						StartPosition: FormStartPosition.CenterScreen)
		if DialogResult.OK == dlg.ShowDialog():
			return dlg.Value

	override def Dispose(disposing as bool):
		FlushCurrentActivity()
		_components.Dispose()
		_system.Dispose()
		super(disposing)

class PromptDialog(Form):

	_value as TextBox
	_message as Label

	def constructor():

		_message = Label(Location: Point(2, 2),
						Size: System.Drawing.Size(200, 18))
		_value = TextBox(
						Location: Point(2, 20),
						Size: System.Drawing.Size(290, 18))

		ok = Button(Text: "OK",
					Location: Point(50, 45),
					DialogResult: DialogResult.OK)

		cancel = Button(Text: "Cancel",
					Location: Point(150, 45),
					DialogResult: DialogResult.Cancel)

		SuspendLayout()
		self.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog
		self.StartPosition = FormStartPosition.CenterParent
		self.Size = System.Drawing.Size(300, 120)
		self.AcceptButton = ok
		self.CancelButton = cancel
		Controls.Add(_message)
		Controls.Add(_value)
		Controls.Add(ok)
		Controls.Add(cancel)
		ResumeLayout(false)

	Message as string:
		set:
			_message.Text = value

	Value:
		get:
			return _value.Text
		set:
			_value.Text = value

Application.Run(MainForm())


reference Boo.Lang.Compiler so the assembly is imported

namespace Boojay.Compilation.Tests

import NUnit.Framework
import Boo.Lang.Parser
import Boo.Lang.Compiler
import Boo.Lang.Compiler.IO
import Boo.Lang.Compiler.Ast
import Boojay.Compilation

[TestFixture]
partial class IntegrationTestFixture:

	def runTestCase(fname as string):
		unit = parse(fname)
		main = unit.Modules[0]
		compile(unit)
		output = runJavaClass(moduleClass(main))
		Assert.IsNotNull(main.Documentation, "Expecting documentation for '${fname}'")
		Assert.AreEqual(normalizeWhiteSpace(main.Documentation), normalizeWhiteSpace(output))

	def normalizeWhiteSpace(s as string):
		return s.Trim().Replace("\r\n", "\n")

	def compile(unit as CompileUnit):
		compiler = newBoojayCompiler()
		result = compiler.Run(unit)
		assert 0 == len(result.Errors), result.Errors.ToString(true)

	def parse(fname as string):
		return BooParser.ParseFile(System.IO.Path.GetFullPath(fname))

	def moduleClass(module as Module):
		return module.FullName + "Module"

	def runJavaClass(className as string):
		return shell("java", "-cp . ${className}")

namespace Boojay.Compilation.Tests

import NUnit.Framework
import Boo.Lang.Parser
import Boo.Lang.Compiler.IO
import Boo.Lang.Compiler.Ast
import Boojay.Compilation

[TestFixture]
partial class IntegrationTestFixture:

	def runTestCase(fname as string):
		unit = parse(fname)
		main = unit.Modules[0]
		compile(unit)
		output = runJavaClass(moduleClass(main))
		Assert.IsNotNull(main.Documentation, "Expecting documentation for '${fname}'")
		Assert.AreEqual(normalizeWhiteSpace(main.Documentation), normalizeWhiteSpace(output))

	def normalizeWhiteSpace(s as string):
		return s.Trim().Replace("\r\n", "\n")

	def compile(unit as CompileUnit):
		compiler = newBoojayCompiler()
		result = compiler.Run(unit)
		assert 0 == len(result.Errors), result.Errors.ToString(true)

	def parse(fname as string):
		return BooParser.ParseFile(System.IO.Path.GetFullPath(fname))

	def moduleClass(module as Module):
		return module.FullName + "Module"

	def runJavaClass(className as string):
		return shell("java", "-cp . ${className}")


simpler MemoKey

simpler MemoKey

namespace Boo.OMeta

import Boo.Adt
import System.Collections.Generic

data OMetaMatch(Input as OMetaInput) = SuccessfulMatch(Value as object) | FailedMatch()

callable OMetaRule(context as OMetaGrammar, input as OMetaInput) as OMetaMatch

interface OMetaGrammar:

	def InstallRule(ruleName as string, rule as OMetaRule)
	def Apply(context as OMetaGrammar, rule as string, input as OMetaInput) as OMetaMatch
	def SuperApply(context as OMetaGrammar, rule as string, input as OMetaInput) as OMetaMatch

class OMetaGrammarRoot(OMetaGrammar):

	class MemoKey:
		final _rule as string
		final _input as OMetaInput

		def constructor(rule as string, input as OMetaInput):
			_rule = rule
			_input = input

		override def Equals(o):
			other as MemoKey = o
			return _input is other._input and _rule is other._rule

		override def GetHashCode():
			return _rule.GetHashCode() ^ _input.GetHashCode()

	_rules = Dictionary[of string, OMetaRule]()
	_memo = Dictionary[of MemoKey, OMetaMatch]()

	def InstallRule(ruleName as string, rule as OMetaRule):
		_rules[ruleName] = rule

	def Apply(context as OMetaGrammar, rule as string, input as OMetaInput):

		key = MemoKey(rule, input)
		memo as OMetaMatch
		if _memo.TryGetValue(key, memo):
			return memo

		_memo[key] = FailedMatch(input)
		memo = Eval(context, rule, input)
		_memo[key] = memo
		return memo

	def Eval(context as OMetaGrammar, rule as string, input as OMetaInput):
		found as OMetaRule
		if _rules.TryGetValue(rule, found):
			return found(context, input)
		return RuleMissing(context, rule, input)

	def SuperApply(context as OMetaGrammar, rule as string, input as OMetaInput):
		return Apply(context, rule, input)

	virtual def RuleMissing(context as OMetaGrammar, rule as string, input as OMetaInput) as OMetaMatch:
		raise "Rule '${rule}' missing!"

class OMetaGrammarPrototype(OMetaGrammarRoot):

	def constructor():
		InstallRule("whitespace", makeRule(char.IsWhiteSpace))
		InstallRule("letter", makeRule(char.IsLetter))
		InstallRule("digit", makeRule(char.IsDigit))

class OMetaDelegatingGrammar(OMetaGrammarRoot):

	_prototype as OMetaGrammar

	def constructor(prototype as OMetaGrammar):
		_prototype = prototype

	override def RuleMissing(context as OMetaGrammar, rule as string, input as OMetaInput):
		return SuperApply(context, rule, input)

	override def SuperApply(context as OMetaGrammar, rule as string, input as OMetaInput):
		return _prototype.SuperApply(context, rule, input)

def makeRule(predicate as System.Predicate[of object]) as OMetaRule:
	def rule(context as OMetaGrammar, input as OMetaInput) as OMetaMatch:
		if not input.IsEmpty and predicate(input.Head):
			return SuccessfulMatch(input.Tail, input.Head)
		return FailedMatch(input)
	return rule

namespace Boo.OMeta

import Boo.Adt
import System.Collections.Generic

data OMetaMatch(Input as OMetaInput) = SuccessfulMatch(Value as object) | FailedMatch()

callable OMetaRule(context as OMetaGrammar, input as OMetaInput) as OMetaMatch

interface OMetaGrammar:

	def InstallRule(ruleName as string, rule as OMetaRule)
	def Apply(context as OMetaGrammar, rule as string, input as OMetaInput) as OMetaMatch
	def SuperApply(context as OMetaGrammar, rule as string, input as OMetaInput) as OMetaMatch

class OMetaGrammarRoot(OMetaGrammar):

	class MemoKey:
		final _rule as string
		final _input as OMetaInput

		def constructor(rule as string, input as OMetaInput):
			_rule = rule
			_input = input

		override def Equals(o):
			other as MemoKey = o
			if _input is not other._input: return false
			return _rule is other._rule

		override def GetHashCode():
			return _rule.GetHashCode() ^ _input.GetHashCode()

	_rules = Dictionary[of string, OMetaRule]()
	_memo = Dictionary[of MemoKey, OMetaMatch]()

	def InstallRule(ruleName as string, rule as OMetaRule):
		_rules[ruleName] = rule

	def Apply(context as OMetaGrammar, rule as string, input as OMetaInput):

		key = MemoKey(rule, input)
		memo as OMetaMatch
		if _memo.TryGetValue(key, memo):
			return memo

		_memo[key] = FailedMatch(input)
		memo = Eval(context, rule, input)
		_memo[key] = memo
		return memo

	def Eval(context as OMetaGrammar, rule as string, input as OMetaInput):
		found as OMetaRule
		if _rules.TryGetValue(rule, found):
			return found(context, input)
		return RuleMissing(context, rule, input)

	def SuperApply(context as OMetaGrammar, rule as string, input as OMetaInput):
		return Apply(context, rule, input)

	virtual def RuleMissing(context as OMetaGrammar, rule as string, input as OMetaInput) as OMetaMatch:
		raise "Rule '${rule}' missing!"

class OMetaGrammarPrototype(OMetaGrammarRoot):

	def constructor():
		InstallRule("whitespace", makeRule(char.IsWhiteSpace))
		InstallRule("letter", makeRule(char.IsLetter))
		InstallRule("digit", makeRule(char.IsDigit))

class OMetaDelegatingGrammar(OMetaGrammarRoot):

	_prototype as OMetaGrammar

	def constructor(prototype as OMetaGrammar):
		_prototype = prototype

	override def RuleMissing(context as OMetaGrammar, rule as string, input as OMetaInput):
		return SuperApply(context, rule, input)

	override def SuperApply(context as OMetaGrammar, rule as string, input as OMetaInput):
		return _prototype.SuperApply(context, rule, input)

def makeRule(predicate as System.Predicate[of object]) as OMetaRule:
	def rule(context as OMetaGrammar, input as OMetaInput) as OMetaMatch:
		if not input.IsEmpty and predicate(input.Head):
			return SuccessfulMatch(input.Tail, input.Head)
		return FailedMatch(input)
	return rule