use comma to separate parameters

use comma to separate parameters

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

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

"""
Interactive Forms-based Console
"""

namespace booish.gui

import System
import System.Text
import System.IO
import System.Reflection
import booish
import System.Windows.Forms
import System.Drawing
import System.Runtime.InteropServices

class KeyChar:

	public static Enter = chr(Keys.Enter)

	public static Dot = chr('.')

	public static Back = chr(Keys.Back)

	public static Esc = chr(Keys.Escape)

class CompletionBox(ListBox):

	_interpreter as InteractiveInterpreter

	def constructor(interpreter):
		self._interpreter = interpreter
		self.Visible = false

	def Fill(type as System.Type):

		members = []
		for member in type.GetMembers():
			if IsValidSuggestion(member):
				if member isa FieldInfo:
					field = member as FieldInfo
					members.Add("${field.Name} as ${getBooTypeName(field.FieldType)}")
				elif member isa PropertyInfo:
					property = member as PropertyInfo
					getField = ("", "getter,")[property.CanRead]
					setField = ("", "setter")[property.CanWrite]
					members.Add("${member.Name} as ${getBooTypeName(property.PropertyType)} (${getField} ${setField})")
				elif member isa MethodInfo:
					method = member as MethodInfo
					params = join("${param.Name} as ${getBooTypeName(param.ParameterType)}"
										for param in method.GetParameters(), ", ")
					returnValue = ""
					returnValue = " as ${getBooTypeName(method.ReturnType)}" if method.ReturnType is not void
					members.Add("${member.Name}(${params})${returnValue}")

		Items.Clear()
		for item in members.Sort():
			Items.Add(item)

	def Show(pos as Point):
		self.Location = pos
		self.Visible = true
		self.Focus()

	private def IsValidSuggestion(member as MemberInfo):
		return false if not IsPublic(member)
		method = member as MethodBase
		if method is not null:
			return false if method.IsSpecialName
		return true

	private def IsPublic(member as MemberInfo):
		if MemberTypes.Method == member.MemberType:
			return cast(MethodBase, member).IsPublic
		if MemberTypes.Property == member.MemberType:
			p as PropertyInfo = member
			return (p.GetGetMethod() or p.GetSetMethod()).IsPublic
		if MemberTypes.Field == member.MemberType:
			return cast(FieldInfo, member).IsPublic
		return true

	override def OnLostFocus(args as EventArgs):
		self.Visible = false
		super(args)

	override def OnKeyPress(args as KeyPressEventArgs):
		finish = def ():
			Parent.Focus()
			args.Handled = true

		if args.KeyChar in KeyChar.Esc, KeyChar.Back:
			finish()
		elif KeyChar.Enter == args.KeyChar:
			cast(TextBox, Parent).SelectedText = /\w+/.Match(SelectedItem).Groups[0].Value
			finish()
		else:
			super(args)

	private def getBooTypeName(type as System.Type):
		return _interpreter.getBooTypeName(type)

class PromptBox(TextBox):

	enum InputState:
		SingleLine = 0
		Block = 1

	_state = InputState.SingleLine

	_block = System.IO.StringWriter()

	_console = StringWriter()

	[getter(Interpreter)]
	_interpreter = InteractiveInterpreter(
								RememberLastValue: true,
								Print: print)

	_completionBox = CompletionBox(_interpreter)

	def constructor():
		self.Dock = DockStyle.Fill
		self.Multiline = true
		self.AcceptsTab = true
		self.ScrollBars = ScrollBars.Vertical
		_interpreter.References.Add(typeof(TextBox).Assembly)
		_interpreter.References.Add(typeof(Font).Assembly)
		_interpreter.SetValue("inspect", inspect)

		Controls.Add(_completionBox)

		prompt()

	CaretPos:
		get:
			p as Point
			assert booish.gui.externs.User32.GetCaretPos(p)
			return p

	def GetCurrentLine():
		line = Lines[-1][4:]
		print("")
		return line

	def SingleLineInputState():
		code = GetCurrentLine()

		if code[-1:] in ":", "\\":
			_state = InputState.Block
			_block.GetStringBuilder().Length = 0
			_block.WriteLine(code)
		else:
			Eval(code)

	def BlockInputState():
		code = GetCurrentLine()
		if 0 == len(code):
			Eval(_block.ToString())
			_state = InputState.SingleLine
		else:
			_block.WriteLine(code)

	override def OnKeyPress(args as KeyPressEventArgs):
		if KeyChar.Enter == args.KeyChar:
			try:
				(SingleLineInputState, BlockInputState)[_state]()
			except x:
				print(x)
			prompt()
			args.Handled = true
		elif KeyChar.Dot == args.KeyChar:
			self.SelectedText = "."
			DotComplete()
			args.Handled = true

		super(args)

	override def OnResize(args as EventArgs):
		_completionBox.Size = System.Drawing.Size(Width*.65, Height*.4)
		super(args)

	def DotComplete():
		m = /((\w|\.)+)\.$/.Match(self.Lines[-1])
		if m.Success:
			// only simple identifiers right now
			expression = m.Groups[1].Value
			if /^\w+$/.IsMatch(expression):
				type = _interpreter.Lookup(expression)
				if type is not null:
					_completionBox.Fill(type)
					_completionBox.Show(CaretPos)

	def Eval(code as string):
		saved = Console.Out
		Console.SetOut(_console)
		try:
			_interpreter.LoopEval(code)
		ensure:
			FlushConsole()
			Console.SetOut(saved)

	def FlushConsole():
		AppendText(_console.ToString())
		_console.GetStringBuilder().Length = 0

	def print(msg):
		AppendText("${msg}\r\n")

	def inspect([required] obj):
		f = Form(Text: "Object Inspector [${obj}]")
		f.Controls.Add(PropertyGrid(
							Dock: DockStyle.Fill,
							SelectedObject: obj,
							Font: Font))
		f.Show()
		return f

	def prompt():
		AppendText((">>> ", "... ")[_state])

def chr(key as Keys):
	return cast(IConvertible, cast(int, key)).ToChar(null)

def chr(s as string):
	assert len(s) == 1
	return s[0]

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

"""
Interactive Forms-based Console
"""

namespace booish.gui

import System
import System.Text
import System.IO
import System.Reflection
import booish
import System.Windows.Forms
import System.Drawing
import System.Runtime.InteropServices

class KeyChar:

	public static Enter = chr(Keys.Enter)

	public static Dot = chr('.')

	public static Back = chr(Keys.Back)

	public static Esc = chr(Keys.Escape)

class CompletionBox(ListBox):

	_interpreter as InteractiveInterpreter

	def constructor(interpreter):
		self._interpreter = interpreter
		self.Visible = false

	def Fill(type as System.Type):

		members = []
		for member in type.GetMembers():
			if IsValidSuggestion(member):
				if member isa FieldInfo:
					field = member as FieldInfo
					members.Add("${field.Name} as ${getBooTypeName(field.FieldType)}")
				elif member isa PropertyInfo:
					property = member as PropertyInfo
					getField = ("", "getter,")[property.CanRead]
					setField = ("", "setter")[property.CanWrite]
					members.Add("${member.Name} as ${getBooTypeName(property.PropertyType)} (${getField} ${setField})")
				elif member isa MethodInfo:
					method = member as MethodInfo
					params = join("${param.Name} as ${getBooTypeName(param.ParameterType)}"
										for param in method.GetParameters())
					returnValue = ""
					returnValue = " as ${getBooTypeName(method.ReturnType)}" if method.ReturnType is not void
					members.Add("${member.Name}(${params})${returnValue}")

		Items.Clear()
		for item in members.Sort():
			Items.Add(item)

	def Show(pos as Point):
		self.Location = pos
		self.Visible = true
		self.Focus()

	private def IsValidSuggestion(member as MemberInfo):
		return false if not IsPublic(member)
		method = member as MethodBase
		if method is not null:
			return false if method.IsSpecialName
		return true

	private def IsPublic(member as MemberInfo):
		if MemberTypes.Method == member.MemberType:
			return cast(MethodBase, member).IsPublic
		if MemberTypes.Property == member.MemberType:
			p as PropertyInfo = member
			return (p.GetGetMethod() or p.GetSetMethod()).IsPublic
		if MemberTypes.Field == member.MemberType:
			return cast(FieldInfo, member).IsPublic
		return true

	override def OnLostFocus(args as EventArgs):
		self.Visible = false
		super(args)

	override def OnKeyPress(args as KeyPressEventArgs):
		finish = def ():
			Parent.Focus()
			args.Handled = true

		if args.KeyChar in KeyChar.Esc, KeyChar.Back:
			finish()
		elif KeyChar.Enter == args.KeyChar:
			cast(TextBox, Parent).SelectedText = /\w+/.Match(SelectedItem).Groups[0].Value
			finish()
		else:
			super(args)

	private def getBooTypeName(type as System.Type):
		return _interpreter.getBooTypeName(type)

class PromptBox(TextBox):

	enum InputState:
		SingleLine = 0
		Block = 1

	_state = InputState.SingleLine

	_block = System.IO.StringWriter()

	_console = StringWriter()

	[getter(Interpreter)]
	_interpreter = InteractiveInterpreter(
								RememberLastValue: true,
								Print: print)

	_completionBox = CompletionBox(_interpreter)

	def constructor():
		self.Dock = DockStyle.Fill
		self.Multiline = true
		self.AcceptsTab = true
		self.ScrollBars = ScrollBars.Vertical
		_interpreter.References.Add(typeof(TextBox).Assembly)
		_interpreter.References.Add(typeof(Font).Assembly)
		_interpreter.SetValue("inspect", inspect)

		Controls.Add(_completionBox)

		prompt()

	CaretPos:
		get:
			p as Point
			assert booish.gui.externs.User32.GetCaretPos(p)
			return p

	def GetCurrentLine():
		line = Lines[-1][4:]
		print("")
		return line

	def SingleLineInputState():
		code = GetCurrentLine()

		if code[-1:] in ":", "\\":
			_state = InputState.Block
			_block.GetStringBuilder().Length = 0
			_block.WriteLine(code)
		else:
			Eval(code)

	def BlockInputState():
		code = GetCurrentLine()
		if 0 == len(code):
			Eval(_block.ToString())
			_state = InputState.SingleLine
		else:
			_block.WriteLine(code)

	override def OnKeyPress(args as KeyPressEventArgs):
		if KeyChar.Enter == args.KeyChar:
			try:
				(SingleLineInputState, BlockInputState)[_state]()
			except x:
				print(x)
			prompt()
			args.Handled = true
		elif KeyChar.Dot == args.KeyChar:
			self.SelectedText = "."
			DotComplete()
			args.Handled = true

		super(args)

	override def OnResize(args as EventArgs):
		_completionBox.Size = System.Drawing.Size(Width*.65, Height*.4)
		super(args)

	def DotComplete():
		m = /((\w|\.)+)\.$/.Match(self.Lines[-1])
		if m.Success:
			// only simple identifiers right now
			expression = m.Groups[1].Value
			if /^\w+$/.IsMatch(expression):
				type = _interpreter.Lookup(expression)
				if type is not null:
					_completionBox.Fill(type)
					_completionBox.Show(CaretPos)

	def Eval(code as string):
		saved = Console.Out
		Console.SetOut(_console)
		try:
			_interpreter.LoopEval(code)
		ensure:
			FlushConsole()
			Console.SetOut(saved)

	def FlushConsole():
		AppendText(_console.ToString())
		_console.GetStringBuilder().Length = 0

	def print(msg):
		AppendText("${msg}\r\n")

	def inspect([required] obj):
		f = Form(Text: "Object Inspector [${obj}]")
		f.Controls.Add(PropertyGrid(
							Dock: DockStyle.Fill,
							SelectedObject: obj,
							Font: Font))
		f.Show()
		return f

	def prompt():
		AppendText((">>> ", "... ")[_state])

