If I were to design my own programming language, which I am not, but who knowns, I'd make it using rules that are as simple as possible, using as few operators as possible built into the language.
This is pure brainstorming but there is several ideas I have, which are mainly made of what I don't like about C and C++ replaced, and what I like being kept.
I'd make it somehow C-like, but with the following differences/simplifications :
In order to be compilable it would requires heavily on automatic vectorisation, but that's not much problems with todays' PCs.
Here you are a sample at what the language I have in mind might be looking :
So basically the deal is to make the language have as few rules as possible, and using as few symbols as possible in order to let the user overloads them for his own private usage. Standard library would of course provide integer and floating point possibilities, as well as the guarantee they get auto-vectorized whenever possible.
This is pure brainstorming but there is several ideas I have, which are mainly made of what I don't like about C and C++ replaced, and what I like being kept.
I'd make it somehow C-like, but with the following differences/simplifications :
- { and } are not used, ( and ) are used instead, so that they are easy to access on a swiss keyboard (I know { and } are easy to access an american keyboard, but they aren't for me).
- Operators are all overloadable for any types
- bool is the only type, other types are built using solely bool, array, struct, union and pointers. char for example would be a typedef for an array of 8 bool
- No opeartor such as '+' or '-' is build in, they are just part of a standard library that can be changed if the user wishes to. Only the boolean &, | and ^ are implemented by default. Everything else sits inside a library.
- Assignement is made with <- operator instead of = which is confusing because it could mean equality.
- Arrays are dereferenced with () instead of []
- Structs and unions are acessed with () just like arrays (basically, everything is always deferenced with ())
- There is no const-correctness, the compiler determines whenever a "variable" is constant and applies the appropriate optimisations when it is
- To avoid the passage by value or reference issue, the arguments of a function are always readonly (i.e. const). If the user wishes to modify them he has to make a local copy manually
- Inside for loops, ',' is used instead of ';' to separate instructions
- It's easy to do pseudo-OO when there's a need to by adding function pointers in a struct, yet it's not "pure" OO or any bullshit like that
- The syntax for function pointers is fixed so that it makes sense (I don't know how yet I admit)
- The while part of a do/while loop is optional, when only "do" is used that's an infinite loop
- Returned values are named, and are usable as local variables. (if no name is given "return" is used automatically)
- It's possible to return multiple values (a.k.a. unnamed structs) easily
- Compile time constant automatically decay in an usable array of bits
- Type modifiers are always arround the type name instead of the declaration name (i.e. bool[8] i, not bool i[8] for declaring a 8-bit integer)
In order to be compilable it would requires heavily on automatic vectorisation, but that's not much problems with todays' PCs.
Here you are a sample at what the language I have in mind might be looking :
Code:
typedef char bool[8]; // Define a 8-bit integer type
char operator==(char a, char b)
(
return <- (a(0) == b(0)) &
(a(1) == b(1)) &
(a(2) == b(2)) &
(a(3) == b(3)) &
(a(4) == b(4)) &
(a(5) == b(5)) &
(a(6) == b(6)) &
(a(7) == b(7));
)
char operator!=(char a, char b)
(
return <- !(a==b);
)
// Unsigned comparison
char operator<(char a, char b)
(
if(a(7) != b(7)) return b(7);
if(a(6) != b(6)) return b(6);
if(a(5) != b(5)) return b(5);
if(a(4) != b(4)) return b(4);
if(a(3) != b(3)) return b(3);
if(a(2) != b(2)) return b(2);
if(a(1) != b(1)) return b(1);
if(a(0) != b(0)) return b(0);
)
bool operator<=(char a, char b)
(
if(a == b) return <- true;
else return <- a<b;
)
bool operator>=(char a, char b)
(
return <- !(a<b);
)
bool operator>(char a, char b)
(
return <- !(a<=b);
)
// Now we can compare 8-bit integers. Let's create a library so that we can add and substract them
(bool sum, bool cin) full_adder(bool a, bool b, bool cin)
(
sum <- a ^ b ^ cin;
cout <- (a ^ b & cin) + (a & b);
)
// Define incrementation (using auto-vectorisation) the compiler optimizes the incrementation in an inc instruction
(char res) operator++(char a)
(
bool carry;
(res(0), carry) <- full_adder(a(0), 1, 0);
(res(1), carry) <- full_adder(a(1), 0, carry);
(res(2), carry) <- full_adder(a(2), 0, carry);
(res(3), carry) <- full_adder(a(3), 0, carry);
(res(4), carry) <- full_adder(a(4), 0, carry);
(res(5), carry) <- full_adder(a(5), 0, carry);
(res(6), carry) <- full_adder(a(6), 0, carry);
(res(7), carry) <- full_adder(a(7), 0, carry);
)
(char res) opearator+(char a, char b) (using auto-vectorisation the compiler optimises this into a simple add operation)
(
char i;
bool carry;
for(i <- 0, i != 7, ++i)
(
(res(i), carry) <- full_adder(a(i), b(i), 0);
)
)
// Now let's define a vector type
typedef char[8] vector;
// We can add vectors
(vector res) operator+(vector a, vector b)
(
char i;
bool carry;
for(i <- 0, i != 7, ++i)
(
(res(i), carry) <- full_adder(a(i), b(i), 0);
)
)
// Let's create a 8x8 matrix type
typedef vector[8] mat64_t;
// We can add matrixes
(mat64_t res) operator+(mat64_t a, mat64_t b)
(
char i;
bool carry;
for(i <- 0, i != 7, ++i)
(
(res(i), carry) <- full_adder(a(i), b(i), 0);
)
)
(vector result) bubble_sort(vector data)
(
result <- data; // data CANNOT be modified because it is automatically called as a const-reference, so we have to copy here
bool sorted;
do
(
sorted <- true;
for(int i <- 0, i<7, ++i)
(
if(result(i) < result(i+1))
(
int temp <- data(i);
data(i+1) <- result(i);
sorted <- false;
)
)
)
while(!sorted); //If we ommited this line the program would still be legal but would never exit, it would *NOT* be a syntax error
)
void main()
(
bool[8] a <- (true, false, false, true, true, false, true, false); // primitive way of assigning 154 to a 8-bit value
char b <- 154; // The intent is here more clearer
bool[8] c <- 154; // It makes it explicit the value is a 8-bit one
char d <- 0x94; // Still the exact same, using hexadecimal notation
char e <- (154); // Equivalent, an array of 1 element is the same as the element iself
char f <- (1, 0, 0, 1, 1, 0, 1, 0); // Yet another way to do it, each digit turns into an array of 1 bit and is thus equivalent
char g <- b + c; // How to make computations and store them in variables
if(b) // ERROR, b is not boolean type
(
)
if(b == 0) // Correct, as 0 will automatically be converted to an array of the correct size (here 8)
(
)
vector h <- 0; // Incorrect, because cannot convert from bool[1] to bool[8][8]
vector i <- (1, 2, 3, 4, 5, 6, 7, 8); // This is correct
vector j <- (((1, 2, 3, 4, 5, 6, 7, 8))); // Also correct, the type is bool[n][8][1][1] which decays into bool[8][8] as arrays of 1 elements are the same as the element itself
char[6] k <- ('h', 'e', 'l', 'l', 'o', 0); // How to build a string
char[6] l <- "hello"; //Would somehow be equivalent
vector m <- "hello "; // Correct, with the null termination this makes a 8-character array
vector n <- "hello"; // Incorrect, cannot convert bool[8][6] into bool[8][8]
vector o <- m+n; //Legal ! We add vectors with a simple '+' !
n <- sort(m); // The vector n is directly tied to "result", so no more than 1 copy was necessary
m <- sort(m); // The copy is optimized out, as the function notices the references points to the same variable
mat64_t p <- (m, n, o, m, n, o, m, n); //Define a 8x8 matrix
mat64_t q <- (m+n, o+n, m+o, p(3), p(2), o, m, n, (1, 2, 3, 4, 5, 4, 3, 2)); //Define another 8x8 matrix
mat64_t r <- p + q; // Legal ! We add matrixes with a '+' !
// Now let's assume types like string, int, etc.. are defined, with operators overloaded so that they are usable like in C
// As well as a print() function
struct
(
string name,
int age,
double height,
) student <- ("max", 21, 1.78); // Affects fields globally
++student(age); // Affects fields individually
student(heigth) <- 1.88;
// Can be done via a normal function overloaded for multiple types
print(student(name)); //Prints max
print(student(age)); //Prints 22
print(student(heigth)); //Prints 1.88
// Can also be done via operator overloading of << if one prefers
print << student(name) << student(age) << student(height);
)
char operator==(char a, char b)
(
return <- (a(0) == b(0)) &
(a(1) == b(1)) &
(a(2) == b(2)) &
(a(3) == b(3)) &
(a(4) == b(4)) &
(a(5) == b(5)) &
(a(6) == b(6)) &
(a(7) == b(7));
)
char operator!=(char a, char b)
(
return <- !(a==b);
)
// Unsigned comparison
char operator<(char a, char b)
(
if(a(7) != b(7)) return b(7);
if(a(6) != b(6)) return b(6);
if(a(5) != b(5)) return b(5);
if(a(4) != b(4)) return b(4);
if(a(3) != b(3)) return b(3);
if(a(2) != b(2)) return b(2);
if(a(1) != b(1)) return b(1);
if(a(0) != b(0)) return b(0);
)
bool operator<=(char a, char b)
(
if(a == b) return <- true;
else return <- a<b;
)
bool operator>=(char a, char b)
(
return <- !(a<b);
)
bool operator>(char a, char b)
(
return <- !(a<=b);
)
// Now we can compare 8-bit integers. Let's create a library so that we can add and substract them
(bool sum, bool cin) full_adder(bool a, bool b, bool cin)
(
sum <- a ^ b ^ cin;
cout <- (a ^ b & cin) + (a & b);
)
// Define incrementation (using auto-vectorisation) the compiler optimizes the incrementation in an inc instruction
(char res) operator++(char a)
(
bool carry;
(res(0), carry) <- full_adder(a(0), 1, 0);
(res(1), carry) <- full_adder(a(1), 0, carry);
(res(2), carry) <- full_adder(a(2), 0, carry);
(res(3), carry) <- full_adder(a(3), 0, carry);
(res(4), carry) <- full_adder(a(4), 0, carry);
(res(5), carry) <- full_adder(a(5), 0, carry);
(res(6), carry) <- full_adder(a(6), 0, carry);
(res(7), carry) <- full_adder(a(7), 0, carry);
)
(char res) opearator+(char a, char b) (using auto-vectorisation the compiler optimises this into a simple add operation)
(
char i;
bool carry;
for(i <- 0, i != 7, ++i)
(
(res(i), carry) <- full_adder(a(i), b(i), 0);
)
)
// Now let's define a vector type
typedef char[8] vector;
// We can add vectors
(vector res) operator+(vector a, vector b)
(
char i;
bool carry;
for(i <- 0, i != 7, ++i)
(
(res(i), carry) <- full_adder(a(i), b(i), 0);
)
)
// Let's create a 8x8 matrix type
typedef vector[8] mat64_t;
// We can add matrixes
(mat64_t res) operator+(mat64_t a, mat64_t b)
(
char i;
bool carry;
for(i <- 0, i != 7, ++i)
(
(res(i), carry) <- full_adder(a(i), b(i), 0);
)
)
(vector result) bubble_sort(vector data)
(
result <- data; // data CANNOT be modified because it is automatically called as a const-reference, so we have to copy here
bool sorted;
do
(
sorted <- true;
for(int i <- 0, i<7, ++i)
(
if(result(i) < result(i+1))
(
int temp <- data(i);
data(i+1) <- result(i);
sorted <- false;
)
)
)
while(!sorted); //If we ommited this line the program would still be legal but would never exit, it would *NOT* be a syntax error
)
void main()
(
bool[8] a <- (true, false, false, true, true, false, true, false); // primitive way of assigning 154 to a 8-bit value
char b <- 154; // The intent is here more clearer
bool[8] c <- 154; // It makes it explicit the value is a 8-bit one
char d <- 0x94; // Still the exact same, using hexadecimal notation
char e <- (154); // Equivalent, an array of 1 element is the same as the element iself
char f <- (1, 0, 0, 1, 1, 0, 1, 0); // Yet another way to do it, each digit turns into an array of 1 bit and is thus equivalent
char g <- b + c; // How to make computations and store them in variables
if(b) // ERROR, b is not boolean type
(
)
if(b == 0) // Correct, as 0 will automatically be converted to an array of the correct size (here 8)
(
)
vector h <- 0; // Incorrect, because cannot convert from bool[1] to bool[8][8]
vector i <- (1, 2, 3, 4, 5, 6, 7, 8); // This is correct
vector j <- (((1, 2, 3, 4, 5, 6, 7, 8))); // Also correct, the type is bool[n][8][1][1] which decays into bool[8][8] as arrays of 1 elements are the same as the element itself
char[6] k <- ('h', 'e', 'l', 'l', 'o', 0); // How to build a string
char[6] l <- "hello"; //Would somehow be equivalent
vector m <- "hello "; // Correct, with the null termination this makes a 8-character array
vector n <- "hello"; // Incorrect, cannot convert bool[8][6] into bool[8][8]
vector o <- m+n; //Legal ! We add vectors with a simple '+' !
n <- sort(m); // The vector n is directly tied to "result", so no more than 1 copy was necessary
m <- sort(m); // The copy is optimized out, as the function notices the references points to the same variable
mat64_t p <- (m, n, o, m, n, o, m, n); //Define a 8x8 matrix
mat64_t q <- (m+n, o+n, m+o, p(3), p(2), o, m, n, (1, 2, 3, 4, 5, 4, 3, 2)); //Define another 8x8 matrix
mat64_t r <- p + q; // Legal ! We add matrixes with a '+' !
// Now let's assume types like string, int, etc.. are defined, with operators overloaded so that they are usable like in C
// As well as a print() function
struct
(
string name,
int age,
double height,
) student <- ("max", 21, 1.78); // Affects fields globally
++student(age); // Affects fields individually
student(heigth) <- 1.88;
// Can be done via a normal function overloaded for multiple types
print(student(name)); //Prints max
print(student(age)); //Prints 22
print(student(heigth)); //Prints 1.88
// Can also be done via operator overloading of << if one prefers
print << student(name) << student(age) << student(height);
)
So basically the deal is to make the language have as few rules as possible, and using as few symbols as possible in order to let the user overloads them for his own private usage. Standard library would of course provide integer and floating point possibilities, as well as the guarantee they get auto-vectorized whenever possible.