iowo/docs/design/iowo-design.typ

179 lines
5 KiB
Text

#import "../template.typ": conf
#import "util/graphics.typ"
#import graphics: arrow
#show: conf.with(
title: [iOwO design],
subtitle: [don't worry, we're just dreaming],
)
= Type data model
== Requirements
- Color-aware
- It can handle colors and colorspaces for images
- OpenColorIO?
- number/number type support
- custom types (structs/enums)
- algebraic enums
- traits (`Numeric`...)
= Execution stages
#graphics.stages-overview
iOwO operates in stages. This has a number of benefits and implications:
- One can just work on one aspect of the stages without having to know about the rest.
- Bugs are easier to trace down to one stage.
- Stages are also replacable, pluggable and usable somewhere else.
- For example, one could write a Just-In-Time compiler to replace the runtime stage, while preserving the source #arrow() graph IR step.
However, this also makes the architecture somewhat more complicated. So here we try our best to describe how each stage looks like. If you have any feedback, feel free to drop it on #link("https://forge.katzen.cafe/katzen-cafe/iowo/issues")[the issues in the repository]!
== Source <source>
```iowo
open base.png >|
open stencil.png >|
mask
|> show
|> (invert | show)
```
Functional and pipeline-based.
However, in contrast to classic shell commands,
instructions can have multiple outputs and multiple inputs.
=== Instructions
The foundation of actually "doing" something.
- Can have any, even infinite, amount of inputs and outputs.
- Their amounts may or may not be equal.
- Inputs and outputs must have their types explicitly declared.
- An instruction with
- at least one output is called a streamer.
- at least one input is called a consumer.
- _both_ at least one input and at least one output is _both_ a streamer and a consumer, and culminatively called a modifier.
- May also contain spaces in its name.
==== Inputs <input>
- Based on position.
- Inputs can be provided both through the pipeline and ad-hoc.
- Ad-hoc inputs are called arguments.
- So all of these are equivalent:
```iowo
add 1 2 -- all inputs as arguments
[ 1 2 ] | add -- all inputs through the pipeline
1 | add 2 -- one input through the pipeline, one as argument
```
==== Outputs
- Also based on position.
=== Pipelines <pipeline>
- Exchange data between streamers and consumers.
==== Simple forwarding
In the simplest case, where inputs map to outputs bijectively#footnote[one to one], pipelines are just pipes and forward unchanged:
```iowo
open owo.png | invert | save owo.png
```
==== Splitting <splitting>
To handle each output of a streamer individually, they can be _split_:
```iowo
mask
|> show -- would show the masked part
|> invert -- would invert the unmasked part
```
==== Combination <combination>
To throw multiple streamers into the inputs of a consumer, they can be _combined_:
```iowo
open base.png >|
open stencil.png >|
mask
```
However, since lists are automatically spliced into inputs, this is equivalent to the above:
```iowo
[
open base.png,
open stencil.png,
]
| mask
```
=== Comments
Done with any of `--` or `//`.
=== Data types
==== Lists
- Signified by `[]` braces.
- If thrown into a pipeline, it automatically works like a streamer.
- Can be delimited by commas.
- Must be delimited by commas if a contained streamer takes an input.
- May have a trailing comma.
- Outputs of streamers are spliced into the contained list.
- In effect, they are automatically flattened.
== Graph IR
#graphics.graph-example
The parsed representation of the source, and also what the runtime operates on.
In a way, this is the AST, except that it's not a tree.
It is represented in iOwO using adjacencies, where essentially the vertices#footnote[Nodes or instructions in this case.] and their edges#footnote[Connections or pipes in this case.] are stored separately.
=== Optimizer
Merges and simplifies instructions in the graph IR.
== Runtime
Runs through all instructions in the graph IR. It does not have any significantly other representation, and despite its name there's _no_ bytecode involved.
=== Scheduler
Looks at the graph IR and decides when the VM should execute what.
=== VM <vm>
= Open questions
- @input
- At which position are arguments injected into instruction inputs?
- How could that be controlled if set to e.g. the end by default?
- Not all inputs are order-independent, e.g. `div`
- Should inputs and outputs really be positional?
- Could make more complex instructions hard to read
- But keyworded could also make code very noisy
- Maybe a middle ground, such that at most 1 input is allowed to be positional?
- @pipeline
- We need some way to reshuffle and reorder outputs and inputs in a pipeline
- @splitting
- How would one split different outputs into a list?
- Should outputs that are not thrown into a consumer be automatically displayed in some kind of debug view?
- Or should that be done instead using a debug `show` instruction or the like?
- Should consumers be called sinks instead?
- Shorter
- More ambiguous if only looking at the first char though