Pikchr

Nested Macro issue
Login

Nested Macro issue

(1) By Martin Gagnon (mgagnon) on 2020-09-28 16:47:42 [link] [source]

Hi,

I start to use the very recent pikchr macro support.

It works so far and help to reduce a lot of copy paste, but found an issue when trying to pass macro argument to a nested macro.

Minimal Reproducible Example:

  • This doesn't works:
define m00 {
  box $1 wid $2 ht $3
}
define m01 {
  m00("foo", $1, $2)  # <-- use of $1 and $2 as argument of a nested macro is the problem.
}
m01(0.2, 0.3)
  • While this works:
define m00 {
  box $1 wid $2 ht $3
}
define m01 {
  m00("foo", 0.2, 0.3)
}
m01()

Regards

(2) By drh on 2020-09-28 18:20:16 in reply to 1 [link] [source]

The code (so people can at least see the error message):

foo
define m00 {
  box $1 wid $2 ht $3
}
define m01 {
  m00("foo", $1, $2)  # <-- use of $1 and $2 as argument of a nested macro is the problem.
}
m01(0.2, 0.3)

I knew about this as I was writing the code, but then I forgot about it again during the heat of battle. The problem might be resolvable, as long as the $1 used as an argument to another macro is the only term in that argument. If you say something like:

   define m01 {
      m00("foo", wid $2)
   }

In other words, if the argument to m00 is a combination with $2 and other stuff, then I don't think I have a way to fix that.

In any event, I have some other more pressing matters to see to first.

(3) By drh on 2020-09-30 10:47:02 in reply to 2 [link] [source]

In my previous post, the Pikchr script that was formerly generating an error message now works.

Note again the restriction: Arguments to a sub-macro can be:

  • Arbitrary text that does not contains any of $1, $2, ..., $9
  • One of the parameters $1, $2, ..., $9 with no other surrounding text

But arguments may not be contain a parameter together with additional text or two or more parameters. This restriction, together with a limit of 10 on the depth of macro nesting, helps to prevent malicious posters from writing Pikchr scripts that use excessive memory.

(4) By Martin Gagnon (mgagnon) on 2020-09-30 15:16:10 in reply to 3 [link] [source]

Thanks you very much, this fix save me a lot lines of pikchr code.

In the following pikchr, I repeat a lot those sbox* macros which use the Probe* macros.

The following example is only a small part of it:

Master S1 rx1 A1 tx1 rx2 A2 tx2 rx3 tx3 rx4 tx4 S2 rx1 B1 tx1 rx2 B2 tx2 rx3 tx3 rx4 tx4 S3 C1 rx1 C2 tx1 C3 rx2 C4 tx2 rx3 tx3 rx4 tx4 S4 D1 rx1 D2 tx1 D3 rx2 D4 tx2 rx3 tx3 rx4 tx4
$prsz = 0.03
$prgap = 0.04
$prlen = 0.30
$boxW = 0.6
$boxH = $prgap*26
$prLabelW = 0.22
$boxGap = $prLabelW + 2*$prgap + $prlen + $boxW + 0.2

define probeConnFirst {
    circle "" rad $prsz at previous box.nw+(-$prlen,-$prgap*2)
    arrow right until even with previous box.w
    text $1 small at $prLabelW left of previous circle
    text $2 small color 0x555555 at 0.1 right of previous arrow.e
}

define probeDisconnFirst {
    circle invis "" rad $prsz*0.3 fill black at previous box.nw+(-$prlen,-$prgap*2)
    arrow color 0xaaaaaa right until even with previous box.w
    text $1 small color 0x555555 at 0.1 right of previous arrow.e
}

define probeConn {
    circle "" rad $prsz at $prgap*2+$3 below previous circle; 
    arrow right until even with end of previous arrow
    text $1 small at $prLabelW left of previous circle
    text $2 small color 0x555555 at 0.1 right of previous arrow.e
}

define probeDisconn {
    circle invis "" rad $prsz*0.2 fill black at $prgap*2+$2 below previous circle
    arrow color 0xaaaaaa right until even with end of previous arrow
    text $1 small color 0x555555 at 0.1 right of previous arrow.e
}

define sboxSingle12 {
    box $1 wid $boxW ht $boxH $4

    probeDisconnFirst("rx1")
    probeConn($2, "tx1", 0)
    probeDisconn("rx2", 0.1)
    probeConn($3, "tx2", 0)
    probeDisconn("rx3", 0.1)
    probeDisconn("tx3", 0)
    probeDisconn("rx4", 0.1)
    probeDisconn("tx4", 0)
}

define sboxX {
    box $1 wid $boxW ht $boxH $6
    probeConnFirst($2, "rx1")
    probeConn($3, "tx1", 0)
    probeConn($4, "rx2", 0.1)
    probeConn($5, "tx2", 0)
    probeDisconn("rx3", 0.1)
    probeDisconn("tx3", 0)
    probeDisconn("rx4", 0.1)
    probeDisconn("tx4", 0)
}

