summaryrefslogtreecommitdiff
path: root/src/mesa/main/arbparse.c
diff options
context:
space:
mode:
authorKarl Rasche <karlrasche@gmail.com>2003-11-19 13:17:33 +0000
committerKarl Rasche <karlrasche@gmail.com>2003-11-19 13:17:33 +0000
commit75b5cb43173cda966682c8c9b9353c48ab6ad285 (patch)
tree3e928c94fb3ec287d81eeea8b97ca70ba9be1ac9 /src/mesa/main/arbparse.c
parent1c27a1e957546ed849b3fd0c35904e9680f113f8 (diff)
Initial checkin of new ARB_frag/vertex program parser
Diffstat (limited to 'src/mesa/main/arbparse.c')
-rw-r--r--src/mesa/main/arbparse.c5717
1 files changed, 5717 insertions, 0 deletions
diff --git a/src/mesa/main/arbparse.c b/src/mesa/main/arbparse.c
new file mode 100644
index 0000000000..33b31db590
--- /dev/null
+++ b/src/mesa/main/arbparse.c
@@ -0,0 +1,5717 @@
+/*
+ * Mesa 3-D graphics library
+ * Version: 5.1
+ *
+ * Copyright (C) 1999-2003 Brian Paul All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * BRIAN PAUL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#define DEBUG_PARSING 0
+
+/**
+ * \file arbparse.c
+ * ARB_*_program parser core
+ * \author Michal Krol, Karl Rasche
+ */
+
+
+#include "mtypes.h"
+#include "glheader.h"
+#include "context.h"
+#include "hash.h"
+#include "imports.h"
+#include "macros.h"
+#include "program.h"
+#include "nvvertprog.h"
+#include "nvfragprog.h"
+#include "arbparse.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+/* TODO:
+ * Fragment Program Stuff:
+ * -----------------------------------------------------
+ * - How does negating on SWZ work?? If any of the components have a -, negate?
+ * - how does thing like 'foo[N]' work in src registers?
+ *
+ * - things from Michal's email
+ * + overflow on atoi
+ * + not-overflowing floats (don't use parse_integer..)
+ *
+ * + fix multiple cases in switches, that might change
+ * (these are things that are #defined to the same value, but occur
+ * only on fp or vp's, which funkifies the switch statements)
+ * - STATE_TEX_* STATE_CLIP_PLANE, etc and PRECISION_HINT_FASTEST/PositionInvariant
+ *
+ * - check all limits of number of various variables
+ * + parameters
+ * + modelview matrix number
+ *
+ * - test! test! test!
+ *
+ * Vertex Program Stuff:
+ * -----------------------------------------------------
+ * - Add in cases for vp attribs
+ * + VERTEX_ATTRIB_MATRIXINDEX -- ??
+ * + VERTEX_ATTRIB_GENERIC
+ * * Test for input alias error --> bleh!
+ *
+ * - ARRAY_INDEX_RELATIVE
+ * - grep for XXX
+ *
+ * Mesa Stuff
+ * -----------------------------------------------------
+ * - vp_src swizzle is GLubyte, fp_src swizzle is GLuint
+ * - fetch state listed in program_parameters list
+ * + WTF should this go???
+ * + currently in nvvertexec.c and s_nvfragprog.c
+ *
+ * - allow for multiple address registers (and fetch address regs properly)
+ *
+ * Cosmetic Stuff
+ * -----------------------------------------------------
+ * - fix compiler warnings
+ * - remove any leftover unused grammer.c stuff (dict_ ?)
+ * - fix grammer.c error handling so its not static
+ * - #ifdef around stuff pertaining to extentions
+ *
+ * Outstanding Questions:
+ * -----------------------------------------------------
+ * - palette matrix? do we support this extension? what is the extention?
+ * - When can we fetch env/local params from their own register files, and when
+ * to we have to fetch them into the main state register file? (think arrays)
+ *
+ * Grammar Changes:
+ * -----------------------------------------------------
+ * - changed optional_exponent rule from:
+ * " exponent .or .true .emit '1' .emit 0x00;\n"
+ * to
+ * " exponent .or .true .emit '1' .emit 0x00 .emit $;\n"
+ *
+ * - XXX: need to recognize "1" as a valid float ?
+ */
+
+typedef unsigned char byte;
+typedef byte *production;
+
+/*-----------------------------------------------------------------------
+ * From here on down is the syntax checking portion
+ */
+
+/* VERSION: 0.3 */
+
+/*
+ INTRODUCTION
+ ------------
+
+ The task is to check the syntax of an input string. Input string is a stream of ASCII
+ characters terminated with null-character ('\0'). Checking it using C language is
+ difficult and hard to implement without bugs. It is hard to maintain and change prior
+ to further syntax changes.
+
+ This is because of high redundancy of the C code. Large blocks of code are duplicated with
+ only small changes. Even using macros does not solve the problem, because macros cannot
+ erase the complexity of the code.
+
+ The resolution is to create a new language that will be highly oriented to our task. Once
+ we describe particular syntax, we are done. We can then focus on the code that implements
+ the language. The size and complexity of it is relatively small than the code that directly
+ checks the syntax.
+
+ First, we must implement our new language. Here, the language is implemented in C, but it
+ could also be implemented in any other language. The code is listed below. We must take
+ a good care that it is bug free. This is simple because the code is simple and clean.
+
+ Next, we must describe the syntax of our new language in itself. Once created and checked
+ manually that it is correct, we can use it to check another scripts.
+
+ Note that our new language loading code does not have to check the syntax. It is because we
+ assume that the script describing itself is correct, and other scripts can be syntactically
+ checked by the former script. The loading code must only do semantic checking which leads us to
+ simple resolving references.
+
+ THE LANGUAGE
+ ------------
+
+ Here I will describe the syntax of the new language (further called "Synek"). It is mainly a
+ sequence of declarations terminated by a semicolon. The declaration consists of a symbol,
+ which is an identifier, and its definition. A definition is in turn a sequence of specifiers
+ connected with ".and" or ".or" operator. These operators cannot be mixed together in a one
+ definition. Specifier can be a symbol, string, character, character range or a special
+ keyword ".true" or ".false".
+
+ On the very beginning of the script there is a declaration of a root symbol and is in the form:
+ .syntax <root_symbol>;
+ The <root_symbol> must be on of the symbols in declaration sequence. The syntax is correct if
+ the root symbol evaluates to true. A symbol evaluates to true if the definition associated with
+ the symbol evaluates to true. Definition evaluation depends on the operator used to connect
+ specifiers in the definition. If ".and" operator is used, definition evaluates to true if and
+ only if all the specifiers evaluate to true. If ".or" operator is used, definition evalutes to
+ true if any of the specifiers evaluates to true. If definition contains only one specifier,
+ it is evaluated as if it was connected with ".true" keyword by ".and" operator.
+
+ If specifier is a ".true" keyword, it always evaluates to true.
+
+ If specifier is a ".false" keyword, it always evaluates to false. Specifier evaluates to false
+ when it does not evaluate to true.
+
+ Character range specifier is in the form:
+ '<first_character>' - '<second_character>'
+ If specifier is a character range, it evaluates to true if character in the stream is greater
+ or equal to <first_character> and less or equal to <second_character>. In that situation
+ the stream pointer is advanced to point to next character in the stream. All C-style escape
+ sequences are supported although trigraph sequences are not. The comparisions are performed
+ on 8-bit unsigned integers.
+
+ Character specifier is in the form:
+ '<single_character>'
+ It evaluates to true if the following character range specifier evaluates to true:
+ '<single_character>' - '<single_character>'
+
+ String specifier is in the form:
+ "<string>"
+ Let N be the number of characters in <string>. Let <string>[i] designate i-th character in
+ <string>. Then the string specifier evaluates to true if and only if for i in the range [0, N)
+ the following character specifier evaluates to true:
+ '<string>[i]'
+ If <string>[i] is a quotation mark, '<string>[i]' is replaced with '\<string>[i]'.
+
+ Symbol specifier can be optionally preceded by a ".loop" keyword in the form:
+ .loop <symbol> (1)
+ where <symbol> is defined as follows:
+ <symbol> <definition>; (2)
+ Construction (1) is replaced by the following code:
+ <symbol$1>
+ and declaration (2) is replaced by the following:
+ <symbol$1> <symbol$2> .or .true;
+ <symbol$2> <symbol> .and <symbol$1>;
+ <symbol> <definition>;
+
+ ESCAPE SEQUENCES
+ ----------------
+
+ Synek supports all escape sequences in character specifiers. The mapping table is listed below.
+ All occurences of the characters in the first column are replaced with the corresponding
+ character in the second column.
+
+ Escape sequence Represents
+ ------------------------------------------------------------------------------------------------
+ \a Bell (alert)
+ \b Backspace
+ \f Formfeed
+ \n New line
+ \r Carriage return
+ \t Horizontal tab
+ \v Vertical tab
+ \' Single quotation mark
+ \" Double quotation mark
+ \\ Backslash
+ \? Literal question mark
+ \ooo ASCII character in octal notation
+ \xhhh ASCII character in hexadecimal notation
+ ------------------------------------------------------------------------------------------------
+
+ RAISING ERRORS
+ --------------
+
+ Any specifier can be followed by a special construction that is executed when the specifier
+ evaluates to false. The construction is in the form:
+ .error <ERROR_TEXT>
+ <ERROR_TEXT> is an identifier declared earlier by error text declaration. The declaration is
+ in the form:
+ .errtext <ERROR_TEXT> "<error_desc>"
+ When specifier evaluates to false and this construction is present, parsing is stopped
+ immediately and <error_desc> is returned as a result of parsing. The error position is also
+ returned and it is meant as an offset from the beggining of the stream to the character that
+ was valid so far. Example:
+
+ (**** syntax script ****)
+
+ .syntax program;
+ .errtext MISSING_SEMICOLON "missing ';'"
+ program declaration .and .loop space .and ';' .error MISSING_SEMICOLON .and
+ .loop space .and '\0';
+ declaration "declare" .and .loop space .and identifier;
+ space ' ';
+
+ (**** sample code ****)
+
+ declare foo ,
+
+ In the example above checking the sample code will result in error message "missing ';'" and
+ error position 12. The sample code is not correct. Note the presence of '\0' specifier to
+ assure that there is no code after semicolon - only spaces.
+ <error_desc> can optionally contain identifier surrounded by dollar signs $. In such a case,
+ the identifier and dollar signs are replaced by a string retrieved by invoking symbol with
+ the identifier name. The starting position is the error position. The lenght of the resulting
+ string is the position after invoking the symbol.
+
+ PRODUCTION
+ ----------
+
+ Synek not only checks the syntax but it can also produce (emit) bytes associated with specifiers
+ that evaluate to true. That is, every specifier and optional error construction can be followed
+ by a number of emit constructions that are in the form:
+ .emit <parameter>
+ <paramater> can be a HEX number, identifier, a star * or a dollar $. HEX number is preceded by
+ 0x or 0X. If <parameter> is an identifier, it must be earlier declared by emit code declaration
+ in the form:
+ .emtcode <identifier> <hex_number>
+
+ When given specifier evaluates to true, all emits associated with the specifier are output
+ in order they were declared. A star means that last-read character should be output instead
+ of constant value. Example:
+
+ (**** syntax script ****)
+
+ .syntax foobar;
+ .emtcode WORD_FOO 0x01
+ .emtcode WORD_BAR 0x02
+ foobar FOO .emit WORD_FOO .or BAR .emit WORD_BAR .or .true .emit 0x00;
+ FOO "foo" .and SPACE;
+ BAR "bar" .and SPACE;
+ SPACE ' ' .or '\0';
+
+ (**** sample text 1 ****)
+
+ foo
+
+ (**** sample text 2 ****)
+
+ foobar
+
+ For both samples the result will be one-element array. For first sample text it will be
+ value 1, for second - 0. Note that every text will be accepted because of presence of
+ .true as an alternative.
+
+ Another example:
+
+ (**** syntax script ****)
+
+ .syntax declaration;
+ .emtcode VARIABLE 0x01
+ declaration "declare" .and .loop space .and
+ identifier .emit VARIABLE .and (1)
+ .true .emit 0x00 .and (2)
+ .loop space .and ';';
+ space ' ' .or '\t';
+ identifier .loop id_char .emit *; (3)
+ id_char 'a'-'z' .or 'A'-'Z' .or '_';
+
+ (**** sample code ****)
+
+ declare fubar;
+
+ In specifier (1) symbol <identifier> is followed by .emit VARIABLE. If it evaluates to
+ true, VARIABLE constant and then production of the symbol is output. Specifier (2) is used
+ to terminate the string with null to signal when the string ends. Specifier (3) outputs
+ all characters that make declared identifier. The result of sample code will be the
+ following array:
+ { 1, 'f', 'u', 'b', 'a', 'r', 0 }
+
+ If .emit is followed by dollar $, it means that current position should be output. Current
+ position is a 32-bit unsigned integer distance from the very beginning of the parsed string to
+ first character consumed by the specifier associated with the .emit instruction. Current
+ position is stored in the output buffer in Little-Endian convention (the lowest byte comes
+ first).
+*/
+
+/**
+ * This is the text describing the rules to parse the grammar
+ */
+#include "arbparse_syn.h"
+
+/**
+ * These should match up with the values defined in arbparse.syn.h
+ */
+
+#define REVISION 0x03
+
+/* program type */
+#define FRAGMENT_PROGRAM 0x01
+#define VERTEX_PROGRAM 0x02
+
+/* program section */
+#define OPTION 0x01
+#define INSTRUCTION 0x02
+#define DECLARATION 0x03
+#define END 0x04
+
+/* fragment program option flags */
+#define ARB_PRECISION_HINT_FASTEST 0x01
+#define ARB_PRECISION_HINT_NICEST 0x02
+#define ARB_FOG_EXP 0x04
+#define ARB_FOG_EXP2 0x08
+#define ARB_FOG_LINEAR 0x10
+
+/* vertex program option flags */
+#define ARB_POSITION_INVARIANT 0x01
+
+/* fragment program instruction class */
+#define F_ALU_INST 0x01
+#define F_TEX_INST 0x02
+
+/* fragment program instruction type */
+#define F_ALU_VECTOR 0x01
+#define F_ALU_SCALAR 0x02
+#define F_ALU_BINSC 0x03
+#define F_ALU_BIN 0x04
+#define F_ALU_TRI 0x05
+#define F_ALU_SWZ 0x06
+#define F_TEX_SAMPLE 0x07
+#define F_TEX_KIL 0x08
+
+/* vertex program instruction type */
+#define V_GEN_ARL 0x01
+#define V_GEN_VECTOR 0x02
+#define V_GEN_SCALAR 0x03
+#define V_GEN_BINSC 0x04
+#define V_GEN_BIN 0x05
+#define V_GEN_TRI 0x06
+#define V_GEN_SWZ 0x07
+
+/* fragment program instruction code */
+#define F_ABS 0x00
+#define F_ABS_SAT 0x01
+#define F_FLR 0x02
+#define F_FLR_SAT 0x03
+#define F_FRC 0x04
+#define F_FRC_SAT 0x05
+#define F_LIT 0x06
+#define F_LIT_SAT 0x07
+#define F_MOV 0x08
+#define F_MOV_SAT 0x09
+#define F_COS 0x0A
+#define F_COS_SAT 0x0B
+#define F_EX2 0x0C
+#define F_EX2_SAT 0x0D
+#define F_LG2 0x0E
+#define F_LG2_SAT 0x0F
+#define F_RCP 0x10
+#define F_RCP_SAT 0x11
+#define F_RSQ 0x12
+#define F_RSQ_SAT 0x13
+#define F_SIN 0x14
+#define F_SIN_SAT 0x15
+#define F_SCS 0x16
+#define F_SCS_SAT 0x17
+#define F_POW 0x18
+#define F_POW_SAT 0x19
+#define F_ADD 0x1A
+#define F_ADD_SAT 0x1B
+#define F_DP3 0x1C
+#define F_DP3_SAT 0x1D
+#define F_DP4 0x1E
+#define F_DP4_SAT 0x1F
+#define F_DPH 0x20
+#define F_DPH_SAT 0x21
+#define F_DST 0x22
+#define F_DST_SAT 0x23
+#define F_MAX 0x24
+#define F_MAX_SAT 0x25
+#define F_MIN 0x26
+#define F_MIN_SAT 0x27
+#define F_MUL 0x28
+#define F_MUL_SAT 0x29
+#define F_SGE 0x2A
+#define F_SGE_SAT 0x2B
+#define F_SLT 0x2C
+#define F_SLT_SAT 0x2D
+#define F_SUB 0x2E
+#define F_SUB_SAT 0x2F
+#define F_XPD 0x30
+#define F_XPD_SAT 0x31
+#define F_CMP 0x32
+#define F_CMP_SAT 0x33
+#define F_LRP 0x34
+#define F_LRP_SAT 0x35
+#define F_MAD 0x36
+#define F_MAD_SAT 0x37
+#define F_SWZ 0x38
+#define F_SWZ_SAT 0x39
+#define F_TEX 0x3A
+#define F_TEX_SAT 0x3B
+#define F_TXB 0x3C
+#define F_TXB_SAT 0x3D
+#define F_TXP 0x3E
+#define F_TXP_SAT 0x3F
+#define F_KIL 0x40
+
+/* vertex program instruction code */
+#define V_ARL 0x01
+#define V_ABS 0x02
+#define V_FLR 0x03
+#define V_FRC 0x04
+#define V_LIT 0x05
+#define V_MOV 0x06
+#define V_EX2 0x07
+#define V_EXP 0x08
+#define V_LG2 0x09
+#define V_LOG 0x0A
+#define V_RCP 0x0B
+#define V_RSQ 0x0C
+#define V_POW 0x0D
+#define V_ADD 0x0E
+#define V_DP3 0x0F
+#define V_DP4 0x10
+#define V_DPH 0x11
+#define V_DST 0x12
+#define V_MAX 0x13
+#define V_MIN 0x14
+#define V_MUL 0x15
+#define V_SGE 0x16
+#define V_SLT 0x17
+#define V_SUB 0x18
+#define V_XPD 0x19
+#define V_MAD 0x1A
+#define V_SWZ 0x1B
+
+/* fragment attribute binding */
+#define FRAGMENT_ATTRIB_COLOR 0x01
+#define FRAGMENT_ATTRIB_TEXCOORD 0x02
+#define FRAGMENT_ATTRIB_FOGCOORD 0x03
+#define FRAGMENT_ATTRIB_POSITION 0x04
+
+/* vertex attribute binding */
+#define VERTEX_ATTRIB_POSITION 0x01
+#define VERTEX_ATTRIB_WEIGHT 0x02
+#define VERTEX_ATTRIB_NORMAL 0x03
+#define VERTEX_ATTRIB_COLOR 0x04
+#define VERTEX_ATTRIB_FOGCOORD 0x05
+#define VERTEX_ATTRIB_TEXCOORD 0x06
+#define VERTEX_ATTRIB_MATRIXINDEX 0x07
+#define VERTEX_ATTRIB_GENERIC 0x08
+
+/* fragment result binding */
+#define FRAGMENT_RESULT_COLOR 0x01
+#define FRAGMENT_RESULT_DEPTH 0x02
+
+/* vertex result binding */
+#define VERTEX_RESULT_POSITION 0x01
+#define VERTEX_RESULT_COLOR 0x02
+#define VERTEX_RESULT_FOGCOORD 0x03
+#define VERTEX_RESULT_POINTSIZE 0x04
+#define VERTEX_RESULT_TEXCOORD 0x05
+
+/* texture target */
+#define TEXTARGET_1D 0x01
+#define TEXTARGET_2D 0x02
+#define TEXTARGET_3D 0x03
+#define TEXTARGET_RECT 0x04
+#define TEXTARGET_CUBE 0x05
+
+/* sign */
+/*
+$3: removed. '+' and '-' are used instead.
+*/
+/*
+#define SIGN_PLUS 0x00
+#define SIGN_MINUS 0x01
+*/
+
+/* face type */
+#define FACE_FRONT 0x00
+#define FACE_BACK 0x01
+
+/* color type */
+#define COLOR_PRIMARY 0x00
+#define COLOR_SECONDARY 0x01
+
+/* component */
+/*
+$3: Added enumerants.
+*/
+#define COMPONENT_X 0x00
+#define COMPONENT_Y 0x01
+#define COMPONENT_Z 0x02
+#define COMPONENT_W 0x03
+#define COMPONENT_0 0x04
+#define COMPONENT_1 0x05
+
+#define ARRAY_INDEX_ABSOLUTE 0x00
+#define ARRAY_INDEX_RELATIVE 0x01
+
+/* matrix name */
+#define MATRIX_MODELVIEW 0x01
+#define MATRIX_PROJECTION 0x02
+#define MATRIX_MVP 0x03
+#define MATRIX_TEXTURE 0x04
+#define MATRIX_PALETTE 0x05
+#define MATRIX_PROGRAM 0x06
+
+/* matrix modifier */
+#define MATRIX_MODIFIER_IDENTITY 0x00
+#define MATRIX_MODIFIER_INVERSE 0x01
+#define MATRIX_MODIFIER_TRANSPOSE 0x02
+#define MATRIX_MODIFIER_INVTRANS 0x03
+
+/* constant type */
+#define CONSTANT_SCALAR 0x01
+#define CONSTANT_VECTOR 0x02
+
+/* program param type */
+#define PROGRAM_PARAM_ENV 0x01
+#define PROGRAM_PARAM_LOCAL 0x02
+
+/* register type */
+#define REGISTER_ATTRIB 0x01
+#define REGISTER_PARAM 0x02
+#define REGISTER_RESULT 0x03
+#define REGISTER_ESTABLISHED_NAME 0x04
+
+/* param binding */
+#define PARAM_NULL 0x00
+#define PARAM_ARRAY_ELEMENT 0x01
+#define PARAM_STATE_ELEMENT 0x02
+#define PARAM_PROGRAM_ELEMENT 0x03
+#define PARAM_PROGRAM_ELEMENTS 0x04
+#define PARAM_CONSTANT 0x05
+
+/* param state property */
+#define STATE_MATERIAL_PARSER 0x01
+#define STATE_LIGHT_PARSER 0x02
+#define STATE_LIGHT_MODEL 0x03
+#define STATE_LIGHT_PROD 0x04
+#define STATE_FOG 0x05
+#define STATE_MATRIX_ROWS 0x06
+/* fragment program only */
+#define STATE_TEX_ENV 0x07
+#define STATE_DEPTH 0x08
+/* vertex program only */
+#define STATE_TEX_GEN 0x07
+#define STATE_CLIP_PLANE 0x08
+#define STATE_POINT 0x09
+
+/* state material property */
+#define MATERIAL_AMBIENT 0x01
+#define MATERIAL_DIFFUSE 0x02
+#define MATERIAL_SPECULAR 0x03
+#define MATERIAL_EMISSION 0x04
+#define MATERIAL_SHININESS 0x05
+
+/* state light property */
+#define LIGHT_AMBIENT 0x01
+#define LIGHT_DIFFUSE 0x02
+#define LIGHT_SPECULAR 0x03
+#define LIGHT_POSITION 0x04
+#define LIGHT_ATTENUATION 0x05
+#define LIGHT_HALF 0x06
+#define LIGHT_SPOT_DIRECTION 0x07
+
+/* state light model property */
+#define LIGHT_MODEL_AMBIENT 0x01
+#define LIGHT_MODEL_SCENECOLOR 0x02
+
+/* state light product property */
+#define LIGHT_PROD_AMBIENT 0x01
+#define LIGHT_PROD_DIFFUSE 0x02
+#define LIGHT_PROD_SPECULAR 0x03
+
+/* state texture environment property */
+#define TEX_ENV_COLOR 0x01
+
+/* state texture generation coord property */
+#define TEX_GEN_EYE 0x01
+#define TEX_GEN_OBJECT 0x02
+
+/* state fog property */
+#define FOG_COLOR 0x01
+#define FOG_PARAMS 0x02
+
+/* state depth property */
+#define DEPTH_RANGE 0x01
+
+/* state point parameters property */
+#define POINT_SIZE 0x01
+#define POINT_ATTENUATION 0x02
+
+/* declaration */
+#define ATTRIB 0x01
+#define PARAM 0x02
+#define TEMP 0x03
+#define OUTPUT 0x04
+#define ALIAS 0x05
+/* vertex program 1.0 only */
+#define ADDRESS 0x06
+
+/*
+ memory management routines
+*/
+static GLvoid *mem_alloc (GLsizei);
+static GLvoid mem_free (GLvoid **);
+static GLvoid *mem_realloc (GLvoid *, GLsizei, GLsizei);
+static byte *str_duplicate (const byte *);
+
+/*
+ internal error messages
+*/
+static const byte *OUT_OF_MEMORY =
+ (byte *) "internal error 1001: out of physical memory";
+static const byte *UNRESOLVED_REFERENCE =
+ (byte *) "internal error 1002: unresolved reference '$'";
+static const byte *INVALID_PARAMETER =
+ (byte *) "internal error 1003: invalid parameter";
+
+static const byte *error_message = NULL;
+static byte *error_param = NULL; /* this is inserted into error_message in place of $ */
+static GLint error_position = -1;
+
+static byte *unknown = (byte *) "???";
+
+static GLvoid
+clear_last_error ()
+{
+ /* reset error message */
+ error_message = NULL;
+
+ /* free error parameter - if error_param is a "???" don't free it - it's static */
+ if (error_param != unknown)
+ mem_free ((GLvoid **) & error_param);
+ else
+ error_param = NULL;
+
+ /* reset error position */
+ error_position = -1;
+}
+
+static GLvoid
+set_last_error (const byte * msg, byte * param, GLint pos)
+{
+ if (error_message != NULL)
+ return;
+
+ error_message = msg;
+ if (param != NULL)
+ error_param = param;
+ else
+ error_param = unknown;
+
+ error_position = pos;
+}
+
+/*
+ memory management routines
+*/
+static GLvoid *
+mem_alloc (GLsizei size)
+{
+ GLvoid *ptr = _mesa_malloc (size);
+ if (ptr == NULL)
+ set_last_error (OUT_OF_MEMORY, NULL, -1);
+ return ptr;
+}
+
+static GLvoid
+mem_free (GLvoid ** ptr)
+{
+ _mesa_free (*ptr);
+ *ptr = NULL;
+}
+
+static GLvoid *
+mem_realloc (GLvoid * ptr, GLsizei old_size, GLsizei new_size)
+{
+ GLvoid *ptr2 = _mesa_realloc (ptr, old_size, new_size);
+ if (ptr2 == NULL)
+ set_last_error (OUT_OF_MEMORY, NULL, -1);
+ return ptr2;
+}
+
+static byte *
+str_duplicate (const byte * str)
+{
+ return (byte *) _mesa_strdup ((const char *) str);
+}
+
+/*
+ emit type typedef
+*/
+typedef enum emit_type_
+{
+ et_byte, /* explicit number */
+ et_stream, /* eaten character */
+ et_position /* current position */
+}
+emit_type;
+
+/*
+ emit typedef
+*/
+typedef struct emit_
+{
+ emit_type m_emit_type;
+ byte m_byte; /* et_byte */
+ struct emit_ *m_next;
+}
+emit;
+
+static GLvoid
+emit_create (emit ** em)
+{
+ *em = mem_alloc (sizeof (emit));
+ if (*em) {
+ (**em).m_emit_type = et_byte;
+ (**em).m_byte = 0;
+ (**em).m_next = NULL;
+ }
+}
+
+static GLvoid
+emit_destroy (emit ** em)
+{
+ if (*em) {
+ emit_destroy (&(**em).m_next);
+ mem_free ((GLvoid **) em);
+ }
+}
+
+static GLvoid
+emit_append (emit ** em, emit ** ne)
+{
+ if (*em)
+ emit_append (&(**em).m_next, ne);
+ else
+ *em = *ne;
+}
+
+/*
+ * error typedef
+ */
+typedef struct error_
+{
+ byte *m_text;
+ byte *m_token_name;
+ struct defntn_ *m_token;
+}
+error;
+
+static GLvoid
+error_create (error ** er)
+{
+ *er = mem_alloc (sizeof (error));
+ if (*er) {
+ (**er).m_text = NULL;
+ (**er).m_token_name = NULL;
+ (**er).m_token = NULL;
+ }
+}
+
+static GLvoid
+error_destroy (error ** er)
+{
+ if (*er) {
+ mem_free ((GLvoid **) & (**er).m_text);
+ mem_free ((GLvoid **) & (**er).m_token_name);
+ mem_free ((GLvoid **) er);
+ }
+}
+
+struct dict_;
+static byte *error_get_token (error *, struct dict_ *, const byte *, GLuint);
+
+/*
+ * specifier type typedef
+*/
+typedef enum spec_type_
+{
+ st_false,
+ st_true,
+ st_byte,
+ st_byte_range,
+ st_string,
+ st_identifier,
+ st_identifier_loop,
+ st_debug
+} spec_type;
+
+
+/*
+ * specifier typedef
+ */
+typedef struct spec_
+{
+ spec_type m_spec_type;
+ byte m_byte[2]; /* st_byte, st_byte_range */
+ byte *m_string; /* st_string */
+ struct defntn_ *m_defntn; /* st_identifier, st_identifier_loop */
+ emit *m_emits;
+ error *m_errtext;
+ struct spec_ *m_next;
+} spec;
+
+
+static GLvoid
+spec_create (spec ** sp)
+{
+ *sp = mem_alloc (sizeof (spec));
+ if (*sp) {
+ (**sp).m_spec_type = st_false;
+ (**sp).m_byte[0] = '\0';
+ (**sp).m_byte[1] = '\0';
+ (**sp).m_string = NULL;
+ (**sp).m_defntn = NULL;
+ (**sp).m_emits = NULL;
+ (**sp).m_errtext = NULL;
+ (**sp).m_next = NULL;
+ }
+}
+
+static GLvoid
+spec_destroy (spec ** sp)
+{
+ if (*sp) {
+ spec_destroy (&(**sp).m_next);
+ emit_destroy (&(**sp).m_emits);
+ error_destroy (&(**sp).m_errtext);
+ mem_free ((GLvoid **) & (**sp).m_string);
+ mem_free ((GLvoid **) sp);
+ }
+}
+
+static GLvoid
+spec_append (spec ** sp, spec ** ns)
+{
+ if (*sp)
+ spec_append (&(**sp).m_next, ns);
+ else
+ *sp = *ns;
+}
+
+/*
+ * operator typedef
+ */
+typedef enum oper_
+{
+ op_none,
+ op_and,
+ op_or
+} oper;
+
+
+/*
+ * definition typedef
+ */
+typedef struct defntn_
+{
+ oper m_oper;
+ spec *m_specs;
+ struct defntn_ *m_next;
+#ifndef NDEBUG
+ GLint m_referenced;
+#endif
+} defntn;
+
+
+static GLvoid
+defntn_create (defntn ** de)
+{
+ *de = mem_alloc (sizeof (defntn));
+ if (*de) {
+ (**de).m_oper = op_none;
+ (**de).m_specs = NULL;
+ (**de).m_next = NULL;
+#ifndef NDEBUG
+ (**de).m_referenced = 0;
+#endif
+ }
+}
+
+static GLvoid
+defntn_destroy (defntn ** de)
+{
+ if (*de) {
+ defntn_destroy (&(**de).m_next);
+ spec_destroy (&(**de).m_specs);
+ mem_free ((GLvoid **) de);
+ }
+}
+
+static GLvoid
+defntn_append (defntn ** de, defntn ** nd)
+{
+ if (*de)
+ defntn_append (&(**de).m_next, nd);
+ else
+ *de = *nd;
+}
+
+/*
+ * dictionary typedef
+ */
+typedef struct dict_
+{
+ defntn *m_defntns;
+ defntn *m_syntax;
+ defntn *m_string;
+ struct dict_ *m_next;
+} dict;
+
+
+static GLvoid
+dict_create (dict ** di)
+{
+ *di = mem_alloc (sizeof (dict));
+ if (*di) {
+ (**di).m_defntns = NULL;
+ (**di).m_syntax = NULL;
+ (**di).m_string = NULL;
+ (**di).m_next = NULL;
+ }
+}
+
+static GLvoid
+dict_destroy (dict ** di)
+{
+ if (*di) {
+ dict_destroy (&(**di).m_next);
+ defntn_destroy (&(**di).m_defntns);
+ mem_free ((GLvoid **) di);
+ }
+}
+
+/*
+ * byte array typedef
+ */
+typedef struct barray_
+{
+ byte *data;
+ GLuint len;
+} barray;
+
+
+static GLvoid
+barray_create (barray ** ba)
+{
+ *ba = mem_alloc (sizeof (barray));
+ if (*ba) {
+ (**ba).data = NULL;
+ (**ba).len = 0;
+ }
+}
+
+static GLvoid
+barray_destroy (barray ** ba)
+{
+ if (*ba) {
+ mem_free ((GLvoid **) & (**ba).data);
+ mem_free ((GLvoid **) ba);
+ }
+}
+
+/*
+ * reallocates byte array to requested size,
+ * returns 0 on success,
+ * returns 1 otherwise
+ */
+static GLint
+barray_resize (barray ** ba, GLuint nlen)
+{
+ byte *new_pointer;
+
+ if (nlen == 0) {
+ mem_free ((void **) &(**ba).data);
+ (**ba).data = NULL;
+ (**ba).len = 0;
+
+ return 0;
+ }
+ else {
+ new_pointer =
+ mem_realloc ((**ba).data, (**ba).len * sizeof (byte),
+ nlen * sizeof (byte));
+ if (new_pointer) {
+ (**ba).data = new_pointer;
+ (**ba).len = nlen;
+
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+/*
+ * adds byte array pointed by *nb to the end of array pointed by *ba,
+ * returns 0 on success,
+ * returns 1 otherwise
+ */
+static GLint
+barray_append (barray ** ba, barray ** nb)
+{
+ GLuint i;
+ const GLuint len = (**ba).len;
+
+ if (barray_resize (ba, (**ba).len + (**nb).len))
+ return 1;
+
+ for (i = 0; i < (**nb).len; i++)
+ (**ba).data[len + i] = (**nb).data[i];
+
+ return 0;
+}
+
+/*
+ * adds emit chain pointed by em to the end of array pointed by *ba,
+ * returns 0 on success,
+ * returns 1 otherwise
+ */
+static GLint
+barray_push (barray ** ba, emit * em, byte c, GLuint pos)
+{
+ emit *temp = em;
+ GLuint count = 0;
+
+ while (temp) {
+ if (temp->m_emit_type == et_position)
+ count += 4; /* position is a 32-bit unsigned integer */
+ else
+ count++;
+
+ temp = temp->m_next;
+ }
+
+ if (barray_resize (ba, (**ba).len + count))
+ return 1;
+
+ while (em) {
+ if (em->m_emit_type == et_byte)
+ (**ba).data[(**ba).len - count--] = em->m_byte;
+ else if (em->m_emit_type == et_stream)
+ (**ba).data[(**ba).len - count--] = c;
+
+ /* This is where the position is emitted into the stream */
+ else { /* em->type == et_position */
+#if 0
+ (**ba).data[(**ba).len - count--] = (byte) pos,
+ (**ba).data[(**ba).len - count--] = (byte) (pos >> 8),
+ (**ba).data[(**ba).len - count--] = (byte) (pos >> 16),
+ (**ba).data[(**ba).len - count--] = (byte) (pos >> 24);
+#else
+ (**ba).data[(**ba).len - count--] = (byte) pos;
+ (**ba).data[(**ba).len - count--] = (byte) (pos / 0x100);
+ (**ba).data[(**ba).len - count--] = (byte) (pos / 0x10000);
+ (**ba).data[(**ba).len - count--] = (byte) (pos / 0x1000000);
+#endif
+ }
+
+ em = em->m_next;
+ }
+
+ return 0;
+}
+
+/*
+ * string to string map typedef
+ */
+typedef struct map_str_
+{
+ byte *key;
+ byte *data;
+ struct map_str_ *next;
+} map_str;
+
+
+static GLvoid
+map_str_create (map_str ** ma)
+{
+ *ma = mem_alloc (sizeof (map_str));
+ if (*ma) {
+ (**ma).key = NULL;
+ (**ma).data = NULL;
+ (**ma).next = NULL;
+ }
+}
+
+static GLvoid
+map_str_destroy (map_str ** ma)
+{
+ if (*ma) {
+ map_str_destroy (&(**ma).next);
+ mem_free ((GLvoid **) & (**ma).key);
+ mem_free ((GLvoid **) & (**ma).data);
+ mem_free ((GLvoid **) ma);
+ }
+}
+
+static GLvoid
+map_str_append (map_str ** ma, map_str ** nm)
+{
+ if (*ma)
+ map_str_append (&(**ma).next, nm);
+ else
+ *ma = *nm;
+}
+
+/*
+ * searches the map for specified key,
+ * if the key is matched, *data is filled with data associated with the key,
+ * returns 0 if the key is matched,
+ * returns 1 otherwise
+ */
+static GLint
+map_str_find (map_str ** ma, const byte * key, byte ** data)
+{
+ while (*ma) {
+ if (strcmp ((const char *) (**ma).key, (const char *) key) == 0) {
+ *data = str_duplicate ((**ma).data);
+ if (*data == NULL)
+ return 1;
+
+ return 0;
+ }
+
+ ma = &(**ma).next;
+ }
+
+ set_last_error (UNRESOLVED_REFERENCE, str_duplicate (key), -1);
+ return 1;
+}
+
+/*
+ * string to byte map typedef
+ */
+typedef struct map_byte_
+{
+ byte *key;
+ byte data;
+ struct map_byte_ *next;
+} map_byte;
+
+static GLvoid
+map_byte_create (map_byte ** ma)
+{
+ *ma = mem_alloc (sizeof (map_byte));
+ if (*ma) {
+ (**ma).key = NULL;
+ (**ma).data = 0;
+ (**ma).next = NULL;
+ }
+}
+
+static GLvoid
+map_byte_destroy (map_byte ** ma)
+{
+ if (*ma) {
+ map_byte_destroy (&(**ma).next);
+ mem_free ((GLvoid **) & (**ma).key);
+ mem_free ((GLvoid **) ma);
+ }
+}
+
+static GLvoid
+map_byte_append (map_byte ** ma, map_byte ** nm)
+{
+ if (*ma)
+ map_byte_append (&(**ma).next, nm);
+ else
+ *ma = *nm;
+}
+
+/*
+ * searches the map for specified key,
+ * if the key is matched, *data is filled with data associated with the key,
+ * returns 0 if the is matched,
+ * returns 1 otherwise
+ */
+static GLint
+map_byte_find (map_byte ** ma, const byte * key, byte * data)
+{
+ while (*ma) {
+ if (strcmp ((const char *) (**ma).key, (const char *) key) == 0) {
+ *data = (**ma).data;
+ return 0;
+ }
+
+ ma = &(**ma).next;
+ }
+
+ set_last_error (UNRESOLVED_REFERENCE, str_duplicate (key), -1);
+ return 1;
+}
+
+/*
+ * string to defntn map typedef
+ */
+typedef struct map_def_
+{
+ byte *key;
+ defntn *data;
+ struct map_def_ *next;
+} map_def;
+
+static GLvoid
+map_def_create (map_def ** ma)
+{
+ *ma = mem_alloc (sizeof (map_def));
+ if (*ma) {
+ (**ma).key = NULL;
+ (**ma).data = NULL;
+ (**ma).next = NULL;
+ }
+}
+
+static GLvoid
+map_def_destroy (map_def ** ma)
+{
+ if (*ma) {
+ map_def_destroy (&(**ma).next);
+ mem_free ((GLvoid **) & (**ma).key);
+ mem_free ((GLvoid **) ma);
+ }
+}
+
+static GLvoid
+map_def_append (map_def ** ma, map_def ** nm)
+{
+ if (*ma)
+ map_def_append (&(**ma).next, nm);
+ else
+ *ma = *nm;
+}
+
+/*
+ * searches the map for specified key,
+ * if the key is matched, *data is filled with data associated with the key,
+ * returns 0 if the is matched,
+ * returns 1 otherwise
+ */
+static GLint
+map_def_find (map_def ** ma, const byte * key, defntn ** data)
+{
+ while (*ma) {
+ if (_mesa_strcmp ((const char *) (**ma).key, (const char *) key) == 0) {
+ *data = (**ma).data;
+
+ return 0;
+ }
+
+ ma = &(**ma).next;
+ }
+
+ set_last_error (UNRESOLVED_REFERENCE, str_duplicate (key), -1);
+ return 1;
+}
+
+/*
+ * returns 1 if given character is a space,
+ * returns 0 otherwise
+ */
+static GLint
+is_space (byte c)
+{
+ return c == ' ' || c == '\t' || c == '\n' || c == '\r';
+}
+
+/*
+ * advances text pointer by 1 if character pointed by *text is a space,
+ * returns 1 if a space has been eaten,
+ * returns 0 otherwise
+ */
+static GLint
+eat_space (const byte ** text)
+{
+ if (is_space (**text)) {
+ (*text)++;
+
+ return 1;
+ }
+
+ return 0;
+}
+
+/*
+ * returns 1 if text points to C-style comment start string "/ *",
+ * returns 0 otherwise
+ */
+static GLint
+is_comment_start (const byte * text)
+{
+ return text[0] == '/' && text[1] == '*';
+}
+
+/*
+ * advances text pointer to first character after C-style comment block - if any,
+ * returns 1 if C-style comment block has been encountered and eaten,
+ * returns 0 otherwise
+ */
+static GLint
+eat_comment (const byte ** text)
+{
+ if (is_comment_start (*text)) {
+ /* *text points to comment block - skip two characters to enter comment body */
+ *text += 2;
+ /* skip any character except consecutive '*' and '/' */
+ while (!((*text)[0] == '*' && (*text)[1] == '/'))
+ (*text)++;
+ /* skip those two terminating characters */
+ *text += 2;
+
+ return 1;
+ }
+
+ return 0;
+}
+
+/*
+ * advances text pointer to first character that is neither space nor C-style comment block
+ */
+static GLvoid
+eat_spaces (const byte ** text)
+{
+ while (eat_space (text) || eat_comment (text));
+}
+
+/*
+ * resizes string pointed by *ptr to successfully add character c to the end of the string,
+ * returns 0 on success,
+ * returns 1 otherwise
+ */
+static GLint
+string_grow (byte ** ptr, GLuint * len, byte c)
+{
+ /* reallocate the string in 16-length increments */
+ if ((*len & 0x0F) == 0x0F || *ptr == NULL) {
+ byte *tmp = mem_realloc (*ptr, (*len) * sizeof (byte),
+ ((*len + 1 + 1 +
+ 0x0F) & ~0x0F) * sizeof (byte));
+ if (tmp == NULL)
+ return 1;
+
+ *ptr = tmp;
+ }
+
+ if (c) {
+ /* append given character */
+ (*ptr)[*len] = c;
+ (*len)++;
+ }
+ (*ptr)[*len] = '\0';
+
+ return 0;
+}
+
+/*
+ * returns 1 if given character is valid identifier character a-z, A-Z, 0-9 or _
+ * returns 0 otherwise
+ */
+static GLint
+is_identifier (byte c)
+{
+ return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') ||
+ (c >= '0' && c <= '9') || c == '_';
+}
+
+/*
+ * copies characters from *text to *id until non-identifier character is encountered,
+ * assumes that *id points to NULL object - caller is responsible for later freeing the string,
+ * text pointer is advanced to point past the copied identifier,
+ * returns 0 if identifier was successfully copied,
+ * returns 1 otherwise
+ */
+static GLint
+get_identifier (const byte ** text, byte ** id)
+{
+ const byte *t = *text;
+ byte *p = NULL;
+ GLuint len = 0;
+
+ if (string_grow (&p, &len, '\0'))
+ return 1;
+
+ /* loop while next character in buffer is valid for identifiers */
+ while (is_identifier (*t)) {
+ if (string_grow (&p, &len, *t++)) {
+ mem_free ((GLvoid **) & p);
+ return 1;
+ }
+ }
+
+ *text = t;
+ *id = p;
+
+ return 0;
+}
+
+/*
+ * returns 1 if given character is HEX digit 0-9, A-F or a-f,
+ * returns 0 otherwise
+ */
+static GLint
+is_hex (byte c)
+{
+ return (c >= '0' && c <= '9') || (c >= 'A' && c <= 'F') || (c >= 'a'
+ && c <= 'f');
+}
+
+/*
+ * returns value of passed character as if it was HEX digit
+ */
+static GLuint
+hex2dec (byte c)
+{
+ if (c >= '0' && c <= '9')
+ return c - '0';
+ if (c >= 'A' && c <= 'F')
+ return c - 'A' + 10;
+ return c - 'a' + 10;
+}
+
+/*
+ * converts sequence of HEX digits pointed by *text until non-HEX digit is encountered,
+ * advances text pointer past the converted sequence,
+ * returns the converted value
+ */
+static GLuint
+hex_convert (const byte ** text)
+{
+ GLuint value = 0;
+
+ while (is_hex (**text)) {
+ value = value * 0x10 + hex2dec (**text);
+ (*text)++;
+ }
+
+ return value;
+}
+
+/*
+ * returns 1 if given character is OCT digit 0-7,
+ * returns 0 otherwise
+ */
+static GLint
+is_oct (byte c)
+{
+ return c >= '0' && c <= '7';
+}
+
+/*
+ * returns value of passed character as if it was OCT digit
+ */
+static GLint
+oct2dec (byte c)
+{
+ return c - '0';
+}
+
+static byte
+get_escape_sequence (const byte ** text)
+{
+ GLint value = 0;
+
+ /* skip '\' character */
+ (*text)++;
+
+ switch (*(*text)++) {
+ case '\'':
+ return '\'';
+ case '"':
+ return '\"';
+ case '?':
+ return '\?';
+ case '\\':
+ return '\\';
+ case 'a':
+ return '\a';
+ case 'b':
+ return '\b';
+ case 'f':
+ return '\f';
+ case 'n':
+ return '\n';
+ case 'r':
+ return '\r';
+ case 't':
+ return '\t';
+ case 'v':
+ return '\v';
+ case 'x':
+ return (byte) hex_convert (text);
+ }
+
+ (*text)--;
+ if (is_oct (**text)) {
+ value = oct2dec (*(*text)++);
+ if (is_oct (**text)) {
+ value = value * 010 + oct2dec (*(*text)++);
+ if (is_oct (**text))
+ value = value * 010 + oct2dec (*(*text)++);
+ }
+ }
+
+ return (byte) value;
+}
+
+/*
+ * copies characters from *text to *str until " or ' character is encountered,
+ * assumes that *str points to NULL object - caller is responsible for later freeing the string,
+ * assumes that *text points to " or ' character that starts the string,
+ * text pointer is advanced to point past the " or ' character,
+ * returns 0 if string was successfully copied,
+ * returns 1 otherwise
+ */
+static GLint
+get_string (const byte ** text, byte ** str)
+{
+ const byte *t = *text;
+ byte *p = NULL;
+ GLuint len = 0;
+ byte term_char;
+
+ if (string_grow (&p, &len, '\0'))
+ return 1;
+
+ /* read " or ' character that starts the string */
+ term_char = *t++;
+ /* while next character is not the terminating character */
+ while (*t && *t != term_char) {
+ byte c;
+
+ if (*t == '\\')
+ c = get_escape_sequence (&t);
+ else
+ c = *t++;
+
+ if (string_grow (&p, &len, c)) {
+ mem_free ((GLvoid **) & p);
+ return 1;
+ }
+ }
+
+ /* skip " or ' character that ends the string */
+ t++;
+
+ *text = t;
+ *str = p;
+ return 0;
+}
+
+/*
+ * gets emit code, the syntax is: ".emtcode" " " <symbol> " " ("0x" | "0X") <hex_value>
+ * assumes that *text already points to <symbol>,
+ * returns 0 if emit code is successfully read,
+ * returns 1 otherwise
+ */
+static GLint
+get_emtcode (const byte ** text, map_byte ** ma)
+{
+ const byte *t = *text;
+ map_byte *m = NULL;
+
+ map_byte_create (&m);
+ if (m == NULL)
+ return 1;
+
+ if (get_identifier (&t, &m->key)) {
+ map_byte_destroy (&m);
+ return 1;
+ }
+ eat_spaces (&t);
+
+ if (*t == '\'') {
+ byte *c;
+
+ if (get_string (&t, &c)) {
+ map_byte_destroy (&m);
+ return 1;
+ }
+
+ m->data = (byte) c[0];
+ mem_free ((GLvoid **) & c);
+ }
+ else {
+ /* skip HEX "0x" or "0X" prefix */
+ t += 2;
+ m->data = (byte) hex_convert (&t);
+ }
+
+ eat_spaces (&t);
+
+ *text = t;
+ *ma = m;
+ return 0;
+}
+
+/*
+ * returns 0 on success,
+ * returns 1 otherwise
+ */
+static GLint
+get_errtext (const byte ** text, map_str ** ma)
+{
+ const byte *t = *text;
+ map_str *m = NULL;
+
+ map_str_create (&m);
+ if (m == NULL)
+ return 1;
+
+ if (get_identifier (&t, &m->key)) {
+ map_str_destroy (&m);
+ return 1;
+ }
+ eat_spaces (&t);
+
+ if (get_string (&t, &m->data)) {
+ map_str_destroy (&m);
+ return 1;
+ }
+ eat_spaces (&t);
+
+ *text = t;
+ *ma = m;
+ return 0;
+}
+
+/*
+ * returns 0 on success,
+ * returns 1 otherwise,
+ */
+static GLint
+get_error (const byte ** text, error ** er, map_str * maps)
+{
+ const byte *t = *text;
+ byte *temp = NULL;
+
+ if (*t != '.')
+ return 0;
+
+ t++;
+ if (get_identifier (&t, &temp))
+ return 1;
+ eat_spaces (&t);
+
+ if (_mesa_strcmp ("error", (char *) temp) != 0) {
+ mem_free ((GLvoid **) & temp);
+ return 0;
+ }
+
+ mem_free ((GLvoid **) & temp);
+
+ error_create (er);
+ if (*er == NULL)
+ return 1;
+
+ if (*t == '\"') {
+ if (get_string (&t, &(**er).m_text)) {
+ error_destroy (er);
+ return 1;
+ }
+ eat_spaces (&t);
+ }
+ else {
+ if (get_identifier (&t, &temp)) {
+ error_destroy (er);
+ return 1;
+ }
+ eat_spaces (&t);
+
+ if (map_str_find (&maps, temp, &(**er).m_text)) {
+ mem_free ((GLvoid **) & temp);
+ error_destroy (er);
+ return 1;
+ }
+
+ mem_free ((GLvoid **) & temp);
+ }
+
+ /* try to extract "token" from "...$token$..." */
+ {
+ char *processed = NULL;
+ GLuint len = 0, i = 0;
+
+ if (string_grow ((byte **) (&processed), &len, '\0')) {
+ error_destroy (er);
+ return 1;
+ }
+
+ while (i < _mesa_strlen ((char *) ((**er).m_text))) {
+ /* check if the dollar sign is repeated - if so skip it */
+ if ((**er).m_text[i] == '$' && (**er).m_text[i + 1] == '$') {
+ if (string_grow ((byte **) (&processed), &len, '$')) {
+ mem_free ((GLvoid **) & processed);
+ error_destroy (er);
+ return 1;
+ }
+
+ i += 2;
+ }
+ else if ((**er).m_text[i] != '$') {
+ if (string_grow ((byte **) (&processed), &len, (**er).m_text[i])) {
+ mem_free ((GLvoid **) & processed);
+ error_destroy (er);
+ return 1;
+ }
+
+ i++;
+ }
+ else {
+ if (string_grow ((byte **) (&processed), &len, '$')) {
+ mem_free ((GLvoid **) & processed);
+ error_destroy (er);
+ return 1;
+ }
+
+ {
+ /* length of token being extracted */
+ GLuint tlen = 0;
+
+ if (string_grow (&(**er).m_token_name, &tlen, '\0')) {
+ mem_free ((GLvoid **) & processed);
+ error_destroy (er);
+ return 1;
+ }
+
+ /* skip the dollar sign */
+ i++;
+
+ while ((**er).m_text[i] != '$') {
+ if (string_grow
+ (&(**er).m_token_name, &tlen, (**er).m_text[i])) {
+ mem_free ((GLvoid **) & processed);
+ error_destroy (er);
+ return 1;
+ }
+
+ i++;
+ }
+
+ /* skip the dollar sign */
+ i++;
+ }
+ }
+ }
+
+ mem_free ((GLvoid **) & (**er).m_text);
+ (**er).m_text = (byte *) processed;
+ }
+
+ *text = t;
+ return 0;
+}
+
+/*
+ * returns 0 on success,
+ * returns 1 otherwise,
+ */
+static GLint
+get_emits (const byte ** text, emit ** em, map_byte * mapb)
+{
+ const byte *t = *text;
+ byte *temp = NULL;
+ emit *e = NULL;
+
+ if (*t != '.')
+ return 0;
+
+ t++;
+ if (get_identifier (&t, &temp))
+ return 1;
+ eat_spaces (&t);
+
+ /* .emit */
+ if (_mesa_strcmp ("emit", (char *) temp) != 0) {
+ mem_free ((GLvoid **) & temp);
+ return 0;
+ }
+
+ mem_free ((GLvoid **) & temp);
+
+ emit_create (&e);
+ if (e == NULL)
+ return 1;
+
+ /* 0xNN */
+ if (*t == '0') {
+ t += 2;
+ e->m_byte = (byte) hex_convert (&t);
+
+ e->m_emit_type = et_byte;
+ }
+ /* * */
+ else if (*t == '*') {
+ t++;
+
+ e->m_emit_type = et_stream;
+ }
+ /* $ */
+ else if (*t == '$') {
+ t++;
+
+ e->m_emit_type = et_position;
+ }
+ /* 'c' */
+ else if (*t == '\'') {
+ if (get_string (&t, &temp)) {
+ emit_destroy (&e);
+ return 1;
+ }
+ e->m_byte = (byte) temp[0];
+
+ mem_free ((GLvoid **) & temp);
+
+ e->m_emit_type = et_byte;
+ }
+ else {
+ if (get_identifier (&t, &temp)) {
+ emit_destroy (&e);
+ return 1;
+ }
+
+ if (map_byte_find (&mapb, temp, &e->m_byte)) {
+ mem_free ((GLvoid **) & temp);
+ emit_destroy (&e);
+ return 1;
+ }
+
+ mem_free ((GLvoid **) & temp);
+
+ e->m_emit_type = et_byte;
+ }
+
+ eat_spaces (&t);
+
+ if (get_emits (&t, &e->m_next, mapb)) {
+ emit_destroy (&e);
+ return 1;
+ }
+
+ *text = t;
+ *em = e;
+ return 0;
+}
+
+/*
+ * returns 0 on success,
+ * returns 1 otherwise,
+ */
+static GLint
+get_spec (const byte ** text, spec ** sp, map_str * maps, map_byte * mapb)
+{
+ const byte *t = *text;
+ spec *s = NULL;
+
+ spec_create (&s);
+ if (s == NULL)
+ return 1;
+
+ if (*t == '\'') {
+ byte *temp = NULL;
+
+ if (get_string (&t, &temp)) {
+ spec_destroy (&s);
+ return 1;
+ }
+ eat_spaces (&t);
+
+ if (*t == '-') {
+ byte *temp2 = NULL;
+
+ /* skip the '-' character */
+ t++;
+ eat_spaces (&t);
+
+ if (get_string (&t, &temp2)) {
+ mem_free ((GLvoid **) & temp);
+ spec_destroy (&s);
+ return 1;
+ }
+ eat_spaces (&t);
+
+ s->m_spec_type = st_byte_range;
+ s->m_byte[0] = *temp;
+ s->m_byte[1] = *temp2;
+
+ mem_free ((GLvoid **) & temp2);
+ }
+ else {
+ s->m_spec_type = st_byte;
+ *s->m_byte = *temp;
+ }
+
+ mem_free ((GLvoid **) & temp);
+ }
+ else if (*t == '"') {
+ if (get_string (&t, &s->m_string)) {
+ spec_destroy (&s);
+ return 1;
+ }
+ eat_spaces (&t);
+
+ s->m_spec_type = st_string;
+ }
+ else if (*t == '.') {
+ byte *keyword = NULL;
+
+ /* skip the dot */
+ t++;
+
+ if (get_identifier (&t, &keyword)) {
+ spec_destroy (&s);
+ return 1;
+ }
+ eat_spaces (&t);
+
+ /* .true */
+ if (_mesa_strcmp ("true", (char *) keyword) == 0) {
+ s->m_spec_type = st_true;
+ }
+ /* .false */
+ else if (_mesa_strcmp ("false", (char *) keyword) == 0) {
+ s->m_spec_type = st_false;
+ }
+ /* .debug */
+ else if (_mesa_strcmp ("debug", (char *) keyword) == 0) {
+ s->m_spec_type = st_debug;
+ }
+ /* .loop */
+ else if (_mesa_strcmp ("loop", (char *) keyword) == 0) {
+ if (get_identifier (&t, &s->m_string)) {
+ mem_free ((GLvoid **) & keyword);
+ spec_destroy (&s);
+ return 1;
+ }
+ eat_spaces (&t);
+
+ s->m_spec_type = st_identifier_loop;
+ }
+
+ mem_free ((GLvoid **) & keyword);
+ }
+ else {
+ if (get_identifier (&t, &s->m_string)) {
+ spec_destroy (&s);
+ return 1;
+ }
+ eat_spaces (&t);
+
+ s->m_spec_type = st_identifier;
+ }
+
+ if (get_error (&t, &s->m_errtext, maps)) {
+ spec_destroy (&s);
+ return 1;
+ }
+
+ if (get_emits (&t, &s->m_emits, mapb)) {
+ spec_destroy (&s);
+ return 1;
+ }
+
+ *text = t;
+ *sp = s;
+ return 0;
+}
+
+/*
+ * returns 0 on success,
+ * returns 1 otherwise,
+ */
+static GLint
+get_definition (const byte ** text, defntn ** de, map_str * maps,
+ map_byte * mapb)
+{
+ const byte *t = *text;
+ defntn *d = NULL;
+
+ defntn_create (&d);
+ if (d == NULL)
+ return 1;
+
+ if (get_spec (&t, &d->m_specs, maps, mapb)) {
+ defntn_destroy (&d);
+ return 1;
+ }
+
+ while (*t != ';') {
+ byte *op = NULL;
+ spec *sp = NULL;
+
+ /* skip the dot that precedes "and" or "or" */
+ t++;
+
+ /* read "and" or "or" keyword */
+ if (get_identifier (&t, &op)) {
+ defntn_destroy (&d);
+ return 1;
+ }
+ eat_spaces (&t);
+
+ if (d->m_oper == op_none) {
+ /* .and */
+ if (_mesa_strcmp ("and", (char *) op) == 0)
+ d->m_oper = op_and;
+ /* .or */
+ else
+ d->m_oper = op_or;
+ }
+
+ mem_free ((GLvoid **) & op);
+
+ if (get_spec (&t, &sp, maps, mapb)) {
+ defntn_destroy (&d);
+ return 1;
+ }
+
+ spec_append (&d->m_specs, &sp);
+ }
+
+ /* skip the semicolon */
+ t++;
+ eat_spaces (&t);
+
+ *text = t;
+ *de = d;
+ return 0;
+}
+
+/*
+ * returns 0 on success,
+ * returns 1 otherwise,
+ */
+static GLint
+update_dependency (map_def * mapd, byte * symbol, defntn ** def)
+{
+ if (map_def_find (&mapd, symbol, def))
+ return 1;
+
+#ifndef NDEBUG
+ (**def).m_referenced = 1;
+#endif
+
+ return 0;
+}
+
+/*
+ * returns 0 on success,
+ * returns 1 otherwise,
+ */
+static GLint
+update_dependencies (dict * di, map_def * mapd, byte ** syntax_symbol,
+ byte ** string_symbol)
+{
+ defntn *de = di->m_defntns;
+
+ if (update_dependency (mapd, *syntax_symbol, &di->m_syntax) ||
+ (*string_symbol != NULL
+ && update_dependency (mapd, *string_symbol, &di->m_string)))
+ return 1;
+
+ mem_free ((GLvoid **) syntax_symbol);
+ mem_free ((GLvoid **) string_symbol);
+
+ while (de) {
+ spec *sp = de->m_specs;
+
+ while (sp) {
+ if (sp->m_spec_type == st_identifier
+ || sp->m_spec_type == st_identifier_loop) {
+ if (update_dependency (mapd, sp->m_string, &sp->m_defntn))
+ return 1;
+
+ mem_free ((GLvoid **) & sp->m_string);
+ }
+
+ if (sp->m_errtext && sp->m_errtext->m_token_name) {
+ if (update_dependency
+ (mapd, sp->m_errtext->m_token_name, &sp->m_errtext->m_token))
+ return 1;
+
+ mem_free ((GLvoid **) & sp->m_errtext->m_token_name);
+ }
+
+ sp = sp->m_next;
+ }
+
+ de = de->m_next;
+ }
+
+ return 0;
+}
+
+typedef enum match_result_
+{
+ mr_not_matched, /* the examined string does not match */
+ mr_matched, /* the examined string matches */
+ mr_error_raised, /* mr_not_matched + error has been raised */
+ mr_dont_emit, /* used by identifier loops only */
+ mr_internal_error /* an internal error has occured such as out of memory */
+} match_result;
+
+static match_result
+match (dict * di, const byte * text, GLuint * index, defntn * de,
+ barray ** ba, GLint filtering_string)
+{
+ GLuint ind = *index;
+ match_result status = mr_not_matched;
+ spec *sp = de->m_specs;
+
+ /* for every specifier in the definition */
+ while (sp) {
+ GLuint i, len, save_ind = ind;
+ barray *array = NULL;
+
+ switch (sp->m_spec_type) {
+ case st_identifier:
+ barray_create (&array);
+ if (array == NULL)
+ return mr_internal_error;
+
+ status =
+ match (di, text, &ind, sp->m_defntn, &array, filtering_string);
+ if (status == mr_internal_error) {
+ barray_destroy (&array);
+ return mr_internal_error;
+ }
+ break;
+ case st_string:
+ len = _mesa_strlen ((char *) (sp->m_string));
+
+ /* prefilter the stream */
+ if (!filtering_string && di->m_string) {
+ barray *ba;
+ GLuint filter_index = 0;
+ match_result result;
+
+ barray_create (&ba);
+ if (ba == NULL)
+ return mr_internal_error;
+
+ result =
+ match (di, text + ind, &filter_index, di->m_string, &ba, 1);
+
+ if (result == mr_internal_error) {
+ barray_destroy (&ba);
+ return mr_internal_error;
+ }
+
+ if (result != mr_matched) {
+ barray_destroy (&ba);
+ status = mr_not_matched;
+ break;
+ }
+
+ barray_destroy (&ba);
+
+ if (filter_index != len
+ || _mesa_strncmp ((char *)sp->m_string, (char *)(text + ind), len)) {
+ status = mr_not_matched;
+ break;
+ }
+
+ status = mr_matched;
+ ind += len;
+ }
+ else {
+ status = mr_matched;
+ for (i = 0; status == mr_matched && i < len; i++)
+ if (text[ind + i] != sp->m_string[i])
+ status = mr_not_matched;
+ if (status == mr_matched)
+ ind += len;
+ }
+ break;
+ case st_byte:
+ status = text[ind] == *sp->m_byte ? mr_matched : mr_not_matched;
+ if (status == mr_matched)
+ ind++;
+ break;
+ case st_byte_range:
+ status = (text[ind] >= sp->m_byte[0]
+ && text[ind] <=
+ sp->m_byte[1]) ? mr_matched : mr_not_matched;
+ if (status == mr_matched)
+ ind++;
+ break;
+ case st_true:
+ status = mr_matched;
+ break;
+ case st_false:
+ status = mr_not_matched;
+ break;
+ case st_debug:
+ status = mr_matched;
+ break;
+ case st_identifier_loop:
+ barray_create (&array);
+ if (array == NULL)
+ return mr_internal_error;
+
+ status = mr_dont_emit;
+ for (;;) {
+ match_result result;
+
+ save_ind = ind;
+ result =
+ match (di, text, &ind, sp->m_defntn, &array,
+ filtering_string);
+
+ if (result == mr_error_raised) {
+ status = result;
+ break;
+ }
+ else if (result == mr_matched) {
+ if (barray_push (ba, sp->m_emits, text[ind - 1], save_ind)
+ || barray_append (ba, &array)) {
+ barray_destroy (&array);
+ return mr_internal_error;
+ }
+ barray_destroy (&array);
+ barray_create (&array);
+ if (array == NULL)
+ return mr_internal_error;
+ }
+ else if (result == mr_internal_error) {
+ barray_destroy (&array);
+ return mr_internal_error;
+ }
+ else
+ break;
+ }
+ break;
+ };
+
+ if (status == mr_error_raised) {
+ barray_destroy (&array);
+
+ return mr_error_raised;
+ }
+
+ if (de->m_oper == op_and && status != mr_matched
+ && status != mr_dont_emit) {
+ barray_destroy (&array);
+
+ if (sp->m_errtext) {
+ set_last_error (sp->m_errtext->m_text,
+ error_get_token (sp->m_errtext, di, text, ind),
+ ind);
+
+ return mr_error_raised;
+ }
+
+ return mr_not_matched;
+ }
+
+ if (status == mr_matched) {
+ if (sp->m_emits)
+ if (barray_push (ba, sp->m_emits, text[ind - 1], save_ind)) {
+ barray_destroy (&array);
+ return mr_internal_error;
+ }
+
+ if (array)
+ if (barray_append (ba, &array)) {
+ barray_destroy (&array);
+ return mr_internal_error;
+ }
+ }
+
+ barray_destroy (&array);
+
+ if (de->m_oper == op_or
+ && (status == mr_matched || status == mr_dont_emit)) {
+ *index = ind;
+ return mr_matched;
+ }
+
+ sp = sp->m_next;
+ }
+
+ if (de->m_oper == op_and
+ && (status == mr_matched || status == mr_dont_emit)) {
+ *index = ind;
+ return mr_matched;
+ }
+
+ return mr_not_matched;
+}
+
+static byte *
+error_get_token (error * er, dict * di, const byte * text, unsigned int ind)
+{
+ byte *str = NULL, *result = NULL;
+
+ if (er->m_token) {
+ barray *ba;
+ unsigned int filter_index = 0;
+
+ barray_create (&ba);
+ if (ba != NULL) {
+ if (match (di, text + ind, &filter_index, er->m_token, &ba, 0) ==
+ mr_matched && filter_index) {
+ str = mem_alloc (filter_index + 1);
+ if (str != NULL) {
+ _mesa_strncpy ((char *) str, (char *) (text + ind),
+ filter_index);
+ str[filter_index] = '\0';
+ }
+ }
+ barray_destroy (&ba);
+ }
+ }
+
+ return str;
+}
+
+typedef struct grammar_load_state_
+{
+ dict *di;
+ byte *syntax_symbol;
+ byte *string_symbol;
+ map_str *maps;
+ map_byte *mapb;
+ map_def *mapd;
+} grammar_load_state;
+
+
+static GLvoid
+grammar_load_state_create (grammar_load_state ** gr)
+{
+ *gr = mem_alloc (sizeof (grammar_load_state));
+ if (*gr) {
+ (**gr).di = NULL;
+ (**gr).syntax_symbol = NULL;
+ (**gr).string_symbol = NULL;
+ (**gr).maps = NULL;
+ (**gr).mapb = NULL;
+ (**gr).mapd = NULL;
+ }
+}
+
+static GLvoid
+grammar_load_state_destroy (grammar_load_state ** gr)
+{
+ if (*gr) {
+ dict_destroy (&(**gr).di);
+ mem_free ((GLvoid **) &(**gr).syntax_symbol);
+ mem_free ((GLvoid **) &(**gr).string_symbol);
+ map_str_destroy (&(**gr).maps);
+ map_byte_destroy (&(**gr).mapb);
+ map_def_destroy (&(**gr).mapd);
+ mem_free ((GLvoid **) gr);
+ }
+}
+
+/*
+ * the API
+ */
+
+/*
+ * loads grammar script from null-terminated ASCII text
+ * returns the grammar object
+ * returns NULL if an error occurs (call grammar_get_last_error to retrieve the error text)
+ */
+
+static dict *
+grammar_load_from_text (const byte * text)
+{
+ dict *d = NULL;
+ grammar_load_state *g = NULL;
+
+ clear_last_error ();
+
+ grammar_load_state_create (&g);
+ if (g == NULL)
+ return NULL;
+
+ dict_create (&g->di);
+ if (g->di == NULL) {
+ grammar_load_state_destroy (&g);
+ return NULL;
+ }
+
+ eat_spaces (&text);
+
+ /* skip ".syntax" keyword */
+ text += 7;
+ eat_spaces (&text);
+
+ /* retrieve root symbol */
+ if (get_identifier (&text, &g->syntax_symbol)) {
+ grammar_load_state_destroy (&g);
+ return NULL;
+ }
+ eat_spaces (&text);
+
+ /* skip semicolon */
+ text++;
+ eat_spaces (&text);
+
+ while (*text) {
+ byte *symbol = NULL;
+ GLint is_dot = *text == '.';
+
+ if (is_dot)
+ text++;
+
+ if (get_identifier (&text, &symbol)) {
+ grammar_load_state_destroy (&g);
+ return NULL;
+ }
+ eat_spaces (&text);
+
+ /* .emtcode */
+ if (is_dot && _mesa_strcmp ((char *) symbol, "emtcode") == 0) {
+ map_byte *ma = NULL;
+
+ mem_free ((void **) &symbol);
+
+ if (get_emtcode (&text, &ma)) {
+ grammar_load_state_destroy (&g);
+ return NULL;
+ }
+
+ map_byte_append (&g->mapb, &ma);
+ }
+ /* .errtext */
+ else if (is_dot && _mesa_strcmp ((char *) symbol, "errtext") == 0) {
+ map_str *ma = NULL;
+
+ mem_free ((GLvoid **) &symbol);
+
+ if (get_errtext (&text, &ma)) {
+ grammar_load_state_destroy (&g);
+ return NULL;
+ }
+
+ map_str_append (&g->maps, &ma);
+ }
+ /* .string */
+ else if (is_dot && _mesa_strcmp ((char *) symbol, "string") == 0) {
+ mem_free ((GLvoid **) (&symbol));
+
+ if (g->di->m_string != NULL) {
+ grammar_load_state_destroy (&g);
+ return NULL;
+ }
+
+ if (get_identifier (&text, &g->string_symbol)) {
+ grammar_load_state_destroy (&g);
+ return NULL;
+ }
+
+ /* skip semicolon */
+ eat_spaces (&text);
+ text++;
+ eat_spaces (&text);
+ }
+ else {
+ defntn *de = NULL;
+ map_def *ma = NULL;
+
+ if (get_definition (&text, &de, g->maps, g->mapb)) {
+ grammar_load_state_destroy (&g);
+ return NULL;
+ }
+
+ defntn_append (&g->di->m_defntns, &de);
+
+ /* if definition consist of only one specifier, give it an ".and" operator */
+ if (de->m_oper == op_none)
+ de->m_oper = op_and;
+
+ map_def_create (&ma);
+ if (ma == NULL) {
+ grammar_load_state_destroy (&g);
+ return NULL;
+ }
+
+ ma->key = symbol;
+ ma->data = de;
+ map_def_append (&g->mapd, &ma);
+ }
+ }
+
+ if (update_dependencies
+ (g->di, g->mapd, &g->syntax_symbol, &g->string_symbol)) {
+ grammar_load_state_destroy (&g);
+ return NULL;
+ }
+
+ d = g->di;
+ g->di = NULL;
+
+ grammar_load_state_destroy (&g);
+
+ return d;
+}
+
+/**
+ * checks if a null-terminated text matches given grammar
+ * returns 0 on error (call grammar_get_last_error to retrieve the error text)
+ * returns 1 on success, the prod points to newly allocated buffer with production and size
+ * is filled with the production size
+ *
+ * \param id - The grammar returned from grammar_load_from_text()
+ * \param text - The program string
+ * \param production - The return parameter for the binary array holding the parsed results
+ * \param size - The return parameter for the size of production
+ *
+ * \return 1 on sucess, 0 on parser error
+ */
+static GLint
+grammar_check (dict * di, const byte * text, byte ** production,
+ GLuint *size)
+{
+ barray *ba = NULL;
+ GLuint index = 0;
+
+ clear_last_error ();
+
+ barray_create (&ba);
+ if (ba == NULL)
+ return 0;
+
+ *production = NULL;
+ *size = 0;
+
+ if (match (di, text, &index, di->m_syntax, &ba, 0) != mr_matched) {
+ barray_destroy (&ba);
+ return 0;
+ }
+
+ *production = mem_alloc (ba->len * sizeof (byte));
+ if (*production == NULL) {
+ barray_destroy (&ba);
+ return 0;
+ }
+
+ memcpy (*production, ba->data, ba->len * sizeof (byte));
+ *size = ba->len;
+ barray_destroy (&ba);
+
+ return 1;
+}
+
+static GLvoid
+grammar_get_last_error (byte * text, int size, int *pos)
+{
+ GLint len = 0, dots_made = 0;
+ const byte *p = error_message;
+
+ *text = '\0';
+#define APPEND_CHARACTER(x) if (dots_made == 0) {\
+ if (len < size - 1) {\
+ text[len++] = (x); text[len] = '\0';\
+ } else {\
+ int i;\
+ for (i = 0; i < 3; i++)\
+ if (--len >= 0)\
+ text[len] = '.';\
+ dots_made = 1;\
+ }\
+}
+
+ if (p) {
+ while (*p) {
+ if (*p == '$') {
+ const byte *r = error_param;
+
+ while (*r) {
+ APPEND_CHARACTER (*r)
+ r++;
+ }
+
+ p++;
+ }
+ else {
+ APPEND_CHARACTER (*p)
+ p++;
+ }
+ }
+ }
+ *pos = error_position;
+}
+
+/*-----------------------------------------------------------------------
+ * From here on down is the semantic checking portion
+ *
+ */
+
+/**
+ * Variable Table Handling functions
+ */
+typedef enum
+{
+ vt_none,
+ vt_address,
+ vt_attrib,
+ vt_param,
+ vt_temp,
+ vt_output,
+ vt_alias
+} var_type;
+
+
+/*
+ * Setting an explicit field for each of the binding properties is a bit wasteful
+ * of space, but it should be much more clear when reading later on..
+ */
+struct var_cache
+{
+ byte *name;
+ var_type type;
+ GLuint address_binding; /* The index of the address register we should
+ * be using */
+ GLuint attrib_binding; /* For type vt_attrib, see nvfragprog.h for values */
+ GLuint attrib_binding_idx; /* The index into the attrib register file corresponding
+ * to the state in attrib_binding */
+ GLuint temp_binding; /* The index of the temp register we are to use */
+ GLuint output_binding; /* For type vt_output, see nvfragprog.h for values */
+ GLuint output_binding_idx; /* This is the index into the result register file
+ * corresponding to the bound result state */
+ struct var_cache *alias_binding; /* For type vt_alias, points to the var_cache entry
+ * * that this is aliased to */
+ GLuint param_binding_type; /* {PROGRAM_STATE_VAR, PROGRAM_LOCAL_PARAM,
+ * PROGRAM_ENV_PARAM} */
+ GLuint param_binding_begin; /* This is the offset into the program_parameter_list where
+ * the tokens representing our bound state (or constants)
+ * start */
+ GLuint param_binding_length; /* This is how many entries in the the program_parameter_list
+ * we take up with our state tokens or constants. Note that
+ * this is _not_ the same as the number of param registers
+ * we eventually use */
+ struct var_cache *next;
+};
+
+static GLvoid
+var_cache_create (struct var_cache **va)
+{
+ *va = _mesa_malloc (sizeof (struct var_cache));
+ if (*va) {
+ (**va).name = NULL;
+ (**va).type = vt_none;
+ (**va).attrib_binding = -1;
+ (**va).temp_binding = -1;
+ (**va).output_binding = -1;
+ (**va).output_binding_idx = -1;
+ (**va).param_binding_type = -1;
+ (**va).param_binding_begin = -1;
+ (**va).param_binding_length = -1;
+ (**va).alias_binding = NULL;
+ (**va).next = NULL;
+ }
+}
+
+static GLvoid
+var_cache_destroy (struct var_cache **va)
+{
+ if (*va) {
+ var_cache_destroy (&(**va).next);
+ _mesa_free (*va);
+ *va = NULL;
+ }
+}
+
+static GLvoid
+var_cache_append (struct var_cache **va, struct var_cache *nv)
+{
+ if (*va)
+ var_cache_append (&(**va).next, nv);
+ else
+ *va = nv;
+}
+
+static struct var_cache *
+var_cache_find (struct var_cache *va, byte * name)
+{
+ struct var_cache *first = va;
+
+ while (va) {
+ if (!strcmp (name, va->name)) {
+ if (va->type == vt_alias)
+ return var_cache_find (first, va->name);
+ return va;
+ }
+
+ va = va->next;
+ }
+
+ return NULL;
+}
+
+/**
+ * constructs an integer from 4 bytes in LE format
+ */
+static GLuint
+parse_position (byte ** inst)
+{
+ GLuint value;
+
+ value = (GLuint) (*(*inst)++);
+ value += (GLuint) (*(*inst)++) * 0x100;
+ value += (GLuint) (*(*inst)++) * 0x10000;
+ value += (GLuint) (*(*inst)++) * 0x1000000;
+
+ return value;
+}
+
+/**
+ * This will, given a string, lookup the string as a variable name in the
+ * var cache. If the name is found, the var cache node corresponding to the
+ * var name is returned. If it is not found, a new entry is allocated
+ *
+ * \param I Points into the binary array where the string identifier begins
+ * \param found 1 if the string was found in the var_cache, 0 if it was allocated
+ * \return The location on the var_cache corresponding the the string starting at I
+ */
+static struct var_cache *
+parse_string (byte ** inst, struct var_cache **vc_head,
+ struct arb_program *Program, GLuint * found)
+{
+ byte *i = *inst;
+ struct var_cache *va = NULL;
+
+ *inst += _mesa_strlen ((char *) i) + 1;
+
+ va = var_cache_find (*vc_head, i);
+
+ if (va) {
+ *found = 1;
+ return va;
+ }
+
+ *found = 0;
+ var_cache_create (&va);
+ va->name = i;
+
+ var_cache_append (vc_head, va);
+
+ return va;
+}
+
+static char *
+parse_string_without_adding (byte ** inst, struct arb_program *Program)
+{
+ byte *i = *inst;
+
+ *inst += _mesa_strlen ((char *) i) + 1;
+
+ return (char *) i;
+}
+
+/**
+ * \return 0 if sign is plus, 1 if sign is minus
+ */
+static GLuint
+parse_sign (byte ** inst)
+{
+ /*return *(*inst)++ != '+'; */
+
+ if (**inst == '-') {
+ *(*inst)++;
+ return 1;
+ }
+ else if (**inst == '+') {
+ *(*inst)++;
+ return 0;
+ }
+
+ return 0;
+}
+
+/**
+ * parses and returns signed integer
+ */
+static GLint
+parse_integer (byte ** inst, struct arb_program *Program)
+{
+ GLint sign;
+ GLint value;
+
+ /* check if *inst points to '+' or '-'
+ * if yes, grab the sign and increment *inst
+ */
+ sign = parse_sign (inst);
+
+ /* now check if *inst points to 0
+ * if yes, increment the *inst and return the default value
+ */
+ if (**inst == 0) {
+ *(*inst)++;
+ return 0;
+ }
+
+ /* parse the integer as you normally would do it */
+ value = _mesa_atoi (parse_string_without_adding (inst, Program));
+
+ /* now, after terminating 0 there is a position
+ * to parse it - parse_position()
+ */
+ Program->Position = parse_position (inst);
+
+ if (sign)
+ value *= -1;
+
+ return value;
+}
+
+/**
+ */
+static GLfloat
+parse_float (byte ** inst, struct arb_program *Program)
+{
+ GLint tmp[5], denom;
+ GLfloat value = 0;
+
+#if 0
+ tmp[0] = parse_sign (inst); /* This is the sign of the number + - >0, - -> 1 */
+#endif
+ tmp[1] = parse_integer (inst, Program); /* This is the integer portion of the number */
+ tmp[2] = parse_integer (inst, Program); /* This is the fractional portion of the number */
+ tmp[3] = parse_sign (inst); /* This is the sign of the exponent */
+ tmp[4] = parse_integer (inst, Program); /* This is the exponent */
+
+ value = (GLfloat) tmp[1];
+ denom = 1;
+ while (denom < tmp[2])
+ denom *= 10;
+ value += (GLfloat) tmp[2] / (GLfloat) denom;
+#if 0
+ if (tmp[0])
+ value *= -1;
+#endif
+ value *= _mesa_pow (10, (GLfloat) tmp[3] * (GLfloat) tmp[4]);
+
+ return value;
+}
+
+/**
+ */
+static GLfloat
+parse_signed_float (byte ** inst, struct arb_program *Program)
+{
+ GLint negate;
+ GLfloat value;
+
+ negate = parse_sign (inst);
+
+ value = parse_float (inst, Program);
+
+ if (negate)
+ value *= -1;
+
+ return value;
+}
+
+/**
+ * This picks out a constant value from the parsed array. The constant vector is r
+ * returned in the *values array, which should be of length 4.
+ *
+ * \param values - The 4 component vector with the constant value in it
+ */
+static GLvoid
+parse_constant (byte ** inst, GLfloat *values, struct arb_program *Program,
+ GLboolean use)
+{
+ GLuint components, i;
+
+
+ switch (*(*inst)++) {
+ case CONSTANT_SCALAR:
+ if (use == GL_TRUE) {
+ values[0] =
+ values[1] =
+ values[2] = values[3] = parse_float (inst, Program);
+ }
+ else {
+ values[0] =
+ values[1] =
+ values[2] = values[3] = parse_signed_float (inst, Program);
+ }
+
+ break;
+ case CONSTANT_VECTOR:
+ values[0] = values[1] = values[2] = 0;
+ values[3] = 1;
+ components = *(*inst)++;
+ for (i = 0; i < components; i++) {
+ values[i] = parse_signed_float (inst, Program);
+ }
+ break;
+ }
+}
+
+
+/**
+ * \param color 0 if color type is primary, 1 if color type is secondary
+ * \return 0 on sucess, 1 on error
+ */
+static GLuint
+parse_color_type (GLcontext * ctx, byte ** inst, struct arb_program *Program,
+ GLint * color)
+{
+ *color = *(*inst)++ != COLOR_PRIMARY;
+ return 0;
+}
+
+/**
+ * \param coord The texture unit index
+ * \return 0 on sucess, 1 on error
+ */
+static GLuint
+parse_texcoord_num (GLcontext * ctx, byte ** inst,
+ struct arb_program *Program, GLuint * coord)
+{
+ *coord = parse_integer (inst, Program);
+
+ if ((*coord < 0) || (*coord >= ctx->Const.MaxTextureUnits)) {
+ _mesa_set_program_error (ctx, Program->Position,
+ "Invalid texture unit index");
+ _mesa_error (ctx, GL_INVALID_OPERATION, "Invalid texture unit index");
+ return 1;
+ }
+
+ Program->TexturesUsed[*coord] = 1;
+ return 0;
+}
+
+/**
+ * \param coord The weight index
+ * \return 0 on sucess, 1 on error
+ */
+static GLuint
+parse_weight_num (GLcontext * ctx, byte ** inst, struct arb_program *Program,
+ GLint * coord)
+{
+ *coord = parse_integer (inst, Program);
+
+ if ((*coord < 0) || (*coord >= 1)) {
+ _mesa_set_program_error (ctx, Program->Position,
+ "Invalid weight index");
+ _mesa_error (ctx, GL_INVALID_OPERATION, "Invalid weight index");
+ return 1;
+ }
+
+ return 0;
+}
+
+/**
+ * \param coord The clip plane index
+ * \return 0 on sucess, 1 on error
+ */
+static GLuint
+parse_clipplane_num (GLcontext * ctx, byte ** inst,
+ struct arb_program *Program, GLint * coord)
+{
+ *coord = parse_integer (inst, Program);
+
+ if ((*coord < 0) || (*coord >= ctx->Const.MaxClipPlanes)) {
+ _mesa_set_program_error (ctx, Program->Position,
+ "Invalid clip plane index");
+ _mesa_error (ctx, GL_INVALID_OPERATION, "Invalid clip plane index");
+ return 1;
+ }
+
+ return 0;
+}
+
+
+
+
+/**
+ * \return 0 on front face, 1 on back face
+ */
+static GLuint
+parse_face_type (byte ** inst)
+{
+ switch (*(*inst)++) {
+ case FACE_FRONT:
+ return 0;
+
+ case FACE_BACK:
+ return 1;
+ }
+}
+
+/**
+ * Given a matrix and a modifier token on the binary array, return tokens
+ * that _mesa_fetch_state() [program.c] can understand.
+ *
+ * \param matrix - the matrix we are talking about
+ * \param matrix_idx - the index of the matrix we have (for texture & program matricies)
+ * \param matrix_modifier - the matrix modifier (trans, inv, etc)
+ * \return 0 on sucess, 1 on failure
+ */
+static GLuint
+parse_matrix (GLcontext * ctx, byte ** inst, struct arb_program *Program,
+ GLint * matrix, GLint * matrix_idx, GLint * matrix_modifier)
+{
+ byte mat = *(*inst)++;
+
+ *matrix_idx = 0;
+
+ switch (mat) {
+ case MATRIX_MODELVIEW:
+ *matrix = STATE_MODELVIEW;
+ *matrix_idx = parse_integer (inst, Program);
+ /* XXX: if (*matrix_idx >= ctx->Const. */
+ break;
+
+ case MATRIX_PROJECTION:
+ *matrix = STATE_PROJECTION;
+ break;
+
+ case MATRIX_MVP:
+ *matrix = STATE_MVP;
+ break;
+
+ case MATRIX_TEXTURE:
+ *matrix = STATE_TEXTURE;
+ *matrix_idx = parse_integer (inst, Program);
+ if (*matrix_idx >= ctx->Const.MaxTextureUnits) {
+ _mesa_set_program_error (ctx, Program->Position,
+ "Invalid Texture Unit");
+ _mesa_error (ctx, GL_INVALID_OPERATION,
+ "Invalid Texture Unit: %d", *matrix_idx);
+ return 1;
+ }
+ break;
+
+ /* XXX: How should we handle the palette matrix? */
+ case MATRIX_PALETTE:
+ *matrix_idx = parse_integer (inst, Program);
+ break;
+
+ case MATRIX_PROGRAM:
+ *matrix = STATE_PROGRAM;
+ *matrix_idx = parse_integer (inst, Program);
+ if (*matrix_idx >= ctx->Const.MaxProgramMatrices) {
+ _mesa_set_program_error (ctx, Program->Position,
+ "Invalid Program Matrix");
+ _mesa_error (ctx, GL_INVALID_OPERATION,
+ "Invalid Program Matrix: %d", *matrix_idx);
+ return 1;
+ }
+ break;
+ }
+
+ switch (*(*inst)++) {
+ case MATRIX_MODIFIER_IDENTITY:
+ *matrix_modifier = 0;
+ break;
+ case MATRIX_MODIFIER_INVERSE:
+ *matrix_modifier = STATE_MATRIX_INVERSE;
+ break;
+ case MATRIX_MODIFIER_TRANSPOSE:
+ *matrix_modifier = STATE_MATRIX_TRANSPOSE;
+ break;
+ case MATRIX_MODIFIER_INVTRANS:
+ *matrix_modifier = STATE_MATRIX_INVTRANS;
+ break;
+ }
+
+ return 0;
+}
+
+
+/**
+ * This parses a state string (rather, the binary version of it) into
+ * a 6-token sequence as described in _mesa_fetch_state() [program.c]
+ *
+ * \param inst - the start in the binary arry to start working from
+ * \param state_tokens - the storage for the 6-token state description
+ * \return - 0 on sucess, 1 on error
+ */
+static GLuint
+parse_state_single_item (GLcontext * ctx, byte ** inst,
+ struct arb_program *Program, GLint * state_tokens)
+{
+ switch (*(*inst)++) {
+ case STATE_MATERIAL_PARSER:
+ state_tokens[0] = STATE_MATERIAL;
+ state_tokens[1] = parse_face_type (inst);
+ switch (*(*inst)++) {
+ case MATERIAL_AMBIENT:
+ state_tokens[2] = STATE_AMBIENT;
+ break;
+ case MATERIAL_DIFFUSE:
+ state_tokens[2] = STATE_DIFFUSE;
+ break;
+ case MATERIAL_SPECULAR:
+ state_tokens[2] = STATE_SPECULAR;
+ break;
+ case MATERIAL_EMISSION:
+ state_tokens[2] = STATE_EMISSION;
+ break;
+ case MATERIAL_SHININESS:
+ state_tokens[2] = STATE_SHININESS;
+ break;
+ }
+ break;
+
+ case STATE_LIGHT_PARSER:
+ state_tokens[0] = STATE_LIGHT;
+ state_tokens[1] = parse_integer (inst, Program);
+
+ /* Check the value of state_tokens[1] against the # of lights */
+ if (state_tokens[1] >= ctx->Const.MaxLights) {
+ _mesa_set_program_error (ctx, Program->Position,
+ "Invalid Light Number");
+ _mesa_error (ctx, GL_INVALID_OPERATION,
+ "Invalid Light Number: %d", state_tokens[1]);
+ return 1;
+ }
+
+ switch (*(*inst)++) {
+ case LIGHT_AMBIENT:
+ state_tokens[2] = STATE_AMBIENT;
+ break;
+ case LIGHT_DIFFUSE:
+ state_tokens[2] = STATE_DIFFUSE;
+ break;
+ case LIGHT_SPECULAR:
+ state_tokens[2] = STATE_SPECULAR;
+ break;
+ case LIGHT_POSITION:
+ state_tokens[2] = STATE_POSITION;
+ break;
+ case LIGHT_ATTENUATION:
+ state_tokens[2] = STATE_ATTENUATION;
+ break;
+ case LIGHT_HALF:
+ state_tokens[2] = STATE_HALF;
+ break;
+ case LIGHT_SPOT_DIRECTION:
+ state_tokens[2] = STATE_SPOT_DIRECTION;
+ break;
+ }
+ break;
+
+ case STATE_LIGHT_MODEL:
+ switch (*(*inst)++) {
+ case LIGHT_MODEL_AMBIENT:
+ state_tokens[0] = STATE_LIGHTMODEL_AMBIENT;
+ break;
+ case LIGHT_MODEL_SCENECOLOR:
+ state_tokens[0] = STATE_LIGHTMODEL_SCENECOLOR;
+ state_tokens[1] = parse_face_type (inst);
+ break;
+ }
+ break;
+
+ case STATE_LIGHT_PROD:
+ state_tokens[0] = STATE_LIGHTPROD;
+ state_tokens[1] = parse_integer (inst, Program);
+
+ /* Check the value of state_tokens[1] against the # of lights */
+ if (state_tokens[1] >= ctx->Const.MaxLights) {
+ _mesa_set_program_error (ctx, Program->Position,
+ "Invalid Light Number");
+ _mesa_error (ctx, GL_INVALID_OPERATION,
+ "Invalid Light Number: %d", state_tokens[1]);
+ return 1;
+ }
+
+ state_tokens[2] = parse_face_type (inst);
+ switch (*(*inst)++) {
+ case LIGHT_PROD_AMBIENT:
+ state_tokens[3] = STATE_AMBIENT;
+ break;
+ case LIGHT_PROD_DIFFUSE:
+ state_tokens[3] = STATE_DIFFUSE;
+ break;
+ case LIGHT_PROD_SPECULAR:
+ state_tokens[3] = STATE_SPECULAR;
+ break;
+ }
+ break;
+
+
+ case STATE_FOG:
+ switch (*(*inst)++) {
+ case FOG_COLOR:
+ state_tokens[0] = STATE_FOG_COLOR;
+ break;
+ case FOG_PARAMS:
+ state_tokens[0] = STATE_FOG_PARAMS;
+ break;
+ }
+ break;
+
+ /* STATE_TEX_ENV == STATE_TEX_GEN */
+ case STATE_TEX_ENV:
+ if (Program->type == GL_FRAGMENT_PROGRAM_ARB) {
+ state_tokens[1] = parse_integer (inst, Program);
+ switch (*(*inst)++) {
+ case TEX_ENV_COLOR:
+ state_tokens[0] = STATE_TEXENV_COLOR;
+ break;
+ }
+ }
+ /* For vertex programs, this case is STATE_TEX_GEN */
+ else {
+ GLuint type, coord;
+
+ state_tokens[0] = STATE_TEXGEN;
+ /*state_tokens[1] = parse_integer (inst, Program);*/ /* Texture Unit */
+
+ if (parse_texcoord_num (ctx, inst, Program, &coord))
+ return 1;
+ state_tokens[1] = coord;
+
+ /* EYE or OBJECT */
+ type = *(*inst++);
+
+ /* 0 - s, 1 - t, 2 - r, 3 - q */
+ coord = *(*inst++);
+
+ if (type == TEX_GEN_EYE) {
+ switch (coord) {
+ case COMPONENT_X:
+ state_tokens[2] = STATE_TEXGEN_EYE_S;
+ break;
+ case COMPONENT_Y:
+ state_tokens[2] = STATE_TEXGEN_EYE_T;
+ break;
+ case COMPONENT_Z:
+ state_tokens[2] = STATE_TEXGEN_EYE_R;
+ break;
+ case COMPONENT_W:
+ state_tokens[2] = STATE_TEXGEN_EYE_Q;
+ break;
+ }
+ }
+ else {
+ switch (coord) {
+ case COMPONENT_X:
+ state_tokens[2] = STATE_TEXGEN_OBJECT_S;
+ break;
+ case COMPONENT_Y:
+ state_tokens[2] = STATE_TEXGEN_OBJECT_T;
+ break;
+ case COMPONENT_Z:
+ state_tokens[2] = STATE_TEXGEN_OBJECT_R;
+ break;
+ case COMPONENT_W:
+ state_tokens[2] = STATE_TEXGEN_OBJECT_Q;
+ break;
+ }
+ }
+ }
+ break;
+
+ /* STATE_DEPTH = STATE_CLIP_PLANE */
+ case STATE_DEPTH:
+ if (Program->type == GL_FRAGMENT_PROGRAM_ARB) {
+ switch (*(*inst)++) {
+ case DEPTH_RANGE:
+ state_tokens[0] = STATE_DEPTH_RANGE;
+ break;
+ }
+ }
+ /* for vertex programs, we want STATE_CLIP_PLANE */
+ else {
+ state_tokens[0] = STATE_CLIPPLANE;
+ state_tokens[1] = parse_integer (inst, Program);
+ if (parse_clipplane_num (ctx, inst, Program, &state_tokens[1]))
+ return 1;
+ }
+ break;
+
+ case STATE_POINT:
+ switch (*(*inst++)) {
+ case POINT_SIZE:
+ state_tokens[0] = STATE_POINT_SIZE;
+ break;
+
+ case POINT_ATTENUATION:
+ state_tokens[0] = STATE_POINT_ATTENUATION;
+ break;
+ }
+ break;
+
+ /* XXX: I think this is the correct format for a matrix row */
+ case STATE_MATRIX_ROWS:
+ state_tokens[0] = STATE_MATRIX;
+
+ if (parse_matrix
+ (ctx, inst, Program, &state_tokens[1], &state_tokens[2],
+ &state_tokens[5]))
+ return 1;
+
+ state_tokens[3] = parse_integer (inst, Program); /* The first row to grab */
+
+ state_tokens[4] = parse_integer (inst, Program); /* Either the last row, 0 */
+ if (state_tokens[4] == 0) {
+ state_tokens[4] = state_tokens[3];
+ }
+ break;
+ }
+
+ return 0;
+}
+
+/**
+ * This parses a state string (rather, the binary version of it) into
+ * a 6-token similar for the state fetching code in program.c
+ *
+ * One might ask, why fetch these parameters into just like you fetch
+ * state when they are already stored in other places?
+ *
+ * Because of array offsets -> We can stick env/local parameters in the
+ * middle of a parameter array and then index someplace into the array
+ * when we execute.
+ *
+ * One optimization might be to only do this for the cases where the
+ * env/local parameters end up inside of an array, and leave the
+ * single parameters (or arrays of pure env/local pareameters) in their
+ * respective register files.
+ *
+ * For ENV parameters, the format is:
+ * state_tokens[0] = STATE_FRAGMENT_PROGRAM / STATE_VERTEX_PROGRAM
+ * state_tokens[1] = STATE_ENV
+ * state_tokens[2] = the parameter index
+ *
+ * for LOCAL parameters, the format is:
+ * state_tokens[0] = STATE_FRAGMENT_PROGRAM / STATE_VERTEX_PROGRAM
+ * state_tokens[1] = STATE_LOCAL
+ * state_tokens[2] = the parameter index
+ *
+ * \param inst - the start in the binary arry to start working from
+ * \param state_tokens - the storage for the 6-token state description
+ * \return - 0 on sucess, 1 on failure
+ */
+static GLuint
+parse_program_single_item (GLcontext * ctx, byte ** inst,
+ struct arb_program *Program, GLint * state_tokens)
+{
+ if (Program->type == GL_FRAGMENT_PROGRAM_ARB)
+ state_tokens[0] = STATE_FRAGMENT_PROGRAM;
+ else
+ state_tokens[0] = STATE_VERTEX_PROGRAM;
+
+
+ switch (*(*inst)++) {
+ case PROGRAM_PARAM_ENV:
+ state_tokens[1] = STATE_ENV;
+ state_tokens[2] = parse_integer (inst, Program);
+ /* Check state_tokens[2] against the number of ENV parameters available */
+ if (((Program->type == GL_FRAGMENT_PROGRAM_ARB) &&
+ (state_tokens[2] >= ctx->Const.MaxFragmentProgramEnvParams))
+ ||
+ ((Program->type == GL_VERTEX_PROGRAM_ARB) &&
+ (state_tokens[2] >= ctx->Const.MaxVertexProgramEnvParams))) {
+ _mesa_set_program_error (ctx, Program->Position,
+ "Invalid Program Env Parameter");
+ _mesa_error (ctx, GL_INVALID_OPERATION,
+ "Invalid Program Env Parameter: %d",
+ state_tokens[2]);
+ return 1;
+ }
+
+ break;
+
+ case PROGRAM_PARAM_LOCAL:
+ state_tokens[1] = STATE_LOCAL;
+ state_tokens[2] = parse_integer (inst, Program);
+ /* Check state_tokens[2] against the number of LOCAL parameters available */
+ if (((Program->type == GL_FRAGMENT_PROGRAM_ARB) &&
+ (state_tokens[2] >= ctx->Const.MaxFragmentProgramLocalParams))
+ ||
+ ((Program->type == GL_VERTEX_PROGRAM_ARB) &&
+ (state_tokens[2] >= ctx->Const.MaxVertexProgramLocalParams))) {
+ _mesa_set_program_error (ctx, Program->Position,
+ "Invalid Program Local Parameter");
+ _mesa_error (ctx, GL_INVALID_OPERATION,
+ "Invalid Program Local Parameter: %d",
+ state_tokens[2]);
+ return 1;
+ }
+ break;
+ }
+
+ return 0;
+}
+
+
+/**
+ * This will handle the binding side of an ATTRIB var declaration
+ *
+ * \param binding - the fragment input register state, defined in nvfragprog.h
+ * \param binding_idx - the index in the attrib register file that binding is associated with
+ * \return returns 0 on sucess, 1 on error
+ *
+ * See nvfragparse.c for attrib register file layout
+ */
+static GLuint
+parse_attrib_binding (GLcontext * ctx, byte ** inst,
+ struct arb_program *Program, GLuint * binding,
+ GLuint * binding_idx)
+{
+ GLuint texcoord;
+ GLint coord;
+ GLint err = 0;
+
+ if (Program->type == GL_FRAGMENT_PROGRAM_ARB) {
+ switch (*(*inst)++) {
+ case FRAGMENT_ATTRIB_COLOR:
+ err = parse_color_type (ctx, inst, Program, &coord);
+ *binding = FRAG_ATTRIB_COL0 + coord;
+ *binding_idx = 1 + coord;
+ break;
+
+ case FRAGMENT_ATTRIB_TEXCOORD:
+ err = parse_texcoord_num (ctx, inst, Program, &texcoord);
+ *binding = FRAG_ATTRIB_TEX0 + texcoord;
+ *binding_idx = 4 + texcoord;
+ break;
+
+ case FRAGMENT_ATTRIB_FOGCOORD:
+ *binding = FRAG_ATTRIB_FOGC;
+ *binding_idx = 3;
+ break;
+
+ case FRAGMENT_ATTRIB_POSITION:
+ *binding = FRAG_ATTRIB_WPOS;
+ *binding_idx = 0;
+ break;
+
+ default:
+ err = 1;
+ break;
+ }
+ }
+ else {
+ switch (*(*inst)++) {
+ case VERTEX_ATTRIB_POSITION:
+ *binding = VERT_ATTRIB_POS;
+ *binding_idx = 0;
+ break;
+
+ case VERTEX_ATTRIB_WEIGHT:
+ {
+ GLint weight;
+
+ err = parse_weight_num (ctx, inst, Program, &weight);
+ *binding = VERT_ATTRIB_WEIGHT;
+ *binding_idx = 1;
+ }
+ break;
+
+ case VERTEX_ATTRIB_NORMAL:
+ *binding = VERT_ATTRIB_NORMAL;
+ *binding_idx = 2;
+ break;
+
+ case VERTEX_ATTRIB_COLOR:
+ {
+ GLint color;
+
+ err = parse_color_type (ctx, inst, Program, &color);
+ if (color) {
+ *binding = VERT_ATTRIB_COLOR1;
+ *binding_idx = 4;
+ }
+ else {
+ *binding = VERT_ATTRIB_COLOR0;
+ *binding_idx = 3;
+ }
+ }
+ break;
+
+ case VERTEX_ATTRIB_FOGCOORD:
+ *binding = VERT_ATTRIB_FOG;
+ *binding_idx = 5;
+ break;
+
+ case VERTEX_ATTRIB_TEXCOORD:
+ {
+ GLuint unit;
+
+ err = parse_texcoord_num (ctx, inst, Program, &unit);
+ *binding = VERT_ATTRIB_TEX0 + unit;
+ *binding_idx = 8 + unit;
+ }
+ break;
+
+ /* XXX: It looks like we don't support this at all, atm */
+ case VERTEX_ATTRIB_MATRIXINDEX:
+ parse_integer (inst, Program);
+ break;
+
+ /* XXX: */
+ case VERTEX_ATTRIB_GENERIC:
+ break;
+
+ default:
+ err = 1;
+ break;
+ }
+ }
+
+ /* Can this even happen? */
+ if (err) {
+ _mesa_set_program_error (ctx, Program->Position,
+ "Bad attribute binding");
+ _mesa_error (ctx, GL_INVALID_OPERATION, "Bad attribute binding");
+ }
+
+ Program->InputsRead |= (1 << *binding_idx);
+
+ return err;
+}
+
+/**
+ * This translates between a binary token for an output variable type
+ * and the mesa token for the same thing.
+ *
+ *
+ * XXX: What is the 'name' for vertex program state? -> do we need it?
+ * I don't think we do;
+ *
+ * See nvfragprog.h for definitions
+ *
+ * \param inst - The parsed tokens
+ * \param binding - The name of the state we are binding too
+ * \param binding_idx - The index into the result register file that this is bound too
+ *
+ * See nvfragparse.c for the register file layout for fragment programs
+ * See nvvertparse.c for the register file layout for vertex programs
+ */
+static GLuint
+parse_result_binding (GLcontext * ctx, byte ** inst, GLuint * binding,
+ GLuint * binding_idx, struct arb_program *Program)
+{
+ GLint a;
+ GLuint b;
+
+ switch (*(*inst)++) {
+ case FRAGMENT_RESULT_COLOR:
+ /* for frag programs, this is FRAGMENT_RESULT_COLOR */
+ if (Program->type == GL_FRAGMENT_PROGRAM_ARB) {
+ *binding = FRAG_OUTPUT_COLR;
+ *binding_idx = 0;
+ }
+ /* for vtx programs, this is VERTEX_RESULT_POSITION */
+ else {
+ *binding_idx = 0;
+ }
+ break;
+
+ case FRAGMENT_RESULT_DEPTH:
+ /* for frag programs, this is FRAGMENT_RESULT_DEPTH */
+ if (Program->type == GL_FRAGMENT_PROGRAM_ARB) {
+ *binding = FRAG_OUTPUT_DEPR;
+ *binding_idx = 2;
+ }
+ /* for vtx programs, this is VERTEX_RESULT_COLOR */
+ else {
+ GLint color_type;
+
+ /* back face */
+ if (parse_face_type (inst)) {
+ if (parse_color_type (ctx, inst, Program, &color_type))
+ return 1;
+
+ /* secondary color */
+ if (color_type) {
+ *binding_idx = 4;
+ }
+ /* primary color */
+ else {
+ *binding_idx = 3;
+ }
+ }
+ /* front face */
+ else {
+ /* secondary color */
+ if (color_type) {
+ *binding_idx = 2;
+ }
+ /* primary color */
+ else {
+ *binding_idx = 1;
+ }
+ }
+ }
+ break;
+
+ case VERTEX_RESULT_FOGCOORD:
+ *binding_idx = 5;
+ break;
+
+ case VERTEX_RESULT_POINTSIZE:
+ *binding_idx = 6;
+ break;
+
+ case VERTEX_RESULT_TEXCOORD:
+ if (parse_texcoord_num (ctx, inst, Program, &b))
+ return 1;
+ *binding_idx = 7 + b;
+ break;
+ }
+
+ Program->OutputsWritten |= (1 << *binding_idx);
+
+ return 0;
+}
+
+/**
+ * This handles the declaration of ATTRIB variables
+ *
+ * XXX: Still needs
+ * parse_vert_attrib_binding(), or something like that
+ *
+ * \return 0 on sucess, 1 on error
+ */
+static GLint
+parse_attrib (GLcontext * ctx, byte ** inst, struct var_cache **vc_head,
+ struct arb_program *Program)
+{
+ GLuint found;
+ char *error_msg;
+ struct var_cache *attrib_var;
+
+ attrib_var = parse_string (inst, vc_head, Program, &found);
+ Program->Position = parse_position (inst);
+ if (found) {
+ error_msg =
+ _mesa_malloc (_mesa_strlen ((char *) attrib_var->name) + 40);
+ _mesa_sprintf (error_msg, "Duplicate Varible Declaration: %s",
+ attrib_var->name);
+
+ _mesa_set_program_error (ctx, Program->Position, error_msg);
+ _mesa_error (ctx, GL_INVALID_OPERATION, error_msg);
+
+ _mesa_free (error_msg);
+ return 1;
+ }
+
+ attrib_var->type = vt_attrib;
+
+ /* I think this is ok now - karl */
+ /* XXX: */
+ /*if (Program->type == GL_FRAGMENT_PROGRAM_ARB) */
+ {
+ if (parse_attrib_binding
+ (ctx, inst, Program, &attrib_var->attrib_binding,
+ &attrib_var->attrib_binding_idx))
+ return 1;
+ }
+
+ Program->Base.NumAttributes++;
+ return 0;
+}
+
+/**
+ * \param use -- TRUE if we're called when declaring implicit parameters,
+ * FALSE if we're declaraing variables. This has to do with
+ * if we get a signed or unsigned float for scalar constants
+ */
+static GLuint
+parse_param_elements (GLcontext * ctx, byte ** inst,
+ struct var_cache *param_var,
+ struct arb_program *Program, GLboolean use)
+{
+ GLint idx;
+ GLuint found, specified_length, err;
+ GLint state_tokens[6];
+ GLfloat const_values[4];
+ char *error_msg;
+
+ err = 0;
+
+ switch (*(*inst)++) {
+ case PARAM_STATE_ELEMENT:
+ if (parse_state_single_item (ctx, inst, Program, state_tokens))
+ return 1;
+
+ /* If we adding STATE_MATRIX that has multiple rows, we need to
+ * unroll it and call _mesa_add_state_reference() for each row
+ */
+ if ((state_tokens[0] == STATE_MATRIX)
+ && (state_tokens[3] != state_tokens[4])) {
+ GLint row;
+ GLint first_row = state_tokens[3];
+ GLint last_row = state_tokens[4];
+
+ for (row = first_row; row <= last_row; row++) {
+ state_tokens[3] = state_tokens[4] = row;
+
+ idx =
+ _mesa_add_state_reference (Program->Parameters,
+ state_tokens);
+ if (param_var->param_binding_begin == -1)
+ param_var->param_binding_begin = idx;
+ param_var->param_binding_length++;
+ Program->Base.NumParameters++;
+ }
+ }
+ else {
+ idx =
+ _mesa_add_state_reference (Program->Parameters, state_tokens);
+ if (param_var->param_binding_begin == -1)
+ param_var->param_binding_begin = idx;
+ param_var->param_binding_length++;
+ Program->Base.NumParameters++;
+ }
+ break;
+
+ case PARAM_PROGRAM_ELEMENT:
+ if (parse_program_single_item (ctx, inst, Program, state_tokens))
+ return 1;
+ idx = _mesa_add_state_reference (Program->Parameters, state_tokens);
+ if (param_var->param_binding_begin == -1)
+ param_var->param_binding_begin = idx;
+ param_var->param_binding_length++;
+ Program->Base.NumParameters++;
+
+ /* Check if there is more: 0 -> we're done, else its an integer */
+ if (**inst) {
+ GLuint out_of_range, new_idx;
+ GLuint start_idx = state_tokens[2] + 1;
+ GLuint end_idx = parse_integer (inst, Program);
+
+ out_of_range = 0;
+ if (Program->type == GL_FRAGMENT_PROGRAM_ARB) {
+ if (((state_tokens[1] == STATE_ENV)
+ && (end_idx >= ctx->Const.MaxFragmentProgramEnvParams))
+ || ((state_tokens[1] == STATE_LOCAL)
+ && (end_idx >=
+ ctx->Const.MaxFragmentProgramLocalParams)))
+ out_of_range = 1;
+ }
+ else {
+ if (((state_tokens[1] == STATE_ENV)
+ && (end_idx >= ctx->Const.MaxFragmentProgramEnvParams))
+ || ((state_tokens[1] == STATE_LOCAL)
+ && (end_idx >=
+ ctx->Const.MaxFragmentProgramLocalParams)))
+ out_of_range = 1;
+ }
+ if (out_of_range) {
+ _mesa_set_program_error (ctx, Program->Position,
+ "Invalid Program Parameter");
+ _mesa_error (ctx, GL_INVALID_OPERATION,
+ "Invalid Program Parameter: %d", end_idx);
+ return 1;
+ }
+
+ for (new_idx = start_idx; new_idx <= end_idx; new_idx++) {
+ state_tokens[2] = new_idx;
+ idx =
+ _mesa_add_state_reference (Program->Parameters,
+ state_tokens);
+ param_var->param_binding_length++;
+ Program->Base.NumParameters++;
+ }
+ }
+ break;
+
+ case PARAM_CONSTANT:
+ parse_constant (inst, const_values, Program, use);
+ idx =
+ _mesa_add_named_constant (Program->Parameters,
+ (char *) param_var->name, const_values);
+ if (param_var->param_binding_begin == -1)
+ param_var->param_binding_begin = idx;
+ param_var->param_binding_length++;
+ Program->Base.NumParameters++;
+ break;
+
+ default:
+ _mesa_set_program_error (ctx, Program->Position,
+ "Unexpected token in parse_param_elements()");
+ _mesa_error (ctx, GL_INVALID_OPERATION,
+ "Unexpected token in parse_param_elements()");
+ return 1;
+ }
+
+ /* Make sure we haven't blown past our parameter limits */
+ if (((Program->type == GL_VERTEX_PROGRAM_ARB) &&
+ (Program->Base.NumParameters >=
+ ctx->Const.MaxVertexProgramLocalParams))
+ || ((Program->type == GL_FRAGMENT_PROGRAM_ARB)
+ && (Program->Base.NumParameters >=
+ ctx->Const.MaxFragmentProgramLocalParams))) {
+ _mesa_set_program_error (ctx, Program->Position,
+ "Too many parameter variables");
+ _mesa_error (ctx, GL_INVALID_OPERATION, "Too many parameter variables");
+ return 1;
+ }
+
+ return err;
+}
+
+/**
+ * This picks out PARAM program parameter bindings.
+ *
+ * XXX: This needs to be stressed & tested
+ *
+ * \return 0 on sucess, 1 on error
+ */
+static GLuint
+parse_param (GLcontext * ctx, byte ** inst, struct var_cache **vc_head,
+ struct arb_program *Program)
+{
+ GLuint found, specified_length, err;
+ GLint state_tokens[6];
+ GLfloat const_values[4];
+ char *error_msg;
+ struct var_cache *param_var;
+
+ err = 0;
+ param_var = parse_string (inst, vc_head, Program, &found);
+ Program->Position = parse_position (inst);
+
+ if (found) {
+ error_msg = _mesa_malloc (_mesa_strlen ((char *) param_var->name) + 40);
+ _mesa_sprintf (error_msg, "Duplicate Varible Declaration: %s",
+ param_var->name);
+
+ _mesa_set_program_error (ctx, Program->Position, error_msg);
+ _mesa_error (ctx, GL_INVALID_OPERATION, error_msg);
+
+ _mesa_free (error_msg);
+ return 1;
+ }
+
+ specified_length = parse_integer (inst, Program);
+
+ if (specified_length < 0) {
+ _mesa_set_program_error (ctx, Program->Position,
+ "Negative parameter array length");
+ _mesa_error (ctx, GL_INVALID_OPERATION,
+ "Negative parameter array length: %d", specified_length);
+ return 1;
+ }
+
+ param_var->type = vt_param;
+ param_var->param_binding_length = 0;
+
+ /* Right now, everything is shoved into the main state register file.
+ *
+ * In the future, it would be nice to leave things ENV/LOCAL params
+ * in their respective register files, if possible
+ */
+ param_var->param_binding_type = PROGRAM_STATE_VAR;
+
+ /* Remember to:
+ * * - add each guy to the parameter list
+ * * - increment the param_var->param_binding_len
+ * * - store the param_var->param_binding_begin for the first one
+ * * - compare the actual len to the specified len at the end
+ */
+ while (**inst != PARAM_NULL) {
+ if (parse_param_elements (ctx, inst, param_var, Program, GL_FALSE))
+ return 1;
+ }
+
+ /* Test array length here! */
+ if (specified_length) {
+ if (specified_length != param_var->param_binding_length) {
+ _mesa_set_program_error (ctx, Program->Position,
+ "Declared parameter array lenght does not match parameter list");
+ _mesa_error (ctx, GL_INVALID_OPERATION,
+ "Declared parameter array lenght does not match parameter list");
+ }
+ }
+
+ (*inst)++;
+
+ return 0;
+}
+
+/**
+ *
+ */
+static GLuint
+parse_param_use (GLcontext * ctx, byte ** inst, struct var_cache **vc_head,
+ struct arb_program *Program, struct var_cache **new_var)
+{
+ struct var_cache *param_var;
+
+ /* First, insert a dummy entry into the var_cache */
+ var_cache_create (&param_var);
+ param_var->name = (byte *) _mesa_strdup (" ");
+ param_var->type = vt_param;
+
+ param_var->param_binding_length = 0;
+ /* Don't fill in binding_begin; We use the default value of -1
+ * to tell if its already initialized, elsewhere.
+ *
+ * param_var->param_binding_begin = 0;
+ */
+ param_var->param_binding_type = PROGRAM_STATE_VAR;
+
+
+ var_cache_append (vc_head, param_var);
+
+ /* Then fill it with juicy parameter goodness */
+ if (parse_param_elements (ctx, inst, param_var, Program, GL_TRUE))
+ return 1;
+
+ *new_var = param_var;
+
+ return 0;
+}
+
+
+/**
+ * This handles the declaration of TEMP variables
+ *
+ * \return 0 on sucess, 1 on error
+ */
+static GLuint
+parse_temp (GLcontext * ctx, byte ** inst, struct var_cache **vc_head,
+ struct arb_program *Program)
+{
+ GLuint found;
+ struct var_cache *temp_var;
+ char *error_msg;
+
+ while (**inst != 0) {
+ temp_var = parse_string (inst, vc_head, Program, &found);
+ Program->Position = parse_position (inst);
+ if (found) {
+ error_msg =
+ _mesa_malloc (_mesa_strlen ((char *) temp_var->name) + 40);
+ _mesa_sprintf (error_msg, "Duplicate Varible Declaration: %s",
+ temp_var->name);
+
+ _mesa_set_program_error (ctx, Program->Position, error_msg);
+ _mesa_error (ctx, GL_INVALID_OPERATION, error_msg);
+
+ _mesa_free (error_msg);
+ return 1;
+ }
+
+ temp_var->type = vt_temp;
+
+ if (((Program->type == GL_FRAGMENT_PROGRAM_ARB) &&
+ (Program->Base.NumTemporaries >=
+ ctx->Const.MaxFragmentProgramTemps))
+ || ((Program->type == GL_VERTEX_PROGRAM_ARB)
+ && (Program->Base.NumTemporaries >=
+ ctx->Const.MaxVertexProgramTemps))) {
+ _mesa_set_program_error (ctx, Program->Position,
+ "Too many TEMP variables declared");
+ _mesa_error (ctx, GL_INVALID_OPERATION,
+ "Too many TEMP variables declared");
+ return 1;
+ }
+
+ temp_var->temp_binding = Program->Base.NumTemporaries;
+ Program->Base.NumTemporaries++;
+ }
+ (*inst)++;
+
+ return 0;
+}
+
+/**
+ * This handles variables of the OUTPUT variety
+ *
+ * \return 0 on sucess, 1 on error
+ */
+static GLuint
+parse_output (GLcontext * ctx, byte ** inst, struct var_cache **vc_head,
+ struct arb_program *Program)
+{
+ GLuint found;
+ struct var_cache *output_var;
+
+ output_var = parse_string (inst, vc_head, Program, &found);
+ Program->Position = parse_position (inst);
+ if (found) {
+ char *error_msg;
+ error_msg =
+ _mesa_malloc (_mesa_strlen ((char *) output_var->name) + 40);
+ _mesa_sprintf (error_msg, "Duplicate Varible Declaration: %s",
+ output_var->name);
+
+ _mesa_set_program_error (ctx, Program->Position, error_msg);
+ _mesa_error (ctx, GL_INVALID_OPERATION, error_msg);
+
+ _mesa_free (error_msg);
+ return 1;
+ }
+
+ output_var->type = vt_output;
+ return parse_result_binding (ctx, inst, &output_var->output_binding,
+ &output_var->output_binding_idx, Program);
+}
+
+/**
+ * This handles variables of the ALIAS kind
+ *
+ * \return 0 on sucess, 1 on error
+ */
+static GLuint
+parse_alias (GLcontext * ctx, byte ** inst, struct var_cache **vc_head,
+ struct arb_program *Program)
+{
+ GLuint found;
+ struct var_cache *temp_var;
+ char *error_msg;
+
+ while (**inst != 0) {
+ temp_var = parse_string (inst, vc_head, Program, &found);
+ Program->Position = parse_position (inst);
+ if (found) {
+ error_msg =
+ _mesa_malloc (_mesa_strlen ((char *) temp_var->name) + 40);
+ _mesa_sprintf (error_msg, "Duplicate Varible Declaration: %s",
+ temp_var->name);
+
+ _mesa_set_program_error (ctx, Program->Position, error_msg);
+ _mesa_error (ctx, GL_INVALID_OPERATION, error_msg);
+
+ _mesa_free (error_msg);
+ return 1;
+ }
+
+ temp_var->type = vt_temp;
+
+ if (((Program->type == GL_FRAGMENT_PROGRAM_ARB) &&
+ (Program->Base.NumTemporaries >=
+ ctx->Const.MaxFragmentProgramTemps))
+ || ((Program->type == GL_VERTEX_PROGRAM_ARB)
+ && (Program->Base.NumTemporaries >=
+ ctx->Const.MaxVertexProgramTemps))) {
+ _mesa_set_program_error (ctx, Program->Position,
+ "Too many TEMP variables declared");
+ _mesa_error (ctx, GL_INVALID_OPERATION,
+ "Too many TEMP variables declared");
+ return 1;
+ }
+
+ temp_var->temp_binding = Program->Base.NumTemporaries;
+ Program->Base.NumTemporaries++;
+ }
+ (*inst)++;
+
+ return 0;
+}
+
+/**
+ * This handles variables of the ADDRESS kind
+ *
+ * \return 0 on sucess, 1 on error
+ */
+static GLuint
+parse_address (GLcontext * ctx, byte ** inst, struct var_cache **vc_head,
+ struct arb_program *Program)
+{
+ GLuint found;
+ struct var_cache *temp_var;
+ char *error_msg;
+
+ while (**inst != 0) {
+ temp_var = parse_string (inst, vc_head, Program, &found);
+ Program->Position = parse_position (inst);
+ if (found) {
+ error_msg =
+ _mesa_malloc (_mesa_strlen ((char *) temp_var->name) + 40);
+ _mesa_sprintf (error_msg, "Duplicate Varible Declaration: %s",
+ temp_var->name);
+
+ _mesa_set_program_error (ctx, Program->Position, error_msg);
+ _mesa_error (ctx, GL_INVALID_OPERATION, error_msg);
+
+ _mesa_free (error_msg);
+ return 1;
+ }
+
+ temp_var->type = vt_address;
+
+ if (Program->Base.NumAddressRegs >=
+ ctx->Const.MaxVertexProgramAddressRegs) {
+ _mesa_set_program_error (ctx, Program->Position,
+ "Too many ADDRESS variables declared");
+ _mesa_error (ctx, GL_INVALID_OPERATION,
+ "Too many ADDRESS variables declared");
+ return 1;
+ }
+
+ temp_var->address_binding = Program->Base.NumAddressRegs;
+ Program->Base.NumAddressRegs++;
+ }
+ (*inst)++;
+
+ return 0;
+}
+
+/**
+ * Parse a program declaration
+ *
+ * \return 0 on sucess, 1 on error
+ */
+static GLint
+parse_declaration (GLcontext * ctx, byte ** inst, struct var_cache **vc_head,
+ struct arb_program *Program)
+{
+ GLint err = 0;
+
+ switch (*(*inst)++) {
+ case ADDRESS:
+ err = parse_address (ctx, inst, vc_head, Program);
+ break;
+
+ case ALIAS:
+ err = parse_alias (ctx, inst, vc_head, Program);
+ break;
+
+ case ATTRIB:
+ err = parse_attrib (ctx, inst, vc_head, Program);
+ break;
+
+ case OUTPUT:
+ err = parse_output (ctx, inst, vc_head, Program);
+ break;
+
+ case PARAM:
+ err = parse_param (ctx, inst, vc_head, Program);
+ break;
+
+ case TEMP:
+ err = parse_temp (ctx, inst, vc_head, Program);
+ break;
+ }
+
+ return err;
+}
+
+/**
+ * Handle the parsing out of a masked destination register
+ *
+ * \param File - The register file we write to
+ * \param Index - The register index we write to
+ * \param WriteMask - The mask controlling which components we write (1->write)
+ *
+ * \return 0 on sucess, 1 on error
+ */
+static GLuint
+parse_masked_dst_reg (GLcontext * ctx, byte ** inst,
+ struct var_cache **vc_head, struct arb_program *Program,
+ GLint * File, GLint * Index, GLboolean * WriteMask)
+{
+ GLuint err, result;
+ byte mask;
+ struct var_cache *dst;
+
+ /* We either have a result register specified, or a
+ * variable that may or may not be writable
+ */
+ switch (*(*inst)++) {
+ case REGISTER_RESULT:
+ if (parse_result_binding
+ (ctx, inst, &result, (GLuint *) Index, Program))
+ return 1;
+ *File = PROGRAM_OUTPUT;
+ break;
+
+ case REGISTER_ESTABLISHED_NAME:
+ dst = parse_string (inst, vc_head, Program, &result);
+ Program->Position = parse_position (inst);
+
+ /* If the name has never been added to our symbol table, we're hosed */
+ if (!result) {
+ _mesa_set_program_error (ctx, Program->Position,
+ "Undefined variable");
+ _mesa_error (ctx, GL_INVALID_OPERATION, "Undefined variable: %s",
+ dst->name);
+ return 1;
+ }
+
+ switch (dst->type) {
+ case vt_output:
+ *File = PROGRAM_OUTPUT;
+ *Index = dst->output_binding_idx;
+ break;
+
+ case vt_temp:
+ *File = PROGRAM_TEMPORARY;
+ *Index = dst->temp_binding;
+ break;
+
+ /* If the var type is not vt_output or vt_temp, no go */
+ default:
+ _mesa_set_program_error (ctx, Program->Position,
+ "Destination register is read only");
+ _mesa_error (ctx, GL_INVALID_OPERATION,
+ "Destination register is read only: %s",
+ dst->name);
+ return 1;
+ }
+ break;
+
+ default:
+ _mesa_set_program_error (ctx, Program->Position,
+ "Unexpected opcode in parse_masked_dst_reg()");
+ _mesa_error (ctx, GL_INVALID_OPERATION,
+ "Unexpected opcode in parse_masked_dst_reg()");
+ return 1;
+ }
+
+ /* And then the mask.
+ * w,a -> bit 0
+ * z,b -> bit 1
+ * y,g -> bit 2
+ * x,r -> bit 3
+ */
+ mask = *(*inst)++;
+
+ WriteMask[0] = (mask & (1 << 3)) >> 3;
+ WriteMask[1] = (mask & (1 << 2)) >> 2;
+ WriteMask[2] = (mask & (1 << 1)) >> 1;
+ WriteMask[3] = (mask & (1));
+
+ return 0;
+}
+
+/**
+ * Handle the parsing out of a masked address register
+ *
+ * \param Index - The register index we write to
+ * \param WriteMask - The mask controlling which components we write (1->write)
+ *
+ * \return 0 on sucess, 1 on error
+ */
+static GLuint
+parse_masked_address_reg (GLcontext * ctx, byte ** inst,
+ struct var_cache **vc_head,
+ struct arb_program *Program, GLint * Index,
+ GLboolean * WriteMask)
+{
+ GLuint err, result;
+ byte mask;
+ struct var_cache *dst;
+
+ dst = parse_string (inst, vc_head, Program, &result);
+ Program->Position = parse_position (inst);
+
+ /* If the name has never been added to our symbol table, we're hosed */
+ if (!result) {
+ _mesa_set_program_error (ctx, Program->Position, "Undefined variable");
+ _mesa_error (ctx, GL_INVALID_OPERATION, "Undefined variable: %s",
+ dst->name);
+ return 1;
+ }
+
+ if (dst->type != vt_address) {
+ _mesa_set_program_error (ctx, Program->Position,
+ "Variable is not of type ADDRESS");
+ _mesa_error (ctx, GL_INVALID_OPERATION,
+ "Variable: %s is not of type ADDRESS", dst->name);
+ return 1;
+ }
+
+ /* Writemask of .x is implied */
+ WriteMask[0] = 1;
+ WriteMask[1] = WriteMask[2] = WriteMask[3] = 0;
+
+ return 0;
+}
+
+/**
+ * Parse out a swizzle mask.
+ *
+ * The values in the input stream are:
+ * COMPONENT_X -> x/r
+ * COMPONENT_Y -> y/g
+ * COMPONENT_Z-> z/b
+ * COMPONENT_W-> w/a
+ *
+ * The values in the output mask are:
+ * 0 -> x/r
+ * 1 -> y/g
+ * 2 -> z/b
+ * 3 -> w/a
+ *
+ * The len parameter allows us to grab 4 components for a vector
+ * swizzle, or just 1 component for a scalar src register selection
+ */
+static GLuint
+parse_swizzle_mask (byte ** inst, GLubyte * mask, GLint len)
+{
+ GLint a;
+
+ for (a = 0; a < 4; a++)
+ mask[a] = a;
+
+ for (a = 0; a < len; a++) {
+ switch (*(*inst)++) {
+ case COMPONENT_X:
+ mask[a] = 0;
+ break;
+
+ case COMPONENT_Y:
+ mask[a] = 1;
+ break;
+
+ case COMPONENT_Z:
+ mask[a] = 2;
+ break;
+
+ case COMPONENT_W:
+ mask[a] = 3;
+ break;
+ }
+ }
+
+ return 0;
+}
+
+/**
+ */
+static GLuint
+parse_extended_swizzle_mask (byte ** inst, GLubyte * mask, GLboolean * Negate)
+{
+ GLint a;
+ byte swz;
+
+ *Negate = GL_FALSE;
+ for (a = 0; a < 4; a++) {
+ if (parse_sign (inst))
+ *Negate = GL_TRUE;
+
+ swz = *(*inst)++;
+
+ switch (swz) {
+ case COMPONENT_0:
+ mask[a] = SWIZZLE_ZERO;
+ break;
+ case COMPONENT_1:
+ mask[a] = SWIZZLE_ONE;
+ break;
+ case COMPONENT_X:
+ mask[a] = 0;
+ break;
+ case COMPONENT_Y:
+ mask[a] = 1;
+ break;
+ case COMPONENT_Z:
+ mask[a] = 2;
+ break;
+ case COMPONENT_W:
+ mask[a] = 3;
+ break;
+
+ }
+#if 0
+ if (swz == 0)
+ mask[a] = SWIZZLE_ZERO;
+ else if (swz == 1)
+ mask[a] = SWIZZLE_ONE;
+ else
+ mask[a] = swz - 2;
+#endif
+
+ }
+
+ return 0;
+}
+
+
+static GLuint
+parse_src_reg (GLcontext * ctx, byte ** inst, struct var_cache **vc_head,
+ struct arb_program *Program, GLint * File, GLint * Index)
+{
+ struct var_cache *src;
+ GLuint binding_state, binding_idx, found, offset;
+
+ /* And the binding for the src */
+ switch (*(*inst)++) {
+ case REGISTER_ATTRIB:
+ if (parse_attrib_binding
+ (ctx, inst, Program, &binding_state, &binding_idx))
+ return 1;
+ *File = PROGRAM_INPUT;
+ *Index = binding_idx;
+ break;
+
+ case REGISTER_PARAM:
+
+ switch (**inst) {
+ case PARAM_ARRAY_ELEMENT:
+ *(*inst)++;
+ src = parse_string (inst, vc_head, Program, &found);
+ Program->Position = parse_position (inst);
+
+ if (!found) {
+ _mesa_set_program_error (ctx, Program->Position,
+ "Undefined variable");
+ _mesa_error (ctx, GL_INVALID_OPERATION,
+ "Undefined variable: %s", src->name);
+ return 1;
+ }
+
+ *File = src->param_binding_type;
+
+ switch (*(*inst)++) {
+ case ARRAY_INDEX_ABSOLUTE:
+ offset = parse_integer (inst, Program);
+
+ if ((offset < 0)
+ || (offset >= src->param_binding_length)) {
+ _mesa_set_program_error (ctx, Program->Position,
+ "Index out of range");
+ _mesa_error (ctx, GL_INVALID_OPERATION,
+ "Index %d out of range for %s", offset,
+ src->name);
+ return 1;
+ }
+
+ *Index = src->param_binding_begin + offset;
+ break;
+
+ /* XXX: */
+ case ARRAY_INDEX_RELATIVE:
+ break;
+ }
+ break;
+
+ default:
+ if (parse_param_use (ctx, inst, vc_head, Program, &src))
+ return 1;
+
+ *File = src->param_binding_type;
+ *Index = src->param_binding_begin;
+ break;
+ }
+ break;
+
+ case REGISTER_ESTABLISHED_NAME:
+ src = parse_string (inst, vc_head, Program, &found);
+ Program->Position = parse_position (inst);
+
+ /* If the name has never been added to our symbol table, we're hosed */
+ if (!found) {
+ _mesa_set_program_error (ctx, Program->Position,
+ "Undefined variable");
+ _mesa_error (ctx, GL_INVALID_OPERATION, "Undefined variable: %s",
+ src->name);
+ return 1;
+ }
+
+ switch (src->type) {
+ case vt_attrib:
+ *File = PROGRAM_INPUT;
+ *Index = src->attrib_binding_idx;
+ break;
+
+ /* XXX: We have to handle offsets someplace in here! -- or are those above? */
+ case vt_param:
+ *File = src->param_binding_type;
+ *Index = src->param_binding_begin;
+ break;
+
+ case vt_temp:
+ *File = PROGRAM_TEMPORARY;
+ *Index = src->temp_binding;
+ break;
+
+ /* If the var type is vt_output no go */
+ default:
+ _mesa_set_program_error (ctx, Program->Position,
+ "destination register is read only");
+ _mesa_error (ctx, GL_INVALID_OPERATION,
+ "destination register is read only: %s",
+ src->name);
+ return 1;
+ }
+ break;
+
+ default:
+ _mesa_set_program_error (ctx, Program->Position,
+ "Unknown token in parse_src_reg");
+ _mesa_error (ctx, GL_INVALID_OPERATION,
+ "Unknown token in parse_src_reg");
+ return 1;
+ }
+
+ return 0;
+}
+
+/**
+ */
+static GLuint
+parse_vector_src_reg (GLcontext * ctx, byte ** inst,
+ struct var_cache **vc_head, struct arb_program *Program,
+ GLint * File, GLint * Index, GLboolean * Negate,
+ GLubyte * Swizzle)
+{
+ /* Grab the sign */
+ *Negate = parse_sign (inst);
+
+ /* And the src reg */
+ if (parse_src_reg (ctx, inst, vc_head, Program, File, Index))
+ return 1;
+
+ /* finally, the swizzle */
+ parse_swizzle_mask (inst, Swizzle, 4);
+
+ return 0;
+}
+
+/**
+ */
+static GLuint
+parse_scalar_src_reg (GLcontext * ctx, byte ** inst,
+ struct var_cache **vc_head, struct arb_program *Program,
+ GLint * File, GLint * Index, GLboolean * Negate,
+ GLubyte * Swizzle)
+{
+ GLint a;
+
+ /* Grab the sign */
+ *Negate = parse_sign (inst);
+
+ /* And the src reg */
+ if (parse_src_reg (ctx, inst, vc_head, Program, File, Index))
+ return 1;
+
+ /* Now, get the component and shove it into all the swizzle slots */
+ parse_swizzle_mask (inst, Swizzle, 1);
+
+ return 0;
+}
+
+/**
+ * This is a big mother that handles getting opcodes into the instruction
+ * and handling the src & dst registers for fragment program instructions
+ */
+static GLuint
+parse_fp_instruction (GLcontext * ctx, byte ** inst,
+ struct var_cache **vc_head, struct arb_program *Program,
+ struct fp_instruction *fp)
+{
+ GLint a, b;
+ GLubyte swz[4]; /* FP's swizzle mask is a GLubyte, while VP's is GLuint */
+ GLuint texcoord;
+ byte class, type, code;
+
+ /* No condition codes in ARB_fp */
+ fp->UpdateCondRegister = 0;
+
+ /* Record the position in the program string for debugging */
+ fp->StringPos = Program->Position;
+
+ /* F_ALU_INST or F_TEX_INST */
+ class = *(*inst)++;
+
+ /* F_ALU_{VECTOR, SCALAR, BINSC, BIN, TRI, SWZ},
+ * F_TEX_{SAMPLE, KIL}
+ */
+ type = *(*inst)++;
+
+ /* The actual opcode name */
+ code = *(*inst)++;
+
+
+ /* Increment the correct count */
+ switch (class) {
+ case F_ALU_INST:
+ Program->NumAluInstructions++;
+ break;
+ case F_TEX_INST:
+ Program->NumTexInstructions++;
+ break;
+ }
+
+ fp->Saturate = 0;
+ fp->Precision = FLOAT32;
+
+ fp->DstReg.CondMask = COND_TR;
+
+ switch (type) {
+ case F_ALU_VECTOR:
+ switch (code) {
+ case F_ABS_SAT:
+ fp->Saturate = 1;
+ case F_ABS:
+ fp->Opcode = FP_OPCODE_ABS;
+ break;
+
+ case F_FLR_SAT:
+ fp->Saturate = 1;
+ case F_FLR:
+ fp->Opcode = FP_OPCODE_FLR;
+ break;
+
+ case F_FRC_SAT:
+ fp->Saturate = 1;
+ case F_FRC:
+ fp->Opcode = FP_OPCODE_FRC;
+ break;
+
+ case F_LIT_SAT:
+ fp->Saturate = 1;
+ case F_LIT:
+ fp->Opcode = FP_OPCODE_LIT;
+ break;
+
+ case F_MOV_SAT:
+ fp->Saturate = 1;
+ case F_MOV:
+ fp->Opcode = FP_OPCODE_MOV;
+ break;
+ }
+
+ if (parse_masked_dst_reg
+ (ctx, inst, vc_head, Program, (GLint *) & fp->DstReg.File,
+ &fp->DstReg.Index, fp->DstReg.WriteMask))
+ return 1;
+
+ fp->SrcReg[0].Abs = GL_FALSE;
+ fp->SrcReg[0].NegateAbs = GL_FALSE;
+ if (parse_vector_src_reg
+ (ctx, inst, vc_head, Program, (GLint *) & fp->SrcReg[0].File,
+ &fp->SrcReg[0].Index, &fp->SrcReg[0].NegateBase,
+ swz))
+ return 1;
+ for (b=0; b<4; b++)
+ fp->SrcReg[0].Swizzle[b] = swz[b];
+ break;
+
+ case F_ALU_SCALAR:
+ switch (code) {
+ case F_COS_SAT:
+ fp->Saturate = 1;
+ case F_COS:
+ fp->Opcode = FP_OPCODE_COS;
+ break;
+
+ case F_EX2_SAT:
+ fp->Saturate = 1;
+ case F_EX2:
+ fp->Opcode = FP_OPCODE_EX2;
+ break;
+
+ case F_LG2_SAT:
+ fp->Saturate = 1;
+ case F_LG2:
+ fp->Opcode = FP_OPCODE_LG2;
+ break;
+
+ case F_RCP_SAT:
+ fp->Saturate = 1;
+ case F_RCP:
+ fp->Opcode = FP_OPCODE_RCP;
+ break;
+
+ case F_RSQ_SAT:
+ fp->Saturate = 1;
+ case F_RSQ:
+ fp->Opcode = FP_OPCODE_RSQ;
+ break;
+
+ case F_SIN_SAT:
+ fp->Saturate = 1;
+ case F_SIN:
+ fp->Opcode = FP_OPCODE_SIN;
+ break;
+
+ case F_SCS_SAT:
+ fp->Saturate = 1;
+ case F_SCS:
+ fp->Opcode = FP_OPCODE_SCS;
+ break;
+ }
+
+ if (parse_masked_dst_reg
+ (ctx, inst, vc_head, Program, (GLint *) & fp->DstReg.File,
+ &fp->DstReg.Index, fp->DstReg.WriteMask))
+ return 1;
+ fp->SrcReg[0].Abs = GL_FALSE;
+ fp->SrcReg[0].NegateAbs = GL_FALSE;
+ if (parse_scalar_src_reg
+ (ctx, inst, vc_head, Program, (GLint *) & fp->SrcReg[0].File,
+ &fp->SrcReg[0].Index, &fp->SrcReg[0].NegateBase,
+ swz))
+ return 1;
+ for (b=0; b<4; b++)
+ fp->SrcReg[0].Swizzle[b] = swz[b];
+ break;
+
+ case F_ALU_BINSC:
+ switch (code) {
+ case F_POW_SAT:
+ fp->Saturate = 1;
+ case F_POW:
+ fp->Opcode = FP_OPCODE_POW;
+ break;
+ }
+
+ if (parse_masked_dst_reg
+ (ctx, inst, vc_head, Program, (GLint *) & fp->DstReg.File,
+ &fp->DstReg.Index, fp->DstReg.WriteMask))
+ return 1;
+ for (a = 0; a < 2; a++) {
+ fp->SrcReg[a].Abs = GL_FALSE;
+ fp->SrcReg[a].NegateAbs = GL_FALSE;
+ if (parse_scalar_src_reg
+ (ctx, inst, vc_head, Program, (GLint *) & fp->SrcReg[a].File,
+ &fp->SrcReg[a].Index, &fp->SrcReg[a].NegateBase,
+ swz))
+ return 1;
+ for (b=0; b<4; b++)
+ fp->SrcReg[a].Swizzle[b] = swz[b];
+ }
+ break;
+
+
+ case F_ALU_BIN:
+ switch (code) {
+ case F_ADD_SAT:
+ fp->Saturate = 1;
+ case F_ADD:
+ fp->Opcode = FP_OPCODE_ADD;
+ break;
+
+ case F_DP3_SAT:
+ fp->Saturate = 1;
+ case F_DP3:
+ fp->Opcode = FP_OPCODE_DP3;
+ break;
+
+ case F_DP4_SAT:
+ fp->Saturate = 1;
+ case F_DP4:
+ fp->Opcode = FP_OPCODE_DP4;
+ break;
+
+ case F_DPH_SAT:
+ fp->Saturate = 1;
+ case F_DPH:
+ fp->Opcode = FP_OPCODE_DPH;
+ break;
+
+ case F_DST_SAT:
+ fp->Saturate = 1;
+ case F_DST:
+ fp->Opcode = FP_OPCODE_DST;
+ break;
+
+ case F_MAX_SAT:
+ fp->Saturate = 1;
+ case F_MAX:
+ fp->Opcode = FP_OPCODE_MAX;
+ break;
+
+ case F_MIN_SAT:
+ fp->Saturate = 1;
+ case F_MIN:
+ fp->Opcode = FP_OPCODE_MIN;
+ break;
+
+ case F_MUL_SAT:
+ fp->Saturate = 1;
+ case F_MUL:
+ fp->Opcode = FP_OPCODE_MUL;
+ break;
+
+ case F_SGE_SAT:
+ fp->Saturate = 1;
+ case F_SGE:
+ fp->Opcode = FP_OPCODE_SGE;
+ break;
+
+ case F_SLT_SAT:
+ fp->Saturate = 1;
+ case F_SLT:
+ fp->Opcode = FP_OPCODE_SLT;
+ break;
+
+ case F_SUB_SAT:
+ fp->Saturate = 1;
+ case F_SUB:
+ fp->Opcode = FP_OPCODE_SUB;
+ break;
+
+ case F_XPD_SAT:
+ fp->Saturate = 1;
+ case F_XPD:
+ fp->Opcode = FP_OPCODE_X2D;
+ break;
+ }
+
+ if (parse_masked_dst_reg
+ (ctx, inst, vc_head, Program, (GLint *) & fp->DstReg.File,
+ &fp->DstReg.Index, fp->DstReg.WriteMask))
+ return 1;
+ for (a = 0; a < 2; a++) {
+ fp->SrcReg[a].Abs = GL_FALSE;
+ fp->SrcReg[a].NegateAbs = GL_FALSE;
+ if (parse_vector_src_reg
+ (ctx, inst, vc_head, Program, (GLint *) & fp->SrcReg[a].File,
+ &fp->SrcReg[a].Index, &fp->SrcReg[a].NegateBase,
+ swz))
+ return 1;
+ for (b=0; b<4; b++)
+ fp->SrcReg[a].Swizzle[b] = swz[b];
+ }
+ break;
+
+ case F_ALU_TRI:
+ switch (code) {
+ case F_CMP_SAT:
+ fp->Saturate = 1;
+ case F_CMP:
+ fp->Opcode = FP_OPCODE_CMP;
+ break;
+
+ case F_LRP_SAT:
+ fp->Saturate = 1;
+ case F_LRP:
+ fp->Opcode = FP_OPCODE_LRP;
+ break;
+
+ case F_MAD_SAT:
+ fp->Saturate = 1;
+ case F_MAD:
+ fp->Opcode = FP_OPCODE_MAD;
+ break;
+ }
+
+ if (parse_masked_dst_reg
+ (ctx, inst, vc_head, Program, (GLint *) & fp->DstReg.File,
+ &fp->DstReg.Index, fp->DstReg.WriteMask))
+ return 1;
+ for (a = 0; a < 3; a++) {
+ fp->SrcReg[a].Abs = GL_FALSE;
+ fp->SrcReg[a].NegateAbs = GL_FALSE;
+ if (parse_vector_src_reg
+ (ctx, inst, vc_head, Program, (GLint *) & fp->SrcReg[a].File,
+ &fp->SrcReg[a].Index, &fp->SrcReg[a].NegateBase,
+ swz))
+ return 1;
+ for (b=0; b<4; b++)
+ fp->SrcReg[a].Swizzle[b] = swz[b];
+ }
+ break;
+
+ case F_ALU_SWZ:
+ switch (code) {
+ case F_SWZ_SAT:
+ fp->Saturate = 1;
+ case F_SWZ:
+ fp->Opcode = FP_OPCODE_SWZ;
+ break;
+ }
+ if (parse_masked_dst_reg
+ (ctx, inst, vc_head, Program, (GLint *) & fp->DstReg.File,
+ &fp->DstReg.Index, fp->DstReg.WriteMask))
+ return 1;
+
+ if (parse_src_reg
+ (ctx, inst, vc_head, Program, (GLint *) & fp->SrcReg[0].File,
+ &fp->SrcReg[0].Index))
+ return 1;
+ parse_extended_swizzle_mask (inst, swz,
+ &fp->SrcReg[0].NegateBase);
+ for (b=0; b<4; b++)
+ fp->SrcReg[0].Swizzle[b] = swz[b];
+ break;
+
+ case F_TEX_SAMPLE:
+ switch (code) {
+ case F_TEX_SAT:
+ fp->Saturate = 1;
+ case F_TEX:
+ fp->Opcode = FP_OPCODE_TEX;
+ break;
+
+ case F_TXP_SAT:
+ fp->Saturate = 1;
+ case F_TXP:
+ fp->Opcode = FP_OPCODE_TXP;
+ break;
+
+ case F_TXB_SAT:
+ fp->Saturate = 1;
+ case F_TXB:
+ fp->Opcode = FP_OPCODE_TXB;
+ break;
+ }
+
+ if (parse_masked_dst_reg
+ (ctx, inst, vc_head, Program, (GLint *) & fp->DstReg.File,
+ &fp->DstReg.Index, fp->DstReg.WriteMask))
+ return 1;
+ fp->SrcReg[0].Abs = GL_FALSE;
+ fp->SrcReg[0].NegateAbs = GL_FALSE;
+ if (parse_vector_src_reg
+ (ctx, inst, vc_head, Program, (GLint *) & fp->SrcReg[0].File,
+ &fp->SrcReg[0].Index, &fp->SrcReg[0].NegateBase,
+ swz))
+ return 1;
+ for (b=0; b<4; b++)
+ fp->SrcReg[0].Swizzle[b] = swz[b];
+
+ /* texImageUnit */
+ if (parse_texcoord_num (ctx, inst, Program, &texcoord))
+ return 1;
+ fp->TexSrcUnit = texcoord;
+
+ /* texTarget */
+ switch (*(*inst)) {
+ case TEXTARGET_1D:
+ fp->TexSrcBit = TEXTURE_1D_BIT;
+ break;
+ case TEXTARGET_2D:
+ fp->TexSrcBit = TEXTURE_2D_BIT;
+ break;
+ case TEXTARGET_3D:
+ fp->TexSrcBit = TEXTURE_3D_BIT;
+ break;
+ case TEXTARGET_RECT:
+ fp->TexSrcBit = TEXTURE_RECT_BIT;
+ break;
+ case TEXTARGET_CUBE:
+ fp->TexSrcBit = TEXTURE_CUBE_BIT;
+ break;
+ }
+ break;
+
+ case F_TEX_KIL:
+ fp->Opcode = FP_OPCODE_KIL;
+ fp->SrcReg[0].Abs = GL_FALSE;
+ fp->SrcReg[0].NegateAbs = GL_FALSE;
+ if (parse_vector_src_reg
+ (ctx, inst, vc_head, Program, (GLint *) & fp->SrcReg[0].File,
+ &fp->SrcReg[0].Index, &fp->SrcReg[0].NegateBase,
+ swz))
+ return 1;
+ for (b=0; b<4; b++)
+ fp->SrcReg[0].Swizzle[b] = swz[b];
+ break;
+ }
+
+ return 0;
+}
+
+/**
+ * This is a big mother that handles getting opcodes into the instruction
+ * and handling the src & dst registers for vertex program instructions
+ */
+static GLuint
+parse_vp_instruction (GLcontext * ctx, byte ** inst,
+ struct var_cache **vc_head, struct arb_program *Program,
+ struct vp_instruction *vp)
+{
+ GLint a;
+ byte type, code;
+
+ /* V_GEN_{ARL, VECTOR, SCALAR, BINSC, BIN, TRI, SWZ} */
+ type = *(*inst)++;
+
+ /* The actual opcode name */
+ code = *(*inst)++;
+
+ vp->SrcReg[0].RelAddr = vp->SrcReg[1].RelAddr = vp->SrcReg[2].RelAddr = 0;
+
+ for (a = 0; a < 4; a++) {
+ vp->SrcReg[0].Swizzle[a] = a;
+ vp->SrcReg[1].Swizzle[a] = a;
+ vp->SrcReg[2].Swizzle[a] = a;
+ vp->DstReg.WriteMask[a] = 1;
+ }
+
+ switch (type) {
+ /* XXX: */
+ case V_GEN_ARL:
+ vp->Opcode = VP_OPCODE_ARL;
+
+ /* Remember to set SrcReg.RelAddr; */
+
+ /* Get the masked address register [dst] */
+ if (parse_masked_address_reg
+ (ctx, inst, vc_head, Program, &vp->DstReg.Index,
+ vp->DstReg.WriteMask))
+ return 1;
+ vp->DstReg.File = PROGRAM_ADDRESS;
+
+
+ /* Get a scalar src register */
+ if (parse_scalar_src_reg
+ (ctx, inst, vc_head, Program, (GLint *) & vp->SrcReg[0].File,
+ &vp->SrcReg[0].Index, &vp->SrcReg[0].Negate,
+ vp->SrcReg[0].Swizzle))
+ return 1;
+
+ break;
+
+ case V_GEN_VECTOR:
+ switch (code) {
+ case V_ABS:
+ vp->Opcode = VP_OPCODE_ABS;
+ break;
+ case V_FLR:
+ vp->Opcode = VP_OPCODE_FLR;
+ break;
+ case V_FRC:
+ vp->Opcode = VP_OPCODE_FRC;
+ break;
+ case V_LIT:
+ vp->Opcode = VP_OPCODE_LIT;
+ break;
+ case V_MOV:
+ vp->Opcode = VP_OPCODE_MOV;
+ break;
+ }
+ if (parse_masked_dst_reg
+ (ctx, inst, vc_head, Program, (GLint *) & vp->DstReg.File,
+ &vp->DstReg.Index, vp->DstReg.WriteMask))
+ return 1;
+ if (parse_vector_src_reg
+ (ctx, inst, vc_head, Program, (GLint *) & vp->SrcReg[0].File,
+ &vp->SrcReg[0].Index, &vp->SrcReg[0].Negate,
+ vp->SrcReg[0].Swizzle))
+ return 1;
+ break;
+
+ case V_GEN_SCALAR:
+ switch (code) {
+ case V_EX2:
+ vp->Opcode = VP_OPCODE_EX2;
+ break;
+ case V_EXP:
+ vp->Opcode = VP_OPCODE_EXP;
+ break;
+ case V_LG2:
+ vp->Opcode = VP_OPCODE_LG2;
+ break;
+ case V_LOG:
+ vp->Opcode = VP_OPCODE_LOG;
+ break;
+ case V_RCP:
+ vp->Opcode = VP_OPCODE_RCP;
+ break;
+ case V_RSQ:
+ vp->Opcode = VP_OPCODE_RSQ;
+ break;
+ }
+ if (parse_masked_dst_reg
+ (ctx, inst, vc_head, Program, (GLint *) & vp->DstReg.File,
+ &vp->DstReg.Index, vp->DstReg.WriteMask))
+ return 1;
+ if (parse_scalar_src_reg
+ (ctx, inst, vc_head, Program, (GLint *) & vp->SrcReg[0].File,
+ &vp->SrcReg[0].Index, &vp->SrcReg[0].Negate,
+ vp->SrcReg[0].Swizzle))
+ return 1;
+ break;
+
+ case V_GEN_BINSC:
+ switch (code) {
+ case V_POW:
+ vp->Opcode = VP_OPCODE_POW;
+ break;
+ }
+ if (parse_masked_dst_reg
+ (ctx, inst, vc_head, Program, (GLint *) & vp->DstReg.File,
+ &vp->DstReg.Index, vp->DstReg.WriteMask))
+ return 1;
+ for (a = 0; a < 2; a++) {
+ if (parse_scalar_src_reg
+ (ctx, inst, vc_head, Program, (GLint *) & vp->SrcReg[a].File,
+ &vp->SrcReg[a].Index, &vp->SrcReg[a].Negate,
+ vp->SrcReg[a].Swizzle))
+ return 1;
+ }
+ break;
+
+ case V_GEN_BIN:
+ switch (code) {
+ case V_ADD:
+ vp->Opcode = VP_OPCODE_ADD;
+ break;
+ case V_DP3:
+ vp->Opcode = VP_OPCODE_DP3;
+ break;
+ case V_DP4:
+ vp->Opcode = VP_OPCODE_DP4;
+ break;
+ case V_DPH:
+ vp->Opcode = VP_OPCODE_DPH;
+ break;
+ case V_DST:
+ vp->Opcode = VP_OPCODE_DST;
+ break;
+ case V_MAX:
+ vp->Opcode = VP_OPCODE_MAX;
+ break;
+ case V_MIN:
+ vp->Opcode = VP_OPCODE_MIN;
+ break;
+ case V_MUL:
+ vp->Opcode = VP_OPCODE_MUL;
+ break;
+ case V_SGE:
+ vp->Opcode = VP_OPCODE_SGE;
+ break;
+ case V_SLT:
+ vp->Opcode = VP_OPCODE_SLT;
+ break;
+ case V_SUB:
+ vp->Opcode = VP_OPCODE_SUB;
+ break;
+ case V_XPD:
+ vp->Opcode = VP_OPCODE_XPD;
+ break;
+ }
+ if (parse_masked_dst_reg
+ (ctx, inst, vc_head, Program, (GLint *) & vp->DstReg.File,
+ &vp->DstReg.Index, vp->DstReg.WriteMask))
+ return 1;
+ for (a = 0; a < 2; a++) {
+ if (parse_vector_src_reg
+ (ctx, inst, vc_head, Program, (GLint *) & vp->SrcReg[a].File,
+ &vp->SrcReg[a].Index, &vp->SrcReg[a].Negate,
+ vp->SrcReg[a].Swizzle))
+ return 1;
+ }
+ break;
+
+ case V_GEN_TRI:
+ switch (code) {
+ case V_MAD:
+ vp->Opcode = VP_OPCODE_MAD;
+ break;
+ }
+
+ if (parse_masked_dst_reg
+ (ctx, inst, vc_head, Program, (GLint *) & vp->DstReg.File,
+ &vp->DstReg.Index, vp->DstReg.WriteMask))
+ return 1;
+ for (a = 0; a < 3; a++) {
+ if (parse_vector_src_reg
+ (ctx, inst, vc_head, Program, (GLint *) & vp->SrcReg[a].File,
+ &vp->SrcReg[a].Index, &vp->SrcReg[a].Negate,
+ vp->SrcReg[a].Swizzle))
+ return 1;
+ }
+ break;
+
+ case V_GEN_SWZ:
+ switch (code) {
+ case V_SWZ:
+ vp->Opcode = VP_OPCODE_SWZ;
+ break;
+ }
+ if (parse_masked_dst_reg
+ (ctx, inst, vc_head, Program, (GLint *) & vp->DstReg.File,
+ &vp->DstReg.Index, vp->DstReg.WriteMask))
+ return 1;
+
+ if (parse_src_reg
+ (ctx, inst, vc_head, Program, (GLint *) & vp->SrcReg[0].File,
+ &vp->SrcReg[0].Index))
+ return 1;
+ parse_extended_swizzle_mask (inst, vp->SrcReg[0].Swizzle,
+ &vp->SrcReg[0].Negate);
+ break;
+ }
+ return 0;
+}
+
+#if DEBUG_PARSING
+
+static GLvoid
+print_state_token (GLint token)
+{
+ switch (token) {
+ case STATE_MATERIAL:
+ fprintf (stderr, "STATE_MATERIAL ");
+ break;
+ case STATE_LIGHT:
+ fprintf (stderr, "STATE_LIGHT ");
+ break;
+
+ case STATE_LIGHTMODEL_AMBIENT:
+ fprintf (stderr, "STATE_AMBIENT ");
+ break;
+
+ case STATE_LIGHTMODEL_SCENECOLOR:
+ fprintf (stderr, "STATE_SCENECOLOR ");
+ break;
+
+ case STATE_LIGHTPROD:
+ fprintf (stderr, "STATE_LIGHTPROD ");
+ break;
+
+ case STATE_TEXGEN:
+ fprintf (stderr, "STATE_TEXGEN ");
+ break;
+
+ case STATE_FOG_COLOR:
+ fprintf (stderr, "STATE_FOG_COLOR ");
+ break;
+
+ case STATE_FOG_PARAMS:
+ fprintf (stderr, "STATE_FOG_PARAMS ");
+ break;
+
+ case STATE_CLIPPLANE:
+ fprintf (stderr, "STATE_CLIPPLANE ");
+ break;
+
+ case STATE_POINT_SIZE:
+ fprintf (stderr, "STATE_POINT_SIZE ");
+ break;
+
+ case STATE_POINT_ATTENUATION:
+ fprintf (stderr, "STATE_ATTENUATION ");
+ break;
+
+ case STATE_MATRIX:
+ fprintf (stderr, "STATE_MATRIX ");
+ break;
+
+ case STATE_MODELVIEW:
+ fprintf (stderr, "STATE_MODELVIEW ");
+ break;
+
+ case STATE_PROJECTION:
+ fprintf (stderr, "STATE_PROJECTION ");
+ break;
+
+ case STATE_MVP:
+ fprintf (stderr, "STATE_MVP ");
+ break;
+
+ case STATE_TEXTURE:
+ fprintf (stderr, "STATE_TEXTURE ");
+ break;
+
+ case STATE_PROGRAM:
+ fprintf (stderr, "STATE_PROGRAM ");
+ break;
+
+ case STATE_MATRIX_INVERSE:
+ fprintf (stderr, "STATE_INVERSE ");
+ break;
+
+ case STATE_MATRIX_TRANSPOSE:
+ fprintf (stderr, "STATE_TRANSPOSE ");
+ break;
+
+ case STATE_MATRIX_INVTRANS:
+ fprintf (stderr, "STATE_INVTRANS ");
+ break;
+
+ case STATE_AMBIENT:
+ fprintf (stderr, "STATE_AMBIENT ");
+ break;
+
+ case STATE_DIFFUSE:
+ fprintf (stderr, "STATE_DIFFUSE ");
+ break;
+
+ case STATE_SPECULAR:
+ fprintf (stderr, "STATE_SPECULAR ");
+ break;
+
+ case STATE_EMISSION:
+ fprintf (stderr, "STATE_EMISSION ");
+ break;
+
+ case STATE_SHININESS:
+ fprintf (stderr, "STATE_SHININESS ");
+ break;
+
+ case STATE_HALF:
+ fprintf (stderr, "STATE_HALF ");
+ break;
+
+ case STATE_POSITION:
+ fprintf (stderr, "STATE_POSITION ");
+ break;
+
+ case STATE_ATTENUATION:
+ fprintf (stderr, "STATE_ATTENUATION ");
+ break;
+
+ case STATE_SPOT_DIRECTION:
+ fprintf (stderr, "STATE_DIRECTION ");
+ break;
+
+ case STATE_TEXGEN_EYE_S:
+ fprintf (stderr, "STATE_TEXGEN_EYE_S ");
+ break;
+
+ case STATE_TEXGEN_EYE_T:
+ fprintf (stderr, "STATE_TEXGEN_EYE_T ");
+ break;
+
+ case STATE_TEXGEN_EYE_R:
+ fprintf (stderr, "STATE_TEXGEN_EYE_R ");
+ break;
+
+ case STATE_TEXGEN_EYE_Q:
+ fprintf (stderr, "STATE_TEXGEN_EYE_Q ");
+ break;
+
+ case STATE_TEXGEN_OBJECT_S:
+ fprintf (stderr, "STATE_TEXGEN_EYE_S ");
+ break;
+
+ case STATE_TEXGEN_OBJECT_T:
+ fprintf (stderr, "STATE_TEXGEN_OBJECT_T ");
+ break;
+
+ case STATE_TEXGEN_OBJECT_R:
+ fprintf (stderr, "STATE_TEXGEN_OBJECT_R ");
+ break;
+
+ case STATE_TEXGEN_OBJECT_Q:
+ fprintf (stderr, "STATE_TEXGEN_OBJECT_Q ");
+ break;
+
+ case STATE_TEXENV_COLOR:
+ fprintf (stderr, "STATE_TEXENV_COLOR ");
+ break;
+
+ case STATE_DEPTH_RANGE:
+ fprintf (stderr, "STATE_DEPTH_RANGE ");
+ break;
+
+ case STATE_VERTEX_PROGRAM:
+ fprintf (stderr, "STATE_VERTEX_PROGRAM ");
+ break;
+
+ case STATE_FRAGMENT_PROGRAM:
+ fprintf (stderr, "STATE_FRAGMENT_PROGRAM ");
+ break;
+
+ case STATE_ENV:
+ fprintf (stderr, "STATE_ENV ");
+ break;
+
+ case STATE_LOCAL:
+ fprintf (stderr, "STATE_LOCAL ");
+ break;
+
+ }
+ fprintf (stderr, "[%d] ", token);
+}
+
+
+
+
+static GLvoid
+debug_variables (GLcontext * ctx, struct var_cache *vc_head,
+ struct arb_program *Program)
+{
+ struct var_cache *vc;
+ GLint a, b;
+
+ fprintf (stderr, "debug_variables, vc_head: %x\n", vc_head);
+
+ /* First of all, print out the contents of the var_cache */
+ vc = vc_head;
+ while (vc) {
+ fprintf (stderr, "[%x]\n", vc);
+ switch (vc->type) {
+ case vt_none:
+ fprintf (stderr, "UNDEFINED %s\n", vc->name);
+ break;
+ case vt_attrib:
+ fprintf (stderr, "ATTRIB %s\n", vc->name);
+ fprintf (stderr, " binding: 0x%x\n", vc->attrib_binding);
+ break;
+ case vt_param:
+ fprintf (stderr, "PARAM %s begin: %d len: %d\n", vc->name,
+ vc->param_binding_begin, vc->param_binding_length);
+ b = vc->param_binding_begin;
+ for (a = 0; a < vc->param_binding_length; a++) {
+ fprintf (stderr, "%s\n",
+ Program->Parameters->Parameters[a + b].Name);
+ if (Program->Parameters->Parameters[a + b].Type == STATE) {
+ print_state_token (Program->Parameters->Parameters[a + b].
+ StateIndexes[0]);
+ print_state_token (Program->Parameters->Parameters[a + b].
+ StateIndexes[1]);
+ print_state_token (Program->Parameters->Parameters[a + b].
+ StateIndexes[2]);
+ print_state_token (Program->Parameters->Parameters[a + b].
+ StateIndexes[3]);
+ print_state_token (Program->Parameters->Parameters[a + b].
+ StateIndexes[4]);
+ print_state_token (Program->Parameters->Parameters[a + b].
+ StateIndexes[5]);
+ }
+ else
+ fprintf (stderr, "%f %f %f %f\n",
+ Program->Parameters->Parameters[a + b].Values[0],
+ Program->Parameters->Parameters[a + b].Values[1],
+ Program->Parameters->Parameters[a + b].Values[2],
+ Program->Parameters->Parameters[a + b].Values[3]);
+ }
+ break;
+ case vt_temp:
+ fprintf (stderr, "TEMP %s\n", vc->name);
+ fprintf (stderr, " binding: 0x%x\n", vc->temp_binding);
+ break;
+ case vt_output:
+ fprintf (stderr, "OUTPUT %s\n", vc->name);
+ fprintf (stderr, " binding: 0x%x\n", vc->output_binding);
+ break;
+ case vt_alias:
+ fprintf (stderr, "ALIAS %s\n", vc->name);
+ fprintf (stderr, " binding: 0x%x (%s)\n",
+ vc->alias_binding, vc->alias_binding->name);
+ break;
+ }
+ vc = vc->next;
+ }
+}
+
+#endif
+
+/**
+ * The main loop for parsing a fragment or vertex program
+ *
+ * \return 0 on sucess, 1 on error
+ */
+
+static GLint
+parse_arb_program (GLcontext * ctx, byte * inst, struct var_cache **vc_head,
+ struct arb_program *Program)
+{
+ GLint err = 0;
+
+ Program->MajorVersion = (GLuint) * inst++;
+ Program->MinorVersion = (GLuint) * inst++;
+
+ while (*inst != END) {
+ switch (*inst++) {
+ /* XXX: */
+ case OPTION:
+
+ if (Program->type == GL_FRAGMENT_PROGRAM_ARB) {
+ switch (*inst++) {
+ case ARB_PRECISION_HINT_FASTEST:
+ Program->HintPrecisionFastest = 1;
+ break;
+
+ case ARB_PRECISION_HINT_NICEST:
+ Program->HintPrecisionNicest = 1;
+ break;
+
+ case ARB_FOG_EXP:
+ Program->HintFogExp = 1;
+ break;
+
+ case ARB_FOG_EXP2:
+ Program->HintFogExp2 = 1;
+ break;
+
+ case ARB_FOG_LINEAR:
+ Program->HintFogLinear = 1;
+ break;
+ }
+ }
+ else {
+ switch (*inst++) {
+ case ARB_POSITION_INVARIANT:
+ Program->HintPositionInvariant = 1;
+ break;
+ }
+ }
+ break;
+
+ case INSTRUCTION:
+ Program->Position = parse_position (&inst);
+
+ if (Program->type == GL_FRAGMENT_PROGRAM_ARB) {
+ /* Realloc Program->FPInstructions */
+ Program->FPInstructions =
+ (struct fp_instruction *) _mesa_realloc (Program->FPInstructions,
+ Program->Base.NumInstructions*sizeof(struct fp_instruction),
+ (Program->Base.NumInstructions+1)*sizeof (struct fp_instruction));
+
+ /* parse the current instruction */
+ err = parse_fp_instruction (ctx, &inst, vc_head, Program,
+ &Program->FPInstructions[Program->Base.NumInstructions]);
+
+ }
+ else {
+ /* Realloc Program->VPInstructions */
+ Program->VPInstructions =
+ (struct vp_instruction *) _mesa_realloc (Program->VPInstructions,
+ Program->Base.NumInstructions*sizeof(struct vp_instruction),
+ (Program->Base.NumInstructions +1)*sizeof(struct vp_instruction));
+
+ /* parse the current instruction */
+ err = parse_vp_instruction (ctx, &inst, vc_head, Program,
+ &Program->VPInstructions[Program->Base.NumInstructions]);
+ }
+
+ /* increment Program->Base.NumInstructions */
+ Program->Base.NumInstructions++;
+ break;
+
+ case DECLARATION:
+ err = parse_declaration (ctx, &inst, vc_head, Program);
+ break;
+
+ default:
+ break;
+ }
+
+ if (err)
+ break;
+ }
+
+ /* Finally, tag on an OPCODE_END instruction */
+ if (Program->type == GL_FRAGMENT_PROGRAM_ARB) {
+ Program->FPInstructions =
+ (struct fp_instruction *) _mesa_realloc (Program->FPInstructions,
+ Program->Base.NumInstructions*sizeof(struct fp_instruction),
+ (Program->Base.NumInstructions+1)*sizeof(struct fp_instruction));
+
+ Program->FPInstructions[Program->Base.NumInstructions].Opcode = FP_OPCODE_END;
+ }
+ else {
+ Program->VPInstructions =
+ (struct vp_instruction *) _mesa_realloc (Program->VPInstructions,
+ Program->Base.NumInstructions*sizeof(struct vp_instruction),
+ (Program->Base.NumInstructions+1)*sizeof(struct vp_instruction));
+
+ Program->VPInstructions[Program->Base.NumInstructions].Opcode = VP_OPCODE_END;
+ }
+
+ /* increment Program->Base.NumInstructions */
+ Program->Base.NumInstructions++;
+
+ return err;
+}
+
+/**
+ * This kicks everything off.
+ *
+ * \param ctx - The GL Context
+ * \param str - The program string
+ * \param len - The program string length
+ * \param Program - The arb_program struct to return all the parsed info in
+ * \return 0 on sucess, 1 on error
+ */
+GLuint
+_mesa_parse_arb_program (GLcontext * ctx, const GLubyte * str, GLsizei len,
+ struct arb_program * Program)
+{
+ GLint a, err, error_pos;
+ char error_msg[300];
+ GLuint parsed_len;
+ struct var_cache *vc_head;
+ dict *dt;
+ byte *parsed, *inst;
+
+#if DEBUG_PARSING
+ fprintf (stderr, "Loading grammar text!\n");
+#endif
+ dt = grammar_load_from_text ((byte *) arb_grammar_text);
+ if (!dt) {
+ grammar_get_last_error ((byte *) error_msg, 300, &error_pos);
+ _mesa_set_program_error (ctx, error_pos, error_msg);
+ _mesa_error (ctx, GL_INVALID_OPERATION,
+ "Error loading grammer rule set");
+ return 1;
+ }
+
+#if DEBUG_PARSING
+ printf ("Checking Grammar!\n");
+#endif
+ err = grammar_check (dt, str, &parsed, &parsed_len);
+
+
+ /* Syntax parse error */
+ if (err == 0) {
+ grammar_get_last_error ((byte *) error_msg, 300, &error_pos);
+ _mesa_set_program_error (ctx, error_pos, error_msg);
+ _mesa_error (ctx, GL_INVALID_OPERATION, "Parse Error");
+
+ dict_destroy (&dt);
+ return 1;
+ }
+
+#if DEBUG_PARSING
+ printf ("Destroying grammer dict [parse retval: %d]\n", err);
+#endif
+ dict_destroy (&dt);
+
+ /* Initialize the arb_program struct */
+ Program->Base.NumInstructions =
+ Program->Base.NumTemporaries =
+ Program->Base.NumParameters =
+ Program->Base.NumAttributes = Program->Base.NumAddressRegs = 0;
+ Program->Parameters = _mesa_new_parameter_list ();
+ Program->InputsRead = 0;
+ Program->OutputsWritten = 0;
+ Program->Position = 0;
+ Program->MajorVersion = Program->MinorVersion = 0;
+ Program->HintPrecisionFastest =
+ Program->HintPrecisionNicest =
+ Program->HintFogExp2 =
+ Program->HintFogExp =
+ Program->HintFogLinear = Program->HintPositionInvariant = 0;
+ for (a = 0; a < MAX_TEXTURE_IMAGE_UNITS; a++)
+ Program->TexturesUsed[a] = 0;
+ Program->NumAluInstructions =
+ Program->NumTexInstructions =
+ Program->NumTexIndirections = 0;
+
+ Program->FPInstructions = NULL;
+ Program->VPInstructions = NULL;
+
+ vc_head = NULL;
+ err = 0;
+
+ /* Start examining the tokens in the array */
+ inst = parsed;
+
+ /* Check the grammer rev */
+ if (*inst++ != REVISION) {
+ _mesa_set_program_error (ctx, 0, "Grammar version mismatch");
+ _mesa_error (ctx, GL_INVALID_OPERATION, "Grammar verison mismatch");
+ err = 1;
+ }
+ else {
+ switch (*inst++) {
+ case FRAGMENT_PROGRAM:
+ Program->type = GL_FRAGMENT_PROGRAM_ARB;
+ break;
+
+ case VERTEX_PROGRAM:
+ Program->type = GL_VERTEX_PROGRAM_ARB;
+ break;
+ }
+
+ err = parse_arb_program (ctx, inst, &vc_head, Program);
+#if DEBUG_PARSING
+ fprintf (stderr, "Symantic analysis returns %d [1 is bad!]\n", err);
+#endif
+ }
+
+ /*debug_variables(ctx, vc_head, Program); */
+
+ /* We're done with the parsed binary array */
+ var_cache_destroy (&vc_head);
+
+ _mesa_free (parsed);
+#if DEBUG_PARSING
+ printf ("_mesa_parse_arb_program() done\n");
+#endif
+ return err;
+}