]> git.draconx.ca Git - cdecl99.git/commitdiff
libcdecl: Combine several postprocessing steps into one.
authorNick Bowler <nbowler@draconx.ca>
Fri, 30 Jun 2023 01:22:44 +0000 (21:22 -0400)
committerNick Bowler <nbowler@draconx.ca>
Sun, 2 Jul 2023 00:16:06 +0000 (20:16 -0400)
We currently have 5 postprocessing steps common to both parse variants,
four of which don't do any modification at all (just syntax checks).
This is overkill.  We can easily combine all five of these operations
into one function which does everything in a single tree traversal.

src/parse-decl.c

index 0f660295e6963d3aaeeccc5e5dc8f3cc081c4267..f156ba9f0052c23146cc2147f4b91b4f29b871a2 100644 (file)
@@ -312,119 +312,118 @@ reduce_parentheses(struct cdecl_declarator **p, struct cdecl_declarator *d)
 }
 
 /*
- * Function parameters and return types have a few restrictions that are
- * really easy to check in comparison to the above absurdity.
+ * Returns nonzero iff the given specifier list contains a specifier
+ * of the indicated type.
  */
-static int
-check_parameters(struct cdecl_declarator **p, struct cdecl_declarator *d)
+static int have_specifier(struct cdecl_declspec *s, unsigned type)
 {
-       struct cdecl_declspec *spec;
-       struct cdecl *param;
-
-       if (d->type != CDECL_DECL_FUNCTION)
-               return 0;
-
-       for (param = d->u.function.parameters; param; param = param->next) {
-               if (!valid_declspecs(param, false))
-                       return -1;
-
-               /* Check for "void" function parameters as a special case. */
-               for (spec = param->specifiers; spec; spec = spec->next) {
-                       if (param->declarators->type != CDECL_DECL_NULL)
-                               continue;
-                       if (spec->type != CDECL_TYPE_VOID)
-                               continue;
-
-                       if (spec != param->specifiers || spec->next != NULL) {
-                               cdecl__errmsg(CDECL__EVOIDPARAM);
-                               return -1;
-                       } else if (d->u.function.parameters->next) {
-                               cdecl__errmsg(CDECL__EVOIDPARAM);
-                               return -1;
-                       } else if (d->u.function.variadic) {
-                               cdecl__errmsg(CDECL__EVOIDPARAM);
-                               return -1;
-                       }
-               }
-       }
-
+       for (; s; s = s->next)
+               if (s->type == type)
+                       return 1;
        return 0;
 }
 
 /*
- * Functions cannot return arrays or functions.  Since the parse tree is
- * "inside-out", we need to look for functions as the child declarator.
+ * Check syntax restrictions on a function declarator's child declarator.
+ * That is, "pointer to function", "array of function" and "function
+ * returning function".
+ *
+ * Returns -1 if the declaration is invalid, or 0 otherwise.
  */
-static int
-check_rettypes(struct cdecl_declarator **p, struct cdecl_declarator *d)
+static int check_function_child(struct cdecl_declarator *d)
 {
-       if (!d->child || d->child->type != CDECL_DECL_FUNCTION)
-               return 0;
+       struct cdecl_pointer *ptr;
 
        switch (d->type) {
+       case CDECL_DECL_POINTER:
+               ptr = &d->u.pointer;
+               if (have_specifier(ptr->qualifiers, CDECL_QUAL_RESTRICT)) {
+                       /* pointer to function cannot be restrict qualified. */
+                       cdecl__errmsg(CDECL__ERESTRICTFUNC);
+                       return -1;
+               }
+               return 0;
        case CDECL_DECL_FUNCTION:
+               /* function returning function is never allowed. */
                cdecl__errmsg(CDECL__ERETFUNC);
                return -1;
        case CDECL_DECL_ARRAY:
-               cdecl__errmsg(CDECL__ERETARRAY);
+               /* array of function is never allowed. */
+               cdecl__errmsg(CDECL__EFUNCARRAY);
                return -1;
        }
 
        return 0;
 }
 
