/*
* Mesa 3-D graphics library
- * Version: 6.6
*
- * Copyright (C) 2005-2006 Brian Paul All Rights Reserved.
+ * Copyright (C) 2005-2008 Brian Paul All Rights Reserved.
+ * Copyright (C) 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"),
* \author Michal Krol
*/
-#include "imports.h"
-#include "grammar_mesa.h"
+#include "main/imports.h"
+#include "shader/grammar/grammar_mesa.h"
#include "slang_preprocess.h"
-static const char *slang_pp_directives_syn =
+LONGSTRING static const char *slang_pp_directives_syn =
#include "library/slang_pp_directives_syn.h"
;
-static const char *slang_pp_expression_syn =
+LONGSTRING static const char *slang_pp_expression_syn =
#include "library/slang_pp_expression_syn.h"
;
-static const char *slang_pp_version_syn =
+LONGSTRING static const char *slang_pp_version_syn =
#include "library/slang_pp_version_syn.h"
;
GLint pos;
grammar_get_last_error ((byte *) (buf), sizeof (buf), &pos);
+ if (buf[0] == 0) {
+ _mesa_snprintf(buf, sizeof(buf), "Preprocessor error");
+ }
slang_info_log_error (log, buf);
}
self->top->effective = self->top->current && self->top[1].effective;
}
-/*
+
+/**
* Extension enables through #extension directive.
* NOTE: Currently, only enable/disable state is stored.
*/
-
typedef struct
{
- GLboolean MESA_shader_debug; /* GL_MESA_shader_debug enable */
+ GLboolean ARB_draw_buffers;
+ GLboolean ARB_texture_rectangle;
} pp_ext;
-/*
+
+/**
* Disable all extensions. Called at startup and on #extension all: disable.
*/
static GLvoid
-pp_ext_disable_all (pp_ext *self)
+pp_ext_disable_all(pp_ext *self)
{
- self->MESA_shader_debug = GL_FALSE;
+ _mesa_memset(self, 0, sizeof(self));
}
+
+/**
+ * Called during preprocessor initialization to set the initial enable/disable
+ * state of extensions.
+ */
static GLvoid
-pp_ext_init (pp_ext *self)
+pp_ext_init(pp_ext *self, const struct gl_extensions *extensions)
{
pp_ext_disable_all (self);
- /* Other initialization code goes here. */
+ self->ARB_draw_buffers = GL_TRUE;
+ if (extensions->NV_texture_rectangle)
+ self->ARB_texture_rectangle = GL_TRUE;
}
+/**
+ * Called in response to #extension directives to enable/disable
+ * the named extension.
+ */
static GLboolean
-pp_ext_set (pp_ext *self, const char *name, GLboolean enable)
+pp_ext_set(pp_ext *self, const char *name, GLboolean enable)
{
- if (_mesa_strcmp (name, "MESA_shader_debug") == 0)
- self->MESA_shader_debug = enable;
- /* Next extension name tests go here. */
+ if (_mesa_strcmp (name, "GL_ARB_draw_buffers") == 0)
+ self->ARB_draw_buffers = enable;
+ else if (_mesa_strcmp (name, "GL_ARB_texture_rectangle") == 0)
+ self->ARB_texture_rectangle = enable;
else
return GL_FALSE;
return GL_TRUE;
}
-/*
- * The state of preprocessor: current line, file and version number, list of all defined macros
- * and the #if/#endif context.
+
+/**
+ * Called in response to #pragma. For example, "#pragma debug(on)" would
+ * call this function as pp_pragma("debug", "on").
+ * \return GL_TRUE if pragma is valid, GL_FALSE if invalid
*/
+static GLboolean
+pp_pragma(struct gl_sl_pragmas *pragmas, const char *pragma, const char *param)
+{
+#if 0
+ printf("#pragma %s %s\n", pragma, param);
+#endif
+ if (_mesa_strcmp(pragma, "optimize") == 0) {
+ if (!param)
+ return GL_FALSE; /* missing required param */
+ if (_mesa_strcmp(param, "on") == 0) {
+ if (!pragmas->IgnoreOptimize)
+ pragmas->Optimize = GL_TRUE;
+ }
+ else if (_mesa_strcmp(param, "off") == 0) {
+ if (!pragmas->IgnoreOptimize)
+ pragmas->Optimize = GL_FALSE;
+ }
+ else {
+ return GL_FALSE; /* invalid param */
+ }
+ }
+ else if (_mesa_strcmp(pragma, "debug") == 0) {
+ if (!param)
+ return GL_FALSE; /* missing required param */
+ if (_mesa_strcmp(param, "on") == 0) {
+ if (!pragmas->IgnoreDebug)
+ pragmas->Debug = GL_TRUE;
+ }
+ else if (_mesa_strcmp(param, "off") == 0) {
+ if (!pragmas->IgnoreDebug)
+ pragmas->Debug = GL_FALSE;
+ }
+ else {
+ return GL_FALSE; /* invalid param */
+ }
+ }
+ /* all other pragmas are silently ignored */
+ return GL_TRUE;
+}
+
+/**
+ * The state of preprocessor: current line, file and version number, list
+ * of all defined macros and the #if/#endif context.
+ */
typedef struct
{
GLint line;
} pp_state;
static GLvoid
-pp_state_init (pp_state *self, slang_info_log *elog)
+pp_state_init (pp_state *self, slang_info_log *elog,
+ const struct gl_extensions *extensions)
{
self->line = 0;
self->file = 1;
+#if FEATURE_es2_glsl
+ self->version = 100;
+#else
self->version = 110;
+#endif
pp_symbols_init (&self->symbols);
- pp_ext_init (&self->ext);
+ pp_ext_init (&self->ext, extensions);
self->elog = elog;
/* Initialize condition stack and create the global context. */
SKIP_WHITE(e->input);
/* Parse macro actual parameters. This can be anything, separated by a colon.
- * TODO: What about nested/grouped parameters by parenthesis? */
+ */
for (i = 0; i < symbol->parameters.count; i++) {
+ GLuint nested_paren_count = 0; /* track number of nested parentheses */
+
if (*e->input == ')') {
slang_info_log_error (e->state->elog, "preprocess error: unexpected ')'.");
return GL_FALSE;
/* Eat all characters up to the comma or closing parentheses. */
pp_symbol_reset (&symbol->parameters.symbols[i]);
- while (!IS_NULL(*e->input) && *e->input != ',' && *e->input != ')')
+ while (!IS_NULL(*e->input)) {
+ /* Exit loop only when all nested parens have been eaten. */
+ if (nested_paren_count == 0 && (*e->input == ',' || *e->input == ')'))
+ break;
+
+ /* Actually count nested parens here. */
+ if (*e->input == '(')
+ nested_paren_count++;
+ else if (*e->input == ')')
+ nested_paren_count--;
+
slang_string_pushc (&symbol->parameters.symbols[i].replacement, *e->input++);
+ }
/* If it was not the last paremeter, skip the comma. Otherwise, skip the
* closing parentheses. */
slang_string_pushi (e->output, e->state->version);
slang_string_pushc (e->output, ' ');
}
+#if FEATURE_es2_glsl
+ else if (_mesa_strcmp (id, "GL_ES") == 0 ||
+ _mesa_strcmp (id, "GL_FRAGMENT_PRECISION_HIGH") == 0) {
+ slang_string_pushc (e->output, ' ');
+ slang_string_pushi (e->output, '1');
+ slang_string_pushc (e->output, ' ');
+ }
+#endif
else {
pp_symbol *symbol;
#define BEHAVIOR_WARN 3
#define BEHAVIOR_DISABLE 4
+#define PRAGMA_NO_PARAM 0
+#define PRAGMA_PARAM 1
+
+
static GLboolean
-preprocess_source (slang_string *output, const char *source, grammar pid, grammar eid,
- slang_info_log *elog)
+preprocess_source (slang_string *output, const char *source,
+ grammar pid, grammar eid,
+ slang_info_log *elog,
+ const struct gl_extensions *extensions,
+ struct gl_sl_pragmas *pragmas)
{
+ static const char *predefined[] = {
+ "__FILE__",
+ "__LINE__",
+ "__VERSION__",
+#if FEATURE_es2_glsl
+ "GL_ES",
+ "GL_FRAGMENT_PRECISION_HIGH",
+#endif
+ NULL
+ };
byte *prod;
GLuint size, i;
pp_state state;
return GL_FALSE;
}
- pp_state_init (&state, elog);
+ pp_state_init (&state, elog, extensions);
+
+ /* add the predefined symbols to the symbol table */
+ for (i = 0; predefined[i]; i++) {
+ pp_symbol *symbol = NULL;
+ symbol = pp_symbols_push(&state.symbols);
+ assert(symbol);
+ slang_string_pushs(&symbol->name,
+ predefined[i], _mesa_strlen(predefined[i]));
+ }
i = 0;
while (i < size) {
else {
const char *id;
GLuint idlen;
+ GLubyte token;
i++;
- switch (prod[i++]) {
+ token = prod[i++];
+ switch (token) {
case TOKEN_END:
/* End of source string.
/* Parse optional macro parameters. */
while (prod[i++] != PARAM_END) {
- if (state.cond.top->effective) {
- pp_symbol *param;
+ pp_symbol *param;
- id = (const char *) (&prod[i]);
- idlen = _mesa_strlen (id);
+ id = (const char *) (&prod[i]);
+ idlen = _mesa_strlen (id);
+ if (state.cond.top->effective) {
pp_annotate (output, "%s, ", id);
param = pp_symbols_push (&symbol->parameters);
if (param == NULL)
id = (const char *) (&prod[i]);
idlen = _mesa_strlen (id);
if (state.cond.top->effective) {
+ slang_string replacement;
+ expand_state es;
+
pp_annotate (output, ") %s", id);
- slang_string_pushs (&symbol->replacement, id, idlen);
+
+ slang_string_init(&replacement);
+ slang_string_pushs(&replacement, id, idlen);
+
+ /* Expand macro replacement. */
+ es.output = &symbol->replacement;
+ es.input = slang_string_cstr(&replacement);
+ es.state = &state;
+ if (!expand(&es, &state.symbols)) {
+ slang_string_free(&replacement);
+ goto error;
+ }
+ slang_string_free(&replacement);
}
i += idlen + 1;
}
}
break;
+ case TOKEN_PRAGMA:
+ {
+ GLint have_param;
+ const char *pragma, *param;
+
+ pragma = (const char *) (&prod[i]);
+ i += _mesa_strlen(pragma) + 1;
+ have_param = (prod[i++] == PRAGMA_PARAM);
+ if (have_param) {
+ param = (const char *) (&prod[i]);
+ i += _mesa_strlen(param) + 1;
+ }
+ else {
+ param = NULL;
+ }
+ pp_pragma(pragmas, pragma, param);
+ }
+ break;
+
case TOKEN_LINE:
id = (const char *) (&prod[i]);
i += _mesa_strlen (id) + 1;
goto error;
}
+ grammar_alloc_free(prod);
pp_state_free (&state);
return GL_TRUE;
error:
+ grammar_alloc_free(prod);
pp_state_free (&state);
return GL_FALSE;
}
+
+/**
+ * Remove the continuation characters from the input string.
+ * This is the very first step in preprocessing and is effective
+ * even inside comment blocks.
+ * If there is a whitespace between a backslash and a newline,
+ * this is not considered as a line continuation.
+ * \return GL_TRUE for success, GL_FALSE otherwise.
+ */
+static GLboolean
+_slang_preprocess_backslashes(slang_string *output,
+ const char *input)
+{
+ while (*input) {
+ if (input[0] == '\\') {
+ /* If a newline follows, eat the backslash and the newline. */
+ if (input[1] == '\r') {
+ if (input[2] == '\n') {
+ input += 3;
+ } else {
+ input += 2;
+ }
+ } else if (input[1] == '\n') {
+ if (input[2] == '\r') {
+ input += 3;
+ } else {
+ input += 2;
+ }
+ } else {
+ /* Leave the backslash alone. */
+ slang_string_pushc(output, *input++);
+ }
+ } else {
+ slang_string_pushc(output, *input++);
+ }
+ }
+ return GL_TRUE;
+}
+
+
+/**
+ * Run preprocessor on source code.
+ * \param extensions indicates which GL extensions are enabled
+ * \param output the post-process results
+ * \param input the input text
+ * \param elog log to record warnings, errors
+ * \param extensions out extension settings
+ * \param pragmas in/out #pragma settings
+ * \return GL_TRUE for success, GL_FALSE for error
+ */
GLboolean
-_slang_preprocess_directives (slang_string *output, const char *input, slang_info_log *elog)
+_slang_preprocess_directives(slang_string *output,
+ const char *input,
+ slang_info_log *elog,
+ const struct gl_extensions *extensions,
+ struct gl_sl_pragmas *pragmas)
{
grammar pid, eid;
GLboolean success;
+ slang_string without_backslashes;
pid = grammar_load_from_text ((const byte *) (slang_pp_directives_syn));
if (pid == 0) {
grammar_destroy (pid);
return GL_FALSE;
}
- success = preprocess_source (output, input, pid, eid, elog);
+
+ slang_string_init(&without_backslashes);
+ success = _slang_preprocess_backslashes(&without_backslashes, input);
+
+ if (0) {
+ _mesa_printf("Pre-processed shader:\n");
+ _mesa_printf("%s", slang_string_cstr(&without_backslashes));
+ _mesa_printf("----------------------\n");
+ }
+
+ if (success) {
+ success = preprocess_source(output,
+ slang_string_cstr(&without_backslashes),
+ pid,
+ eid,
+ elog,
+ extensions,
+ pragmas);
+ }
+
+ slang_string_free(&without_backslashes);
grammar_destroy (eid);
grammar_destroy (pid);
+
+ if (0) {
+ _mesa_printf("Post-processed shader:\n");
+ _mesa_printf("%s", slang_string_cstr(output));
+ _mesa_printf("----------------------\n");
+ }
+
return success;
}