use strict;

package multibyte;

sub size {
	my($value) = @_;
	my $size;
	for ($size = 1; $value >= 0x80; $value >>= 7) {
		$size ++;
	}
	return $size;
}

###############################################################################

package asm;

use constant INTEGER_8		=> 0;
use constant INTEGER_16		=> 1;
use constant INTEGER_32		=> 2;
use constant FLOAT_32		=> 3;
use constant UTF8_STRING	=> 4;
use constant EMPTY_STRING	=> 5;
use constant STRING			=> 6;

use vars qw($OUT $VERBOSE);

sub _put_mb {
	my($value) = @_;
	my $tmp = chr($value & 0x7f);
	for ($value >>= 7; $value != 0; $value >>= 7) {
		$tmp = chr(0x80 | ($value & 0x7f)) . $tmp;
	}
	print $OUT $tmp;
}

sub _put_uint8 {
	my($value) = @_;
	print $OUT chr $value;
}

sub _put_int8 {
	my($value) = @_;
	print $OUT pack "c", $value;
}

sub _put_uint16 {
	my($value) = @_;
	print $OUT pack "n", $value;
}

sub _put_int16 {
	my($value) = @_;
	print $OUT pack "n", unpack "v", pack "s", $value;
}

sub _put_int32 {
	my($value) = @_;
	print $OUT pack "N", unpack "V", pack "l", $value;
}

sub _put_float32 {
	my($value) = @_;
	print $OUT pack "f", $value;
}

sub _put_string {
	my($value) = @_;
	print $OUT $value;
}

my @mnemo = (
	"?",
	"JUMP_FW",
	"JUMP_FW_W",
	"JUMP_BW",
	"JUMP_BW_W",
	"TJUMP_FW",
	"TJUMP_FW_W",
	"TJUMP_BW",
	"TJUMP_BW_W",
	"CALL",
	"CALL_LIB",
	"CALL_LIB_W",
	"CALL_URL",
	"CALL_URL_W",
	"LOAD_VAR",
	"STORE_VAR",
	"INCR_VAR",
	"DECR_VAR",
	"LOAD_CONST",
	"LOAD_CONST_W",
	"CONST_0",
	"CONST_1",
	"CONST_M1",
	"CONST_ES",
	"CONST_INVALID",
	"CONST_TRUE",
	"CONST_FALSE",
	"INCR",
	"DECR",
	"ADD_ASG",
	"SUB_ASG",
	"UMINUS",
	"ADD",
	"SUB",
	"MUL",
	"DIV",
	"IDIV",
	"REM",
	"B_AND",
	"B_OR",
	"B_XOR",
	"B_NOT",
	"B_LSHIFT",
	"B_RSSHIFT",
	"B_RSZSHIFT",
	"EQ",
	"LE",
	"LT",
	"GE",
	"GT",
	"NE",
	"NOT",
	"SCAND",
	"SCOR",
	"TOBOOL",
	"POP",
	"TYPEOF",
	"ISVALID",
	"RETURN",
	"RETURN_ES",
	"DEBUG",
	"?",
	"?",
	"?",
	"STORE_VAR_S",
	"?",
	"?",
	"?",
	"?",
	"?",
	"?",
	"?",
	"?",
	"?",
	"?",
	"?",
	"?",
	"?",
	"?",
	"?",
	"LOAD_CONST_S",
	"?",
	"?",
	"?",
	"?",
	"?",
	"?",
	"?",
	"?",
	"?",
	"?",
	"?",
	"?",
	"?",
	"?",
	"?",
	"CALL_S",
	"?",
	"?",
	"?",
	"?",
	"?",
	"?",
	"?",
	"CALL_LIB_S",
	"?",
	"?",
	"?",
	"?",
	"?",
	"?",
	"?",
	"INCR_VAR_S",
	"?",
	"?",
	"?",
	"?",
	"?",
	"?",
	"?",
	"?",
	"?",
	"?",
	"?",
	"?",
	"?",
	"?",
	"?",
	"JUMP_FW_S",
	"?",
	"?",
	"?",
	"?",
	"?",
	"?",
	"?",
	"?",
	"?",
	"?",
	"?",
	"?",
	"?",
	"?",
	"?",
	"?",
	"?",
	"?",
	"?",
	"?",
	"?",
	"?",
	"?",
	"?",
	"?",
	"?",
	"?",
	"?",
	"?",
	"?",
	"?",
	"JUMP_BW_S",
	"?",
	"?",
	"?",
	"?",
	"?",
	"?",
	"?",
	"?",
	"?",
	"?",
	"?",
	"?",
	"?",
	"?",
	"?",
	"?",
	"?",
	"?",
	"?",
	"?",
	"?",
	"?",
	"?",
	"?",
	"?",
	"?",
	"?",
	"?",
	"?",
	"?",
	"?",
	"TJUMP_FW_S",
	"?",
	"?",
	"?",
	"?",
	"?",
	"?",
	"?",
	"?",
	"?",
	"?",
	"?",
	"?",
	"?",
	"?",
	"?",
	"?",
	"?",
	"?",
	"?",
	"?",
	"?",
	"?",
	"?",
	"?",
	"?",
	"?",
	"?",
	"?",
	"?",
	"?",
	"?",
	"LOAD_VAR_S",
	"?",
	"?",
	"?",
	"?",
	"?",
	"?",
	"?",
	"?",
	"?",
	"?",
	"?",
	"?",
	"?",
	"?",
	"?",
	"?",
	"?",
	"?",
	"?",
	"?",
	"?",
	"?",
	"?",
	"?",
	"?",
	"?",
	"?",
	"?",
	"?",
	"?",
	"?"
);

sub asmOpcode1 {
	my($bytecode) = @_;
	print $VERBOSE sprintf("%-14s\t", $mnemo[$bytecode])
			if (defined $VERBOSE);
	_put_uint8($bytecode);
}

sub asmOpcode1s {
	my($bytecode,$offset) = @_;
	print $VERBOSE sprintf("%-14s%7u\t", $mnemo[$bytecode], $offset)
			if (defined $VERBOSE);
	_put_uint8(($bytecode | $offset));
}

sub asmOpcode2 {
	my($bytecode,$offset) = @_;
	#  LOAD_CONST
	print $VERBOSE sprintf("%-14s%7u\t", $mnemo[$bytecode], $offset)
			if (defined $VERBOSE);
	_put_uint8($bytecode);
	_put_uint8($offset);
}

sub asmOpcode2s {
	my($bytecode,$idx1,$idx2) = @_;
	#  CALL_LIB_S
	print $VERBOSE sprintf("%-14s%7u%7u\t", $mnemo[$bytecode], $idx1, $idx2)
			if (defined $VERBOSE);
	_put_uint8($bytecode | $idx1);
	_put_uint8($idx2);
}