def chr(key as Keys):
	return cast(IConvertible, cast(int, key)).ToChar(null)

def chr(s as string):
	assert len(s) == 1
	return s[0]


more complete test case

more complete test case

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

"""
QuackSet("", ("param1",), "value1")
QuackGet("", ("param2",))
QuackSet("", ("param3", "param4"), "value2")
QuackGet("", ("param5", "param6"))
QuackSet("Item", ("param7",), "value3")
QuackGet("Item", ("param8",))
QuackSet("Item", ("param9", "param10"), "value4")
QuackGet("Item", ("param11", "param12"))
"""
import System

class Expando(IQuackFu):

	def QuackSet(name as string, parameters as (object), value):
		traceMethod("QuackSet", name, parameters, value)

	def QuackGet(name as string, parameters as (object)):
		traceMethod("QuackGet", name, parameters)

	def QuackInvoke(name as string, args as (object)) as object:
		pass

def traceMethod(name as string, *params):
	print "${name}(${csv(params)})"

def repr(o) as string:
	if o isa string: return "\"${o}\""
	return arrayRepr(o)

def arrayRepr(a as (object)):
	if len(a) == 1: return "(${repr(a[0])},)"
	return "(${csv(a)})"

def csv(collection):
	return join(repr(item) for item in collection, ', ')

e as duck = Expando()
e["param1"] = "value1"
value = e["param2"]

e["param3", "param4"] = "value2"
value = e["param5", "param6"]

e.Item["param7"] = "value3"
value = e.Item["param8"]

e.Item["param9", "param10"] = "value4"
value = e.Item["param11", "param12"]

"""
QuackSet("", ("param1",), "value1")
QuackGet("", ("param2",))
QuackSet("", ("param3", "param4"), "value2")
QuackGet("", ("param5", "param6"))
"""
import System

class Expando(IQuackFu):

	def QuackSet(name as string, parameters as (object), value):
		traceMethod("QuackSet", name, parameters, value)

	def QuackGet(name as string, parameters as (object)):
		traceMethod("QuackGet", name, parameters)

	def QuackInvoke(name as string, args as (object)) as object:
		pass

def traceMethod(name as string, *params):
	print "${name}(${csv(params)})"

def repr(o) as string:
	if o isa string: return "\"${o}\""
	return arrayRepr(o)

def arrayRepr(a as (object)):
	if len(a) == 1: return "(${repr(a[0])},)"
	return "(${csv(a)})"

def csv(collection):
	return join(repr(item) for item in collection, ', ')

e as duck = Expando()
e["param1"] = "value1"
value = e["param2"]

e["param3", "param4"] = "value2"
value = e["param5", "param6"]


remove name provider work around

remove name provider work around

namespace BooJs.Compiler.Visitors

import Boo.Lang.Environments
import Boo.Lang.Compiler.Ast
import Boo.Lang.Compiler.TypeSystem
import Boo.Lang.PatternMatching

import BooJs.Compiler(UniqueNameProvider)
import BooJs.Compiler.Utils
import BooJs.Compiler.TypeSystem(RuntimeMethodCache)
import BooJs.Compiler.Mozilla as Moz

