diff options
Diffstat (limited to 'src/mesa/main/arbparse.c')
-rw-r--r-- | src/mesa/main/arbparse.c | 5717 |
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 (¶m_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; +} |