sub asmOpcode3 {
	my($bytecode,$idx1,$idx2) = @_;
	#  CALL_LIB
	print $VERBOSE sprintf("%-14s%7u%7u\t", $mnemo[$bytecode], $idx1, $idx2)
			if (defined $VERBOSE);
	_put_uint8($bytecode);
	_put_uint8($idx1);
	_put_uint8($idx2);
}

sub asmOpcode3w {
	my($bytecode,$offset) = @_;
	#  LOAD_CONST_W, JUMP_xW_W
	print $VERBOSE sprintf("%-14s%7u\t", $mnemo[$bytecode], $offset)
			if (defined $VERBOSE);
	_put_uint8($bytecode);
	_put_uint16($offset);
}

sub asmOpcode4 {
	my($bytecode,$idx1,$idx2,$idx3) = @_;
	#  CALL_URL
	print $VERBOSE sprintf("%-14s%7u%7u%7u\t", $mnemo[$bytecode], $idx1, $idx2, $idx3)
			if (defined $VERBOSE);
	_put_uint8($bytecode);
	_put_uint8($idx1);
	_put_uint8($idx2);
	_put_uint8($idx3);
}

sub asmOpcode4w {
	my($bytecode,$idx1,$idx2) = @_;
	#  CALL_LIB_W
	print $VERBOSE sprintf("%-14s%7u%7u\t", $mnemo[$bytecode], $idx1, $idx2)
			if (defined $VERBOSE);
	_put_uint8($bytecode);
	_put_uint8($idx1);
	_put_uint16($idx2);
}

sub asmOpcode6 {
	my($bytecode,$idx1,$idx2,$idx3) = @_;
	#  CALL_URL_W
	print $VERBOSE sprintf("%-14s%7u%7u%7u\t", $mnemo[$bytecode], $idx1, $idx2, $idx3)
			if (defined $VERBOSE);
	_put_uint8($bytecode);
	_put_uint16($idx1);
	_put_uint16($idx2);
	_put_uint8($idx3);
}

sub asmByte {
	my($str,$value) = @_;
	print $VERBOSE "$str $value\n"
			if (defined $VERBOSE);
	_put_uint8($value);
}

sub asmMultiByte {
	my($str,$value) = @_;
	print $VERBOSE "$str $value\n"
			if (defined $VERBOSE);
	_put_mb($value);
}

sub asmFunctionName {
	my($idx,$name) = @_;
	my $len = length $name;
	print $VERBOSE "$idx\t[$len]\t$name\n"
			if (defined $VERBOSE);
	_put_uint8($idx);
	_put_uint8($len);
	_put_string($name);
}

sub asmPragma1 {
	my($type,$value1) = @_;
	print $VERBOSE sprintf("prag%7u%7u\n", $type, $value1)
			if (defined $VERBOSE);
	_put_uint8($type);
	_put_mb($value1);
}

sub asmPragma2 {
	my($type,$value1,$value2) = @_;
	print $VERBOSE sprintf("prag%7u%7u%7u\n", $type, $value1, $value2)
			if (defined $VERBOSE);
	_put_uint8($type);
	_put_mb($value1);
	_put_mb($value2);
}

sub asmPragma3 {
	my($type,$value1,$value2,$value3) = @_;
	print $VERBOSE sprintf("prag%7u%7u%7u%7u\n", $type, $value1, $value2, $value3)
			if (defined $VERBOSE);
	_put_uint8($type);
	_put_mb($value1);
	_put_mb($value2);
	_put_mb($value3);
}

sub asmConstantInteger8 {
	my($idx,$value) = @_;
	print $VERBOSE sprintf("cst%-7u%7u%7d\n", $idx, INTEGER_8, $value)
			if (defined $VERBOSE);
	_put_uint8(INTEGER_8);
	_put_int8($value);
}

sub asmConstantInteger16 {
	my($idx,$value) = @_;
	print $VERBOSE sprintf("cst%-7u%7u%7d\n", $idx, INTEGER_16, $value)
			if (defined $VERBOSE);
	_put_uint8(INTEGER_16);
	_put_int16($value);
}

sub asmConstantInteger32 {
	my($idx,$value) = @_;
	print $VERBOSE sprintf("cst%-7u%7u%7d\n", $idx, INTEGER_32, $value)
			if (defined $VERBOSE);
	_put_uint8(INTEGER_32);
	_put_int32($value);
}

sub asmConstantFloat32 {
	my($idx,$value) = @_;
	print $VERBOSE sprintf("cst%-7u%7u %f\n", $idx, FLOAT_32, $value)
			if (defined $VERBOSE);
	_put_uint8(FLOAT_32);
	_put_float32($value);
}

sub asmConstantStringUTF8 {
	my($idx,$value) = @_;
	my $len = length $value;
	print $VERBOSE sprintf("cst%-7u%7u\t[%u]\t%s\n", $idx, UTF8_STRING, $len, $value)
			if (defined $VERBOSE);
	_put_uint8(UTF8_STRING);
	_put_mb($len);
	_put_string($value);
}

sub asmConstantString {
	my($idx,$value) = @_;
	my $len = length $value;
	print $VERBOSE sprintf("cst%-7u%7u\t[%u]\t%s\n", $idx, STRING, $len, $value)
			if (defined $VERBOSE);
	_put_uint8(STRING);
	_put_mb($len);
	_put_string($value);
}

sub asmComment {
	my($comment) = @_;
	if (defined $comment) {
		print $VERBOSE "; $comment\n"
				if (defined $VERBOSE);
	} else {
		print $VERBOSE "\n"
				if (defined $VERBOSE);
	}
}

###############################################################################

package verbose;

use vars qw($_Lineno);

sub Init {
	my($filename) = @_;
	$_Lineno = 0;
	open IN,$filename
		or die "can't open $filename ($!).\n";
}

sub Source {
	my($opcode) = @_;
	if (defined $asm::VERBOSE) {
		my $lineno = $opcode->{Lineno};
		while ($lineno > $_Lineno) {
			my $line = <IN>;
			print $asm::VERBOSE sprintf(";line:%5d;\t", $_Lineno + 1);
			print $asm::VERBOSE $line if ($line);
			$_Lineno ++;
		}
	}
}

sub End {
	close IN;
}

###############################################################################

package constantVisitor;

use Carp;

use constant INT8_MIN	=>   -128;
use constant INT8_MAX	=>    127;
use constant INT16_MIN	=> -32768;
use constant INT16_MAX	=>  32767;

