General Category > Ideas

[Email suggestion No. 1] macros in C2

(1/2) > >>

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