Live data from Hacker News

Clockwise/Spiral Rule

c-faq.com

1–10 of 17 posts

Re: Clockwise/Spiral Rule

#2
I always found this to be one of C's (and it's descendants) more ridiculous aspects.

You should not need an entire algorithm just to mentally parse a declaration. The rules should be sufficiently straight forward so that the advanced pattern matching in our brains should suffice.

It is excusable since C is old, but ridiculous nonetheless.

Re: Clockwise/Spiral Rule

#3
Any time this comes up I like to point out that the spiral rule is wrong. It is instructive in a way because one learns more about C declaration syntax, but it is even more instructive to recognize why it is wrong.

The spiral rule works only if there is no pointer to pointer or array of array in the type. But take this for example:

        +----------------------------+
        | +-----------------------+  |
        | | +------------------+  |  |
        | | | +-------------+  |  |  |
        | | | | +--------+  |  |  |  |
        | | | | |  +--+  |  |  |  |  |
        | | | | |  ^  |  |  |  |  |  |
    int * * ¦ ¦ ¦ xxx[1][2][3] |  |  |
     ^  | | | | |     |  |  |  |  |  |
     |  | | | | +-----+  |  |  |  |  |
     |  | | | +----------+  |  |  |  |
     |  | | +---------------+  |  |  |
     |  | ---------------------+  |  |
     |  +-------------------------+  |
     +-------------------------------+
The type of xxx is a [1-element] array of [2-element] array of [3-element] array of pointer to pointer to ints. I drew a spiral that passes through each specifier in the correct order.

Notice that to make the spiral correct it has to skip the pointer specifiers in the first three loops. This is marked by ¦. This is not mentioned in the original spiral rules and one could be forgiven to parse the expression as xxx -> [1] -> pointer -> [2] -> etc. following a spiral that doesn't skip the pointers.

The Right-Left Rule is quoted less frequently on HN but it's a correct algorithm for deciphering C types: http://cseweb.ucsd.edu/~ricko/rt_lt.rule.html

The spiral rule can be modified to process all array specifiers before all pointer specifiers, but then you'd have to specify that the order to do so is right and then left. At that point it's just the Right-Left Rule.

Re: Clockwise/Spiral Rule

#4
post #2

I always found this to be one of C's (and it's descendants) more ridiculous aspects. You should not need an entire algorithm just to mentally parse a declaration. The rules should be sufficiently straight forward so that the advanced pattern matching in our brains should suffice. It is excusable since C is old, but ridiculous nonetheless.

I recall reading that the reason for this was that it made the implementation of the compiler a lot easier

Re: Clockwise/Spiral Rule

#6
The real rule turns out to be simple (although non-obvious): "Declaration matches usage."

So, for example, if you want a pointer to a function returning a pointer to an array of 3 integers, you'd want

  (*(*f)())[3 - 1]
to evaluate to an int, so the declaration would be

  int (*(*f)())[3];

Re: Clockwise/Spiral Rule

#7
post #2

I always found this to be one of C's (and it's descendants) more ridiculous aspects. You should not need an entire algorithm just to mentally parse a declaration. The rules should be sufficiently straight forward so that the advanced pattern matching in our brains should suffice. It is excusable since C is old, but ridiculous nonetheless.

Pretty much everyone agrees that the "best" syntax for declarations is Go-style "x: int;" and not C-style "int x;", but to be fair you don't see declarations like that very often. Once you understand the (admittedly bad) syntax for "pointer to function returning type" as opposed to "function returning pointer to type", most declarations you're likely to find aren't hard to read.

I mantain some C code as part of my job and I don't recall declarations ever being a problem to mentally parse.

Re: Clockwise/Spiral Rule

#8
post #2

I always found this to be one of C's (and it's descendants) more ridiculous aspects. You should not need an entire algorithm just to mentally parse a declaration. The rules should be sufficiently straight forward so that the advanced pattern matching in our brains should suffice. It is excusable since C is old, but ridiculous nonetheless.

It really is not excusable, since

a) As far as I'm aware no other languages at that time suffered from this ridiculous defect.

b) It doesn't really seem like the thing where you'd need 20/20 hindsight to figure out it's a bad idea.

The same applies to a lot of the lasting braindamage C has inflicted on the computing industry. It's just that people have been numbed to it thanks to constant exposure as every single popular language in the last twenty years has aped at least some of C's defects.

Other major problems either introduced or spread by C include:

- the execrable octal number syntax

- fallthrough-as-default case statements

- non-first class arrays

- strings that don't know their length

- the unsafe/ub-by-default mindset

- braindamaged linking

- abusing = for assignment, combined with making assignment an expression. For most of C's history you'd not even get a compiler warning for if (a = b) ...

- generally there is a very unhealthy mix of of being highly imperative and statement based and at the same time having a lot of ad hoc ways to "expressionify" things (comma operator, ternary, pre-and-post increment/decrement), which, combined with undefined order of evaluation, encourages all sorts of errors.

- error-prone optional braces syntax (dangling if etc.)

- remainder (rather than modulo) operator

- very nasty implicit number promotion rules

- messed up operator precedence

- "if (!pred(x) || a - the c pre-processor

- a broken comment syntax (how do you comment out a region with comments inside?)

- spuriously non-grepable function definitions/declarations

Re: Clockwise/Spiral Rule

#9
Yay, yet another post on HN about mental clutches people use to read C declarations.

It helps a bit browsing through the source code of some of the first C compilers. The Unix Heritage Society[1] hosts source tar balls for very old Unixen, including the first C compilers.

One of the other posts in this thread ("Declaration matches usage.") is pretty close. Basically they originally [ab]used part of the expression parser plus some hacks on top to parse declarations, i.e. something like parsing ';' and then assigning to the root node and pushing it downwards through the tree to derive the type for the free variables at the leaves.

That's why

  int *a, b;
makes `a` a pointer, but not `b` (and why it's written that way). The int type is assigned to the root node (the `,` operator which has lowest precedence), then pushed to the nodes attached to it. Thus, `b` is derived to be of type `int` as well as ` * a`, which in the next step determines `a` to be of type `pointer-to-int`.

I recall reading a quote from Dennis Ritchie somewhere that the `( * name)` syntax for function pointers was required to "guide the parser" (read: hacks on top), but can't seem to find it right now.

Note that this was all before ANSI times. The current declaration rules have of course been derived and extended from there, but it helps a little understanding some of the oddities.

But even when reading through the C syntax in the first ANSI standard, you might realise that most of the expressiveness in C actually comes from, well it's expression syntax, and you can still shoehorn most of the declaration syntax in there as well, covering probably most of the language syntax using just the expression parser. (Which helps if you are in the 70ies, writing a compiler on a PDP-11 for your little toy OS, unaware that you are building the foundations for lots of things to come after)

[1] https://www.tuhs.org/

Re: Clockwise/Spiral Rule

#10
post #2

I always found this to be one of C's (and it's descendants) more ridiculous aspects. You should not need an entire algorithm just to mentally parse a declaration. The rules should be sufficiently straight forward so that the advanced pattern matching in our brains should suffice. It is excusable since C is old, but ridiculous nonetheless.

A simple fix to the language would be to require everything to either be on the left or the right of the variable.

I wonder why that wasn't done?

Post reply on HN