sub new {
	my $proto = shift;
	my $class = ref($proto) || $proto;
	my $self = {};
	bless($self, $class);
	my($parser) = @_;
	$self->{parser} = $parser;
	$self->{nb} = 0;
	$self->{size} = 0;
	$self->{action} = 0;
	$self->{cst} = {
		TYPE_INTEGER		=> {},
		TYPE_FLOAT			=> {},
		TYPE_STRING			=> {},
		TYPE_UTF8_STRING	=> {}
	};
	return $self;
}

sub visitUrl {
	my $self = shift;
	my($opcode) = @_;
	my $def = $opcode->{Definition};
	if ($def->{NbUse} == 0) {
		unless ($self->{action}) {
			$self->{parser}->genWarning($opcode,"Unreferenced url - $def->{Symbol}.\n");
		}
	} else {
		unless ($self->{action}) {
			$def->{Index} = $self->{nb};
		}
		$opcode->{Value}->visit($self);		# LOAD_CONST
	}
}

sub visitAccessDomain {
	my $self = shift;
	my($opcode) = @_;
	$opcode->{Value}->visit($self);			# LOAD_CONST
}

sub visitAccessPath {
	my $self = shift;
	my($opcode) = @_;
	$opcode->{Value}->visit($self);			# LOAD_CONST
}

sub visitMetaName {}

sub visitMetaHttpEquiv {}

sub visitMetaUserAgent {
	my $self = shift;
	my($opcode) = @_;
	$opcode->{Value}->visit($self);			# LOAD_CONST
}

sub visitFunction {
	my $self = shift;
	my($opcode) = @_;
	$opcode->{Value}->visitActive($self)
			if (defined $opcode->{Value});
}

sub visitLoadVar {}

sub visitStoreVar {}

sub visitIncrVar {}

sub visitDecrVar {}

sub visitAddAsg {}

sub visitSubAsg {}

sub visitLabel {}

sub visitPop {}

sub visitToBool {}

sub visitScOr {}

sub visitScAnd {}

sub visitReturn {}

sub visitReturnES {}

sub visitCall {}

sub visitCallLib {}

sub visitCallUrl {
	my $self = shift;
	my($opcode) = @_;
	my $def = $opcode->{Definition};
	my $value = $def->{FunctionName};
	unless ($self->{action}) {
		if (exists $self->{cst}->{TYPE_UTF8_STRING}{$value}) {
			$opcode->{Index} = $self->{cst}->{TYPE_UTF8_STRING}{$value};
			$opcode->{Doublon} = 1;
			return;
		}
	}
	if ($self->{action}) {
		asm::asmConstantString($opcode->{Index},$value)
				unless (exists $opcode->{Doublon});
	} else {
		$opcode->{Index} = $self->{nb};
		$self->{cst}->{TYPE_UTF8_STRING}{$value} = $self->{nb};
		$self->{size} += 1;
		$self->{size} += multibyte::size(length $value);
		$self->{size} += length $value;
		$self->{nb} += 1;
	}
}

sub visitJump {}

sub visitFalseJump {}

sub visitUnaryOp {}

sub visitBinaryOp {}

sub visitLoadConst {
	my $self = shift;
	my($opcode) = @_;
	my $type = $opcode->{TypeDef};
	if      ($type eq 'TYPE_INTEGER') {
		$self->{parser}->checkRangeInteger($opcode, $opcode->{Value});
	} elsif ($type eq 'TYPE_FLOAT') {
		$self->{parser}->checkRangeFloat($opcode, $opcode->{Value});
	}
	$type = $opcode->{TypeDef};
	if (	   $type eq 'TYPE_BOOLEAN'
			or $type eq 'TYPE_INVALID' ) {
		return;
	}
	my $value = $opcode->{Value};
	unless ($self->{action}) {
		if (exists $self->{cst}->{$type}{$value}) {
			$opcode->{Index} = $self->{cst}->{$type}{$value};
			$opcode->{Doublon} = 1;
			return;
		}
	}
	if      ($type eq 'TYPE_INTEGER') {
		return if ($value >= -1 and $value <= 1);
		if ($value >= INT8_MIN and $value <= INT8_MAX) {
			if ($self->{action}) {
				asm::asmConstantInteger8($opcode->{Index},$value)
						unless (exists $opcode->{Doublon});
			} else {
				$opcode->{Index} = $self->{nb};
				$self->{cst}->{TYPE_INTEGER}{$value} = $self->{nb};
				$self->{size} += 2;
				$self->{nb} += 1;
			}
		} elsif ($value >= INT16_MIN and $value <= INT16_MAX) {
			if ($self->{action}) {
				asm::asmConstantInteger16($opcode->{Index},$value)
						unless (exists $opcode->{Doublon});
			} else {
				$opcode->{Index} = $self->{nb};
				$self->{cst}->{TYPE_INTEGER}{$value} = $self->{nb};
				$self->{size} += 3;
				$self->{nb} += 1;
			}
		} else {
			if ($self->{action}) {
				asm::asmConstantInteger32($opcode->{Index},$value)
						unless (exists $opcode->{Doublon});
			} else {
				$opcode->{Index} = $self->{nb};
				$self->{cst}->{TYPE_INTEGER}{$value} = $self->{nb};
				$self->{size} += 5;
				$self->{nb} += 1;
			}
		}
	} elsif ($type eq 'TYPE_FLOAT') {
		if ($self->{action}) {
			asm::asmConstantFloat32($opcode->{Index},$value)
					unless (exists $opcode->{Doublon});
		} else {
			$opcode->{Index} = $self->{nb};
			$self->{cst}->{TYPE_FLOAT}{$value} = $self->{nb};
			$self->{size} += 5;
			$self->{nb} += 1;
		}
	} elsif ($type eq 'TYPE_UTF8_STRING') {
		return unless (length $value);
		if ($self->{action}) {
			asm::asmConstantStringUTF8($opcode->{Index},$value)
					unless (exists $opcode->{Doublon});
		} else {
			$opcode->{Index} = $self->{nb};
			$self->{cst}->{TYPE_UTF8_STRING}{$value} = $self->{nb};
			$self->{size} += 1;
			$self->{size} += multibyte::size(length $value);
			$self->{size} += length $value;
			$self->{nb} += 1;
		}
	} elsif ($type eq 'TYPE_STRING') {
		return unless (length $value);
		if ($self->{action}) {
			asm::asmConstantString($opcode->{Index},$value)
					unless (exists $opcode->{Doublon});
		} else {
			$opcode->{Index} = $self->{nb};
			$self->{cst}->{TYPE_STRING}{$value} = $self->{nb};
			$self->{size} += 1;
			$self->{size} += multibyte::size(length $value);
			$self->{size} += length $value;
			$self->{nb} += 1;
		}
	} else {
		croak "INTERNAL ERROR in constantVisitor::visitLoadConst $type $value\n";
	}
}

###############################################################################

package pragmaVisitor;

