OPTIONS
{
	DEBUG_SCANNER = false;
	DEBUG_PARSER = false;
	CASE_SENSITIVE = true;
	USE_EXCEPTIONS = true;
	DEFAULT_LOOKAHEAD = 1000000000;
	SHARP_LINES = false;
	TOKENS_SPAN_EOF = false;
	COUNT_COLUMNS = false;
	PROFILING_FILE = "";
//	STRING_CLASS = std::string;
	NAMESPACE_NAME = "ps20";
	SRC_EXTENSION = "cpp";
	HDR_EXTENSION = "hpp";
}

{ 
#pragma warning(disable : 4290)
#pragma warning(disable : 4786)
}

TOKEN ps20_token
{
public:
	int integer;
	double real;
}

{
#include "Error.hpp"

#pragma warning(disable : 4290)
#pragma warning(disable : 4786)
}

SCANNER ps20_scanner
{
	<START> SKIP
	{
		<BLANK: " " | "\t" | "\v" | "\f">
		<COMMENT1: "//" (~['\n','\r'])* >
		<COMMENT2: ";" (~['\n','\r'])* >
	}
	
	<START> MORE
	{
		<LONG_COMMENT1: "/*" > {pushState(LONG_COMMENT);}
	}

	<LONG_COMMENT> MORE
	{
		<LONG_COMMENT2: "\n"/* | "\r" | "\n\r" | "\r\n"*/>
		<LONG_COMMENT3: ~['*','\n','\r']*>
		<LONG_COMMENT4: ['*']+~['/','\n']>
		<LONG_COMMENT5: ['*']+"\n">
	}

	<LONG_COMMENT> SKIP
	{
		<LONG_COMMENT: ['*']+"/" > {popState();}
	}
	
	<START> TOKEN
	{
		<EOL: "\n" | "\r" | "\n\r" | "\r\n">
	}

	<START> KEYWORD
	{
		<PS_2_0: "ps_2_0">
		<DCL: "dcl">
		<DCL_2D: "dcl_2d">
		<DCL_CUBE: "dcl_cube">
		<DCL_VOLUME: "dcl_volume">	
		<DEF: "def">
	
		<ABS: "abs">
		<ADD: "add">
		<CMP: "cmp">
		<CRS: "crs">
		<DP2ADD: "dp2add">
		<DP3: "dp3">
		<DP4: "dp4">
		<EXP: "exp">
		<FRC: "frc">
		<LOG: "log">
		<LRP: "lrp">
		<M3X2: "m3x2">
		<M3X3: "m3x3">
		<M3X4: "m3x4">
		<M4X3: "m4x3">
		<M4X4: "m4x4">
		<MAD: "mad">
		<MAX: "max">
		<MIN: "min">
		<MOV: "mov">
		<MUL: "mul">
		<NOP: "nop">
		<NRM: "nrm">
		<POW: "pow">
		<RCP: "rcp">
		<RSQ: "rsq">
		<SINCOS: "sincos">
		<SUB: "sub">
		
		<TEXKILL: "texkill">
		<TEXLD: "texld">
		<TEXLDB: "texldb">
		<TEXLDP: "texldp">
		
		<_SAT: "_sat">
		<_PP: "_pp">
	}

	<START> TOKEN
	{
		<#DIGIT: ['0'-'9']>
		<#DIGITS: <DIGIT>+>
		<#SIGN: ['+','-']>
		<#EXPONENT: ['e','E']>
		<INTEGER: <SIGN>? <DIGITS>>
		<REAL: (<SIGN>? <DIGITS>? ("." <DIGITS>) |
		        (("." <DIGITS>)? <EXPONENT> <SIGN>? <DIGITS>)) "f"?>
	}

	<START> KEYWORD
	{
		<V: "v"<DIGITS>>
		{
			token->integer = atoi(token->image().c_str() + 1);
			if(token->integer >= 2) throw swShader::Error("Syntax swShader::Error: v%d index out of range", token->integer);
		}
		
		<C: "c"<DIGITS>>
		{
			token->integer = atoi(token->image().c_str() + 1);
			if(token->integer >= 32) throw swShader::Error("Syntax swShader::Error: c%d index out of range", token->integer);
		}
		
		<T: "t"<DIGITS>>
		{
			token->integer = atoi(token->image().c_str() + 1);
			if(token->integer >= 8) throw swShader::Error("Syntax swShader::Error: t%d index out of range", token->integer);
		}
		
		<S: "s"<DIGITS>>
		{
			token->integer = atoi(token->image().c_str() + 1);
			if(token->integer >= 16) throw swShader::Error("Syntax swShader::Error: s%d index out of range", token->integer);
		}
		
		<R: "r"<DIGITS>>
		{
			token->integer = atoi(token->image().c_str() + 1);
			if(token->integer >= 12) throw swShader::Error("Syntax swShader::Error: r%d index out of range", token->integer);
		}
		
		<O_C: "oC"<DIGITS>>
		{
			token->integer = atoi(token->image().c_str() + 2);
			if(token->integer >= 4) throw swShader::Error("Syntax swShader::Error: oC%d index out of range", token->integer);
		}
		
		<O_DEPTH: "oDepth">
	}
	
	<START> KEYWORD
	{
		<X: "x" | "r">
		<Y: "y" | "g">
		<Z: "z" | "b">
		<W: "w" | "a">
	}

	<START> KEYWORD
	{
		<COMMA: ",">
		<DOT: ".">
		<MINUS: "-">
	}
}