class MozillaAstVisitor(FastDepthFirstVisitor):
"""
Transforms a Boo AST into a Mozilla AST
"""
    # Holds the latest conversion from a Boo node to a Moz node
    _return as Moz.Node

    protected def Return(node as Moz.Node):
        _return = node

    protected def Apply(node as Node) as Moz.Node:
        _return = null
        Visit node
        return _return

    protected def loc(node as Node):
        obj = Moz.SourceLocation()
        obj.source = node.LexicalInfo.FileName
        obj.start = Moz.SourceLocation.Position(line: node.LexicalInfo.Line, column: node.LexicalInfo.Column-1)

        # Compute an approximate end position if not available
        if node.EndSourceLocation and node.EndSourceLocation.Line >= 0:
            line, column = (node.EndSourceLocation.Line, node.EndSourceLocation.Column-1)
        else:
            parts = node.ToCodeString().Split(char('\n'))
            line, column = (node.LexicalInfo.Line, node.LexicalInfo.Column-1)
            if len(parts) > 1:
                line += len(parts) - 1
                column = 0
            column += len(parts[-1])

        obj.end = Moz.SourceLocation.Position(line: line, column: column)

        return obj

    def Run(node as Node) as Moz.Node:
        Visit node
        return _return

    def OnBoolLiteralExpression(node as BoolLiteralExpression):
        Return Moz.Literal(loc: loc(node), value: node.Value)

    def OnNullLiteralExpression(node as NullLiteralExpression):
        Return Moz.Literal(loc: loc(node), value: null)

    def OnIntegerLiteralExpression(node as IntegerLiteralExpression):
        n as Moz.IExpression
        if node.Value < 0:
            n = Moz.Literal(loc: loc(node), value: -node.Value)
            n = Moz.UnaryExpression(loc: loc(node), operator: '-', argument: n)
         else:
            n = Moz.Literal(loc: loc(node), value: node.Value)

        Return n

    def OnDoubleLiteralExpression(node as DoubleLiteralExpression):
        n as Moz.IExpression
        if node.Value < 0:
            n = Moz.Literal(loc: loc(node), value: -node.Value)
            n = Moz.UnaryExpression(loc: loc(node), operator: '-', argument: n)
         else:
            n = Moz.Literal(loc: loc(node), value: node.Value)

        Return n

    def OnStringLiteralExpression(node as StringLiteralExpression):
        Return Moz.Literal(loc: loc(node), value: node.Value)

    def OnRELiteralExpression(node as RELiteralExpression):
        Return Moz.Literal(loc: loc(node), value: node.Value, regexp: true)

    def OnSelfLiteralExpression(node as SelfLiteralExpression):
        Return Moz.ThisExpression(loc: loc(node))

    def OnListLiteralExpression(node as ListLiteralExpression):
        n = Moz.ArrayExpression(loc: loc(node))
        for itm in node.Items:
            exp = Apply(itm)
            n.elements.Add(exp)
        Return n

    def OnHashLiteralExpression(node as HashLiteralExpression):
        n = Moz.ObjectExpression(loc: loc(node))
        for pair in node.Items:
            prop = Moz.ObjectExpression.Prop(key: Apply(pair.First), value: Apply(pair.Second))
            n.properties.Add(prop)
        Return n

    def OnEnumDefinition(node as EnumDefinition):
        # Create an object with the enum values
        o = Moz.ObjectExpression(loc: loc(node))
        for member as EnumMember in node.Members:
            assert member.Initializer != null, 'Enum definition without an initializer value!'
            o.properties.Add(
                Moz.ObjectExpression.Prop(
                    key: Moz.Literal(value: member.Name),
                    value: Apply(member.Initializer)
                )
            )

        # Assign the object to a variable
        v = Moz.VariableDeclarator(loc: loc(node))
        v.id = Moz.Identifier(loc: loc(node), name: node.FullName)
        v.init = o
        n = Moz.VariableDeclaration(loc: loc(node), kind: 'var')
        n.declarations.Add(v)

        Return n

    def OnModule(node as Module):
        n = Moz.Program(loc: loc(node))

        deps = List of Moz.IExpression() { Moz.Literal('exports'), Moz.Literal('Boo') }
        refs = List of Moz.IPattern() { Moz.Identifier('exports'), Moz.Identifier('Boo') }

        # Get namespace mapping annotations
        mapping as Hash = node['nsmapping']
        asmrefs as Hash = node['nsasmrefs']

        for ns in mapping.Keys:
            if ns in asmrefs:
                context().TraceInfo('Namespace mapping {0} => {1}', ns, mapping[ns] + ':' + asmrefs[ns])
                deps.Add( Moz.Literal(ns + ':' + asmrefs[ns]) )
            else:
                context().TraceInfo('Namespace mapping {0} => {1}', ns, mapping[ns])
                deps.Add( Moz.Literal(ns) )
            refs.Add( Moz.Identifier(mapping[ns]) )

        # Use Boo.define to bootstrap the module contents
        call = Moz.CallExpression()
        call.callee = Moz.MemberExpression(
            object: Moz.Identifier(name: 'Boo'),
            property: Moz.Identifier(name: 'define')
        )
        call.arguments.Add(Moz.Literal(value: (node.Namespace.ToString() if node.Namespace else '')))
        call.arguments.Add(Moz.ArrayExpression(elements: deps))
        fn = Moz.FunctionExpression(params: refs, body: Moz.BlockStatement())
        call.arguments.Add(fn)
        n.body.Add(Moz.ExpressionStatement(call))

        st as Moz.IStatement
        for member in node.Members:
            st = Apply(member)
            if st is null:
                continue

            if st isa Moz.BlockStatement:
                fn.body.body += (st as Moz.BlockStatement).body
            else:
                fn.body.body.Add(st)

        # Export public symbols
        # js: exports.symbol = symbol
        members = TypeMemberCollection()
        for member in node.Members:
            if member.IsSynthetic and member.Name.EndsWith('Module'):
                members.AddRange((member as ClassDefinition).Members)
            else:
                members.Add(member)

        for member in members:
            continue unless member.IsPublic and not member.IsInternal

            expr = Moz.BinaryExpression(
                left: Moz.MemberExpression(
                    object: Moz.Identifier('exports'),
                    property: Moz.Identifier(member.Name)
                ),
                operator: '=',
                right: Moz.Identifier(member.Name)
            )

            st = Moz.ExpressionStatement(expr)
            fn.body.body.Add(st)

        if not node.Globals.IsEmpty:
            rcall = Moz.CallExpression()
            rcall.callee = Moz.MemberExpression(
                object: Moz.Identifier(name: 'Boo'),
                property: Moz.Identifier(name: 'require')
            )
            # Clone the deps list replacing 'exports' by the namespace
            deps = deps.GetRange(0, len(deps))
            deps[0] = Moz.Literal(node.Namespace or '')
            rcall.arguments.Add(Moz.ArrayExpression(elements: deps))
            fn = Moz.FunctionExpression(params: refs, body: Moz.BlockStatement())
            rcall.arguments.Add(fn)
            n.body.Add(Moz.ExpressionStatement(rcall))

            for global in node.Globals.Statements:
                st = Apply(global)
                fn.body.body.Add(st)

        #n['module'] = node
        Return n

    def OnClassDefinition(node as ClassDefinition):
        n = Moz.BlockStatement()

        # Detect module class and output its members directly
        if node.IsSynthetic and node.IsFinal and node.Name.EndsWith('Module'):
            members = [m for m in node.Members if m.NodeType != NodeType.Constructor]
            st as Moz.IStatement
            for member in members:
                match Apply(member):
                    case null:
                        continue
                    case bs=Moz.BlockStatement():
                        n.body += bs.body
                    case _:
                        n.body.Add(_)

            Return n
            return

        # TODO: Handle visibility (internals should not be exposed)

        # Bar = (function (__super__) {
        #     // Constructor
        #     function Bar() {
        #         // Allow the intantiation without using the `new` operator (TODO: do we really want this?)
        #         if (!(this instanceof Bar)) {
        #             return new (Function.prototype.bind.apply(Bar, [this].concat(Array.prototype.slice.call(arguments)) ));
        #         }
        #
        #         // Initialize fields (TODO: can this be moved to the prototype? at least the non private ones)
        #         this.field = '';
        #
        #         // Bind instance methods (TODO: Support annotation to avoid binding)
        #         this.foo = Boo.bind(this.foo, this);
        #
        #         // Constructor code (TODO: how to handle overloads?)
        #         // ...
        #     }
        #
        #     // Setup inheritance
        #     Bar.prototype = Boo.create(__super__.prototype);
        #     Bar.prototype.constructor = Bar;
        #
        #     // Reflect metadata
        #     Bar.prototype.$boo$interfaces = [IEnumerator];
        #
        #     // Instance members
        #     Bar.prototype.foo = function Bar$foo() {
        #         __super__.prototype.foo.call(this);
        #     };
        #
        #
        #     // Inherit "static" properties (TODO: Is this actually needed?)
        #     for (var prop in __super__) {
        #         if (__super__.hasOwnProperty(prop)) {
        #             Bar[prop] = __super__[prop];
        #         }
        #     }
        #
        #     // Static members
        #     Bar.bars = function Bar$bars() {};
        #
        #     // Static constructor
        #     // ...
        #
        #     return Bar;
        # })(Foo);

        block = Moz.BlockStatement()

        # Constructor
        # js: function Bar() {}
        cons = Moz.FunctionDeclaration(loc: loc(node))
        cons.body = Moz.BlockStatement()
        cons.id = Moz.Identifier(node.Name)
        block.body.Add(cons)

        # Initialize fields
        # TODO: Shouldn't fields with primitive initializers be moved to the prototype?
        # TODO: Boo already moves field initialization to the constructors
        for f as Field in [m for m in node.Members if m.NodeType == NodeType.Field]:
            assign = Moz.AssignmentExpression(operator:'=')
            assign.left = Moz.Identifier(loc:loc(f), name:'this.' + f.Name)
            assign.right = Apply(f.Initializer)
            # TODO: Disabled until the basics are working
            #cons.body.body.Add(Moz.ExpressionStatement(assign))

        # Handle constructors
        members = [m for m in node.Members if m.NodeType == NodeType.Constructor]
        for c as Constructor in members:
            continue if not c
            # TODO: Disabled until the basics are working
            #cons.body.body.Add(Apply(c))

        # Static members
        # js: Bar.sbar = function Bar_sbar (arg) { }
        members = [m for m in node.Members if m.NodeType == NodeType.Method and m.IsStatic]
        for method as Method in members:
            assign = Moz.AssignmentExpression(operator:'=')
            assign.left = Moz.Identifier(name:node.Name + '.' + method.Name)
            fn = Apply(method) as Moz.FunctionDeclaration
            assign.right = Moz.FunctionExpression(
                loc: fn.loc,
                id: Moz.Identifier((node.Name + '.' + fn.id.name).Replace('.', '_')),
                params: fn.params,
                body: fn.body
            )
            block.body.Add(Moz.ExpressionStatement(assign))

        # Setup inheritance
        # js: Bar.prototype = Boo.create(__super__.prototype)
        assign = Moz.AssignmentExpression(operator:'=')
        assign.left = Moz.Identifier(name:node.Name + '.prototype')
        assign.right = Moz.CallExpression()
        (assign.right as Moz.CallExpression).callee = Moz.Identifier('Boo.create')
        (assign.right as Moz.CallExpression).arguments.Add(
            Moz.Identifier('__super__.prototype')
            # Moz.Identifier(
            #     ('Object' if typeSystem().IsSystemObject(node.BaseTypes.First.Entity)
            #         else node.BaseTypes.First.Entity.FullName) + '.prototype'
            # )
        )
        block.body.Add(Moz.ExpressionStatement(assign))

        # js: Bar.prototype.constructor = Bar
        assign = Moz.AssignmentExpression(operator:'=')
        assign.left = Moz.Identifier(name:node.Name + '.prototype.constructor')
        assign.right = Moz.Identifier(name:node.Name)
        block.body.Add(Moz.ExpressionStatement(assign))

        # Metadata
        # js: Bar.prototype.$boo$interfaces = [ IEnumerable ]
        interfaces = Moz.ArrayExpression()
        for t in node.BaseTypes:
            continue if t is node.BaseTypes.First
            continue if typeSystem().IsSystemObject(t.Entity)
            interfaces.elements.Add(
                Moz.Identifier(t.Entity.FullName)
            )
        assign = Moz.AssignmentExpression(operator:'=')
        assign.left = Moz.Identifier(name:node.Name + '.prototype.$boo$interfaces')
        assign.right = interfaces
        block.body.Add(Moz.ExpressionStatement(assign))

        # Instance members
        # js: Bar.prototype.bar = function Bar$bar (arg) { }
        members = [m for m in node.Members if m.NodeType == NodeType.Method and not m.IsStatic]
        for method as Method in members:
            assign = Moz.AssignmentExpression(operator:'=')
            assign.left = Moz.Identifier(name:node.Name + '.prototype.' + method.Name)
            fn = Apply(method) as Moz.FunctionDeclaration
            assign.right = Moz.FunctionExpression(
                loc: fn.loc,
                id: Moz.Identifier((node.Name + '.' + fn.id.name).Replace('.', '$')),
                params: fn.params,
                body: fn.body
            )
            block.body.Add(Moz.ExpressionStatement(assign))

        # Wrap everything in a self calling function and assign to a variable
        # js: Bar = (function(__super__){ ... return Bar; })(Object)
        block.body.Add(
            Moz.ReturnStatement(argument: Moz.Identifier(node.Name))
        )

        func = Moz.FunctionExpression(body:block)
        func.params = List of Moz.IPattern() { Moz.Identifier('__super__') }
        call = Moz.CallExpression(callee:func)
        call.arguments.Add(
            Moz.Identifier(
                ('Object' if typeSystem().IsSystemObject(node.BaseTypes.First.Entity)
                    else node.BaseTypes.First.Entity.FullName)
            )
        )
        decl = Moz.VariableDeclarator(loc:loc(node))
        decl.id = Moz.Identifier(node.Name)
        # decl.init = Moz.CallExpression(callee: Moz.FunctionExpression(body:block))
        decl.init = call
        decl_st = Moz.VariableDeclaration(loc:loc(node), kind:'var')
        decl_st.declarations.Add(decl)

        Return decl_st

    def OnField(node as Field):
        pass

    def OnConstructor(node as Constructor):
        OnMethod(node)
        return

        ifst = Moz.IfStatement()
        # TODO: Apply test based on arguments and constructor parameters
        ifst.test = Moz.Identifier(name:'true')

        ifst.consequent = Apply(node.Body)

        Return ifst

    def OnMethod(node as Method):
        n = Moz.FunctionDeclaration(loc: loc(node))
        n.id = Moz.Identifier(loc: loc(node), name: node.Name)
        for param in node.Parameters:
            p = Moz.Identifier(loc: loc(param), name: param.Name)
            n.params.Add(p)

        n.body = Apply(node.Body)
        Return n

    def OnDeclarationStatement(node as DeclarationStatement):
        # TODO: Generate type annotated comments
        v = Moz.VariableDeclarator(loc: loc(node))
        v.id = Moz.Identifier(name: node.Declaration.Name)
        v.init = Apply(node.Initializer)

        vd = Moz.VariableDeclaration(loc: loc(node), kind: 'var')
        vd.declarations.Add(v)
        Return vd

    def OnBlock(node as Block):
        b = Moz.BlockStatement(loc: loc(node))
        for st in node.Statements:
            mst = Apply(st) as Moz.IStatement
            if mst:
                b.body.Add(mst)

        Return b

    def OnExpressionStatement(node as ExpressionStatement):
        n = Moz.ExpressionStatement(loc: loc(node))
        n.expression = Apply(node.Expression)
        Return n

    def OnReturnStatement(node as ReturnStatement):
        n = Moz.ReturnStatement(loc: loc(node))
        n.argument = Apply(node.Expression)
        Return n

    def OnIfStatement(node as IfStatement):

        ifst = Moz.IfStatement(loc: loc(node))
        ifst.test = Apply(node.Condition)
        ifst.consequent = Apply(node.TrueBlock)
        if node.FalseBlock and not node.FalseBlock.IsEmpty:
            blk = node.FalseBlock
            if len(blk.Statements) == 1 and blk.FirstStatement isa IfStatement:
                ifst.alternate = Apply(blk.FirstStatement)
            else:
                ifst.alternate = Apply(blk)

        Return ifst

    def OnForStatement(node as ForStatement):
        # TODO: Optimize range(items.length) to cache the length value
        # TODO: Optimize range(f, t) and range(f, t, s)
        #       for (i=f; (s > 0 && i < t) || (s < 0 && i > t); i += s)

        # Only those for statements iterating over simple `range` should have survived
        mie = node.Iterator as MethodInvocationExpression
        if len(mie.Arguments) == 1:
            start = Moz.Literal(0)
            length = Apply(mie.Arguments[0])
        else:
            start = Apply(mie.Arguments[0])
            length = Apply(mie.Arguments[1])

        index = Moz.Identifier(node.Declarations[0].Name)

        fst = Moz.ForStatement(loc: loc(node))
        fst.init = Moz.AssignmentExpression(
            left: index,
            operator: '=',
            right: start
        )
        fst.test = Moz.BinaryExpression(
            left: index,
            operator: '<',
            right: length
        )
        fst.update = Moz.UpdateExpression(
            argument: index,
            operator: '++',
            prefix: false
        )

        fst.body = Apply(node.Block)
        Return fst

    def OnWhileStatement(node as WhileStatement):
        wst = Moz.WhileStatement(loc: loc(node))
        wst.test = Apply(node.Condition)
        wst.body = Apply(node.Block)

        Return wst

    def OnTryStatement(node as TryStatement):

        n = Moz.TryStatement(loc: loc(node))
        n.block = Apply(node.ProtectedBlock)

        assert len(node.ExceptionHandlers) <= 1, 'Multiple exceptions handlers should be processed in previous steps'
        if len(node.ExceptionHandlers):
            hdl = node.ExceptionHandlers[0]
            h = Moz.CatchClause(loc: loc(hdl))
            h.param = Moz.Identifier(loc: loc(hdl.Declaration), name: hdl.Declaration.Name)
            h.body = Apply(hdl.Block)
            n.handlers.Add(h)

        if node.EnsureBlock:
            n.finalizer = Apply(node.EnsureBlock)

        Return n

    def OnBreakStatement(node as BreakStatement):
        Return Moz.BreakStatement(loc: loc(node))

    def OnContinueStatement(node as ContinueStatement):
        Return Moz.ContinueStatement(loc: loc(node))

    def OnLabelStatement(node as LabelStatement):
        n = Moz.LabeledStatement(loc: loc(node))
        n.label = Moz.Identifier(loc: loc(node), name: node.Name)

        # In Mozilla AST labels are always associated with a statement.
        stmts = (node.ParentNode as Block).Statements
        idx = stmts.IndexOf(node)
        st = stmts[idx + 1]
        stmts.RemoveAt(idx + 1)

        if st.NodeType not in (NodeType.ForStatement, NodeType.WhileStatement):
            raise 'Javascript only allows label on looping statements'
        n.body = Apply(st)

        Return n

    def OnGotoStatement(node as GotoStatement):
        n = Moz.ContinueStatement(loc: loc(node))
        n.label = Moz.Identifier(loc: loc(node.Label), name: node.Label.Name)
        Return n

    def OnRaiseStatement(node as RaiseStatement):
        # TODO: Move this to clean step
        /*
        if false and Context.Parameters.Debug:
            n = Moz.CallExpression(loc: loc(node))
            n.callee = Moz.MemberExpression(
                loc: loc(node),
                object: Moz.Identifier(name: 'Boo'),
                property: Moz.Identifier(name: 'raise')
            )
            # TODO: We should make sure it's a constructor
            if node.Exception isa MethodInvocationExpression:
                c = Moz.NewExpression(loc: loc(node.Exception))
                c._constructor = Apply(node.Exception)
                n.arguments.Add(c)
            else:
                n.arguments.Add(Apply(node.Exception))

            lex = node.Exception.LexicalInfo
            n.arguments.Add(Moz.Literal(value: lex.FileName))
            n.arguments.Add(Moz.Literal(value: lex.Line))

            Return Moz.ExpressionStatement(loc: loc(node), expression: n)
        else:
        */
        t = Moz.ThrowStatement(loc: loc(node))
        t.argument = Apply(node.Exception)
        # TODO: We should make sure it's a constructor
        /*
        if false and node.Exception isa MethodInvocationExpression:
            c = Moz.NewExpression(loc: loc(node.Exception))
            c._constructor = Apply(node.Exception)
            t.argument = c
        else:
        */

        Return t

    def OnReferenceExpression(node as ReferenceExpression):
        Return Moz.Identifier(loc: loc(node), name: node.Name)

    def OnMemberReferenceExpression(node as MemberReferenceExpression):
        n = Moz.MemberExpression(loc: loc(node), property: Moz.Identifier(loc: loc(node), name: node.Name))
        n.object = Apply(node.Target)
        Return n

    def OnConditionalExpression(node as ConditionalExpression):
    """ Convert to the ternary operator.
            (10 if true else 20)  -->  true ? 10 : 20
    """
        c = Moz.ConditionalExpression(loc: loc(node))
        c.test = Apply(node.Condition)
        c.consequent = Apply(node.TrueValue)
        c.alternate = Apply(node.FalseValue)
        Return c

    def OnSlicingExpression(node as SlicingExpression):
        n = Moz.MemberExpression(
            loc: loc(node),
            object: Apply(node.Target),
            property: Apply(node.Indices[0].Begin),
            computed: true
        )

        Return n

    def OnMacroStatement(node as MacroStatement):
        # Special handling for switch/case constructs (used in generators)
        if node.Name == 'switch':
            switch = Moz.SwitchStatement(loc: loc(node))
            switch.discriminant = Apply(node.Arguments[0])
            for casemacro as MacroStatement in node.Body.Statements:
                assert casemacro isa MacroStatement

                case = Moz.SwitchCase(loc: loc(casemacro))
                case.test = Apply(casemacro.Arguments[0])
                for st in casemacro.Body.Statements:
                    case.consequent.Add(Apply(st))

                switch.cases.Add(case)

            Return switch
        else:
            raise 'Macro statements should have been already resolved'

    def OnSuperLiteralExpression(node as SuperLiteralExpression):
        n = Moz.Identifier('__super__')
        Return n

    def OnMethodInvocationExpression(node as MethodInvocationExpression):

        # Detect constructors
        if node.ContainsAnnotation('constructor') or \
           node.Target.Entity and node.Target.Entity.EntityType == EntityType.Constructor and not isFactory(node.Target):
           #node.Target.Entity isa IConstructor and not isFactory(node.Target):
            c = Moz.NewExpression(loc: loc(node))
            c._constructor = Apply(node.Target)
            for arg in node.Arguments:
                c.arguments.Add(Apply(arg))
            Return c
        # Detect sequences
        elif node.Target.ToString() == '@':
            s = Moz.SequenceExpression(loc: loc(node))
            for arg in node.Arguments:
                s.expressions.Add(Apply(arg))
            Return s
        # Detect super()
        elif node.Target.NodeType == NodeType.SuperLiteralExpression:
            method = node.GetAncestor[of Method]()
            n = Moz.CallExpression(loc: loc(node))
            n.callee = Moz.Identifier('__super__.prototype.' + method.Name + '.call')
            n.arguments.Add(Moz.ThisExpression())
            for arg in node.Arguments:
                n.arguments.Add(Apply(arg))
            Return n

        else:
            n = Moz.CallExpression(loc: loc(node))
            n.callee = Apply(node.Target)
            for arg in node.Arguments:
                n.arguments.Add(Apply(arg))

            # Detect calls to eval() with a simple string argument to define a
            # verbatim version of the expression.
            # cache = my(RuntimeMethodCache)
            cache = My[of RuntimeMethodCache].Instance
            if node.Target.Entity is cache.Eval and \
               len(node.Arguments) == 1 and \
               node.Arguments[0].NodeType == NodeType.StringLiteralExpression:
                n.verbatim = (node.Arguments[0] as StringLiteralExpression).Value

            Return n

    def OnBlockExpression(node as BlockExpression):
        n = Moz.FunctionExpression(loc: loc(node))
        for param in node.Parameters:
            p = Moz.Identifier(loc: loc(param), name: param.Name)
            n.params.Add(p)

        n.body = Apply(node.Body)
        Return n

    def OnBinaryExpression(node as BinaryExpression):
        n = Moz.BinaryExpression(loc: loc(node))
        match node.Operator:
            case BinaryOperatorType.ReferenceEquality:
                if NodeType.NullLiteralExpression in (node.Left.NodeType, node.Right.NodeType):
                    Return(ProcessIsNull(node))
                    return

                n.operator = '==='
            case BinaryOperatorType.ReferenceInequality:
                if NodeType.NullLiteralExpression in (node.Left.NodeType, node.Right.NodeType):
                    ue = Moz.UnaryExpression(loc: loc(node), operator: '!')
                    ue.argument = ProcessIsNull(node)
                    Return ue
                    return

                n.operator = '!=='

            case BinaryOperatorType.Equality:
                n.operator = '=='
            case BinaryOperatorType.Inequality:
                n.operator = '!='

            case BinaryOperatorType.Member:
                n.operator = 'in'
            case BinaryOperatorType.Addition:
                n.operator = '+'
            case BinaryOperatorType.Subtraction:
                n.operator = '-'
            case BinaryOperatorType.Multiply:
                n.operator = '*'
            case BinaryOperatorType.Division:
                n.operator = '/'
            case BinaryOperatorType.Modulus:
                n.operator = '%'
            case BinaryOperatorType.BitwiseAnd:
                n.operator = '&'
            case BinaryOperatorType.BitwiseOr:
                n.operator = '|'
            case BinaryOperatorType.ExclusiveOr:
                n.operator = '^'
            case BinaryOperatorType.GreaterThan:
                n.operator = '>'
            case BinaryOperatorType.GreaterThanOrEqual:
                n.operator = '>='
            case BinaryOperatorType.LessThan:
                n.operator = '<'
            case BinaryOperatorType.LessThanOrEqual:
                n.operator = '<='
            case BinaryOperatorType.ShiftLeft:
                n.operator = '<<'
            case BinaryOperatorType.ShiftRight:
                n.operator = '>>'

            case BinaryOperatorType.Assign:
                n = Moz.AssignmentExpression(loc: loc(node), operator: '=')
            case BinaryOperatorType.InPlaceBitwiseAnd:
                n = Moz.AssignmentExpression(loc: loc(node), operator: '&=')
            case BinaryOperatorType.InPlaceBitwiseOr:
                n = Moz.AssignmentExpression(loc: loc(node), operator: '|=')
            case BinaryOperatorType.InPlaceExclusiveOr:
                n = Moz.AssignmentExpression(loc: loc(node), operator: '^=')

            case BinaryOperatorType.And:
                n = Moz.LogicalExpression(loc: loc(node), operator: '&&')
            case BinaryOperatorType.Or:
                n = Moz.LogicalExpression(loc: loc(node), operator: '||')

            otherwise:
                raise 'Operator not supported ' + node.Operator

        n.left = Apply(node.Left)
        n.right = Apply(node.Right)
        Return n

    def OnUnaryExpression(node as UnaryExpression):
        match node.Operator:
            case UnaryOperatorType.UnaryNegation:
                n = Moz.UnaryExpression(loc: loc(node), operator: '-')
            case UnaryOperatorType.LogicalNot:
                n = Moz.UnaryExpression(loc: loc(node), operator: '!')
            case UnaryOperatorType.OnesComplement:
                n = Moz.UnaryExpression(loc: loc(node), operator: '~')
            case UnaryOperatorType.Increment:
                n = Moz.UpdateExpression(loc: loc(node), operator: '++', prefix: true)
            case UnaryOperatorType.Decrement:
                n = Moz.UpdateExpression(loc: loc(node), operator: '--', prefix: true)
            case UnaryOperatorType.PostIncrement:
                n = Moz.UpdateExpression(loc: loc(node), operator: '++', prefix: false)
            case UnaryOperatorType.PostDecrement:
                n = Moz.UpdateExpression(loc: loc(node), operator: '--', prefix: false)
            otherwise:
                raise 'Operator not supported ' + node.Operator

        n.argument = Apply(node.Operand)
        Return n

    protected def ProcessIsNull(node as BinaryExpression) as Moz.IExpression:
        itm = (node.Right if node.Left.NodeType == NodeType.NullLiteralExpression else node.Left)
        call = Moz.CallExpression(loc: loc(node))
        call.callee = Moz.Identifier('Boo.isNull')
        call.arguments.Add(Apply(itm))
        return call