use constant ACCESS_DOMAIN						=> 0;
use constant ACCESS_PATH						=> 1;
use constant USER_AGENT_PROPERTY				=> 2;
use constant USER_AGENT_PROPERTY_AND_SCHEME		=> 3;

sub new {
	my $proto = shift;
	my $class = ref($proto) || $proto;
	my $self = {};
	bless($self, $class);
	my($parser) = @_;
	$self->{parser} = $parser;
	$self->{nb} = 0;
	$self->{size} = 0;
	$self->{action} = 0;
	return $self;
}

sub visitUrl {}

sub visitAccessDomain {
	my $self = shift;
	my($opcode) = @_;
	my $pragma = $opcode->{Value};
	my $value = $pragma->{OpCode}->{Index};
	if ($self->{action}) {
		verbose::Source($opcode);
		asm::asmPragma1(ACCESS_DOMAIN,$value);
	} else {
		$self->{size} += 1;
		$self->{size} += multibyte::size($value);
		$self->{nb} += 1;
	}
}

sub visitAccessPath {
	my $self = shift;
	my($opcode) = @_;
	my $pragma = $opcode->{Value};
	my $value = $pragma->{OpCode}->{Index};
	if ($self->{action}) {
		verbose::Source($opcode);
		asm::asmPragma1(ACCESS_PATH,$value);
	} else {
		$self->{size} += 1;
		$self->{size} += multibyte::size($value);
		$self->{nb} += 1;
	}
}

sub visitMetaName {}

sub visitMetaHttpEquiv {}

sub visitMetaUserAgent {
	my $self = shift;
	my($opcode) = @_;
	my $pragma1 = $opcode->{Value};
	my $value1 = $pragma1->{OpCode}->{Index};
	my $pragma2 = $pragma1->{Next};
	my $value2 = $pragma2->{OpCode}->{Index};
	my $pragma3 = $pragma2->{Next};
	if (defined $pragma3) {
		my $value3 = $pragma3->{OpCode}->{Index};
		if ($self->{action}) {
			verbose::Source($opcode);
			asm::asmPragma3(USER_AGENT_PROPERTY_AND_SCHEME,$value1,$value2,$value3);
		} else {
			$self->{size} += 1;
			$self->{size} += multibyte::size($value1);
			$self->{size} += multibyte::size($value2);
			$self->{size} += multibyte::size($value3);
			$self->{nb} += 1;
		}
	} else {
		if ($self->{action}) {
			verbose::Source($opcode);
			asm::asmPragma2(USER_AGENT_PROPERTY,$value1,$value2);
		} else {
			$self->{size} += 1;
			$self->{size} += multibyte::size($value1);
			$self->{size} += multibyte::size($value2);
			$self->{nb} += 1;
		}
	}
}

###############################################################################

package codeVisitor;

use Carp;

use constant JUMP_FW_S		=> 0x80;
use constant JUMP_FW		=> 0x01;
use constant JUMP_FW_W		=> 0x02;
use constant JUMP_BW_S		=> 0xA0;
use constant JUMP_BW		=> 0x03;
use constant JUMP_BW_W		=> 0x04;
use constant TJUMP_FW_S		=> 0xC0;
use constant TJUMP_FW		=> 0x05;
use constant TJUMP_FW_W		=> 0x06;
use constant TJUMP_BW		=> 0x07;
use constant TJUMP_BW_W		=> 0x08;
use constant CALL_S			=> 0x60;
use constant CALL			=> 0x09;
use constant CALL_LIB_S		=> 0x68;
use constant CALL_LIB		=> 0x0A;
use constant CALL_LIB_W		=> 0x0B;
use constant CALL_URL		=> 0x0C;
use constant CALL_URL_W		=> 0x0D;
use constant LOAD_VAR_S		=> 0xE0;
use constant LOAD_VAR		=> 0x0E;
use constant STORE_VAR_S	=> 0x40;
use constant STORE_VAR		=> 0x0F;
use constant INCR_VAR_S		=> 0x70;
use constant INCR_VAR		=> 0x10;
use constant DECR_VAR		=> 0x11;
use constant LOAD_CONST_S	=> 0x50;
use constant LOAD_CONST		=> 0x12;
use constant LOAD_CONST_W	=> 0x13;
use constant CONST_0		=> 0x14;
use constant CONST_1		=> 0x15;
use constant CONST_M1		=> 0x16;
use constant CONST_ES		=> 0x17;
use constant CONST_INVALID	=> 0x18;
use constant CONST_TRUE		=> 0x19;
use constant CONST_FALSE	=> 0x1A;
use constant INCR			=> 0x1B;
use constant DECR			=> 0x1C;
use constant ADD_ASG		=> 0x1D;
use constant SUB_ASG		=> 0x1E;
use constant UMINUS			=> 0x1F;
use constant ADD			=> 0x20;
use constant SUB			=> 0x21;
use constant MUL			=> 0x22;
use constant DIV			=> 0x23;
use constant IDIV			=> 0x24;
use constant REM			=> 0x25;
use constant B_AND			=> 0x26;
use constant B_OR			=> 0x27;
use constant B_XOR			=> 0x28;
use constant B_NOT			=> 0x29;
use constant B_LSHIFT		=> 0x2A;
use constant B_RSSHIFT		=> 0x2B;
use constant B_RSZSHIFT		=> 0x2C;
use constant _EQ			=> 0x2D;
use constant _LE			=> 0x2E;
use constant _LT			=> 0x2F;
use constant _GE			=> 0x30;
use constant _GT			=> 0x31;
use constant _NE			=> 0x32;
use constant NOT			=> 0x33;
use constant SCAND			=> 0x34;
use constant SCOR			=> 0x35;
use constant TOBOOL			=> 0x36;
use constant POP			=> 0x37;
use constant TYPEOF			=> 0x38;
use constant ISVALID		=> 0x39;
use constant RETURN			=> 0x3A;
use constant RETURN_ES		=> 0x3B;
use constant DEBUG			=> 0x3C;

use constant UINT3_MAX		=> 7;
use constant UINT4_MAX		=> 15;
use constant UINT5_MAX		=> 31;
use constant UINT8_MAX		=> 255;
use constant UINT16_MAX		=> 65535;

sub new {
	my $proto = shift;
	my $class = ref($proto) || $proto;
	my $self = {};
	bless($self, $class);
	my($parser) = @_;
	$self->{parser} = $parser;
	$self->{nb} = 0;
	$self->{size} = 0;
	$self->{action} = 0;
	return $self;
}

