[Mesa-dev] [RFC] ir_reader: Add a pattern matching system and use it everywhere.

Kenneth Graunke kenneth at whitecape.org
Tue Jan 4 14:57:57 PST 2011


Previously, the IR reader was riddled with code that:
1. Checked for the right number of list elements (via a linked list walk)
2. Retrieved references to each component (via ->next->next pointers)
3. Downcasted as necessary to make sure that each sub-component was the
   right type (i.e. symbol, int, list).
4. Checking that the tag (i.e. "declare") was correct.

This was all very ad-hoc and a bit ugly.  Error checking had to be done
at both steps 1, 3, and 4.  Most code didn't even check the tag, relying
on the caller to do so.  Not all callers did.

The new pattern matching module performs the whole process in a single
straightforward function call, resulting in shorter, more readable code.
---

Hello!

This is the first in a series of IR reader cleanups.  Before pushing, I wanted
to make sure that it builds on MSVC.  GCC 4.5 is fine with it, and I think
everything I'm doing is standard (if "clever") C++, but I'm a bit hesitant.

Please let me know if it breaks the build.  Otherwise, I'll probably push this
and the follow-on changes soon.

Thanks,
Kenneth

 src/glsl/ir_reader.cpp    |  482 ++++++++++++++++-----------------------------
 src/glsl/s_expression.cpp |   46 +++++
 src/glsl/s_expression.h   |   39 ++++
 3 files changed, 259 insertions(+), 308 deletions(-)

diff --git a/src/glsl/ir_reader.cpp b/src/glsl/ir_reader.cpp
index 5a718d3..4181dc8 100644
--- a/src/glsl/ir_reader.cpp
+++ b/src/glsl/ir_reader.cpp
@@ -21,8 +21,6 @@
  * DEALINGS IN THE SOFTWARE.
  */
 
-#include <cstdarg>
-
 extern "C" {
 #include <talloc.h>
 }
@@ -43,7 +41,7 @@ static void scan_for_prototypes(_mesa_glsl_parse_state *, exec_list *,
 static ir_function *read_function(_mesa_glsl_parse_state *, s_list *,
 				  bool skip_body);
 static void read_function_sig(_mesa_glsl_parse_state *, ir_function *,
-			      s_list *, bool skip_body);
+			      s_expression *, bool skip_body);
 
 static void read_instructions(_mesa_glsl_parse_state *, exec_list *,
 			      s_expression *, ir_loop *);
