3 ms·
Ugh, the C syntax makes it so hard to parse, humans or machine. I recently submitted a PR for the `c2nim` import tool to handle double function pointers. I did
by elcritch 4y ago
Ugh, the C syntax makes it so hard to parse, humans or machine.
I recently submitted a PR for the `c2nim` import tool to handle double function pointers. I didn't actually understand the C code until I got the transpiler working despite cdecl.org:
// posix signal
void(*signal(int, void (*)(int)))(int);
in Nim becomes:
var signal*: proc (a1: cint; a2: proc (a1: cint)): proc (a1: cint)
Well I'm 99.5% certain at least. Even now I'm uncertain of the C syntax. And I've not been bold enough to test 3rd order C function pointers. I figure that's probably C code you don't wanna touch if possible.
https://github.com/nim-lang/c2nim/blob/11f2c5363dfe7e8c7c8ce78680aa9eefa762c2a3/testsuite/results/pointerdecls.nim https://github.com/nim-lang/c2nim/blob/11f2c5363dfe7e8c7c8ce...
The other annoying one is that "signed" and "unsigned" are basically adjectives, but "long" can be both a type and a modifier. So it's difficult to parse unless you're the target C compiler. Technically you can, but you have to use backtracking.
- secondcoming 4y agoThat nim version isn’t much better IMO, but I am unfamiliar with the language.
- kelnos 4y agoI'm completely unfamiliar with the language but was able to read it from left-to-right very easily: var signal*: proc (a1: cint; a2: proc (a1: cint)): proc (a1: cint) That reads to me as: Variable signal is a pointer to a function that that takes two arguments (an int, and a function that takes an int), and returns a function that takes one argument (an int). Of course, I'm assuming my interpretation is correct... perhaps it's not ;)
- elcritch 4y agoThat's right. It's actually just an ugly bit of code though it's pragmatic. Note that Nim uses "*" as a public symbol sigil not a pointer. Its a bit odd at first coming from C but is nice and consistent.
- shirleyquirk 4y agoI agree, I prefer Nim's alternate syntax for this sort of stuff: var signal*: (cint,(cint)->void) -> (cint)->void
- slavik81 4y agoThe signed and unsigned keywords work the same way as long, e.g., `unsigned` alone is equivalent to `unsigned int`.
- elcritch 4y agoYah but you can't have "unsigned unsigned". You can and do have "long long", or possibly a "long long long" in addition to the saner "long int".
- constantcrying 4y ago>Yah but you can't have "unsigned unsigned". Seems like a good thing to be honest.
- constantcrying 4y ago>Ugh, the C syntax makes it so hard to parse, humans or machine. As a generalization I completely disagree. But for function pointers specifically, yes, they are easily the worst part of the C syntax. >void(signal(int, void ()(int)))(int); That's just declaring signal as function (int, pointer to function (int) returning void) returning pointer to function (int) returning void, DUH. Thank you cdecl.org But notice how absolutely ugly it is that the final return type comes first, that is due to the C function signatures.
- xormapmap 4y ago> void(signal(int, void ()(int)))(int); This is trivial to parse if you use spiral rule, humans or machine.
- int_19h 4y agoThe spiral rule is itself inherently non-trivial in application.
- WalterBright 4y ago> difficult to parse Not really. Just create a bit mask. https://github.com/dlang/dmd/blob/master/compiler/src/dmd/cparse.d#L2163 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/cp...
- elcritch 4y agoThanks! I'd read you had a full c compiler, this is awesome. Looks like there's only about ~388 lines of logic to the bitmask? haha your definition of difficult might not match mine. ;)
- jcranmer 4y agoThe way you read a declaration in C is imagine you're evaluating an expression. So the example you give: void(*signal(int, void (*)(int)))(int) The result of the expression is void. Parentheses go first in order of operations, so we evaluate this first: (*signal(int, void (*)(int))) Function call binds before dereference, so this goes first: signal(int, void (*)(int)) "signal" becomes the actual declared name. At the bottom, we have a function that takes two parameters, an int, and a function pointer that takes an int and returns void. Now we work outwards. This function returns a pointer, that when dereferenced, can be called with an int, and returns void when doing so. In layman's terms, we have a function "signal" that takes an int and a function pointer and returns a function pointer, and both function pointers are to a function that takes an int and returns void. Is it easy? If you can read C expressions, it can be easy. But it is definitely tedious, and does not come out quickly--it violates the principle that you have <type> <variable name>; it is not what I would consider readable or good design. And it would be easier if types were consistently read in one direction (like Rust types).