On blocks

On blocks

(1) By Andreas Kupries (akupries) on 2020-09-19 21:14:57 [source]

The "[...]" style subblocks are supported and they work just as well.

I am not sure about that. The "[...]" and "{...}" have behavioral differences, to the point that the two forms can be considered complementary.

"[...]" blocks:

  • Scopes variables, places, and backreferences ("previous", "nth", etc.)

  • Does not scope layout direction and position (The "here" symbol). Changes made in the block are visible outside.

"{...}" blocks on the other hand behave the other way around:

  • No scoping of variables, places, and backrefs. No enclosing block element, with compass points.

  • Scopes changes to layout direction and position in the block. Exiting the block resets direction and position to what they were before the block.

    In BWL, page 4, just before section 5.

    If a set of commands is enclosed in braces "{...}", the current position and direction of motion when the group is finished will be exactly where it was when entered. Nothing else is restored. There is also a more general way to group objects, using "[" and "]", which is discussed in Section 9.

I believe that both behaviors have their place and uses.

Sometimes you even want both. Which could be had by either "{[...]}", or "[{...}]" Although less so than in PIC, there the combination is pretty crucial to enabling the results of macros to be (nearly?) indistinguishable from a builtin class. In other words an outer "[...]" to make it an element, and a nested "{...}" to prevent the innards of the block from mangling direction and position.

(2) By Andreas Kupries (akupries) on 2020-09-19 22:36:22 in reply to 1 [link] [source]

This made me realize that we are near to having a simple macro facility already, without proc definitions and the like.


Proposing the following:

  • Add syntax "X := ..." as extension of "X: ...". The new syntax collects the object as normal. The object is not drawn however, and the statement does not affect layout direction, and position.

  • All objects X defined with the new operator above can be used in place of a builtin class name. Essentially a short form of "same as X", which in turn is a variant of "same as" without a leading class name, copying X in full, attributes and sub elements.

  • Add syntax "VAR ?= VALUE" which assigns the value to the variable if and only if the variable is not defined yet.

  • If an ":=" defined object X is a "[...]" block then all variables V assigned with the new "?=" operator inside of the block are made accessible as attributes when X is used, allowing the user to override these defaults.


Note that the examples are incomplete with respect to all the possible attributes we may wish to give to the symbols. Especially the diamond.

diamond := [ { 
    @height ?= boxht
    @width  ?= boxwid
    @radius ?= 0
    box invis width @width height @height
    line radius @radius from last.w to last.n to last.e to last.s close
} ]

diamond height 150% width 75%
flag_de := [ {
    @height ?= boxht
    @width ?= boxwid
    boxht  = @height/3
    boxwid = @width
    box fill black 
    box fill red   color red
    box fill gold  color gold
} ]

flag_de width 150% height 80%
and_gate := [ {
B: box invis wid boxht/2 ; S: B.ne ; C: B.e ; E: B.se ; sr = dist(S, C)
line \
  from B.nw \
  to S \
  to sr heading  15 from C \
  to sr heading  30 from C \
  to sr heading  45 from C \
  to sr heading  60 from C \
  to sr heading  75 from C \
  to sr heading  90 from C \
  to sr heading 105 from C \
  to sr heading 120 from C \
  to sr heading 135 from C \
  to sr heading 150 from C \
  to sr heading 165 from C \
  to E to B.sw to B.nw \
  close fill white
# ATTENTION: Spline does not `fill`, even closed, like a line would.
# When going to spline we can remove the 15, 45, 75, etc. points.
# We also have to double the `to B.sw` clause to get the proper sharp turn
} ]

Flow of thoughts leading to the summary

Assuming that "same as" copies not just the attributes, but for "[]"-blocks the entire set of children as well.

Define a custom symbol via

warning: [ { ... definition of the warning symbol ... } ]

Use the custom symbol via:

[] same as warning

Issues with the idea above:

  • No arguments. That is ok, I called it "simple" above for a reason.

  • The "X: ..." syntax does not just define X, it also draws the block. We do not want that for a symbol definition. Note that adding an "invis" then keeps it invisible at use time also. So maybe a small new syntax:

    X := ...

    Define X without drawing

  • The "[] same as X" syntax is cumbersome. It might be easier to read as "same as warning". In other words allow "same as X" without a preceding class name. In that form the new object will also have the same class as the referenced X.

  • Sizing. We can currently only move the copy to a different location. For symbols/icons, as Stephan has started to collect in his pikmojicon, rescaling them for the context they are used in would be useful at least, if not outright required. To make that not too complex maybe a new "scale" attribute ?

With these changes in place we can create utility libraries of common symbols, reusable in many diagrams, and in many places of the same diagram.

They are still not quite builtins, but near enough I believe, to be useful.

  • The compass points for example are always box offsets.
  • You cannot change the styles, colors, etc. of the symbol (Although I have an idea for that percolating in the back of my mind).

Reworked example:

flag_de := [ { down ; boxht *= 1/2
               box wid 150% fill black 
               box wid 150% fill red   color red
               box wid 150% fill gold  color gold } ]

diamond := [ { box invis ; line from last.w to last.n to last.e to last.s close } ]

# ... much later ...

same as diamond
same as flag_de at last scaled 1/2

Another alternate syntax, instead of "same as" without class just allow any FOO defined via "FOO := ...." as a new class name.

That would actually fit with my idea for attributes in such definitions as well.

A new assignment operator "?=", used as "var ?= value". The assignment is only made if the variable is not defined yet. In other words, this kind of assignment supplies a default value for the variable, to be used if nothing is specified in the context.

I originally had the idea in the context of https://pikchr.org/home/forumpost/b2281e2d34 as a means of allowing parameterization of symbol definitions by putting this kind of default assignment at the beginning, and then supply parameters either as command line arguments of "pikchr", or as supplemental pikchr sources coming before the symbol file.

However in the context of blocks this kind of operator would allow for

diamond := [ { 
    @height ?= boxht ; @width ?= boxwid ; @radius ?= 0
    box invis wid @width ht @height
    line radius @radius from last.w to last.n to last.e to last.s close
} ]

diamond height 150% width 75%

# This example is incomplete, not setting up parameters/attributes
# for color, fill, thickness, etc.

In other words, the "?=" assigned variables of the block become the parameters which can be set as attributes when the block is used.

... A last thing I have not worked out yet, is text handling. ... Can we attach text to "[...]" blocks ? ... Checking, apparently not.

Going back to compass points.

IIRC legacy PIC allows access to locations inside a "[...]" block, by exporting "X: ..." places, i.e. making them accessible as, for example "last [] .X"

BWK page 13, section 9:

You can get at the internal place names with constructs like "last [].A" or "B.A" where "B" is a name attached to a block: "B: [ ... ; A: ...; ]"

Implementing that would allow export of custom compass points for user-defined symbols.

Another consequence might be the need for syntax to distinguish hidden and exported locations. ... I am tending to syntax for hidden, to preserve the original behaviour.

This for things like logic gates, where we would wish to export the attachment points for inputs and output, (i.e ".a", ".b", ".z", ...), but none of the helper locations used to construct the gate.