1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
|
/* Shader expressions: a tiny integer language for the one number a shader
* takes, compiled to bytecode at config load.
*
* The idea is smaller than "shaders in a language". Every built-in shader here
* is already `colour_op(amount)` where the amount is a pure function of where
* the cell is:
*
* vignette amount = f(x, y) ruler amount = (x == col) * amount
* zebra amount = f(y) margin amount = (x >= col) * amount
*
* So the part worth handing to a config is the *amount*, not the colour maths.
* That keeps the mixing in C where it is fast and correct, gives an expression
* no way to produce an invalid cell (it produces one byte), and \u2014 because the
* result is a pure function of position \u2014 lets the whole program be evaluated
* once into a per-cell map and then reused, which is what makes an interpreter
* affordable at all. Measured: 56ns/cell interpreted, 2ns/cell from the map.
*
* The language is deliberately total. No loops, no recursion, no jumps: even
* `?:` compiles to evaluating both sides and selecting, which is safe because
* nothing in here has an effect. Evaluation is therefore O(program length),
* a bad expression cannot hang a session, and there is nothing to sandbox.
*
* variables x y cols rows curx cury cursor focused t since above below
* operators + - * / % < > <= >= == != && || ! ?:
* functions min max abs clamp dist
*
* `x`/`y` are cell positions inside the rect the pass runs over (a pane's
* contents, or its frame for a chrome pass), `t` is a millisecond clock for
* animation, `since` is how many milliseconds the pane has been in the state
* it is in — which is what a one-shot effect needs, because `t` says what time
* it is and not how long ago something happened. `above`/`below` are the lines
* of scrollback hidden past the viewport's top and bottom edges, both 0 at
* the present — which is what lets an effect say "there is more this way".
* `dist` corrects for a cell being about twice as tall as it is wide. Division and modulo by zero are 0
* rather than a trap: an expression that is wrong should look wrong, not take
* the session with it.
*/
#ifndef SLOSH_EXPR_H
#define SLOSH_EXPR_H
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
typedef struct expr_prog expr_prog_t;
/* What a program reads, worked out by the compiler rather than declared. This
* is what decides whether its output can be cached and against what, so it is
* derived from the source and can never disagree with it. */
enum {
EXPR_DEP_POS = 1 << 0, /* x, y */
EXPR_DEP_CURSOR = 1 << 1, /* curx, cury, cursor */
EXPR_DEP_TIME = 1 << 2, /* t: recomputed every frame, never cached */
EXPR_DEP_FOCUS = 1 << 3, /* focused */
/* cols, rows. Not part of the cache key -- the key always carries the rect,
* because a map is per rect by construction -- but it still has to be a
* dependency, or a program reading only the size would look constant and be
* folded at compile time, where the size is not known yet and is zero. */
EXPR_DEP_SIZE = 1 << 4,
EXPR_DEP_SCROLL = 1 << 5, /* above, below */
};
/* Compile, or NULL with a reason in `err`. A program that reads nothing is
* folded to its value at compile time and still returned, so callers have one
* shape to handle. */
expr_prog_t *expr_compile(const char *src, char *err, size_t errcap);
void expr_free(expr_prog_t *p);
/* What it reads (EXPR_DEP_*). 0 means the result is a constant. */
unsigned expr_deps(const expr_prog_t *p);
/* The value of a program that reads nothing; undefined otherwise. */
int expr_constant(const expr_prog_t *p);
/* Everything a program can see. Filled by the shader pass. */
typedef struct {
int x, y, cols, rows;
int curx, cury, cursor;
int focused;
int64_t t;
/* Milliseconds since the pane entered its current state, clamped at 0. */
int64_t since;
/* Lines of scrollback hidden above and below the viewport, both 0 at the
* present (and for a pass over anything that is not a pane's contents). */
int above, below;
} expr_env_t;
/* Evaluate for one cell. Clamped to 0..255 by the caller, not here: the
* language has no opinion about what its number is for. */
int expr_eval(const expr_prog_t *p, const expr_env_t *env);
/* The text this program was compiled from, owned by the program. For the
* config dump, which has to write back the line somebody wrote rather than
* pretend an expression was a number. */
const char *expr_source(const expr_prog_t *p);
/* The language's sine, for C code that animates: degrees in (any angle
* wraps), -255..255 out -- the same table `sin()` in an expression uses. */
int expr_sin(int deg);
/* Evaluate for every cell of a cols x rows rect into `out` (cols*rows bytes,
* clamped to 0..255), reusing the last result when nothing it reads has
* changed. Returns false when the program reads the clock, which is the one
* case there is nothing to reuse. */
bool expr_amount_map(expr_prog_t *p, const expr_env_t *env, uint8_t **out);
#endif /* SLOSH_EXPR_H */
|