namespace BooJs.Compiler.Visitors

import Boo.Lang.Environments
import Boo.Lang.Compiler.Ast
import Boo.Lang.Compiler.TypeSystem
import Boo.Lang.PatternMatching

import BooJs.Compiler(UniqueNameProvider)
import BooJs.Compiler.Utils
import BooJs.Compiler.TypeSystem(RuntimeMethodCache)
import BooJs.Compiler.Mozilla as Moz

class MozillaAstVisitor(FastDepthFirstVisitor):
"""
Transforms a Boo AST into a Mozilla AST
"""
    # Holds the latest conversion from a Boo node to a Moz node
    _return as Moz.Node

    protected def Return(node as Moz.Node):
        _return = node

    protected def Apply(node as Node) as Moz.Node:
        _return = null
        Visit node
        return _return

    protected def loc(node as Node):
        obj = Moz.SourceLocation()
        obj.source = node.LexicalInfo.FileName
        obj.start = Moz.SourceLocation.Position(line: node.LexicalInfo.Line, column: node.LexicalInfo.Column-1)

        # Compute an approximate end position if not available
        if node.EndSourceLocation and node.EndSourceLocation.Line >= 0:
            line, column = (node.EndSourceLocation.Line, node.EndSourceLocation.Column-1)
        else:
            parts = node.ToCodeString().Split(char('\n'))
            line, column = (node.LexicalInfo.Line, node.LexicalInfo.Column-1)
            if len(parts) > 1:
                line += len(parts) - 1
                column = 0
            column += len(parts[-1])

        obj.end = Moz.SourceLocation.Position(line: line, column: column)

        return obj

    _nameProvider = UniqueNameProvider()
    _renamed = {}
    protected def RenameIdent(name as  string) as string:
    """ HACK: Since we can't override the default UniqueNameProvider we have to do some dirty
              regexp replace :(
    """
        return name unless name =~ /\$\d+$/

        if name not in _renamed:
            parts = name.Split(char('$'))
            _renamed[name] = _nameProvider.GetUniqueName(*parts[1:-1])

        return _renamed[name]

    def Run(node as Node) as Moz.Node:
        _renamed = {}
        _nameProvider.Reset()

        Visit node
        return _return

    def OnBoolLiteralExpression(node as BoolLiteralExpression):
        Return Moz.Literal(loc: loc(node), value: node.Value)

    def OnNullLiteralExpression(node as NullLiteralExpression):
        Return Moz.Literal(loc: loc(node), value: null)

    def OnIntegerLiteralExpression(node as IntegerLiteralExpression):
        n as Moz.IExpression
        if node.Value < 0:
            n = Moz.Literal(loc: loc(node), value: -node.Value)
            n = Moz.UnaryExpression(loc: loc(node), operator: '-', argument: n)
         else:
            n = Moz.Literal(loc: loc(node), value: node.Value)

        Return n

    def OnDoubleLiteralExpression(node as DoubleLiteralExpression):
        n as Moz.IExpression
        if node.Value < 0:
            n = Moz.Literal(loc: loc(node), value: -node.Value)
            n = Moz.UnaryExpression(loc: loc(node), operator: '-', argument: n)
         else:
            n = Moz.Literal(loc: loc(node), value: node.Value)

        Return n

    def OnStringLiteralExpression(node as StringLiteralExpression):
        Return Moz.Literal(loc: loc(node), value: node.Value)

    def OnRELiteralExpression(node as RELiteralExpression):
        Return Moz.Literal(loc: loc(node), value: node.Value, regexp: true)

    def OnSelfLiteralExpression(node as SelfLiteralExpression):
        Return Moz.ThisExpression(loc: loc(node))

    def OnListLiteralExpression(node as ListLiteralExpression):
        n = Moz.ArrayExpression(loc: loc(node))
        for itm in node.Items:
            exp = Apply(itm)
            n.elements.Add(exp)
        Return n

    def OnHashLiteralExpression(node as HashLiteralExpression):
        n = Moz.ObjectExpression(loc: loc(node))
        for pair in node.Items:
            prop = Moz.ObjectExpression.Prop(key: Apply(pair.First), value: Apply(pair.Second))
            n.properties.Add(prop)
        Return n

    def OnEnumDefinition(node as EnumDefinition):
        # Create an object with the enum values
        o = Moz.ObjectExpression(loc: loc(node))
        for member as EnumMember in node.Members:
            assert member.Initializer != null, 'Enum definition without an initializer value!'
            o.properties.Add(
                Moz.ObjectExpression.Prop(
                    key: Moz.Literal(value: member.Name),
                    value: Apply(member.Initializer)
                )
            )

        # Assign the object to a variable
        v = Moz.VariableDeclarator(loc: loc(node))
        v.id = Moz.Identifier(loc: loc(node), name: node.FullName)
        v.init = o
        n = Moz.VariableDeclaration(loc: loc(node), kind: 'var')
        n.declarations.Add(v)

        Return n

    def OnModule(node as Module):
        n = Moz.Program(loc: loc(node))

        deps = List of Moz.IExpression() { Moz.Literal('exports'), Moz.Literal('Boo') }
        refs = List of Moz.IPattern() { Moz.Identifier('exports'), Moz.Identifier('Boo') }

        # Get namespace mapping annotations
        mapping as Hash = node['nsmapping']
        asmrefs as Hash = node['nsasmrefs']

        for ns in mapping.Keys:
            if ns in asmrefs:
                context().TraceInfo('Namespace mapping {0} => {1}', ns, mapping[ns] + ':' + asmrefs[ns])
                deps.Add( Moz.Literal(ns + ':' + asmrefs[ns]) )
            else:
                context().TraceInfo('Namespace mapping {0} => {1}', ns, mapping[ns])
                deps.Add( Moz.Literal(ns) )
            refs.Add( Moz.Identifier(mapping[ns]) )

        # Use Boo.define to bootstrap the module contents
        call = Moz.CallExpression()
        call.callee = Moz.MemberExpression(
            object: Moz.Identifier(name: 'Boo'),
            property: Moz.Identifier(name: 'define')
        )
        call.arguments.Add(Moz.Literal(value: (node.Namespace.ToString() if node.Namespace else '')))
        call.arguments.Add(Moz.ArrayExpression(elements: deps))
        fn = Moz.FunctionExpression(params: refs, body: Moz.BlockStatement())
        call.arguments.Add(fn)
        n.body.Add(Moz.ExpressionStatement(call))

        st as Moz.IStatement
        for member in node.Members:
            st = Apply(member)
            if st is null:
                continue

            if st isa Moz.BlockStatement:
                fn.body.body += (st as Moz.BlockStatement).body
            else:
                fn.body.body.Add(st)

        # Export public symbols
        # js: exports.symbol = symbol
        members = TypeMemberCollection()
        for member in node.Members:
            if member.IsSynthetic and member.Name.EndsWith('Module'):
                members.AddRange((member as ClassDefinition).Members)
            else:
                members.Add(member)

        for member in members:
            continue unless member.IsPublic and not member.IsInternal

            expr = Moz.BinaryExpression(
                left: Moz.MemberExpression(
                    object: Moz.Identifier('exports'),
                    property: Moz.Identifier(member.Name)
                ),
                operator: '=',
                right: Moz.Identifier(member.Name)
            )

            st = Moz.ExpressionStatement(expr)
            fn.body.body.Add(st)

        if not node.Globals.IsEmpty:
            rcall = Moz.CallExpression()
            rcall.callee = Moz.MemberExpression(
                object: Moz.Identifier(name: 'Boo'),
                property: Moz.Identifier(name: 'require')
            )
            # Clone the deps list replacing 'exports' by the namespace
            deps = deps.GetRange(0, len(deps))
            deps[0] = Moz.Literal(node.Namespace or '')
            rcall.arguments.Add(Moz.ArrayExpression(elements: deps))
            fn = Moz.FunctionExpression(params: refs, body: Moz.BlockStatement())
            rcall.arguments.Add(fn)
            n.body.Add(Moz.ExpressionStatement(rcall))

            for global in node.Globals.Statements:
                st = Apply(global)
                fn.body.body.Add(st)

        #n['module'] = node
        Return n

    def OnClassDefinition(node as ClassDefinition):
        n = Moz.BlockStatement()

        # Detect module class and output its members directly
        if node.IsSynthetic and node.IsFinal and node.Name.EndsWith('Module'):
            members = [m for m in node.Members if m.NodeType != NodeType.Constructor]
            st as Moz.IStatement
            for member in members:
                match Apply(member):
                    case null:
                        continue
                    case bs=Moz.BlockStatement():
                        n.body += bs.body
                    case _:
                        n.body.Add(_)

            Return n
            return

        # TODO: Handle visibility (internals should not be exposed)

        # Bar = (function (__super__) {
        #     // Constructor
        #     function Bar() {
        #         // Allow the intantiation without using the `new` operator (TODO: do we really want this?)
        #         if (!(this instanceof Bar)) {
        #             return new (Function.prototype.bind.apply(Bar, [this].concat(Array.prototype.slice.call(arguments)) ));
        #         }
        #
        #         // Initialize fields (TODO: can this be moved to the prototype? at least the non private ones)
        #         this.field = '';
        #
        #         // Bind instance methods (TODO: Support annotation to avoid binding)
        #         this.foo = Boo.bind(this.foo, this);
        #
        #         // Constructor code (TODO: how to handle overloads?)
        #         // ...
        #     }
        #
        #     // Setup inheritance
        #     Bar.prototype = Boo.create(__super__.prototype);
        #     Bar.prototype.constructor = Bar;
        #
        #     // Reflect metadata
        #     Bar.prototype.$boo$interfaces = [IEnumerator];
        #
        #     // Instance members
        #     Bar.prototype.foo = function Bar$foo() {
        #         __super__.prototype.foo.call(this);
        #     };
        #
        #
        #     // Inherit "static" properties (TODO: Is this actually needed?)
        #     for (var prop in __super__) {
        #         if (__super__.hasOwnProperty(prop)) {
        #             Bar[prop] = __super__[prop];
        #         }
        #     }
        #
        #     // Static members
        #     Bar.bars = function Bar$bars() {};
        #
        #     // Static constructor
        #     // ...
        #
        #     return Bar;
        # })(Foo);

        block = Moz.BlockStatement()

        # Constructor
        # js: function Bar() {}
        cons = Moz.FunctionDeclaration(loc: loc(node))
        cons.body = Moz.BlockStatement()
        cons.id = Moz.Identifier(node.Name)
        block.body.Add(cons)

        # Initialize fields
        # TODO: Shouldn't fields with primitive initializers be moved to the prototype?
        # TODO: Boo already moves field initialization to the constructors
        for f as Field in [m for m in node.Members if m.NodeType == NodeType.Field]:
            assign = Moz.AssignmentExpression(operator:'=')
            assign.left = Moz.Identifier(loc:loc(f), name:'this.' + f.Name)
            assign.right = Apply(f.Initializer)
            # TODO: Disabled until the basics are working
            #cons.body.body.Add(Moz.ExpressionStatement(assign))

        # Handle constructors
        members = [m for m in node.Members if m.NodeType == NodeType.Constructor]
        for c as Constructor in members:
            continue if not c
            # TODO: Disabled until the basics are working
            #cons.body.body.Add(Apply(c))

        # Static members
        # js: Bar.sbar = function Bar_sbar (arg) { }
        members = [m for m in node.Members if m.NodeType == NodeType.Method and m.IsStatic]
        for method as Method in members:
            assign = Moz.AssignmentExpression(operator:'=')
            assign.left = Moz.Identifier(name:node.Name + '.' + method.Name)
            fn = Apply(method) as Moz.FunctionDeclaration
            assign.right = Moz.FunctionExpression(
                loc: fn.loc,
                id: Moz.Identifier((node.Name + '.' + fn.id.name).Replace('.', '_')),
                params: fn.params,
                body: fn.body
            )
            block.body.Add(Moz.ExpressionStatement(assign))

        # Setup inheritance
        # js: Bar.prototype = Boo.create(__super__.prototype)
        assign = Moz.AssignmentExpression(operator:'=')
        assign.left = Moz.Identifier(name:node.Name + '.prototype')
        assign.right = Moz.CallExpression()
        (assign.right as Moz.CallExpression).callee = Moz.Identifier('Boo.create')
        (assign.right as Moz.CallExpression).arguments.Add(
            Moz.Identifier('__super__.prototype')
            # Moz.Identifier(
            #     ('Object' if typeSystem().IsSystemObject(node.BaseTypes.First.Entity)
            #         else node.BaseTypes.First.Entity.FullName) + '.prototype'
            # )
        )
        block.body.Add(Moz.ExpressionStatement(assign))

        # js: Bar.prototype.constructor = Bar
        assign = Moz.AssignmentExpression(operator:'=')
        assign.left = Moz.Identifier(name:node.Name + '.prototype.constructor')
        assign.right = Moz.Identifier(name:node.Name)
        block.body.Add(Moz.ExpressionStatement(assign))

        # Metadata
        # js: Bar.prototype.$boo$interfaces = [ IEnumerable ]
        interfaces = Moz.ArrayExpression()
        for t in node.BaseTypes:
            continue if t is node.BaseTypes.First
            continue if typeSystem().IsSystemObject(t.Entity)
            interfaces.elements.Add(
                Moz.Identifier(t.Entity.FullName)
            )
        assign = Moz.AssignmentExpression(operator:'=')
        assign.left = Moz.Identifier(name:node.Name + '.prototype.$boo$interfaces')
        assign.right = interfaces
        block.body.Add(Moz.ExpressionStatement(assign))

        # Instance members
        # js: Bar.prototype.bar = function Bar$bar (arg) { }
        members = [m for m in node.Members if m.NodeType == NodeType.Method and not m.IsStatic]
        for method as Method in members:
            assign = Moz.AssignmentExpression(operator:'=')
            assign.left = Moz.Identifier(name:node.Name + '.prototype.' + method.Name)
            fn = Apply(method) as Moz.FunctionDeclaration
            assign.right = Moz.FunctionExpression(
                loc: fn.loc,
                id: Moz.Identifier((node.Name + '.' + fn.id.name).Replace('.', '$')),
                params: fn.params,
                body: fn.body
            )
            block.body.Add(Moz.ExpressionStatement(assign))

        # Wrap everything in a self calling function and assign to a variable
        # js: Bar = (function(__super__){ ... return Bar; })(Object)
        block.body.Add(
            Moz.ReturnStatement(argument: Moz.Identifier(node.Name))
        )

        func = Moz.FunctionExpression(body:block)
        func.params = List of Moz.IPattern() { Moz.Identifier('__super__') }
        call = Moz.CallExpression(callee:func)
        call.arguments.Add(
            Moz.Identifier(
                ('Object' if typeSystem().IsSystemObject(node.BaseTypes.First.Entity)
                    else node.BaseTypes.First.Entity.FullName)
            )
        )
        decl = Moz.VariableDeclarator(loc:loc(node))
        decl.id = Moz.Identifier(node.Name)
        # decl.init = Moz.CallExpression(callee: Moz.FunctionExpression(body:block))
        decl.init = call
        decl_st = Moz.VariableDeclaration(loc:loc(node), kind:'var')
        decl_st.declarations.Add(decl)

        Return decl_st

    def OnField(node as Field):
        pass

    def OnConstructor(node as Constructor):
        OnMethod(node)
        return

        ifst = Moz.IfStatement()
        # TODO: Apply test based on arguments and constructor parameters
        ifst.test = Moz.Identifier(name:'true')

        ifst.consequent = Apply(node.Body)

        Return ifst

    def OnMethod(node as Method):
        n = Moz.FunctionDeclaration(loc: loc(node))
        n.id = Moz.Identifier(loc: loc(node), name: node.Name)
        for param in node.Parameters:
            p = Moz.Identifier(loc: loc(param), name: param.Name)
            n.params.Add(p)

        n.body = Apply(node.Body)
        Return n

    def OnDeclarationStatement(node as DeclarationStatement):
        # TODO: Generate type annotated comments
        v = Moz.VariableDeclarator(loc: loc(node))
        v.id = Moz.Identifier(name: RenameIdent(node.Declaration.Name))
        v.init = Apply(node.Initializer)

        vd = Moz.VariableDeclaration(loc: loc(node), kind: 'var')
        vd.declarations.Add(v)
        Return vd

    def OnBlock(node as Block):
        b = Moz.BlockStatement(loc: loc(node))
        for st in node.Statements:
            mst = Apply(st) as Moz.IStatement
            if mst:
                b.body.Add(mst)

        Return b

    def OnExpressionStatement(node as ExpressionStatement):
        n = Moz.ExpressionStatement(loc: loc(node))
        n.expression = Apply(node.Expression)
        Return n

    def OnReturnStatement(node as ReturnStatement):
        n = Moz.ReturnStatement(loc: loc(node))
        n.argument = Apply(node.Expression)
        Return n

    def OnIfStatement(node as IfStatement):

        ifst = Moz.IfStatement(loc: loc(node))
        ifst.test = Apply(node.Condition)
        ifst.consequent = Apply(node.TrueBlock)
        if node.FalseBlock and not node.FalseBlock.IsEmpty:
            blk = node.FalseBlock
            if len(blk.Statements) == 1 and blk.FirstStatement isa IfStatement:
                ifst.alternate = Apply(blk.FirstStatement)
            else:
                ifst.alternate = Apply(blk)

        Return ifst

    def OnForStatement(node as ForStatement):
        # TODO: Optimize range(items.length) to cache the length value
        # TODO: Optimize range(f, t) and range(f, t, s)
        #       for (i=f; (s > 0 && i < t) || (s < 0 && i > t); i += s)

        # Only those for statements iterating over simple `range` should have survived
        mie = node.Iterator as MethodInvocationExpression
        if len(mie.Arguments) == 1:
            start = Moz.Literal(0)
            length = Apply(mie.Arguments[0])
        else:
            start = Apply(mie.Arguments[0])
            length = Apply(mie.Arguments[1])

        index = Moz.Identifier(RenameIdent(node.Declarations[0].Name))

        fst = Moz.ForStatement(loc: loc(node))
        fst.init = Moz.AssignmentExpression(
            left: index,
            operator: '=',
            right: start
        )
        fst.test = Moz.BinaryExpression(
            left: index,
            operator: '<',
            right: length
        )
        fst.update = Moz.UpdateExpression(
            argument: index,
            operator: '++',
            prefix: false
        )

        fst.body = Apply(node.Block)
        Return fst

    def OnWhileStatement(node as WhileStatement):
        wst = Moz.WhileStatement(loc: loc(node))
        wst.test = Apply(node.Condition)
        wst.body = Apply(node.Block)

        Return wst

    def OnTryStatement(node as TryStatement):

        n = Moz.TryStatement(loc: loc(node))
        n.block = Apply(node.ProtectedBlock)

        assert len(node.ExceptionHandlers) <= 1, 'Multiple exceptions handlers should be processed in previous steps'
        if len(node.ExceptionHandlers):
            hdl = node.ExceptionHandlers[0]
            h = Moz.CatchClause(loc: loc(hdl))
            h.param = Moz.Identifier(loc: loc(hdl.Declaration), name: hdl.Declaration.Name)
            h.body = Apply(hdl.Block)
            n.handlers.Add(h)

        if node.EnsureBlock:
            n.finalizer = Apply(node.EnsureBlock)

        Return n

    def OnBreakStatement(node as BreakStatement):
        Return Moz.BreakStatement(loc: loc(node))

    def OnContinueStatement(node as ContinueStatement):
        Return Moz.ContinueStatement(loc: loc(node))

    def OnLabelStatement(node as LabelStatement):
        n = Moz.LabeledStatement(loc: loc(node))
        n.label = Moz.Identifier(loc: loc(node), name: node.Name)

        # In Mozilla AST labels are always associated with a statement.
        stmts = (node.ParentNode as Block).Statements
        idx = stmts.IndexOf(node)
        st = stmts[idx + 1]
        stmts.RemoveAt(idx + 1)

        if st.NodeType not in (NodeType.ForStatement, NodeType.WhileStatement):
            raise 'Javascript only allows label on looping statements'
        n.body = Apply(st)

        Return n

    def OnGotoStatement(node as GotoStatement):
        n = Moz.ContinueStatement(loc: loc(node))
        n.label = Moz.Identifier(loc: loc(node.Label), name: node.Label.Name)
        Return n

    def OnRaiseStatement(node as RaiseStatement):
        # TODO: Move this to clean step
        /*
        if false and Context.Parameters.Debug:
            n = Moz.CallExpression(loc: loc(node))
            n.callee = Moz.MemberExpression(
                loc: loc(node),
                object: Moz.Identifier(name: 'Boo'),
                property: Moz.Identifier(name: 'raise')
            )
            # TODO: We should make sure it's a constructor
            if node.Exception isa MethodInvocationExpression:
                c = Moz.NewExpression(loc: loc(node.Exception))
                c._constructor = Apply(node.Exception)
                n.arguments.Add(c)
            else:
                n.arguments.Add(Apply(node.Exception))

            lex = node.Exception.LexicalInfo
            n.arguments.Add(Moz.Literal(value: lex.FileName))
            n.arguments.Add(Moz.Literal(value: lex.Line))

            Return Moz.ExpressionStatement(loc: loc(node), expression: n)
        else:
        */
        t = Moz.ThrowStatement(loc: loc(node))
        t.argument = Apply(node.Exception)
        # TODO: We should make sure it's a constructor
        /*
        if false and node.Exception isa MethodInvocationExpression:
            c = Moz.NewExpression(loc: loc(node.Exception))
            c._constructor = Apply(node.Exception)
            t.argument = c
        else:
        */

        Return t

    def OnReferenceExpression(node as ReferenceExpression):
        Return Moz.Identifier(loc: loc(node), name: RenameIdent(node.Name))

    def OnMemberReferenceExpression(node as MemberReferenceExpression):
        n = Moz.MemberExpression(loc: loc(node), property: Moz.Identifier(loc: loc(node), name: node.Name))
        n.object = Apply(node.Target)
        Return n

    def OnConditionalExpression(node as ConditionalExpression):
    """ Convert to the ternary operator.
            (10 if true else 20)  -->  true ? 10 : 20
    """
        c = Moz.ConditionalExpression(loc: loc(node))
        c.test = Apply(node.Condition)
        c.consequent = Apply(node.TrueValue)
        c.alternate = Apply(node.FalseValue)
        Return c

    def OnSlicingExpression(node as SlicingExpression):
        n = Moz.MemberExpression(
            loc: loc(node),
            object: Apply(node.Target),
            property: Apply(node.Indices[0].Begin),
            computed: true
        )

        Return n

    def OnMacroStatement(node as MacroStatement):
        # Special handling for switch/case constructs (used in generators)
        if node.Name == 'switch':
            switch = Moz.SwitchStatement(loc: loc(node))
            switch.discriminant = Apply(node.Arguments[0])
            for casemacro as MacroStatement in node.Body.Statements:
                assert casemacro isa MacroStatement

                case = Moz.SwitchCase(loc: loc(casemacro))
                case.test = Apply(casemacro.Arguments[0])
                for st in casemacro.Body.Statements:
                    case.consequent.Add(Apply(st))

                switch.cases.Add(case)

            Return switch
        else:
            raise 'Macro statements should have been already resolved'

    def OnSuperLiteralExpression(node as SuperLiteralExpression):
        n = Moz.Identifier('__super__')
        Return n

    def OnMethodInvocationExpression(node as MethodInvocationExpression):

        # Detect constructors
        if node.ContainsAnnotation('constructor') or \
           node.Target.Entity and node.Target.Entity.EntityType == EntityType.Constructor and not isFactory(node.Target):
           #node.Target.Entity isa IConstructor and not isFactory(node.Target):
            c = Moz.NewExpression(loc: loc(node))
            c._constructor = Apply(node.Target)
            for arg in node.Arguments:
                c.arguments.Add(Apply(arg))
            Return c
        # Detect sequences
        elif node.Target.ToString() == '@':
            s = Moz.SequenceExpression(loc: loc(node))
            for arg in node.Arguments:
                s.expressions.Add(Apply(arg))
            Return s
        # Detect super()
        elif node.Target.NodeType == NodeType.SuperLiteralExpression:
            method = node.GetAncestor[of Method]()
            n = Moz.CallExpression(loc: loc(node))
            n.callee = Moz.Identifier('__super__.prototype.' + method.Name + '.call')
            n.arguments.Add(Moz.ThisExpression())
            for arg in node.Arguments:
                n.arguments.Add(Apply(arg))
            Return n

        else:
            n = Moz.CallExpression(loc: loc(node))
            n.callee = Apply(node.Target)
            for arg in node.Arguments:
                n.arguments.Add(Apply(arg))

            # Detect calls to eval() with a simple string argument to define a
            # verbatim version of the expression.
            # cache = my(RuntimeMethodCache)
            cache = My[of RuntimeMethodCache].Instance
            if node.Target.Entity is cache.Eval and \
               len(node.Arguments) == 1 and \
               node.Arguments[0].NodeType == NodeType.StringLiteralExpression:
                n.verbatim = (node.Arguments[0] as StringLiteralExpression).Value

            Return n

    def OnBlockExpression(node as BlockExpression):
        n = Moz.FunctionExpression(loc: loc(node))
        for param in node.Parameters:
            p = Moz.Identifier(loc: loc(param), name: param.Name)
            n.params.Add(p)

        n.body = Apply(node.Body)
        Return n

    def OnBinaryExpression(node as BinaryExpression):
        n = Moz.BinaryExpression(loc: loc(node))
        match node.Operator:
            case BinaryOperatorType.ReferenceEquality:
                if NodeType.NullLiteralExpression in (node.Left.NodeType, node.Right.NodeType):
                    Return(ProcessIsNull(node))
                    return

                n.operator = '==='
            case BinaryOperatorType.ReferenceInequality:
                if NodeType.NullLiteralExpression in (node.Left.NodeType, node.Right.NodeType):
                    ue = Moz.UnaryExpression(loc: loc(node), operator: '!')
                    ue.argument = ProcessIsNull(node)
                    Return ue
                    return

                n.operator = '!=='

            case BinaryOperatorType.Equality:
                n.operator = '=='
            case BinaryOperatorType.Inequality:
                n.operator = '!='

            case BinaryOperatorType.Member:
                n.operator = 'in'
            case BinaryOperatorType.Addition:
                n.operator = '+'
            case BinaryOperatorType.Subtraction:
                n.operator = '-'
            case BinaryOperatorType.Multiply:
                n.operator = '*'
            case BinaryOperatorType.Division:
                n.operator = '/'
            case BinaryOperatorType.Modulus:
                n.operator = '%'
            case BinaryOperatorType.BitwiseAnd:
                n.operator = '&'
            case BinaryOperatorType.BitwiseOr:
                n.operator = '|'
            case BinaryOperatorType.ExclusiveOr:
                n.operator = '^'
            case BinaryOperatorType.GreaterThan:
                n.operator = '>'
            case BinaryOperatorType.GreaterThanOrEqual:
                n.operator = '>='
            case BinaryOperatorType.LessThan:
                n.operator = '<'
            case BinaryOperatorType.LessThanOrEqual:
                n.operator = '<='
            case BinaryOperatorType.ShiftLeft:
                n.operator = '<<'
            case BinaryOperatorType.ShiftRight:
                n.operator = '>>'

            case BinaryOperatorType.Assign:
                n = Moz.AssignmentExpression(loc: loc(node), operator: '=')
            case BinaryOperatorType.InPlaceBitwiseAnd:
                n = Moz.AssignmentExpression(loc: loc(node), operator: '&=')
            case BinaryOperatorType.InPlaceBitwiseOr:
                n = Moz.AssignmentExpression(loc: loc(node), operator: '|=')
            case BinaryOperatorType.InPlaceExclusiveOr:
                n = Moz.AssignmentExpression(loc: loc(node), operator: '^=')

            case BinaryOperatorType.And:
                n = Moz.LogicalExpression(loc: loc(node), operator: '&&')
            case BinaryOperatorType.Or:
                n = Moz.LogicalExpression(loc: loc(node), operator: '||')

            otherwise:
                raise 'Operator not supported ' + node.Operator

        n.left = Apply(node.Left)
        n.right = Apply(node.Right)
        Return n

    def OnUnaryExpression(node as UnaryExpression):
        match node.Operator:
            case UnaryOperatorType.UnaryNegation:
                n = Moz.UnaryExpression(loc: loc(node), operator: '-')
            case UnaryOperatorType.LogicalNot:
                n = Moz.UnaryExpression(loc: loc(node), operator: '!')
            case UnaryOperatorType.OnesComplement:
                n = Moz.UnaryExpression(loc: loc(node), operator: '~')
            case UnaryOperatorType.Increment:
                n = Moz.UpdateExpression(loc: loc(node), operator: '++', prefix: true)
            case UnaryOperatorType.Decrement:
                n = Moz.UpdateExpression(loc: loc(node), operator: '--', prefix: true)
            case UnaryOperatorType.PostIncrement:
                n = Moz.UpdateExpression(loc: loc(node), operator: '++', prefix: false)
            case UnaryOperatorType.PostDecrement:
                n = Moz.UpdateExpression(loc: loc(node), operator: '--', prefix: false)
            otherwise:
                raise 'Operator not supported ' + node.Operator

        n.argument = Apply(node.Operand)
        Return n

    protected def ProcessIsNull(node as BinaryExpression) as Moz.IExpression:
        itm = (node.Right if node.Left.NodeType == NodeType.NullLiteralExpression else node.Left)
        call = Moz.CallExpression(loc: loc(node))
        call.callee = Moz.Identifier('Boo.isNull')
        call.arguments.Add(Apply(itm))
        return call


