[Piglit] [PATCH 1/8] shader_runner: Rework script parser utils.

Francisco Jerez currojerez at riseup.net
Tue Oct 18 23:17:39 UTC 2016


This introduces a collection of text parsing primitives meant to
replace the current helper functions from parser_utils.h and other
manual string manipulation done in shader_runner.c.  The design
principles are:

 - Make things easier for the caller to handle failure.  Some of the
   current parser code doesn't handle failure at all (e.g. the whole
   get_* helpers), which will cause the script to misbehave silently
   in presence of a syntax error.  Other parser functions
   (e.g. lookup_enum_string or process_comparison) kill the piglit
   test with failure status when the input cannot be parsed, which
   prevents the caller from handling parse errors gracefully in cases
   where failure is expected (e.g. because a symbol has multiple
   alternative production rules).

   All parser primitives introduced here return a boolean result that
   represents whether the symbol could be parsed from the input
   string.  The REQUIRE() macro is provided to encourage printing
   informative diagnostic messages in error conditions.

 - Allow composition of parser primitives to form more complex parsers
   with similar high-level behavior.  Chained parsers can be
   represented easily using the '&&' operator.  E.g.:

   | return parse_str(line, "foo", &rest) &&
   |        parse_bar(rest, &bar, &rest);

   will parse "foo" followed by a 'bar' value and leave 'rest'
   pointing at the following text to allow subsequent composition [the
   parser utils currently in use would require hard-coding the
   character length of the first symbol, which is error-prone and can
   get hairy if the length of the symbol is not known beforehand].

   Look-ahead can be achieved by passing NULL as last argument which
   will simply check whether the symbol can be parsed without updating
   the current parse location.  E.g. to check whether a symbol starts
   with 'GL_' and then parse it as a GL enum:

   | return parse_str(line, "GL_", NULL) &&
   |        parse_enum_gl(line, &e, &rest);

 - Allow parsing of slightly more complex grammars where a single
   non-terminal symbol can have multiple alternative production rules.

   Selection of one of the alternatives can be represented succinctly
   by combining multiple parsers with the '||' operator (the result
   will be left-biased in presence of ambiguity due to the
   short-circuit rule of C boolean operators).  E.g. to parse a string
   of the form '<name-string> <pname-enum> <type-enum or integer>' you
   can do something along the lines of:

   | return parse_word_copy(line, name, sizeof(name), &rest) &&
   |        parse_enum_tab(all_pnames, rest, &pname, &rest) &&
   |        (parse_enum_tab(all_types, rest, &expected, &rest) ||
   |         parse_int(rest, &expected, &rest));

See the doxygen documentation below for more details.
---
 tests/shaders/parser_utils.c | 66 ++++++++++++++++++++++++++++++++++-
 tests/shaders/parser_utils.h | 83 +++++++++++++++++++++++++++++++++++++++++++-
 2 files changed, 147 insertions(+), 2 deletions(-)

diff --git a/tests/shaders/parser_utils.c b/tests/shaders/parser_utils.c
index e534a4c..470144e 100644
--- a/tests/shaders/parser_utils.c
+++ b/tests/shaders/parser_utils.c
@@ -1,5 +1,5 @@
 /*
- * Copyright © 2010 Intel Corporation
+ * Copyright © 2010-2016 Intel Corporation
  *
  * Permission is hereby granted, free of charge, to any person obtaining a
  * copy of this software and associated documentation files (the "Software"),
@@ -24,6 +24,70 @@
 #include <ctype.h>
 #include "parser_utils.h"
 
+bool
+parse_whitespace(const char *s, const char **rest)
+{
+	const char *end = s;
+	for (; *end && *end != '\n' && isspace(*end); end++);
+
+	if (rest)
+		*rest = end;
+
+	return end != s;
+}
+
+bool
+parse_str(const char *s, const char *lit, const char **rest)
+{
+	const char *t;
+	parse_whitespace(s, &t);
+	const bool ret = strncmp(t, lit, strlen(lit)) == 0;
+
+	if (rest)
+		*rest = (ret ? t + strlen(lit) : s);
+
+	return ret;
+}
+
+bool
+parse_word(const char *s, const char **t, const char **rest)
+{
+	parse_whitespace(s, t);
+
+	const char *end = *t;
+	for (; *end && !isspace(*end); end++);
+
+	if (rest)
+		*rest = (*t != end ? end : s);
+
+	return *t != end;
+}
+
+bool
+parse_word_copy(const char *s, char *t, unsigned n, const char **rest)
+{
+	const char *start, *end;
+	const bool ret = parse_word(s, &start, &end) && end - start < n;
+
+	if (ret) {
+		memcpy(t, start, end - start);
+		t[end - start] = 0;
+	}
+	if (rest)
+		*rest = (ret ? end : s);
+
+	return ret;
+}
+
+bool
+parse_enum_gl(const char *s, GLenum *e, const char **rest)
+{
+	char name[512];
+	const bool ret = parse_word_copy(s, name, sizeof(name), rest);
+	*e = (ret ? piglit_get_gl_enum_from_name(name) : GL_NONE);
+	return ret;
+}
+
 /**
  * Skip over whitespace upto the end of line
  */
