import { InitTag, HasMeta, HasContexts, HasInit, TaggedToken, TokenTag, TokenByTag, ContextKey, ContextToken, ContextMeta, ParserResult } from './model'; export interface RenderFunc, Token extends ContextToken, Meta> { (handle: RenderHandle, token: Exclude): Type; } export interface JoinFunc, Meta> { (handle: RenderHandle, elms: Type[]): Type; } export interface RenderHandle, Meta> { r: RenderMethods; // render methods j: JoinFunc; // join method c: Ctx; // current context m: Meta; // render meta } export type ContextRenderMethods, Meta> = { [Tag in TokenTag>]: RenderFunc, Tag>, Meta>; }; export type RenderMethods = { [Ctx in ContextKey]: ContextRenderMethods; }; export type TaggedRenderRule, Tag extends TokenTag>, Meta> = [ Ctx, Tag, RenderFunc, Tag>, Meta> ]; export type RenderRule, Meta> = TaggedRenderRule>, Meta>; export type RenderRules = RenderRule, ContextMeta>[]; export interface Renderer> { r: RenderMethods>; j: JoinFunc>; s: Ctx; } export interface InitRender { (...rules: RenderRules): Renderer; >(start: Ctx, ...rules: RenderRules): Renderer; } export function makeRender(join: JoinFunc): InitRender { return ((...rules: any[]) => initRender(join, ...rules)) as InitRender; } function initRender(join: JoinFunc, ContextMeta>, ...rules: RenderRules): Renderer; function initRender>(join: JoinFunc, ContextMeta>, start: Ctx, ...rules: RenderRules): Renderer; function initRender(join: JoinFunc, ContextMeta>, ...rules: RenderRules): Renderer { let start = typeof rules[0] == 'number' ? rules.shift() : 0; const ctxs: (ContextKey)[] = []; for (const [ctx] of rules) { if (ctxs.indexOf(ctx) < 0) { ctxs.push(ctx); } } const renderers = {} as RenderMethods>; for (const context of ctxs) { const funcs = {} as ContextRenderMethods>; for (const [ctx, tag, func] of rules) { if (ctx == context) { funcs[tag] = func; } } renderers[context] = funcs; } return { r: renderers, j: join, s: start }; } export function render>({ r, j, s }: Renderer, [meta, tokens]: ParserResult): Type { const $: RenderHandle> = { r, j, m: meta, c: s }; return renderNest($, tokens); } export function renderNest, NestedCtx extends ContextKey>($: RenderHandle>, tokens: ContextToken[], ctx: NestedCtx): Type; export function renderNest>($: RenderHandle>, tokens: ContextToken[]): Type; export function renderNest, NestedCtx extends ContextKey>($: RenderHandle>, tokens: ContextToken[], ctx: Ctx | NestedCtx = $.c): Type { const $$ = $.c === ctx ? $ : { ...$, c: ctx }; const elms: Type[] = []; for (const token of tokens) { elms.push(($$.r[$$.c] as any)[(token as any as TaggedToken).$]($$, token)); } return $$.j($$, elms); }