remove old option and add documentation about coroutine exceptions

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

# @author	Cedric Vivier <cedricv@neonux.com>
#
# [coroutine] AST attribute
#
# Compile with:   booc -t:library -o:Coroutine.dll CoroutineAttribute.dll
#
# Look at tests/testcases/attributes/coroutine-*.boo for usage examples.
#
# OPTIONS:
#
#   Looping: bool   - Does the coroutine loop? (default: true)
#
#   Default: expression
#                   The value to return whenever the coroutine is terminated
#                   (Looping:false) or when Timeout has expired.
#                   If Default has not been set the coroutine will raise
#                   the corresponding exception (see 'Exceptions' below)
#
#   DefaultLastValue: bool
#                   When true default value is the last value. (default :false)
#
#   Future: bool    - Compute the future before it is requested (default: false)
#                   NB: 1st call to the coroutine will be synchronous
#
#   Timeout: long   - Timeout (in ms) before a blocking future call will return
#                   Default or raise a CoroutineFutureNotReadyException.
#                   (default: -1 means no timeout)
#
#   ThreadSafe: bool
#                   If true, there will be no lock on the method when Future is
#                   enabled. (default: false)
#
#   Parallel: int	- Number of futures to compute in parallel (default: 1)
#                   UNSUPPORTED		TODO:
#
#
# EXCEPTIONS:
#
#   CoroutineTerminatedException:
#                   This exception is raised whenever the coroutine has finished
#                   processing and Looping is false
#
#   CoroutineFutureNotReadyException
#                  This exception is raised whenever a Timeout is set and the
#                  result of the coroutine is not yet available.
#

