CDSoft / pp

PP - Generic preprocessor (with pandoc in mind) - macros, literate programming, diagrams, scripts...
http://cdelord.fr/pp
GNU General Public License v3.0
252 stars 21 forks source link

Extend `!undef` to Accept Multiple Parameters/Symbols #33

Closed tajmone closed 6 years ago

tajmone commented 6 years ago

I've discovered that if I create a macro that takes optional parameters, and then the macro is invoked without passing the last optional parameters (eg: \3 and \4) at all (ie: not even empty values), the corresponding parameters of a previous macro call might creep in.

For example:

!firstmacro(param1)(param2)(param3)(param4)
!secondmacro(param1)(param2)

... where in !secondmacro params \3 and \4 are optional, and checked with !ifdef(3)(...) and !ifdef(4)(...), and in the above example the macro will see the 3rd and 4th params previously passed to !firstmacro.

A solution would be to always call the macro with empty values for unused parameters:

!firstmacro(param1)(param2)(param3)(param4)
!secondmacro(param1)(param2)()()

But to make life easier in actual usage, a more elegant solution seems to be undefining at the end of each custom macro all the possible parameter-symbols used by it:

!undef(4) !undef(3) !undef(2) !undef(1)

I think that !undef's syntax should allow an unlimeted number of parameters to simplify destroying multiple symbols at once:

!undef(4)(3)(2)(1)

... it would be more friendly an neat.

CDSoft commented 6 years ago

That's indeed a serious bug because of the way variables and arguments are internally represented. Removing arguments at the end of the macro is dangerous because you can remove arguments of the caller. I need to rewrite some part of the preprocessor to separate variables, arguments and some other internal definitions that are mixed in the same type of data.

tajmone commented 6 years ago

I see.

Maybe this bug creeped in when you fixed issue #29. I remember that before that fix parameter symbols where local to a macro and were destroyed at macro's end, but I thought the new state of affairs was a desired result of the #29 fix.

If I understood correctly, a macro's parameters (\1 ... \n) are special symbols whose lifespan should be restricted to the macro being called, and they should be only local in scope.

Isn't there a script in the test folder to test macros definitions, nesting and parameters?

CDSoft commented 6 years ago

I think this bug is way older. Anyway I have a fix. I changed some types to separate things that were stored in the same data structure (macros, arguments, but also any parameters). I has a huge impact on the whole source code but thanks to the power of Haskell, it's a matter of changing types, adding a few code to handle macro arguments, compiling and it works. I love Haskell ;-)

I need some more time to clean the code and add some specific test cases.

The test folder contains tests for nested macros but it doesn't check if macro arguments are really local.

I hope I can release something in a few days. It may be a bit longer. My fix has a infinite loop when a macro gives an undefined parameter to another macro...

CDSoft commented 6 years ago

The version 1.11 should fix this problem. There are no binary available yet. I can not compile Windows binaries with Wine because of a new Haskell module. Workaround: use the source...

tajmone commented 6 years ago

Right, I'll finally install Haskell on Win OS also (currently I've installed it only on Ubuntu).

Is this Win binary compilation problem temporary? (ie: waiting for a module to be updated)

Just asking because my final project will download and unpack prebuilt PP binaries of the specific release indicated in the project settings.

CDSoft commented 6 years ago

I don't know how to fix this problem. I compile windows binaries under Linux with Wine. A module that is used in the last version requires a POSIX environment, which Wine doesn't provide. And I have no "real" Windows. I can try to find an alternative to this module.

tajmone commented 6 years ago

I did some quick search on Haskell cross-compilation, to get a better picture.

It seems that common solutions when Wine fails are to either:

  1. Use LLVM backend,
  2. Compile on Windows running in a virtual machine.

The latter seems the easiest solution, but it would still require possessing a Windows disk + license (but it's less invasive than installing Win on a separate HD partition).

I don't know much about the former solution.

I can setup Haskell on my Win OS and compile it for you, this might help in terms of testing new releases on Windows, but it wouldn't make things easier in terms of long-term maintainance of the prebuilt binaries on your website downloads page.

In the meantime I'll try to setup Haskell on my machine (Win 10 x64) and see if I can compile this specific release, and let you know.

CDSoft commented 6 years ago

I have removed the dependency to the module that doesn't compile on Wine. There are now binaries 1.11 for Linux and Windows.

tajmone commented 6 years ago

Works perferctly! Thanks...

The bug seemed to creep only in nested calls.

I've tested it with:

!def(ONE)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
!ifdef(1)(first: \1)

!ifdef(2)(second: \2)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

!def(TWO)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
!ifne(\1)()(first: \1)

!ifne(\2)()(second: \2)

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

!def(TEST1)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
!ONE(\1)(\2)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

!def(TEST2)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
!TWO(\1)(\2)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

# TEST ONE

!TEST1(double)(double)

!TEST1(single)

# TEST TWO

!TEST2(double)(double)

!TEST2(single)

... previous PP would always behave as if 2 params were present. Now it works correctly.

This would allow to avoid passing empty optional final parameters in macros calls (currently I had to always pass empty params, which was a bit verbose).