sub visitFunction {
	my $self = shift;
	my($opcode) = @_;
	my $func = $opcode->{Value};
	my $def = $opcode->{Definition};
	my $save_size = $self->{size};
	$self->{size} = 0;
	if ($self->{action}) {
		my $FunctionSize = $opcode->{Index};
		asm::asmComment("function prologue");
		asm::asmByte("NumberOfArguments", $def->{NumberOfArguments});
		asm::asmByte("NumberOfLocalVariables", $def->{NumberOfLocalVariables});
		asm::asmMultiByte("FunctionSize", $FunctionSize);
		asm::asmComment("function code");
		$func->visitActive($self)
				if (defined $func);
		verbose::Source($opcode);
	} else {
		my $nb = $self->_indexeVariables($func,$def->{NumberOfArguments});
		if ($nb > UINT8_MAX) {
			$self->{parser}->genError($opcode, "too many variables");
		} else {
			$def->{NumberOfLocalVariables} = $nb - $def->{NumberOfArguments};
			my $func_size;
			do {
				$func_size = $self->{size};
				$self->{size} = 0;
				$func->visitActive($self)
						if (defined $func);
#				print "size : $self->{size}\n";
			}
			while (     $func_size != $self->{size}
					and !exists $self->{parser}->YYData->{nb_error} );
		}
		$opcode->{Index} = $self->{size};
	}
	$self->{size} = $save_size;
	$self->{size} += 2;
	$self->{size} += multibyte::size($opcode->{Index});
	$self->{size} += $opcode->{Index};
}

sub _indexeVariables {
	my $self = shift;
	my($func,$nb_args) = @_;
	my $idx = $nb_args;
	if (defined $func) {
		for (my $node = $func->getFirstActive(); defined $node; $node = $node->getNextActive()) {
			my $opcode = $node->{OpCode};
			if (	   $opcode->isa('LoadVar')
					or $opcode->isa('StoreVar')
					or $opcode->isa('IncrVar')
					or $opcode->isa('DecrVar')
					or $opcode->isa('AddAsg')
					or $opcode->isa('SubAsg') ) {
				my $def = $opcode->{Definition};
				if ($def->{ID} == 0xffff) {
					$def->{ID} = $idx;
					$idx ++;
				}
			}
		}
	}
	return $idx;
}

sub visitLoadVar {
	my $self = shift;
	my($opcode) = @_;
	if ($self->{action}) {
		verbose::Source($opcode);
	}
	my $def = $opcode->{Definition};
	my $vindex = $def->{ID};
	croak "INTERNAL ERROR in codeVisitor::visitDecrVar\n"
			unless ($vindex <= UINT8_MAX);
	if ($vindex <= UINT5_MAX) {
		if ($self->{action}) {
			asm::asmOpcode1s(LOAD_VAR_S, $vindex);
		}
		$self->{size} += 1;
	} else {
		if ($self->{action}) {
			asm::asmOpcode2(LOAD_VAR, $vindex);
		}
		$self->{size} += 2;
	}
	if ($self->{action}) {
		asm::asmComment($def->{Symbol});
	}
}

sub visitStoreVar {
	my $self = shift;
	my($opcode) = @_;
	if ($self->{action}) {
		verbose::Source($opcode);
	}
	my $def = $opcode->{Definition};
	my $vindex = $def->{ID};
	croak "INTERNAL ERROR in codeVisitor::visitDecrVar\n"
			unless ($vindex <= UINT8_MAX);
	if ($vindex <= UINT4_MAX) {
		if ($self->{action}) {
			asm::asmOpcode1s(STORE_VAR_S, $vindex);
		}
		$self->{size} += 1;
	} else {
		if ($self->{action}) {
			asm::asmOpcode2(STORE_VAR, $vindex);
		}
		$self->{size} += 2;
	}
	if ($self->{action}) {
		asm::asmComment($def->{Symbol});
	}
}

sub visitIncrVar {
	my $self = shift;
	my($opcode) = @_;
	if ($self->{action}) {
		verbose::Source($opcode);
	}
	my $def = $opcode->{Definition};
	my $vindex = $def->{ID};
	croak "INTERNAL ERROR in codeVisitor::visitDecrVar\n"
			unless ($vindex <= UINT8_MAX);
	if ($vindex <= UINT3_MAX) {
		if ($self->{action}) {
			asm::asmOpcode1s(INCR_VAR_S, $vindex);
		}
		$self->{size} += 1;
	} else {
		if ($self->{action}) {
			asm::asmOpcode2(INCR_VAR, $vindex);
		}
		$self->{size} += 2;
	}
	if ($self->{action}) {
		asm::asmComment($def->{Symbol});
	}
}

sub visitDecrVar {
	my $self = shift;
	my($opcode) = @_;
	my $def = $opcode->{Definition};
	my $vindex = $def->{ID};
	croak "INTERNAL ERROR in codeVisitor::visitDecrVar\n"
			unless ($vindex <= UINT8_MAX);
	if ($self->{action}) {
		verbose::Source($opcode);
		asm::asmOpcode2(DECR_VAR, $vindex);
		asm::asmComment($def->{Symbol});
	}
	$self->{size} += 2;
}

sub visitAddAsg {
	my $self = shift;
	my($opcode) = @_;
	my $def = $opcode->{Definition};
	my $vindex = $def->{ID};
	croak "INTERNAL ERROR in codeVisitor::visitAddAsg\n"
			unless ($vindex <= UINT8_MAX);
	if ($self->{action}) {
		verbose::Source($opcode);
		asm::asmOpcode2(ADD_ASG, $vindex);
		asm::asmComment($def->{Symbol});
	}
	$self->{size} += 2;
}

sub visitSubAsg {
	my $self = shift;
	my($opcode) = @_;
	my $def = $opcode->{Definition};
	my $vindex = $def->{ID};
	croak "INTERNAL ERROR in codeVisitor::visitSubAsg\n"
			unless ($vindex <= UINT8_MAX);
	if ($self->{action}) {
		verbose::Source($opcode);
		asm::asmOpcode2(SUB_ASG, $vindex);
		asm::asmComment($def->{Symbol});
	}
	$self->{size} += 2;
}

sub visitLabel {
	my $self = shift;
	my($opcode) = @_;
	if ($self->{action}) {
		# no verbose
		asm::asmComment($opcode->{Definition}->{Symbol});
	}
	$opcode->{Definition}->{Index} = $self->{size};
}

sub visitPop {
	my $self = shift;
	my($opcode) = @_;
	if ($self->{action}) {
		# no verbose
		asm::asmOpcode1(POP);
		asm::asmComment();
	}
	$self->{size} += 1;
}

sub visitToBool {
	my $self = shift;
	my($opcode) = @_;
	if ($self->{action}) {
		verbose::Source($opcode);
		asm::asmOpcode1(TOBOOL);
		asm::asmComment();
	}
	$self->{size} += 1;
}

