Fixed BooParserTestFixture.splicing-1

Fixed BooParserTestFixture.splicing-1

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)+
"""

	if len(list_of.Arguments) == 2:
		rule, separator = list_of.Arguments
	else:
		rule, separator = list_of.Arguments[0], [| COMMA |]

	block as Block = list_of.ParentNode

	listRuleName = ReferenceExpression(Name: "${rule}_list")
	listRule = [| $listRuleName = ((($rule >> first), ++(($separator, $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 = "|]"
		splice_begin = "$"
		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 = "<"
		semicolon = ";"
		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

	empty_line = ending_spaces, newline

	ending_spaces = --end_space >> value ^ value

	end_space =  semicolon | space

	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", "do", "import", "pass", "return", "true", \
		"false", "and", "or", "as", "not", "if", "isa", "is", "null", \
		"for", "interface", "internal", "in", "yield", "self", "super", "of", \
		"event", "private", "protected", "public", "enum", \
		"callable", "unless", "static", "final", "virtual", "override", "abstract", \
		"transient", "raise", "else", "elif", "typeof", "then", "struct", "constructor", \
		"goto", "from", "namespace"

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

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

	namespace_declaration = (NAMESPACE, qualified_name)

	docstring = (TDQ, ++(~tdq, ( (('\\', '$') ^ '$')| _)) >> s, TDQ) ^ makeString(s)

	import_declaration = ( (IMPORT, qualified_name >> qn), (((FROM, (dqs | SQS | qualified_name)) | "") >> assembly), ( (AS, ID) | "") >> alias, eol) ^ newImport(qn, assembly, alias)

	dqs = (DQ, ++(~DQ, _) >> s, DQ) ^ makeString(s)

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

	module_member = assembly_attribute | type_def | method | (~(~~(ID, AS), stmt_declaration), ~stmt_expression, ~stmt_goto, stmt_macro)

	type_def = class_def | struct_def | interface_def | enum_def | callable_def

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

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

	struct_def = (
		attributes >> attrs,
		member_modifiers >> mod,
		STRUCT, ID >> structName, optional_generic_parameters >> genericParameters, super_types >> superTypes, begin_block, struct_body >> body, end_block
	) ^ newStruct(attrs, mod, structName, genericParameters, superTypes, body)

	interface_def = (
		attributes >> attrs,
		member_modifiers >> mod,
		INTERFACE, ID >> name, optional_generic_parameters >> genericParameters, super_types >> superTypes, begin_block, interface_body >> body, end_block
	) ^ newInterface(attrs, mod, name, genericParameters, 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) | ""

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

	struct_body = no_member | (++struct_member >> members ^ members)

	interface_body = no_member | (++interface_member >> members ^ members)

	no_member = (PASS, eol) ^ null

	class_member = type_def | property_def | constructor_method | method | field | event_def

	struct_member = constructor_method | method | field

	interface_member = property_def | method_signature

	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)

	method_signature = (
		attributes >> attrs,
		member_modifiers >> mod,
		DEF, ID >> name,
		optional_generic_parameters >> genericParameters,
		method_parameters >> parameters,
		attributes >> returnTypeAttributes, optional_type >> type, eol
	) ^ newGenericMethod(attrs, mod, name, genericParameters, parameters, returnTypeAttributes, type, null)

	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
	) ^ newField(attrs, mod, name, type, initializer)

	field_initializer = (ASSIGN, block_expression) | ((ASSIGN, rvalue >> v, eol) ^ v) | eol

	member_modifiers = --(
		(PRIVATE ^ TypeMemberModifiers.Private)
		| (PUBLIC ^ TypeMemberModifiers.Public)
		| (INTERNAL ^ TypeMemberModifiers.Internal)
		| (PROTECTED ^ TypeMemberModifiers.Protected)
		| (FINAL ^ TypeMemberModifiers.Final)
		| (STATIC ^ TypeMemberModifiers.Static)
		| (VIRTUAL ^ TypeMemberModifiers.Virtual)
		| (OVERRIDE ^ TypeMemberModifiers.Override)
		| (TRANSIENT ^ TypeMemberModifiers.Transient)
		| (ABSTRACT ^ TypeMemberModifiers.Abstract)
	) >> all ^ all

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

	constructor_method = (
		attributes >> attrs,
		member_modifiers >> mod,
		DEF, CONSTRUCTOR,
		optional_generic_parameters >> genericParameters,
		method_parameters >> parameters,
		block >> body
	) ^ newConstructor(attrs, mod, genericParameters, parameters, body)

	method_parameters = (LPAREN, optional_parameter_list >> parameters, param_array >> paramArray, RPAREN) ^ [parameters, paramArray]

	param_array = ((attributes >> attrs, STAR, ID >> name, optional_array_type >> type) ^ newParameterDeclaration(attrs, name, type)) | ("" ^ null)

	optional_array_type = (AS, type_reference_array) | ""

	optional_generic_parameters = generic_parameters | ""

	generic_parameters = (LBRACK, (OF | ""), generic_parameter_list >> parameters, RBRACK) ^ parameters

	generic_parameter = (ID >> name, optional_generic_parameter_constraints >> genericParameterConstraints) ^ newGenericParameterDeclaration(name, genericParameterConstraints)

	optional_generic_parameter_constraints = generic_parameter_constraints | ""

	generic_parameter_constraints = (LPAREN, generic_parameter_constraint_list >> constraints, RPAREN) ^ constraints

	generic_parameter_constraint = ( (CLASS | STRUCT | CONSTRUCTOR) >> constraint ^ newGenericParameterConstraint(constraint) ) | type_reference

	list_of generic_parameter_constraint

	list_of generic_parameter

	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 | multi_line_block | multi_line_block_with_doc | single_line_block

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

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

	multi_line_block_with_doc = (begin_block_with_doc >> doc, ++stmt >> stmts, end_block)  ^ [doc, newBlock(stmts)]

	single_line_block = (COLON, stmt_line >> line) ^ newBlock(line)

	begin_block = COLON, INDENT

	begin_block_with_doc = (COLON,
		--EOL,
		docstring >> s,
		INDENT) ^ s

	end_block = DEDENT

	stmt = stmt_block | stmt_line

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

	stmt_unpack = (declaration_list >> declarations, ASSIGN, rvalue >> e, stmt_modifier >> m) ^ newUnpackStatement(declarations, e, m)

	stmt_raise = (RAISE, expression >> e, stmt_modifier >> m) ^ RaiseStatement(Exception: e, Modifier: m)

	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|SEMICOLON)) ^ value)

	stmt_goto = ((GOTO, ID >> label, stmt_modifier >> m) ^ newGotoStatement(label, m)) | ((COLON, ID >> label, eol) ^ LabelStatement(Name: tokenValue(label)))

	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, block_expression >> e)
			| ((ASSIGN, rvalue >> e), eol)
			| eol
		) ^ newDeclarationStatement(d, e)

	declaration = (ID >> name, optional_type >> typeRef) ^ newDeclaration(name, typeRef)

	stmt_block = stmt_if | stmt_for

	stmt_for = (FOR, declaration_list >> dl, IN, rvalue >> e, block >> body, or_block >> orBlock, then_block >> thenBlock) ^ newForStatement(dl, e, body, orBlock, thenBlock)

	or_block = ((OR, block >> orBlock) ^ orBlock) | ( "" ^ null)

	then_block = ((THEN, block >> thenBlock) ^ thenBlock) | ( "" ^ null)

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

	false_block = ((ELIF, assignment >> e, block >> trueBlock, false_block >> falseBlock) ^ newBlock(newIfStatement(e, trueBlock, falseBlock))) | \
		((ELSE, block >> falseBlock) ^ falseBlock) | ( "" ^ null)

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

	optional_assignment = assignment | ""

	stmt_expression_block = (expression >> l, (ASSIGN | ASSIGN_INPLACE) >> op, block_expression >> r) ^ ExpressionStatement(newInfixExpression(op, l, r))

	block_expression = invocation_with_block | closure_block | dsl_friendly_invocation

	invocation_with_block = (member_reference >> e and (e isa MethodInvocationExpression), (closure_block) >> c) ^ newInvocationWithBlock(e, c)

	dsl_friendly_invocation = (member_reference >> e and ((e isa MemberReferenceExpression) or (e isa ReferenceExpression)), (block) >> c) ^ newInvocation(e, [BlockExpression(Body: c)],null)

	closure_block = ((DEF | DO), optional_parameters >> parameters, block >> body) ^ newBlockExpression(parameters, body)

	optional_parameters = method_parameters | ("" ^ [[], null])

	stmt_expression = stmt_expression_block \
		| ((dsl_friendly_invocation >> e) ^ ExpressionStatement(Expression: e)) | (((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 | conditional_expression

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

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

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

	conditional_expression = (
		(or_expression >> trueValue, IF, conditional_expression >> condition, ELSE, conditional_expression >> falseValue) ^  newConditionalExpression(condition, trueValue, falseValue)
	) | or_expression

	infix or_expression, OR, and_expression

	infix and_expression, AND, not_expression

	prefix not_expression, NOT, explode_operator

	prefix explode_operator, STAR , membership_expression

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

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

	isa_expression = (comparison >> l, ISA >> op, type_reference >> type) ^ newInfixExpression(op, l, newTypeofExpression(type))  | 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, term

	infix term, (PLUS | MINUS), factor

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

	infix bitwise_shift_expression, (BITWISE_SHIFT_LEFT | BITWISE_SHIFT_RIGHT), 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 = invocation_expression | atom

	invocation_expression = (member_reference >> target, optional_generic_arguments >> generic_args, invocation_arguments >> args) ^ newInvocation(target, args, generic_args)

	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_splice \
		| type_reference_generic_definition \
		| type_reference_generic \
		| type_reference_simple \
		| type_reference_array \
		| type_reference_callable

	type_reference_splice = SPLICE_BEGIN, atom >> e ^ SpliceTypeReference(Expression: e)

	splice_expression = SPLICE_BEGIN, atom >> e ^ SpliceExpression(Expression: e)

	type_reference_generic_definition = (qualified_name >> qname, generic_placeholders >> placeholders) ^ newGenericTypeDefinitionReference(qname, placeholders)

	generic_placeholders = ((LBRACK, OF, STAR_list >> placeholders, RBRACK) ^ placeholders) | ( (OF, STAR_list >> placeholders) ^ placeholders)

	list_of STAR

	type_reference_generic = (qualified_name >> qname, generic_arguments >> args) ^ newGenericTypeReference(qname, args)

	generic_arguments = ((LBRACK, (OF | ""), type_reference_list >> args, RBRACK) ^ args) \
		| ((OF, type_reference >> arg) ^ [arg])

	optional_generic_arguments = generic_arguments | ""

	type_reference_callable = (
		CALLABLE, LPAREN, optional_type_reference_list >> params, RPAREN, optional_type >> type
	) ^ newCallableTypeReference(params, type) | ((CALLABLE)^ SimpleTypeReference("callable"))

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

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

	atom = float | integer | boolean | reference | array_literal | list_literal \
		| string_interpolation | string_literal | reg_exp_string | null_literal | parenthesized_expression  \
		| self_literal | super_literal | quasi_quote | closure | hash_literal \
		| type_literal | splice_expression

	type_literal = (TYPEOF, LPAREN, type_reference >> type, RPAREN) ^ newTypeofExpression(type)

	closure = (LBRACE, closure_parameters >> parameters, closure_stmt_list >> body, RBRACE) ^ newBlockExpression(parameters, newBlock(body))

	closure_parameters = ((optional_parameter_list >> parameters, BITWISE_OR) ^ [parameters, null]) | ("" ^ [[],null])

	list_of closure_stmt, SEMICOLON

	closure_stmt = closure_stmt_expression | closure_stmt_macro | closure_stmt_return | closure_stmt_raise | closure_stmt_unpack

	closure_stmt_macro = (ID >> name, assignment_list >> args, closure_stmt_modifier >> m) ^ newMacro(name, args, null, m)

	closure_stmt_modifier = stmt_modifier_node | ~~(RBRACE | SEMICOLON)

	closure_stmt_return = (RETURN, rvalue >> e, closure_stmt_modifier >> m) ^ ReturnStatement(Expression: e, Modifier: m)

	closure_stmt_raise = (RAISE, expression >> e, closure_stmt_modifier >> m) ^ RaiseStatement(Exception: e, Modifier: m)

	closure_stmt_expression = (assignment >> e, closure_stmt_modifier >> m) ^ ExpressionStatement(Expression: e, Modifier: m)

	closure_stmt_unpack = (declaration_list >> declarations, ASSIGN, rvalue >> e, closure_stmt_modifier >> m) ^ newUnpackStatement(declarations, e, m)

	optional_stmt_modifier_node = stmt_modifier_node | ""

	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, optional_stmt_modifier_node >> m) ^ ReturnStatement(Expression: e, Modifier: m)

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

	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)

	reg_exp_string = ( (("/" | "@/"), (--(~"/", _) >> s), "/")  ) ^ RELiteralExpression(makeString("/", s, "/"))

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

	float = ( (fractional_constant >> n, (exponent_part | "") >> e , floating_suffix ) ^ newFloat(makeString(n,e))) | ((NUM >> n, exponent_part >> e, floating_suffix)  ^ newFloat(makeString(tokenValue(n),e)))

	fractional_constant = ((NUM >> a , DOT , NUM >> b) ^ makeString(tokenValue(a),".",tokenValue(b))) | ( (DOT , NUM >> b) ^ makeString(".",tokenValue(b)) ) | ( (NUM >> a , DOT, ~(ID)) ^ makeString(tokenValue(a), ".") )

	exponent_part = ( ("e" | "E") , exposignopt >> e , NUM >> d ) ^ makeString("e", e, tokenValue(d))

	exposignopt = ( (PLUS | MINUS) >> e ^ makeString(tokenValue(e)) ) | ""

	floating_suffix = "f" | "l" | "F" | "L" | ""

	boolean = true_literal | false_literal

	true_literal = TRUE ^ [| true |]

	false_literal = FALSE ^ [| false |]

	eol = (++EOL | ~_) ^ null

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)+
"""

	if len(list_of.Arguments) == 2:
		rule, separator = list_of.Arguments
	else:
		rule, separator = list_of.Arguments[0], [| COMMA |]

	block as Block = list_of.ParentNode

	listRuleName = ReferenceExpression(Name: "${rule}_list")
	listRule = [| $listRuleName = ((($rule >> first), ++(($separator, $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 = "<"
		semicolon = ";"
		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

	empty_line = ending_spaces, newline

	ending_spaces = --end_space >> value ^ value

	end_space =  semicolon | space

	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", "do", "import", "pass", "return", "true", \
		"false", "and", "or", "as", "not", "if", "isa", "is", "null", \
		"for", "interface", "internal", "in", "yield", "self", "super", "of", \
		"event", "private", "protected", "public", "enum", \
		"callable", "unless", "static", "final", "virtual", "override", "abstract", \
		"transient", "raise", "else", "elif", "typeof", "then", "struct", "constructor", \
		"goto", "from", "namespace"

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

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

	namespace_declaration = (NAMESPACE, qualified_name)

	docstring = (TDQ, ++(~tdq, ( (('\\', '$') ^ '$')| _)) >> s, TDQ) ^ makeString(s)

	import_declaration = ( (IMPORT, qualified_name >> qn), (((FROM, (dqs | SQS | qualified_name)) | "") >> assembly), ( (AS, ID) | "") >> alias, eol) ^ newImport(qn, assembly, alias)

	dqs = (DQ, ++(~DQ, _) >> s, DQ) ^ makeString(s)

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

	module_member = assembly_attribute | type_def | method | (~(~~(ID, AS), stmt_declaration), ~stmt_expression, ~stmt_goto, stmt_macro)

	type_def = class_def | struct_def | interface_def | enum_def | callable_def

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

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

	struct_def = (
		attributes >> attrs,
		member_modifiers >> mod,
		STRUCT, ID >> structName, optional_generic_parameters >> genericParameters, super_types >> superTypes, begin_block, struct_body >> body, end_block
	) ^ newStruct(attrs, mod, structName, genericParameters, superTypes, body)

	interface_def = (
		attributes >> attrs,
		member_modifiers >> mod,
		INTERFACE, ID >> name, optional_generic_parameters >> genericParameters, super_types >> superTypes, begin_block, interface_body >> body, end_block
	) ^ newInterface(attrs, mod, name, genericParameters, 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) | ""

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

	struct_body = no_member | (++struct_member >> members ^ members)

	interface_body = no_member | (++interface_member >> members ^ members)

	no_member = (PASS, eol) ^ null

	class_member = type_def | property_def | constructor_method | method | field | event_def

	struct_member = constructor_method | method | field

	interface_member = property_def | method_signature

	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)

	method_signature = (
		attributes >> attrs,
		member_modifiers >> mod,
		DEF, ID >> name,
		optional_generic_parameters >> genericParameters,
		method_parameters >> parameters,
		attributes >> returnTypeAttributes, optional_type >> type, eol
	) ^ newGenericMethod(attrs, mod, name, genericParameters, parameters, returnTypeAttributes, type, null)

	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
	) ^ newField(attrs, mod, name, type, initializer)

	field_initializer = (ASSIGN, block_expression) | ((ASSIGN, rvalue >> v, eol) ^ v) | eol

	member_modifiers = --(
		(PRIVATE ^ TypeMemberModifiers.Private)
		| (PUBLIC ^ TypeMemberModifiers.Public)
		| (INTERNAL ^ TypeMemberModifiers.Internal)
		| (PROTECTED ^ TypeMemberModifiers.Protected)
		| (FINAL ^ TypeMemberModifiers.Final)
		| (STATIC ^ TypeMemberModifiers.Static)
		| (VIRTUAL ^ TypeMemberModifiers.Virtual)
		| (OVERRIDE ^ TypeMemberModifiers.Override)
		| (TRANSIENT ^ TypeMemberModifiers.Transient)
		| (ABSTRACT ^ TypeMemberModifiers.Abstract)
	) >> all ^ all

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

	constructor_method = (
		attributes >> attrs,
		member_modifiers >> mod,
		DEF, CONSTRUCTOR,
		optional_generic_parameters >> genericParameters,
		method_parameters >> parameters,
		block >> body
	) ^ newConstructor(attrs, mod, genericParameters, parameters, body)

	method_parameters = (LPAREN, optional_parameter_list >> parameters, param_array >> paramArray, RPAREN) ^ [parameters, paramArray]

	param_array = ((attributes >> attrs, STAR, ID >> name, optional_array_type >> type) ^ newParameterDeclaration(attrs, name, type)) | ("" ^ null)

	optional_array_type = (AS, type_reference_array) | ""

	optional_generic_parameters = generic_parameters | ""

	generic_parameters = (LBRACK, (OF | ""), generic_parameter_list >> parameters, RBRACK) ^ parameters

	generic_parameter = (ID >> name, optional_generic_parameter_constraints >> genericParameterConstraints) ^ newGenericParameterDeclaration(name, genericParameterConstraints)

	optional_generic_parameter_constraints = generic_parameter_constraints | ""

	generic_parameter_constraints = (LPAREN, generic_parameter_constraint_list >> constraints, RPAREN) ^ constraints

	generic_parameter_constraint = ( (CLASS | STRUCT | CONSTRUCTOR) >> constraint ^ newGenericParameterConstraint(constraint) ) | type_reference

	list_of generic_parameter_constraint

	list_of generic_parameter

	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 | multi_line_block | multi_line_block_with_doc | single_line_block

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

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

	multi_line_block_with_doc = (begin_block_with_doc >> doc, ++stmt >> stmts, end_block)  ^ [doc, newBlock(stmts)]

	single_line_block = (COLON, stmt_line >> line) ^ newBlock(line)

	begin_block = COLON, INDENT

	begin_block_with_doc = (COLON,
		--EOL,
		docstring >> s,
		INDENT) ^ s

	end_block = DEDENT

	stmt = stmt_block | stmt_line

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

	stmt_unpack = (declaration_list >> declarations, ASSIGN, rvalue >> e, stmt_modifier >> m) ^ newUnpackStatement(declarations, e, m)

	stmt_raise = (RAISE, expression >> e, stmt_modifier >> m) ^ RaiseStatement(Exception: e, Modifier: m)

	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|SEMICOLON)) ^ value)

	stmt_goto = ((GOTO, ID >> label, stmt_modifier >> m) ^ newGotoStatement(label, m)) | ((COLON, ID >> label, eol) ^ LabelStatement(Name: tokenValue(label)))

	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, block_expression >> e)
			| ((ASSIGN, rvalue >> e), eol)
			| eol
		) ^ newDeclarationStatement(d, e)

	declaration = (ID >> name, optional_type >> typeRef) ^ newDeclaration(name, typeRef)

	stmt_block = stmt_if | stmt_for

	stmt_for = (FOR, declaration_list >> dl, IN, rvalue >> e, block >> body, or_block >> orBlock, then_block >> thenBlock) ^ newForStatement(dl, e, body, orBlock, thenBlock)

	or_block = ((OR, block >> orBlock) ^ orBlock) | ( "" ^ null)

	then_block = ((THEN, block >> thenBlock) ^ thenBlock) | ( "" ^ null)

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

	false_block = ((ELIF, assignment >> e, block >> trueBlock, false_block >> falseBlock) ^ newBlock(newIfStatement(e, trueBlock, falseBlock))) | \
		((ELSE, block >> falseBlock) ^ falseBlock) | ( "" ^ null)

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

	optional_assignment = assignment | ""

	stmt_expression_block = (expression >> l, (ASSIGN | ASSIGN_INPLACE) >> op, block_expression >> r) ^ ExpressionStatement(newInfixExpression(op, l, r))

	block_expression = invocation_with_block | closure_block | dsl_friendly_invocation

	invocation_with_block = (member_reference >> e and (e isa MethodInvocationExpression), (closure_block) >> c) ^ newInvocationWithBlock(e, c)

	dsl_friendly_invocation = (member_reference >> e and ((e isa MemberReferenceExpression) or (e isa ReferenceExpression)), (block) >> c) ^ newInvocation(e, [BlockExpression(Body: c)],null)

	closure_block = ((DEF | DO), optional_parameters >> parameters, block >> body) ^ newBlockExpression(parameters, body)

	optional_parameters = method_parameters | ("" ^ [[], null])

	stmt_expression = stmt_expression_block \
		| ((dsl_friendly_invocation >> e) ^ ExpressionStatement(Expression: e)) | (((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 | conditional_expression

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

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

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

	conditional_expression = (
		(or_expression >> trueValue, IF, conditional_expression >> condition, ELSE, conditional_expression >> falseValue) ^  newConditionalExpression(condition, trueValue, falseValue)
	) | or_expression

	infix or_expression, OR, and_expression

	infix and_expression, AND, not_expression

	prefix not_expression, NOT, explode_operator

	prefix explode_operator, STAR , membership_expression

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

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

	isa_expression = (comparison >> l, ISA >> op, type_reference >> type) ^ newInfixExpression(op, l, newTypeofExpression(type))  | 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, term

	infix term, (PLUS | MINUS), factor

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

	infix bitwise_shift_expression, (BITWISE_SHIFT_LEFT | BITWISE_SHIFT_RIGHT), 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 = invocation_expression | atom

	invocation_expression = (member_reference >> target, optional_generic_arguments >> generic_args, invocation_arguments >> args) ^ newInvocation(target, args, generic_args)

	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_generic_definition \
		| type_reference_generic \
		| type_reference_simple \
		| type_reference_array \
		| type_reference_callable

	type_reference_generic_definition = (qualified_name >> qname, generic_placeholders >> placeholders) ^ newGenericTypeDefinitionReference(qname, placeholders)

	generic_placeholders = ((LBRACK, OF, STAR_list >> placeholders, RBRACK) ^ placeholders) | ( (OF, STAR_list >> placeholders) ^ placeholders)

	list_of STAR

	type_reference_generic = (qualified_name >> qname, generic_arguments >> args) ^ newGenericTypeReference(qname, args)

	generic_arguments = ((LBRACK, (OF | ""), type_reference_list >> args, RBRACK) ^ args) \
		| ((OF, type_reference >> arg) ^ [arg])

	optional_generic_arguments = generic_arguments | ""

	type_reference_callable = (
		CALLABLE, LPAREN, optional_type_reference_list >> params, RPAREN, optional_type >> type
	) ^ newCallableTypeReference(params, type) | ((CALLABLE)^ SimpleTypeReference("callable"))

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

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

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

	type_literal = (TYPEOF, LPAREN, type_reference >> type, RPAREN) ^ newTypeofExpression(type)

	closure = (LBRACE, closure_parameters >> parameters, closure_stmt_list >> body, RBRACE) ^ newBlockExpression(parameters, newBlock(body))

	closure_parameters = ((optional_parameter_list >> parameters, BITWISE_OR) ^ [parameters, null]) | ("" ^ [[],null])

	list_of closure_stmt, SEMICOLON

	closure_stmt = closure_stmt_expression | closure_stmt_macro | closure_stmt_return | closure_stmt_raise | closure_stmt_unpack

	closure_stmt_macro = (ID >> name, assignment_list >> args, closure_stmt_modifier >> m) ^ newMacro(name, args, null, m)

	closure_stmt_modifier = stmt_modifier_node | ~~(RBRACE | SEMICOLON)

	closure_stmt_return = (RETURN, rvalue >> e, closure_stmt_modifier >> m) ^ ReturnStatement(Expression: e, Modifier: m)

	closure_stmt_raise = (RAISE, expression >> e, closure_stmt_modifier >> m) ^ RaiseStatement(Exception: e, Modifier: m)

	closure_stmt_expression = (assignment >> e, closure_stmt_modifier >> m) ^ ExpressionStatement(Expression: e, Modifier: m)

	closure_stmt_unpack = (declaration_list >> declarations, ASSIGN, rvalue >> e, closure_stmt_modifier >> m) ^ newUnpackStatement(declarations, e, m)

	optional_stmt_modifier_node = stmt_modifier_node | ""

	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, optional_stmt_modifier_node >> m) ^ ReturnStatement(Expression: e, Modifier: m)

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

	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)

	reg_exp_string = ( (("/" | "@/"), (--(~"/", _) >> s), "/")  ) ^ RELiteralExpression(makeString("/", s, "/"))

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

	float = ( (fractional_constant >> n, (exponent_part | "") >> e , floating_suffix ) ^ newFloat(makeString(n,e))) | ((NUM >> n, exponent_part >> e, floating_suffix)  ^ newFloat(makeString(tokenValue(n),e)))

	fractional_constant = ((NUM >> a , DOT , NUM >> b) ^ makeString(tokenValue(a),".",tokenValue(b))) | ( (DOT , NUM >> b) ^ makeString(".",tokenValue(b)) ) | ( (NUM >> a , DOT, ~(ID)) ^ makeString(tokenValue(a), ".") )

	exponent_part = ( ("e" | "E") , exposignopt >> e , NUM >> d ) ^ makeString("e", e, tokenValue(d))

	exposignopt = ( (PLUS | MINUS) >> e ^ makeString(tokenValue(e)) ) | ""

	floating_suffix = "f" | "l" | "F" | "L" | ""

	boolean = true_literal | false_literal

	true_literal = TRUE ^ [| true |]

	false_literal = FALSE ^ [| false |]

	eol = (++EOL | ~_) ^ null


Copy referenced files to target dir

Copy referenced files to target dir

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

#region license
// Copyright (c) 2004, Daniel Grunwald (daniel@danielgrunwald.de)
// All rights reserved.
//
// BooBinding is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// Boo Explorer is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Foobar; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
#endregion

namespace BooBinding

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

import ICSharpCode.Core.Services

import ICSharpCode.SharpDevelop.Internal.Project
import ICSharpCode.SharpDevelop.Gui
import ICSharpCode.SharpDevelop.Services

class BooBindingCompilerManager:
	static _normalError  = /(?<file>.*)\((?<line>\d+),(?<column>\d+)\):\s+(?<number>[\d\w]+):\s+(?<message>.*)/
	static _generalError = /(?<error>.+)\s+(?<number>[\d\w]+):\s+(?<message>.*)/

	def GetCompiledOutputName(fileName as string) as string:
		return Path.ChangeExtension(fileName, ".exe")

	def GetCompiledOutputName(project as IProject) as string:
		p as BooProject = project;
		compilerparameters as BooCompilerParameters = p.ActiveConfiguration

		fileUtilityService as FileUtilityService = ServiceManager.Services.GetService(typeof(FileUtilityService))
		exe = fileUtilityService.GetDirectoryNameWithSeparator(compilerparameters.OutputDirectory) + compilerparameters.OutputAssembly
		if (compilerparameters.CompileTarget == CompileTarget.Library):
			return exe + ".dll"
		else:
			return exe + ".exe"

	def CanCompile(fileName as string) as bool:
		return Path.GetExtension(fileName).ToLower() == ".boo";

	static _compilerNotFound = "Boo compiler not found.\nYou have to specify the path where booc.exe is in the project options!\n"

	private def MakeError(text as string):
		cr = CompilerResults(TempFileCollection())
		cr.Errors.Add(CompilerError(ErrorText: text))
		return DefaultCompilerResult(cr, text)

	private def Compile(compilerparameters as BooCompilerParameters, fileNames as (string), outputFile as string, p as IProject) as ICompilerResult:
		fileUtilityService as FileUtilityService = ServiceManager.Services.GetService(typeof(FileUtilityService))
		messageService as IMessageService = ServiceManager.Services.GetService(typeof(IMessageService))

		if compilerparameters.BooPath == "":
			messageService.ShowError(_compilerNotFound)
			return MakeError(_compilerNotFound)
		booDir = fileUtilityService.GetDirectoryNameWithSeparator(compilerparameters.BooPath)
		compilerName = booDir + "booc.exe"

		if not File.Exists(compilerName):
			messageService.ShowError(_compilerNotFound)
			return MakeError(_compilerNotFound)

		cmd = StringBuilder()
		cmd.Append("\"${compilerName}\"")

		cmd.Append(" -o:\"${outputFile}\"")

		cmd.Append(" -vv") if compilerparameters.Verbose

		cmd.Append(" -t:")
		if (compilerparameters.CompileTarget == CompileTarget.WinExe):
			cmd.Append("winexe")
		else:
			if (compilerparameters.CompileTarget == CompileTarget.Library):
				cmd.Append("library");
			else:
				cmd.Append("exe");

		// Default references are already added by booc.rsp

		if p != null:
			// write references
			for lib as ProjectReference in p.ProjectReferences:
				cmd.Append(" \"-r:${lib.GetReferencedFileName(p)}\"");
			// write embedded resources
			for finfo as ProjectFile in p.ProjectFiles:
				if finfo.Subtype != Subtype.Directory and finfo.BuildAction == BuildAction.EmbedAsResource:
					cmd.Append(" \"-resource:${finfo.Name}\"")

		for fileName as string in fileNames:
			cmd.Append(" \"${Path.GetFullPath(fileName)}\"")

		outstr = cmd.ToString()
		tf = TempFileCollection()
		output = ""
		error = ""
		Executor.ExecWaitWithCapture(outstr, Path.GetFullPath(compilerparameters.OutputDirectory), tf, output, error)

		result = ParseOutput(tf, output)

		outputDirectory = Path.GetDirectoryName(outputFile)

		CopyToDirIgnoringErrors(Path.Combine(booDir, "Boo.dll"), outputDirectory)
		for lib as ProjectReference in p.ProjectReferences:
			if lib.LocalCopy:
				CopyToDirIgnoringErrors(
					lib.GetReferencedFileName(p),
					outputDirectory)

		File.Delete(output)
		File.Delete(error)
		return result

	def CopyToDirIgnoringErrors(fname as string, outputDirectory as string):
		try:
			File.Copy(fname, Path.Combine(outputDirectory, Path.GetFileName(fname)))
		except:
			pass

	def CompileFile(fileName as string, compilerparameters as BooCompilerParameters) as ICompilerResult:
		compilerparameters.OutputDirectory = Path.GetDirectoryName(fileName)
		compilerparameters.OutputAssembly  = Path.GetFileNameWithoutExtension(fileName)

		return Compile(compilerparameters, (fileName,), GetCompiledOutputName(fileName), null)

	def CompileProject(project as BooProject) as ICompilerResult:
		compilerparameters as BooCompilerParameters = project.ActiveConfiguration

		fileNames = ArrayList()

		for	finfo as ProjectFile in project.ProjectFiles:
			if finfo.Subtype != Subtype.Directory:
				if finfo.BuildAction == BuildAction.Compile:
						fileNames.Add(finfo.Name)

		exe = GetCompiledOutputName(project)
		if compilerparameters.CompileTarget == CompileTarget.WinExe:
			WriteManifestFile(exe)

		return Compile(compilerparameters, fileNames.ToArray(typeof(string)), exe, project)

	def WriteManifestFile(fileName as string):
		manifestFile = fileName + ".manifest"
		return if File.Exists(manifestFile)
		using sw = StreamWriter(manifestFile):
			sw.WriteLine("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>")
			sw.WriteLine("")
			sw.WriteLine("<assembly xmlns=\"urn:schemas-microsoft-com:asm.v1\" manifestVersion=\"1.0\">")
			sw.WriteLine("	<dependency>")
			sw.WriteLine("		<dependentAssembly>")
			sw.WriteLine("			<assemblyIdentity")
			sw.WriteLine("				type=\"win32\"")
			sw.WriteLine("				name=\"Microsoft.Windows.Common-Controls\"")
			sw.WriteLine("				version=\"6.0.0.0\"")
			sw.WriteLine("				processorArchitecture=\"X86\"")
			sw.WriteLine("				publicKeyToken=\"6595b64144ccf1df\"")
			sw.WriteLine("				language=\"*\"")
			sw.WriteLine("			/>")
			sw.WriteLine("		</dependentAssembly>")
			sw.WriteLine("	</dependency>")
			sw.WriteLine("</assembly>")

	def ParseOutput(tf as TempFileCollection, file as string) as ICompilerResult:
		compilerOutput = StringBuilder();

		using sr = File.OpenText(file):
			cr = CompilerResults(tf)

			curLine = sr.ReadLine()
			while curLine != null:
				compilerOutput.Append(curLine)
				compilerOutput.Append("\n")
				curLine = curLine.Trim();
				if curLine.Length == 0:
					curLine = sr.ReadLine()
					continue

				error = CompilerError()

				// try to match standard errors
				match = _normalError.Match(curLine)
				if match.Success:
					error.Column      = Int32.Parse(match.Result('${column}'));
					error.Line        = Int32.Parse(match.Result('${line}'));
					error.FileName    = Path.GetFullPath(match.Result('${file}'));
					//error.IsWarning   = match.Result('${error}') == "warning";
					error.ErrorNumber = match.Result('${number}');
					error.ErrorText   = match.Result('${message}');
				else:
					match = _generalError.Match(curLine); // try to match general csc errors
					if match.Success:
						error.IsWarning   = match.Result('${error}') == "warning";
						error.ErrorNumber = match.Result('${number}');
						error.ErrorText   = match.Result('${message}');
					else:
						// give up and skip the line
						curLine = sr.ReadLine()
						continue

				cr.Errors.Add(error);

				curLine = sr.ReadLine()
		return DefaultCompilerResult(cr, compilerOutput.ToString())

#region license
// Copyright (c) 2004, Daniel Grunwald (daniel@danielgrunwald.de)
// All rights reserved.
//
// BooBinding is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// Boo Explorer is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Foobar; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
#endregion

namespace BooBinding

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

import ICSharpCode.Core.Services

import ICSharpCode.SharpDevelop.Internal.Project
import ICSharpCode.SharpDevelop.Gui
import ICSharpCode.SharpDevelop.Services

class BooBindingCompilerManager:
	static _normalError  = /(?<file>.*)\((?<line>\d+),(?<column>\d+)\):\s+(?<number>[\d\w]+):\s+(?<message>.*)/
	static _generalError = /(?<error>.+)\s+(?<number>[\d\w]+):\s+(?<message>.*)/

	def GetCompiledOutputName(fileName as string) as string:
		return Path.ChangeExtension(fileName, ".exe")

	def GetCompiledOutputName(project as IProject) as string:
		p as BooProject = project;
		compilerparameters as BooCompilerParameters = p.ActiveConfiguration

		fileUtilityService as FileUtilityService = ServiceManager.Services.GetService(typeof(FileUtilityService))
		exe = fileUtilityService.GetDirectoryNameWithSeparator(compilerparameters.OutputDirectory) + compilerparameters.OutputAssembly
		if (compilerparameters.CompileTarget == CompileTarget.Library):
			return exe + ".dll"
		else:
			return exe + ".exe"

	def CanCompile(fileName as string) as bool:
		return Path.GetExtension(fileName).ToLower() == ".boo";

	static _compilerNotFound = "Boo compiler not found.\nYou have to specify the path where booc.exe is in the project options!\n"

	private def MakeError(text as string):
		cr = CompilerResults(TempFileCollection())
		cr.Errors.Add(CompilerError(ErrorText: text))
		return DefaultCompilerResult(cr, text)

	private def Compile(compilerparameters as BooCompilerParameters, fileNames as (string), outputFile as string, p as IProject) as ICompilerResult:
		fileUtilityService as FileUtilityService = ServiceManager.Services.GetService(typeof(FileUtilityService))
		messageService as IMessageService = ServiceManager.Services.GetService(typeof(IMessageService))

		if compilerparameters.BooPath == "":
			messageService.ShowError(_compilerNotFound)
			return MakeError(_compilerNotFound)
		booDir = fileUtilityService.GetDirectoryNameWithSeparator(compilerparameters.BooPath)
		compilerName = booDir + "booc.exe"

		if not File.Exists(compilerName):
			messageService.ShowError(_compilerNotFound)
			return MakeError(_compilerNotFound)

		cmd = StringBuilder()
		cmd.Append("\"${compilerName}\"")

		cmd.Append(" -o:\"${outputFile}\"")

		cmd.Append(" -vv") if compilerparameters.Verbose

		cmd.Append(" -t:")
		if (compilerparameters.CompileTarget == CompileTarget.WinExe):
			cmd.Append("winexe")
		else:
			if (compilerparameters.CompileTarget == CompileTarget.Library):
				cmd.Append("library");
			else:
				cmd.Append("exe");

		// Default references are already added by booc.rsp

		if p != null:
			// write references
			for lib as ProjectReference in p.ProjectReferences:
				cmd.Append(" \"-r:${lib.GetReferencedFileName(p)}\"");
			// write embedded resources
			for finfo as ProjectFile in p.ProjectFiles:
				if finfo.Subtype != Subtype.Directory and finfo.BuildAction == BuildAction.EmbedAsResource:
					cmd.Append(" \"-resource:${finfo.Name}\"")

		for fileName as string in fileNames:
			cmd.Append(" \"${Path.GetFullPath(fileName)}\"")

		outstr = cmd.ToString()
		tf = TempFileCollection()
		output = ""
		error = ""
		Executor.ExecWaitWithCapture(outstr, Path.GetFullPath(compilerparameters.OutputDirectory), tf, output, error)

		result = ParseOutput(tf, output)

		try:
			File.Copy(booDir + "Boo.dll", Path.Combine(Path.GetDirectoryName(outputFile), "Boo.dll"))
		except:
			pass

		File.Delete(output)
		File.Delete(error)
		return result

	def CompileFile(fileName as string, compilerparameters as BooCompilerParameters) as ICompilerResult:
		compilerparameters.OutputDirectory = Path.GetDirectoryName(fileName)
		compilerparameters.OutputAssembly  = Path.GetFileNameWithoutExtension(fileName)

		return Compile(compilerparameters, (fileName,), GetCompiledOutputName(fileName), null)

	def CompileProject(project as BooProject) as ICompilerResult:
		compilerparameters as BooCompilerParameters = project.ActiveConfiguration

		fileNames = ArrayList()

		for	finfo as ProjectFile in project.ProjectFiles:
			if finfo.Subtype != Subtype.Directory:
				if finfo.BuildAction == BuildAction.Compile:
						fileNames.Add(finfo.Name)

		exe = GetCompiledOutputName(project)
		if compilerparameters.CompileTarget == CompileTarget.WinExe:
			WriteManifestFile(exe)

		return Compile(compilerparameters, fileNames.ToArray(typeof(string)), exe, project)

	def WriteManifestFile(fileName as string):
		manifestFile = fileName + ".manifest"
		return if File.Exists(manifestFile)
		using sw = StreamWriter(manifestFile):
			sw.WriteLine("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>")
			sw.WriteLine("")
			sw.WriteLine("<assembly xmlns=\"urn:schemas-microsoft-com:asm.v1\" manifestVersion=\"1.0\">")
			sw.WriteLine("	<dependency>")
			sw.WriteLine("		<dependentAssembly>")
			sw.WriteLine("			<assemblyIdentity")
			sw.WriteLine("				type=\"win32\"")
			sw.WriteLine("				name=\"Microsoft.Windows.Common-Controls\"")
			sw.WriteLine("				version=\"6.0.0.0\"")
			sw.WriteLine("				processorArchitecture=\"X86\"")
			sw.WriteLine("				publicKeyToken=\"6595b64144ccf1df\"")
			sw.WriteLine("				language=\"*\"")
			sw.WriteLine("			/>")
			sw.WriteLine("		</dependentAssembly>")
			sw.WriteLine("	</dependency>")
			sw.WriteLine("</assembly>")

	def ParseOutput(tf as TempFileCollection, file as string) as ICompilerResult:
		compilerOutput = StringBuilder();

		using sr = File.OpenText(file):
			cr = CompilerResults(tf)

			curLine = sr.ReadLine()
			while curLine != null:
				compilerOutput.Append(curLine)
				compilerOutput.Append("\n")
				curLine = curLine.Trim();
				if curLine.Length == 0:
					curLine = sr.ReadLine()
					continue

				error = CompilerError()

				// try to match standard errors
				match = _normalError.Match(curLine)
				if match.Success:
					error.Column      = Int32.Parse(match.Result('${column}'));
					error.Line        = Int32.Parse(match.Result('${line}'));
					error.FileName    = Path.GetFullPath(match.Result('${file}'));
					//error.IsWarning   = match.Result('${error}') == "warning";
					error.ErrorNumber = match.Result('${number}');
					error.ErrorText   = match.Result('${message}');
				else:
					match = _generalError.Match(curLine); // try to match general csc errors
					if match.Success:
						error.IsWarning   = match.Result('${error}') == "warning";
						error.ErrorNumber = match.Result('${number}');
						error.ErrorText   = match.Result('${message}');
					else:
						// give up and skip the line
						curLine = sr.ReadLine()
						continue

				cr.Errors.Add(error);

				curLine = sr.ReadLine()
		return DefaultCompilerResult(cr, compilerOutput.ToString())


basic save functionality

basic save functionality

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

import System
import System.IO
import Boo.IO
import Gdk from "gdk-sharp" as Gdk
import Gtk from "gtk-sharp"
import GtkSourceView from "gtksourceview-sharp"

class BooEditor(ScrolledWindow):

	static _booSourceLanguage = SourceLanguagesManager().GetLanguageFromMimeType("text/x-boo")

	[getter(Buffer)]
	_buffer = SourceBuffer(_booSourceLanguage,
							Highlight: true)

	_view = SourceView(_buffer,
						ShowLineNumbers: true,
						AutoIndent: true,
						TabsWidth: 4)

	[getter(FileName)]
	_fname as string

	def constructor():
		self.SetPolicy(PolicyType.Automatic, PolicyType.Automatic)
		self.Add(_view)

	def Open([required] fname as string):
		_buffer.Text = TextFile.ReadFile(fname)
		_buffer.Modified = false
		_fname = fname

	def SaveAs([required] fname as string):
		TextFile.WriteFile(fname, _buffer.Text)
		_fname = fname
		_buffer.Modified = false

	def Redo():
		_buffer.Redo()

	def Undo():
		_buffer.Undo()

class MainWindow(Window):

	_status = Statusbar(HasResizeGrip: false)
	_notebookEditors = Notebook(TabPos: PositionType.Top, Scrollable: true)
	_notebookHelpers = Notebook(TabPos: PositionType.Top, Scrollable: true)
	_notebookOutline = Notebook(TabPos: PositionType.Bottom, Scrollable: true)
	_documentOutline = TreeView()
	_accelGroup = AccelGroup()
	_editors = [] # workaround for gtk# bug #61703

	def constructor():
		super("Boo Explorer")

		self.AddAccelGroup(_accelGroup)
		self.Maximize()
		self.DeleteEvent += OnDelete

		_notebookOutline.AppendPage(_documentOutline, Label("Document Outline"))

		vbox = VBox(false, 1)
		vbox.PackStart(CreateMenuBar(), false, false, 0)

		editPanel = VPaned()
		editPanel.Pack1(_notebookEditors, true, true)
		editPanel.Pack2(_notebookHelpers, false, true)

		mainPanel = HPaned()
		mainPanel.Pack2(_notebookOutline, false, false)
		mainPanel.Pack1(editPanel, true, true)

		vbox.PackStart(mainPanel, true, true, 0)
		vbox.PackStart(_status, false, false, 0)

		self.Add(vbox)

		self.NewDocument()

	private def AppendEditor(editor as BooEditor):
		label = editor.FileName or "unnamed.boo"
		_notebookEditors.AppendPage(editor, Label(label))
		_editors.Add(editor)
		editor.ShowAll()
		_notebookEditors.CurrentPage = _notebookEditors.NPages-1

	def NewDocument():
		self.AppendEditor(BooEditor())

	def OpenDocument(fname as string):
		editor = BooEditor()
		editor.Open(fname)
		self.AppendEditor(editor)

	private def CreateMenuBar():
		mb = MenuBar()

		file = Menu()
		file.Append(ImageMenuItem(Stock.New, _accelGroup, Activated: _menuItemNew_Activated))
		file.Append(ImageMenuItem(Stock.Open, _accelGroup, Activated: _menuItemOpen_Activated))
		file.Append(ImageMenuItem(Stock.Save, _accelGroup, Activated: _menuItemSave_Activated))
		file.Append(SeparatorMenuItem())
		file.Append(ImageMenuItem(Stock.Quit, _accelGroup, Activated: _menuItemExit_Activated))

		edit = Menu()
		edit.Append(ImageMenuItem(Stock.Undo, _accelGroup, Activated: _menuItemUndo_Activated))
		edit.Append(ImageMenuItem(Stock.Redo, _accelGroup, Activated: _menuItemRedo_Activated))
		edit.Append(SeparatorMenuItem())
		edit.Append(ImageMenuItem(Stock.Cut, _accelGroup))
		edit.Append(ImageMenuItem(Stock.Copy, _accelGroup))
		edit.Append(ImageMenuItem(Stock.Paste, _accelGroup))
		edit.Append(ImageMenuItem(Stock.Delete, _accelGroup))
		edit.Append(SeparatorMenuItem())
		edit.Append(ImageMenuItem(Stock.Preferences, _accelGroup))

		tools = Menu()
		tools.Append(mi=ImageMenuItem(Stock.Execute, _accelGroup, Activated: _menuItemExecute_Activated))
		mi.AddAccelerator("activate", _accelGroup, AccelKey(Gdk.Key.F5, Enum.ToObject(Gdk.ModifierType, 0), AccelFlags.Visible))

		documents = Menu()
		documents.Append(ImageMenuItem(Stock.Close, _accelGroup))

		mb.Append(MenuItem("_File", Submenu: file))
		mb.Append(MenuItem("_Edit", Submenu: edit))
		mb.Append(MenuItem("_Tools", Submenu: tools))
		mb.Append(MenuItem("_Documents", Submenu: documents))
		return mb

	CurrentEditor as BooEditor:
		get:
			// can't do the simpler:
			// editor as BooEditor = _notebookEditors.CurrentPageWidget
			// because of gtk# bug #61703
			return _editors[_notebookEditors.CurrentPage]

	private def _menuItemExecute_Activated(sender, args as EventArgs):

		compiler = Boo.Lang.Compiler.BooCompiler()
		compiler.Parameters.Input.Add(
				Boo.Lang.Compiler.IO.StringInput(CurrentEditor.FileName or "unnamed.boo",
												CurrentEditor.Buffer.Text))
		compiler.Parameters.Pipeline = Boo.Lang.Compiler.Pipelines.Run()

		result = compiler.Run()
		print(result.Errors.ToString()) if len(result.Errors)

	private def _menuItemNew_Activated(sender, args as EventArgs):
		self.NewDocument()

	private def _menuItemOpen_Activated(sender, args as EventArgs):
		fs = FileSelection("Open file", SelectMultiple: true)
		fs.Complete("*.boo")
		try:
			if cast(int, ResponseType.Ok) == fs.Run():
				for fname in fs.Selections:
					self.OpenDocument(fname)
		ensure:
			fs.Hide()

	private def _menuItemSave_Activated(sender, args as EventArgs):
		editor = CurrentEditor
		fname = editor.FileName
		if fname is null:
			fs = FileSelection("Save As", SelectMultiple: false)
			if cast(int, ResponseType.Ok) != fs.Run():
				return
			fs.Hide()
			fname = fs.Selections[0]
		editor.SaveAs(fname)
		_notebookEditors.SetTabLabelText(editor, editor.FileName)

	private def _menuItemUndo_Activated(sender, args as EventArgs):
		CurrentEditor.Undo()

	private def _menuItemRedo_Activated(sender, args as EventArgs):
		CurrentEditor.Redo()

	private def _menuItemExit_Activated(sender, args as EventArgs):
		Application.Quit()

	def OnDelete(sender, args as DeleteEventArgs):
		Application.Quit()
		args.RetVal = true

Application.Init()

MainWindow().ShowAll()

Application.Run()

import System
import Boo.IO.TextFile
import Gdk from "gdk-sharp" as Gdk
import Gtk from "gtk-sharp"
import GtkSourceView from "gtksourceview-sharp"

class BooEditor(ScrolledWindow):

	static _booSourceLanguage = SourceLanguagesManager().GetLanguageFromMimeType("text/x-boo")

	_buffer = SourceBuffer(_booSourceLanguage, Highlight: true)
	_view = SourceView(_buffer, ShowLineNumbers: true, AutoIndent: true)

	[getter(FileName)]
	_fname as string

	def constructor([required] fname as string):
		_fname = fname
		self.SetPolicy(PolicyType.Automatic, PolicyType.Automatic)
		self.Add(_view)

	Text:
		get:
			return _buffer.Text

		set:
			_buffer.Text = value

	def Redo():
		_buffer.Redo()

	def Undo():
		_buffer.Undo()

class MainWindow(Window):

	_status = Statusbar(HasResizeGrip: false)
	_notebookEditors = Notebook(TabPos: PositionType.Top, Scrollable: true)
	_notebookHelpers = Notebook(TabPos: PositionType.Top, Scrollable: true)
	_notebookOutline = Notebook(TabPos: PositionType.Bottom, Scrollable: true)
	_documentOutline = TreeView()
	_accelGroup = AccelGroup()
	_editors = [] # workaround for gtk# bug #61703

	def constructor():
		super("Boo Explorer")

		self.AddAccelGroup(_accelGroup)
		self.SetDefaultSize(600, 400)
		self.DeleteEvent += OnDelete

		_notebookOutline.AppendPage(_documentOutline, Label("Document Outline"))

		vbox = VBox(false, 1)
		vbox.PackStart(CreateMenuBar(), false, false, 0)

		editPanel = VPaned()
		editPanel.Pack1(_notebookEditors, true, true)
		editPanel.Pack2(_notebookHelpers, false, true)

		mainPanel = HPaned()
		mainPanel.Pack2(_notebookOutline, false, false)
		mainPanel.Pack1(editPanel, true, true)

		vbox.PackStart(mainPanel, true, true, 0)
		vbox.PackStart(_status, false, false, 0)

		self.Add(vbox)

		self.NewDocument()

	private def AppendEditor(editor as BooEditor):
		_notebookEditors.AppendPage(editor, Label(editor.FileName))
		_editors.Add(editor)
		editor.ShowAll()

	def NewDocument():
		self.AppendEditor(BooEditor("unnamed.boo"))

	def OpenDocument(fname as string):
		self.AppendEditor(BooEditor(fname, Text: ReadFile(fname)))

	private def CreateMenuBar():
		mb = MenuBar()

		file = Menu()
		file.Append(ImageMenuItem(Stock.New, _accelGroup, Activated: _menuItemNew_Activated))
		file.Append(ImageMenuItem(Stock.Open, _accelGroup, Activated: _menuItemOpen_Activated))
		file.Append(ImageMenuItem(Stock.Save, _accelGroup, Activated: _menuItemSave_Activated))
		file.Append(SeparatorMenuItem())
		file.Append(ImageMenuItem(Stock.Quit, _accelGroup, Activated: _menuItemExit_Activated))

		edit = Menu()
		edit.Append(ImageMenuItem(Stock.Undo, _accelGroup, Activated: _menuItemUndo_Activated))
		edit.Append(ImageMenuItem(Stock.Redo, _accelGroup, Activated: _menuItemRedo_Activated))
		edit.Append(SeparatorMenuItem())
		edit.Append(ImageMenuItem(Stock.Cut, _accelGroup))
		edit.Append(ImageMenuItem(Stock.Copy, _accelGroup))
		edit.Append(ImageMenuItem(Stock.Paste, _accelGroup))
		edit.Append(ImageMenuItem(Stock.Delete, _accelGroup))
		edit.Append(SeparatorMenuItem())
		edit.Append(ImageMenuItem(Stock.Preferences, _accelGroup))

		tools = Menu()
		tools.Append(mi=ImageMenuItem(Stock.Execute, _accelGroup, Activated: _menuItemExecute_Activated))
		mi.AddAccelerator("activate", _accelGroup, AccelKey(Gdk.Key.F5, Gdk.ModifierType.ControlMask, AccelFlags.Visible))

		documents = Menu()
		documents.Append(ImageMenuItem(Stock.Close, _accelGroup))

		mb.Append(MenuItem("_File", Submenu: file))
		mb.Append(MenuItem("_Edit", Submenu: edit))
		mb.Append(MenuItem("_Tools", Submenu: tools))
		mb.Append(MenuItem("_Documents", Submenu: documents))
		return mb

	CurrentEditor as BooEditor:
		get:
			// can't do the simpler:
			// editor as BooEditor = _notebookEditors.CurrentPageWidget
			// because of gtk# bug #61703
			return _editors[_notebookEditors.CurrentPage]

	private def _menuItemExecute_Activated(sender, args as EventArgs):

		compiler = Boo.Lang.Compiler.BooCompiler()
		compiler.Parameters.Input.Add(
				Boo.Lang.Compiler.IO.StringInput("<script>", CurrentEditor.Text))
		compiler.Parameters.Pipeline = Boo.Lang.Compiler.Pipelines.Run()

		result = compiler.Run()
		print(result.Errors.ToString()) if len(result.Errors)

	private def _menuItemNew_Activated(sender, args as EventArgs):
		self.NewDocument()

	private def _menuItemOpen_Activated(sender, args as EventArgs):
		fs = FileSelection("Select a file", SelectMultiple: true)
		try:
			if cast(int, ResponseType.Ok) == fs.Run():
				for fname in fs.Selections:
					self.OpenDocument(fname)
		ensure:
			fs.Hide()

	private def _menuItemSave_Activated(sender, args as EventArgs):
		pass

	private def _menuItemUndo_Activated(sender, args as EventArgs):
		CurrentEditor.Undo()

	private def _menuItemRedo_Activated(sender, args as EventArgs):
		CurrentEditor.Redo()

	private def _menuItemExit_Activated(sender, args as EventArgs):
		Application.Quit()

	def OnDelete(sender, args as DeleteEventArgs):
		Application.Quit()
		args.RetVal = true

Application.Init()

MainWindow().ShowAll()

Application.Run()


Fixed generators with protected blocks without except cases

namespace BooJs.Compiler.Steps

import System
import Boo.Lang.Compiler(CompilerErrorFactory)
import Boo.Lang.Compiler.Ast
import Boo.Lang.Compiler.Steps
import Boo.Lang.Compiler.TypeSystem.Internal
import Boo.Lang.PatternMatching

class ProcessGenerators(AbstractTransformerCompilerStep):
"""
    Specialized step to process generators replacing Boo's one. Code is
    transformed to a reentrant state machine, allowing to halt execution
    where a yield is found and resume later on from that same point.
"""
    class TransformGenerator(FastDepthFirstVisitor):
        [getter(States)]
        _states = StatementCollection()

        property State as int = 0

        Current as Block:
            get: return States[State]

        _statemachinelabel = ReferenceExpression('statemachine')
        _gotostatemachine = GotoStatement(Label: _statemachinelabel)

        def constructor():
            CreateState()

        def CreateState():
            block = Block()
            States.Add(block)
            return len(States)-1

        def OnExpressionStatement(node as ExpressionStatement):
            Current.Add(node)

        def OnForStatement(node as ForStatement):
            Current.Add(node)

        def OnYieldStatement(node as YieldStatement):
            if node.GetAncestor(NodeType.IfStatement) or node.GetAncestor(NodeType.UnlessStatement):
                raise CompilerErrorFactory.NotImplemented(node, 'Yield is not currently supported inside if or unless statements')

            # Create a re-entry state
            nextstate = CreateState()

            # Setup jump to next state and return value
            Current.Add([| __state = $(nextstate) |])
            Current.Add([| return $(node.Expression) |])

            # Continue in the re-entry state
            State = nextstate

        def OnReturnStatement(node as ReturnStatement):
            if node.GetAncestor(NodeType.IfStatement) or node.GetAncestor(NodeType.UnlessStatement):
                raise CompilerErrorFactory.NotImplemented(node, 'Return is not currently supported inside if or unless statements')

            # If it has an expression just handle as a yield
            if node.Expression:
                ynode = YieldStatement(node.LexicalInfo, Expression: node.Expression)
                OnYieldStatement(ynode)
            # Otherwise just create a new state where the generator terminates
            else:
                nextstate = CreateState()
                Current.Add([| __state = $(nextstate) |])
                Current.Add(_gotostatemachine)
                State = nextstate

            # At this point we want to always stop the generator
            Current.Add([| raise Boo.STOP |])

        def OnWhileStatement(node as WhileStatement):
            # Loop always starts in a new step
            loopstate = CreateState()

            # Make sure the current state ends up in the loop one
            Current.Add([| __state = $(loopstate) |])
            Current.Add(_gotostatemachine)

            State = loopstate

            # Create new step for exiting the loop but don't use it yet
            exitstate = CreateState()

            # Check if we should skip the loop by negating the condition
            ifs = [|
                if not $(node.Condition):
                    __state = $(exitstate)
                    $(_gotostatemachine)
            |]
            Current.Add(ifs)

            # Process the loop body
            Visit node.Block

            # Go back to the start of the loop to check if we have more iterations left
            Current.Add([| __state = $(loopstate) |])
            Current.Add(_gotostatemachine)

            # Continue in the exit step previously created
            State = exitstate

        def OnTryStatement(node as TryStatement):
            # A try block always initiates an state
            trystate = CreateState()

            # Make sure the current state ends up in the new one
            Current.Add([| __state = $(trystate) |])
            Current.Add(_gotostatemachine)

            State = trystate

            # Register ensure block
            if node.EnsureBlock:
                Current.Add([| __final.push( $(BlockExpression(Body: node.EnsureBlock)) ) |])

            Visit node.ProtectedBlock

            # Execute ensure block after we reach the end of the protected block
            if node.EnsureBlock:
                Current.Add( [| __final.pop()() |] )

            # Create a new state for exiting the try/except block
            afterstate = CreateState()

            # Skip the exception handler state
            Current.Add( [| __state = $afterstate |] )
            Current.Add(_gotostatemachine)

            if not node.ExceptionHandlers.IsEmpty:
                # Create a new state for the exception handler
                exceptstate = CreateState()

                # Register the states covered by this protected block
                tryblock = States[trystate] as Block
                catchst = [| $exceptstate |]
                for state in range(trystate, exceptstate):
                    catchst = [| __catch[$state] = $catchst |]
                tryblock.Insert(0, catchst)

                # Produce the exception handler
                State = exceptstate
                Visit node.ExceptionHandlers

            # Continue in the after state
            State = afterstate

        def OnUnlessStatement(node as UnlessStatement):
        """ We just include them in the current state, no special processing is performed
            currently. This means that neither yield nor return statements can be used inside.
        """
            super(node)
            Current.Add(node)

        def OnIfStatement(node as IfStatement):
        """ We just include them in the current state, no special processing is performed
            currently. This means that neither yield nor return statements can be used inside.
        """
            super(node)
            Current.Add(node)

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

    def HasTryStatement(node as Block) as bool:
        for st in node.Statements:
            match st:
                case TryStatement():
                    return true
                case wst=WhileStatement():
                    return HasTryStatement(wst.Block)
                case ist=IfStatement():
                    return HasTryStatement(ist.TrueBlock) or HasTryStatement(ist.FalseBlock)
                case bst=Block():
                    return HasTryStatement(bst)
                otherwise:
                    continue

        return false

    override def EnterMethod(node as Method):
        # Types should be already resolved so we can just check if it was flagged as a generator
        entity = node.Entity as InternalMethod
        return entity and entity.IsGenerator

    override def LeaveMethod(node as Method):

        has_try = HasTryStatement(node.Body)

        # Transform the method body into a list of states
        transformer = TransformGenerator()
        transformer.OnBlock(node.Body)

        # If the method is a closure we look for the original method node to declare locals in it
        method = node
        if node.IsSynthetic:
            for member in (node.ParentNode as TypeDefinition).Members:
                if member.LexicalInfo.Equals(node.LexicalInfo):
                    method = member
                    break

        # Remove the original method contents
        node.Body.Clear()
        # Create a new local to keep the current step of the generator
        CodeBuilder.DeclareLocal(method, '__state', TypeSystemServices.IntType)
        if has_try:
            CodeBuilder.DeclareLocal(method, '__catch', TypeSystemServices.HashType)
            CodeBuilder.DeclareLocal(method, '__final', TypeSystemServices.ArrayType)
            node.Body.Add( CodeBuilder.CreateAssignment(ReferenceExpression('__catch'), HashLiteralExpression()) )
            node.Body.Add( CodeBuilder.CreateAssignment(ReferenceExpression('__final'), ListLiteralExpression()) )

        # Add value check to first state
        valuecheck = [|
            if __value is not Boo.UNDEF or __error is not Boo.UNDEF:
                raise TypeError('Generator not started yet, unable to process sent value/error')
        |]
        (transformer.States[0] as Block).Insert(0, valuecheck)

        # Wrap the steps into a switch/case construct
        switch = MacroStatement(Name: 'switch')
        switch.Arguments.Add(ReferenceExpression('__state'))
        for idx as int, step as Block in enumerate(transformer.States):
            case = MacroStatement()
            case.Name = 'case'
            case.Arguments.Add(IntegerLiteralExpression(idx))
            case.Body = step
            switch.Body.Add(case)

        statemachine as Statement
        statemachine = [|
            # NOTE: To simplify we will raise the sent error from the top of the generator
            #       function and not from the current step. Unless real world scenarios show
            #       that it's desirable to have it raised from more exact locations, it doesn't
            #       seem to have a great value added in contrast to its cost.
            if __error:
                raise __error

            $switch

            # Set the state to an imposible value
            __state = -1
            raise Boo.STOP
        |]

        # Wrap the state machine to support exception handling
        if has_try:
            statemachine = [|
                try:
                    $statemachine
                except:
                    if __state in __catch:
                        __error = null  # Make sure any reported error is removed
                        __state = __catch[__state]
                        goto statemachine

                    # Position the generator to the terminating state
                    __state = -1

                    # Ensure all finally blocks are executed
                    while __final.length:
                        __final.pop()()

                    # Re-raise the exception
                    raise __e
            |]

        # Make the method return a generator
        generator = [|
            return Boo.generator do(__value, __error):
                :statemachine
                $statemachine
        |]
        node.Body.Add(generator)

namespace BooJs.Compiler.Steps

import System
import Boo.Lang.Compiler(CompilerErrorFactory)
import Boo.Lang.Compiler.Ast
import Boo.Lang.Compiler.Steps
import Boo.Lang.Compiler.TypeSystem.Internal
import Boo.Lang.PatternMatching

class ProcessGenerators(AbstractTransformerCompilerStep):
"""
    Specialized step to process generators replacing Boo's one. Code is
    transformed to a reentrant state machine, allowing to halt execution
    where a yield is found and resume later on from that same point.
"""
    class TransformGenerator(FastDepthFirstVisitor):
        [getter(States)]
        _states = StatementCollection()

        property State as int = 0

        Current as Block:
            get: return States[State]

        _statemachinelabel = ReferenceExpression('statemachine')
        _gotostatemachine = GotoStatement(Label: _statemachinelabel)

        def constructor():
            CreateState()

        def CreateState():
            block = Block()
            States.Add(block)
            return len(States)-1

        def OnExpressionStatement(node as ExpressionStatement):
            Current.Add(node)

        def OnForStatement(node as ForStatement):
            Current.Add(node)

        def OnYieldStatement(node as YieldStatement):
            if node.GetAncestor(NodeType.IfStatement) or node.GetAncestor(NodeType.UnlessStatement):
                raise CompilerErrorFactory.NotImplemented(node, 'Yield is not currently supported inside if or unless statements')

            # Create a re-entry state
            nextstate = CreateState()

            # Setup jump to next state and return value
            Current.Add([| __state = $(nextstate) |])
            Current.Add([| return $(node.Expression) |])

            # Continue in the re-entry state
            State = nextstate

        def OnReturnStatement(node as ReturnStatement):
            if node.GetAncestor(NodeType.IfStatement) or node.GetAncestor(NodeType.UnlessStatement):
                raise CompilerErrorFactory.NotImplemented(node, 'Return is not currently supported inside if or unless statements')

            # If it has an expression just handle as a yield
            if node.Expression:
                ynode = YieldStatement(node.LexicalInfo, Expression: node.Expression)
                OnYieldStatement(ynode)
            # Otherwise just create a new state where the generator terminates
            else:
                nextstate = CreateState()
                Current.Add([| __state = $(nextstate) |])
                Current.Add(_gotostatemachine)
                State = nextstate

            # At this point we want to always stop the generator
            Current.Add([| raise Boo.STOP |])

        def OnWhileStatement(node as WhileStatement):
            # Loop always starts in a new step
            loopstate = CreateState()

            # Make sure the current state ends up in the loop one
            Current.Add([| __state = $(loopstate) |])
            Current.Add(_gotostatemachine)

            State = loopstate

            # Create new step for exiting the loop but don't use it yet
            exitstate = CreateState()

            # Check if we should skip the loop by negating the condition
            ifs = [|
                if not $(node.Condition):
                    __state = $(exitstate)
                    $(_gotostatemachine)
            |]
            Current.Add(ifs)

            # Process the loop body
            Visit node.Block

            # Go back to the start of the loop to check if we have more iterations left
            Current.Add([| __state = $(loopstate) |])
            Current.Add(_gotostatemachine)

            # Continue in the exit step previously created
            State = exitstate

        def OnTryStatement(node as TryStatement):
            # A try block always initiates an state
            trystate = CreateState()

            # Make sure the current state ends up in the new one
            Current.Add([| __state = $(trystate) |])
            Current.Add(_gotostatemachine)

            State = trystate

            # Register ensure block
            if node.EnsureBlock:
                Current.Add([| __final.push( $(BlockExpression(Body: node.EnsureBlock)) ) |])

            Visit node.ProtectedBlock

            # Execute ensure block after we reach the end of the protected block
            if node.EnsureBlock:
                Current.Add( [| __final.pop()() |] )

            # Create a new state for the exception handler
            exceptstate = CreateState()

            # Register the states covered by this protected block
            tryblock = States[trystate] as Block
            catchst = [| $exceptstate |]
            for state in range(trystate, exceptstate):
                catchst = [| __catch[$state] = $catchst |]
            tryblock.Insert(0, catchst)

            # Create a new state for exiting the try/except block
            afterstate = CreateState()

            # Skip the exception handler state
            Current.Add( [| __state = $afterstate |] )
            Current.Add(_gotostatemachine)

            # Produce the exception handler
            State = exceptstate
            Visit node.ExceptionHandlers

            # Continue in the after state
            State = afterstate

        def OnUnlessStatement(node as UnlessStatement):
        """ We just include them in the current state, no special processing is performed
            currently. This means that neither yield nor return statements can be used inside.
        """
            super(node)
            Current.Add(node)

        def OnIfStatement(node as IfStatement):
        """ We just include them in the current state, no special processing is performed
            currently. This means that neither yield nor return statements can be used inside.
        """
            super(node)
            Current.Add(node)

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

    def HasTryStatement(node as Block) as bool:
        for st in node.Statements:
            match st:
                case TryStatement():
                    return true
                case wst=WhileStatement():
                    return HasTryStatement(wst.Block)
                case ist=IfStatement():
                    return HasTryStatement(ist.TrueBlock) or HasTryStatement(ist.FalseBlock)
                case bst=Block():
                    return HasTryStatement(bst)
                otherwise:
                    continue

        return false

    override def EnterMethod(node as Method):
        # Types should be already resolved so we can just check if it was flagged as a generator
        entity = node.Entity as InternalMethod
        return entity and entity.IsGenerator

    override def LeaveMethod(node as Method):

        has_try = HasTryStatement(node.Body)

        # Transform the method body into a list of states
        transformer = TransformGenerator()
        transformer.OnBlock(node.Body)

        # If the method is a closure we look for the original method node to declare locals in it
        method = node
        if node.IsSynthetic:
            for member in (node.ParentNode as TypeDefinition).Members:
                if member.LexicalInfo.Equals(node.LexicalInfo):
                    method = member
                    break

        # Remove the original method contents
        node.Body.Clear()
        # Create a new local to keep the current step of the generator
        CodeBuilder.DeclareLocal(method, '__state', TypeSystemServices.IntType)
        if has_try:
            CodeBuilder.DeclareLocal(method, '__catch', TypeSystemServices.HashType)
            CodeBuilder.DeclareLocal(method, '__final', TypeSystemServices.ArrayType)
            node.Body.Add( CodeBuilder.CreateAssignment(ReferenceExpression('__catch'), HashLiteralExpression()) )
            node.Body.Add( CodeBuilder.CreateAssignment(ReferenceExpression('__final'), ListLiteralExpression()) )

        # Add value check to first state
        valuecheck = [|
            if __value is not Boo.UNDEF or __error is not Boo.UNDEF:
                raise TypeError('Generator not started yet, unable to process sent value/error')
        |]
        (transformer.States[0] as Block).Insert(0, valuecheck)

        # Wrap the steps into a switch/case construct
        switch = MacroStatement(Name: 'switch')
        switch.Arguments.Add(ReferenceExpression('__state'))
        for idx as int, step as Block in enumerate(transformer.States):
            case = MacroStatement()
            case.Name = 'case'
            case.Arguments.Add(IntegerLiteralExpression(idx))
            case.Body = step
            switch.Body.Add(case)

        statemachine as Statement
        statemachine = [|
            # NOTE: To simplify we will raise the sent error from the top of the generator
            #       function and not from the current step. Unless real world scenarios show
            #       that it's desirable to have it raised from more exact locations, it doesn't
            #       seem to have a great value added in contrast to its cost.
            if __error:
                raise __error

            $switch

            # Set the state to an imposible value
            __state = -1
            raise Boo.STOP
        |]

        # Wrap the state machine to support exception handling
        if has_try:
            statemachine = [|
                try:
                    $statemachine
                except:
                    if __state in __catch:
                        __error = null  # Make sure any reported error is removed
                        __state = __catch[__state]
                        goto statemachine

                    # Position the generator to the terminating state
                    __state = -1

                    # Ensure all finally blocks are executed
                    while __final.length:
                        __final.pop()()

                    # Re-raise the exception
                    raise __e
            |]

        # Make the method return a generator
        generator = [|
            return Boo.generator do(__value, __error):
                :statemachine
                $statemachine
        |]
        node.Body.Add(generator)