chmod a-x
[mesa.git] / src / mesa / shader / slang / slang_assemble_typeinfo.c
index 778ba04ced8be7d2ec1843dc1697357c15ce7e78..21b31091ea772b236ee53179a9e56ad9ea2d5fdb 100644 (file)
-/*\r
- * Mesa 3-D graphics library\r
- * Version:  6.5\r
- *\r
- * Copyright (C) 2005-2006  Brian Paul   All Rights Reserved.\r
- *\r
- * Permission is hereby granted, free of charge, to any person obtaining a\r
- * copy of this software and associated documentation files (the "Software"),\r
- * to deal in the Software without restriction, including without limitation\r
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,\r
- * and/or sell copies of the Software, and to permit persons to whom the\r
- * Software is furnished to do so, subject to the following conditions:\r
- *\r
- * The above copyright notice and this permission notice shall be included\r
- * in all copies or substantial portions of the Software.\r
- *\r
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS\r
- * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\r
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL\r
- * BRIAN PAUL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN\r
- * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\r
- */\r
-\r
-/**\r
- * \file slang_assemble_typeinfo.c\r
- * slang type info\r
- * \author Michal Krol\r
- */\r
-\r
-#include "imports.h"\r
-#include "slang_utility.h"\r
-#include "slang_assemble_typeinfo.h"\r
-\r
-/* slang_assembly_typeinfo */\r
-\r
-int slang_assembly_typeinfo_construct (slang_assembly_typeinfo *ti)\r
-{\r
-       if (!slang_type_specifier_construct (&ti->spec))\r
-               return 0;\r
-       ti->array_size = NULL;\r
-       return 1;\r
-}\r
-\r
-void slang_assembly_typeinfo_destruct (slang_assembly_typeinfo *ti)\r
-{\r
-       slang_type_specifier_destruct (&ti->spec);\r
-       /* do not free ti->array_size */\r
-}\r
-\r
-/* _slang_typeof_operation() */\r
-\r
-static int typeof_existing_function (const char *name, slang_operation *params,\r
-       unsigned int num_params, slang_assembly_name_space *space, slang_type_specifier *spec,\r
-       slang_atom_pool *atoms)\r
-{\r
-       slang_atom atom;\r
-       int exists;\r
-\r
-       atom = slang_atom_pool_atom (atoms, name);\r
-       if (!_slang_typeof_function (atom, params, num_params, space, spec, &exists, atoms))\r
-               return 0;\r
-       return exists;\r
-}\r
-\r
-int _slang_typeof_operation (slang_operation *op, slang_assembly_name_space *space,\r
-       slang_assembly_typeinfo *ti, slang_atom_pool *atoms)\r
-{\r
-       ti->can_be_referenced = 0;\r
-       ti->is_swizzled = 0;\r
-\r
-       switch (op->type)\r
-       {\r
-       case slang_oper_block_no_new_scope:\r
-       case slang_oper_block_new_scope:\r
-       case slang_oper_variable_decl:\r
-       case slang_oper_asm:\r
-       case slang_oper_break:\r
-       case slang_oper_continue:\r
-       case slang_oper_discard:\r
-       case slang_oper_return:\r
-       case slang_oper_if:\r
-       case slang_oper_while:\r
-       case slang_oper_do:\r
-       case slang_oper_for:\r
-       case slang_oper_void:\r
-               ti->spec.type = slang_spec_void;\r
-               break;\r
-       case slang_oper_expression:\r
-       case slang_oper_assign:\r
-       case slang_oper_addassign:\r
-       case slang_oper_subassign:\r
-       case slang_oper_mulassign:\r
-       case slang_oper_divassign:\r
-       case slang_oper_preincrement:\r
-       case slang_oper_predecrement:\r
-               if (!_slang_typeof_operation (op->children, space, ti, atoms))\r
-                       return 0;\r
-               break;\r
-       case slang_oper_literal_bool:\r
-       case slang_oper_logicalor:\r
-       case slang_oper_logicalxor:\r
-       case slang_oper_logicaland:\r
-       case slang_oper_equal:\r
-       case slang_oper_notequal:\r
-       case slang_oper_less:\r
-       case slang_oper_greater:\r
-       case slang_oper_lessequal:\r
-       case slang_oper_greaterequal:\r
-       case slang_oper_not:\r
-               ti->spec.type = slang_spec_bool;\r
-               break;\r
-       case slang_oper_literal_int:\r
-               ti->spec.type = slang_spec_int;\r
-               break;\r
-       case slang_oper_literal_float:\r
-               ti->spec.type = slang_spec_float;\r
-               break;\r
-       case slang_oper_identifier:\r
-               {\r
-                       slang_variable *var;\r
-\r
-                       var = _slang_locate_variable (op->locals, op->a_id, 1);\r
-                       if (var == NULL)\r
-                               return 0;\r
-                       if (!slang_type_specifier_copy (&ti->spec, &var->type.specifier))\r
-                               return 0;\r
-                       ti->can_be_referenced = 1;\r
-                       ti->array_size = var->array_size;\r
-               }\r
-               break;\r
-       case slang_oper_sequence:\r
-               /* TODO: check [0] and [1] if they match */\r
-               if (!_slang_typeof_operation (&op->children[1], space, ti, atoms))\r
-                       return 0;\r
-               ti->can_be_referenced = 0;\r
-               ti->is_swizzled = 0;\r
-               break;\r
-       /*case slang_oper_modassign:*/\r
-       /*case slang_oper_lshassign:*/\r
-       /*case slang_oper_rshassign:*/\r
-       /*case slang_oper_orassign:*/\r
-       /*case slang_oper_xorassign:*/\r
-       /*case slang_oper_andassign:*/\r
-       case slang_oper_select:\r
-               /* TODO: check [1] and [2] if they match */\r
-               if (!_slang_typeof_operation (&op->children[1], space, ti, atoms))\r
-                       return 0;\r
-               ti->can_be_referenced = 0;\r
-               ti->is_swizzled = 0;\r
-               break;\r
-       /*case slang_oper_bitor:*/\r
-       /*case slang_oper_bitxor:*/\r
-       /*case slang_oper_bitand:*/\r
-       /*case slang_oper_lshift:*/\r
-       /*case slang_oper_rshift:*/\r
-       case slang_oper_add:\r
-               if (!typeof_existing_function ("+", op->children, 2, space, &ti->spec, atoms))\r
-                       return 0;\r
-               break;\r
-       case slang_oper_subtract:\r
-               if (!typeof_existing_function ("-", op->children, 2, space, &ti->spec, atoms))\r
-                       return 0;\r
-               break;\r
-       case slang_oper_multiply:\r
-               if (!typeof_existing_function ("*", op->children, 2, space, &ti->spec, atoms))\r
-                       return 0;\r
-               break;\r
-       case slang_oper_divide:\r
-               if (!typeof_existing_function ("/", op->children, 2, space, &ti->spec, atoms))\r
-                       return 0;\r
-               break;\r
-       /*case slang_oper_modulus:*/\r
-       case slang_oper_plus:\r
-               if (!_slang_typeof_operation (op->children, space, ti, atoms))\r
-                       return 0;\r
-               ti->can_be_referenced = 0;\r
-               ti->is_swizzled = 0;\r
-               break;\r
-       case slang_oper_minus:\r
-               if (!typeof_existing_function ("-", op->children, 1, space, &ti->spec, atoms))\r
-                       return 0;\r
-               break;\r
-       /*case slang_oper_complement:*/\r
-       case slang_oper_subscript:\r
-               {\r
-                       slang_assembly_typeinfo _ti;\r
-\r
-                       if (!slang_assembly_typeinfo_construct (&_ti))\r
-                               return 0;\r
-                       if (!_slang_typeof_operation (op->children, space, &_ti, atoms))\r
-                       {\r
-                               slang_assembly_typeinfo_destruct (&_ti);\r
-                               return 0;\r
-                       }\r
-                       ti->can_be_referenced = _ti.can_be_referenced;\r
-                       if (_ti.spec.type == slang_spec_array)\r
-                       {\r
-                               if (!slang_type_specifier_copy (&ti->spec, _ti.spec._array))\r
-                               {\r
-                                       slang_assembly_typeinfo_destruct (&_ti);\r
-                                       return 0;\r
-                               }\r
-                       }\r
-                       else\r
-                       {\r
-                               if (!_slang_type_is_vector (_ti.spec.type) && !_slang_type_is_matrix (_ti.spec.type))\r
-                               {\r
-                                       slang_assembly_typeinfo_destruct (&_ti);\r
-                                       return 0;\r
-                               }\r
-                               ti->spec.type = _slang_type_base (_ti.spec.type);\r
-                       }\r
-                       slang_assembly_typeinfo_destruct (&_ti);\r
-               }\r
-               break;\r
-       case slang_oper_call:\r
-               {\r
-                       int exists;\r
-\r
-                       if (!_slang_typeof_function (op->a_id, op->children, op->num_children, space, &ti->spec,\r
-                                       &exists, atoms))\r
-                               return 0;\r
-                       if (!exists)\r
-                       {\r
-/*                             slang_struct *s = slang_struct_scope_find (space->structs, op->identifier, 1);\r
-                               if (s != NULL)\r
-                               {\r
-                                       ti->spec.type = slang_spec_struct;\r
-                                       ti->spec._struct = (slang_struct *) slang_alloc_malloc (sizeof (slang_struct));\r
-                                       if (ti->spec._struct == NULL)\r
-                                               return 0;\r
-                                       if (!slang_struct_construct (ti->spec._struct))\r
-                                       {\r
-                                               slang_alloc_free (ti->spec._struct);\r
-                                               ti->spec._struct = NULL;\r
-                                               return 0;\r
-                                       }\r
-                                       if (!slang_struct_copy (ti->spec._struct, s))\r
-                                               return 0;\r
-                               }\r
-                               else\r
-*/                             {\r
-                                       const char *name;\r
-                                       slang_type_specifier_type type;\r
-\r
-                                       name = slang_atom_pool_id (atoms, op->a_id);\r
-                                       type = slang_type_specifier_type_from_string (name);\r
-                                       if (type == slang_spec_void)\r
-                                               return 0;\r
-                                       ti->spec.type = type;\r
-                               }\r
-                       }\r
-               }\r
-               break;\r
-       case slang_oper_field:\r
-               {\r
-                       slang_assembly_typeinfo _ti;\r
-\r
-                       if (!slang_assembly_typeinfo_construct (&_ti))\r
-                               return 0;\r
-                       if (!_slang_typeof_operation (op->children, space, &_ti, atoms))\r
-                       {\r
-                               slang_assembly_typeinfo_destruct (&_ti);\r
-                               return 0;\r
-                       }\r
-                       if (_ti.spec.type == slang_spec_struct)\r
-                       {\r
-                               slang_variable *field;\r
-                               \r
-                               field = _slang_locate_variable (_ti.spec._struct->fields, op->a_id, 0);\r
-                               if (field == NULL)\r
-                               {\r
-                                       slang_assembly_typeinfo_destruct (&_ti);\r
-                                       return 0;\r
-                               }\r
-                               if (!slang_type_specifier_copy (&ti->spec, &field->type.specifier))\r
-                               {\r
-                                       slang_assembly_typeinfo_destruct (&_ti);\r
-                                       return 0;\r
-                               }\r
-                               ti->can_be_referenced = _ti.can_be_referenced;\r
-                       }\r
-                       else\r
-                       {\r
-                               unsigned int rows;\r
-                               const char *swizzle;\r
-                               slang_type_specifier_type base;\r
-\r
-                               /* determine the swizzle of the field expression */\r
-                               if (!_slang_type_is_vector (_ti.spec.type))\r
-                               {\r
-                                       slang_assembly_typeinfo_destruct (&_ti);\r
-                                       return 0;\r
-                               }\r
-                               rows = _slang_type_dim (_ti.spec.type);\r
-                               swizzle = slang_atom_pool_id (atoms, op->a_id);\r
-                               if (!_slang_is_swizzle (swizzle, rows, &ti->swz))\r
-                               {\r
-                                       slang_assembly_typeinfo_destruct (&_ti);\r
-                                       return 0;\r
-                               }\r
-                               ti->is_swizzled = 1;\r
-                               ti->can_be_referenced = _ti.can_be_referenced && _slang_is_swizzle_mask (&ti->swz,\r
-                                       rows);\r
-                               if (_ti.is_swizzled)\r
-                               {\r
-                                       slang_swizzle swz;\r
-\r
-                                       /* swizzle the swizzle */\r
-                                       _slang_multiply_swizzles (&swz, &_ti.swz, &ti->swz);\r
-                                       ti->swz = swz;\r
-                               }\r
-                               base = _slang_type_base (_ti.spec.type);\r
-                               switch (ti->swz.num_components)\r
-                               {\r
-                               case 1:\r
-                                       ti->spec.type = base;\r
-                                       break;\r
-                               case 2:\r
-                                       switch (base)\r
-                                       {\r
-                                       case slang_spec_float:\r
-                                               ti->spec.type = slang_spec_vec2;\r
-                                               break;\r
-                                       case slang_spec_int:\r
-                                               ti->spec.type = slang_spec_ivec2;\r
-                                               break;\r
-                                       case slang_spec_bool:\r
-                                               ti->spec.type = slang_spec_bvec2;\r
-                                               break;\r
-                                       }\r
-                                       break;\r
-                               case 3:\r
-                                       switch (base)\r
-                                       {\r
-                                       case slang_spec_float:\r
-                                               ti->spec.type = slang_spec_vec3;\r
-                                               break;\r
-                                       case slang_spec_int:\r
-                                               ti->spec.type = slang_spec_ivec3;\r
-                                               break;\r
-                                       case slang_spec_bool:\r
-                                               ti->spec.type = slang_spec_bvec3;\r
-                                               break;\r
-                                       }\r
-                                       break;\r
-                               case 4:\r
-                                       switch (base)\r
-                                       {\r
-                                       case slang_spec_float:\r
-                                               ti->spec.type = slang_spec_vec4;\r
-                                               break;\r
-                                       case slang_spec_int:\r
-                                               ti->spec.type = slang_spec_ivec4;\r
-                                               break;\r
-                                       case slang_spec_bool:\r
-                                               ti->spec.type = slang_spec_bvec4;\r
-                                               break;\r
-                                       }\r
-                                       break;\r
-                               default:\r
-                                       break;\r
-                               }\r
-                       }\r
-                       slang_assembly_typeinfo_destruct (&_ti);\r
-               }\r
-               break;\r
-       case slang_oper_postincrement:\r
-       case slang_oper_postdecrement:\r
-               if (!_slang_typeof_operation (op->children, space, ti, atoms))\r
-                       return 0;\r
-               ti->can_be_referenced = 0;\r
-               ti->is_swizzled = 0;\r
-               break;\r
-       default:\r
-               return 0;\r
-       }\r
-       return 1;\r
-}\r
-\r
-/* _slang_typeof_function() */\r
-\r
-int _slang_typeof_function (slang_atom a_name, slang_operation *params, unsigned int num_params,\r
-       slang_assembly_name_space *space, slang_type_specifier *spec, int *exists, slang_atom_pool *atoms)\r
-{\r
-       slang_function *fun;\r
-       \r
-       fun = _slang_locate_function (space->funcs, a_name, params, num_params, space, atoms);\r
-       *exists = fun != NULL;\r
-       if (fun == NULL)\r
-               return 1;\r
-       return slang_type_specifier_copy (spec, &fun->header.type.specifier);\r
-}\r
-\r
-/* _slang_type_is_matrix() */\r
-\r
-int _slang_type_is_matrix (slang_type_specifier_type ty)\r
-{\r
-       switch (ty)\r
-       {\r
-       case slang_spec_mat2:\r
-       case slang_spec_mat3:\r
-       case slang_spec_mat4:\r
-               return 1;\r
-       default:\r
-               return 0;\r
-       }\r
-}\r
-\r
-/* _slang_type_is_vector() */\r
-\r
-int _slang_type_is_vector (slang_type_specifier_type ty)\r
-{\r
-       switch (ty)\r
-       {\r
-       case slang_spec_vec2:\r
-       case slang_spec_vec3:\r
-       case slang_spec_vec4:\r
-       case slang_spec_ivec2:\r
-       case slang_spec_ivec3:\r
-       case slang_spec_ivec4:\r
-       case slang_spec_bvec2:\r
-       case slang_spec_bvec3:\r
-       case slang_spec_bvec4:\r
-               return 1;\r
-       default:\r
-               return 0;\r
-       }\r
-}\r
-\r
-/* _slang_type_base_of_vector() */\r
-\r
-slang_type_specifier_type _slang_type_base (slang_type_specifier_type ty)\r
-{\r
-       switch (ty)\r
-       {\r
-       case slang_spec_float:\r
-       case slang_spec_vec2:\r
-       case slang_spec_vec3:\r
-       case slang_spec_vec4:\r
-               return slang_spec_float;\r
-       case slang_spec_int:\r
-       case slang_spec_ivec2:\r
-       case slang_spec_ivec3:\r
-       case slang_spec_ivec4:\r
-               return slang_spec_int;\r
-       case slang_spec_bool:\r
-       case slang_spec_bvec2:\r
-       case slang_spec_bvec3:\r
-       case slang_spec_bvec4:\r
-               return slang_spec_bool;\r
-       case slang_spec_mat2:\r
-               return slang_spec_vec2;\r
-       case slang_spec_mat3:\r
-               return slang_spec_vec3;\r
-       case slang_spec_mat4:\r
-               return slang_spec_vec4;\r
-       default:\r
-               return slang_spec_void;\r
-       }\r
-}\r
-\r
-/* _slang_type_dim */\r
-\r
-unsigned int _slang_type_dim (slang_type_specifier_type ty)\r
-{\r
-       switch (ty)\r
-       {\r
-       case slang_spec_float:\r
-       case slang_spec_int:\r
-       case slang_spec_bool:\r
-               return 1;\r
-       case slang_spec_vec2:\r
-       case slang_spec_ivec2:\r
-       case slang_spec_bvec2:\r
-       case slang_spec_mat2:\r
-               return 2;\r
-       case slang_spec_vec3:\r
-       case slang_spec_ivec3:\r
-       case slang_spec_bvec3:\r
-       case slang_spec_mat3:\r
-               return 3;\r
-       case slang_spec_vec4:\r
-       case slang_spec_ivec4:\r
-       case slang_spec_bvec4:\r
-       case slang_spec_mat4:\r
-               return 4;\r
-       default:\r
-               return 0;\r
-       }\r
-}\r
-\r
+/*
+ * Mesa 3-D graphics library
+ * Version:  6.5
+ *
+ * Copyright (C) 2005-2006  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.
+ */
+
+/**
+ * \file slang_assemble_typeinfo.c
+ * slang type info
+ * \author Michal Krol
+ */
+
+#include "imports.h"
+#include "slang_assemble.h"
+#include "slang_compile.h"
+#include "slang_error.h"
+
+
+GLvoid
+slang_type_specifier_ctr(slang_type_specifier * self)
+{
+   self->type = slang_spec_void;
+   self->_struct = NULL;
+   self->_array = NULL;
+}
+
+GLvoid
+slang_type_specifier_dtr(slang_type_specifier * self)
+{
+   if (self->_struct != NULL) {
+      slang_struct_destruct(self->_struct);
+      slang_alloc_free(self->_struct);
+   }
+   if (self->_array != NULL) {
+      slang_type_specifier_dtr(self->_array);
+      slang_alloc_free(self->_array);
+   }
+}
+
+GLboolean
+slang_type_specifier_copy(slang_type_specifier * x,
+                          const slang_type_specifier * y)
+{
+   slang_type_specifier z;
+
+   slang_type_specifier_ctr(&z);
+   z.type = y->type;
+   if (z.type == slang_spec_struct) {
+      z._struct = (slang_struct *) slang_alloc_malloc(sizeof(slang_struct));
+      if (z._struct == NULL) {
+         slang_type_specifier_dtr(&z);
+         return GL_FALSE;
+      }
+      if (!slang_struct_construct(z._struct)) {
+         slang_alloc_free(z._struct);
+         slang_type_specifier_dtr(&z);
+         return GL_FALSE;
+      }
+      if (!slang_struct_copy(z._struct, y->_struct)) {
+         slang_type_specifier_dtr(&z);
+         return GL_FALSE;
+      }
+   }
+   else if (z.type == slang_spec_array) {
+      z._array =
+         (slang_type_specifier *)
+         slang_alloc_malloc(sizeof(slang_type_specifier));
+      if (z._array == NULL) {
+         slang_type_specifier_dtr(&z);
+         return GL_FALSE;
+      }
+      slang_type_specifier_ctr(z._array);
+      if (!slang_type_specifier_copy(z._array, y->_array)) {
+         slang_type_specifier_dtr(&z);
+         return GL_FALSE;
+      }
+   }
+   slang_type_specifier_dtr(x);
+   *x = z;
+   return GL_TRUE;
+}
+
+GLboolean
+slang_type_specifier_equal(const slang_type_specifier * x,
+                           const slang_type_specifier * y)
+{
+   if (x->type != y->type)
+      return 0;
+   if (x->type == slang_spec_struct)
+      return slang_struct_equal(x->_struct, y->_struct);
+   if (x->type == slang_spec_array)
+      return slang_type_specifier_equal(x->_array, y->_array);
+   return 1;
+}
+
+
+GLboolean
+slang_assembly_typeinfo_construct(slang_assembly_typeinfo * ti)
+{
+   slang_type_specifier_ctr(&ti->spec);
+   ti->array_len = 0;
+   return GL_TRUE;
+}
+
+GLvoid
+slang_assembly_typeinfo_destruct(slang_assembly_typeinfo * ti)
+{
+   slang_type_specifier_dtr(&ti->spec);
+}
+
+
+/**
+ * Determine the return type of a function.
+ * \param name  name of the function
+ * \param params  array of function parameters
+ * \param num_params  number of parameters
+ * \param space  namespace to use
+ * \param spec  returns the function's type
+ * \param atoms  atom pool
+ * \return GL_TRUE for success, GL_FALSE if failure
+ */
+static GLboolean
+typeof_existing_function(const char *name, const slang_operation * params,
+                         GLuint num_params,
+                         const slang_assembly_name_space * space,
+                         slang_type_specifier * spec,
+                         slang_atom_pool * atoms)
+{
+   slang_atom atom;
+   GLboolean exists;
+
+   atom = slang_atom_pool_atom(atoms, name);
+   if (!_slang_typeof_function(atom, params, num_params, space, spec,
+                               &exists, atoms))
+      return GL_FALSE;
+   return exists;
+}
+
+GLboolean
+_slang_typeof_operation(const slang_assemble_ctx * A,
+                        const slang_operation * op,
+                        slang_assembly_typeinfo * ti)
+{
+   return _slang_typeof_operation_(op, &A->space, ti, A->atoms);
+}
+
+
+/**
+ * Determine the return type of an operation.
+ * \param op  the operation node
+ * \param space  the namespace to use
+ * \param ti  the returned type
+ * \param atoms  atom pool
+ * \return GL_TRUE for success, GL_FALSE if failure
+ */
+GLboolean
+_slang_typeof_operation_(const slang_operation * op,
+                         const slang_assembly_name_space * space,
+                         slang_assembly_typeinfo * ti,
+                         slang_atom_pool * atoms)
+{
+   ti->can_be_referenced = GL_FALSE;
+   ti->is_swizzled = GL_FALSE;
+
+   switch (op->type) {
+   case slang_oper_block_no_new_scope:
+   case slang_oper_block_new_scope:
+   case slang_oper_variable_decl:
+   case slang_oper_asm:
+   case slang_oper_break:
+   case slang_oper_continue:
+   case slang_oper_discard:
+   case slang_oper_return:
+   case slang_oper_if:
+   case slang_oper_while:
+   case slang_oper_do:
+   case slang_oper_for:
+   case slang_oper_void:
+      ti->spec.type = slang_spec_void;
+      break;
+   case slang_oper_expression:
+   case slang_oper_assign:
+   case slang_oper_addassign:
+   case slang_oper_subassign:
+   case slang_oper_mulassign:
+   case slang_oper_divassign:
+   case slang_oper_preincrement:
+   case slang_oper_predecrement:
+      if (!_slang_typeof_operation_(op->children, space, ti, atoms))
+         return GL_FALSE;
+      break;
+   case slang_oper_literal_bool:
+   case slang_oper_logicalor:
+   case slang_oper_logicalxor:
+   case slang_oper_logicaland:
+   case slang_oper_equal:
+   case slang_oper_notequal:
+   case slang_oper_less:
+   case slang_oper_greater:
+   case slang_oper_lessequal:
+   case slang_oper_greaterequal:
+   case slang_oper_not:
+      ti->spec.type = slang_spec_bool;
+      break;
+   case slang_oper_literal_int:
+      ti->spec.type = slang_spec_int;
+      break;
+   case slang_oper_literal_float:
+      ti->spec.type = slang_spec_float;
+      break;
+   case slang_oper_identifier:
+      {
+         slang_variable *var;
+         var = _slang_locate_variable(op->locals, op->a_id, GL_TRUE);
+         if (var == NULL)
+            RETURN_ERROR2("undefined variable", (char *) op->a_id, 0);
+         if (!slang_type_specifier_copy(&ti->spec, &var->type.specifier))
+            RETURN_OUT_OF_MEMORY();
+         ti->can_be_referenced = GL_TRUE;
+         ti->array_len = var->array_len;
+      }
+      break;
+   case slang_oper_sequence:
+      /* TODO: check [0] and [1] if they match */
+      if (!_slang_typeof_operation_(&op->children[1], space, ti, atoms))
+         RETURN_NIL();
+      ti->can_be_referenced = GL_FALSE;
+      ti->is_swizzled = GL_FALSE;
+      break;
+      /*case slang_oper_modassign: */
+      /*case slang_oper_lshassign: */
+      /*case slang_oper_rshassign: */
+      /*case slang_oper_orassign: */
+      /*case slang_oper_xorassign: */
+      /*case slang_oper_andassign: */
+   case slang_oper_select:
+      /* TODO: check [1] and [2] if they match */
+      if (!_slang_typeof_operation_(&op->children[1], space, ti, atoms))
+         RETURN_NIL();
+      ti->can_be_referenced = GL_FALSE;
+      ti->is_swizzled = GL_FALSE;
+      break;
+      /*case slang_oper_bitor: */
+      /*case slang_oper_bitxor: */
+      /*case slang_oper_bitand: */
+      /*case slang_oper_lshift: */
+      /*case slang_oper_rshift: */
+   case slang_oper_add:
+      if (!typeof_existing_function("+", op->children, 2, space,
+                                    &ti->spec, atoms))
+         RETURN_NIL();
+      break;
+   case slang_oper_subtract:
+      if (!typeof_existing_function("-", op->children, 2, space,
+                                    &ti->spec, atoms))
+         RETURN_NIL();
+      break;
+   case slang_oper_multiply:
+      if (!typeof_existing_function("*", op->children, 2, space,
+                                    &ti->spec, atoms))
+         RETURN_NIL();
+      break;
+   case slang_oper_divide:
+      if (!typeof_existing_function("/", op->children, 2, space,
+                                    &ti->spec, atoms))
+         RETURN_NIL();
+      break;
+      /*case slang_oper_modulus: */
+   case slang_oper_plus:
+      if (!_slang_typeof_operation_(op->children, space, ti, atoms))
+         RETURN_NIL();
+      ti->can_be_referenced = GL_FALSE;
+      ti->is_swizzled = GL_FALSE;
+      break;
+   case slang_oper_minus:
+      if (!typeof_existing_function("-", op->children, 1, space,
+                                    &ti->spec, atoms))
+         RETURN_NIL();
+      break;
+      /*case slang_oper_complement: */
+   case slang_oper_subscript:
+      {
+         slang_assembly_typeinfo _ti;
+
+         if (!slang_assembly_typeinfo_construct(&_ti))
+            RETURN_NIL();
+         if (!_slang_typeof_operation_(op->children, space, &_ti, atoms)) {
+            slang_assembly_typeinfo_destruct(&_ti);
+            RETURN_NIL();
+         }
+         ti->can_be_referenced = _ti.can_be_referenced;
+         if (_ti.spec.type == slang_spec_array) {
+            if (!slang_type_specifier_copy(&ti->spec, _ti.spec._array)) {
+               slang_assembly_typeinfo_destruct(&_ti);
+               RETURN_NIL();
+            }
+         }
+         else {
+            if (!_slang_type_is_vector(_ti.spec.type)
+                && !_slang_type_is_matrix(_ti.spec.type)) {
+               slang_assembly_typeinfo_destruct(&_ti);
+               RETURN_ERROR("cannot index a non-array type", 0);
+            }
+            ti->spec.type = _slang_type_base(_ti.spec.type);
+         }
+         slang_assembly_typeinfo_destruct(&_ti);
+      }
+      break;
+   case slang_oper_call:
+      {
+         GLboolean exists;
+
+         if (!_slang_typeof_function(op->a_id, op->children, op->num_children,
+                                     space, &ti->spec, &exists, atoms))
+            RETURN_NIL();
+         if (!exists) {
+            slang_struct *s =
+               slang_struct_scope_find(space->structs, op->a_id, GL_TRUE);
+            if (s != NULL) {
+               ti->spec.type = slang_spec_struct;
+               ti->spec._struct =
+                  (slang_struct *) slang_alloc_malloc(sizeof(slang_struct));
+               if (ti->spec._struct == NULL)
+                  RETURN_NIL();
+               if (!slang_struct_construct(ti->spec._struct)) {
+                  slang_alloc_free(ti->spec._struct);
+                  ti->spec._struct = NULL;
+                  RETURN_NIL();
+               }
+               if (!slang_struct_copy(ti->spec._struct, s))
+                  RETURN_NIL();
+            }
+            else {
+               const char *name;
+               slang_type_specifier_type type;
+
+               name = slang_atom_pool_id(atoms, op->a_id);
+               type = slang_type_specifier_type_from_string(name);
+               if (type == slang_spec_void)
+                  RETURN_ERROR2("function not found", name, 0);
+               ti->spec.type = type;
+            }
+         }
+      }
+      break;
+   case slang_oper_field:
+      {
+         slang_assembly_typeinfo _ti;
+
+         if (!slang_assembly_typeinfo_construct(&_ti))
+            RETURN_NIL();
+         if (!_slang_typeof_operation_(op->children, space, &_ti, atoms)) {
+            slang_assembly_typeinfo_destruct(&_ti);
+            RETURN_NIL();
+         }
+         if (_ti.spec.type == slang_spec_struct) {
+            slang_variable *field;
+
+            field = _slang_locate_variable(_ti.spec._struct->fields, op->a_id,
+                                           GL_FALSE);
+            if (field == NULL) {
+               slang_assembly_typeinfo_destruct(&_ti);
+               RETURN_NIL();
+            }
+            if (!slang_type_specifier_copy(&ti->spec, &field->type.specifier)) {
+               slang_assembly_typeinfo_destruct(&_ti);
+               RETURN_NIL();
+            }
+            ti->can_be_referenced = _ti.can_be_referenced;
+         }
+         else {
+            GLuint rows;
+            const char *swizzle;
+            slang_type_specifier_type base;
+
+            /* determine the swizzle of the field expression */
+#if 000
+            if (!_slang_type_is_vector(_ti.spec.type)) {
+               slang_assembly_typeinfo_destruct(&_ti);
+               RETURN_ERROR("Can't swizzle scalar expression", 0);
+            }
+#endif
+            rows = _slang_type_dim(_ti.spec.type);
+            swizzle = slang_atom_pool_id(atoms, op->a_id);
+            if (!_slang_is_swizzle(swizzle, rows, &ti->swz)) {
+               slang_assembly_typeinfo_destruct(&_ti);
+               RETURN_ERROR("Bad swizzle", 0);
+            }
+            ti->is_swizzled = GL_TRUE;
+            ti->can_be_referenced = _ti.can_be_referenced
+               && _slang_is_swizzle_mask(&ti->swz, rows);
+            if (_ti.is_swizzled) {
+               slang_swizzle swz;
+
+               /* swizzle the swizzle */
+               _slang_multiply_swizzles(&swz, &_ti.swz, &ti->swz);
+               ti->swz = swz;
+            }
+            base = _slang_type_base(_ti.spec.type);
+            switch (ti->swz.num_components) {
+            case 1:
+               ti->spec.type = base;
+               break;
+            case 2:
+               switch (base) {
+               case slang_spec_float:
+                  ti->spec.type = slang_spec_vec2;
+                  break;
+               case slang_spec_int:
+                  ti->spec.type = slang_spec_ivec2;
+                  break;
+               case slang_spec_bool:
+                  ti->spec.type = slang_spec_bvec2;
+                  break;
+               default:
+                  break;
+               }
+               break;
+            case 3:
+               switch (base) {
+               case slang_spec_float:
+                  ti->spec.type = slang_spec_vec3;
+                  break;
+               case slang_spec_int:
+                  ti->spec.type = slang_spec_ivec3;
+                  break;
+               case slang_spec_bool:
+                  ti->spec.type = slang_spec_bvec3;
+                  break;
+               default:
+                  break;
+               }
+               break;
+            case 4:
+               switch (base) {
+               case slang_spec_float:
+                  ti->spec.type = slang_spec_vec4;
+                  break;
+               case slang_spec_int:
+                  ti->spec.type = slang_spec_ivec4;
+                  break;
+               case slang_spec_bool:
+                  ti->spec.type = slang_spec_bvec4;
+                  break;
+               default:
+                  break;
+               }
+               break;
+            default:
+               break;
+            }
+         }
+         slang_assembly_typeinfo_destruct(&_ti);
+      }
+      break;
+   case slang_oper_postincrement:
+   case slang_oper_postdecrement:
+      if (!_slang_typeof_operation_(op->children, space, ti, atoms))
+         RETURN_NIL();
+      ti->can_be_referenced = GL_FALSE;
+      ti->is_swizzled = GL_FALSE;
+      break;
+   default:
+      RETURN_NIL();
+   }
+
+   return GL_TRUE;
+}
+
+
+
+/**
+ * Determine the return type of a function.
+ * \param a_name  the function name
+ * \param param  function parameters (overloading)
+ * \param num_params  number of parameters to function
+ * \param space  namespace to search
+ * \param exists  returns GL_TRUE or GL_FALSE to indicate existance of function
+ * \return GL_TRUE for success, GL_FALSE if failure (bad function name)
+ */
+GLboolean
+_slang_typeof_function(slang_atom a_name, const slang_operation * params,
+                       GLuint num_params,
+                       const slang_assembly_name_space * space,
+                       slang_type_specifier * spec, GLboolean * exists,
+                       slang_atom_pool * atoms)
+{
+   slang_function *fun = _slang_locate_function(space->funcs, a_name, params,
+                                                num_params, space, atoms);
+   *exists = fun != NULL;
+   if (!fun)
+      return GL_TRUE;  /* yes, not false */
+   return slang_type_specifier_copy(spec, &fun->header.type.specifier);
+}
+
+
+
+/**
+ * Determine if a type is a matrix.
+ * \return GL_TRUE if is a matrix, GL_FALSE otherwise.
+ */
+GLboolean
+_slang_type_is_matrix(slang_type_specifier_type ty)
+{
+   switch (ty) {
+   case slang_spec_mat2:
+   case slang_spec_mat3:
+   case slang_spec_mat4:
+      return GL_TRUE;
+   default:
+      return GL_FALSE;
+   }
+}
+
+
+/**
+ * Determine if a type is a vector.
+ * \return GL_TRUE if is a vector, GL_FALSE otherwise.
+ */
+GLboolean
+_slang_type_is_vector(slang_type_specifier_type ty)
+{
+   switch (ty) {
+   case slang_spec_vec2:
+   case slang_spec_vec3:
+   case slang_spec_vec4:
+   case slang_spec_ivec2:
+   case slang_spec_ivec3:
+   case slang_spec_ivec4:
+   case slang_spec_bvec2:
+   case slang_spec_bvec3:
+   case slang_spec_bvec4:
+      return GL_TRUE;
+   default:
+      return GL_FALSE;
+   }
+}
+
+
+/**
+ * Given a vector type, return the type of the vector's elements
+ */
+slang_type_specifier_type
+_slang_type_base(slang_type_specifier_type ty)
+{
+   switch (ty) {
+   case slang_spec_float:
+   case slang_spec_vec2:
+   case slang_spec_vec3:
+   case slang_spec_vec4:
+      return slang_spec_float;
+   case slang_spec_int:
+   case slang_spec_ivec2:
+   case slang_spec_ivec3:
+   case slang_spec_ivec4:
+      return slang_spec_int;
+   case slang_spec_bool:
+   case slang_spec_bvec2:
+   case slang_spec_bvec3:
+   case slang_spec_bvec4:
+      return slang_spec_bool;
+   case slang_spec_mat2:
+      return slang_spec_vec2;
+   case slang_spec_mat3:
+      return slang_spec_vec3;
+   case slang_spec_mat4:
+      return slang_spec_vec4;
+   default:
+      return slang_spec_void;
+   }
+}
+
+
+/**
+ * Return the dimensionality of a vector or matrix type.
+ */
+GLuint
+_slang_type_dim(slang_type_specifier_type ty)
+{
+   switch (ty) {
+   case slang_spec_float:
+   case slang_spec_int:
+   case slang_spec_bool:
+      return 1;
+   case slang_spec_vec2:
+   case slang_spec_ivec2:
+   case slang_spec_bvec2:
+   case slang_spec_mat2:
+      return 2;
+   case slang_spec_vec3:
+   case slang_spec_ivec3:
+   case slang_spec_bvec3:
+   case slang_spec_mat3:
+      return 3;
+   case slang_spec_vec4:
+   case slang_spec_ivec4:
+   case slang_spec_bvec4:
+   case slang_spec_mat4:
+      return 4;
+   default:
+      return 0;
+   }
+}