tdfx: condition always evaluates to false in SetupDoubleTexEnvVoodoo3()
[mesa.git] / src / mesa / shader / slang / slang_preprocess.c
index 72281eda57de9db59fd2aa9163898a95aabbbf4d..e9a24cc009af923bf396ac133c9623343f848eaa 100644 (file)
@@ -1,8 +1,8 @@
 /*
  * Mesa 3-D graphics library
- * Version:  6.5.2
  *
- * 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"),
@@ -28,8 +28,8 @@
  * \author Michal Krol
  */
 
-#include "imports.h"
-#include "grammar_mesa.h"
+#include "main/imports.h"
+#include "shader/grammar/grammar_mesa.h"
 #include "slang_preprocess.h"
 
 LONGSTRING static const char *slang_pp_directives_syn =
@@ -51,6 +51,9 @@ grammar_error_to_log (slang_info_log *log)
    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);
 }
 
@@ -475,48 +478,108 @@ pp_cond_stack_reevaluate (pp_cond_stack *self)
    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;
@@ -529,13 +592,18 @@ typedef struct
 } 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. */
@@ -633,8 +701,10 @@ expand_symbol (expand_state *e, pp_symbol *symbol)
       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;
@@ -642,8 +712,19 @@ expand_symbol (expand_state *e, pp_symbol *symbol)
 
          /* 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. */
@@ -725,6 +806,14 @@ expand (expand_state *e, pp_symbols *symbols)
             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;
 
@@ -821,10 +910,27 @@ parse_if (slang_string *output, const byte *prod, GLuint *pi, GLint *result, pp_
 #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;
@@ -834,7 +940,16 @@ preprocess_source (slang_string *output, const char *source, grammar pid, gramma
       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) {
@@ -878,9 +993,11 @@ preprocess_source (slang_string *output, const char *source, grammar pid, gramma
       else {
          const char *id;
          GLuint idlen;
+         GLubyte token;
 
          i++;
-         switch (prod[i++]) {
+         token = prod[i++];
+         switch (token) {
 
          case TOKEN_END:
             /* End of source string.
@@ -918,11 +1035,11 @@ preprocess_source (slang_string *output, const char *source, grammar pid, gramma
 
                /* 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)
@@ -936,8 +1053,23 @@ preprocess_source (slang_string *output, const char *source, grammar pid, gramma
                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;
             }
@@ -1097,6 +1229,25 @@ preprocess_source (slang_string *output, const char *source, grammar pid, gramma
             }
             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;
@@ -1148,11 +1299,66 @@ error:
    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) {
@@ -1165,9 +1371,36 @@ _slang_preprocess_directives (slang_string *output, const char *input, slang_inf
       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;
 }