namespace Coroutine

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

[AttributeUsage(AttributeTargets.Method)]
public class CoroutineAttribute(AbstractAstAttribute):

	Looping as BoolLiteralExpression:
		set:
			_looping = value.Value
	_looping = true

	Default as Expression:
		set:
			_default = value
			_defaultSet = true
	_default as Expression = null
	_defaultSet = false

	DefaultLastValue as BoolLiteralExpression:
		set:
			_defaultLastValue = value.Value
	_defaultLastValue = false

	Future as BoolLiteralExpression:
		set:
			_future = value.Value
	_future = false

	Timeout as IntegerLiteralExpression:
		set:
			_timeout = value.Value
	_timeout = -1L

	ThreadSafe as BoolLiteralExpression:
		set:
			_threadSafe = value.Value
	_threadSafe = false

	Parallel as IntegerLiteralExpression: #TODO: compute parallel futures
		set:
			raise NotSupportedException("parallel futures are not yet supported!")
	_parallel = 1

	final ResetEventImplementationTypeName = "System.Threading.AutoResetEvent"
	final ThreadImplementationTypeName = "System.Threading.Thread"

	_m as Method
	_generatorName as string

	public override def Apply(node as Node):
		_m = node as Method
		SanityCheck()

		_generatorName = "__"+_m.Name

		#enumerator type
		if _m.ReturnType is null:
			_m.ReturnType = SimpleTypeReference("System.Object")
		et = GenericTypeReference()
		et.Name = "System.Collections.Generic.IEnumerator"
		et.GenericArguments.Add(_m.ReturnType)

		eRef = GetReferenceForNewField("__enumerator", et)#ref to enumerator

		if _defaultSet or _defaultLastValue:
			f = CreateField("__default", _m.ReturnType)
			if _defaultSet:
				f.Initializer = _default
			_m.DeclaringType.Members.Add(f)
			dRef = ReferenceExpression(f.Name)#ref to default value

		if _future:
			rRef = GetReferenceForNewField("__reset", SimpleTypeReference(ResetEventImplementationTypeName))
			rtRef = GetReferenceForNewField("__resetThread", SimpleTypeReference(ResetEventImplementationTypeName))
			tRef = GetReferenceForNewField("__thread", SimpleTypeReference(ThreadImplementationTypeName))
			f = CreateField("__lock", SimpleTypeReference("System.Object"))
			f.Initializer = [| System.Object() |]
			_m.DeclaringType.Members.Add(f)
			lRef = ReferenceExpression(f.Name)#ref to lock object

		#generator invocation
		gInvoc = MethodInvocationExpression(MemberReferenceExpression(SelfLiteralExpression(), _generatorName))
		for parameter in _m.Parameters:
			gInvoc.Arguments.Add(ReferenceExpression(parameter.Name))

		facade = _m.Clone() as Method
		facade.IsSynthetic = true

		if not _future:
			facade.Body = [|
				block:
					if $eRef is null:
						$eRef = $gInvoc
			|].Block
			moveNext = [| $(eRef).MoveNext() |]
			if _looping:
				#$(eRef).Reset() not used to handle new arguments passed in at reset
				b = [|
						block:
							if not $moveNext:
								$eRef = $gInvoc
								$moveNext
					|].Block
				facade.Body.Add(b)
			elif _defaultSet or _defaultLastValue:
				b = [|
						block:
							if not $moveNext:
								return $dRef
					|].Block
				facade.Body.Add(b)
			else:
				b = [|
						block:
							if not $moveNext:
								raise CoroutineTerminatedException()
					|].Block
				facade.Body.Add(b)
			if _defaultLastValue:
				b = [|
						block:
							$dRef = $(eRef).Current
							return $dRef
					|].Block
				facade.Body.Add(b)
			else:
				facade.Body.Add([| return $(eRef).Current |])

		#future
		else:
			t = Method(tRef.Name+"__callable")
			t.IsSynthetic = true
			if _m.IsStatic or _m.DeclaringType isa Module:
				t.Modifiers |= TypeMemberModifiers.Static
			t.Modifiers |= TypeMemberModifiers.Private
			t.Body = [|
						block:
							while true:
								$(rtRef).WaitOne($_timeout, false)
								break if not $(eRef).MoveNext()
								$(rRef).Set()
							$tRef = null
							$(rRef).Set()
					|].Block
			_m.DeclaringType.Members.Add(t)
			callableRef = ReferenceExpression(t.Name)

			if _defaultSet:
				facade.Body = [|
					block:
						future as $(_m.ReturnType) = $dRef
				|].Block
			else:
				facade.Body = [|
					block:
						future as $(_m.ReturnType)
				|].Block
			b = [|
					block:
						:init
						if $eRef is null:
							$eRef = $gInvoc
							$rRef = System.Threading.AutoResetEvent(false)
							$rtRef = System.Threading.AutoResetEvent(true)
							$tRef = Thread($callableRef)
							$(tRef).IsBackground = true
							$(tRef).Name = "coroutine ${$(facade.Name)}"
							$(tRef).Start()
						if $tRef is not null and $(tRef).IsAlive:
							gotIt = $(rRef).WaitOne($_timeout, false)
				|].Block
			if _looping:
				b2 = [|
						block:
							if $tRef is null or not $(tRef).IsAlive:
								$eRef = null
								goto init
					|].Block
			elif _defaultSet or _defaultLastValue:
				b2 = [|
						block:
							if $tRef is null or not $(tRef).IsAlive:
								return $dRef
					|].Block
			else:
				b2 = [|
						block:
							if $tRef is null or not $(tRef).IsAlive:
								raise CoroutineTerminatedException()
					|].Block
			b.Add(b2)

			if _defaultSet or _defaultLastValue:
				b2 = [|
						block:
							if not gotIt:
								return $dRef
					|].Block
			else:
				b2 = [|
						block:
							if not gotIt:
								raise CoroutineFutureNotReadyException()
					|].Block
			b.Add(b2)

			if _defaultLastValue:
				b.Add([| $dRef = $(eRef).Current |])
			b.Add([| future = $(eRef).Current |])
			b.Add([| $(rtRef).Set() |])

			if not _threadSafe:
				lockBlock = [|
								block:
									lock $lRef:
										$b
							|].Block
				facade.Body.Add(lockBlock)
			else:
				facade.Body.Add(b)
			facade.Body.Add([| return future |])

		#hide the generator implementation
		_m.Name = _generatorName
		_m.Modifiers |= TypeMemberModifiers.Private
		_m.ReturnType = et

		#promote the facade
		_m.DeclaringType.Members.Add(facade)

	private def CreateField(name as string, type as TypeReference) as Field:
		f = Field()
		f.Name = "${name}${_generatorName}${Context.AllocIndex()}"
		f.IsSynthetic = true
		if _m.IsStatic or _m.DeclaringType isa Module:
			f.Modifiers |= TypeMemberModifiers.Static
		f.Modifiers |= TypeMemberModifiers.Private
		f.Type = type
		return f

	private def GetReferenceForNewField(name as string, type as TypeReference) as ReferenceExpression:
		f = CreateField(name, type)
		_m.DeclaringType.Members.Add(f)
		return ReferenceExpression(f.Name)

	private def SanityCheck():
		if _m is null:
			InvalidNodeForAttribute('Method')
			return
		if _looping and _defaultSet:
			Errors.Add(CompilerErrorFactory.CustomError(self.LexicalInfo, "Looping and Default are mutually exclusive"))
			return
		if _defaultSet and _defaultLastValue:
			Errors.Add(CompilerErrorFactory.CustomError(self.LexicalInfo, "DefaultLastValue and Default are mutually exclusive"))
			return
		if -1L != _timeout and not _future:
			Errors.Add(CompilerErrorFactory.CustomError(self.LexicalInfo, "Timeout requires Future:true"))
			return
		if 1 != _parallel and not _future:
			Errors.Add(CompilerErrorFactory.CustomError(self.LexicalInfo, "Parallel requires Future:true"))
			return

