include 'ril.lm'

namespace js_out
	token _IN_ /''/
	token _EX_ /''/

	lex
		token comment /
			'//' any* :> '\n' |
			'/*' any* :>> '*/'
		/

		token id
			/[a-zA-Z_][a-zA-Z_0-9]*/

		token number /
			[0-9]+
		/

		token symbol /
			'!' | '#' | '$' | '%' | '&' | '(' | ')' | '*' |
			'+' | ',' | '-' | '.' | '/' | ':' | ';' | '<' |
			'=' | '>' | '?' | '@' | '[' | ']' | '^' | '|' |
			'~' /

		literal `{ `}

		token string /
				'"' ( [^"\\] | '\\' any ) * '"' |
				"'" ( [^'\\] | '\\' any ) * "'"
			/

		ignore
			/[ \t\v\r\n]+/
	end

	def item
		[comment]
	|	[id]
	|	[number]
	|	[symbol]
	|	[string]
	|	[`{ _IN_ item* _EX_ `} ]

	def js_out
		[_IN_ _EX_ item*]
end

namespace js_gen

	global Parser: parser<js_out::js_out>

	void tok_list( TL: host::tok* )
	{
		for Tok: host::tok in repeat(TL) {
			switch Tok
			case [host::`${ StmtList: stmt* host::`}$] {
				send Parser
					"{
					"	[stmt_list( StmtList )]
					"}
			}
			case [host::`={ Expr: expr host::`}=] {
				send Parser
					"([expr( Expr )])"
			}
			case [E: escape] {
				Str: str = $E
				send Parser
					"[Str.suffix( 1 )]"
			}
			default {
				send Parser
					[Tok]
			}
		}
	}

	void embedded_host( EmbeddedHost: embedded_host )
	{
		switch EmbeddedHost
		case [`host `( string `, uint `) `={ TL: host::tok* host::`}=]
		{
			send Parser
				"([tok_list( TL )])"
		}
		case [`host `( string `, uint `) `${ TL: host::tok* host::`}$]
		{
			send Parser 
				"{
				"	[tok_list( TL )]
				"}
		}
		case [`host `( string `, uint `) `@{ TL: host::tok* host::`}@]
		{
			send Parser
				[tok_list( TL )]
		}
	}

	void expr_factor( ExprFactor: expr_factor )
	{
		switch ExprFactor
		case [EH: embedded_host]
		{
			send Parser
				[embedded_host( EH )]
		}
		case [O:`( TL: expr C: `)]
		{
			send Parser
				[O expr(TL) C]
		}
		case [ident O: `[ TL: expr C: `]]
		{
			send Parser
				[ExprFactor.ident O expr( TL ) C]
		}
		case ['offset' '(' ident ',' expr ')']
		{
			send Parser
				[expr( ExprFactor.expr )]
		}
		case ['deref' '(' ident ',' expr ')']
		{
			send Parser	[ExprFactor.ident]
			if $ExprFactor.ident == 'data'
				send Parser ['.charCodeAt(' expr( ExprFactor.expr ) ')']
			else
				send Parser ['[' expr( ExprFactor.expr ) ']']
		}
		case [T: `TRUE]
		{
			T.data = 'true'
			send Parser [T]
		}
		case [F: `FALSE]
		{
			F.data = 'false'
			send Parser [F]
		}
		case [N: `nil]
		{
			N.data = '-1'
			send Parser [N]
		}
		case [Number: number]
		{
			number( Number )
		}
		case [E1: embedded_host `-> E2: expr_factor]
		{
			embedded_host( E1 )
			expr_factor( E2 )
		}
		case [`cast Open: `( type Close: `) expr_factor]
		{
			expr_factor( ExprFactor._expr_factor )
		}
		default {
			# Catches cases not specified
			send Parser [ExprFactor]
		}
	}

	void lvalue( ExprFactor: lvalue )
	{
		switch ExprFactor
		case [EH: embedded_host]
		{
			send Parser
				[embedded_host( EH )]
		}
		case [ident O: `[ TL: expr C: `]]
		{
			send Parser
				[ExprFactor.ident O expr( TL ) C]
		}
		case [I: ident `[ E: expr `] `. F: ident]
		{
			send Parser
				"[I]\[[ expr( E )]\].[F]
		}
		case [E1: embedded_host `-> E2: lvalue]
		{
			# The accessor operator is contained wihtin the lhs.
			embedded_host( E1 )
			lvalue( E2 )
		}
		default {
			# Catches cases not specified
			send Parser [ExprFactor]
		}
	}

	void expr_factor_op( ExprFactorOp: expr_factor_op )
	{
		switch ExprFactorOp
		case [B: `! expr_factor_op]
		{
			send Parser [B]
			expr_factor_op( ExprFactorOp._expr_factor_op )
		}
		case [T: `~ expr_factor_op]
		{
			send Parser [T]
			expr_factor_op( ExprFactorOp._expr_factor_op )
		}
		case [expr_factor]
		{
			expr_factor( ExprFactorOp.expr_factor )
		}
	}

	void expr_bitwise( ExprBitwise: expr_bitwise )
	{
		switch ExprBitwise
		case [expr_bitwise A: `& expr_factor_op]
		{
			expr_bitwise( ExprBitwise._expr_bitwise )
			send Parser [A]
			expr_factor_op( ExprBitwise.expr_factor_op )
		}
		case [expr_factor_op]
		{
			expr_factor_op( ExprBitwise.expr_factor_op )
		}
	}

	void expr_mult( ExprMult: expr_mult )
	{
		switch ExprMult
		case [expr_mult T: `* expr_bitwise]
		{
			expr_mult( ExprMult._expr_mult )
			send Parser [T]
			expr_bitwise( ExprMult.expr_bitwise )
		}
		case [expr_bitwise]
		{
			expr_bitwise( ExprMult.expr_bitwise )
		}
	}

	void expr_add( ExprAdd: expr_add )
	{
		switch ExprAdd
		case [expr_add Op: add_op expr_mult]
		{
			expr_add( ExprAdd._expr_add )
			send Parser [Op]
			expr_mult( ExprAdd.expr_mult )
		}
		case [expr_mult]
		{
			expr_mult( ExprAdd.expr_mult )
		}
	}

	void expr_shift( ExprShift: expr_shift )
	{
		switch ExprShift
		case [expr_shift Op: shift_op expr_add]
		{
			expr_shift( ExprShift._expr_shift )
			send Parser [Op]
			expr_add( ExprShift.expr_add )
		}
		case [expr_add]
		{
			expr_add( ExprShift.expr_add )
		}
	}

	void expr_test_op( Op: test_op )
	{
		switch Op
		case [ `== ]
			send Parser '==='
		case [ `!= ]
			send Parser '!=='
		default
			send Parser [Op]
	}

	void expr_test( ExprTest: expr_test )
	{
		switch ExprTest
		case [expr_test test_op expr_shift]
		{
			expr_test( ExprTest._expr_test )
			expr_test_op( ExprTest.test_op )
			expr_shift( ExprTest.expr_shift )
		}
		case [expr_shift]
		{
			expr_shift( ExprTest.expr_shift )
		}
	}

	void expr( Expr: expr )
	{
		expr_test( Expr.expr_test )
	}

	void number( Number: number )
	{
		switch Number
		case [`u `( uint `) ]
			send Parser [Number.uint]
		default
			send Parser [Number]
	}

	void type( Type: type )
	{
		switch Type
		case 'u8'
			send Parser 'Uint8'
		case 'u16'
			send Parser 'Uint16'
		case 'u32'
			send Parser 'Uint32'
		case 's8'
			send Parser 'Int8'
		case 's16'
			send Parser 'Int16'
		case 's32'
			send Parser 'Int32'
		default
			send Parser 'Float64'
	}

	void num_list( NumList: num_list )
	{
		number( NumList.number )
		for CommaNum: comma_num in NumList {
			send Parser [', ']
			number( CommaNum.number )
		}
	}

	void stmt( Stmt: stmt )
	{
		switch Stmt
		case [EH: embedded_host]
		{
			send Parser
				[embedded_host( EH )]
		}
		case [A: static_array] {
			send Parser
				"var [A.ident] = new [type(A.type)]Array(\[ [num_list(A.num_list)]\]);
		}
		case [V: static_value] {
			send Parser
				"var [V.ident] = [V.number];
		}
		case [
				'if' O: `( IfExpr: expr C: `) IfStmt: stmt
				ElseIfClauseList: else_if_clause* ElseClauseOpt: else_clause?
		] {
			send Parser
				"if ( [expr(IfExpr)] )
				"	[stmt(IfStmt)]

			for ElseIfClause: else_if_clause in repeat( ElseIfClauseList ) {
				match ElseIfClause
					['else if (' ElseIfExpr: expr ')' ElseIfStmt: stmt]

				send Parser
					"else if ( [expr(ElseIfExpr)] )
					"	[stmt(ElseIfStmt)]
			}

			if ( match ElseClauseOpt ['else' ElseStmt: stmt] ) {
				send Parser
					"else
					"	[stmt(ElseStmt)]
			}
		}
		case ['while' '(' WhileExpr: expr ')' WhileStmt: stmt] {
			send Parser
				"while ( [expr(WhileExpr)] )
				"	[stmt(WhileStmt)]
		}
		case ['switch' '(' SwitchExpr: expr ')' '{' StmtList: stmt* '}'] {
			send Parser
				"switch ( [expr(SwitchExpr)] ) {
				"	[stmt_list(StmtList)]
				"}
		}
		case [ExprExpr: expr Semi: `;] {
			send Parser
				[expr(ExprExpr) Semi]
		}
		case [L: `{ TL: stmt* R: `}] {
			send Parser
				"{
				"	[stmt_list(TL)]
				"}
		}
		case [
			TypeList: opt_const Type: type
			Ident: ident OptInit: opt_init Semi: `;
		]
		{
			send Parser
				"var [Ident]

			if match OptInit [E: `= expr] {
				send Parser
					[E expr(OptInit.expr)]
			}

			send Parser
				[Semi]
		}
		case [Export: export_stmt]
		{
			send Parser
				"var [Export.ident] = [number(Export.number)];
		}
		case ['fallthrough' ';']
		{
			# Nothing needed here.
		}
		case [Index: index_stmt]
		{
			send Parser
				"var [Index.ident]"

			if match Index.opt_init [E: `= expr] {
				send Parser
					[E expr(Index.opt_init.expr)]
			}

			send Parser
				";
		}
		case [case_block]
		{
			send Parser
				"case [expr( Stmt.case_block.expr )]:
				"[stmt_list( Stmt.case_block._repeat_stmt )]
				"break;
		}
		case [default_block]
		{
			send Parser
				"default:
				"[stmt_list( Stmt.default_block._repeat_stmt )]
				"break;
		}
		case [goto_label] { }
		case [G: goto_stmt] { }
		case [AS: assign_stmt]
		{
			send Parser
				"[lvalue(AS.LValue) AS.assign_op expr(AS.expr)];
		}
		default {
			# catches unspecified cases
			send Parser [Stmt]
		}
	}

	void stmt_list( StmtList: stmt* )
	{
		for Stmt: stmt in repeat( StmtList )
			stmt( Stmt )
	}

	void trans( Output: stream, Start: start )
	{
		Parser = new parser<js_out::js_out>()

		send Parser
			"'use strict';

		if ( Start.opt_bom.bom )
			send Output [Start.opt_bom.bom]

		stmt_list( Start._repeat_stmt )

		CO: js_out::js_out = Parser->finish()

		if CO {
			send Output
				[CO]
		}
		else {
			send stderr
				"failed to parse output: [Parser->error]
		}
	}
end

void trans( Output: stream, Start: start )
{
	js_gen::trans( Output, Start )
}

include 'rlhc-main.lm'
