# Dev Notes
This living document covers *active* design thinking / questions.
Once the design is perfected (lol!) there shouldn't be anything left here.
## Design gotchas
### Per-element pipeline
A clean design here would be something like:
1. Derive SvgElement from event(s), including any tail or content
2. Evaluate attributes (e.g. expressions, variable lookup)
3. Resolve positioning, setting updated attributes as required
4. Render events required for the modified element(s), including any relevant whitespace
This probably works effectively for a simple input document.
It gets tricky where things like 'evaluate attributes' has side-effects,
such as iterating a PRNG, or setting variables (especially things such
as `k="{{$k + 1}}"`), which are not idempotent.
This requires care to ensure these operations only happen once during
the pipeline (while restoring variable state might be ok for the 'increment'
side effect, un-getting a random number from a generator isn't something
I want to deal with).
There are also cases where info is only known at a later point (e.g.
forward references for element layout, or evaluating bounding boxes of
container elements). Sometimes we need to back out of processing in order
to re-try later, but this typically happens during 'resolve positioning'
step, and if the 'evaluate attribute' step has already happened, side-effects
to the context have already occurred and may not be un-doable.
One approach would be to do a topological sort on references early on,
avoiding the need to 'abort and retry' on reference errors, but that
fails if we have e.g. `id="thing-$k"` or `xy="#{thing-$k}:h"`, where
the references depend on 'current' document state. (I'm keen to keep the
ability to have non-static ids.)
### Error handling
The current error handling of svgdx isn't great; end-user experience is OK,
with unresolvable elements output with line number info, but internally it's
a bit of a mess.
The current state effectively uses `Result` objects and `?` propagation to
emulate exceptions, and post-hoc checks of 'did it go better this time' to
determine whether progress has been made or the processing has stalled.
`anyhow` has made this easy, but I'm not sure it's good; the code really needs
to distinguish between 'retryable' errors (e.g. reference lookup to something
which might be a forward reference) and unrecoverable errors. Other than reference
errors propagated to enable forward references, many error conditions just
leave attribute values (for example) unchanged; having `<circle r="thing"/>`
should probably be a hard error rather than being left as-is; it's more likely
there's a missing '$' in there somewhere, and reporting this as an error
would be useful.
Alternatively having warnings when e.g. `strp` fails might be helpful, though
the current system only returns errors or the document. Perhaps metadata
analogous to the `data-src-line` - e.g. `data-warning` - would allow the
client program (using `config.add_metadata`) to extract the info without
changing to return the document and some out-of-band data?
Tidying up the error handling would also improve the UX; it wouldn't just list
unresolvable elements, but *why* a particular element could not be resolved.
Therefore should probably investigate `thiserror` or similar, either to augment
or replace `anyhow`.
### Regex replacement
During earlier WASM investigations using `twiggy` showed that well over 50% of
the code size was due to `regex` handling. Even after moving to `regex-lite` it's
still a significant proportion of the code size.
Should investigate moving to `nom` to both centralize parsing and see if it has
code size / perf improvements (or maybe just hand-code something - there's nothing
particularly tricky AFAICT).
## Open questions
### Round-tripping / preserving SVG document structure
Early on the goal for `svgdx` was that any SVG document not using svgdx extensions
would be untouched by processing. Structures such as `AttrMap` and `ClassList`
carefully maintain input order. However this increases complexity in various places
(e.g. handling text between elements, rather than as element content).
Relaxing the 'no-op for SVG' requirement, and instead ensuring that any *output*
of svgdx processing would be untouched (i.e. round-tripping for a subset of SVG,
rather than effectively all possible XML) would make things easier and more
consistent.
Some middle ground is still possible here, e.g. if an element starts on a new line
in the input, do the same in the output. But preserving arbitrary XML 'tail' text
(rather than recreating it based on line number and indent values) may not be necessary.
Early in svgdx development I was very focussed on whether the output *text* looked
'right', but I'm now using svgdx-editor a lot more during development, and care
more about whether the output document *rendered as SVG* looks 'right'.
Therefore should probably give up on trying to process arbitrary Text / CDATA events
and immediately connect them as element content, and recreate whitespace based on
indent values. (Note 'recreating' whitespace is already necessary when additional
elements are generated, such as from `text` attributes on graphics elements.)
### Self-consistent, or consistent with SVG?
Consider `<use>`. It takes `x` and `y` attributes, which are (semantically) translated
by an SVG user agent into `transform="translate(x, y)"` on the instanced target.
For consistency with this, `<reuse>` should probably do the same thing. But internal
consistency and uniform positioning (i.e. that any sufficient constraint on x/y/x2/y2/...
will position elements) would imply that `x`/`y` denote the top-left of the target
bounding-box... In some cases this is the same, but if a circle defined with `cxy="0"`
is `<use>`d then the bounding box will be `r` to the upper left of the given `x`/`y`
attribute pair...
Should probably prioritise consistency with SVG, and have `<reuse>` be as similar as
possible to `<use>`, so it's main benefit is in templating where context-dependent
expressions make a difference. Downside: `x` / `y` are special cased, where other
attributes are effectively local variables for the re-used target.
### Auto-numeric Expression Handling
In general, expressions evaluated in `{{..}}` contexts are converted to numbers,
and function calls are evaluated in place. However in some conditions (e.g. `while`
conditions on loops) the value must always be a number, so the brace pairs may be
omitted.
Should this be extended to every attribute which requires a numeric value? Or remove
the shortcut on the existing evaluations using this? Consistency doesn't have to be
absolute, but where it is lacking there needs to be a principled / documented reason
for it, which currently isn't the case.
Motivation: should `<loop count="$expr">` be changed to automatically use numeric
evaluation for `$expr`?
### String lists
Numeric data lists can be provided with a comma separated list of values; strings
cannot be similarly specified. For a short list, using a dynamic id variable is
a reasonable workaround, but this is more inconsistency.
```
<var num_list="1, 2, 3, 4, 5"/>
<var s0="Monday" s1="Tuesday" s2="Wednesday" s3="Thursday" s4="Friday"/>
<rect xy="0" wh="0"/>
<loop count="5" loop-var="i">
<rect xy="^:v" wh="10 5" text="{{select($i, $num_list)}}"/>
<rect xy="^:v" wh="10 5" text="$s${i}"/>
</loop>
```
### Previous N reference
It's often useful to refer to the 'last-but-one' element, which isn't currently
possible without using an `id`.
Perhaps something like `^^`, with reasonable extension (e.g. no more than 10)
to earlier elements. This would allow nicer grids which need to alternate `:h`
and `:v` type relative positions.
### Auto-style class combinations
Is it better to have combination class names for auto-styles, or require multiple
classes to implement this?
e.g. is it better to have `class="d-flow-rev-fast"` or `class="d-flow d-flow-rev d-flow-fast"`,
or some combination where only one parameter (e.g. flow speed) is allowed as part of
the 'base' class name? (so `d-flow-rev` is a separate 'boolean' flag, but speed can
be included as e.g. `d-flow-faster`). If a 'simple' reverse flow is needed, this does
require `class="d-flow d-flow-rev"`, which feels verbose, as though `d-flow-rev` should
imply `d-flow`?
The first is clearly more concise, but it's not obvious which order the various aspects
should be. Maybe they could be entirely dynamic, including use of numbers in class
names? e.g. `d-text-size-12` or similar.
The existing text attributes tend to use the 'separate classes' approach, e.g.
`class="d-text-bigger d-text-mono"`
Note ergonomics should drive this rather than ease of implementation.
### Auto-style rules - inherited in nested elements?
If a class such as `d-red` is applied to a `<g>` element, should it apply to all the
contained elements (which don't override it)?
Applying to `.d-red, .d-red *` or similar would do, or even `.d-red g.d-red *`, but
will this allow override be a more local application of a class?
The following is an example of how this could work. Note that if the second rule of
each pair is `g.b-xyz *` rather than `.b-xyz *`, the specifity exceeds the bare `b-blue`
of rect 'd', so that's not feasible. With CSS, being more precise results in increasing
priority, when it would be nice if these could be independent...
```xml
<svg>
<style>
.b-red:not(text), .b-red *:not(text) {fill:red;}
.b-blue:not(text), .b-blue *:not(text) {fill:blue;}
</style>
<rect wh="3" text="a"/>
<rect wh="^" xy="^:v 2" class="b-red" text="b"/>
<g class="b-red">
<rect wh="^" xy="5 0" text="c"/>
<rect wh="^" xy="^:v 2" class="b-blue" text="d"/>
</g>
</svg>
```
Should probably change most of the rules to include the `.class` to be `.class, .class *`.
### Local attribute ordering.
Consider the following two use-cases of local attributes:
* **group / scope** level - here the _closer_ the definition of the variable is to the actual
instance, the higher the priority it has.
* **reuse** scope - here the _further away_ the definition of the variable is, the higher
priority it has. Closer definitions may act as defaults, but they can always be overridden
by the "call site" of the instancing.
Both these cases assume that there are multiple levels of hierarchy, i.e. nested `<g>` elements
or `<reuse>` elements which refer to other `<reuse>` elements. In either case, the actually
'instantiation' place is the most important and most local, but the cases differ in where that
is relative to the actual elements which represent rendered shapes.
The group scope approach works fine as is, but the reuse scope only works 'one level deep' -
if a top-level `<reuse>` defines an attribute-variable, this will be used in the final render.
But 'multi-level' reuse doesn't currently follow this naturally, i.e. the following doesn't
work, when it seems a reasonable use-case to have 'defaults' (and potentially multiple layers
of specialisation):
```xml
<specs>
<g id="a"><rect xy="0" wh="5" text="$t"/></g>
<reuse id="t1" href="#a" t="1"/>
<reuse id="t2" href="#a" t="2"/>
<reuse id="t3" href="#a" t="3"/>
</specs>
<reuse href="#t2"/>
```
### Variable lookup in expressions
When should variable lookup happen?
It seems useful to have element references which may be parameterized by a variable,
e.g. `xy="$el:h"`, and then define `el="#blob"` elsewhere. This is catered for fine;
prior to any positioning, attributes are evaluated, and at that point it will be
replaced with `xy="#blob:h"`, which later feeds in to positioning logic.
Note this implies a further sequencing operation: 'compound' attributes such as `xy`
already have an implicit 'splitting' action, such that `xy` will be split into `x`, `y`
attribute pairs if there isn't a relative position (e.g. ':h') involved. But this is
still reasonable: first expand variables, then check for relative positioning, then
finally split (as appropriate) into different target attributes.
Where this gets more complex is when a variable defines another variable.
```xml
<var v1="1" v2="2"/>
<var select="v1"/>
<var target="$$select"/>
<text text="$target"/>
```
### Empty entries in variable lists
What should the following return?
```xml
<text text="{{count(,,,,)}}"/>
```
There are three reasonable alternatives:
* It should return 0, having first filtered out any empty entries (in `expr_list()` or
similar).
* It should return 5; a comma implies entries on either side, so should be n+1 where n
is the number of commas present. There should probably be an additional 'Null' variant
of `ExprValue` to account for this.
* It is an error, and therefore is unchanged. This is the current behaviour. This aligns
with expressions (including all current functions) taking only numeric values.
This should probably (only) be revisited once `ExprValue` is extended to take string values
(both lists and single values).
### Variable types: const and global
Variables are 'local' to the scope they are defined (through attributes on a container
element) or set (via the `<var>` element). Once any nested element closes, the variable
values set in that scope disappear / revert to any previously set values.
This is probably the expected behaviour and seems a sensible default.
However there may be cases where 'global' variables which ignore scope are useful. One
example would be to count the number of times a particular fragment is `reuse`d; this
could be done with something along the lines of `<global reuse_count="{{$reuse_count + 1}}"/>`
Whether `global` is the right element name, or it should be done through some other
convention (e.g. variables beginning with a certain character?) is an open question.
A separate use-case would be for 'constant' values, where they are frozen when first set
(and possibly generate errors if a subsequent set - at least to a different value - is
attempted).
This could be useful for 'constants' such as $PI, $SQRT2 and similar, and might be implemented
using something like `<const PI="3.1415927"/>`.