105 lines
3.3 KiB
Go
105 lines
3.3 KiB
Go
package fejkdata
|
|
|
|
import (
|
|
"github.com/larvit/fejkdata/internal/drawstate"
|
|
"github.com/larvit/fejkdata/internal/invariant"
|
|
)
|
|
|
|
// readValue is what one read drew: its text, and whether it landed on a null.
|
|
type readValue struct {
|
|
text string
|
|
null bool
|
|
}
|
|
|
|
// renderEnv is where a render reads: the frames of the name scopes rendering; and row, the row
|
|
// of the table rendering, which its columns read. It passes by value, and what is read from it
|
|
// reaches a map key, an interface or a func value only as a copy; else env, and with it every
|
|
// render's frames, moves to the heap.
|
|
type renderEnv struct {
|
|
frames *frameStack
|
|
row renderedRow
|
|
// base is the depth of the frame stack when the read rendering the category entered it,
|
|
// below which no frame is the category's.
|
|
base int
|
|
// pick is the named pick being rendered, nil outside one; pickAt is the key in it of what renders.
|
|
pick *namedPick
|
|
pickAt pickKey
|
|
}
|
|
|
|
type renderedRow struct {
|
|
t *table
|
|
index int
|
|
}
|
|
|
|
// at returns the env n renders in, n being the leaf of a path whose rows sit in pins; where n
|
|
// is a table's row or column, the env carries the row pins holds for that table.
|
|
func (env renderEnv) at(n node, pins *pinSet) renderEnv {
|
|
switch n := n.(type) {
|
|
case *tableRow:
|
|
env.row = renderedRow{n.t, pins.MustRow(n.t.rows)}
|
|
case *tableColumn:
|
|
env.row = renderedRow{n.t, pins.MustRow(n.t.rows)}
|
|
}
|
|
return env
|
|
}
|
|
|
|
func (env renderEnv) rowOf(t *table) int {
|
|
if env.row.t != t {
|
|
panic(invariant.Broken("a column of %s renders in an env holding no row of it", t.rows.Segment()))
|
|
}
|
|
return env.row.index
|
|
}
|
|
|
|
// enter starts a read landing on n: the read sees no frame opened before it, and gets a frame for
|
|
// each name scope around n, from memo where there is one, so reads sharing memo read one pick of
|
|
// each name. It returns the mark that closes those frames.
|
|
func (env renderEnv) enter(n node, memo *drawMemo) (renderEnv, int) {
|
|
env = env.entering()
|
|
for scope := scopeAround(n); scope != nil; scope = scope.up {
|
|
if len(scope.order) > 0 {
|
|
env.frames.push(memo.enteredFrame(scope))
|
|
}
|
|
}
|
|
return env, env.base
|
|
}
|
|
|
|
// entering is env as a read entering a category sees it: no frame opened before it, and no pick.
|
|
func (env renderEnv) entering() renderEnv {
|
|
env.base, env.pick = len(env.frames.frames), nil
|
|
return env
|
|
}
|
|
|
|
// renderFrame opens a fresh frame of t's scope, where t binds names and no read entering the
|
|
// category opened one, returning the mark that closes it, or -1.
|
|
func (env renderEnv) renderFrame(t *template) int {
|
|
own := t.ownScope()
|
|
if own == nil || env.frameOf(own) != nil {
|
|
return -1
|
|
}
|
|
return env.frames.push(newPickFrame(own))
|
|
}
|
|
|
|
// frameOf is the frame of scope rendering since the read entering the category, nil where none is.
|
|
func (env renderEnv) frameOf(scope *nameScope) *pickFrame {
|
|
for i := len(env.frames.frames) - 1; i >= env.base; i-- {
|
|
if f := env.frames.frames[i]; f.scope == scope {
|
|
return f
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// drawRowOf draws the row a render of t reads: inside the pick's rows where t renders as part of one.
|
|
func (env renderEnv) drawRowOf(s *drawstate.State, t *table) int {
|
|
if env.pick != nil {
|
|
return t.rows.DrawIn(s, &env.pick.pins)
|
|
}
|
|
return t.rows.Draw(s)
|
|
}
|
|
|
|
// keeps reports whether a read of env's name addresses the level a starts at.
|
|
func (env renderEnv) keeps(a arm) bool {
|
|
_, kept := env.pick.named.addressed[env.pickAt.under(a.levels[0])]
|
|
return kept
|
|
}
|