sub visitScOr {
	my $self = shift;
	my($opcode) = @_;
	if ($self->{action}) {
		verbose::Source($opcode);
		asm::asmOpcode1(SCOR);
		asm::asmComment();
	}
	$self->{size} += 1;
}

sub visitScAnd {
	my $self = shift;
	my($opcode) = @_;
	if ($self->{action}) {
		verbose::Source($opcode);
		asm::asmOpcode1(SCAND);
		asm::asmComment();
	}
	$self->{size} += 1;
}

sub visitReturn {
	my $self = shift;
	my($opcode) = @_;
	if ($self->{action}) {
		verbose::Source($opcode);
		asm::asmOpcode1(RETURN);
		asm::asmComment();
	}
	$self->{size} += 1;
}

sub visitReturnES {
	my $self = shift;
	my($opcode) = @_;
	if ($self->{action}) {
		verbose::Source($opcode);
		asm::asmOpcode1(RETURN_ES);
		asm::asmComment();
	}
	$self->{size} += 1;
}

sub visitCall {
	my $self = shift;
	my($opcode) = @_;
	if ($self->{action}) {
		verbose::Source($opcode);
	}
	my $def = $opcode->{Definition};
	my $symb = $def->{Symbol};
	if ($def->{Type} ne 'UNDEF_FUNC') {
		my $nb_args = $def->{NumberOfArguments};
		my $findex = $def->{ID};
		croak "INTERNAL ERROR in codeVisitor::visitCallLib\n"
				unless ($nb_args <= UINT8_MAX);
		croak "INTERNAL ERROR in codeVisitor::visitCall\n"
				unless ($findex <= UINT8_MAX);
		if ($nb_args != $opcode->{Index}) {
			$self->{parser}->genError($opcode,"Wrong argument number for local function - $symb.\n");
		} elsif ($findex <= UINT3_MAX) {
			if ($self->{action}) {
				asm::asmOpcode1s(CALL_S, $findex);
			}
			$self->{size} += 1;
		} else {
			if ($self->{action}) {
				asm::asmOpcode2(CALL, $findex);
			}
			$self->{size} += 2;
		}
		if ($self->{action}) {
			asm::asmComment($def->{Symbol});
		}
	} else {
		$self->{parser}->genError($opcode,"Undefined function - $symb.\n");
	}
}

sub visitCallLib {
	my $self = shift;
	my($opcode) = @_;
	if ($self->{action}) {
		verbose::Source($opcode);
	}
	my $def = $opcode->{Definition};
	my $findex = $def->{ID};
	my $lindex = $def->{LibraryID};
	croak "INTERNAL ERROR in codeVisitor::visitCallLib\n"
			unless ($findex <= UINT8_MAX);
	if ($findex <= UINT3_MAX and $lindex <= UINT8_MAX) {
		if ($self->{action}) {
			asm::asmOpcode2s(CALL_LIB_S, $findex, $lindex);
		}
		$self->{size} += 2;
	} elsif ($lindex <= UINT8_MAX) {
		if ($self->{action}) {
			asm::asmOpcode3(CALL_LIB, $findex, $lindex);
		}
		$self->{size} += 3;
	} else {
		if ($self->{action}) {
			asm::asmOpcode4w(CALL_LIB_W, $findex, $lindex);
		}
		$self->{size} += 4;
	}
	if ($self->{action}) {
		asm::asmComment($def->{Symbol});
	}
}

sub visitCallUrl {
	my $self = shift;
	my($opcode) = @_;
	if ($self->{action}) {
		verbose::Source($opcode);
	}
	my $urlindex = $opcode->{Url}->{Index};
	my $findex = $opcode->{Index};
	my $def = $opcode->{Definition};
	my $nb_args = $def->{NumberOfArguments};
	croak "INTERNAL ERROR in codeVisitor::visitCallUrl\n"
			unless ($urlindex <= UINT16_MAX and $findex <= UINT16_MAX);
	croak "INTERNAL ERROR in codeVisitor::visitCallUrl\n"
			unless ($nb_args <= UINT8_MAX);
	if ($urlindex <= UINT8_MAX and $findex <= UINT8_MAX) {
		if ($self->{action}) {
			asm::asmOpcode4(CALL_URL, $urlindex, $findex, $nb_args);
		}
		$self->{size} += 4;
	} else {
		if ($self->{action}) {
			asm::asmOpcode6(CALL_URL_W, $urlindex, $findex, $nb_args);
		}
		$self->{size} += 6;
	}
	if ($self->{action}) {
		asm::asmComment($def->{Symbol});
	}
}

sub visitJump {
	my $self = shift;
	my($opcode) = @_;
	my $def = $opcode->{Definition};
	my $dest = $def->{Index};
	# no verbose
	if ($dest > $self->{size}) {
		my $offset = $dest - $self->{size};
		if      ($offset <= UINT5_MAX + 1) {
			if ($self->{action}) {
				asm::asmOpcode1s(JUMP_FW_S, $offset - 1);
			}
			$self->{size} += 1;
		} elsif ($offset <= UINT8_MAX + 2) {
			if ($self->{action}) {
				asm::asmOpcode2(JUMP_FW, $offset - 2);
			}
			$self->{size} += 2;
		} elsif ($offset <= UINT16_MAX + 3) {
			if ($self->{action}) {
				asm::asmOpcode3w(JUMP_FW_W, $offset - 3);
			}
			$self->{size} += 3;
		} else {
			if ($self->{action}) {
				$self->{parser}->genError($opcode, "Too long JUMP_FW");
			}
			$self->{size} += 3;
		}
	} else {
		my $offset = $self->{size} - $dest;
		if      ($offset <= UINT5_MAX) {
			if ($self->{action}) {
				asm::asmOpcode1s(JUMP_BW_S, $offset);
			}
			$self->{size} += 1;
		} elsif ($offset <= UINT8_MAX) {
			if ($self->{action}) {
				asm::asmOpcode2(JUMP_BW, $offset);
			}
			$self->{size} += 2;
		} elsif ($offset <= UINT16_MAX) {
			if ($self->{action}) {
				asm::asmOpcode3w(JUMP_BW_W, $offset);
			}
			$self->{size} += 3;
		} else {
			if ($self->{action}) {
				$self->{parser}->genError($opcode, "Too long JUMP_BW");
			}
			$self->{size} += 3;
		}
	}
	if ($self->{action}) {
		asm::asmComment($def->{Symbol});
	}
}