diff --git a/tests/shaders/parser_utils.h b/tests/shaders/parser_utils.h
index 538fa6b..8b766bc 100644
--- a/tests/shaders/parser_utils.h
+++ b/tests/shaders/parser_utils.h
@@ -1,5 +1,5 @@
 /*
- * Copyright © 2010 Intel Corporation
+ * Copyright © 2010-2016 Intel Corporation
  *
  * Permission is hereby granted, free of charge, to any person obtaining a
  * copy of this software and associated documentation files (the "Software"),
@@ -21,9 +21,90 @@
  * DEALINGS IN THE SOFTWARE.
  */
 
+/**
+ * \file parser_utils.h
+ *
+ * These are a bunch of plain-text parsing utilities, most of them
+ * have the form:
+ *   boolean-like parse_foo(input-string, output-foo, output-string)
+ *
+ * If the input is a well-formed string representation of a "foo"
+ * value, as many characters will be read from the string as they are
+ * needed to initialize the "foo" object returned via the first output
+ * argument, and the boolean return value will evaluate to true.  If
+ * the output string argument is not NULL, a pointer one past the last
+ * character consumed to parse a "foo" value will be returned to the
+ * caller so that the rest of the document can be processed (e.g. by
+ * passing the output string as input string of another parse
+ * function).
+ *
+ * If the input cannot be parsed as a "foo" object, the boolean return
+ * value will evaluate to false and the input string will be returned
+ * as output string as-is (which mimicks the behavior of the C
+ * standard library strto* functions).  The "foo" output argument will
+ * be left in an undefined state in that case.
+ */
+#ifndef PIGLIT_PARSER_UTILS_H
+#define PIGLIT_PARSER_UTILS_H
+
 #include <stdbool.h>
+#include "piglit-util-gl.h"
+
+/**
+ * Parse one or more whitespace characters (other than newline) from
+ * the input string.
+ */
+bool
+parse_whitespace(const char *s, const char **rest);
+
+/**
+ * Parse an exact match of string \p lit, optionally preceded by
+ * whitespace.
+ */
+bool
+parse_str(const char *s, const char *lit, const char **rest);
+
+/**
+ * Parse a single non-empty whitespace-separated token.  On success \p
+ * t and \p rest will respectively point at the first and one past the
+ * last character of the result.
+ */
+bool
+parse_word(const char *s, const char **t, const char **rest);
+
+/**
+ * Like parse_word(), but the result is copied into the fixed-size
+ * buffer pointed to by \p t and null-terminated.
+ *
+ * The parse is considered to fail if the size of the result
+ * (including the terminating null character) would have exceded the
+ * number of characters allocated for it in the buffer as given by the
+ * \p n argument.
+ */
+bool
+parse_word_copy(const char *s, char *t, unsigned n, const char **rest);
+
+/**
+ * Parse a GL_* symbolic constant.
+ */
+bool
+parse_enum_gl(const char *s, GLenum *e, const char **rest);
 
 const char *eat_whitespace(const char *src);
 const char *eat_text(const char *src);
 bool string_match(const char *string, const char *line);
 const char *strcpy_to_space(char *dst, const char *src);
+
+/**
+ * Abort the Piglit test with failure status if the boolean expression
+ * (typically the result of a chain of parse function calls) evaluates
+ * to false.
+ */
+#define REQUIRE(b, ...) do {					\
+		if (!(b)) {					\
+			fprintf(stderr, __VA_ARGS__);		\
+			piglit_report_result(PIGLIT_FAIL);	\
+		}						\
+	} while (0)
+
+#endif
-- 
2.9.0



More information about the Piglit mailing list