@@ -124,47 +122,22 @@ ir_read_error(_mesa_glsl_parse_state *state, s_expression *expr,
 static const glsl_type *
 read_type(_mesa_glsl_parse_state *st, s_expression *expr)
 {
-   s_list *list = SX_AS_LIST(expr);
-   if (list != NULL) {
-      s_symbol *type_sym = SX_AS_SYMBOL(list->subexpressions.get_head());
-      if (type_sym == NULL) {
-	 ir_read_error(st, expr, "expected type (array ...) or (struct ...)");
-	 return NULL;
-      }
-      if (strcmp(type_sym->value(), "array") == 0) {
-	 if (list->length() != 3) {
-	    ir_read_error(st, expr, "expected type (array <type> <int>)");
-	    return NULL;
-	 }
-
-	 // Read base type
-	 s_expression *base_expr = (s_expression*) type_sym->next;
-	 const glsl_type *base_type = read_type(st, base_expr);
-	 if (base_type == NULL) {
-	    ir_read_error(st, NULL, "when reading base type of array");
-	    return NULL;
-	 }
-
-	 // Read array size
-	 s_int *size = SX_AS_INT(base_expr->next);
-	 if (size == NULL) {
-	    ir_read_error(st, expr, "found non-integer array size");
-	    return NULL;
-	 }
+   s_expression *s_base_type;
+   s_int *s_size;
 
-	 return glsl_type::get_array_instance(base_type, size->value());
-      } else if (strcmp(type_sym->value(), "struct") == 0) {
-	 assert(false); // FINISHME
-      } else {
-	 ir_read_error(st, expr, "expected (array ...) or (struct ...); "
-				 "found (%s ...)", type_sym->value());
+   if (MATCH(expr, 3, "array", s_base_type, s_size)) {
+      const glsl_type *base_type = read_type(st, s_base_type);
+      if (base_type == NULL) {
+	 ir_read_error(st, NULL, "when reading base type of array type");
 	 return NULL;
       }
+
+      return glsl_type::get_array_instance(base_type, s_size->value());
    }
    
    s_symbol *type_sym = SX_AS_SYMBOL(expr);
    if (type_sym == NULL) {
-      ir_read_error(st, expr, "expected <type> (symbol or list)");
+      ir_read_error(st, expr, "expected <type>");
       return NULL;
    }
 
@@ -207,17 +180,12 @@ read_function(_mesa_glsl_parse_state *st, s_list *list, bool skip_body)
 {
    void *ctx = st;
    bool added = false;
-   if (list->length() < 3) {
+   s_symbol *name;
+   if (!PARTIAL_MATCH(list, 2, "function", name)) {
       ir_read_error(st, list, "Expected (function <name> (signature ...) ...)");
       return NULL;
    }
 
-   s_symbol *name = SX_AS_SYMBOL(list->subexpressions.head->next);
-   if (name == NULL) {
-      ir_read_error(st, list, "Expected (function <name> ...)");
-      return NULL;
-   }
-
    ir_function *f = st->symbols->get_function(name->value());
    if (f == NULL) {
       f = new(ctx) ir_function(name->value());
@@ -229,46 +197,31 @@ read_function(_mesa_glsl_parse_state *st, s_list *list, bool skip_body)
    it.next(); // skip "function" tag
    it.next(); // skip function name
    for (/* nothing */; it.has_next(); it.next()) {
-      s_list *siglist = SX_AS_LIST(it.get());
-      if (siglist == NULL) {
-	 ir_read_error(st, list, "Expected (function (signature ...) ...)");
-	 return NULL;
-      }
-
-      s_symbol *tag = SX_AS_SYMBOL(siglist->subexpressions.get_head());
-      if (tag == NULL || strcmp(tag->value(), "signature") != 0) {
-	 ir_read_error(st, siglist, "Expected (signature ...)");
-	 return NULL;
-      }
-
-      read_function_sig(st, f, siglist, skip_body);
+      s_expression *s_sig = (s_expression *) it.get();
+      read_function_sig(st, f, s_sig, skip_body);
    }
    return added ? f : NULL;
 }
 
 static void
-read_function_sig(_mesa_glsl_parse_state *st, ir_function *f, s_list *list,
-		  bool skip_body)
+read_function_sig(_mesa_glsl_parse_state *st, ir_function *f,
+		  s_expression *expr, bool skip_body)
 {
    void *ctx = st;
-   if (list->length() != 4) {
-      ir_read_error(st, list, "Expected (signature <type> (parameters ...) "
+   s_expression *type_expr;
+   s_list *paramlist;
+   s_list *body_list;
+
+   if (!MATCH(expr, 4, "signature", type_expr, paramlist, body_list)) {
+      ir_read_error(st, expr, "Expected (signature <type> (parameters ...) "
 			      "(<instruction> ...))");
       return;
    }
 
-   s_expression *type_expr = (s_expression*) list->subexpressions.head->next;
    const glsl_type *return_type = read_type(st, type_expr);
    if (return_type == NULL)
       return;
 
-   s_list *paramlist = SX_AS_LIST(type_expr->next);
-   s_list *body_list = SX_AS_LIST(type_expr->next->next);
-   if (paramlist == NULL || body_list == NULL) {
-      ir_read_error(st, list, "Expected (signature <type> (parameters ...) "
-			      "(<instruction> ...))");
-      return;
-   }
    s_symbol *paramtag = SX_AS_SYMBOL(paramlist->subexpressions.get_head());
    if (paramtag == NULL || strcmp(paramtag->value(), "parameters") != 0) {
       ir_read_error(st, paramlist, "Expected (parameters ...)");
@@ -298,13 +251,13 @@ read_function_sig(_mesa_glsl_parse_state *st, ir_function *f, s_list *list,
    } else if (sig != NULL) {
       const char *badvar = sig->qualifiers_match(&hir_parameters);
       if (badvar != NULL) {
-	 ir_read_error(st, list, "function `%s' parameter `%s' qualifiers "
+	 ir_read_error(st, expr, "function `%s' parameter `%s' qualifiers "
 		       "don't match prototype", f->name, badvar);
 	 return;
       }
 
       if (sig->return_type != return_type) {
-	 ir_read_error(st, list, "function `%s' return type doesn't "
+	 ir_read_error(st, expr, "function `%s' return type doesn't "
 		       "match prototype", f->name);
 	 return;
       }
@@ -319,7 +272,7 @@ read_function_sig(_mesa_glsl_parse_state *st, ir_function *f, s_list *list,
 
    if (!skip_body && !body_list->subexpressions.is_empty()) {
       if (sig->is_defined) {
-	 ir_read_error(st, list, "function %s redefined", f->name);
+	 ir_read_error(st, expr, "function %s redefined", f->name);
 	 return;
       }
       st->current_function = sig;
@@ -406,42 +359,28 @@ read_instruction(_mesa_glsl_parse_state *st, s_expression *expr,
    return inst;
 }
 
-
 static ir_variable *
 read_declaration(_mesa_glsl_parse_state *st, s_list *list)
 {
-   void *ctx = st;
-   if (list->length() != 4) {
+   s_list *s_quals;
+   s_expression *s_type;
+   s_symbol *s_name;
+   if (!MATCH(list, 4, "declare", s_quals, s_type, s_name)) {
       ir_read_error(st, list, "expected (declare (<qualifiers>) <type> "
 			      "<name>)");
       return NULL;
    }
 
-   s_list *quals = SX_AS_LIST(list->subexpressions.head->next);
-   if (quals == NULL) {
-      ir_read_error(st, list, "expected a list of variable qualifiers");
-      return NULL;
-   }
-
-   s_expression *type_expr = (s_expression*) quals->next;
-   const glsl_type *type = read_type(st, type_expr);
+   const glsl_type *type = read_type(st, s_type);
    if (type == NULL)
       return NULL;
 
-   s_symbol *var_name = SX_AS_SYMBOL(type_expr->next);
-   if (var_name == NULL) {
-      ir_read_error(st, list, "expected variable name, found non-symbol");
-      return NULL;
-   }
-
-   ir_variable *var = new(ctx) ir_variable(type, var_name->value(),
-					   ir_var_auto);
+   ir_variable *var = new(st) ir_variable(type, s_name->value(), ir_var_auto);
 
-   foreach_iter(exec_list_iterator, it, quals->subexpressions) {
+   foreach_iter(exec_list_iterator, it, s_quals->subexpressions) {
       s_symbol *qualifier = SX_AS_SYMBOL(it.get());
       if (qualifier == NULL) {
 	 ir_read_error(st, list, "qualifier list must contain only symbols");
-	 delete var;
 	 return NULL;
       }
 
@@ -468,7 +407,6 @@ read_declaration(_mesa_glsl_parse_state *st, s_list *list)
 	 var->interpolation = ir_var_noperspective;
       } else {
 	 ir_read_error(st, list, "unknown qualifier: %s", qualifier->value());
-	 delete var;
 	 return NULL;
       }
    }
@@ -483,27 +421,25 @@ read_declaration(_mesa_glsl_parse_state *st, s_list *list)
 static ir_if *
 read_if(_mesa_glsl_parse_state *st, s_list *list, ir_loop *loop_ctx)
 {
-   void *ctx = st;
-   if (list->length() != 4) {
+   s_expression *s_cond;
+   s_expression *s_then;
+   s_expression *s_else;
+   if (!MATCH(list, 4, "if", s_cond, s_then, s_else)) {
       ir_read_error(st, list, "expected (if <condition> (<then> ...) "
-                          "(<else> ...))");
+			      "(<else> ...))");
       return NULL;
    }
 
-   s_expression *cond_expr = (s_expression*) list->subexpressions.head->next;
-   ir_rvalue *condition = read_rvalue(st, cond_expr);
+   ir_rvalue *condition = read_rvalue(st, s_cond);
    if (condition == NULL) {
       ir_read_error(st, NULL, "when reading condition of (if ...)");
       return NULL;
    }
 
-   s_expression *then_expr = (s_expression*) cond_expr->next;
-   s_expression *else_expr = (s_expression*) then_expr->next;
-
-   ir_if *iff = new(ctx) ir_if(condition);
+   ir_if *iff = new(st) ir_if(condition);
 
-   read_instructions(st, &iff->then_instructions, then_expr, loop_ctx);
-   read_instructions(st, &iff->else_instructions, else_expr, loop_ctx);
+   read_instructions(st, &iff->then_instructions, s_then, loop_ctx);
+   read_instructions(st, &iff->else_instructions, s_else, loop_ctx);
    if (st->error) {
       delete iff;
       iff = NULL;
@@ -515,23 +451,17 @@ read_if(_mesa_glsl_parse_state *st, s_list *list, ir_loop *loop_ctx)
 static ir_loop *
 read_loop(_mesa_glsl_parse_state *st, s_list *list)
 {
-   void *ctx = st;
-   if (list->length() != 6) {
+   s_expression *s_counter, *s_from, *s_to, *s_inc, *s_body;
+   if (!MATCH(list, 6, "loop", s_counter, s_from, s_to, s_inc, s_body)) {
       ir_read_error(st, list, "expected (loop <counter> <from> <to> "
 			      "<increment> <body>)");
       return NULL;
    }
 
-   s_expression *count_expr = (s_expression*) list->subexpressions.head->next;
-   s_expression *from_expr  = (s_expression*) count_expr->next;
-   s_expression *to_expr    = (s_expression*) from_expr->next;
-   s_expression *inc_expr   = (s_expression*) to_expr->next;
-   s_expression *body_expr  = (s_expression*) inc_expr->next;
-
    // FINISHME: actually read the count/from/to fields.
 
-   ir_loop *loop = new(ctx) ir_loop;
-   read_instructions(st, &loop->body_instructions, body_expr, loop);
+   ir_loop *loop = new(st) ir_loop;
+   read_instructions(st, &loop->body_instructions, s_body, loop);
    if (st->error) {
       delete loop;
       loop = NULL;
@@ -543,21 +473,19 @@ read_loop(_mesa_glsl_parse_state *st, s_list *list)
 static ir_return *
 read_return(_mesa_glsl_parse_state *st, s_list *list)
 {
-   void *ctx = st;
-   if (list->length() != 2) {
+   s_expression *expr;
+   if (!MATCH(list, 2, "return", expr)) {
       ir_read_error(st, list, "expected (return <rvalue>)");
       return NULL;
    }
 
-   s_expression *expr = (s_expression*) list->subexpressions.head->next;
-
    ir_rvalue *retval = read_rvalue(st, expr);
    if (retval == NULL) {
       ir_read_error(st, NULL, "when reading return value");
       return NULL;
    }
 
-   return new(ctx) ir_return(retval);
+   return new(st) ir_return(retval);
 }
 
 
@@ -597,37 +525,25 @@ read_rvalue(_mesa_glsl_parse_state *st, s_expression *expr)
 static ir_assignment *
 read_assignment(_mesa_glsl_parse_state *st, s_list *list)
 {
-   void *ctx = st;
-   if (list->length() != 5) {
+   s_expression *cond_expr, *lhs_expr, *rhs_expr;
+   s_list       *mask_list;
+
+   if (!MATCH(list, 5, "assign", cond_expr, mask_list, lhs_expr, rhs_expr)) {
       ir_read_error(st, list, "expected (assign <condition> (<write mask>) "
 			      "<lhs> <rhs>)");
       return NULL;
    }
 
-   s_expression *cond_expr = (s_expression*) list->subexpressions.head->next;
-   s_list       *mask_list = SX_AS_LIST(cond_expr->next);
-   s_expression *lhs_expr  = (s_expression*) cond_expr->next->next;
-   s_expression *rhs_expr  = (s_expression*) lhs_expr->next;
-
    ir_rvalue *condition = read_rvalue(st, cond_expr);
    if (condition == NULL) {
       ir_read_error(st, NULL, "when reading condition of assignment");
       return NULL;
    }
 
-   if (mask_list == NULL || mask_list->length() > 1) {
-      ir_read_error(st, mask_list, "expected () or (<write mask>)");
-      return NULL;
-   }
-
    unsigned mask = 0;
-   if (mask_list->length() == 1) {
-      s_symbol *mask_symbol = SX_AS_SYMBOL(mask_list->subexpressions.head);
-      if (mask_symbol == NULL) {
-	 ir_read_error(st, list, "expected a write mask; found non-symbol");
-	 return NULL;
-      }
 
+   s_symbol *mask_symbol;
+   if (MATCH(mask_list, 1, mask_symbol)) {
       const char *mask_str = mask_symbol->value();
       unsigned mask_length = strlen(mask_str);
       if (mask_length > 4) {
@@ -645,6 +561,9 @@ read_assignment(_mesa_glsl_parse_state *st, s_list *list)
 	 }
 	 mask |= 1 << idx_map[mask_str[i] - 'w'];
       }
+   } else if (!mask_list->subexpressions.is_empty()) {
+      ir_read_error(st, mask_list, "expected () or (<write mask>)");
+      return NULL;
    }
 
    ir_dereference *lhs = read_dereference(st, lhs_expr);
@@ -664,21 +583,17 @@ read_assignment(_mesa_glsl_parse_state *st, s_list *list)
       return NULL;
    }
 
-   return new(ctx) ir_assignment(lhs, rhs, condition, mask);
+   return new(st) ir_assignment(lhs, rhs, condition, mask);
 }
 
 static ir_call *
 read_call(_mesa_glsl_parse_state *st, s_list *list)
 {
    void *ctx = st;
-   if (list->length() != 3) {
-      ir_read_error(st, list, "expected (call <name> (<param> ...))");
-      return NULL;
-   }
+   s_symbol *name;
+   s_list *params;
 
-   s_symbol *name = SX_AS_SYMBOL(list->subexpressions.head->next);
-   s_list *params = SX_AS_LIST(list->subexpressions.head->next->next);
-   if (name == NULL || params == NULL) {
+   if (!MATCH(list, 3, "call", name, params)) {
       ir_read_error(st, list, "expected (call <name> (<param> ...))");
       return NULL;
    }
@@ -716,61 +631,52 @@ static ir_expression *
 read_expression(_mesa_glsl_parse_state *st, s_list *list)
 {
    void *ctx = st;
-   const unsigned list_length = list->length();
-   if (list_length < 4) {
+   s_expression *s_type;
+   s_symbol *s_op;
+   s_expression *s_arg1;
+   if (!PARTIAL_MATCH(list, 4, "expression", s_type, s_op, s_arg1)) {
       ir_read_error(st, list, "expected (expression <type> <operator> "
 			      "<operand> [<operand>])");
       return NULL;
    }
+   s_expression *s_arg2 = (s_expression *) s_arg1->next; // may be tail sentinel
 
-   s_expression *type_expr = (s_expression*) list->subexpressions.head->next;
-   const glsl_type *type = read_type(st, type_expr);
+   const glsl_type *type = read_type(st, s_type);
    if (type == NULL)
       return NULL;
 
    /* Read the operator */
-   s_symbol *op_sym = SX_AS_SYMBOL(type_expr->next);
-   if (op_sym == NULL) {
-      ir_read_error(st, list, "expected operator, found non-symbol");
-      return NULL;
-   }
-
-   ir_expression_operation op = ir_expression::get_operator(op_sym->value());
+   ir_expression_operation op = ir_expression::get_operator(s_op->value());
    if (op == (ir_expression_operation) -1) {
-      ir_read_error(st, list, "invalid operator: %s", op_sym->value());
+      ir_read_error(st, list, "invalid operator: %s", s_op->value());
       return NULL;
    }
     
-   /* Now that we know the operator, check for the right number of operands */ 
-   if (ir_expression::get_num_operands(op) == 2) {
-      if (list_length != 5) {
-	 ir_read_error(st, list, "expected (expression <type> %s <operand> "
-				 " <operand>)", op_sym->value());
-	 return NULL;
-      }
-   } else {
-      if (list_length != 4) {
-	 ir_read_error(st, list, "expected (expression <type> %s <operand>)",
-		       op_sym->value());
-	 return NULL;
-      }
+   unsigned num_operands = ir_expression::get_num_operands(op);
+   if (num_operands == 1 && !s_arg1->next->is_tail_sentinel()) {
+      ir_read_error(st, list, "expected (expression <type> %s <operand>)",
+		    s_op->value());
+      return NULL;
    }
 
-   s_expression *exp1 = (s_expression*) (op_sym->next);
-   ir_rvalue *arg1 = read_rvalue(st, exp1);
+   ir_rvalue *arg1 = read_rvalue(st, s_arg1);
+   ir_rvalue *arg2 = NULL;
    if (arg1 == NULL) {
       ir_read_error(st, NULL, "when reading first operand of %s",
-		    op_sym->value());
+		    s_op->value());
       return NULL;
    }
 
-   ir_rvalue *arg2 = NULL;
-   if (ir_expression::get_num_operands(op) == 2) {
-      s_expression *exp2 = (s_expression*) (exp1->next);
-      arg2 = read_rvalue(st, exp2);
+   if (num_operands == 2) {
+      if (s_arg2->is_tail_sentinel() || !s_arg2->next->is_tail_sentinel()) {
+	 ir_read_error(st, list, "expected (expression <type> %s <operand> "
+				 "<operand>)", s_op->value());
+	 return NULL;
+      }
+      arg2 = read_rvalue(st, s_arg2);
       if (arg2 == NULL) {
 	 ir_read_error(st, NULL, "when reading second operand of %s",
-		       op_sym->value());
+		       s_op->value());
 	 return NULL;
       }
    }
@@ -781,24 +687,19 @@ read_expression(_mesa_glsl_parse_state *st, s_list *list)
 static ir_swizzle *
 read_swizzle(_mesa_glsl_parse_state *st, s_list *list)
 {
-   if (list->length() != 3) {
+   s_symbol *swiz;
+   s_expression *sub;
+   if (!MATCH(list, 3, "swiz", swiz, sub)) {
       ir_read_error(st, list, "expected (swiz <swizzle> <rvalue>)");
       return NULL;
    }
 
-   s_symbol *swiz = SX_AS_SYMBOL(list->subexpressions.head->next);
-   if (swiz == NULL) {
-      ir_read_error(st, list, "expected a valid swizzle; found non-symbol");
-      return NULL;
-   }
-
    if (strlen(swiz->value()) > 4) {
       ir_read_error(st, list, "expected a valid swizzle; found %s",
 		    swiz->value());
       return NULL;
    }
 
-   s_expression *sub = (s_expression*) swiz->next;
    ir_rvalue *rvalue = read_rvalue(st, sub);
    if (rvalue == NULL)
       return NULL;
@@ -815,17 +716,17 @@ static ir_constant *
 read_constant(_mesa_glsl_parse_state *st, s_list *list)
 {
    void *ctx = st;
-   if (list->length() != 3) {
+   s_expression *type_expr;
+   s_list *values;
+   if (!MATCH(list, 3, "constant", type_expr, values)) {
       ir_read_error(st, list, "expected (constant <type> (...))");
       return NULL;
    }
 
-   s_expression *type_expr = (s_expression*) list->subexpressions.head->next;
    const glsl_type *type = read_type(st, type_expr);
    if (type == NULL)
       return NULL;
 
-   s_list *values = SX_AS_LIST(type_expr->next);
    if (values == NULL) {
       ir_read_error(st, list, "expected (constant <type> (...))");
       return NULL;
@@ -931,12 +832,8 @@ static ir_dereference_variable *
 read_var_ref(_mesa_glsl_parse_state *st, s_list *list)
 {
    void *ctx = st;
-   if (list->length() != 2) {
-      ir_read_error(st, list, "expected (var_ref <variable name>)");
-      return NULL;
-   }
-   s_symbol *var_name = SX_AS_SYMBOL(list->subexpressions.head->next);
-   if (var_name == NULL) {
+   s_symbol *var_name;
+   if (!MATCH(list, 2, "var_ref", var_name)) {
       ir_read_error(st, list, "expected (var_ref <variable name>)");
       return NULL;
    }
@@ -954,19 +851,19 @@ static ir_dereference_array *
 read_array_ref(_mesa_glsl_parse_state *st, s_list *list)
 {
    void *ctx = st;
-   if (list->length() != 3) {
+   s_expression *subj_expr;
+   s_expression *idx_expr;
+   if (!MATCH(list, 3, "array_ref", subj_expr, idx_expr)) {
       ir_read_error(st, list, "expected (array_ref <rvalue> <index>)");
       return NULL;
    }
 
-   s_expression *subj_expr = (s_expression*) list->subexpressions.head->next;
    ir_rvalue *subject = read_rvalue(st, subj_expr);
    if (subject == NULL) {
       ir_read_error(st, NULL, "when reading the subject of an array_ref");
       return NULL;
    }
 
-   s_expression *idx_expr = (s_expression*) subj_expr->next;
    ir_rvalue *idx = read_rvalue(st, idx_expr);
    return new(ctx) ir_dereference_array(subject, idx);
 }
@@ -975,169 +872,138 @@ static ir_dereference_record *
 read_record_ref(_mesa_glsl_parse_state *st, s_list *list)
 {
    void *ctx = st;
-   if (list->length() != 3) {
+   s_expression *subj_expr;
+   s_symbol *field;
+   if (!MATCH(list, 3, "record_ref", subj_expr, field)) {
       ir_read_error(st, list, "expected (record_ref <rvalue> <field>)");
       return NULL;
    }
 
-   s_expression *subj_expr = (s_expression*) list->subexpressions.head->next;
    ir_rvalue *subject = read_rvalue(st, subj_expr);
    if (subject == NULL) {
       ir_read_error(st, NULL, "when reading the subject of a record_ref");
       return NULL;
    }
-
-   s_symbol *field = SX_AS_SYMBOL(subj_expr->next);
-   if (field == NULL) {
-      ir_read_error(st, list, "expected (record_ref ... <field name>)");
-      return NULL;
-   }
    return new(ctx) ir_dereference_record(subject, field->value());
 }
 
-static bool
-valid_texture_list_length(ir_texture_opcode op, s_list *list)
-{
-   unsigned required_length = 7;
-   if (op == ir_txf)
-      required_length = 5;
-   else if (op == ir_tex)
-      required_length = 6;
-
-   return list->length() == required_length;
-}
-
 static ir_texture *
 read_texture(_mesa_glsl_parse_state *st, s_list *list)
 {
-   void *ctx = st;
-   s_symbol *tag = SX_AS_SYMBOL(list->subexpressions.head);
-   assert(tag != NULL);
-
-   ir_texture_opcode op = ir_texture::get_opcode(tag->value());
-   if (op == (ir_texture_opcode) -1)
-      return NULL;
-
-   if (!valid_texture_list_length(op, list)) {
-      ir_read_error(st, NULL, "invalid list size in (%s ...)", tag->value());
-      return NULL;
+   s_symbol *tag = NULL;
+   s_expression *s_sampler = NULL;
+   s_expression *s_coord = NULL;
+   s_list *s_offsets = NULL;
+   s_expression *s_proj = NULL;
+   s_list *s_shadow = NULL;
+   s_expression *s_lod = NULL;
+
+   ir_texture_opcode op;
+
+   if (MATCH(list, 6, "tex", s_sampler, s_coord, s_offsets, s_proj, s_shadow)) {
+      op = ir_tex;
+   } else if (MATCH(list, 5, "txf", s_sampler, s_coord, s_offsets, s_lod)) {
+      op = ir_txf;
+   } else if (MATCH(list, 7, tag, s_sampler, s_coord, s_offsets, s_proj, s_shadow, s_lod)) {
+      op = ir_texture::get_opcode(tag->value());
+      if (op == -1)
+	 return NULL;
    }
 
-   ir_texture *tex = new(ctx) ir_texture(op);
+   ir_texture *tex = new(st) ir_texture(op);
 
    // Read sampler (must be a deref)
-   s_expression *sampler_expr = (s_expression *) tag->next;
-   ir_dereference *sampler = read_dereference(st, sampler_expr);
+   ir_dereference *sampler = read_dereference(st, s_sampler);
    if (sampler == NULL) {
-      ir_read_error(st, NULL, "when reading sampler in (%s ...)", tag->value());
+      ir_read_error(st, NULL, "when reading sampler in (%s ...)",
+		    tex->opcode_string());
       return NULL;
    }
    tex->set_sampler(sampler);
 
    // Read coordinate (any rvalue)
-   s_expression *coordinate_expr = (s_expression *) sampler_expr->next;
-   tex->coordinate = read_rvalue(st, coordinate_expr);
+   tex->coordinate = read_rvalue(st, s_coord);
    if (tex->coordinate == NULL) {
       ir_read_error(st, NULL, "when reading coordinate in (%s ...)",
-		    tag->value());
+		    tex->opcode_string());
       return NULL;
    }
 
    // Read texel offset, i.e. (0 0 0)
-   s_list *offset_list = SX_AS_LIST(coordinate_expr->next);
-   if (offset_list == NULL || offset_list->length() != 3) {
-      ir_read_error(st, offset_list, "expected (<int> <int> <int>)");
-      return NULL;
-   }
-   s_int *offset_x = SX_AS_INT(offset_list->subexpressions.head);
-   s_int *offset_y = SX_AS_INT(offset_list->subexpressions.head->next);
-   s_int *offset_z = SX_AS_INT(offset_list->subexpressions.head->next->next);
-   if (offset_x == NULL || offset_y == NULL || offset_z == NULL) {
-      ir_read_error(st, offset_list, "expected (<int> <int> <int>)");
+   s_int *offset_x;
+   s_int *offset_y;
+   s_int *offset_z;
+   if (!MATCH(s_offsets, 3, offset_x, offset_y, offset_z)) {
+      ir_read_error(st, s_offsets, "expected (<int> <int> <int>)");
       return NULL;
    }
    tex->offsets[0] = offset_x->value();
    tex->offsets[1] = offset_y->value();
    tex->offsets[2] = offset_z->value();
 
-   if (op == ir_txf) {
-      s_expression *lod_expr = (s_expression *) offset_list->next;
-      tex->lod_info.lod = read_rvalue(st, lod_expr);
-      if (tex->lod_info.lod == NULL) {
-	 ir_read_error(st, NULL, "when reading LOD in (txf ...)");
-	 return NULL;
-      }
-   } else {
-      s_expression *proj_expr = (s_expression *) offset_list->next;
-      s_int *proj_as_int = SX_AS_INT(proj_expr);
+   if (op != ir_txf) {
+      s_int *proj_as_int = SX_AS_INT(s_proj);
       if (proj_as_int && proj_as_int->value() == 1) {
 	 tex->projector = NULL;
       } else {
-	 tex->projector = read_rvalue(st, proj_expr);
+	 tex->projector = read_rvalue(st, s_proj);
 	 if (tex->projector == NULL) {
 	    ir_read_error(st, NULL, "when reading projective divide in (%s ..)",
-	                  tag->value());
+	                  tex->opcode_string());
 	    return NULL;
 	 }
       }
 
-      s_list *shadow_list = SX_AS_LIST(proj_expr->next);
-      if (shadow_list == NULL) {
-	 ir_read_error(st, NULL, "shadow comparitor must be a list");
-	 return NULL;
-      }
-      if (shadow_list->subexpressions.is_empty()) {
-	 tex->shadow_comparitor= NULL;
+      if (s_shadow->subexpressions.is_empty()) {
+	 tex->shadow_comparitor = NULL;
       } else {
-	 tex->shadow_comparitor = read_rvalue(st, shadow_list);
+	 tex->shadow_comparitor = read_rvalue(st, s_shadow);
 	 if (tex->shadow_comparitor == NULL) {
 	    ir_read_error(st, NULL, "when reading shadow comparitor in (%s ..)",
-			  tag->value());
+			  tex->opcode_string());
 	    return NULL;
 	 }
       }
-      s_expression *lod_expr = (s_expression *) shadow_list->next;
-
-      switch (op) {
-      case ir_txb:
-	 tex->lod_info.bias = read_rvalue(st, lod_expr);
-	 if (tex->lod_info.bias == NULL) {
-	    ir_read_error(st, NULL, "when reading LOD bias in (txb ...)");
-	    return NULL;
-	 }
-	 break;
-      case ir_txl:
-	 tex->lod_info.lod = read_rvalue(st, lod_expr);
-	 if (tex->lod_info.lod == NULL) {
-	    ir_read_error(st, NULL, "when reading LOD in (txl ...)");
-	    return NULL;
-	 }
-	 break;
-      case ir_txd: {
-	 s_list *lod_list = SX_AS_LIST(lod_expr);
-	 if (lod_list->length() != 2) {
-	    ir_read_error(st, lod_expr, "expected (dPdx dPdy) in (txd ...)");
-	    return NULL;
-	 }
-	 s_expression *dx_expr = (s_expression *) lod_list->subexpressions.head;
-	 s_expression *dy_expr = (s_expression *) dx_expr->next;
+   }
 
-	 tex->lod_info.grad.dPdx = read_rvalue(st, dx_expr);
-	 if (tex->lod_info.grad.dPdx == NULL) {
-	    ir_read_error(st, NULL, "when reading dPdx in (txd ...)");
-	    return NULL;
-	 }
-	 tex->lod_info.grad.dPdy = read_rvalue(st, dy_expr);
-	 if (tex->lod_info.grad.dPdy == NULL) {
-	    ir_read_error(st, NULL, "when reading dPdy in (txd ...)");
-	    return NULL;
-	 }
-	 break;
+   switch (op) {
+   case ir_txb:
+      tex->lod_info.bias = read_rvalue(st, s_lod);
+      if (tex->lod_info.bias == NULL) {
+	 ir_read_error(st, NULL, "when reading LOD bias in (txb ...)");
+	 return NULL;
+      }
+      break;
+   case ir_txl:
+   case ir_txf:
+      tex->lod_info.lod = read_rvalue(st, s_lod);
+      if (tex->lod_info.lod == NULL) {
+	 ir_read_error(st, NULL, "when reading LOD in (%s ...)",
+		       tex->opcode_string());
+	 return NULL;
+      }
+      break;
+   case ir_txd: {
+      s_expression *s_dx, *s_dy;
+      if (!MATCH(s_lod, 2, s_dx, s_dy)) {
+	 ir_read_error(st, s_lod, "expected (dPdx dPdy) in (txd ...)");
+	 return NULL;
+      }
+      tex->lod_info.grad.dPdx = read_rvalue(st, s_dx);
+      if (tex->lod_info.grad.dPdx == NULL) {
+	 ir_read_error(st, NULL, "when reading dPdx in (txd ...)");
+	 return NULL;
+      }
+      tex->lod_info.grad.dPdy = read_rvalue(st, s_dy);
+      if (tex->lod_info.grad.dPdy == NULL) {
+	 ir_read_error(st, NULL, "when reading dPdy in (txd ...)");
+	 return NULL;
       }
-      default:
-	 // tex doesn't have any extra parameters and txf was handled earlier.
-	 break;
-      };
+      break;
    }
+   default:
+      // tex doesn't have any extra parameters.
+      break;
+   };
    return tex;
 }
diff --git a/src/glsl/s_expression.cpp b/src/glsl/s_expression.cpp
index e420cd6..a00bfa7 100644
--- a/src/glsl/s_expression.cpp
+++ b/src/glsl/s_expression.cpp
@@ -139,3 +139,49 @@ void s_list::print()
    printf(")");
 }
 
+// --------------------------------------------------
+
+bool
+s_pattern::match(s_expression *expr)
+{
+   switch (type)
+   {
+   case EXPR:   *p_expr = expr; break;
+   case LIST:   if (expr->is_list())   *p_list   = (s_list *)   expr; break;
+   case SYMBOL: if (expr->is_symbol()) *p_symbol = (s_symbol *) expr; break;
+   case NUMBER: if (expr->is_number()) *p_number = (s_number *) expr; break;
+   case INT:    if (expr->is_int())    *p_int    = (s_int *)    expr; break;
+   case STRING:
+      s_symbol *sym = SX_AS_SYMBOL(expr);
+      if (sym != NULL && strcmp(sym->value(), literal) == 0)
+	 return true;
+      return false;
+   };
+
+   return *p_expr == expr;
+}
+
+bool
+s_match(s_expression *top, unsigned n, s_pattern *pattern, bool partial)
+{
+   s_list *list = SX_AS_LIST(top);
+   if (list == NULL)
+      return false;
+
+   unsigned i = 0;
+   foreach_iter(exec_list_iterator, it, list->subexpressions) {
+      if (i >= n)
+	 return partial; /* More actual items than the pattern expected */
+
+      s_expression *expr = (s_expression *) it.get();
+      if (expr == NULL || !pattern[i].match(expr))
+	 return false;
+
+      i++;
+   }
+
+   if (i < n)
+      return false; /* Less actual items than the pattern expected */
+
+   return true;
+}
diff --git a/src/glsl/s_expression.h b/src/glsl/s_expression.h
index 29d800e..5720c9c 100644
--- a/src/glsl/s_expression.h
+++ b/src/glsl/s_expression.h
@@ -29,6 +29,7 @@
 #include "strtod.h"
 #include "list.h"
 
+/* Type-safe downcasting macros (also safe to pass NULL) */
 #define SX_AS_(t,x) ((x) && ((s_expression*) x)->is_##t()) ? ((s_##t*) (x)) \
                                                            : NULL
 #define SX_AS_LIST(x)   SX_AS_(list, x)
@@ -36,6 +37,12 @@
 #define SX_AS_NUMBER(x) SX_AS_(number, x)
 #define SX_AS_INT(x)    SX_AS_(int, x)
 
+/* Pattern matching macros */
+#define MATCH(list, n, ...) \
+   s_match(list, n, (s_pattern[]){__VA_ARGS__}, false)
+#define PARTIAL_MATCH(list, n, ...) \
+   s_match(list, n, (s_pattern[]){__VA_ARGS__}, true)
+
 /* For our purposes, S-Expressions are:
  * - <int>
  * - <float>
@@ -140,4 +147,36 @@ public:
    exec_list subexpressions;
 };
 
+// ------------------------------------------------------------
+
+/**
+ * Part of a pattern to match - essentially a record holding a pointer to the
+ * storage for the component to match, along with the appropriate type.
+ */
+class s_pattern {
+public:
+   s_pattern(s_expression *&s) : p_expr(&s),   type(EXPR)   { }
+   s_pattern(s_list       *&s) : p_list(&s),   type(LIST)   { }
+   s_pattern(s_symbol     *&s) : p_symbol(&s), type(SYMBOL) { }
+   s_pattern(s_number     *&s) : p_number(&s), type(NUMBER) { }
+   s_pattern(s_int        *&s) : p_int(&s),    type(INT)    { }
+   s_pattern(const char *str)  : literal(str), type(STRING) { }
+
+   bool match(s_expression *expr);
+
+private:
+   union {
+      s_expression **p_expr;
+      s_list       **p_list;
+      s_symbol     **p_symbol;
+      s_number     **p_number;
+      s_int        **p_int;
+      const char *literal;
+   };
+   enum { EXPR, LIST, SYMBOL, NUMBER, INT, STRING } type;
+};
+
+bool
+s_match(s_expression *top, unsigned n, s_pattern *pattern, bool partial);
+
 #endif /* S_EXPRESSION_H */
-- 
1.7.3.4



More information about the mesa-dev mailing list