Help to build a macro to mimic the With of VBA

Hi guys!

Sometimes, the With feature of VBA can be very useful. Consider something like:

    general.insert("simProgress", generalFrame.simProgress);
    general.insert("simulationDate", simulationDate);
    general.insert("a", generalFrame.a);
    general.insert("e", generalFrame.e);
    general.insert("i", generalFrame.i);
    general.insert("raan", generalFrame.raan);
    general.insert("w", generalFrame.w);
    general.insert("f", generalFrame.f);
    general.insert("lat", generalFrame.lat);
    general.insert("lon", generalFrame.lon);
    general.insert("h", generalFrame.h);
    general.insert("eclipse", generalFrame.eclipse == 0x01);
    general.insert("gmst", generalFrame.GMST);
    general.insert("sun_i", v_sun_i);

I would like to write a macro that I can do something like:

@with generalFrame
    general.insert("simProgress", .simProgress);
    general.insert("simulationDate", simulationDate);
    general.insert("a", .a);
    general.insert("e", .e);
    general.insert("i", .i);
    general.insert("raan", .raan);
    general.insert("w", .w);
    general.insert("f", .f);
    general.insert("lat", .lat);
    general.insert("lon", .lon);
    general.insert("h", .h);
    general.insert("eclipse", .eclipse == 0x01);
    general.insert("gmst", .GMST);
    general.insert("sun_i", v_sun_i);
end

I thought it was simple, every time I find an expression starting with ., then I replace with general.. However, .lat, for example, is not a valid Julia expression. Hence, I am stuck and now I need help :slight_smile:

1 Like

I’m not entirely sure what the effect of the VBA code is… As Kristoffer often says if you want to write a macro start with the code that you /the user would write, and then the desired code that you would expect if the macro would work as intended.
If you can produce an MWE along those lines I’m sure people here can help!

1 Like

The effect I want to achieve is pretty simple. I want to have a macro to avoid typing the name of a structure over and over again. So, the very minimum example is to turn

mutable struct MyStructure
    a::Int64
    b::Int64
    c::Int64
end

mystruct = MyStructure(0,1,2)

@with mystruct begin
    .a = 1
    .b = 2
    .c = 3
end

into

mutable struct MyStructure
    a::Int64
    b::Int64
    c::Int64
end

mystruct = MyStructure(0,1,2)

mystruct.a = 1
mystruct.b = 2
mystruct.c = 3
1 Like

Macros transform Julia syntax to other Julia syntax. If you don’t want the input to be Julia syntax then you need to use a string macro.

4 Likes

hum I see, thanks for the tip. The problem with the string macro is that the editors will show without the highlights. Maybe I need to find a good replacement literal.

Do g = generalFrame then use g.a etc.
Only 1 more character to type, no macro required, and it’s clearer to read :wink:

4 Likes

OK, you convinced me :slight_smile:

I think for DataFrames something of the sort already exists

What type is general there? Feels like you should be able to automate that all with a dictionary or similar.

1 Like

It is a structure with configuration parameters (it has something like 100 variables). I think it can be written using fieldnames but I am not sure if the effort is worthy. The parameters may or may not need a unit conversion.

Does Parameters.jl help at all?

1 Like

You could use a specific pattern, eg an underscore

_simProgress

and walk the code using MacroTools.jl to replace each symbol beginning with an underscore with the relevant expression. You would, of course, have to distinguish assignment, which could be tricky.

Generally, these patterns are more useful for interpreted languages, where the contents of a composite type can be appended to the list of variables at runtime — eg R has similar with() and within() macros. But for Julia, the suggestion by @dpsanders is much more idiomatic.

1 Like

This might be a nice use of a let block to keep the temporary names in a confined scope:

let g = general, gf = generalFrame
    g.insert("simProgress",     gf.simProgress);
    g.insert("simulationDate",  simulationDate);
    g.insert("a",               gf.a);
    g.insert("e",               gf.e);
    g.insert("i",               gf.i);
    g.insert("raan",            gf.raan);
    g.insert("w",               gf.w);
    g.insert("f",               gf.f);
    g.insert("lat",             gf.lat);
    g.insert("lon",             gf.lon);
    g.insert("h",               gf.h);
    g.insert("eclipse",         gf.eclipse == 0x01);
    g.insert("gmst",            gf.GMST);
    g.insert("sun_i",           v_sun_i);
end
4 Likes

Sometimes, it seems an overkill to use Parameters.jl IMHO. But I do use it a lot.

This is indeed awesome! Thanks!