class CoroutineTerminatedException(System.Exception):
	pass

class CoroutineFutureNotReadyException(System.Exception):
	pass

# @author	Cedric Vivier <cedricv@neonux.com>
#
# [coroutine] AST attribute
#
# Compile with:   booc -t:library -o:Coroutine.dll CoroutineAttribute.dll
#
# Look at tests/testcases/attributes/coroutine-*.boo for usage examples.
#
# Attribute options/arguments:
#
#   Looping: bool   - Does the coroutine loop? (default: true)
#
#   Default: expression
#                   The value to return when the coroutine is terminated (with
#                   Looping false) or when it is blocking (with Blocking false)
#                   If Default has not been set the coroutine will raise
#                   a CoroutineTerminatedException if invoked after termination.
#
#   DefaultLastValue: bool
#                   When true default value is the last value. (default :false)
#
#   Future: bool    - Compute the future before it is requested (default: false)
#                   NB: 1st call to the coroutine will be synchronous
#
#   Timeout: long   - Timeout (in ms) before a blocking future call will return
#                   Default or raise a CoroutineFutureNotReadyException.
#                   (default: -1 means no timeout)
#
#   ThreadSafe: bool
#                   If true, there will be no lock on the method when Future is
#                   enabled. (default: false)
#
#   Parallel: int	- Number of futures to compute in parallel (default: 1)
#                   UNSUPPORTED		TODO:
#