sub visitFalseJump {
	my $self = shift;
	my($opcode) = @_;
	my $def = $opcode->{Definition};
	my $dest = $def->{Index};
	# no verbose
	if ($dest > $self->{size}) {
		my $offset = $dest - $self->{size};
		if      ($offset <= UINT5_MAX + 1) {
			if ($self->{action}) {
				asm::asmOpcode1s(TJUMP_FW_S, $offset - 1);
			}
			$self->{size} += 1;
		} elsif ($offset <= UINT8_MAX + 2) {
			if ($self->{action}) {
				asm::asmOpcode2(TJUMP_FW, $offset - 2);
			}
			$self->{size} += 2;
		} elsif ($offset <= UINT16_MAX + 3) {
			if ($self->{action}) {
				asm::asmOpcode3w(TJUMP_FW_W, $offset - 3);
			}
			$self->{size} += 3;
		} else {
			if ($self->{action}) {
				$self->{parser}->genError($opcode, "Too long TJUMP_FW");
			}
			$self->{size} += 3;
		}
	} else {
		my $offset = $self->{size} - $dest;
		if      ($offset <= UINT8_MAX) {
			if ($self->{action}) {
				asm::asmOpcode2(TJUMP_BW, $offset);
			}
			$self->{size} += 2;
		} elsif ($offset <= UINT16_MAX) {
			if ($self->{action}) {
				asm::asmOpcode3w(TJUMP_BW_W, $offset);
			}
			$self->{size} += 3;
		} else {
			if ($self->{action}) {
				$self->{parser}->genError($opcode, "Too long TJUMP_BW");
			}
			$self->{size} += 3;
		}
	}
	if ($self->{action}) {
		asm::asmComment($def->{Symbol});
	}
}

sub visitUnaryOp {
	my $self = shift;
	my($opcode) = @_;
	if ($self->{action}) {
		verbose::Source($opcode);
		my $oper = $opcode->{Operator};
		if      ($oper eq 'typeof') {
			asm::asmOpcode1(TYPEOF);
		} elsif ($oper eq 'isvalid') {
			asm::asmOpcode1(ISVALID);
		} elsif ($oper eq '-') {
			asm::asmOpcode1(UMINUS);
		} elsif ($oper eq '~') {
			asm::asmOpcode1(B_NOT);
		} elsif ($oper eq '!') {
			asm::asmOpcode1(NOT);
		} elsif ($oper eq '++') {
			asm::asmOpcode1(INCR);
		} elsif ($oper eq '--') {
			asm::asmOpcode1(DECR);
		} else {
			croak "INTERNAL ERROR in codeVisitor::visitUnaryOp (oper:$oper)\n";
		}
		asm::asmComment();
	}
	$self->{size} += 1;
}

sub visitBinaryOp {
	my $self = shift;
	my($opcode) = @_;
	if ($self->{action}) {
		verbose::Source($opcode);
		my $oper = $opcode->{Operator};
		if      ($oper eq '+') {
			asm::asmOpcode1(ADD);
		} elsif ($oper eq '-') {
			asm::asmOpcode1(SUB);
		} elsif ($oper eq '*') {
			asm::asmOpcode1(MUL);
		} elsif ($oper eq '/') {
			asm::asmOpcode1(DIV);
		} elsif ($oper eq 'div') {
			asm::asmOpcode1(IDIV);
		} elsif ($oper eq '%') {
			asm::asmOpcode1(REM);
		} elsif ($oper eq '<<') {
			asm::asmOpcode1(B_LSHIFT);
		} elsif ($oper eq '>>') {
			asm::asmOpcode1(B_RSSHIFT);
		} elsif ($oper eq '>>>') {
			asm::asmOpcode1(B_RSZSHIFT);
		} elsif ($oper eq '<') {
			asm::asmOpcode1(_LT);
		} elsif ($oper eq '>') {
			asm::asmOpcode1(_GT);
		} elsif ($oper eq '<=') {
			asm::asmOpcode1(_LE);
		} elsif ($oper eq '>=') {
			asm::asmOpcode1(_GE);
		} elsif ($oper eq '==') {
			asm::asmOpcode1(_EQ);
		} elsif ($oper eq '!=') {
			asm::asmOpcode1(_NE);
		} elsif ($oper eq '&') {
			asm::asmOpcode1(B_AND);
		} elsif ($oper eq '^') {
			asm::asmOpcode1(B_XOR);
		} elsif ($oper eq '|') {
			asm::asmOpcode1(B_OR);
		} else {
			croak "INTERNAL ERROR in codeVisitor::visitBinaryOp (oper:$oper)\n";
		}
		asm::asmComment();
	}
	$self->{size} += 1;
}

sub visitLoadConst {
	my $self = shift;
	my($opcode) = @_;
	if ($self->{action}) {
		verbose::Source($opcode);
	}
	my $type = $opcode->{TypeDef};
	my $value = $opcode->{Value};
#	print "index $opcode->{Index} cst $value\n";
	if      ($type eq 'TYPE_INVALID') {
		if ($self->{action}) {
			asm::asmOpcode1(CONST_INVALID);
			asm::asmComment();
		}
		$self->{size} += 1;
	} elsif ($type eq 'TYPE_BOOLEAN') {
		if ($self->{action}) {
			if ($value) {
				asm::asmOpcode1(CONST_TRUE);
				asm::asmComment();
			} else {
				asm::asmOpcode1(CONST_FALSE);
				asm::asmComment();
			}
		}
		$self->{size} += 1;
	} elsif ($type eq 'TYPE_STRING' or $type eq 'TYPE_UTF8_STRING') {
		if (length $value == 0) {
			if ($self->{action}) {
				asm::asmOpcode1(CONST_ES);
				asm::asmComment();
			}
			$self->{size} += 1;
		} else {
			goto load_const;
		}
	} elsif ($type eq 'TYPE_FLOAT') {
load_const:
		my $cindex = $opcode->{Index};
		croak "INTERNAL ERROR in codeVisitor::visitLoadConst\n"
				unless ($cindex <= UINT16_MAX);
		if ($cindex <= UINT4_MAX) {
			if ($self->{action}) {
				asm::asmOpcode1s(LOAD_CONST_S, $cindex);
			}
			$self->{size} += 1;
		} elsif ($cindex <= UINT8_MAX) {
			if ($self->{action}) {
				asm::asmOpcode2(LOAD_CONST, $cindex);
			}
			$self->{size} += 2;
		} else {
			if ($self->{action}) {
				asm::asmOpcode3w(LOAD_CONST_W, $cindex);
			}
			$self->{size} += 3;
		}
		if ($self->{action}) {
			asm::asmComment($value);
		}
	} elsif ($type eq 'TYPE_INTEGER') {
		if      ($value == 0) {
			if ($self->{action}) {
				asm::asmOpcode1(CONST_0);
				asm::asmComment();
			}
			$self->{size} += 1;
		} elsif ($value == 1) {
			if ($self->{action}) {
				asm::asmOpcode1(CONST_1);
				asm::asmComment();
			}
			$self->{size} += 1;
		} elsif ($value == -1) {
			if ($self->{action}) {
				asm::asmOpcode1(CONST_M1);
				asm::asmComment();
			}
			$self->{size} += 1;
		} else {
			goto load_const;
		}
	} else {
		croak "INTERNAL ERROR in codeVisitor::visitLoadConst (type:$type)\n";
	}
}

