Cleaning up generator step

Cleaning up generator step

namespace BooJs.Compiler.Steps

from Boo.Lang.Compiler import CompilerErrorFactory
from Boo.Lang.Compiler.Ast import *
from Boo.Lang.Compiler.Steps import AbstractTransformerCompilerStep
from Boo.Lang.Compiler.TypeSystem.Internal import *
from Boo.Lang.PatternMatching import *

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

    The generated code is convoluted but pretty fast on modern browsers, it
    runs roughly at half the speed of an user land forEach implementation
    and about 70% the speed of Firefox's native generators.
    http://jsperf.com/boojs-generator-loop

"""
    static final REF_VALUE = ReferenceExpression(Name:'_value_')
    static final REF_ERROR = ReferenceExpression(Name:'_error_')
    static final REF_STATE = ReferenceExpression(Name:'_state_')
    static final REF_EXCEPT = ReferenceExpression(Name:'_except_')
    static final REF_ENSURE = ReferenceExpression(Name:'_ensure_')
    static final REF_STATEMACHINE = ReferenceExpression(Name:'statemachine')
    static final JUMP = GotoStatement(Label: REF_STATEMACHINE)

    class TransformGenerator(FastDepthFirstVisitor):
        [getter(States)]
        _states = StatementCollection()

        property State as int = 0

        Current as Block:
            get: return States[State]

        def constructor():
            CreateState()

            # Add value check to first state
            valuecheck = [|
                if $REF_VALUE is not Boo.UNDEF or $REF_ERROR is not Boo.UNDEF:
                    raise TypeError('Generator not started yet, unable to process sent value/error')
            |]
            Current.Add(valuecheck)

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

        def OnYieldStatement(node as YieldStatement):
            # Create a re-entry state
            nextstate = CreateState()

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

            # Continue in the re-entry state
            State = nextstate

        def OnReturnStatement(node as ReturnStatement):
            # Make sure the generator is stopped even if re-entered
            Current.Add([| $REF_STATE = -1 |])
            Current.Add(node)

        def OnRaiseStatement(node as RaiseStatement):
            # Make sure the generator is stopped even if re-entered
            Current.Add([| $REF_STATE = -1 |])
            Current.Add(node)

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

        def OnForStatement(node as ForStatement):
            # We assume that loops with yields where converted to while statements
            Current.Add(node)

        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([| $REF_STATE = $(loopstate) |])
            if State != loopstate - 1:
                Current.Add(JUMP)

            # Process the loop body
            State = loopstate
            Visit node.Block

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

            # 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
            State = loopstate
            ifs = [|
                if not $(node.Condition):
                    $REF_STATE = $exitstate
                    $JUMP
            |]
            # Place it as the first statement in the loop state
            Current.Insert(0, ifs)

            # Continue in the exit step previously created
            State = exitstate

        def OnTryStatement(node as TryStatement):
            # A try block always initiates an state. This simplifies a bit the
            # mapping of protected blocks.
            trystate = CreateState()

            # Make sure the current state ends up in the new one
            Current.Add([| $REF_STATE = $(trystate) |])
            if State != trystate - 1:
                Current.Add(JUMP)

            State = trystate

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

            Visit node.ProtectedBlock

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

            # Skip the exception handler state
            Current.Add( [| $REF_STATE = $afterstate |] )
            if State != afterstate - 1:
                Current.Add(JUMP)

            unless node.ExceptionHandlers.IsEmpty:
                # Register the states covered by this protected block
                State = trystate
                Current.Insert(0, [| $(REF_EXCEPT).push($exceptstate) |])

                # Produce the exception handler
                State = exceptstate
                # HACK: Assign the value to the global ex holder
                Current.Add([| _ex_ = $(REF_VALUE) |])

                Visit node.ExceptionHandlers

                # HACK: If we reached the end add a new guard so we pop it in afterstate
                #       This avoids creating a specific state for the it.
                Current.Add([| $(REF_EXCEPT).push(-1) |])

                # Make sure we end up in the after state
                Current.Add([| $REF_STATE = $afterstate |])
                if State != afterstate - 1:
                    Current.Add(JUMP)

                # Remove the guard once we are out of the except block
                State = afterstate
                Current.Add([| $(REF_EXCEPT).pop() |])

            # Continue in the after state
            State = afterstate

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

            # We need to make absolutely sure we jump out to a clean state
            # NOTE: nested try/except blocks may have created new states
            nextstate = CreateState()
            Current.Add([| $REF_STATE = $nextstate |])
            if State != nextstate - 1:
                Current.Add(JUMP)

            State = nextstate

        def OnUnlessStatement(node as UnlessStatement):
            # We just convert to a negated IfStatement
            ifnode = IfStatement(node.LexicalInfo)
            ifnode.Condition = [| not $(node.Condition) |]
            ifnode.TrueBlock = node.Block
            Visit(ifnode)

        def OnIfStatement(node as IfStatement):
            # Optimize common case of simple if without else clause
            if not node.FalseBlock or node.FalseBlock.IsEmpty:
                placeholder = ExpressionStatement()
                block = [|
                    if not $(node.Condition):
                        $placeholder
                        $JUMP
                |]
                Current.Add(block)

                # Dump the conditional block
                Visit(node.TrueBlock)

                # Create an exit state and setup the jump
                exitstate = CreateState()
                placeholder.Expression = [| $REF_STATE = $exitstate |]
                State = exitstate
                return

            truestate = CreateState()
            falsestate = CreateState()
            block = [|
                if $(node.Condition):
                    $REF_STATE = $truestate
                    $JUMP
                else:
                    $REF_STATE = $falsestate
                    $JUMP
            |]
            Current.Add(block)

            State = truestate
            Visit(node.TrueBlock)
            truestate = State

            State = falsestate
            Visit(node.FalseBlock)
            falsestate = State

            exitstate = CreateState()

            # Point true and false blocks to the exit
            State = truestate
            Current.Add([| $REF_STATE = $exitstate |])
            if State != exitstate - 1:
                Current.Add(JUMP)
            State = falsestate
            Current.Add([| $REF_STATE = $exitstate |])
            if State != exitstate - 1:
                Current.Add(JUMP)

            # Continue in the exit state
            State = exitstate

    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):
    """ NOTE: We just handle methods. Boo will create generators in a separate method even for
              block expressions. We will later remove them but since right now their body references
              the same node, we can just work with them and block expressions will be automatically
              updated too.
    """
        # 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 state machine
        transformer = TransformGenerator()
        transformer.OnBlock(node.Body)

        # Remove the original method contents
        node.Body.Clear()
        # Create a new local to keep the current state of the generator
        CodeBuilder.DeclareLocal(node, REF_STATE.Name, TypeSystemServices.IntType)
        node.Body.Add( CodeBuilder.CreateAssignment(REF_STATE, IntegerLiteralExpression(0)) )
        if has_try:
            CodeBuilder.DeclareLocal(node, REF_EXCEPT.Name, TypeSystemServices.ArrayType)
            CodeBuilder.DeclareLocal(node, REF_ENSURE.Name, TypeSystemServices.ArrayType)
            node.Body.Add( CodeBuilder.CreateAssignment(REF_EXCEPT, ListLiteralExpression()) )
            node.Body.Add( CodeBuilder.CreateAssignment(REF_ENSURE, ListLiteralExpression()) )

        # Wrap the steps into a switch/case construct
        # TODO: Use Boo's native switch construct
        switch = MacroStatement(Name: 'switch')
        switch.Arguments.Add(REF_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 state. 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 $REF_ERROR:
                raise $REF_ERROR

            $switch

            # Set the state to an impossible value
            $REF_STATE = -1
        |]

        # Wrap the state machine to support exception handling
        if has_try:
            statemachine = [|
                try:
                    $statemachine
                except:
                    # HACK: Assign the exception to _value_ so we can work with it
                    #       in a matched except block or override it from the ensures
                    $REF_VALUE = _ex_

                    if $(REF_EXCEPT).length:
                        $REF_ERROR = null  # Make sure any reported error is removed
                        $REF_STATE = $(REF_EXCEPT).pop()
                        goto statemachine

                    # Position the generator to the terminating state
                    $REF_STATE = -1

                    # Ensure all finally blocks are executed before exiting
                    # NOTE: Any error raised inside an ensure block overrides the previous
                    while $(REF_ENSURE).length:
                        try:
                            $(REF_ENSURE).pop()()
                        except:
                            $REF_VALUE = _ex_

                    # Re-raise the exception
                    raise $REF_VALUE
            |]

        # Make the method return a generator
        generator = [|
            # Wrap the state machine with the runtime helper
            return Boo.generator do($REF_VALUE, $REF_ERROR):
                :statemachine
                $statemachine
                # If the execution reaches the end we want to notify the stop of the iteration
                raise Boo.STOP
        |]

        node.Body.Add(generator)

namespace BooJs.Compiler.Steps

from Boo.Lang.Compiler import CompilerErrorFactory
from Boo.Lang.Compiler.Ast import *
from Boo.Lang.Compiler.Steps import AbstractTransformerCompilerStep
from Boo.Lang.Compiler.TypeSystem.Internal import *
from Boo.Lang.PatternMatching import *

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

    The generated code is convoluted but pretty fast on modern browsers, it
    runs roughly at half the speed of an user land forEach implementation
    and about 70% the speed of Firefox's native generators.
    http://jsperf.com/boojs-generator-loop

"""
    static final REF_VALUE = ReferenceExpression(Name:'_value_')
    static final REF_ERROR = ReferenceExpression(Name:'_error_')
    static final REF_STATE = ReferenceExpression(Name:'_state_')
    static final REF_EXCEPT = ReferenceExpression(Name:'_except_')
    static final REF_ENSURE = ReferenceExpression(Name:'_ensure_')
    static final REF_STATEMACHINE = ReferenceExpression(Name:'statemachine')
    static final JUMP = GotoStatement(Label: REF_STATEMACHINE)

    class TransformGenerator(FastDepthFirstVisitor):
        [getter(States)]
        _states = StatementCollection()

        property State as int = 0

        Current as Block:
            get: return States[State]

        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):
            # We assume that loops with yields where converted to while statements
            Current.Add(node)

        def OnYieldStatement(node as YieldStatement):
            # Create a re-entry state
            nextstate = CreateState()

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

            # Continue in the re-entry state
            State = nextstate

        def OnReturnStatement(node as ReturnStatement):
            # 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([| $REF_STATE = $nextstate |])
                if State != nextstate - 1:
                    Current.Add(JUMP)
                State = nextstate

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

        def OnRaiseStatement(node as RaiseStatement):
            # Create a new state in case the generator is entered again
            nextstate = CreateState()
            Current.Add([| $REF_STATE = $nextstate |])

            # Raise now
            Current.Add(node)

            # At this point we want to always stop the generator
            State = nextstate
            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([| $REF_STATE = $(loopstate) |])
            if State != loopstate - 1:
                Current.Add(JUMP)

            # Process the loop body
            State = loopstate
            Visit node.Block

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

            # 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
            State = loopstate
            ifs = [|
                if not $(node.Condition):
                    $REF_STATE = $exitstate
                    $JUMP
            |]
            # Place it as the first statement in the loop state
            Current.Insert(0, ifs)

            # 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([| $REF_STATE = $(trystate) |])
            if State != trystate - 1:
                Current.Add(JUMP)

            State = trystate

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

            Visit node.ProtectedBlock

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

            # Skip the exception handler state
            Current.Add( [| $REF_STATE = $afterstate |] )
            if State != afterstate - 1:
                Current.Add(JUMP)

            unless node.ExceptionHandlers.IsEmpty:
                # Register the states covered by this protected block
                State = trystate
                Current.Insert(0, [| $(REF_EXCEPT).push($exceptstate) |])

                # Produce the exception handler
                State = exceptstate
                # HACK: Assign the value to the global ex holder
                Current.Add([| _ex_ = $(REF_VALUE) |])

                Visit node.ExceptionHandlers

                # HACK: If we reached the end add a new guard so we pop it in afterstate
                #       This avoids creating a specific state for the it.
                Current.Add([| $(REF_EXCEPT).push(-1) |])

                # Make sure we end up in the after state
                Current.Add([| $REF_STATE = $afterstate |])
                if State != afterstate - 1:
                    Current.Add(JUMP)

                # Remove the guard once we are out of the except block
                State = afterstate
                Current.Add([| $(REF_EXCEPT).pop() |])

            # Continue in the after state
            State = afterstate

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

            # We need to make absolutely sure we jump out to a clean state
            # NOTE: nested try/except blocks may have created new states
            nextstate = CreateState()
            Current.Add([| $REF_STATE = $nextstate |])
            if State != nextstate - 1:
                Current.Add(JUMP)

            State = nextstate

        def OnUnlessStatement(node as UnlessStatement):
        """ We just convert to a simple IfStatement
        """
            ifnode = IfStatement(node.LexicalInfo)
            ifnode.Condition = [| not $(node.Condition) |]
            ifnode.TrueBlock = node.Block
            Visit(ifnode)

        def OnIfStatement(node as IfStatement):
            # Optimize common case of simple if without else clause
            if not node.FalseBlock or node.FalseBlock.IsEmpty:
                placeholder = ExpressionStatement()
                block = [|
                    if not $(node.Condition):
                        $placeholder
                        $JUMP
                |]
                Current.Add(block)

                # Dump the conditional block
                Visit(node.TrueBlock)

                # Create an exit state and setup the jump
                exitstate = CreateState()
                placeholder.Expression = [| $REF_STATE = $exitstate |]
                State = exitstate
                return

            truestate = CreateState()
            falsestate = CreateState()
            block = [|
                if $(node.Condition):
                    $REF_STATE = $truestate
                    $JUMP
                else:
                    $REF_STATE = $falsestate
                    $JUMP
            |]
            Current.Add(block)

            State = truestate
            Visit(node.TrueBlock)
            truestate = State

            State = falsestate
            Visit(node.FalseBlock)
            falsestate = State

            exitstate = CreateState()

            # Point true and false blocks to the exit
            State = truestate
            Current.Add([| $REF_STATE = $exitstate |])
            if State != exitstate - 1:
                Current.Add(JUMP)
            State = falsestate
            Current.Add([| $REF_STATE = $exitstate |])
            if State != exitstate - 1:
                Current.Add(JUMP)

            # Continue in the exit state
            State = exitstate

    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):
    """ NOTE: We just handle methods. Boo will create generators in a separate method even for
              block expressions. We will later remove them but since right now their body references
              the same node, we can just work with them and block expressions will be automatically
              updated too.
    """
        # 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 state machine
        transformer = TransformGenerator()
        transformer.OnBlock(node.Body)

        # Remove the original method contents
        node.Body.Clear()
        # Create a new local to keep the current state of the generator
        CodeBuilder.DeclareLocal(node, REF_STATE.Name, TypeSystemServices.IntType)
        node.Body.Add( CodeBuilder.CreateAssignment(REF_STATE, IntegerLiteralExpression(0)) )
        if has_try:
            CodeBuilder.DeclareLocal(node, REF_EXCEPT.Name, TypeSystemServices.ArrayType)
            CodeBuilder.DeclareLocal(node, REF_ENSURE.Name, TypeSystemServices.ArrayType)
            node.Body.Add( CodeBuilder.CreateAssignment(REF_EXCEPT, ListLiteralExpression()) )
            node.Body.Add( CodeBuilder.CreateAssignment(REF_ENSURE, ListLiteralExpression()) )

        # Add value check to first state
        valuecheck = [|
            if $REF_VALUE is not Boo.UNDEF or $REF_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
        # TODO: Use Boo's native switch construct
        switch = MacroStatement(Name: 'switch')
        switch.Arguments.Add(REF_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 state. 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 $REF_ERROR:
                raise $REF_ERROR

            $switch

            # Set the state to an impossible value
            $REF_STATE = -1
        |]

        # Wrap the state machine to support exception handling
        if has_try:
            statemachine = [|
                try:
                    $statemachine
                except:
                    # HACK: Assign the exception to _value_ so we can work with it
                    #       in a matched except block or override it from the ensures
                    $REF_VALUE = _ex_

                    if $(REF_EXCEPT).length:
                        $REF_ERROR = null  # Make sure any reported error is removed
                        $REF_STATE = $(REF_EXCEPT).pop()
                        goto statemachine

                    # Position the generator to the terminating state
                    $REF_STATE = -1

                    # Ensure all finally blocks are executed before exiting
                    # NOTE: Any error raised inside an ensure block overrides the previous
                    while $(REF_ENSURE).length:
                        try:
                            $(REF_ENSURE).pop()()
                        except:
                            $REF_VALUE = _ex_

                    # Re-raise the exception
                    raise $REF_VALUE
            |]

        # Make the method return a generator
        generator = [|
            # Wrap the state machine with the runtime helper
            return Boo.generator do($REF_VALUE, $REF_ERROR):
                :statemachine
                $statemachine
                # If the execution reaches the end we want to notify the stop of the iteration
                raise Boo.STOP
        |]

        node.Body.Add(generator)


initial support for interfaces and fields

namespace Boo.OMeta.Parser

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

import System.Globalization

macro infix:

	l, op, r = infix.Arguments

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

macro infixr:

	l, op, r = infixr.Arguments

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

macro prefix:

	rule, op, next = prefix.Arguments

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

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

	list_of expression

Expands to something that matches:

	expression (COMMA expression)+
"""

	rule, = list_of.Arguments

	block as Block = list_of.ParentNode

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

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

ometa BooParser < WhitespaceSensitiveTokenizer:

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

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

	space = line_comment | super

	line_comment = '#', --(~newline, _)

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

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

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

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

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

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

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

	module_member = class_def | interface_def | method

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

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

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

	begin_block = COLON, INDENT

	end_block = DEDENT

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

	interface_body = no_member

	no_member = (PASS, eol) ^ null

	class_member = method | class_def | field

	field = (ID >> name, optional_type >> type, field_initializer >> initializer, eol) ^ newField(name, type, initializer)

	field_initializer = (ASSIGN, rvalue) | ""

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

	list_of parameter

	parameter = (ID >> name, optional_type >> type) ^ ParameterDeclaration(Name: tokenValue(name), Type: type)

	optional_type = (AS, type_reference) | ""

	block = empty_block | non_empty_block

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

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

	stmt = stmt_block | stmt_line

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

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

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

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

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

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

	stmt_block = stmt_if | stmt_for

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

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

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

	optional_assignment = assignment | ""

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

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

	rvalue = assignment_list >> items ^ newRValue(items)

	list_of assignment

	infixr assignment, (ASSIGN | ASSIGN_INPLACE), expression

	expression = or_expression

	infix or_expression, OR, and_expression

	infix and_expression, AND, not_expression

	prefix not_expression, NOT, membership_expression

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

	// need to fix memoization for rule with arguments
	infix identity_test_expression, (IS, ((NOT ^ makeToken("is not")) | ("" ^ makeToken("is")))), comparison

	infix comparison, (EQUALITY | INEQUALITY | GREATER_THAN | GREATER_THAN_EQ | LESS_THAN | LESS_THAN_EQ), bitwise_or_expression

	infix bitwise_or_expression, BITWISE_OR, bitwise_xor_expression

	infix bitwise_xor_expression, XOR, bitwise_and_expression

	infix bitwise_and_expression, BITWISE_AND, bitwise_shift_expression

	infix bitwise_shift_expression, (BITWISE_SHIFT_LEFT | BITWISE_SHIFT_RIGHT), term

	infix term, (PLUS | MINUS), factor

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

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

	prefix ones_complement_expression, ONES_COMPLEMENT, exponentiation_expression

	infix exponentiation_expression, EXPONENTIATION, try_cast

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

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

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

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

	list_of expression

	list_of declaration

	list_of type_reference

	list_of slice

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

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

	type_reference = type_reference_simple | type_reference_array

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

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

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

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

	null_literal = NULL ^ [| null |]

	super_literal = SUPER ^ [| super |]

	self_literal = SELF ^ [| self |]

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

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

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

	array_literal = array_literal_empty | array_literal_single | array_literal_multi

	array_literal_empty = (LPAREN, 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, RBRACK) ^ newListLiteral(items)

	reference = ID >> r ^ newReference(r)

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

	boolean = true_literal | false_literal

	true_literal = TRUE ^ [| true |]

	false_literal = FALSE ^ [| false |]

	eol = ++EOL | ~_

namespace Boo.OMeta.Parser

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

import System.Globalization

macro infix:

	l, op, r = infix.Arguments

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

macro infixr:

	l, op, r = infixr.Arguments

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

macro prefix:

	rule, op, next = prefix.Arguments

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

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

	list_of expression

Expands to something that matches:

	expression (COMMA expression)+
"""

	rule, = list_of.Arguments

	block as Block = list_of.ParentNode

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

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

ometa BooParser < WhitespaceSensitiveTokenizer:

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

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

	space = line_comment | super

	line_comment = '#', --(~newline, _)

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

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

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

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

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

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

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

	module_member = class_def | method

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

	begin_block = COLON, INDENT

	end_block = DEDENT

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

	class_member = method | class_def

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

	list_of parameter

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

	block = empty_block | non_empty_block

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

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

	stmt = stmt_block | stmt_line

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

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

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

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

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

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

	stmt_block = stmt_if | stmt_for

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

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

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

	optional_assignment = assignment | ""

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

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

	rvalue = assignment_list >> items ^ newRValue(items)

	list_of assignment

	infixr assignment, (ASSIGN | ASSIGN_INPLACE), expression

	expression = or_expression

	infix or_expression, OR, and_expression

	infix and_expression, AND, not_expression

	prefix not_expression, NOT, membership_expression

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

	// need to fix memoization for rule with arguments
	infix identity_test_expression, (IS, ((NOT ^ makeToken("is not")) | ("" ^ makeToken("is")))), comparison

	infix comparison, (EQUALITY | INEQUALITY | GREATER_THAN | GREATER_THAN_EQ | LESS_THAN | LESS_THAN_EQ), bitwise_or_expression

	infix bitwise_or_expression, BITWISE_OR, bitwise_xor_expression

	infix bitwise_xor_expression, XOR, bitwise_and_expression

	infix bitwise_and_expression, BITWISE_AND, bitwise_shift_expression

	infix bitwise_shift_expression, (BITWISE_SHIFT_LEFT | BITWISE_SHIFT_RIGHT), term

	infix term, (PLUS | MINUS), factor

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

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

	prefix ones_complement_expression, ONES_COMPLEMENT, exponentiation_expression

	infix exponentiation_expression, EXPONENTIATION, try_cast

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

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

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

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

	list_of slice

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

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

	type_reference = type_reference_simple | type_reference_array

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

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

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

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

	null_literal = NULL ^ [| null |]

	super_literal = SUPER ^ [| super |]

	self_literal = SELF ^ [| self |]

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

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

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

	array_literal = array_literal_empty | array_literal_single | array_literal_multi

	array_literal_empty = (LPAREN, 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, RBRACK) ^ newListLiteral(items)

	list_of expression

	list_of declaration

	reference = ID >> r ^ newReference(r)

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

	boolean = true_literal | false_literal

	true_literal = TRUE ^ [| true |]

	false_literal = FALSE ^ [| false |]

	eol = ++EOL | ~_


not predicate does not consume input

not predicate does not consume input

namespace Paragraphs

import Boo.Pegs

indent = ""
def ods(m):
	print indent, m

peg:
	document = ++[para, line], eof
	para = inset, enter, line, --((@inset, inset) & para | (@inset, line)), leave
	inset = ++[" ", "\t"]
	line = ++(not "\n", any()), { ods $text }, eol
	eol = "\n"
	eof = not any()

	enter = do:
		ods "{"
		indent += "\t"

	leave = do:
		indent = indent[:-1]
		ods "}"

s = """first line
second line
   p11
   p12
   	p21
   		p3
   	p22
   p13
f
third line
"""

print PegContext(s).Match(document)
#print PegDebugContext(s).Match(document)

namespace Paragraphs

import Boo.Pegs

indent = ""
def ods(m):
	print indent, m

peg:
	document = ++[para, line], eof
	para = inset, enter, line, --((@inset, inset) & para | (@inset, line)), leave
	inset = ++[" ", "\t"]
	line = ++(not "\n"), { ods $text }, eol
	eol = "\n"
	eof = not any()

	enter = do:
		ods "{"
		indent += "\t"

	leave = do:
		indent = indent[:-1]
		ods "}"

s = """first line
second line
   p11
   p12
   	p21
   		p3
   	p22
   p13
f
third line
"""

print PegContext(s).Match(document)


ensure warning settings always reflect the active module

namespace UnityScript.Steps

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

import Boo.Lang.Environments

import UnityScript.Core
import UnityScript.Macros
import UnityScript.TypeSystem

class ProcessUnityScriptMethods(ProcessMethodBodiesWithDuckTyping):

	deferred IEnumerable_GetEnumerator = Types.IEnumerable.GetMethod("GetEnumerator");

	deferred IEnumerator_MoveNext = Types.IEnumerator.GetMethod("MoveNext");

	deferred IEnumerator_get_Current = Types.IEnumerator.GetProperty("Current").GetGetMethod();

	deferred _StartCoroutine = NameResolutionService.ResolveMethod(UnityScriptTypeSystem.ScriptBaseType, "StartCoroutine_Auto")

	deferred _UnityRuntimeServices_GetEnumerator = ResolveUnityRuntimeMethod("GetEnumerator")

	deferred _UnityRuntimeServices_Update = ResolveUnityRuntimeMethod("Update")

	deferred _UnityRuntimeServices_GetTypeOf = ResolveUnityRuntimeMethod("GetTypeOf")

	_implicit = false

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

		// don't transform
		//     foo == null
		// into:
		//     foo is null
		// but into:
		//     foo.Equals(null)
		self.OptimizeNullComparisons = false

	def ResolveUnityRuntimeMethod(name as string):
		return NameResolutionService.ResolveMethod(UnityRuntimeServicesType, name)

	def ResolveUnityRuntimeField(name as string):
		return NameResolutionService.ResolveField(UnityRuntimeServicesType, name)

	deferred UnityRuntimeServicesType = TypeSystemServices.Map(UnityScript.Lang.UnityRuntimeServices)

	UnityScriptTypeSystem as UnityScript.TypeSystem.UnityScriptTypeSystem:
		get: return self.TypeSystemServices

	UnityScriptParameters as UnityScript.UnityScriptCompilerParameters:
		get: return _context.Parameters

	override def GetGeneratorReturnType(generator as InternalMethod):
		return TypeSystemServices.IEnumeratorType

	override protected def MemberNotFound(node as MemberReferenceExpression, ns as INamespace):
		if Strict:
			super(node, ns)
		else:
			BindQuack(node)

	override protected def LocalToReuseFor(d as Declaration):
		if DeclarationAnnotations.ShouldForceNewVariableFor(d):
			AssertUniqueLocal(d)
			return null
		return super(d)

	override def OnModule(module as Module):
		EnterModuleContext(module)
		super(module)

	override def VisitMemberPreservingContext(node as TypeMember):

		module = node.EnclosingModule
		if module is _activeModule:
			super(node)
			return

		preserving Parameters.Strict, _implicit, ActiveModule:
			EnterModuleContext(module)
			super(node)

	private def EnterModuleContext(module as Module):
		Parameters.Strict = Pragmas.IsEnabledOn(module, Pragmas.Strict)
		_implicit = Pragmas.IsEnabledOn(module, Pragmas.Implicit)
		ActiveModule = module

	ImplicitDowncast:
		get: return Boo.Lang.Compiler.CompilerWarningFactory.Codes.ImplicitDowncast

	ActiveModule:
		get: return _activeModule
		set:
			_activeModule = value
			UpdateWarningSettingsForActiveModule()

	def UpdateWarningSettingsForActiveModule():
		if Pragmas.IsDisabledOn(_activeModule, Pragmas.Downcast):
			Parameters.DisableWarning(ImplicitDowncast)
		else:
			Parameters.EnableWarning(ImplicitDowncast)

	_activeModule as Module

	Strict:
		get: return Parameters.Strict

	override def OnMethod(node as Method):
		super(node)
		CheckForEmptyCoroutine(node)
		return if Parameters.OutputType == CompilerOutputType.Library
		CheckEntryPoint(node)

	def CheckForEmptyCoroutine(node as Method):
		if not IsEmptyCoroutine(node):
			return
		node.Body.Add([| return $(EmptyEnumeratorReference) |])

	def IsEmptyCoroutine(node as Method):
		entity as InternalMethod = GetEntity(node)
		return entity.ReturnType is GetGeneratorReturnType(entity) and HasNeitherReturnNorYield(node)

	deferred EmptyEnumeratorReference = CodeBuilder.CreateMemberReference(ResolveUnityRuntimeField("EmptyEnumerator"))

	def CheckEntryPoint(node as Method):
		if not node.IsStatic: return
		if not node.IsPublic: return
		if node.Name != "Main": return
		if GetType(node.ReturnType) is not TypeSystemServices.VoidType: return

		ContextAnnotations.SetEntryPoint(_context, node)

	override def ProcessAutoLocalDeclaration(node as BinaryExpression, reference as ReferenceExpression):
		if (Strict and not _implicit) and not IsCompilerGenerated(reference):
			EmitUnknownIdentifierError(reference, reference.Name)
		else:
			super(node, reference)

	def IsCompilerGenerated(reference as ReferenceExpression):
		return reference.Name.Contains('$')

	override protected def ProcessBuiltinInvocation(node as MethodInvocationExpression, function as BuiltinFunction):
		if function is UnityScriptTypeSystem.UnityScriptEval:
			EvalAnnotation.Mark(_currentMethod.Method)
			BindExpressionType(node, TypeSystemServices.ObjectType)
			return
		if function is UnityScriptTypeSystem.UnityScriptTypeof:
			ProcessTypeofBuiltin(node);
			return
		super(node, function)

	private def ProcessTypeofBuiltin(node as MethodInvocationExpression):
		if node.Arguments.Count != 1:
			Error(node, CompilerError("UCE0001", node.Target.LexicalInfo, "'typeof' takes a single argument.", null))
			return

		type = node.Arguments[0].Entity as IType
		if type is not null:
			node.ParentNode.Replace(node, CodeBuilder.CreateTypeofExpression(type))
			return

		node.Target = CodeBuilder.CreateReference(_UnityRuntimeServices_GetTypeOf)
		BindExpressionType(node, TypeSystemServices.TypeType)

	override protected def ProcessMethodInvocation(node as MethodInvocationExpression, method as IMethod):
	"""
	Automatically detects coroutine invocations in assignments and as standalone
	expressions and generates StartCoroutine invocations.
	"""
		super(node, method)

		if not IsPossibleStartCoroutineInvocation(node):
			return

		if method.IsStatic: return

		tss = self.UnityScriptTypeSystem
		if not tss.IsScriptType(method.DeclaringType): return
		if not tss.IsGenerator(method): return

		parentNode = node.ParentNode
		parentNode.Replace(
			node,
			CodeBuilder.CreateMethodInvocation(
				TargetForStartCoroutineInvocation(node, method),
				_StartCoroutine,
				node))

	def TargetForStartCoroutineInvocation(node as MethodInvocationExpression, method as IMethod):
		target = cast(MemberReferenceExpression, node.Target).Target
		if target isa SuperLiteralExpression: // super becomes self for coroutine invocation
			return CodeBuilder.CreateSelfReference(target.LexicalInfo, method.DeclaringType)
		return target.CloneNode()

	override def ProcessStaticallyTypedAssignment(node as BinaryExpression):
		TryToResolveAmbiguousAssignment(node)
		ApplyImplicitArrayConversion(node)
		if ValidateAssignment(node):
			BindExpressionType(node, GetExpressionType(node.Right))
		else:
			Error(node)

	def ApplyImplicitArrayConversion(node as BinaryExpression):
		leftType = GetExpressionType(node.Left)
		return unless leftType.IsArray

		rightType = GetExpressionType(node.Right)
		return unless rightType is UnityScriptLangArray()

		node.Right = CodeBuilder.CreateCast(leftType,
						CodeBuilder.CreateMethodInvocation(
							node.Right,
							ResolveMethod(rightType, "ToBuiltin"),
							CodeBuilder.CreateTypeofExpression(leftType.ElementType)))

	def UnityScriptLangArray():
		return TypeSystemServices.Map(UnityScript.Lang.Array)

	override def OnForStatement(node as ForStatement):
		assert 1 == len(node.Declarations)
		Visit(node.Iterator)
		if NeedsUpdateableIteration(node):
			ProcessUpdateableIteration(node)
		else:
			ProcessNormalIteration(node)

	def ProcessNormalIteration(node as ForStatement):
		node.Iterator = ProcessIterator(node.Iterator, node.Declarations)
		VisitForStatementBlock(node)

	def ProcessUpdateableIteration(node as ForStatement):
		originalIterator = node.Iterator
		newIterator = CodeBuilder.CreateMethodInvocation(_UnityRuntimeServices_GetEnumerator, node.Iterator)
		newIterator.LexicalInfo = LexicalInfo(node.Iterator.LexicalInfo)
		node.Iterator = newIterator
		ProcessDeclarationForIterator(node.Declarations[0], GetEnumeratorItemType(GetExpressionType(originalIterator)))
		VisitForStatementBlock(node)
		TransformIteration(node)

	def TransformIteration(node as ForStatement):
		iterator = CodeBuilder.DeclareLocal(
						_currentMethod.Method,
						_context.GetUniqueName("iterator"),
						TypeSystemServices.IEnumeratorType)
		iterator.IsUsed = true
		body = Block(node.LexicalInfo)
		body.Add(
			CodeBuilder.CreateAssignment(
				node.LexicalInfo,
				CodeBuilder.CreateReference(iterator),
				node.Iterator))

		// while __iterator.MoveNext():
		ws = WhileStatement(node.LexicalInfo)
		ws.Condition = CodeBuilder.CreateMethodInvocation(
						CodeBuilder.CreateReference(iterator),
						IEnumerator_MoveNext)

		current = CodeBuilder.CreateMethodInvocation(
							CodeBuilder.CreateReference(iterator),
							IEnumerator_get_Current)

		//	item = __iterator.Current
		loopVariable as InternalLocal = TypeSystemServices.GetEntity(node.Declarations[0])
		ws.Block.Add(
				CodeBuilder.CreateAssignment(
					node.LexicalInfo,
					CodeBuilder.CreateReference(loopVariable),
					current))
		ws.Block.Add(node.Block)

		LoopVariableUpdater(self, _context, iterator, loopVariable).Visit(node)

		body.Add(ws)
		node.ParentNode.Replace(node, body)

	def NeedsUpdateableIteration(node as ForStatement):
		iteratorType = GetExpressionType(node.Iterator)
		if iteratorType.IsArray: return false
		return true

	class LoopVariableUpdater(DepthFirstVisitor):

		_parent as ProcessUnityScriptMethods
		_context as CompilerContext
		_iteratorVariable as IEntity
		_loopVariable as IEntity
		_found as bool

		def constructor(parent as ProcessUnityScriptMethods, context as CompilerContext, iteratorVariable as IEntity, loopVariable as IEntity):
			_parent = parent
			_context = context
			_iteratorVariable = iteratorVariable
			_loopVariable = loopVariable

		override def OnExpressionStatement(node as ExpressionStatement):
			_found = false
			Visit(node.Expression)
			if not _found: return

			parentNode = node.ParentNode

			codeBuilder = _context.CodeBuilder
			block = Block(node.LexicalInfo)
			block.Add(node)
			block.Add(
				codeBuilder.CreateMethodInvocation(
					_parent._UnityRuntimeServices_Update,
					codeBuilder.CreateReference(_iteratorVariable),
					codeBuilder.CreateReference(_loopVariable)))

			parentNode.Replace(node, block)

		override def OnReferenceExpression(node as ReferenceExpression):
			if _found: return

			referent = node.Entity
			_found = referent is _loopVariable

namespace UnityScript.Steps

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

import Boo.Lang.Environments

import UnityScript.Core
import UnityScript.Macros
import UnityScript.TypeSystem

class ProcessUnityScriptMethods(ProcessMethodBodiesWithDuckTyping):

	deferred IEnumerable_GetEnumerator = Types.IEnumerable.GetMethod("GetEnumerator");

	deferred IEnumerator_MoveNext = Types.IEnumerator.GetMethod("MoveNext");

	deferred IEnumerator_get_Current = Types.IEnumerator.GetProperty("Current").GetGetMethod();

	deferred _StartCoroutine = NameResolutionService.ResolveMethod(UnityScriptTypeSystem.ScriptBaseType, "StartCoroutine_Auto")

	deferred _UnityRuntimeServices_GetEnumerator = ResolveUnityRuntimeMethod("GetEnumerator")

	deferred _UnityRuntimeServices_Update = ResolveUnityRuntimeMethod("Update")

	deferred _UnityRuntimeServices_GetTypeOf = ResolveUnityRuntimeMethod("GetTypeOf")

	_implicit = false

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

		// don't transform
		//     foo == null
		// into:
		//     foo is null
		// but into:
		//     foo.Equals(null)
		self.OptimizeNullComparisons = false

	def ResolveUnityRuntimeMethod(name as string):
		return NameResolutionService.ResolveMethod(UnityRuntimeServicesType, name)

	def ResolveUnityRuntimeField(name as string):
		return NameResolutionService.ResolveField(UnityRuntimeServicesType, name)

	deferred UnityRuntimeServicesType = TypeSystemServices.Map(UnityScript.Lang.UnityRuntimeServices)

	UnityScriptTypeSystem as UnityScript.TypeSystem.UnityScriptTypeSystem:
		get: return self.TypeSystemServices

	UnityScriptParameters as UnityScript.UnityScriptCompilerParameters:
		get: return _context.Parameters

	override def GetGeneratorReturnType(generator as InternalMethod):
		return TypeSystemServices.IEnumeratorType

	override protected def MemberNotFound(node as MemberReferenceExpression, ns as INamespace):
		if Strict:
			super(node, ns)
		else:
			BindQuack(node)

	override protected def LocalToReuseFor(d as Declaration):
		if DeclarationAnnotations.ShouldForceNewVariableFor(d):
			AssertUniqueLocal(d)
			return null
		return super(d)

	override def OnModule(module as Module):
		preserving _activeModule, Parameters.Strict, _implicit:
			EnterModuleContext(module)
			super(module)

	override def VisitMemberPreservingContext(node as TypeMember):

		module = node.EnclosingModule
		if module is _activeModule:
			super(node)
			return

		preserving _activeModule, Parameters.Strict, _implicit:
			EnterModuleContext(module)
			super(node)

	private def EnterModuleContext(module as Module):
		_activeModule = module
		Parameters.Strict = Pragmas.IsEnabledOn(module, Pragmas.Strict)
		_implicit = Pragmas.IsEnabledOn(module, Pragmas.Implicit)
		if Pragmas.IsDisabledOn(module, Pragmas.Downcast):
			Parameters.DisableWarning(ImplicitDowncast)
		else:
			Parameters.EnableWarning(ImplicitDowncast)

	ImplicitDowncast:
		get: return Boo.Lang.Compiler.CompilerWarningFactory.Codes.ImplicitDowncast

	_activeModule as Module

	Strict:
		get: return Parameters.Strict

	override def OnMethod(node as Method):
		super(node)
		CheckForEmptyCoroutine(node)
		return if Parameters.OutputType == CompilerOutputType.Library
		CheckEntryPoint(node)

	def CheckForEmptyCoroutine(node as Method):
		if not IsEmptyCoroutine(node):
			return
		node.Body.Add([| return $(EmptyEnumeratorReference) |])

	def IsEmptyCoroutine(node as Method):
		entity as InternalMethod = GetEntity(node)
		return entity.ReturnType is GetGeneratorReturnType(entity) and HasNeitherReturnNorYield(node)

	deferred EmptyEnumeratorReference = CodeBuilder.CreateMemberReference(ResolveUnityRuntimeField("EmptyEnumerator"))

	def CheckEntryPoint(node as Method):
		if not node.IsStatic: return
		if not node.IsPublic: return
		if node.Name != "Main": return
		if GetType(node.ReturnType) is not TypeSystemServices.VoidType: return

		ContextAnnotations.SetEntryPoint(_context, node)

	override def ProcessAutoLocalDeclaration(node as BinaryExpression, reference as ReferenceExpression):
		if (Strict and not _implicit) and not IsCompilerGenerated(reference):
			EmitUnknownIdentifierError(reference, reference.Name)
		else:
			super(node, reference)

	def IsCompilerGenerated(reference as ReferenceExpression):
		return reference.Name.Contains('$')

	override protected def ProcessBuiltinInvocation(node as MethodInvocationExpression, function as BuiltinFunction):
		if function is UnityScriptTypeSystem.UnityScriptEval:
			EvalAnnotation.Mark(_currentMethod.Method)
			BindExpressionType(node, TypeSystemServices.ObjectType)
			return
		if function is UnityScriptTypeSystem.UnityScriptTypeof:
			ProcessTypeofBuiltin(node);
			return
		super(node, function)

	private def ProcessTypeofBuiltin(node as MethodInvocationExpression):
		if node.Arguments.Count != 1:
			Error(node, CompilerError("UCE0001", node.Target.LexicalInfo, "'typeof' takes a single argument.", null))
			return

		type = node.Arguments[0].Entity as IType
		if type is not null:
			node.ParentNode.Replace(node, CodeBuilder.CreateTypeofExpression(type))
			return

		node.Target = CodeBuilder.CreateReference(_UnityRuntimeServices_GetTypeOf)
		BindExpressionType(node, TypeSystemServices.TypeType)

	override protected def ProcessMethodInvocation(node as MethodInvocationExpression, method as IMethod):
	"""
	Automatically detects coroutine invocations in assignments and as standalone
	expressions and generates StartCoroutine invocations.
	"""
		super(node, method)

		if not IsPossibleStartCoroutineInvocation(node):
			return

		if method.IsStatic: return

		tss = self.UnityScriptTypeSystem
		if not tss.IsScriptType(method.DeclaringType): return
		if not tss.IsGenerator(method): return

		parentNode = node.ParentNode
		parentNode.Replace(
			node,
			CodeBuilder.CreateMethodInvocation(
				TargetForStartCoroutineInvocation(node, method),
				_StartCoroutine,
				node))

	def TargetForStartCoroutineInvocation(node as MethodInvocationExpression, method as IMethod):
		target = cast(MemberReferenceExpression, node.Target).Target
		if target isa SuperLiteralExpression: // super becomes self for coroutine invocation
			return CodeBuilder.CreateSelfReference(target.LexicalInfo, method.DeclaringType)
		return target.CloneNode()

	override def ProcessStaticallyTypedAssignment(node as BinaryExpression):
		TryToResolveAmbiguousAssignment(node)
		ApplyImplicitArrayConversion(node)
		if ValidateAssignment(node):
			BindExpressionType(node, GetExpressionType(node.Right))
		else:
			Error(node)

	def ApplyImplicitArrayConversion(node as BinaryExpression):
		leftType = GetExpressionType(node.Left)
		return unless leftType.IsArray

		rightType = GetExpressionType(node.Right)
		return unless rightType is UnityScriptLangArray()

		node.Right = CodeBuilder.CreateCast(leftType,
						CodeBuilder.CreateMethodInvocation(
							node.Right,
							ResolveMethod(rightType, "ToBuiltin"),
							CodeBuilder.CreateTypeofExpression(leftType.ElementType)))

	def UnityScriptLangArray():
		return TypeSystemServices.Map(UnityScript.Lang.Array)

	override def OnForStatement(node as ForStatement):
		assert 1 == len(node.Declarations)
		Visit(node.Iterator)
		if NeedsUpdateableIteration(node):
			ProcessUpdateableIteration(node)
		else:
			ProcessNormalIteration(node)

	def ProcessNormalIteration(node as ForStatement):
		node.Iterator = ProcessIterator(node.Iterator, node.Declarations)
		VisitForStatementBlock(node)

	def ProcessUpdateableIteration(node as ForStatement):
		originalIterator = node.Iterator
		newIterator = CodeBuilder.CreateMethodInvocation(_UnityRuntimeServices_GetEnumerator, node.Iterator)
		newIterator.LexicalInfo = LexicalInfo(node.Iterator.LexicalInfo)
		node.Iterator = newIterator
		ProcessDeclarationForIterator(node.Declarations[0], GetEnumeratorItemType(GetExpressionType(originalIterator)))
		VisitForStatementBlock(node)
		TransformIteration(node)

	def TransformIteration(node as ForStatement):
		iterator = CodeBuilder.DeclareLocal(
						_currentMethod.Method,
						_context.GetUniqueName("iterator"),
						TypeSystemServices.IEnumeratorType)
		iterator.IsUsed = true
		body = Block(node.LexicalInfo)
		body.Add(
			CodeBuilder.CreateAssignment(
				node.LexicalInfo,
				CodeBuilder.CreateReference(iterator),
				node.Iterator))

		// while __iterator.MoveNext():
		ws = WhileStatement(node.LexicalInfo)
		ws.Condition = CodeBuilder.CreateMethodInvocation(
						CodeBuilder.CreateReference(iterator),
						IEnumerator_MoveNext)

		current = CodeBuilder.CreateMethodInvocation(
							CodeBuilder.CreateReference(iterator),
							IEnumerator_get_Current)

		//	item = __iterator.Current
		loopVariable as InternalLocal = TypeSystemServices.GetEntity(node.Declarations[0])
		ws.Block.Add(
				CodeBuilder.CreateAssignment(
					node.LexicalInfo,
					CodeBuilder.CreateReference(loopVariable),
					current))
		ws.Block.Add(node.Block)

		LoopVariableUpdater(self, _context, iterator, loopVariable).Visit(node)

		body.Add(ws)
		node.ParentNode.Replace(node, body)

	def NeedsUpdateableIteration(node as ForStatement):
		iteratorType = GetExpressionType(node.Iterator)
		if iteratorType.IsArray: return false
		return true

	class LoopVariableUpdater(DepthFirstVisitor):

		_parent as ProcessUnityScriptMethods
		_context as CompilerContext
		_iteratorVariable as IEntity
		_loopVariable as IEntity
		_found as bool

		def constructor(parent as ProcessUnityScriptMethods, context as CompilerContext, iteratorVariable as IEntity, loopVariable as IEntity):
			_parent = parent
			_context = context
			_iteratorVariable = iteratorVariable
			_loopVariable = loopVariable

		override def OnExpressionStatement(node as ExpressionStatement):
			_found = false
			Visit(node.Expression)
			if not _found: return

			parentNode = node.ParentNode

			codeBuilder = _context.CodeBuilder
			block = Block(node.LexicalInfo)
			block.Add(node)
			block.Add(
				codeBuilder.CreateMethodInvocation(
					_parent._UnityRuntimeServices_Update,
					codeBuilder.CreateReference(_iteratorVariable),
					codeBuilder.CreateReference(_loopVariable)))

			parentNode.Replace(node, block)

		override def OnReferenceExpression(node as ReferenceExpression):
			if _found: return

			referent = node.Entity
			_found = referent is _loopVariable


Boo if elif else 사용 구문 보정.

Boo if elif else 사용 구문 보정.

import UnityEngine

class TextVisibleControl (MonoBehaviour):
	public objText_Editor as GameObject
	public objText_Window as GameObject
	public objText_iOS as GameObject
	public objText_Android as GameObject

	def Start ():
		if Application.isEditor:
			objText_Editor.SetActive(true)

		// No 'switch' in Boo
		if RuntimePlatform.Android == Application.platform:
    		objText_Android.SetActive(true) // Will Show On Android Build
    	if RuntimePlatform.IPhonePlayer == Application.platform:
    		objText_iOS.SetActive(true) // Will Show On iOS Build
    	else :
    		objText_Window.SetActive(true) // Will Show On exe Build

	def Update ():
		pass

import UnityEngine

class TextVisibleControl (MonoBehaviour):
	public objText_Editor as GameObject
	public objText_Window as GameObject
	public objText_iOS as GameObject
	public objText_Android as GameObject

	def Start ():
		if Application.isEditor:
			objText_Editor.SetActive(true)

		// No 'switch' in Boo
		if RuntimePlatform.Android == Application.platform:
    		objText_Android.SetActive(true) // Will Show On Android Build
    	if RuntimePlatform.IPhonePlayer == Application.platform:
    		objText_iOS.SetActive(true) // Will Show On iOS Build
    	else
    		objText_Window.SetActive(true) // Will Show On exe Build

	def Update ():
		pass