M: box "Master" wid $boxW ht $boxH
sboxSingle12("S1", "A1", "A2", at $boxGap right of previous box) 
sboxSingle12("S2", "B1", "B2", at $boxGap right of previous box) 
sboxX("S3", "C1", "C2", "C3", "C4", at $boxGap right of previous box) 
sboxX("S4", "D1", "D2", "D3", "D4", at $boxGap right of previous box) 
arrow -> from M.s down 0.2 then right to 0.2 below 2nd box.s up 0.2 
arrow -> down 0.2 then right to 0.2 below 3rd box.s up 0.2
arrow -> down 0.2 then right to 0.2 below 4th box.s up 0.2
arrow -> down 0.2 then right to 0.2 below 5th box.s up 0.2

Note:

It would be useful to share a file with macros across different pikchr block. May be via a kind of include "<file|url>/to/file/with/macros" directive ?

Right now, I have multiple pikchr markdown fenced block that need those sbox* macros, I need to repeat the macro definition on every pikchr block.

(5) By Stephan Beal (stephan) on 2020-09-30 15:35:52 in reply to 4 [link] [source]

It would be useful to share a file with macros across different pikchr block. May be via a kind of include "<file|url>/to/file/with/macros" directive ?

pikchr is designed to behave well on internet-facing servers, where "behave well" requires minimizing potential attack surface. Adding the ability to include arbitrary files, and then the OS-dependent APIs needed to handle them (see fossil's UTF8-related handling needed for Windows), would be a huge step in the opposite direction from that goal.

In environments, like fossil, where the server generally offers users no access to filesystem-level resources except via its database handle, an include feature would require very different semantics from a C-like include feature.

Features like include belong in a level above pikchr - whatever scripting language or preprocessor one cares to use (noting that a C preprocessor could be used as-is for that purpose).

(6) By drh on 2020-09-30 15:59:17 in reply to 4 [link] [source]

Stephan is right - we should not allow "include" on an internet-facing system like this.

However.... Legacy PIC has the idea that variables and macro definitions that are define in one PIC section of a document are carried forward into all subsequent PIC sections of the same document. Pikchr does not currently do that. In Pikchr, at the moment, each "~~~ pikchr" block starts fresh with variables set to their defaults and no macros defined. But, we could change that so that if a single document contained multiple "~~~ pikchr" blocks, the variables and macros could be set up in just the first block and then reused in all subsequent blocks of the same document. This would complicate the C-language API a bit, but it is doable. I have put off doing it because there has not (as of yet) been a need.

What is your use-case Martin? Do you have multiple circuit diagrams within the same document, where you would benefit from having macros from the first diagram hang around to be reused in subsequent diagrams of the same document?

(7) By Martin Gagnon (mgagnon) on 2020-09-30 16:22:57 in reply to 6 [link] [source]

Do you have multiple circuit diagrams within the same document, where you would benefit from having macros from the first diagram hang around to be reused in subsequent diagrams of the same document?

Yes, I would definitively benefit from this. I would need to repeat the macros on each document instead of on each diagram.

Right now I have 2 different documents with about 4 different diagrams on each using the same set of macros.

(8) By Pepijn Van Eeckhoudt (pepijnve) on 2020-09-30 19:32:01 in reply to 4 [link] [source]

Hi Martin,

I don't know if it's an option for you, but if you're not tied to markdown one possible workaround is to use asciidoc include macros for this. You can write something like this:

[pikchr]
----
include::my_macros.txt[]

sboxSingle12("S1", "A1", "A2", at $boxGap right of previous box)
----

and the asciidoctor implementation will inline the referenced my_macros.txt file for you.

(9) By Martin Gagnon (mgagnon) on 2020-09-30 21:28:24 in reply to 8 [link] [source]

Right now I use pikchr with markdown that is integrated inside fossil and I would prefer to keep it like this.

Thanks for the suggestion.

(10) By Warren Young (wyoung) on 2020-10-01 20:44:03 in reply to 6 [source]

Do you have multiple circuit diagrams within the same document, where you would benefit from having macros from the first diagram hang around to be reused in subsequent diagrams of the same document?

It turns out that Fossil's own docs have a relevant use case: the pre-Pikchr rebaseharm.md doc reused the old rebase04.svg file twice, the second time to avoid making the user scroll back up to see where it was used the first time.

"See figure 4" three pages after the figure is presented is justified in a book where paper costs money, but on the web, we had the right solution here: just reference it again inline.

Pikchr doesn't currently allow that. We have to repeat the diagram code and re-render it.

Cross Pikchr block macros would solve it, though they're a bit overkill, in this case: this would be a zero-parameter macro.

Martin's use case is also valuable: I can definitely see organizations building up common macro libraries to simplify the writing of later diagrams. I haven't looked into the code behind your railroad diagrams, drh, but I'd bet there's a lot of repetition across diagrams in there.