/* * 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" /* 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..) * + test for 0 on matrix rows, or give a default value to 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 -- ?? * * - 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 ? * - XXX: this fails: * "MUL result.color.xyz, R0, program.local[35] ;" * but this works: * "MUL result.color.primary.xyz, R0, program.local[35] ;" * -> see progs/tests/arbvptorus.c * * - changed "progLocalParam\n" changed from: * " \"local\" .emit PROGRAM_PARAM_LOCAL .and lbracket .and progLocalParamNum * .and rbracket;\n" * to: * " \"local\" .emit PROGRAM_PARAM_LOCAL .and lbracket .and progLocalParamNum * .and rbracket .emit 0x00;\n" * so we can distinguish between the progLocalParam and progLocalParams rules * * - made the same change as above to the progEnvParam rule */ typedef GLubyte *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 ; The 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: '' - '' If specifier is a character range, it evaluates to true if character in the stream is greater or equal to and less or equal to . 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: '' It evaluates to true if the following character range specifier evaluates to true: '' - '' String specifier is in the form: "" Let N be the number of characters in . Let [i] designate i-th character in . 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: '[i]' If [i] is a quotation mark, '[i]' is replaced with '\[i]'. Symbol specifier can be optionally preceded by a ".loop" keyword in the form: .loop (1) where is defined as follows: ; (2) Construction (1) is replaced by the following code: and declaration (2) is replaced by the following: .or .true; .and ; ; 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 is an identifier declared earlier by error text declaration. The declaration is in the form: .errtext "" When specifier evaluates to false and this construction is present, parsing is stopped immediately and 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. 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 can be a HEX number, identifier, a star * or a dollar $. HEX number is preceded by 0x or 0X. If is an identifier, it must be earlier declared by emit code declaration in the form: .emtcode 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 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 GLubyte *str_duplicate (const GLubyte *); /* internal error messages */ static const GLubyte *OUT_OF_MEMORY = (GLubyte *) "internal error 1001: out of physical memory"; static const GLubyte *UNRESOLVED_REFERENCE = (GLubyte *) "internal error 1002: unresolved reference '$'"; /* static const GLubyte *INVALID_PARAMETER = (GLubyte *) "internal error 1003: invalid parameter"; */ static const GLubyte *error_message = NULL; static GLubyte *error_param = NULL; /* this is inserted into error_message in place of $ */ static GLint error_position = -1; static GLubyte *unknown = (GLubyte *) "???"; 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 GLubyte * msg, GLubyte * 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 GLubyte * str_duplicate (const GLubyte * str) { return (GLubyte *) _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; GLubyte 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_ { GLubyte *m_text; GLubyte *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 GLubyte *error_get_token (error *, struct dict_ *, const GLubyte *, 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; GLubyte m_byte[2]; /* st_byte, st_byte_range */ GLubyte *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); } } /* * GLubyte array typedef */ typedef struct barray_ { GLubyte *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 GLubyte array to requested size, * returns 0 on success, * returns 1 otherwise */ static GLint barray_resize (barray ** ba, GLuint nlen) { GLubyte *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 (GLubyte), nlen * sizeof (GLubyte)); if (new_pointer) { (**ba).data = new_pointer; (**ba).len = nlen; return 0; } } return 1; } /* * adds GLubyte 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. * \return 0 on success, 1 otherwise. */ static GLint barray_push (barray ** ba, emit * em, GLubyte 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--] = (GLubyte) pos, (**ba).data[(**ba).len - count--] = (GLubyte) (pos >> 8), (**ba).data[(**ba).len - count--] = (GLubyte) (pos >> 16), (**ba).data[(**ba).len - count--] = (GLubyte) (pos >> 24); #else (**ba).data[(**ba).len - count--] = (GLubyte) pos; (**ba).data[(**ba).len - count--] = (GLubyte) (pos / 0x100); (**ba).data[(**ba).len - count--] = (GLubyte) (pos / 0x10000); (**ba).data[(**ba).len - count--] = (GLubyte) (pos / 0x1000000); #endif } em = em->m_next; } return 0; } /** * string to string map typedef */ typedef struct map_str_ { GLubyte *key; GLubyte *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, * \return 0 if the key is matched, 1 otherwise */ static GLint map_str_find (map_str ** ma, const GLubyte * key, GLubyte ** 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 GLubyte map typedef */ typedef struct map_byte_ { GLubyte *key; GLubyte 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, * \return 0 if the is matched, 1 otherwise */ static GLint map_byte_find (map_byte ** ma, const GLubyte * key, GLubyte * 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_ { GLubyte *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, * \return 0 if the is matched, 1 otherwise */ static GLint map_def_find (map_def ** ma, const GLubyte * 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 (GLubyte 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 GLubyte ** 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 GLubyte * 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 GLubyte ** 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 GLubyte ** 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 (GLubyte ** ptr, GLuint * len, GLubyte c) { /* reallocate the string in 16-length increments */ if ((*len & 0x0F) == 0x0F || *ptr == NULL) { GLubyte *tmp = mem_realloc (*ptr, (*len) * sizeof (GLubyte), ((*len + 1 + 1 + 0x0F) & ~0x0F) * sizeof (GLubyte)); 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 (GLubyte 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 GLubyte ** text, GLubyte ** id) { const GLubyte *t = *text; GLubyte *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 (GLubyte 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 (GLubyte 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 GLubyte ** 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 (GLubyte c) { return c >= '0' && c <= '7'; } /* * returns value of passed character as if it was OCT digit */ static GLint oct2dec (GLubyte c) { return c - '0'; } static GLubyte get_escape_sequence (const GLubyte ** 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 (GLubyte) 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 (GLubyte) 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 GLubyte ** text, GLubyte ** str) { const GLubyte *t = *text; GLubyte *p = NULL; GLuint len = 0; GLubyte 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) { GLubyte 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" " " " " ("0x" | "0X") * assumes that *text already points to , * returns 0 if emit code is successfully read, * returns 1 otherwise */ static GLint get_emtcode (const GLubyte ** text, map_byte ** ma) { const GLubyte *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 == '\'') { GLubyte *c; if (get_string (&t, &c)) { map_byte_destroy (&m); return 1; } m->data = (GLubyte) c[0]; mem_free ((GLvoid **) & c); } else { /* skip HEX "0x" or "0X" prefix */ t += 2; m->data = (GLubyte) hex_convert (&t); } eat_spaces (&t); *text = t; *ma = m; return 0; } /* * returns 0 on success, * returns 1 otherwise */ static GLint get_errtext (const GLubyte ** text, map_str ** ma) { const GLubyte *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 GLubyte ** text, error ** er, map_str * maps) { const GLubyte *t = *text; GLubyte *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 ((GLubyte **) (&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 ((GLubyte **) (&processed), &len, '$')) { mem_free ((GLvoid **) & processed); error_destroy (er); return 1; } i += 2; } else if ((**er).m_text[i] != '$') { if (string_grow ((GLubyte **) (&processed), &len, (**er).m_text[i])) { mem_free ((GLvoid **) & processed); error_destroy (er); return 1; } i++; } else { if (string_grow ((GLubyte **) (&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 = (GLubyte *) processed; } *text = t; return 0; } /* * returns 0 on success, * returns 1 otherwise, */ static GLint get_emits (const GLubyte ** text, emit ** em, map_byte * mapb) { const GLubyte *t = *text; GLubyte *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 = (GLubyte) 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 = (GLubyte) 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 GLubyte ** text, spec ** sp, map_str * maps, map_byte * mapb) { const GLubyte *t = *text; spec *s = NULL; spec_create (&s); if (s == NULL) return 1; if (*t == '\'') { GLubyte *temp = NULL; if (get_string (&t, &temp)) { spec_destroy (&s); return 1; } eat_spaces (&t); if (*t == '-') { GLubyte *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 == '.') { GLubyte *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 GLubyte ** text, defntn ** de, map_str * maps, map_byte * mapb) { const GLubyte *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 != ';') { GLubyte *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, GLubyte * 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, GLubyte ** syntax_symbol, GLubyte ** 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 GLubyte * 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 GLubyte * error_get_token (error * er, dict * di, const GLubyte * text, unsigned int ind) { GLubyte *str = NULL; if (er->m_token) { barray *ba; GLuint 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; GLubyte *syntax_symbol; GLubyte *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 GLubyte * 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) { GLubyte *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 GLubyte * text, GLubyte ** 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 (GLubyte)); if (*production == NULL) { barray_destroy (&ba); return 0; } memcpy (*production, ba->data, ba->len * sizeof (GLubyte)); *size = ba->len; barray_destroy (&ba); return 1; } static GLvoid grammar_get_last_error (GLubyte * text, GLint size, GLint *pos) { GLint len = 0, dots_made = 0; const GLubyte *p = error_message; *text = '\0'; #define APPEND_CHARACTER(x) if (dots_made == 0) {\ if (len < size - 1) {\ text[len++] = (x); text[len] = '\0';\ } else {\ GLint i;\ for (i = 0; i < 3; i++)\ if (--len >= 0)\ text[len] = '.';\ dots_made = 1;\ }\ } if (p) { while (*p) { if (*p == '$') { const GLubyte *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 { GLubyte *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 attrib_is_generic; /* If the attrib was specified through a generic * vertex attrib */ 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).attrib_is_generic = 0; (**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, GLubyte * name) { struct var_cache *first = va; while (va) { if (!strcmp ( (const char*) name, (const char*) 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 GLubytes in LE format */ static GLuint parse_position (GLubyte ** 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 (GLubyte ** inst, struct var_cache **vc_head, struct arb_program *Program, GLuint * found) { GLubyte *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 (GLubyte ** inst, struct arb_program *Program) { GLubyte *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 (GLubyte ** 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 (GLubyte ** 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 (GLubyte ** 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 (GLubyte ** 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 (GLubyte ** 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, GLubyte ** inst, struct arb_program *Program, GLint * color) { *color = *(*inst)++ != COLOR_PRIMARY; return 0; } /** * Get an integer corresponding to a generic vertex attribute. * * \return 0 on sucess, 1 on error */ static GLuint parse_generic_attrib_num(GLcontext *ctx, GLubyte ** inst, struct arb_program *Program, GLuint *attrib) { *attrib = parse_integer(inst, Program); if ((*attrib < 0) || (*attrib > MAX_VERTEX_PROGRAM_ATTRIBS)) { _mesa_set_program_error (ctx, Program->Position, "Invalid generic vertex attribute index"); _mesa_error (ctx, GL_INVALID_OPERATION, "Invalid generic vertex attribute index"); return 1; } return 0; } /** * \param coord The texture unit index * \return 0 on sucess, 1 on error */ static GLuint parse_texcoord_num (GLcontext * ctx, GLubyte ** 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, GLubyte ** 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, GLubyte ** 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 (GLubyte ** inst) { switch (*(*inst)++) { case FACE_FRONT: return 0; case FACE_BACK: return 1; } return 0; } /** * 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, GLubyte ** inst, struct arb_program *Program, GLint * matrix, GLint * matrix_idx, GLint * matrix_modifier) { GLubyte 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, GLubyte ** 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, GLubyte ** 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; } /** * For ARB_vertex_program, programs are not allowed to use both an explicit * vertex attribute and a generic vertex attribute corresponding to the same * state. See section 2.14.3.1 of the GL_ARB_vertex_program spec. * * This will walk our var_cache and make sure that nobody does anything fishy. * * \return 0 on sucess, 1 on error */ static GLuint generic_attrib_check(struct var_cache *vc_head) { int a; struct var_cache *curr; GLubyte explicit[MAX_VERTEX_PROGRAM_ATTRIBS], generic[MAX_VERTEX_PROGRAM_ATTRIBS]; for (a=0; atype == vt_attrib) { if (curr->attrib_is_generic) generic[ curr->attrib_binding_idx ] = 1; else explicit[ curr->attrib_binding_idx ] = 1; } curr = curr->next; } for (a=0; atype == 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; case VERTEX_ATTRIB_GENERIC: { GLuint attrib; if (!parse_generic_attrib_num(ctx, inst, Program, &attrib)) { *is_generic = 1; switch (attrib) { case 0: *binding = VERT_ATTRIB_POS; break; case 1: *binding = VERT_ATTRIB_WEIGHT; break; case 2: *binding = VERT_ATTRIB_NORMAL; break; case 3: *binding = VERT_ATTRIB_COLOR0; break; case 4: *binding = VERT_ATTRIB_COLOR1; break; case 5: *binding = VERT_ATTRIB_FOG; break; case 6: break; case 7: break; default: *binding = VERT_ATTRIB_TEX0 + (attrib-8); break; } *binding_idx = attrib; } } 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, GLubyte ** inst, GLuint * binding, GLuint * binding_idx, struct arb_program *Program) { 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; GLuint face_type = parse_face_type(inst); GLint color_type_ret = parse_color_type(ctx, inst, Program, &color_type); /* back face */ if (face_type) { if (color_type_ret) 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, GLubyte ** 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, &attrib_var->attrib_is_generic)) return 1; if (generic_attrib_check(*vc_head)) { _mesa_set_program_error (ctx, Program->Position, "Cannot use both a generic vertex attribute and a specific attribute of the same type"); _mesa_error (ctx, GL_INVALID_OPERATION, "Cannot use both a generic vertex attribute and a specific attribute of the same type"); 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, GLubyte ** inst, struct var_cache *param_var, struct arb_program *Program, GLboolean use) { GLint idx; GLuint err; GLint state_tokens[6]; GLfloat const_values[4]; 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++; } } else { (*(*inst)++); } 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, GLubyte ** inst, struct var_cache **vc_head, struct arb_program *Program) { GLuint found, specified_length, err; 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, GLubyte ** 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 = (GLubyte *) _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, GLubyte ** 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, GLubyte ** 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, GLubyte ** inst, struct var_cache **vc_head, struct arb_program *Program) { GLuint found; struct var_cache *temp_var; char *error_msg; 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_alias; temp_var->alias_binding = 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, "Alias value %s is not defined", temp_var->alias_binding->name); _mesa_set_program_error (ctx, Program->Position, error_msg); _mesa_error (ctx, GL_INVALID_OPERATION, error_msg); _mesa_free (error_msg); return 1; } return 0; } /** * This handles variables of the ADDRESS kind * * \return 0 on sucess, 1 on error */ static GLuint parse_address (GLcontext * ctx, GLubyte ** 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, GLubyte ** 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, GLubyte ** inst, struct var_cache **vc_head, struct arb_program *Program, GLint * File, GLint * Index, GLboolean * WriteMask) { GLuint result; GLubyte 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, "0: Undefined variable"); _mesa_error (ctx, GL_INVALID_OPERATION, "0: 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, GLubyte ** inst, struct var_cache **vc_head, struct arb_program *Program, GLint * Index, GLboolean * WriteMask) { struct var_cache *dst; GLuint result; 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, "1: Undefined variable"); _mesa_error (ctx, GL_INVALID_OPERATION, "1: 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 (GLubyte ** 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 (GLubyte ** inst, GLubyte * mask, GLboolean * Negate) { GLint a; GLubyte 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, GLubyte ** inst, struct var_cache **vc_head, struct arb_program *Program, GLint * File, GLint * Index) { struct var_cache *src; GLuint binding_state, binding_idx, is_generic, found, offset; /* And the binding for the src */ switch (*(*inst)++) { case REGISTER_ATTRIB: if (parse_attrib_binding (ctx, inst, Program, &binding_state, &binding_idx, &is_generic)) return 1; *File = PROGRAM_INPUT; *Index = binding_idx; /* We need to insert a dummy variable into the var_cache so we can * catch generic vertex attrib aliasing errors */ var_cache_create(&src); src->type = vt_attrib; src->name = (GLubyte *)_mesa_strdup("Dummy Attrib Variable"); src->attrib_binding = binding_state; src->attrib_binding_idx = binding_idx; src->attrib_is_generic = is_generic; var_cache_append(vc_head, src); if (generic_attrib_check(*vc_head)) { _mesa_set_program_error (ctx, Program->Position, "Cannot use both a generic vertex attribute and a specific attribute of the same type"); _mesa_error (ctx, GL_INVALID_OPERATION, "Cannot use both a generic vertex attribute and a specific attribute of the same type"); return 1; } 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, "2: Undefined variable"); _mesa_error (ctx, GL_INVALID_OPERATION, "2: 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, "3: Undefined variable"); _mesa_error (ctx, GL_INVALID_OPERATION, "3: 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, GLubyte ** 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, GLubyte ** 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; /* 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, GLubyte ** 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; GLubyte 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, GLubyte ** inst, struct var_cache **vc_head, struct arb_program *Program, struct vp_instruction *vp) { GLint a; GLubyte 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, GLubyte * 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; GLubyte *parsed, *inst; #if DEBUG_PARSING fprintf (stderr, "Loading grammar text!\n"); #endif dt = grammar_load_from_text ((GLubyte *) arb_grammar_text); if (!dt) { grammar_get_last_error ((GLubyte *) 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 ((GLubyte *) 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; }