A grammar is necessary (not sufficient of course), unavoidable in fact so we need a grammar and I'm of the opinion that a grammar based on PL/I subset G is extremely attractive for reasons I've already articulated. A criticism I have of many, many newer languages like Rust, like Hare like Zig like Go like Swift even C# is that they are based on the C grammar and therefore do - unfortunately - stand to repeat some of the sins of the past.
I see grammar as an arbitrary choice, related more to what the designer considers "pretty" than anything else.
It is the language paradigm (generic approach to problem solving), and concepts, that matter.
If you start with a grammar, you are basically starting with the statement "This is the way all possible concepts in this programming language shall be expressed."
If you start at the conceptual level, you sketch out the features of the language, and can then choose a grammar that best suits the needs.
In my opinion, the only reason to start from the grammar upwards is if you 1) believe you already know everything you need to know about the language being designed, and/or 2) how it looks is of paramount importance to you. (These are the two reasons I've seen in the wild, that is.)
Let's use my own gripes with C as a starting point, and examine how the above affects the design process and the result.
Because of embedded uses, I want to allow pointers to specific addresses and to individual objects, but I do not want them to be extensible to arrays, except via explicit constructs that also specify the size/range of the array.
I do not want to force the ABI to pass arrays as (origin, length) or (origin, step, length) tuples, because my aim is to be able to prove that all accesses are valid using static code analysis tools. If a cosmic ray flips a bit and that causes a crash because of lack of
runtime bounds checking, I'm okay with that. (This, of course, is a design choice, and by no means the only one possible!)
I also like being able to redefine basic operators for custom object types. I don't want to go whole-hog object-oriented, making the operator overloading object-specific; I'm fine with the operator overloading being purely based on static types only. (I am aiming at small to medium-sized embedded devices, and the cost of the indirection via object pointers is too much for this niche, in my opinion. Another design choice.)
I now cobble together something that seems to compile a small snippet of source code to pseudo-assembly.
Uh-oh, problem. Because I do not force ABIs to explicitly pass a parameter describing the array length, I need a way to explicitly extract the origin and the length of an array. I could use built-in functions or address and length operators. Except that if I have already decided on a grammar, my choice is dictated by that grammar. Oops.
In real life, both options have their upsides and downsides. When the length is needed in a function where the array was passed as a parameter, we really do need to pass the length also. There are several options for this particular design choice, but they all boil down to the fact that in a function specification, we need a way to specify that a parameter is the length of a specific array also passed as a parameter. (With this information, my compiler can tell at compile-time whether the length operator or built-in function is allowed.) This significantly affects the grammar, of course!
If my grammar was locked in already, I'd have to cobble together a less than optimal workaround.
Even though I have piles of experience myself, I
know I am not aware of all the features and details beforehand. But I also know that I can construct the grammar as I go along, and collect and implement all the features of the new programming language. Having the grammar be defined by the features, instead of vice versa, gives me the most leeway.
For example, if I wanted this language to be mostly compatible with C otherwise, I would make the address-of and length-of operators, so that those porting code would need to pay attention to every case where a pointer is used as an array or vice versa. C already has an address-of operator &, but it might be confusing, and make the static code analysis more difficult. Instead, I might choose say @ and #, or
origin and
length (keyword operators, like
sizeof). But
I do not know yet; I would experiment with it in practice –– on unsuspecting victims, preferably –– to see if they grasp the concept intuitively, and therefore would be likely to apply this to write memory-safer code. Locking in the grammar up front makes such experiments irrelevant; the decision has been made already.
Thus, the obvious conclusion from going at it grammar-first is that it is grandiose, futile, waste of time, or all three.
Do not forget that others are not commenting about
you personally, they are commenting your
approach and what you describe in your output.
This is also why I use a pseudonym, instead of my real name: it helps me remember that any negative feedback I get is based on my output, my communications, and not my person. I can change my communication style somewhat –– although verbosity seems to be a fixed feature for me ––, but there is no reason to think my person is under attack, so although discussion can get very heated at times, there is no reason to consider it as between persons: it is just a heated discussion. In a different concurrent discussion with the same members, I can be (and have been), quite calm and relaxed –– again, because it is not about persons, the heat is only about the subject matter. I think sometimes a little heat is good, because it shows that others care, and because it (at least for me) causes one to reconsider their own output, to see if the heat is on track, or just a mistargeted flamethrower. Useful, in other words.