summaryrefslogtreecommitdiff
path: root/src/glsl/cl/sl_cl_parse.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/glsl/cl/sl_cl_parse.c')
-rw-r--r--src/glsl/cl/sl_cl_parse.c2714
1 files changed, 2714 insertions, 0 deletions
diff --git a/src/glsl/cl/sl_cl_parse.c b/src/glsl/cl/sl_cl_parse.c
new file mode 100644
index 0000000000..a9db65c7ad
--- /dev/null
+++ b/src/glsl/cl/sl_cl_parse.c
@@ -0,0 +1,2714 @@
+/**************************************************************************
+ *
+ * Copyright 2009 VMware, Inc.
+ * 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, sub license, 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 (including the
+ * next paragraph) 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 NON-INFRINGEMENT.
+ * IN NO EVENT SHALL TUNGSTEN GRAPHICS AND/OR ITS SUPPLIERS 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.
+ *
+ **************************************************************************/
+
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+#include "../pp/sl_pp_public.h"
+#include "sl_cl_parse.h"
+
+
+/* revision number - increment after each change affecting emitted output */
+#define REVISION 5
+
+/* external declaration (or precision or invariant stmt) */
+#define EXTERNAL_NULL 0
+#define EXTERNAL_FUNCTION_DEFINITION 1
+#define EXTERNAL_DECLARATION 2
+#define DEFAULT_PRECISION 3
+#define INVARIANT_STMT 4
+
+/* precision */
+#define PRECISION_DEFAULT 0
+#define PRECISION_LOW 1
+#define PRECISION_MEDIUM 2
+#define PRECISION_HIGH 3
+
+/* declaration */
+#define DECLARATION_FUNCTION_PROTOTYPE 1
+#define DECLARATION_INIT_DECLARATOR_LIST 2
+
+/* function type */
+#define FUNCTION_ORDINARY 0
+#define FUNCTION_CONSTRUCTOR 1
+#define FUNCTION_OPERATOR 2
+
+/* function call type */
+#define FUNCTION_CALL_NONARRAY 0
+#define FUNCTION_CALL_ARRAY 1
+
+/* operator type */
+#define OPERATOR_ADDASSIGN 1
+#define OPERATOR_SUBASSIGN 2
+#define OPERATOR_MULASSIGN 3
+#define OPERATOR_DIVASSIGN 4
+/*#define OPERATOR_MODASSIGN 5*/
+/*#define OPERATOR_LSHASSIGN 6*/
+/*#define OPERATOR_RSHASSIGN 7*/
+/*#define OPERATOR_ORASSIGN 8*/
+/*#define OPERATOR_XORASSIGN 9*/
+/*#define OPERATOR_ANDASSIGN 10*/
+#define OPERATOR_LOGICALXOR 11
+/*#define OPERATOR_BITOR 12*/
+/*#define OPERATOR_BITXOR 13*/
+/*#define OPERATOR_BITAND 14*/
+#define OPERATOR_LESS 15
+#define OPERATOR_GREATER 16
+#define OPERATOR_LESSEQUAL 17
+#define OPERATOR_GREATEREQUAL 18
+/*#define OPERATOR_LSHIFT 19*/
+/*#define OPERATOR_RSHIFT 20*/
+#define OPERATOR_MULTIPLY 21
+#define OPERATOR_DIVIDE 22
+/*#define OPERATOR_MODULUS 23*/
+#define OPERATOR_INCREMENT 24
+#define OPERATOR_DECREMENT 25
+#define OPERATOR_PLUS 26
+#define OPERATOR_MINUS 27
+/*#define OPERATOR_COMPLEMENT 28*/
+#define OPERATOR_NOT 29
+
+/* init declarator list */
+#define DECLARATOR_NONE 0
+#define DECLARATOR_NEXT 1
+
+/* variable declaration */
+#define VARIABLE_NONE 0
+#define VARIABLE_IDENTIFIER 1
+#define VARIABLE_INITIALIZER 2
+#define VARIABLE_ARRAY_EXPLICIT 3
+#define VARIABLE_ARRAY_UNKNOWN 4
+
+/* type qualifier */
+#define TYPE_QUALIFIER_NONE 0
+#define TYPE_QUALIFIER_CONST 1
+#define TYPE_QUALIFIER_ATTRIBUTE 2
+#define TYPE_QUALIFIER_VARYING 3
+#define TYPE_QUALIFIER_UNIFORM 4
+#define TYPE_QUALIFIER_FIXEDOUTPUT 5
+#define TYPE_QUALIFIER_FIXEDINPUT 6
+
+/* invariant qualifier */
+#define TYPE_VARIANT 90
+#define TYPE_INVARIANT 91
+
+/* centroid qualifier */
+#define TYPE_CENTER 95
+#define TYPE_CENTROID 96
+
+/* type specifier */
+#define TYPE_SPECIFIER_VOID 0
+#define TYPE_SPECIFIER_BOOL 1
+#define TYPE_SPECIFIER_BVEC2 2
+#define TYPE_SPECIFIER_BVEC3 3
+#define TYPE_SPECIFIER_BVEC4 4
+#define TYPE_SPECIFIER_INT 5
+#define TYPE_SPECIFIER_IVEC2 6
+#define TYPE_SPECIFIER_IVEC3 7
+#define TYPE_SPECIFIER_IVEC4 8
+#define TYPE_SPECIFIER_FLOAT 9
+#define TYPE_SPECIFIER_VEC2 10
+#define TYPE_SPECIFIER_VEC3 11
+#define TYPE_SPECIFIER_VEC4 12
+#define TYPE_SPECIFIER_MAT2 13
+#define TYPE_SPECIFIER_MAT3 14
+#define TYPE_SPECIFIER_MAT4 15
+#define TYPE_SPECIFIER_SAMPLER1D 16
+#define TYPE_SPECIFIER_SAMPLER2D 17
+#define TYPE_SPECIFIER_SAMPLER3D 18
+#define TYPE_SPECIFIER_SAMPLERCUBE 19
+#define TYPE_SPECIFIER_SAMPLER1DSHADOW 20
+#define TYPE_SPECIFIER_SAMPLER2DSHADOW 21
+#define TYPE_SPECIFIER_SAMPLER2DRECT 22
+#define TYPE_SPECIFIER_SAMPLER2DRECTSHADOW 23
+#define TYPE_SPECIFIER_STRUCT 24
+#define TYPE_SPECIFIER_TYPENAME 25
+
+/* OpenGL 2.1 */
+#define TYPE_SPECIFIER_MAT23 26
+#define TYPE_SPECIFIER_MAT32 27
+#define TYPE_SPECIFIER_MAT24 28
+#define TYPE_SPECIFIER_MAT42 29
+#define TYPE_SPECIFIER_MAT34 30
+#define TYPE_SPECIFIER_MAT43 31
+
+/* type specifier array */
+#define TYPE_SPECIFIER_NONARRAY 0
+#define TYPE_SPECIFIER_ARRAY 1
+
+/* structure field */
+#define FIELD_NONE 0
+#define FIELD_NEXT 1
+#define FIELD_ARRAY 2
+
+/* operation */
+#define OP_END 0
+#define OP_BLOCK_BEGIN_NO_NEW_SCOPE 1
+#define OP_BLOCK_BEGIN_NEW_SCOPE 2
+#define OP_DECLARE 3
+#define OP_ASM 4
+#define OP_BREAK 5
+#define OP_CONTINUE 6
+#define OP_DISCARD 7
+#define OP_RETURN 8
+#define OP_EXPRESSION 9
+#define OP_IF 10
+#define OP_WHILE 11
+#define OP_DO 12
+#define OP_FOR 13
+#define OP_PUSH_VOID 14
+#define OP_PUSH_BOOL 15
+#define OP_PUSH_INT 16
+#define OP_PUSH_FLOAT 17
+#define OP_PUSH_IDENTIFIER 18
+#define OP_SEQUENCE 19
+#define OP_ASSIGN 20
+#define OP_ADDASSIGN 21
+#define OP_SUBASSIGN 22
+#define OP_MULASSIGN 23
+#define OP_DIVASSIGN 24
+/*#define OP_MODASSIGN 25*/
+/*#define OP_LSHASSIGN 26*/
+/*#define OP_RSHASSIGN 27*/
+/*#define OP_ORASSIGN 28*/
+/*#define OP_XORASSIGN 29*/
+/*#define OP_ANDASSIGN 30*/
+#define OP_SELECT 31
+#define OP_LOGICALOR 32
+#define OP_LOGICALXOR 33
+#define OP_LOGICALAND 34
+/*#define OP_BITOR 35*/
+/*#define OP_BITXOR 36*/
+/*#define OP_BITAND 37*/
+#define OP_EQUAL 38
+#define OP_NOTEQUAL 39
+#define OP_LESS 40
+#define OP_GREATER 41
+#define OP_LESSEQUAL 42
+#define OP_GREATEREQUAL 43
+/*#define OP_LSHIFT 44*/
+/*#define OP_RSHIFT 45*/
+#define OP_ADD 46
+#define OP_SUBTRACT 47
+#define OP_MULTIPLY 48
+#define OP_DIVIDE 49
+/*#define OP_MODULUS 50*/
+#define OP_PREINCREMENT 51
+#define OP_PREDECREMENT 52
+#define OP_PLUS 53
+#define OP_MINUS 54
+/*#define OP_COMPLEMENT 55*/
+#define OP_NOT 56
+#define OP_SUBSCRIPT 57
+#define OP_CALL 58
+#define OP_FIELD 59
+#define OP_POSTINCREMENT 60
+#define OP_POSTDECREMENT 61
+#define OP_PRECISION 62
+#define OP_METHOD 63
+
+/* parameter qualifier */
+#define PARAM_QUALIFIER_IN 0
+#define PARAM_QUALIFIER_OUT 1
+#define PARAM_QUALIFIER_INOUT 2
+
+/* function parameter */
+#define PARAMETER_NONE 0
+#define PARAMETER_NEXT 1
+
+/* function parameter array presence */
+#define PARAMETER_ARRAY_NOT_PRESENT 0
+#define PARAMETER_ARRAY_PRESENT 1
+
+
+struct parse_dict {
+ int _void;
+ int _float;
+ int _int;
+ int _bool;
+ int vec2;
+ int vec3;
+ int vec4;
+ int bvec2;
+ int bvec3;
+ int bvec4;
+ int ivec2;
+ int ivec3;
+ int ivec4;
+ int mat2;
+ int mat3;
+ int mat4;
+ int mat2x3;
+ int mat3x2;
+ int mat2x4;
+ int mat4x2;
+ int mat3x4;
+ int mat4x3;
+ int sampler1D;
+ int sampler2D;
+ int sampler3D;
+ int samplerCube;
+ int sampler1DShadow;
+ int sampler2DShadow;
+ int sampler2DRect;
+ int sampler2DRectShadow;
+
+ int invariant;
+
+ int centroid;
+
+ int precision;
+ int lowp;
+ int mediump;
+ int highp;
+
+ int _const;
+ int attribute;
+ int varying;
+ int uniform;
+ int __fixed_output;
+ int __fixed_input;
+
+ int in;
+ int out;
+ int inout;
+
+ int _struct;
+
+ int __constructor;
+ int __operator;
+ int ___asm;
+
+ int _if;
+ int _else;
+ int _for;
+ int _while;
+ int _do;
+
+ int _continue;
+ int _break;
+ int _return;
+ int discard;
+
+ int _false;
+ int _true;
+};
+
+
+struct parse_context {
+ struct sl_pp_context *context;
+ const struct sl_pp_token_info *input;
+
+ struct parse_dict dict;
+
+ unsigned char *out_buf;
+ unsigned int out_cap;
+
+ unsigned int shader_type;
+ unsigned int parsing_builtin;
+
+ char error[256];
+};
+
+
+struct parse_state {
+ unsigned int in;
+ unsigned int out;
+};
+
+
+static __inline unsigned int
+_emit(struct parse_context *ctx,
+ unsigned int *out,
+ unsigned char b)
+{
+ if (*out == ctx->out_cap) {
+ ctx->out_cap += 4096;
+ ctx->out_buf = (unsigned char *)realloc(ctx->out_buf, ctx->out_cap * sizeof(unsigned char));
+ }
+ ctx->out_buf[*out] = b;
+ return (*out)++;
+}
+
+
+static void
+_update(struct parse_context *ctx,
+ unsigned int out,
+ unsigned char b)
+{
+ ctx->out_buf[out] = b;
+}
+
+
+static void
+_error(struct parse_context *ctx,
+ char *msg)
+{
+ if (ctx->error[0] == '\0') {
+ strcpy(ctx->error, msg);
+ }
+}
+
+
+static int
+_parse_token(struct parse_context *ctx,
+ enum sl_pp_token token,
+ struct parse_state *ps)
+{
+ if (ctx->input[ps->in].token == token) {
+ ps->in++;
+ return 0;
+ }
+ return -1;
+}
+
+
+static int
+_parse_id(struct parse_context *ctx,
+ int id,
+ struct parse_state *ps)
+{
+ if (ctx->input[ps->in].token == SL_PP_IDENTIFIER &&
+ ctx->input[ps->in].data.identifier == id) {
+ ps->in++;
+ return 0;
+ }
+ return -1;
+}
+
+
+static int
+_parse_identifier(struct parse_context *ctx,
+ struct parse_state *ps)
+{
+ if (ctx->input[ps->in].token == SL_PP_IDENTIFIER) {
+ const char *cstr = sl_pp_context_cstr(ctx->context, ctx->input[ps->in].data.identifier);
+
+ do {
+ _emit(ctx, &ps->out, *cstr);
+ } while (*cstr++);
+ ps->in++;
+ return 0;
+ }
+ return -1;
+}
+
+
+static int
+_parse_float(struct parse_context *ctx,
+ struct parse_state *ps)
+{
+ if (ctx->input[ps->in].token == SL_PP_FLOAT) {
+ const char *cstr = sl_pp_context_cstr(ctx->context, ctx->input[ps->in].data._float);
+
+ _emit(ctx, &ps->out, 1);
+ do {
+ _emit(ctx, &ps->out, *cstr);
+ } while (*cstr++);
+ ps->in++;
+ return 0;
+ }
+ return -1;
+}
+
+
+static int
+_parse_uint(struct parse_context *ctx,
+ struct parse_state *ps)
+{
+ if (ctx->input[ps->in].token == SL_PP_UINT) {
+ const char *cstr = sl_pp_context_cstr(ctx->context, ctx->input[ps->in].data._uint);
+
+ _emit(ctx, &ps->out, 1);
+ do {
+ _emit(ctx, &ps->out, *cstr);
+ } while (*cstr++);
+ ps->in++;
+ return 0;
+ }
+ return -1;
+}
+
+
+/**************************************/
+
+
+static int
+_parse_unary_expression(struct parse_context *ctx,
+ struct parse_state *ps);
+
+static int
+_parse_conditional_expression(struct parse_context *ctx,
+ struct parse_state *ps);
+
+
+static int
+_parse_constant_expression(struct parse_context *ctx,
+ struct parse_state *ps);
+
+
+static int
+_parse_primary_expression(struct parse_context *ctx,
+ struct parse_state *ps);
+
+
+static int
+_parse_statement(struct parse_context *ctx,
+ struct parse_state *ps);
+
+
+static int
+_parse_type_specifier(struct parse_context *ctx,
+ struct parse_state *ps);
+
+
+static int
+_parse_declaration(struct parse_context *ctx,
+ struct parse_state *ps);
+
+
+static int
+_parse_statement_list(struct parse_context *ctx,
+ struct parse_state *ps);
+
+
+static int
+_parse_assignment_expression(struct parse_context *ctx,
+ struct parse_state *ps);
+
+
+static int
+_parse_precision(struct parse_context *ctx,
+ struct parse_state *ps);
+
+
+static int
+_parse_overriden_operator(struct parse_context *ctx,
+ struct parse_state *ps)
+{
+ unsigned int op;
+
+ if (_parse_token(ctx, SL_PP_INCREMENT, ps) == 0) {
+ op = OPERATOR_INCREMENT;
+ } else if (_parse_token(ctx, SL_PP_ADDASSIGN, ps) == 0) {
+ op = OPERATOR_ADDASSIGN;
+ } else if (_parse_token(ctx, SL_PP_PLUS, ps) == 0) {
+ op = OPERATOR_PLUS;
+ } else if (_parse_token(ctx, SL_PP_DECREMENT, ps) == 0) {
+ op = OPERATOR_DECREMENT;
+ } else if (_parse_token(ctx, SL_PP_SUBASSIGN, ps) == 0) {
+ op = OPERATOR_SUBASSIGN;
+ } else if (_parse_token(ctx, SL_PP_MINUS, ps) == 0) {
+ op = OPERATOR_MINUS;
+ } else if (_parse_token(ctx, SL_PP_NOT, ps) == 0) {
+ op = OPERATOR_NOT;
+ } else if (_parse_token(ctx, SL_PP_MULASSIGN, ps) == 0) {
+ op = OPERATOR_MULASSIGN;
+ } else if (_parse_token(ctx, SL_PP_STAR, ps) == 0) {
+ op = OPERATOR_MULTIPLY;
+ } else if (_parse_token(ctx, SL_PP_DIVASSIGN, ps) == 0) {
+ op = OPERATOR_DIVASSIGN;
+ } else if (_parse_token(ctx, SL_PP_SLASH, ps) == 0) {
+ op = OPERATOR_DIVIDE;
+ } else if (_parse_token(ctx, SL_PP_LESSEQUAL, ps) == 0) {
+ op = OPERATOR_LESSEQUAL;
+ } else if (_parse_token(ctx, SL_PP_LESS, ps) == 0) {
+ op = OPERATOR_LESS;
+ } else if (_parse_token(ctx, SL_PP_GREATEREQUAL, ps) == 0) {
+ op = OPERATOR_GREATEREQUAL;
+ } else if (_parse_token(ctx, SL_PP_GREATER, ps) == 0) {
+ op = OPERATOR_GREATER;
+ } else if (_parse_token(ctx, SL_PP_XOR, ps) == 0) {
+ op = OPERATOR_LOGICALXOR;
+ } else {
+ return -1;
+ }
+
+ _emit(ctx, &ps->out, op);
+ return 0;
+}
+
+
+static int
+_parse_function_decl_identifier(struct parse_context *ctx,
+ struct parse_state *ps)
+{
+ struct parse_state p = *ps;
+ unsigned int e = _emit(ctx, &p.out, 0);
+
+ if (ctx->parsing_builtin && _parse_id(ctx, ctx->dict.__constructor, &p) == 0) {
+ _update(ctx, e, FUNCTION_CONSTRUCTOR);
+ *ps = p;
+ return 0;
+ }
+
+ if (ctx->parsing_builtin && _parse_id(ctx, ctx->dict.__operator, &p) == 0) {
+ _update(ctx, e, FUNCTION_OPERATOR);
+ if (_parse_overriden_operator(ctx, &p) == 0) {
+ *ps = p;
+ return 0;
+ }
+ return -1;
+ }
+
+ if (_parse_identifier(ctx, &p) == 0) {
+ _update(ctx, e, FUNCTION_ORDINARY);
+ *ps = p;
+ return 0;
+ }
+
+ return -1;
+}
+
+
+static int
+_parse_invariant_qualifier(struct parse_context *ctx,
+ struct parse_state *ps)
+{
+ if (_parse_id(ctx, ctx->dict.invariant, ps)) {
+ return -1;
+ }
+ _emit(ctx, &ps->out, TYPE_INVARIANT);
+ return 0;
+}
+
+
+static int
+_parse_centroid_qualifier(struct parse_context *ctx,
+ struct parse_state *ps)
+{
+ if (_parse_id(ctx, ctx->dict.centroid, ps)) {
+ return -1;
+ }
+ _emit(ctx, &ps->out, TYPE_CENTROID);
+ return 0;
+}
+
+
+static int
+_parse_type_qualifier(struct parse_context *ctx,
+ struct parse_state *ps)
+{
+ struct parse_state p = *ps;
+ unsigned int e = _emit(ctx, &p.out, 0);
+ int id;
+
+ if (ctx->input[p.in].token != SL_PP_IDENTIFIER) {
+ return -1;
+ }
+ id = ctx->input[p.in].data.identifier;
+
+ if (id == ctx->dict._const) {
+ _update(ctx, e, TYPE_QUALIFIER_CONST);
+ } else if (ctx->shader_type == 2 && id == ctx->dict.attribute) {
+ _update(ctx, e, TYPE_QUALIFIER_ATTRIBUTE);
+ } else if (id == ctx->dict.varying) {
+ _update(ctx, e, TYPE_QUALIFIER_VARYING);
+ } else if (id == ctx->dict.uniform) {
+ _update(ctx, e, TYPE_QUALIFIER_UNIFORM);
+ } else if (ctx->parsing_builtin && id == ctx->dict.__fixed_output) {
+ _update(ctx, e, TYPE_QUALIFIER_FIXEDOUTPUT);
+ } else if (ctx->parsing_builtin && id == ctx->dict.__fixed_input) {
+ _update(ctx, e, TYPE_QUALIFIER_FIXEDINPUT);
+ } else {
+ return -1;
+ }
+ _parse_token(ctx, SL_PP_IDENTIFIER, &p);
+ *ps = p;
+ return 0;
+}
+
+
+static int
+_parse_struct_declarator(struct parse_context *ctx,
+ struct parse_state *ps)
+{
+ struct parse_state p = *ps;
+ unsigned int e;
+
+ if (_parse_identifier(ctx, &p)) {
+ return -1;
+ }
+ e = _emit(ctx, &p.out, FIELD_NONE);
+ *ps = p;
+
+ if (_parse_token(ctx, SL_PP_LBRACKET, &p)) {
+ return 0;
+ }
+ if (_parse_constant_expression(ctx, &p)) {
+ _error(ctx, "expected constant integral expression");
+ return -1;
+ }
+ if (_parse_token(ctx, SL_PP_RBRACKET, &p)) {
+ _error(ctx, "expected `]'");
+ return -1;
+ }
+ _update(ctx, e, FIELD_ARRAY);
+ *ps = p;
+ return 0;
+}
+
+
+static int
+_parse_struct_declarator_list(struct parse_context *ctx,
+ struct parse_state *ps)
+{
+ struct parse_state p = *ps;
+
+ if (_parse_struct_declarator(ctx, &p)) {
+ return -1;
+ }
+
+ for (;;) {
+ *ps = p;
+ _emit(ctx, &p.out, FIELD_NEXT);
+ if (_parse_token(ctx, SL_PP_COMMA, &p)) {
+ return 0;
+ }
+ if (_parse_struct_declarator(ctx, &p)) {
+ return 0;
+ }
+ }
+}
+
+
+static int
+_parse_struct_declaration(struct parse_context *ctx,
+ struct parse_state *ps)
+{
+ struct parse_state p = *ps;
+
+ if (_parse_type_specifier(ctx, &p)) {
+ return -1;
+ }
+ if (_parse_struct_declarator_list(ctx, &p)) {
+ return -1;
+ }
+ if (_parse_token(ctx, SL_PP_SEMICOLON, &p)) {
+ return -1;
+ }
+ _emit(ctx, &p.out, FIELD_NONE);
+ *ps = p;
+ return 0;
+}
+
+
+static int
+_parse_struct_declaration_list(struct parse_context *ctx,
+ struct parse_state *ps)
+{
+ struct parse_state p = *ps;
+
+ if (_parse_struct_declaration(ctx, &p)) {
+ return -1;
+ }
+
+ for (;;) {
+ *ps = p;
+ _emit(ctx, &p.out, FIELD_NEXT);
+ if (_parse_struct_declaration(ctx, &p)) {
+ return 0;
+ }
+ }
+}
+
+
+static int
+_parse_struct_specifier(struct parse_context *ctx,
+ struct parse_state *ps)
+{
+ struct parse_state p = *ps;
+
+ if (_parse_id(ctx, ctx->dict._struct, &p)) {
+ return -1;
+ }
+ if (_parse_identifier(ctx, &p)) {
+ _emit(ctx, &p.out, '\0');
+ }
+ if (_parse_token(ctx, SL_PP_LBRACE, &p)) {
+ _error(ctx, "expected `{'");
+ return -1;
+ }
+ if (_parse_struct_declaration_list(ctx, &p)) {
+ return -1;
+ }
+ if (_parse_token(ctx, SL_PP_RBRACE, &p)) {
+ return -1;
+ }
+ _emit(ctx, &p.out, FIELD_NONE);
+ *ps = p;
+ return 0;
+}
+
+
+static int
+_parse_type_specifier_nonarray(struct parse_context *ctx,
+ struct parse_state *ps)
+{
+ struct parse_state p = *ps;
+ unsigned int e = _emit(ctx, &p.out, 0);
+ int id;
+
+ if (_parse_struct_specifier(ctx, &p) == 0) {
+ _update(ctx, e, TYPE_SPECIFIER_STRUCT);
+ *ps = p;
+ return 0;
+ }
+
+ if (ctx->input[p.in].token != SL_PP_IDENTIFIER) {
+ return -1;
+ }
+ id = ctx->input[p.in].data.identifier;
+
+ if (id == ctx->dict._void) {
+ _update(ctx, e, TYPE_SPECIFIER_VOID);
+ } else if (id == ctx->dict._float) {
+ _update(ctx, e, TYPE_SPECIFIER_FLOAT);
+ } else if (id == ctx->dict._int) {
+ _update(ctx, e, TYPE_SPECIFIER_INT);
+ } else if (id == ctx->dict._bool) {
+ _update(ctx, e, TYPE_SPECIFIER_BOOL);
+ } else if (id == ctx->dict.vec2) {
+ _update(ctx, e, TYPE_SPECIFIER_VEC2);
+ } else if (id == ctx->dict.vec3) {
+ _update(ctx, e, TYPE_SPECIFIER_VEC3);
+ } else if (id == ctx->dict.vec4) {
+ _update(ctx, e, TYPE_SPECIFIER_VEC4);
+ } else if (id == ctx->dict.bvec2) {
+ _update(ctx, e, TYPE_SPECIFIER_BVEC2);
+ } else if (id == ctx->dict.bvec3) {
+ _update(ctx, e, TYPE_SPECIFIER_BVEC3);
+ } else if (id == ctx->dict.bvec4) {
+ _update(ctx, e, TYPE_SPECIFIER_BVEC4);
+ } else if (id == ctx->dict.ivec2) {
+ _update(ctx, e, TYPE_SPECIFIER_IVEC2);
+ } else if (id == ctx->dict.ivec3) {
+ _update(ctx, e, TYPE_SPECIFIER_IVEC3);
+ } else if (id == ctx->dict.ivec4) {
+ _update(ctx, e, TYPE_SPECIFIER_IVEC4);
+ } else if (id == ctx->dict.mat2) {
+ _update(ctx, e, TYPE_SPECIFIER_MAT2);
+ } else if (id == ctx->dict.mat3) {
+ _update(ctx, e, TYPE_SPECIFIER_MAT3);
+ } else if (id == ctx->dict.mat4) {
+ _update(ctx, e, TYPE_SPECIFIER_MAT4);
+ } else if (id == ctx->dict.mat2x3) {
+ _update(ctx, e, TYPE_SPECIFIER_MAT23);
+ } else if (id == ctx->dict.mat3x2) {
+ _update(ctx, e, TYPE_SPECIFIER_MAT32);
+ } else if (id == ctx->dict.mat2x4) {
+ _update(ctx, e, TYPE_SPECIFIER_MAT24);
+ } else if (id == ctx->dict.mat4x2) {
+ _update(ctx, e, TYPE_SPECIFIER_MAT42);
+ } else if (id == ctx->dict.mat3x4) {
+ _update(ctx, e, TYPE_SPECIFIER_MAT34);
+ } else if (id == ctx->dict.mat4x3) {
+ _update(ctx, e, TYPE_SPECIFIER_MAT43);
+ } else if (id == ctx->dict.sampler1D) {
+ _update(ctx, e, TYPE_SPECIFIER_SAMPLER1D);
+ } else if (id == ctx->dict.sampler2D) {
+ _update(ctx, e, TYPE_SPECIFIER_SAMPLER2D);
+ } else if (id == ctx->dict.sampler3D) {
+ _update(ctx, e, TYPE_SPECIFIER_SAMPLER3D);
+ } else if (id == ctx->dict.samplerCube) {
+ _update(ctx, e, TYPE_SPECIFIER_SAMPLERCUBE);
+ } else if (id == ctx->dict.sampler1DShadow) {
+ _update(ctx, e, TYPE_SPECIFIER_SAMPLER1DSHADOW);
+ } else if (id == ctx->dict.sampler2DShadow) {
+ _update(ctx, e, TYPE_SPECIFIER_SAMPLER2DSHADOW);
+ } else if (id == ctx->dict.sampler2DRect) {
+ _update(ctx, e, TYPE_SPECIFIER_SAMPLER2DRECT);
+ } else if (id == ctx->dict.sampler2DRectShadow) {
+ _update(ctx, e, TYPE_SPECIFIER_SAMPLER2DRECTSHADOW);
+ } else if (_parse_identifier(ctx, &p) == 0) {
+ _update(ctx, e, TYPE_SPECIFIER_TYPENAME);
+ *ps = p;
+ return 0;
+ } else {
+ return -1;
+ }
+
+ _parse_token(ctx, SL_PP_IDENTIFIER, &p);
+ *ps = p;
+ return 0;
+}
+
+
+static int
+_parse_type_specifier_array(struct parse_context *ctx,
+ struct parse_state *ps)
+{
+ struct parse_state p = *ps;
+
+ if (_parse_token(ctx, SL_PP_LBRACKET, &p)) {
+ return -1;
+ }
+ if (_parse_constant_expression(ctx, &p)) {
+ _error(ctx, "expected constant integral expression");
+ return -1;
+ }
+ if (_parse_token(ctx, SL_PP_RBRACKET, &p)) {
+ _error(ctx, "expected `]'");
+ return -1;
+ }
+ *ps = p;
+ return 0;
+}
+
+
+static int
+_parse_type_specifier(struct parse_context *ctx,
+ struct parse_state *ps)
+{
+ struct parse_state p = *ps;
+ unsigned int e;
+
+ if (_parse_type_specifier_nonarray(ctx, &p)) {
+ return -1;
+ }
+
+ e = _emit(ctx, &p.out, TYPE_SPECIFIER_ARRAY);
+ if (_parse_type_specifier_array(ctx, &p)) {
+ _update(ctx, e, TYPE_SPECIFIER_NONARRAY);
+ }
+ *ps = p;
+ return 0;
+}
+
+
+static int
+_parse_fully_specified_type(struct parse_context *ctx,
+ struct parse_state *ps)
+{
+ struct parse_state p = *ps;
+
+ if (_parse_invariant_qualifier(ctx, &p)) {
+ _emit(ctx, &p.out, TYPE_VARIANT);
+ }
+ if (_parse_centroid_qualifier(ctx, &p)) {
+ _emit(ctx, &p.out, TYPE_CENTER);
+ }
+ if (_parse_type_qualifier(ctx, &p)) {
+ _emit(ctx, &p.out, TYPE_QUALIFIER_NONE);
+ }
+ if (_parse_precision(ctx, &p)) {
+ _emit(ctx, &p.out, PRECISION_DEFAULT);
+ }
+ if (_parse_type_specifier(ctx, &p)) {
+ return -1;
+ }
+ *ps = p;
+ return 0;
+}
+
+
+static int
+_parse_function_header(struct parse_context *ctx,
+ struct parse_state *ps)
+{
+ struct parse_state p = *ps;
+
+ if (_parse_fully_specified_type(ctx, &p)) {
+ return -1;
+ }
+ if (_parse_function_decl_identifier(ctx, &p)) {
+ return -1;
+ }
+ if (_parse_token(ctx, SL_PP_LPAREN, &p)) {
+ return -1;
+ }
+ *ps = p;
+ return 0;
+}
+
+
+static int
+_parse_parameter_qualifier(struct parse_context *ctx,
+ struct parse_state *ps)
+{
+ unsigned int e = _emit(ctx, &ps->out, PARAM_QUALIFIER_IN);
+
+ if (_parse_id(ctx, ctx->dict.out, ps) == 0) {
+ _update(ctx, e, PARAM_QUALIFIER_OUT);
+ } else if (_parse_id(ctx, ctx->dict.inout, ps) == 0) {
+ _update(ctx, e, PARAM_QUALIFIER_INOUT);
+ } else {
+ _parse_id(ctx, ctx->dict.in, ps);
+ }
+ return 0;
+}
+
+
+static int
+_parse_function_identifier(struct parse_context *ctx,
+ struct parse_state *ps)
+{
+ struct parse_state p;
+ unsigned int e;
+
+ if (_parse_identifier(ctx, ps)) {
+ return -1;
+ }
+ e = _emit(ctx, &ps->out, FUNCTION_CALL_NONARRAY);
+
+ p = *ps;
+ if (_parse_token(ctx, SL_PP_LBRACKET, &p)) {
+ return 0;
+ }
+ if (_parse_constant_expression(ctx, &p)) {
+ _error(ctx, "expected constant integral expression");
+ return -1;
+ }
+ if (_parse_token(ctx, SL_PP_RBRACKET, &p)) {
+ _error(ctx, "expected `]'");
+ return -1;
+ }
+ _update(ctx, e, FUNCTION_CALL_ARRAY);
+ *ps = p;
+ return 0;
+}
+
+
+static int
+_parse_function_call_header(struct parse_context *ctx,
+ struct parse_state *ps)
+{
+ struct parse_state p = *ps;
+
+ if (_parse_function_identifier(ctx, &p)) {
+ return -1;
+ }
+ if (_parse_token(ctx, SL_PP_LPAREN, &p)) {
+ return -1;
+ }
+ *ps = p;
+ return 0;
+}
+
+
+static int
+_parse_assign_expression(struct parse_context *ctx,
+ struct parse_state *ps)
+{
+ struct parse_state p = *ps;
+ unsigned int op;
+
+ if (_parse_unary_expression(ctx, &p)) {
+ return -1;
+ }
+
+ if (_parse_token(ctx, SL_PP_ASSIGN, &p) == 0) {
+ op = OP_ASSIGN;
+ } else if (_parse_token(ctx, SL_PP_MULASSIGN, &p) == 0) {
+ op = OP_MULASSIGN;
+ } else if (_parse_token(ctx, SL_PP_DIVASSIGN, &p) == 0) {
+ op = OP_DIVASSIGN;
+ } else if (_parse_token(ctx, SL_PP_ADDASSIGN, &p) == 0) {
+ op = OP_ADDASSIGN;
+ } else if (_parse_token(ctx, SL_PP_SUBASSIGN, &p) == 0) {
+ op = OP_SUBASSIGN;
+ } else {
+ return -1;
+ }
+
+ if (_parse_assignment_expression(ctx, &p)) {
+ return -1;
+ }
+ _emit(ctx, &p.out, op);
+
+ *ps = p;
+ return 0;
+}
+
+
+static int
+_parse_assignment_expression(struct parse_context *ctx,
+ struct parse_state *ps)
+{
+ if (_parse_assign_expression(ctx, ps) == 0) {
+ return 0;
+ }
+
+ if (_parse_conditional_expression(ctx, ps) == 0) {
+ return 0;
+ }
+
+ return -1;
+}
+
+
+static int
+_parse_function_call_header_with_parameters(struct parse_context *ctx,
+ struct parse_state *ps)
+{
+ struct parse_state p = *ps;
+
+ if (_parse_function_call_header(ctx, &p)) {
+ return -1;
+ }
+ if (_parse_assignment_expression(ctx, &p)) {
+ return -1;
+ }
+ _emit(ctx, &p.out, OP_END);
+ for (;;) {
+ *ps = p;
+ if (_parse_token(ctx, SL_PP_COMMA, &p)) {
+ return 0;
+ }
+ if (_parse_assignment_expression(ctx, &p)) {
+ return 0;
+ }
+ _emit(ctx, &p.out, OP_END);
+ }
+}
+
+
+static int
+_parse_function_call_header_no_parameters(struct parse_context *ctx,
+ struct parse_state *ps)
+{
+ if (_parse_function_call_header(ctx, ps)) {
+ return -1;
+ }
+ _parse_id(ctx, ctx->dict._void, ps);
+ return 0;
+}
+
+
+static int
+_parse_function_call_generic(struct parse_context *ctx,
+ struct parse_state *ps)
+{
+ struct parse_state p = *ps;
+
+ if (_parse_function_call_header_with_parameters(ctx, &p) == 0) {
+ if (_parse_token(ctx, SL_PP_RPAREN, &p) == 0) {
+ *ps = p;
+ return 0;
+ }
+ _error(ctx, "expected `)'");
+ return -1;
+ }
+
+ p = *ps;
+ if (_parse_function_call_header_no_parameters(ctx, &p) == 0) {
+ if (_parse_token(ctx, SL_PP_RPAREN, &p) == 0) {
+ *ps = p;
+ return 0;
+ }
+ _error(ctx, "expected `)'");
+ return -1;
+ }
+
+ return -1;
+}
+
+
+static int
+_parse_method_call(struct parse_context *ctx,
+ struct parse_state *ps)
+{
+ struct parse_state p = *ps;
+
+ _emit(ctx, &p.out, OP_METHOD);
+ if (_parse_identifier(ctx, &p)) {
+ return -1;
+ }
+ if (_parse_token(ctx, SL_PP_DOT, &p)) {
+ return -1;
+ }
+ if (_parse_function_call_generic(ctx, &p)) {
+ return -1;
+ }
+ _emit(ctx, &p.out, OP_END);
+ *ps = p;
+ return 0;
+}
+
+
+static int
+_parse_regular_function_call(struct parse_context *ctx,
+ struct parse_state *ps)
+{
+ struct parse_state p = *ps;
+
+ _emit(ctx, &p.out, OP_CALL);
+ if (_parse_function_call_generic(ctx, &p)) {
+ return -1;
+ }
+ _emit(ctx, &p.out, OP_END);
+ *ps = p;
+ return 0;
+}
+
+
+static int
+_parse_function_call(struct parse_context *ctx,
+ struct parse_state *ps)
+{
+ if (_parse_regular_function_call(ctx, ps) == 0) {
+ return 0;
+ }
+
+ if (_parse_method_call(ctx, ps) == 0) {
+ return 0;
+ }
+
+ return -1;
+}
+
+
+static int
+_parse_expression(struct parse_context *ctx,
+ struct parse_state *ps)
+{
+ struct parse_state p = *ps;
+
+ if (_parse_assignment_expression(ctx, &p)) {
+ return -1;
+ }
+
+ for (;;) {
+ *ps = p;
+ if (_parse_token(ctx, SL_PP_COMMA, &p)) {
+ return 0;
+ }
+ if (_parse_assignment_expression(ctx, &p)) {
+ return 0;
+ }
+ _emit(ctx, &p.out, OP_SEQUENCE);
+ }
+}
+
+
+static int
+_parse_postfix_expression(struct parse_context *ctx,
+ struct parse_state *ps)
+{
+ struct parse_state p;
+
+ if (_parse_function_call(ctx, ps)) {
+ if (_parse_primary_expression(ctx, ps)) {
+ return -1;
+ }
+ }
+
+ for (p = *ps;;) {
+ *ps = p;
+ if (_parse_token(ctx, SL_PP_INCREMENT, &p) == 0) {
+ _emit(ctx, &p.out, OP_POSTINCREMENT);
+ } else if (_parse_token(ctx, SL_PP_DECREMENT, &p) == 0) {
+ _emit(ctx, &p.out, OP_POSTDECREMENT);
+ } else if (_parse_token(ctx, SL_PP_LBRACKET, &p) == 0) {
+ if (_parse_expression(ctx, &p)) {
+ _error(ctx, "expected an integral expression");
+ return -1;
+ }
+ if (_parse_token(ctx, SL_PP_RBRACKET, &p)) {
+ _error(ctx, "expected `]'");
+ return -1;
+ }
+ _emit(ctx, &p.out, OP_SUBSCRIPT);
+ } else if (_parse_token(ctx, SL_PP_DOT, &p) == 0) {
+ _emit(ctx, &p.out, OP_FIELD);
+ if (_parse_identifier(ctx, &p)) {
+ return 0;
+ }
+ } else {
+ return 0;
+ }
+ }
+}
+
+
+static int
+_parse_unary_expression(struct parse_context *ctx,
+ struct parse_state *ps)
+{
+ struct parse_state p;
+ unsigned int op;
+
+ if (_parse_postfix_expression(ctx, ps) == 0) {
+ return 0;
+ }
+
+ p = *ps;
+ if (_parse_token(ctx, SL_PP_INCREMENT, &p) == 0) {
+ op = OP_PREINCREMENT;
+ } else if (_parse_token(ctx, SL_PP_DECREMENT, &p) == 0) {
+ op = OP_PREDECREMENT;
+ } else if (_parse_token(ctx, SL_PP_PLUS, &p) == 0) {
+ op = OP_PLUS;
+ } else if (_parse_token(ctx, SL_PP_MINUS, &p) == 0) {
+ op = OP_MINUS;
+ } else if (_parse_token(ctx, SL_PP_NOT, &p) == 0) {
+ op = OP_NOT;
+ } else {
+ return -1;
+ }
+
+ if (_parse_unary_expression(ctx, &p)) {
+ return -1;
+ }
+ _emit(ctx, &p.out, op);
+ *ps = p;
+ return 0;
+}
+
+
+static int
+_parse_multiplicative_expression(struct parse_context *ctx,
+ struct parse_state *ps)
+{
+ struct parse_state p = *ps;
+
+ if (_parse_unary_expression(ctx, &p)) {
+ return -1;
+ }
+ for (;;) {
+ unsigned int op;
+
+ *ps = p;
+ if (_parse_token(ctx, SL_PP_STAR, &p) == 0) {
+ op = OP_MULTIPLY;
+ } else if (_parse_token(ctx, SL_PP_SLASH, &p) == 0) {
+ op = OP_DIVIDE;
+ } else {
+ return 0;
+ }
+ if (_parse_unary_expression(ctx, &p)) {
+ return 0;
+ }
+ _emit(ctx, &p.out, op);
+ }
+}
+
+
+static int
+_parse_additive_expression(struct parse_context *ctx,
+ struct parse_state *ps)
+{
+ struct parse_state p = *ps;
+
+ if (_parse_multiplicative_expression(ctx, &p)) {
+ return -1;
+ }
+ for (;;) {
+ unsigned int op;
+
+ *ps = p;
+ if (_parse_token(ctx, SL_PP_PLUS, &p) == 0) {
+ op = OP_ADD;
+ } else if (_parse_token(ctx, SL_PP_MINUS, &p) == 0) {
+ op = OP_SUBTRACT;
+ } else {
+ return 0;
+ }
+ if (_parse_multiplicative_expression(ctx, &p)) {
+ return 0;
+ }
+ _emit(ctx, &p.out, op);
+ }
+}
+
+
+static int
+_parse_relational_expression(struct parse_context *ctx,
+ struct parse_state *ps)
+{
+ struct parse_state p = *ps;
+
+ if (_parse_additive_expression(ctx, &p)) {
+ return -1;
+ }
+ for (;;) {
+ unsigned int op;
+
+ *ps = p;
+ if (_parse_token(ctx, SL_PP_LESS, &p) == 0) {
+ op = OP_LESS;
+ } else if (_parse_token(ctx, SL_PP_GREATER, &p) == 0) {
+ op = OP_GREATER;
+ } else if (_parse_token(ctx, SL_PP_LESSEQUAL, &p) == 0) {
+ op = OP_LESSEQUAL;
+ } else if (_parse_token(ctx, SL_PP_GREATEREQUAL, &p) == 0) {
+ op = OP_GREATEREQUAL;
+ } else {
+ return 0;
+ }
+ if (_parse_additive_expression(ctx, &p)) {
+ return 0;
+ }
+ _emit(ctx, &p.out, op);
+ }
+}
+
+
+static int
+_parse_equality_expression(struct parse_context *ctx,
+ struct parse_state *ps)
+{
+ struct parse_state p = *ps;
+
+ if (_parse_relational_expression(ctx, &p)) {
+ return -1;
+ }
+ for (;;) {
+ unsigned int op;
+
+ *ps = p;
+ if (_parse_token(ctx, SL_PP_EQUAL, &p) == 0) {
+ op = OP_EQUAL;
+ } else if (_parse_token(ctx, SL_PP_NOTEQUAL, &p) == 0) {
+ op = OP_NOTEQUAL;
+ } else {
+ return 0;
+ }
+ if (_parse_relational_expression(ctx, &p)) {
+ return -1;
+ }
+ _emit(ctx, &p.out, op);
+ }
+}
+
+
+static int
+_parse_logical_and_expression(struct parse_context *ctx,
+ struct parse_state *ps)
+{
+ struct parse_state p = *ps;
+
+ if (_parse_equality_expression(ctx, &p)) {
+ return -1;
+ }
+ for (;;) {
+ *ps = p;
+ if (_parse_token(ctx, SL_PP_AND, &p)) {
+ return 0;
+ }
+ if (_parse_equality_expression(ctx, &p)) {
+ return 0;
+ }
+ _emit(ctx, &p.out, OP_LOGICALAND);
+ }
+}
+
+
+static int
+_parse_logical_xor_expression(struct parse_context *ctx,
+ struct parse_state *ps)
+{
+ struct parse_state p = *ps;
+
+ if (_parse_logical_and_expression(ctx, &p)) {
+ return -1;
+ }
+ for (;;) {
+ *ps = p;
+ if (_parse_token(ctx, SL_PP_XOR, &p)) {
+ return 0;
+ }
+ if (_parse_logical_and_expression(ctx, &p)) {
+ return 0;
+ }
+ _emit(ctx, &p.out, OP_LOGICALXOR);
+ }
+}
+
+
+static int
+_parse_logical_or_expression(struct parse_context *ctx,
+ struct parse_state *ps)
+{
+ struct parse_state p = *ps;
+
+ if (_parse_logical_xor_expression(ctx, &p)) {
+ return -1;
+ }
+ for (;;) {
+ *ps = p;
+ if (_parse_token(ctx, SL_PP_OR, &p)) {
+ return 0;
+ }
+ if (_parse_logical_xor_expression(ctx, &p)) {
+ return 0;
+ }
+ _emit(ctx, &p.out, OP_LOGICALOR);
+ }
+}
+
+
+static int
+_parse_conditional_expression(struct parse_context *ctx,
+ struct parse_state *ps)
+{
+ struct parse_state p = *ps;
+
+ if (_parse_logical_or_expression(ctx, &p)) {
+ return -1;
+ }
+ for (;;) {
+ *ps = p;
+ if (_parse_token(ctx, SL_PP_QUESTION, &p)) {
+ return 0;
+ }
+ if (_parse_expression(ctx, &p)) {
+ return 0;
+ }
+ if (_parse_token(ctx, SL_PP_COLON, &p)) {
+ return 0;
+ }
+ if (_parse_conditional_expression(ctx, &p)) {
+ return 0;
+ }
+ _emit(ctx, &p.out, OP_SELECT);
+ }
+}
+
+
+static int
+_parse_constant_expression(struct parse_context *ctx,
+ struct parse_state *ps)
+{
+ if (_parse_conditional_expression(ctx, ps)) {
+ return -1;
+ }
+ _emit(ctx, &ps->out, OP_END);
+ return 0;
+}
+
+
+static int
+_parse_parameter_declarator_array(struct parse_context *ctx,
+ struct parse_state *ps)
+{
+ struct parse_state p = *ps;
+
+ if (_parse_token(ctx, SL_PP_LBRACKET, &p)) {
+ return -1;
+ }
+ if (_parse_constant_expression(ctx, &p)) {
+ _error(ctx, "expected constant integral expression");
+ return -1;
+ }
+ if (_parse_token(ctx, SL_PP_RBRACKET, &p)) {
+ _error(ctx, "expected `]'");
+ return -1;
+ }
+ *ps = p;
+ return 0;
+}
+
+
+static int
+_parse_parameter_declarator(struct parse_context *ctx,
+ struct parse_state *ps)
+{
+ struct parse_state p = *ps;
+ unsigned int e;
+
+ if (_parse_type_specifier(ctx, &p)) {
+ return -1;
+ }
+ if (_parse_identifier(ctx, &p)) {
+ return -1;
+ }
+ e = _emit(ctx, &p.out, PARAMETER_ARRAY_PRESENT);
+ if (_parse_parameter_declarator_array(ctx, &p)) {
+ _update(ctx, e, PARAMETER_ARRAY_NOT_PRESENT);
+ }
+ *ps = p;
+ return 0;
+}
+
+
+static int
+_parse_parameter_type_specifier_array(struct parse_context *ctx,
+ struct parse_state *ps)
+{
+ struct parse_state p = *ps;
+
+ if (_parse_token(ctx, SL_PP_LBRACKET, &p)) {
+ return -1;
+ }
+ if (_parse_constant_expression(ctx, &p)) {
+ _error(ctx, "expected constant integral expression");
+ return -1;
+ }
+ if (_parse_token(ctx, SL_PP_RBRACKET, &p)) {
+ _error(ctx, "expected `]'");
+ return -1;
+ }
+ *ps = p;
+ return 0;
+}
+
+
+static int
+_parse_parameter_type_specifier(struct parse_context *ctx,
+ struct parse_state *ps)
+{
+ struct parse_state p = *ps;
+ unsigned int e;
+
+ if (_parse_type_specifier(ctx, &p)) {
+ return -1;
+ }
+ _emit(ctx, &p.out, '\0');
+
+ e = _emit(ctx, &p.out, PARAMETER_ARRAY_PRESENT);
+ if (_parse_parameter_type_specifier_array(ctx, &p)) {
+ _update(ctx, e, PARAMETER_ARRAY_NOT_PRESENT);
+ }
+ *ps = p;
+ return 0;
+}
+
+
+static int
+_parse_parameter_declaration(struct parse_context *ctx,
+ struct parse_state *ps)
+{
+ struct parse_state p = *ps;
+ unsigned int e = _emit(ctx, &p.out, PARAMETER_NEXT);
+
+ (void) e;
+
+ if (_parse_type_qualifier(ctx, &p)) {
+ _emit(ctx, &p.out, TYPE_QUALIFIER_NONE);
+ }
+ _parse_parameter_qualifier(ctx, &p);
+ if (_parse_precision(ctx, &p)) {
+ _emit(ctx, &p.out, PRECISION_DEFAULT);
+ }
+ if (_parse_parameter_declarator(ctx, &p) == 0) {
+ *ps = p;
+ return 0;
+ }
+ if (_parse_parameter_type_specifier(ctx, &p) == 0) {
+ *ps = p;
+ return 0;
+ }
+
+ return -1;
+}
+
+
+static int
+_parse_function_header_with_parameters(struct parse_context *ctx,
+ struct parse_state *ps)
+{
+ struct parse_state p = *ps;
+
+ if (_parse_function_header(ctx, &p)) {
+ return -1;
+ }
+ if (_parse_parameter_declaration(ctx, &p)) {
+ return -1;
+ }
+
+ for (;;) {
+ *ps = p;
+ if (_parse_token(ctx, SL_PP_COMMA, &p)) {
+ return 0;
+ }
+ if (_parse_parameter_declaration(ctx, &p)) {
+ return 0;
+ }
+ }
+}
+
+
+static int
+_parse_function_declarator(struct parse_context *ctx,
+ struct parse_state *ps)
+{
+ if (_parse_function_header_with_parameters(ctx, ps) == 0) {
+ return 0;
+ }
+
+ if (_parse_function_header(ctx, ps) == 0) {
+ return 0;
+ }
+
+ return -1;
+}
+
+
+static int
+_parse_function_prototype(struct parse_context *ctx,
+ struct parse_state *ps)
+{
+ struct parse_state p = *ps;
+
+ if (_parse_function_header(ctx, &p) == 0) {
+ if (_parse_id(ctx, ctx->dict._void, &p) == 0) {
+ if (_parse_token(ctx, SL_PP_RPAREN, &p) == 0) {
+ _emit(ctx, &p.out, PARAMETER_NONE);
+ *ps = p;
+ return 0;
+ }
+ _error(ctx, "expected `)'");
+ return -1;
+ }
+ }
+
+ p = *ps;
+ if (_parse_function_declarator(ctx, &p) == 0) {
+ if (_parse_token(ctx, SL_PP_RPAREN, &p) == 0) {
+ _emit(ctx, &p.out, PARAMETER_NONE);
+ *ps = p;
+ return 0;
+ }
+ _error(ctx, "expected `)'");
+ return -1;
+ }
+
+ return -1;
+}
+
+
+static int
+_parse_precision(struct parse_context *ctx,
+ struct parse_state *ps)
+{
+ int id;
+ unsigned int precision;
+
+ if (ctx->input[ps->in].token != SL_PP_IDENTIFIER) {
+ return -1;
+ }
+ id = ctx->input[ps->in].data.identifier;
+
+ if (id == ctx->dict.lowp) {
+ precision = PRECISION_LOW;
+ } else if (id == ctx->dict.mediump) {
+ precision = PRECISION_MEDIUM;
+ } else if (id == ctx->dict.highp) {
+ precision = PRECISION_HIGH;
+ } else {
+ return -1;
+ }
+
+ _parse_token(ctx, SL_PP_IDENTIFIER, ps);
+ _emit(ctx, &ps->out, precision);
+ return 0;
+}
+
+
+static int
+_parse_prectype(struct parse_context *ctx,
+ struct parse_state *ps)
+{
+ int id;
+ unsigned int type;
+
+ if (ctx->input[ps->in].token != SL_PP_IDENTIFIER) {
+ return -1;
+ }
+ id = ctx->input[ps->in].data.identifier;
+
+ if (id == ctx->dict._int) {
+ type = TYPE_SPECIFIER_INT;
+ } else if (id == ctx->dict._float) {
+ type = TYPE_SPECIFIER_FLOAT;
+ } else if (id == ctx->dict.sampler1D) {
+ type = TYPE_SPECIFIER_SAMPLER1D;
+ } else if (id == ctx->dict.sampler2D) {
+ type = TYPE_SPECIFIER_SAMPLER2D;
+ } else if (id == ctx->dict.sampler3D) {
+ type = TYPE_SPECIFIER_SAMPLER3D;
+ } else if (id == ctx->dict.samplerCube) {
+ type = TYPE_SPECIFIER_SAMPLERCUBE;
+ } else if (id == ctx->dict.sampler1DShadow) {
+ type = TYPE_SPECIFIER_SAMPLER1DSHADOW;
+ } else if (id == ctx->dict.sampler2DShadow) {
+ type = TYPE_SPECIFIER_SAMPLER2DSHADOW;
+ } else if (id == ctx->dict.sampler2DRect) {
+ type = TYPE_SPECIFIER_SAMPLER2DRECT;
+ } else if (id == ctx->dict.sampler2DRectShadow) {
+ type = TYPE_SPECIFIER_SAMPLER2DRECTSHADOW;
+ } else {
+ return -1;
+ }
+
+ _parse_token(ctx, SL_PP_IDENTIFIER, ps);
+ _emit(ctx, &ps->out, type);
+ return 0;
+}
+
+
+static int
+_parse_precision_stmt(struct parse_context *ctx,
+ struct parse_state *ps)
+{
+ struct parse_state p = *ps;
+
+ if (_parse_id(ctx, ctx->dict.precision, &p)) {
+ return -1;
+ }
+ if (_parse_precision(ctx, &p)) {
+ return -1;
+ }
+ if (_parse_prectype(ctx, &p)) {
+ return -1;
+ }
+ if (_parse_token(ctx, SL_PP_SEMICOLON, &p)) {
+ return -1;
+ }
+ *ps = p;
+ return 0;
+}
+
+
+static int
+_parse_floatconstant(struct parse_context *ctx,
+ struct parse_state *ps)
+{
+ struct parse_state p = *ps;
+
+ _emit(ctx, &p.out, OP_PUSH_FLOAT);
+ if (_parse_float(ctx, &p)) {
+ return -1;
+ }
+ *ps = p;
+ return 0;
+}
+
+
+static int
+_parse_intconstant(struct parse_context *ctx,
+ struct parse_state *ps)
+{
+ struct parse_state p = *ps;
+
+ _emit(ctx, &p.out, OP_PUSH_INT);
+ if (_parse_uint(ctx, &p)) {
+ return -1;
+ }
+ *ps = p;
+ return 0;
+}
+
+
+static int
+_parse_boolconstant(struct parse_context *ctx,
+ struct parse_state *ps)
+{
+ if (_parse_id(ctx, ctx->dict._false, ps) == 0) {
+ _emit(ctx, &ps->out, OP_PUSH_BOOL);
+ _emit(ctx, &ps->out, 2); /* radix */
+ _emit(ctx, &ps->out, '0');
+ _emit(ctx, &ps->out, '\0');
+ return 0;
+ }
+
+ if (_parse_id(ctx, ctx->dict._true, ps) == 0) {
+ _emit(ctx, &ps->out, OP_PUSH_BOOL);
+ _emit(ctx, &ps->out, 2); /* radix */
+ _emit(ctx, &ps->out, '1');
+ _emit(ctx, &ps->out, '\0');
+ return 0;
+ }
+
+ return -1;
+}
+
+
+static int
+_parse_variable_identifier(struct parse_context *ctx,
+ struct parse_state *ps)
+{
+ struct parse_state p = *ps;
+
+ _emit(ctx, &p.out, OP_PUSH_IDENTIFIER);
+ if (_parse_identifier(ctx, &p)) {
+ return -1;
+ }
+ *ps = p;
+ return 0;
+}
+
+
+static int
+_parse_primary_expression(struct parse_context *ctx,
+ struct parse_state *ps)
+{
+ struct parse_state p;
+
+ if (_parse_floatconstant(ctx, ps) == 0) {
+ return 0;
+ }
+ if (_parse_boolconstant(ctx, ps) == 0) {
+ return 0;
+ }
+ if (_parse_intconstant(ctx, ps) == 0) {
+ return 0;
+ }
+ if (_parse_variable_identifier(ctx, ps) == 0) {
+ return 0;
+ }
+
+ p = *ps;
+ if (_parse_token(ctx, SL_PP_LPAREN, &p)) {
+ return -1;
+ }
+ if (_parse_expression(ctx, &p)) {
+ return -1;
+ }
+ if (_parse_token(ctx, SL_PP_RPAREN, &p)) {
+ return -1;
+ }
+
+ *ps = p;
+ return 0;
+}
+
+
+static int
+_parse_asm_argument(struct parse_context *ctx,
+ struct parse_state *ps)
+{
+ if (_parse_variable_identifier(ctx, ps) == 0) {
+ struct parse_state p = *ps;
+
+ if (_parse_token(ctx, SL_PP_DOT, &p)) {
+ return 0;
+ }
+ _emit(ctx, &p.out, OP_FIELD);
+ if (_parse_identifier(ctx, &p)) {
+ return 0;
+ }
+ *ps = p;
+ return 0;
+ }
+
+ if (_parse_floatconstant(ctx, ps) == 0) {
+ return 0;
+ }
+
+ return -1;
+}
+
+
+static int
+_parse_asm_arguments(struct parse_context *ctx,
+ struct parse_state *ps)
+{
+ struct parse_state p = *ps;
+
+ if (_parse_asm_argument(ctx, &p)) {
+ return -1;
+ }
+ _emit(ctx, &p.out, OP_END);
+
+ for (;;) {
+ *ps = p;
+ if (_parse_token(ctx, SL_PP_COMMA, &p)) {
+ return 0;
+ }
+ if (_parse_asm_argument(ctx, &p)) {
+ return 0;
+ }
+ _emit(ctx, &p.out, OP_END);
+ }
+}
+
+
+static int
+_parse_asm_statement(struct parse_context *ctx,
+ struct parse_state *ps)
+{
+ struct parse_state p = *ps;
+
+ if (_parse_id(ctx, ctx->dict.___asm, &p)) {
+ return -1;
+ }
+ if (_parse_identifier(ctx, &p)) {
+ return -1;
+ }
+ if (_parse_asm_arguments(ctx, &p)) {
+ return -1;
+ }
+ if (_parse_token(ctx, SL_PP_SEMICOLON, &p)) {
+ return -1;
+ }
+ _emit(ctx, &p.out, OP_END);
+ *ps = p;
+ return 0;
+}
+
+
+static int
+_parse_selection_statement(struct parse_context *ctx,
+ struct parse_state *ps)
+{
+ struct parse_state p = *ps;
+
+ _emit(ctx, &p.out, OP_IF);
+ if (_parse_id(ctx, ctx->dict._if, &p)) {
+ return -1;
+ }
+ if (_parse_token(ctx, SL_PP_LPAREN, &p)) {
+ _error(ctx, "expected `('");
+ return -1;
+ }
+ if (_parse_expression(ctx, &p)) {
+ _error(ctx, "expected an expression");
+ return -1;
+ }
+ if (_parse_token(ctx, SL_PP_RPAREN, &p)) {
+ _error(ctx, "expected `)'");
+ return -1;
+ }
+ _emit(ctx, &p.out, OP_END);
+ if (_parse_statement(ctx, &p)) {
+ return -1;
+ }
+
+ *ps = p;
+ if (_parse_id(ctx, ctx->dict._else, &p) == 0) {
+ if (_parse_statement(ctx, &p) == 0) {
+ *ps = p;
+ return 0;
+ }
+ }
+
+ _emit(ctx, &ps->out, OP_EXPRESSION);
+ _emit(ctx, &ps->out, OP_PUSH_VOID);
+ _emit(ctx, &ps->out, OP_END);
+ return 0;
+}
+
+
+static int
+_parse_expression_statement(struct parse_context *ctx,
+ struct parse_state *ps)
+{
+ struct parse_state p = *ps;
+
+ if (_parse_expression(ctx, &p)) {
+ _emit(ctx, &p.out, OP_PUSH_VOID);
+ }
+ if (_parse_token(ctx, SL_PP_SEMICOLON, &p)) {
+ return -1;
+ }
+ _emit(ctx, &p.out, OP_END);
+ *ps = p;
+ return 0;
+}
+
+
+static int
+_parse_for_init_statement(struct parse_context *ctx,
+ struct parse_state *ps)
+{
+ struct parse_state p = *ps;
+ unsigned int e = _emit(ctx, &p.out, OP_EXPRESSION);
+
+ if (_parse_expression_statement(ctx, &p) == 0) {
+ *ps = p;
+ return 0;
+ }
+
+ if (_parse_declaration(ctx, &p) == 0) {
+ _update(ctx, e, OP_DECLARE);
+ *ps = p;
+ return 0;
+ }
+
+ return -1;
+}
+
+
+static int
+_parse_initializer(struct parse_context *ctx,
+ struct parse_state *ps)
+{
+ if (_parse_assignment_expression(ctx, ps) == 0) {
+ _emit(ctx, &ps->out, OP_END);
+ return 0;
+ }
+ return -1;
+}
+
+
+static int
+_parse_condition_initializer(struct parse_context *ctx,
+ struct parse_state *ps)
+{
+ struct parse_state p = *ps;
+
+ _emit(ctx, &p.out, OP_DECLARE);
+ _emit(ctx, &p.out, DECLARATION_INIT_DECLARATOR_LIST);
+ if (_parse_fully_specified_type(ctx, &p)) {
+ return -1;
+ }
+ _emit(ctx, &p.out, VARIABLE_IDENTIFIER);
+ if (_parse_identifier(ctx, &p)) {
+ return -1;
+ }
+ if (_parse_token(ctx, SL_PP_ASSIGN, &p)) {
+ _error(ctx, "expected `='");
+ return -1;
+ }
+ _emit(ctx, &p.out, VARIABLE_INITIALIZER);
+ if (_parse_initializer(ctx, &p)) {
+ _error(ctx, "expected an initialiser");
+ return -1;
+ }
+ _emit(ctx, &p.out, DECLARATOR_NONE);
+ *ps = p;
+ return 0;
+}
+
+
+static int
+_parse_condition(struct parse_context *ctx,
+ struct parse_state *ps)
+{
+ struct parse_state p;
+
+ if (_parse_condition_initializer(ctx, ps) == 0) {
+ return 0;
+ }
+
+ p = *ps;
+ _emit(ctx, &p.out, OP_EXPRESSION);
+ if (_parse_expression(ctx, &p) == 0) {
+ _emit(ctx, &p.out, OP_END);
+ *ps = p;
+ return 0;
+ }
+
+ return -1;
+}
+
+
+static int
+_parse_for_rest_statement(struct parse_context *ctx,
+ struct parse_state *ps)
+{
+ struct parse_state p = *ps;
+
+ if (_parse_condition(ctx, &p)) {
+ _emit(ctx, &p.out, OP_EXPRESSION);
+ _emit(ctx, &p.out, OP_PUSH_BOOL);
+ _emit(ctx, &p.out, 2);
+ _emit(ctx, &p.out, '1');
+ _emit(ctx, &p.out, '\0');
+ _emit(ctx, &p.out, OP_END);
+ }
+ if (_parse_token(ctx, SL_PP_SEMICOLON, &p)) {
+ return -1;
+ }
+ if (_parse_expression(ctx, &p)) {
+ _emit(ctx, &p.out, OP_PUSH_VOID);
+ }
+ _emit(ctx, &p.out, OP_END);
+ *ps = p;
+ return 0;
+}
+
+
+static int
+_parse_iteration_statement(struct parse_context *ctx,
+ struct parse_state *ps)
+{
+ struct parse_state p = *ps;
+
+ if (_parse_id(ctx, ctx->dict._while, &p) == 0) {
+ _emit(ctx, &p.out, OP_WHILE);
+ if (_parse_token(ctx, SL_PP_LPAREN, &p)) {
+ _error(ctx, "expected `('");
+ return -1;
+ }
+ if (_parse_condition(ctx, &p)) {
+ _error(ctx, "expected an expression");
+ return -1;
+ }
+ if (_parse_token(ctx, SL_PP_RPAREN, &p)) {
+ _error(ctx, "expected `)'");
+ return -1;
+ }
+ if (_parse_statement(ctx, &p)) {
+ return -1;
+ }
+ *ps = p;
+ return 0;
+ }
+
+ if (_parse_id(ctx, ctx->dict._do, &p) == 0) {
+ _emit(ctx, &p.out, OP_DO);
+ if (_parse_statement(ctx, &p)) {
+ return -1;
+ }
+ if (_parse_id(ctx, ctx->dict._while, &p)) {
+ return -1;
+ }
+ if (_parse_token(ctx, SL_PP_LPAREN, &p)) {
+ _error(ctx, "expected `('");
+ return -1;
+ }
+ if (_parse_expression(ctx, &p)) {
+ _error(ctx, "expected an expression");
+ return -1;
+ }
+ if (_parse_token(ctx, SL_PP_RPAREN, &p)) {
+ _error(ctx, "expected `)'");
+ return -1;
+ }
+ _emit(ctx, &p.out, OP_END);
+ if (_parse_token(ctx, SL_PP_SEMICOLON, &p)) {
+ _error(ctx, "expected `;'");
+ return -1;
+ }
+ *ps = p;
+ return 0;
+ }
+
+ if (_parse_id(ctx, ctx->dict._for, &p) == 0) {
+ _emit(ctx, &p.out, OP_FOR);
+ if (_parse_token(ctx, SL_PP_LPAREN, &p)) {
+ _error(ctx, "expected `('");
+ return -1;
+ }
+ if (_parse_for_init_statement(ctx, &p)) {
+ return -1;
+ }
+ if (_parse_for_rest_statement(ctx, &p)) {
+ return -1;
+ }
+ if (_parse_token(ctx, SL_PP_RPAREN, &p)) {
+ _error(ctx, "expected `)'");
+ return -1;
+ }
+ if (_parse_statement(ctx, &p)) {
+ return -1;
+ }
+ *ps = p;
+ return 0;
+ }
+
+ return -1;
+}
+
+
+static int
+_parse_jump_statement(struct parse_context *ctx,
+ struct parse_state *ps)
+{
+ struct parse_state p = *ps;
+ unsigned int e = _emit(ctx, &p.out, 0);
+
+ if (_parse_id(ctx, ctx->dict._continue, &p) == 0) {
+ _update(ctx, e, OP_CONTINUE);
+ } else if (_parse_id(ctx, ctx->dict._break, &p) == 0) {
+ _update(ctx, e, OP_BREAK);
+ } else if (_parse_id(ctx, ctx->dict._return, &p) == 0) {
+ _update(ctx, e, OP_RETURN);
+ if (_parse_expression(ctx, &p)) {
+ _emit(ctx, &p.out, OP_PUSH_VOID);
+ }
+ _emit(ctx, &p.out, OP_END);
+ } else if (ctx->shader_type == 1 && _parse_id(ctx, ctx->dict.discard, &p) == 0) {
+ _update(ctx, e, OP_DISCARD);
+ } else {
+ return -1;
+ }
+ if (_parse_token(ctx, SL_PP_SEMICOLON, &p)) {
+ return -1;
+ }
+ *ps = p;
+ return 0;
+}
+
+
+static int
+_parse_simple_statement(struct parse_context *ctx,
+ struct parse_state *ps)
+{
+ struct parse_state p;
+ unsigned int e;
+
+ if (_parse_selection_statement(ctx, ps) == 0) {
+ return 0;
+ }
+
+ if (_parse_iteration_statement(ctx, ps) == 0) {
+ return 0;
+ }
+
+ if (_parse_jump_statement(ctx, ps) == 0) {
+ return 0;
+ }
+
+ p = *ps;
+ e = _emit(ctx, &p.out, OP_EXPRESSION);
+ if (_parse_expression_statement(ctx, &p) == 0) {
+ *ps = p;
+ return 0;
+ }
+
+ if (_parse_precision_stmt(ctx, &p) == 0) {
+ _update(ctx, e, OP_PRECISION);
+ *ps = p;
+ return 0;
+ }
+
+ if (ctx->parsing_builtin && _parse_asm_statement(ctx, &p) == 0) {
+ _update(ctx, e, OP_ASM);
+ *ps = p;
+ return 0;
+ }
+
+ if (_parse_declaration(ctx, &p) == 0) {
+ _update(ctx, e, OP_DECLARE);
+ *ps = p;
+ return 0;
+ }
+
+ return -1;
+}
+
+
+static int
+_parse_compound_statement(struct parse_context *ctx,
+ struct parse_state *ps)
+{
+ struct parse_state p = *ps;
+
+ if (_parse_token(ctx, SL_PP_LBRACE, &p)) {
+ return -1;
+ }
+ _emit(ctx, &p.out, OP_BLOCK_BEGIN_NEW_SCOPE);
+ _parse_statement_list(ctx, &p);
+ if (_parse_token(ctx, SL_PP_RBRACE, &p)) {
+ return -1;
+ }
+ _emit(ctx, &p.out, OP_END);
+ *ps = p;
+ return 0;
+}
+
+
+static int
+_parse_statement(struct parse_context *ctx,
+ struct parse_state *ps)
+{
+ if (_parse_compound_statement(ctx, ps) == 0) {
+ return 0;
+ }
+
+ if (_parse_simple_statement(ctx, ps) == 0) {
+ return 0;
+ }
+
+ return -1;
+}
+
+
+static int
+_parse_statement_list(struct parse_context *ctx,
+ struct parse_state *ps)
+{
+ struct parse_state p = *ps;
+
+ if (_parse_statement(ctx, &p)) {
+ return -1;
+ }
+
+ for (;;) {
+ *ps = p;
+ if (_parse_statement(ctx, &p)) {
+ return 0;
+ }
+ }
+}
+
+
+static int
+_parse_compound_statement_no_new_scope(struct parse_context *ctx,
+ struct parse_state *ps)
+{
+ struct parse_state p = *ps;
+
+ if (_parse_token(ctx, SL_PP_LBRACE, &p)) {
+ return -1;
+ }
+ _emit(ctx, &p.out, OP_BLOCK_BEGIN_NO_NEW_SCOPE);
+ _parse_statement_list(ctx, &p);
+ if (_parse_token(ctx, SL_PP_RBRACE, &p)) {
+ return -1;
+ }
+ _emit(ctx, &p.out, OP_END);
+ *ps = p;
+ return 0;
+}
+
+
+static int
+_parse_function_definition(struct parse_context *ctx,
+ struct parse_state *ps)
+{
+ struct parse_state p = *ps;
+
+ if (_parse_function_prototype(ctx, &p)) {
+ return -1;
+ }
+ if (_parse_compound_statement_no_new_scope(ctx, &p)) {
+ return -1;
+ }
+ *ps = p;
+ return 0;
+}
+
+
+static int
+_parse_invariant_stmt(struct parse_context *ctx,
+ struct parse_state *ps)
+{
+ struct parse_state p = *ps;
+
+ if (_parse_id(ctx, ctx->dict.invariant, &p)) {
+ return -1;
+ }
+ if (_parse_identifier(ctx, &p)) {
+ return -1;
+ }
+ if (_parse_token(ctx, SL_PP_SEMICOLON, &p)) {
+ return -1;
+ }
+ *ps = p;
+ return 0;
+}
+
+
+static int
+_parse_single_declaration(struct parse_context *ctx,
+ struct parse_state *ps)
+{
+ struct parse_state p = *ps;
+ unsigned int e;
+
+ if (_parse_fully_specified_type(ctx, &p)) {
+ return -1;
+ }
+
+ e = _emit(ctx, &p.out, VARIABLE_IDENTIFIER);
+ if (_parse_identifier(ctx, &p)) {
+ _update(ctx, e, VARIABLE_NONE);
+ *ps = p;
+ return 0;
+ }
+
+ e = _emit(ctx, &p.out, VARIABLE_NONE);
+ *ps = p;
+
+ if (_parse_token(ctx, SL_PP_ASSIGN, &p) == 0) {
+ _update(ctx, e, VARIABLE_INITIALIZER);
+ if (_parse_initializer(ctx, &p) == 0) {
+ *ps = p;
+ return 0;
+ }
+ _error(ctx, "expected an initialiser");
+ return -1;
+ }
+ p = *ps;
+
+ if (_parse_token(ctx, SL_PP_LBRACKET, &p) == 0) {
+ if (_parse_constant_expression(ctx, &p)) {
+ _update(ctx, e, VARIABLE_ARRAY_UNKNOWN);
+ } else {
+ _update(ctx, e, VARIABLE_ARRAY_EXPLICIT);
+ }
+ if (_parse_token(ctx, SL_PP_RBRACKET, &p) == 0) {
+ *ps = p;
+ return 0;
+ }
+ _error(ctx, "expected `]'");
+ return -1;
+ }
+ return 0;
+}
+
+
+static int
+_parse_init_declarator_list(struct parse_context *ctx,
+ struct parse_state *ps)
+{
+ struct parse_state p = *ps;
+
+ if (_parse_single_declaration(ctx, &p)) {
+ return -1;
+ }
+
+ for (;;) {
+ unsigned int e;
+
+ *ps = p;
+ if (_parse_token(ctx, SL_PP_COMMA, &p)) {
+ break;
+ }
+ _emit(ctx, &p.out, DECLARATOR_NEXT);
+ _emit(ctx, &p.out, VARIABLE_IDENTIFIER);
+ if (_parse_identifier(ctx, &p)) {
+ break;
+ }
+
+ e = _emit(ctx, &p.out, VARIABLE_NONE);
+ *ps = p;
+
+ if (_parse_token(ctx, SL_PP_ASSIGN, &p) == 0) {
+ if (_parse_initializer(ctx, &p) == 0) {
+ _update(ctx, e, VARIABLE_INITIALIZER);
+ *ps = p;
+ continue;
+ }
+ _error(ctx, "expected an initialiser");
+ break;
+ }
+ p = *ps;
+
+ if (_parse_token(ctx, SL_PP_LBRACKET, &p) == 0) {
+ unsigned int arr;
+
+ if (_parse_constant_expression(ctx, &p)) {
+ arr = VARIABLE_ARRAY_UNKNOWN;
+ } else {
+ arr = VARIABLE_ARRAY_EXPLICIT;
+ }
+ if (_parse_token(ctx, SL_PP_RBRACKET, &p) == 0) {
+ _update(ctx, e, arr);
+ *ps = p;
+ continue;
+ }
+ _error(ctx, "expected `]'");
+ break;
+ }
+ p = *ps;
+ }
+
+ _emit(ctx, &ps->out, DECLARATOR_NONE);
+ return 0;
+}
+
+
+static int
+_parse_declaration(struct parse_context *ctx,
+ struct parse_state *ps)
+{
+ struct parse_state p = *ps;
+ unsigned int e = _emit(ctx, &p.out, DECLARATION_FUNCTION_PROTOTYPE);
+
+ if (_parse_function_prototype(ctx, &p)) {
+ if (_parse_init_declarator_list(ctx, &p)) {
+ return -1;
+ }
+ _update(ctx, e, DECLARATION_INIT_DECLARATOR_LIST);
+ }
+ if (_parse_token(ctx, SL_PP_SEMICOLON, &p)) {
+ _error(ctx, "expected `;'");
+ return -1;
+ }
+ *ps = p;
+ return 0;
+}
+
+
+static int
+_parse_external_declaration(struct parse_context *ctx,
+ struct parse_state *ps)
+{
+ struct parse_state p = *ps;
+ unsigned int e = _emit(ctx, &p.out, 0);
+
+ if (_parse_precision_stmt(ctx, &p) == 0) {
+ _update(ctx, e, DEFAULT_PRECISION);
+ *ps = p;
+ return 0;
+ }
+
+ if (_parse_function_definition(ctx, &p) == 0) {
+ _update(ctx, e, EXTERNAL_FUNCTION_DEFINITION);
+ *ps = p;
+ return 0;
+ }
+
+ if (_parse_invariant_stmt(ctx, &p) == 0) {
+ _update(ctx, e, INVARIANT_STMT);
+ *ps = p;
+ return 0;
+ }
+
+ if (_parse_declaration(ctx, &p) == 0) {
+ _update(ctx, e, EXTERNAL_DECLARATION);
+ *ps = p;
+ return 0;
+ }
+
+ _error(ctx, "expected an identifier");
+ return -1;
+}
+
+
+static int
+_parse_translation_unit(struct parse_context *ctx,
+ struct parse_state *ps)
+{
+ _emit(ctx, &ps->out, REVISION);
+ if (_parse_external_declaration(ctx, ps)) {
+ return -1;
+ }
+ while (_parse_external_declaration(ctx, ps) == 0) {
+ }
+ _emit(ctx, &ps->out, EXTERNAL_NULL);
+ if (_parse_token(ctx, SL_PP_EOF, ps)) {
+ return -1;
+ }
+ return 0;
+}
+
+
+#define ADD_NAME_STR(CTX, NAME, STR)\
+ do {\
+ (CTX).dict.NAME = sl_pp_context_add_unique_str((CTX).context, (STR));\
+ if ((CTX).dict.NAME == -1) {\
+ return -1;\
+ }\
+ } while (0)
+
+#define ADD_NAME(CTX, NAME) ADD_NAME_STR(CTX, NAME, #NAME)
+
+
+int
+sl_cl_compile(struct sl_pp_context *context,
+ const struct sl_pp_token_info *input,
+ unsigned int shader_type,
+ unsigned int parsing_builtin,
+ unsigned char **output,
+ unsigned int *cboutput,
+ char *error,
+ unsigned int cberror)
+{
+ struct parse_context ctx;
+ struct parse_state ps;
+
+ ctx.context = context;
+ ctx.input = input;
+
+ ADD_NAME_STR(ctx, _void, "void");
+ ADD_NAME_STR(ctx, _float, "float");
+ ADD_NAME_STR(ctx, _int, "int");
+ ADD_NAME_STR(ctx, _bool, "bool");
+ ADD_NAME(ctx, vec2);
+ ADD_NAME(ctx, vec3);
+ ADD_NAME(ctx, vec4);
+ ADD_NAME(ctx, bvec2);
+ ADD_NAME(ctx, bvec3);
+ ADD_NAME(ctx, bvec4);
+ ADD_NAME(ctx, ivec2);
+ ADD_NAME(ctx, ivec3);
+ ADD_NAME(ctx, ivec4);
+ ADD_NAME(ctx, mat2);
+ ADD_NAME(ctx, mat3);
+ ADD_NAME(ctx, mat4);
+ ADD_NAME(ctx, mat2x3);
+ ADD_NAME(ctx, mat3x2);
+ ADD_NAME(ctx, mat2x4);
+ ADD_NAME(ctx, mat4x2);
+ ADD_NAME(ctx, mat3x4);
+ ADD_NAME(ctx, mat4x3);
+ ADD_NAME(ctx, sampler1D);
+ ADD_NAME(ctx, sampler2D);
+ ADD_NAME(ctx, sampler3D);
+ ADD_NAME(ctx, samplerCube);
+ ADD_NAME(ctx, sampler1DShadow);
+ ADD_NAME(ctx, sampler2DShadow);
+ ADD_NAME(ctx, sampler2DRect);
+ ADD_NAME(ctx, sampler2DRectShadow);
+
+ ADD_NAME(ctx, invariant);
+
+ ADD_NAME(ctx, centroid);
+
+ ADD_NAME(ctx, precision);
+ ADD_NAME(ctx, lowp);
+ ADD_NAME(ctx, mediump);
+ ADD_NAME(ctx, highp);
+
+ ADD_NAME_STR(ctx, _const, "const");
+ ADD_NAME(ctx, attribute);
+ ADD_NAME(ctx, varying);
+ ADD_NAME(ctx, uniform);
+ ADD_NAME(ctx, __fixed_output);
+ ADD_NAME(ctx, __fixed_input);
+
+ ADD_NAME(ctx, in);
+ ADD_NAME(ctx, out);
+ ADD_NAME(ctx, inout);
+
+ ADD_NAME_STR(ctx, _struct, "struct");
+
+ ADD_NAME(ctx, __constructor);
+ ADD_NAME(ctx, __operator);
+ ADD_NAME_STR(ctx, ___asm, "__asm");
+
+ ADD_NAME_STR(ctx, _if, "if");
+ ADD_NAME_STR(ctx, _else, "else");
+ ADD_NAME_STR(ctx, _for, "for");
+ ADD_NAME_STR(ctx, _while, "while");
+ ADD_NAME_STR(ctx, _do, "do");
+
+ ADD_NAME_STR(ctx, _continue, "continue");
+ ADD_NAME_STR(ctx, _break, "break");
+ ADD_NAME_STR(ctx, _return, "return");
+ ADD_NAME(ctx, discard);
+
+ ADD_NAME_STR(ctx, _false, "false");
+ ADD_NAME_STR(ctx, _true, "true");
+
+ ctx.out_buf = NULL;
+ ctx.out_cap = 0;
+
+ ctx.shader_type = shader_type;
+ ctx.parsing_builtin = 1;
+
+ ctx.error[0] = '\0';
+
+ ps.in = 0;
+ ps.out = 0;
+
+ if (_parse_translation_unit(&ctx, &ps)) {
+ strncpy(error, ctx.error, cberror);
+ return -1;
+ }
+
+ *output = ctx.out_buf;
+ *cboutput = ps.out;
+ return 0;
+}