###############################################################################

package parser;

use I18N::Charset;

use constant WMLS_MAJOR_VERSION		=> 1;
use constant WMLS_MINOR_VERSION		=> 1;

sub genError {
	my $parser = shift;
	my($opcode,$msg) = @_;

	if (exists $parser->YYData->{nb_error}) {
		$parser->YYData->{nb_error} ++;
	} else {
		$parser->YYData->{nb_error} = 1;
	}

	print STDOUT '#',$parser->YYData->{filename},':',$opcode->{Lineno},'#Error: ',$msg
			if (		exists $parser->YYData->{verbose_error}
					and $parser->YYData->{verbose_error});
}

sub genWarning {
	my $parser = shift;
	my($opcode,$msg) = @_;

	if (exists $parser->YYData->{nb_warning}) {
		$parser->YYData->{nb_warning} ++;
	} else {
		$parser->YYData->{nb_warning} = 1;
	}

	print STDOUT '#',$parser->YYData->{filename},':',$opcode->{Lineno},'#Warning: ',$msg
			if (		exists $parser->YYData->{verbose_warning}
					and $parser->YYData->{verbose_warning});
}

sub generate {
	my $parser = shift;

	my $CharacterSet = charset_name_to_mib($parser->YYData->{encoding});
	# ConstantPool
	my $CodeSize = 0;
	my $constantVisitor = new constantVisitor($parser);
	$parser->YYData->{PragmaList}->visit($constantVisitor)
			if (defined $parser->YYData->{PragmaList});
	$parser->YYData->{FunctionList}->visitActive($constantVisitor)
			if (defined $parser->YYData->{FunctionList});
	my $NumberOfConstants = $constantVisitor->{nb};
	$parser->genError($parser->YYData->{FunctionList},"Too many constants ($NumberOfConstants)")
			if ($NumberOfConstants > 65535);
	$CodeSize += multibyte::size($NumberOfConstants);
	$CodeSize += multibyte::size($CharacterSet);
	$CodeSize += $constantVisitor->{size};
	# PragmaPool
	my $pragmaVisitor = new pragmaVisitor($parser);
	$parser->YYData->{PragmaList}->visit($pragmaVisitor)
			if (defined $parser->YYData->{PragmaList});
	my $NumberOfPragmas = $pragmaVisitor->{nb};
	$parser->genError($parser->YYData->{PragmaList},"Too many pragmas ($NumberOfPragmas)")
			if ($NumberOfPragmas > 65535);
	$CodeSize += multibyte::size($NumberOfPragmas);
	$CodeSize += $pragmaVisitor->{size};
	# FunctionPool
	my $NumberOfFunctions = 0;
	for (my $func = $parser->YYData->{FunctionList}; defined $func; $func = $func->{Next}) {
		$NumberOfFunctions ++;
	}
	$parser->genError($parser->YYData->{FunctionList},"Too many functions ($NumberOfFunctions).\n")
			if ($NumberOfFunctions > 255);
	$CodeSize += 1;			# NumberOfFunctions
	my $NumberOfFunctionNames = 0;
	for (my $func = $parser->YYData->{FunctionList}; defined $func; $func = $func->{Next}) {
		my $def = $func->{OpCode}->{Definition};
		next if ($def->{Type} ne 'PUBLIC_FUNC');
		$NumberOfFunctionNames ++;
		$CodeSize += 1;		# idx
		$CodeSize += 1;		# length
		$CodeSize += length $def->{Symbol};
	}
	$parser->genError($parser->YYData->{FunctionList}->{OpCode},"No external function defined.\n")
			unless ($NumberOfFunctionNames);
	$CodeSize += 1;			# NumberOfFunctionNames
	my $codeVisitor = new codeVisitor($parser);
	$parser->YYData->{FunctionList}->visitActive($codeVisitor)
			if (defined $parser->YYData->{FunctionList});
	$CodeSize += $codeVisitor->{size};

	unless (exists $parser->YYData->{nb_error}) {
		my $filename = $parser->YYData->{filename};
		$filename =~ s/\.wmls$//;
		$filename .= '.wmlsc';
		open OUT,">$filename"
				or die "can't open $filename ($!)\n";
		binmode OUT,":raw";

		$asm::OUT = \*OUT;
		asm::asmComment($filename);
		asm::asmComment("");
		asm::asmComment("Bytecode Header");
		asm::asmComment("");
		asm::asmByte("VersionNumber", 16 * (WMLS_MAJOR_VERSION - 1) + WMLS_MINOR_VERSION);
		asm::asmMultiByte("CodeSize", $CodeSize);
		asm::asmComment("Constant Pool");
		asm::asmComment("");
		asm::asmMultiByte("NumberOfConstants", $NumberOfConstants);
		asm::asmMultiByte("CharacterSet", $CharacterSet);
		$constantVisitor->{action} = 1;
		$parser->YYData->{PragmaList}->visit($constantVisitor)
				if (defined $parser->YYData->{PragmaList});
		$parser->YYData->{FunctionList}->visitActive($constantVisitor)
				if (defined $parser->YYData->{FunctionList});
		asm::asmComment("Pragma Pool");
		asm::asmComment("");
		asm::asmMultiByte("NumberOfPragmas", $NumberOfPragmas);
		$pragmaVisitor->{nb} = 0;
		$pragmaVisitor->{action} = 1;
		$pragmaVisitor->{out} = \*OUT;
		$parser->YYData->{PragmaList}->visit($pragmaVisitor)
				if (defined $parser->YYData->{PragmaList});
		asm::asmComment("Function Pool");
		asm::asmComment("");
		asm::asmByte("NumberOfFunctions", $NumberOfFunctions);
		asm::asmComment("Function Name Table");
		asm::asmComment("");
		asm::asmByte("NumberOfFunctionNames", $NumberOfFunctionNames);
		for (my $func = $parser->YYData->{FunctionList}; defined $func; $func = $func->{Next}) {
			my $def = $func->{OpCode}->{Definition};
			next if ($def->{Type} ne 'PUBLIC_FUNC');
			asm::asmFunctionName($def->{ID},$def->{Symbol});
		}
		asm::asmComment("Functions");
		asm::asmComment("");
		$codeVisitor->{action} = 1;
		$parser->YYData->{FunctionList}->visitActive($codeVisitor)
				if (defined $parser->YYData->{FunctionList});

		close OUT;
		unlink($filename) if (exists $parser->YYData->{nb_error});
	}
}

1;