namespace Coroutine

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

[AttributeUsage(AttributeTargets.Method)]
public class CoroutineAttribute(AbstractAstAttribute):

	Looping as BoolLiteralExpression:
		set:
			_looping = value.Value
	_looping = true

	Default as Expression:
		set:
			_default = value
			_defaultSet = true
	_default as Expression = null
	_defaultSet = false

	DefaultLastValue as BoolLiteralExpression:
		set:
			_defaultLastValue = value.Value
	_defaultLastValue = false

	Future as BoolLiteralExpression:
		set:
			_future = value.Value
	_future = false

	Timeout as IntegerLiteralExpression:
		set:
			_timeout = value.Value
	_timeout = -1L

	ThreadSafe as BoolLiteralExpression:
		set:
			_threadSafe = value.Value
	_threadSafe = false

	Parallel as IntegerLiteralExpression: #TODO: compute parallel futures
		set:
			raise NotSupportedException("parallel futures are not yet supported!")
	_parallel = 1

	final ResetEventImplementationTypeName = "System.Threading.AutoResetEvent"
	final ThreadImplementationTypeName = "System.Threading.Thread"

	_m as Method
	_generatorName as string

	public override def Apply(node as Node):
		_m = node as Method
		SanityCheck()

		_generatorName = "__"+_m.Name

		#enumerator type
		if _m.ReturnType is null:
			_m.ReturnType = SimpleTypeReference("System.Object")
		et = GenericTypeReference()
		et.Name = "System.Collections.Generic.IEnumerator"
		et.GenericArguments.Add(_m.ReturnType)

		eRef = GetReferenceForNewField("__enumerator", et)#ref to enumerator

		if _defaultSet or _defaultLastValue:
			f = CreateField("__default", _m.ReturnType)
			if _defaultSet:
				f.Initializer = _default
			_m.DeclaringType.Members.Add(f)
			dRef = ReferenceExpression(f.Name)#ref to default value

		if _future:
			rRef = GetReferenceForNewField("__reset", SimpleTypeReference(ResetEventImplementationTypeName))
			rtRef = GetReferenceForNewField("__resetThread", SimpleTypeReference(ResetEventImplementationTypeName))
			tRef = GetReferenceForNewField("__thread", SimpleTypeReference(ThreadImplementationTypeName))
			f = CreateField("__lock", SimpleTypeReference("System.Object"))
			f.Initializer = [| System.Object() |]
			_m.DeclaringType.Members.Add(f)
			lRef = ReferenceExpression(f.Name)#ref to lock object

		#generator invocation
		gInvoc = MethodInvocationExpression(MemberReferenceExpression(SelfLiteralExpression(), _generatorName))
		for parameter in _m.Parameters:
			gInvoc.Arguments.Add(ReferenceExpression(parameter.Name))

		facade = _m.Clone() as Method
		facade.IsSynthetic = true

		if not _future:
			facade.Body = [|
				block:
					if $eRef is null:
						$eRef = $gInvoc
			|].Block
			moveNext = [| $(eRef).MoveNext() |]
			if _looping:
				#$(eRef).Reset() not used to handle new arguments passed in at reset
				b = [|
						block:
							if not $moveNext:
								$eRef = $gInvoc
								$moveNext
					|].Block
				facade.Body.Add(b)
			elif _defaultSet or _defaultLastValue:
				b = [|
						block:
							if not $moveNext:
								return $dRef
					|].Block
				facade.Body.Add(b)
			else:
				b = [|
						block:
							if not $moveNext:
								raise CoroutineTerminatedException()
					|].Block
				facade.Body.Add(b)
			if _defaultLastValue:
				b = [|
						block:
							$dRef = $(eRef).Current
							return $dRef
					|].Block
				facade.Body.Add(b)
			else:
				facade.Body.Add([| return $(eRef).Current |])

		#future
		else:
			t = Method(tRef.Name+"__callable")
			t.IsSynthetic = true
			if _m.IsStatic or _m.DeclaringType isa Module:
				t.Modifiers |= TypeMemberModifiers.Static
			t.Modifiers |= TypeMemberModifiers.Private
			t.Body = [|
						block:
							while true:
								$(rtRef).WaitOne($_timeout, false)
								break if not $(eRef).MoveNext()
								$(rRef).Set()
							$tRef = null
							$(rRef).Set()
					|].Block
			_m.DeclaringType.Members.Add(t)
			callableRef = ReferenceExpression(t.Name)

			if _defaultSet:
				facade.Body = [|
					block:
						future as $(_m.ReturnType) = $dRef
				|].Block
			else:
				facade.Body = [|
					block:
						future as $(_m.ReturnType)
				|].Block
			b = [|
					block:
						:init
						if $eRef is null:
							$eRef = $gInvoc
							$rRef = System.Threading.AutoResetEvent(false)
							$rtRef = System.Threading.AutoResetEvent(true)
							$tRef = Thread($callableRef)
							$(tRef).IsBackground = true
							$(tRef).Name = "coroutine ${$(facade.Name)}"
							$(tRef).Start()
						if $tRef is not null and $(tRef).IsAlive:
							gotIt = $(rRef).WaitOne($_timeout, false)
				|].Block
			if _looping:
				b2 = [|
						block:
							if $tRef is null or not $(tRef).IsAlive:
								$eRef = null
								goto init
					|].Block
			elif _defaultSet or _defaultLastValue:
				b2 = [|
						block:
							if $tRef is null or not $(tRef).IsAlive:
								return $dRef
					|].Block
			else:
				b2 = [|
						block:
							if $tRef is null or not $(tRef).IsAlive:
								raise CoroutineTerminatedException()
					|].Block
			b.Add(b2)

			if _defaultSet or _defaultLastValue:
				b2 = [|
						block:
							if not gotIt:
								return $dRef
					|].Block
			else:
				b2 = [|
						block:
							if not gotIt:
								raise CoroutineFutureNotReadyException()
					|].Block
			b.Add(b2)

			if _defaultLastValue:
				b.Add([| $dRef = $(eRef).Current |])
			b.Add([| future = $(eRef).Current |])
			b.Add([| $(rtRef).Set() |])

			if not _threadSafe:
				lockBlock = [|
								block:
									lock $lRef:
										$b
							|].Block
				facade.Body.Add(lockBlock)
			else:
				facade.Body.Add(b)
			facade.Body.Add([| return future |])

		#hide the generator implementation
		_m.Name = _generatorName
		_m.Modifiers |= TypeMemberModifiers.Private
		_m.ReturnType = et

		#promote the facade
		_m.DeclaringType.Members.Add(facade)

	private def CreateField(name as string, type as TypeReference) as Field:
		f = Field()
		f.Name = "${name}${_generatorName}${Context.AllocIndex()}"
		f.IsSynthetic = true
		if _m.IsStatic or _m.DeclaringType isa Module:
			f.Modifiers |= TypeMemberModifiers.Static
		f.Modifiers |= TypeMemberModifiers.Private
		f.Type = type
		return f

	private def GetReferenceForNewField(name as string, type as TypeReference) as ReferenceExpression:
		f = CreateField(name, type)
		_m.DeclaringType.Members.Add(f)
		return ReferenceExpression(f.Name)

	private def SanityCheck():
		if _m is null:
			InvalidNodeForAttribute('Method')
			return
		if _looping and _defaultSet:
			Errors.Add(CompilerErrorFactory.CustomError(self.LexicalInfo, "Looping and Default are mutually exclusive"))
			return
		if _defaultSet and _defaultLastValue:
			Errors.Add(CompilerErrorFactory.CustomError(self.LexicalInfo, "DefaultLastValue and Default are mutually exclusive"))
			return
		if -1L != _timeout and not _future:
			Errors.Add(CompilerErrorFactory.CustomError(self.LexicalInfo, "Timeout requires Future:true"))
			return
		if 1 != _parallel and not _future:
			Errors.Add(CompilerErrorFactory.CustomError(self.LexicalInfo, "Parallel requires Future:true"))
			return

class CoroutineTerminatedException(System.Exception):
	pass

class CoroutineFutureNotReadyException(System.Exception):
	pass