General Category > Ideas
[Email suggestion No. 1] macros in C2
magnusi:
It has been stated in previously that macros need to be changed and not like the ones in C/C++, since those were limited/impacted by the age in which C and C++ were created in. When I was playing with Rust, which is a great programming language with some oddities (like filenames having significance in code, odd build system, variables immutable by default etc.), I stumbled upon Rust's macros, which are a great part of the language I otherwise think is okay, but not overly great. This is how a macro in Rust looks
--- Code: ---macro_rules! foo {
(x => $e:expr) => (println!("mode X: {}", $e));
(y => $e:expr) => (println!("mode Y: {}", $e));
}
--- End code ---
The following macro would be then used as:
--- Code: ---foo!(x => 5); or foo!(y => 10);
--- End code ---
because this example uses Rust's pattern matching. I think that a modification of Rust macros, suited for C2's cause, could be a good way how macros could work. Here is a simple suggestion of rules C2 macros could follow:
* Each starts with keyword like macro, followed by name and the content of the macro.
* The content may be either single value/statement or a mutiple lines of stuff in {} brackets.
* Macros are multi-line by default, no need to use \ to extend number of lines
* Macros must contain parse-able C2 code
* When used, an exclamation mark ! must be added to the name of the macro and semicolon at the and as if it was a regular statement. example macro:
--- Code: ---macro myassert(desc, test) {
tests_ran++;
if (!(test)) {
char* error;
asprintf(&error, "[%s:%d]\x1b[31mTEST FAILED\x1b[0m: %s\n",__FILE__, __LINE__, desc);
asprintf(&finalmessage, "%s%s", finalmessage, error);
}
else {
char* success;
tests_passed++;
asprintf(&success, "[%s:%d]\x1b[32mTEST PASSED\x1b[0m: %s\n", __FILE__, __LINE__, desc);
asprintf(&finalmessage, "%s%s", finalmessage, success);
}
}
--- End code ---
Which could then be used as
--- Code: ---myassert!("Test if 1 == 1", 1 == 1);
--- End code ---
Of course, this is just a simple suggestion, it is just an idea how fitting and readable yet distinguishable macros could work in C2.
admin:
It's a bit strange that some language cling to macros, while others
(C#, Java, etc) never seem to need them. To make a really good macro
system, I tried to write do the *purpose* of macros. What I came up
with:
* replacing common code
* inlining (instead of function). This one is actually not valid,
since a function call is superior if the compiler can inline it.
* feature selectionthe difference with a function is that a macro is able to 'access'
the 'calling' scope.
The Nim language is a bit in the same domain as C2. It has some pretty
nice ideas as well. For macros they stated (something like): "with macros
you can change the AST". Indeed the case if you think about it.
http://nim-lang.org/docs/tut2.html#macros
I agree with your design (1-5). My additions:
6. A macro can be public or not, just like other declarations.
7. There needs to be a distinction
(also described in the nim link): There are *expression* macros and
*statement/decl* macros. You cannot have a declaration if a place wher
an expression is expected. It might be possible to discover this, but
it might be needed for a developer to specify this as well..
8. The argument of a macro must be an identifier (only). This avoids
nasty issues like in C:
--- Code: --- #define MAX(x, y) (x > y ? x : y)
int c = MAX(a++, b++)
--- End code ---
as this expands to
--- Code: ---int c = ((a++) > (b++) ? (a++) : (b++));
--- End code ---
magnusi:
--- Quote from: admin on July 21, 2016, 07:51:26 AM ---It's a bit strange that some language cling to macros, while others
(C#, Java, etc) never seem to need them. To make a really good macro
system, I tried to write do the *purpose* of macros. What I came up
with:
* replacing common code
* inlining (instead of function). This one is actually not valid,
since a function call is superior if the compiler can inline it.
* feature selectionthe difference with a function is that a macro is able to 'access'
the 'calling' scope.
The Nim language is a bit in the same domain as C2. It has some pretty
nice ideas as well. For macros they stated (something like): "with macros
you can change the AST". Indeed the case if you think about it.
http://nim-lang.org/docs/tut2.html#macros
I agree with your design (1-5). My additions:
6. A macro can be public or not, just like other declarations.
7. There needs to be a distinction
(also described in the nim link): There are *expression* macros and
*statement/decl* macros. You cannot have a declaration if a place wher
an expression is expected. It might be possible to discover this, but
it might be needed for a developer to specify this as well..
8. The argument of a macro must be an identifier (only). This avoids
nasty issues like in C:
--- Code: --- #define MAX(x, y) (x > y ? x : y)
int c = MAX(a++, b++)
--- End code ---
as this expands to
--- Code: ---int c = ((a++) > (b++) ? (a++) : (b++));
--- End code ---
--- End quote ---
I agree with everything here. I propose a small addition to rule 8 and that is to allow literals, too. So
--- Code: ---mymacro!(5, 10); //fine
mymacro!(x, y); //fine
mymacro!(x++, x*y) //not fine
--- End code ---
admin:
Maybe we can also create a new point that forbids the use of macro's within a macro?
magnusi:
--- Quote from: admin on July 22, 2016, 09:17:40 AM ---Maybe we can also create a new point that forbids the use of macro's within a macro?
--- End quote ---
Yes, recursive macros and so on are the bane of C/C++ code and to prevent it is a good idea.
As a technical suggestion, I suggest that macros will expand to AST rather than source code, to prevent the need of preprocessors as it is seen in C/C++.
So to sum up the rules and looks:
design rules:
* Each macro has to start with the "macro" keyword, followed by the macro's identifier and the contents of the macro
* The content may be either single value/statement or a multiple lines of statements in {} brackets.
* Macros support multi-line content by default, no need to use \ to extend number of lines
* Macros must contain parse-able C2 code
* When used, an exclamation mark ! must be added to the name of the macro.
* A macro can be public or not, just like other declarations.
* There needs to be a distinction
(also described in the nim link): There are *expression* macros and
*statement/decl* macros. You cannot have a declaration if a place where
an expression is expected. It might be possible to discover this, but
it might be needed for a developer to specify this as well..
* The argument of a macro must only be an identifier or a literal, expressions are not allowed.
* No macro can be used within other macros
For point 7, I think that it could be figured out from the insides of the macro beforehand and each macro would be internally pre-assigned a type whether it's a "function" macro or an "expression" macro. Expression macros would be only able to contain an expression, otherwise it could be handled as a statement/function macro
syntax:
Declaration:
--- Code: ---
//statement macros
macro loudmultiplication(x, y) printf("value %d at line %d in file %s\n", (x*y), __LINE__, __FILE__);
public macro loudaddition(x, y) printf("value %d at line %d in file %s\n", (x+y), __LINE__, __FILE__);
public macro printfactorial(limit) {
int64 factorial = 1;
if(limit > 19) printf("error: the number is too big for signed 64-bit integers");
else {
for(int8 i = 0; i <= limit; i++) {
factorial *= i;
}
printf("factorial: %d", factorial);
}
}
macro myassert(desc, test) {
tests_ran++;
if (!(test)) {
char* error;
asprintf(&error, "[%s:%d]\x1b[31mTEST FAILED\x1b[0m: %s\n",__FILE__, __LINE__, desc);
asprintf(&finalmessage, "%s%s", finalmessage, error);
}
else {
char* success;
tests_passed++;
asprintf(&success, "[%s:%d]\x1b[32mTEST PASSED\x1b[0m: %s\n", __FILE__, __LINE__, desc);
asprintf(&finalmessage, "%s%s", finalmessage, success);
}
}
//expression or literal macros
macro dummyname "dummy";
macro makearray(count) malloc(sizeof(int32) * count);
macro anytrue(x, y, z, a, b, c) {
( x || y
|| z || a
|| b || c )
}
//usage
public func void testmacros() {
loudmultiplication!(25, 14);
loudaddition!(15, 41);
printfactorial!(14);
myassert!("test whether true is true", true);
puts(dummyname);
int32[] = makearray(5);
}
--- End code ---
On a second thought, how about allowing boolean expressions, too? It is better for unit testing and assertions.
Navigation
[0] Message Index
[#] Next page
Go to full version