Hi,
Let's start from the very beginnng - the syntax and grammar of the language. What is needed, and what is desirable?
Most languages have a few basic things - comments, variables, mathmatical operators, do/until and while loops, if/elseif/else and functions/procedures. On top of this it's convenient to add some extra things like arrays and type-checking. C has all of this (although the type-checking is debatable).
The syntax and grammar should be readable, so that people who have never even seen the language before can at least read the source code and have a fair chance of understanding it. BASIC and Pascal fit in this category, but C fails miserably.
To improve productivity you want some form of error detection, so that if the source code is wrong it can tell the author exactly where the problem is (e.g. so that they don't need to spend hours searching for a missing bracket or curly brace). Unfortunately C fails miserably again.
It's also a very good idea to remove all machine-dependant behaviour from the language, so that people can write portable code. C tries, but if you need a 32 bit integer do you use a short, an int or a long? This includes the "standard libraries", which should be standard and should do enough to write simple applications. For C, the standard libraries are standard, but don't do enough. This means non-standard libraries are often required, which makes things difficult because they are non-standard. To get around the problems caused people use other tools (like autoconfigure).
Once you've decided on the syntax & grammar, the next step is the pre-processor. Quite frankly I'm used to assembly where the pre-processor is normally reasonably powerful. The pre-processor for C is a joke. To get around this people use scripting languages to "pre-pre-process" (e.g. Perl).
Next comes the parser, but for C the syntax & grammar makes it one of the hardest languages to parse.
Finally there's compiling, optimizing, assembling and linking. These steps seem fairly similar regardless of the syntax and grammer. However there's certain optimizations that can be done relatively easily if the syntax & grammer is suitable.
The first optimization is "function inlining". My idea here is to inline everything (so that there's one huge function), then optimize it, then do pattern matching to find commonly repeated sequences to turn back into functions, then optimize again. For a simple example, consider the following:
Code: Select all
int foo(char a, char b) {
b = b * a;
return b + a + 3;
}
int bar(char c, char d) {
return foo(3, c) + foo(2, d) - foo(c, d) + foo(c , 5);
}
First this would be expanded:
Code: Select all
int bar(char b, char c) {
temp1 = 3 * c;
temp1 = temp1 + 3 + 3;
temp2 = 2 * d;
temp2 = temp2 + 2 + 3;
temp3 = c * 5;
temp3 = temp3 + c + 5;
return temp1 - temp2 + temp3;
}
Then optimized:
Code: Select all
int bar(char c, char d) {
return 9 * c - 2 * d + 6;
}
Then converted back to smaller functions:
Code: Select all
int tempFunction(char a, char b) {
return a * b;
}
int bar(char c, char d) {
return tempFunction(9, c) - tempFunction(2, d) + 6;
}
Of course the example is too small, but you get the idea. This also allows the compiler to optimize for size or speed relatively easily, just by being more intelligent when deciding which matched patterns are converted back into functions.
Another idea is the ability to replace an entire function with a table lookup. For example, consider this:
Code: Select all
Uint16 someFunction(Uint8 a, Uint16 b) {
Uint16 result;
..heaps of complex stuff with no side-effects..
return result;
}
It's reasonably obvious from the function declaration that the complex stuff can be replaced by a table of 256 * 65536 16 bit integers. During compilation, the table can be auto-generated by interpretting the function with each possible combination of input parameters.
With strong type-checking this becomes more viable - what if the variable "a" above always contained either 0, 1 or 2, and the variable "b" was always between 50 and 1050? In this case you could automatically generate code to check that the input paramaters are within range and then replace the function with a table of 3 * 1000 16 bit integers.
For consistancy, you could use the same syntax to access the elements of an array as you would to call a function. For example:
Code: Select all
char foo[5] = {1, 3, 5, 7, 11};
char bar(char a) {
return a * a;
}
char example(void) {
char x, y;
return foo(x) + bar(y);
}
This allows the array "foo" to be changed into a function, or the function "bar" to be changed into an array at any time, without any of the other code being changed.
[continued]