{
#include "PS_2_0Assembler.hpp"

#include "Instruction.hpp"
#include "Operand.hpp"
#include "Error.hpp"

#include <string.h>
#include <list>

#pragma warning(disable : 4290)
#pragma warning(disable : 4786)
}

PARSER ps20_parser : public swShader::PS_2_0Assembler
{
	() main ()
	{
		(instruction() <EOL>+ {newInstruction();})*
		instruction()?

		catch(ScanException&) {throw swShader::Error("Syntax error");}
		catch(ParseException&) {throw swShader::Error("Parsing error");}
		catch(swShader::Error &error) {throw error;}
		catch(...) {throw swShader::INTERNAL_ERROR;}
	}

	() instruction ()
	{
		{
			m = swShader::Instruction::INVALID;
			x = swShader::Instruction::_NONE;
			d = swShader::voidOperand;
			s0 = swShader::voidOperand;
			s1 = swShader::voidOperand;
			s2 = swShader::voidOperand;
			s3 = swShader::voidOperand;
		}
	
		(
			absInstruction() |
			addInstruction() |
			cmpInstruction() |
			crsInstruction() |
			dclInstruction() |
			dcl_2dInstruction() |
			dcl_cubeInstruction() |
			dcl_volumeInstruction() |
			defInstruction() |
			dp2addInstruction() |
			dp3Instruction() |
			dp4Instruction() |
			expInstruction() |
			frcInstruction() |
			logInstruction() |
			lrpInstruction() |
			m3x2Instruction() |
			m3x3Instruction() |
			m3x4Instruction() |
			m4x3Instruction() |
			m4x4Instruction() |
			madInstruction() |
			maxInstruction() |
			minInstruction() |
			movInstruction() |
			mulInstruction() |
			nopInstruction() |
			nrmInstruction() |
			powInstruction() |
			psInstruction() |
			rcpInstruction() |
			rsqInstruction() |
			sincosInstruction() |
			subInstruction() |
			texkillInstruction() |
			texldInstruction() |
			texldbInstruction() |
			texldpInstruction()
		)
		
		catch(ParseException&) {throw swShader::Error("Expected an instruction");}
		
		{
			setMnemonic(m);
			setModifier(x);
			setDestination(d);
			setSource0(s0);
			setSource1(s1);
			setSource2(s2);
			setSource3(s3);
		}
	}

	// Setup instructions
	() psInstruction ()
	{
		<PS_2_0> {m = swShader::Instruction::PS_2_0;}
	}
	
	() dclInstruction ()
	{
		<DCL> {m = swShader::Instruction::DCL;}
		dest()
	}
		
	() dcl_2dInstruction ()
	{
		<DCL_2D> {m = swShader::Instruction::DCL_2D;}
		sn()
	}
	
	() dcl_cubeInstruction ()
	{
		<DCL_CUBE> {m = swShader::Instruction::DCL_CUBE;}
		sn()
	}
	
	() dcl_volumeInstruction ()
	{
		<DCL_VOLUME> {m = swShader::Instruction::DCL_VOLUME;}
		sn()
	}
	
	() defInstruction ()
	{
		<DEF> {m = swShader::Instruction::DEF;}
		cn() <COMMA> fValue1() <COMMA> fValue2() <COMMA> fValue3() <COMMA> fValue4()
	}

	// Arithmetic instructions
	() absInstruction ()
	{
		<ABS> {m = swShader::Instruction::ABS;} instructionModifier()
		dst() <COMMA> src()
	}
	
	() addInstruction ()
	{
		<ADD> {m = swShader::Instruction::ADD;} instructionModifier()
		dst() <COMMA> src0() <COMMA> src1()
	}
	
	() cmpInstruction ()
	{
		<CMP> {m = swShader::Instruction::CMP;} instructionModifier()
		dst() <COMMA> src0() <COMMA> src1() <COMMA> src2()
	}
	
	() crsInstruction ()
	{
		<CRS> {m = swShader::Instruction::CRS;} instructionModifier()
		dst() <COMMA> src0() <COMMA> src1()
	}
	
	() dp2addInstruction ()
	{
		<DP2ADD> {m = swShader::Instruction::DP2ADD;} instructionModifier()
		dst() <COMMA> src0() <COMMA> src1() <COMMA> src2()
	}
	
	() dp3Instruction ()
	{
		<DP3> {m = swShader::Instruction::DP3;} instructionModifier()
		dst() <COMMA> src0() <COMMA> src1()
	}
	
	() dp4Instruction ()
	{
		<DP4> {m = swShader::Instruction::DP4;} instructionModifier()
		dst() <COMMA> src0() <COMMA> src1()
	}
	
	() expInstruction ()
	{
		<EXP> {m = swShader::Instruction::EXP;} instructionModifier()
		dst() <COMMA> src()
	}
	
	() frcInstruction ()
	{
		<FRC> {m = swShader::Instruction::FRC;} instructionModifier()
		dst() <COMMA> src()
	}
	
	() logInstruction ()
	{
		<LOG> {m = swShader::Instruction::LOG;} instructionModifier()
		dst() <COMMA> src()
	}
	
	() lrpInstruction ()
	{
		<LRP> {m = swShader::Instruction::LRP;} instructionModifier()
		dst() <COMMA> src0() <COMMA> src1() <COMMA> src2()
	}
	
	() m3x2Instruction ()
	{
		<M3X2> {m = swShader::Instruction::M3X2;} instructionModifier()
		dst() <COMMA> src0() <COMMA> src1()
	}
	
	() m3x3Instruction ()
	{
		<M3X3> {m = swShader::Instruction::M3X3;} instructionModifier()
		dst() <COMMA> src0() <COMMA> src1()
	}
	
	() m3x4Instruction ()
	{
		<M3X4> {m = swShader::Instruction::M3X4;} instructionModifier()
		dst() <COMMA> src0() <COMMA> src1()
	}
	
	() m4x3Instruction ()
	{
		<M4X3> {m = swShader::Instruction::M4X3;} instructionModifier()
		dst() <COMMA> src0() <COMMA> src1()
	}
	
	() m4x4Instruction ()
	{
		<M4X4> {m = swShader::Instruction::M4X4;} instructionModifier()
		dst() <COMMA> src0() <COMMA> src1()
	}
	
	() madInstruction ()
	{
		<MAD> {m = swShader::Instruction::MAD;} instructionModifier()
		dst() <COMMA> src0() <COMMA> src1() <COMMA> src2()
	}
	
	() maxInstruction ()
	{
		<MAX> {m = swShader::Instruction::MAX;} instructionModifier()
		dst() <COMMA> src0() <COMMA> src1()
	}
	
	() minInstruction ()
	{
		<MIN> {m = swShader::Instruction::MIN;} instructionModifier()
		dst() <COMMA> src0() <COMMA> src1()
	}
	
	() movInstruction ()
	{
		<MOV> {m = swShader::Instruction::MOV;} instructionModifier()
		(dst() | outputRegister()) <COMMA> src()
	}
	
	() mulInstruction ()
	{
		<MUL> {m = swShader::Instruction::MUL;} instructionModifier()
		dst() <COMMA> src0() <COMMA> src1()
	}
	
	() nopInstruction ()
	{
		<NOP> {m = swShader::Instruction::NOP;}
	}
	
	() nrmInstruction ()
	{
		<NRM> {m = swShader::Instruction::NRM;} instructionModifier()
		dst() <COMMA> src()
	}
	
	() powInstruction ()
	{
		<POW> {m = swShader::Instruction::POW;} instructionModifier()
		dst() <COMMA> src0() <COMMA> src1()
	}
	
	() rcpInstruction ()
	{
		<RCP> {m = swShader::Instruction::RCP;} instructionModifier()
		dst() <COMMA> src()
	}
	
	() rsqInstruction ()
	{
		<RSQ> {m = swShader::Instruction::RSQ;} instructionModifier()
		dst() <COMMA> src()
	}
	
	() sincosInstruction ()
	{
		<SINCOS> {m = swShader::Instruction::SINCOS;} instructionModifier()
		dst() <COMMA> src0() <COMMA> src1() <COMMA> src2()
	}
	
	() subInstruction ()
	{
		<SUB> {m = swShader::Instruction::SUB;} instructionModifier()
		dst() <COMMA> src0() <COMMA> src1()
	}
	
	// Texture instructions
	() texkillInstruction ()
	{
		<TEXKILL> {m = swShader::Instruction::TEXKILL;}
		dst() <COMMA> src0() <COMMA> src1()
	}
	
	() texldInstruction ()
	{
		<TEXLD>	{m = swShader::Instruction::TEXLD;}
		dst() <COMMA> src0() <COMMA> src1()
	}
	
	() texldbInstruction ()
	{
		<TEXLDB> {m = swShader::Instruction::TEXLDB;}
		dst() <COMMA> src0() <COMMA> src1()
	}
	
	() texldpInstruction ()
	{
		<TEXLDP> {m = swShader::Instruction::TEXLDP;}
		dst() <COMMA> src0() <COMMA> src1()
	}

	// Modifiers and registers
	() instructionModifier ()
	{
		{
			x = swShader::Instruction::_NONE;
		}
		
		(
			(<_SAT> {x = swShader::Instruction::_SAT;}) |
			(<_PP> {x = swShader::Instruction::_PP;})
		)?
	}

	() dst ()
	{
		(temporaryRegister() writeMask())
		
		catch(ParseException&) {throw swShader::Error("Expected a temporary register r#");}
	
		{d = t;}
	}
	
	() dest ()
	{
		(colorRegister() | inputTextureCoordinateRegister())
		
		catch(ParseException&) {throw swShader::Error("Expected a register v# or t#");}
	
		{d = t;}
	}
	
	() sn ()
	{
		samplerRegister()

		catch(ParseException&) {throw swShader::Error("Expected a sampler register s#");}

		{d = t;}
	}
	
	() cn ()
	{
		constantFloatRegister()
		
		catch(ParseException&) {throw swShader::Error("Expected a constant float register c#");}

		{d = t;}
	}
	
	() fValue1 ()
	{
		(<REAL> | <INTEGER>)
		
		catch(ParseException&) {throw swShader::Error("Expected a floating-point literal");}

		{s0.value = (float)strtod(token->image().c_str(), 0);}
	}

	() fValue2 ()
	{
		(<REAL> | <INTEGER>)
		
		catch(ParseException&) {throw swShader::Error("Expected a floating-point literal");}

		{s1.value = (float)strtod(token->image().c_str(), 0);}
	}
	
	() fValue3 ()
	{
		(<REAL> | <INTEGER>)
		
		catch(ParseException&) {throw swShader::Error("Expected a floating-point literal");}

		{s2.value = (float)strtod(token->image().c_str(), 0);}
	}
	
	() fValue4 ()
	{
		(<REAL> | <INTEGER>)
		
		catch(ParseException&) {throw swShader::Error("Expected a floating-point literal");}

		{s3.value = (float)strtod(token->image().c_str(), 0);}
	}
	
	() src ()
	{
		(sourceModifier() inputRegister() sourceSwizzle()) {s0 = t;}
	}

	() src0 ()
	{
		(sourceModifier() inputRegister() sourceSwizzle()) {s0 = t;}
	}
	
	() src1 ()
	{
		(sourceModifier() inputRegister() sourceSwizzle()) {s1 = t;}
	}
	
	() src2 ()
	{
		(sourceModifier() inputRegister() sourceSwizzle()) {s2 = t;}
	}
	
	() outputRegister()
	{
		(outputColorRegister() | outputDepthRegister()) {d = t;}
	}
	
	() inputRegister ()
	{
		colorRegister() |
		constantFloatRegister() |
		inputTextureCoordinateRegister() |
		samplerRegister() |
		temporaryRegister()
	}
	
	() sourceModifier ()
	{
		{t.mod = swShader::Operand::SourceModifier::NONE;}
	
		(
			<MINUS>
			
			{t.mod = swShader::Operand::SourceModifier::NEGATE;}
		)?
	}
	
	() writeMask ()
	{
		{
			// Mask all components
			t.sel.x = swShader::Operand::X;
			t.sel.y = swShader::Operand::Y;
			t.sel.z = swShader::Operand::Z;
			t.sel.w = swShader::Operand::W;
		}
	
		(
			<DOT>
			
			{
				// Mask all components
				t.sel.x = swShader::Operand::M;
				t.sel.y = swShader::Operand::M;
				t.sel.z = swShader::Operand::M;
				t.sel.w = swShader::Operand::M;
			}
			
			(
				(
					(<X> {t.sel.x = swShader::Operand::X;})
					(<Y> {t.sel.y = swShader::Operand::Y;})?
					(<Z> {t.sel.z = swShader::Operand::Z;})?
					(<W> {t.sel.w = swShader::Operand::W;})?
				)
					|
				(
					(<Y> {t.sel.y = swShader::Operand::Y;})
					(<Z> {t.sel.z = swShader::Operand::Z;})?
					(<W> {t.sel.w = swShader::Operand::W;})?
				) 
					|
				(
					(<Z> {t.sel.z = swShader::Operand::Z;}) 
					(<W> {t.sel.w = swShader::Operand::W;})?
				) 
					|
				(
					(<W> {t.sel.w = swShader::Operand::W;})
				)
			)
		)?
	}

	() sourceSwizzle ()
	{
		{
			t.sel.x = swShader::Operand::Component::X;
			t.sel.y = swShader::Operand::Component::Y;
			t.sel.z = swShader::Operand::Component::Z;
			t.sel.w = swShader::Operand::Component::W;
		}
	
		(
			<DOT>
			
			(
				(<X> {t.sel.x = swShader::Operand::X; t.sel.y = swShader::Operand::X; t.sel.z = swShader::Operand::X; t.sel.w = swShader::Operand::X;}) |
				(<Y> {t.sel.x = swShader::Operand::Y; t.sel.y = swShader::Operand::Y; t.sel.z = swShader::Operand::Y; t.sel.w = swShader::Operand::Y;}) |
				(<Z> {t.sel.x = swShader::Operand::Z; t.sel.y = swShader::Operand::Z; t.sel.z = swShader::Operand::Z; t.sel.w = swShader::Operand::Z;}) |
				(<W> {t.sel.x = swShader::Operand::W; t.sel.y = swShader::Operand::W; t.sel.z = swShader::Operand::W; t.sel.w = swShader::Operand::W;})
			)
			(
				(<X> {t.sel.y = swShader::Operand::X; t.sel.z = swShader::Operand::X; t.sel.w = swShader::Operand::X;}) |
				(<Y> {t.sel.y = swShader::Operand::Y; t.sel.z = swShader::Operand::Y; t.sel.w = swShader::Operand::Y;}) |
				(<Z> {t.sel.y = swShader::Operand::Z; t.sel.z = swShader::Operand::Z; t.sel.w = swShader::Operand::Z;}) |
				(<W> {t.sel.y = swShader::Operand::W; t.sel.z = swShader::Operand::W; t.sel.w = swShader::Operand::W;})
			)?
			(
				(<X> {t.sel.z = swShader::Operand::X; t.sel.w = swShader::Operand::X;}) |
				(<Y> {t.sel.z = swShader::Operand::Y; t.sel.w = swShader::Operand::Y;}) |
				(<Z> {t.sel.z = swShader::Operand::Z; t.sel.w = swShader::Operand::Z;}) |
				(<W> {t.sel.z = swShader::Operand::W; t.sel.w = swShader::Operand::W;})
			)?
			(
				(<X> {t.sel.w = swShader::Operand::X;}) |
				(<Y> {t.sel.w = swShader::Operand::Y;}) |
				(<Z> {t.sel.w = swShader::Operand::Z;}) |
				(<W> {t.sel.w = swShader::Operand::W;})
			)?
		)?
	}
	
	() colorRegister ()
	{
		<V> {t.type = swShader::Operand::COLOR_REGISTER; t.index = token->integer;}
	}
	
	() constantFloatRegister ()
	{
		<C> {t.type = swShader::Operand::CONSTANT_FLOAT_REGISTER; t.index = token->integer;}
	}

	() inputTextureCoordinateRegister ()
	{
		<T> {t.type = swShader::Operand::INPUT_TEXTURE_COORDINATE_REGISTER; t.index = token->integer;}

		writeMask()?
	}
	
	() samplerRegister ()
	{
		<S> {t.type = swShader::Operand::SAMPLER_REGISTER; t.index = token->integer;}
	}

	() temporaryRegister ()
	{
		<R> {t.type = swShader::Operand::TEMPORARY_REGISTER; t.index = token->integer;}
	}

	() outputColorRegister ()
	{
		<O_C> {t.type = swShader::Operand::OUTPUT_COLOR_REGISTER; t.index = token->integer;}
	}

	() outputDepthRegister ()
	{
		<O_DEPTH> {t.type = swShader::Operand::OUTPUT_DEPTH_REGISTER;}
	}

	{
	private:
		swShader::Operand t;   // Temporary operand
	
		swShader::Instruction::Mnemonic m;  // Operation
		swShader::Instruction::Modifier x;  // Instrution modifier
		swShader::Operand d;   // Destination
		swShader::Operand s0;   // Source 0
		swShader::Operand s1;   // Source 1
		swShader::Operand s2;   // Source 2
		swShader::Operand s3;   // Source 3
	}
}