-static int
-check_arrays(struct cdecl_declarator **p, struct cdecl_declarator *d)
+/*
+ * Check a function parameter declaration for validity, which means it has a
+ * valid combination of declaration specifiers and, if it is a void parameter,
+ * that it is the one special case where this is allowed.
+ *
+ * Returns -1 if the declaration is invalid, or 0 otherwise.
+ */
+static int check_function_param(struct cdecl_function *f, struct cdecl *param)
 {
-       if (!d->child || d->child->type != CDECL_DECL_ARRAY)
-               return 0;
-
-       switch (d->type) {
-       case CDECL_DECL_FUNCTION:
-               cdecl__errmsg(CDECL__EFUNCARRAY);
+       if (!valid_declspecs(param, false))
                return -1;
+
+       /* Check for "void" function parameters as a special case. */
+       if (param->declarators->type == CDECL_DECL_NULL
+           && have_specifier(param->specifiers, CDECL_TYPE_VOID))
+       {
+               struct cdecl *fp = f->parameters;
+
+               if (f->variadic || fp->next || fp->specifiers->next) {
+                       cdecl__errmsg(CDECL__EVOIDPARAM);
+                       return -1;
+               }
        }
 
        return 0;
 }
 
-static int
-normalize_specs(struct cdecl_declarator **p, struct cdecl_declarator *d)
+/*
+ * Normalize the specifier lists for function parameters, and then check the
+ * function declarator for validity.
+ *
+ * Returns -1 if the declaration is invalid, or 0 otherwise.
+ */
+static int postproc_function(struct cdecl_declarator *d)
 {
-       struct cdecl_function *func;
-       struct cdecl_pointer *ptr;
+       struct cdecl_function *func = &d->u.function;
+       struct cdecl *param;
+       int rc;
 
-       switch (d->type) {
-       case CDECL_DECL_POINTER:
-               ptr = &d->u.pointer;
-               ptr->qualifiers = cdecl__normalize_specs(ptr->qualifiers);
-               break;
-       case CDECL_DECL_FUNCTION:
-               func = &d->u.function;
-               for (struct cdecl *i = func->parameters; i; i = i->next)
-                       i->specifiers = cdecl__normalize_specs(i->specifiers);
-               break;
+       for (param = func->parameters; param; param = param->next) {
+               param->specifiers = cdecl__normalize_specs(param->specifiers);
+
+               if ((rc = check_function_param(func, param)) < 0)
+                       return rc;
        }
 
-       return 0;
+       return check_function_child(d->child);
 }
 
 static int
-check_qualifiers(struct cdecl_declarator **p, struct cdecl_declarator *d)
+postproc_common(struct cdecl_declarator **p, struct cdecl_declarator *d)
 {
-       struct cdecl_declspec *spec;
        struct cdecl_pointer *ptr;
 
-       if (!d->child || d->child->type != CDECL_DECL_POINTER)
+       switch (d->type) {
+       case CDECL_DECL_POINTER:
+               ptr = &d->u.pointer;
+               ptr->qualifiers = cdecl__normalize_specs(ptr->qualifiers);
                return 0;
-
-       ptr = &d->child->u.pointer;
-       for (spec = ptr->qualifiers; spec; spec = spec->next) {
-               if (spec->type == CDECL_QUAL_RESTRICT
-                   && d->type == CDECL_DECL_FUNCTION) {
-                       cdecl__errmsg(CDECL__ERESTRICTFUNC);
+       case CDECL_DECL_FUNCTION:
+               return postproc_function(d);
+       case CDECL_DECL_ARRAY:
+               if (d->child && d->child->type == CDECL_DECL_FUNCTION) {
+                       /* function returning array is never allowed. */
+                       cdecl__errmsg(CDECL__ERETARRAY);
                        return -1;
                }
+               return 0;
        }
 
        return 0;
@@ -517,15 +516,7 @@ static int do_postprocess(struct cdecl *decl, int english_mode)
                                return 0;
                }
 
-               if (forall_declarators(i, check_parameters) < 0)
-                       return 0;
-               if (forall_declarators(i, check_rettypes) < 0)
-                       return 0;
-               if (forall_declarators(i, check_arrays) < 0)
-                       return 0;
-               if (forall_declarators(i, normalize_specs) < 0)
-                       return 0;
-               if (forall_declarators(i, check_qualifiers) < 0)
+               if (forall_declarators(i, postproc_common) < 0)
                        return 0;
 
                if (!valid_declspecs(i, true))