refactor(mustache): Simplified Node parser.

This commit is contained in:
Spencer Brower
2026-07-21 16:34:10 -04:00
parent 5960d3686c
commit 986038f54c
2 changed files with 141 additions and 169 deletions
BIN
View File
Binary file not shown.
+47 -75
View File
@@ -53,19 +53,18 @@ Node :: struct {
filters: [dynamic; MAX_PIPES]Pipe_Filter, filters: [dynamic; MAX_PIPES]Pipe_Filter,
is_dynamic: bool, is_dynamic: bool,
indent: string, indent: string,
first_child: int, children: []Node,
child_count: int,
content: string, content: string,
pos: int, pos: int,
} }
// node_span returns the number of flat-array entries a node occupies: // node_span returns the number of flat-array entries a node occupies:
// 1 for leaf nodes, 1 + child_count for container nodes (whose children // 1 for leaf nodes, 1 + len(children) for container nodes (whose children
// are stored contiguously after them in the array). // are stored contiguously after them in the array).
node_span :: proc(n: Node) -> int { node_span :: proc(n: Node) -> int {
#partial switch n.kind { #partial switch n.kind {
case .Section, .Inverted, .Parent, .Block: case .Section, .Inverted, .Parent, .Block:
return 1 + n.child_count return 1 + len(n.children)
case: case:
return 1 return 1
} }
@@ -78,9 +77,7 @@ Template :: struct {
} }
Block_Override :: struct { Block_Override :: struct {
all_nodes: []Node, nodes: []Node,
first: int,
count: int,
source: Template, source: Template,
} }
@@ -122,7 +119,7 @@ parse :: proc(
} }
tmpl.source = source tmpl.source = source
tmpl.path = path tmpl.path = path
deindent_blocks(tmpl.nodes[:], 0, len(tmpl.nodes), allocator) deindent_blocks(tmpl.nodes[:], allocator)
return tmpl, nil return tmpl, nil
} }
@@ -144,7 +141,7 @@ render :: proc(
append(&ctx, data) append(&ctx, data)
all_nodes := tmpl.nodes[:] all_nodes := tmpl.nodes[:]
err = render_nodes(tmpl, all_nodes, all_nodes, &ctx, partials, &builder) err = render_nodes(tmpl, all_nodes, &ctx, partials, &builder)
if err != nil { if err != nil {
return result, err return result, err
} }
@@ -170,6 +167,7 @@ parse_tokens :: proc(
nodes = make([dynamic]Node, 0, len(tokens), allocator) nodes = make([dynamic]Node, 0, len(tokens), allocator)
pos := 0 pos := 0
err = parse_section(tokens, &pos, &nodes, "", source, allocator, 0) err = parse_section(tokens, &pos, &nodes, "", source, allocator, 0)
assert(len(nodes) <= cap(nodes))
return return
} }
@@ -187,12 +185,12 @@ parse_section :: proc(
switch tok.kind { switch tok.kind {
case .Text: case .Text:
append(nodes, Node{kind = .Text, text = tok.value, first_child = -1, pos = tok.pos}) append(nodes, Node{kind = .Text, text = tok.value, pos = tok.pos})
pos^ += 1 pos^ += 1
case .Variable: case .Variable:
idx := len(nodes) idx := len(nodes)
append(nodes, Node{kind = .Variable, first_child = -1}) append(nodes, Node{kind = .Variable})
pipe_key, perr := parse_pipeline(tok.value, &nodes[idx].filters, tok.pos) pipe_key, perr := parse_pipeline(tok.value, &nodes[idx].filters, tok.pos)
if perr != nil { if perr != nil {
return Error_Body { return Error_Body {
@@ -206,7 +204,7 @@ parse_section :: proc(
case .Unescaped: case .Unescaped:
idx := len(nodes) idx := len(nodes)
append(nodes, Node{kind = .Unescaped, first_child = -1}) append(nodes, Node{kind = .Unescaped})
pipe_key, perr := parse_pipeline(tok.value, &nodes[idx].filters, tok.pos) pipe_key, perr := parse_pipeline(tok.value, &nodes[idx].filters, tok.pos)
if perr != nil { if perr != nil {
return Error_Body { return Error_Body {
@@ -226,7 +224,7 @@ parse_section :: proc(
idx := len(nodes) idx := len(nodes)
content_start := 0 content_start := 0
if pos^ < len(tokens) {content_start = tokens[pos^].pos} if pos^ < len(tokens) {content_start = tokens[pos^].pos}
append(nodes, Node{kind = .Section, first_child = -1, pos = tok.pos}) append(nodes, Node{kind = .Section, pos = tok.pos})
pipe_key, perr := parse_pipeline(tok.value, &nodes[idx].filters, tok.pos) pipe_key, perr := parse_pipeline(tok.value, &nodes[idx].filters, tok.pos)
if perr != nil { if perr != nil {
return Error_Body { return Error_Body {
@@ -239,8 +237,7 @@ parse_section :: proc(
parse_section(tokens, pos, nodes, pipe_key, source, allocator, tok.pos) or_return parse_section(tokens, pos, nodes, pipe_key, source, allocator, tok.pos) or_return
close_pos := 0 close_pos := 0
if pos^ - 1 >= 0 && pos^ - 1 < len(tokens) {close_pos = tokens[pos^ - 1].pos} if pos^ - 1 >= 0 && pos^ - 1 < len(tokens) {close_pos = tokens[pos^ - 1].pos}
nodes[idx].first_child = idx + 1 nodes[idx].children = nodes[idx + 1:len(nodes)]
nodes[idx].child_count = len(nodes) - idx - 1
nodes[idx].content = source[content_start:close_pos] nodes[idx].content = source[content_start:close_pos]
case .Inverted_Open: case .Inverted_Open:
@@ -248,7 +245,7 @@ parse_section :: proc(
idx := len(nodes) idx := len(nodes)
content_start := 0 content_start := 0
if pos^ < len(tokens) {content_start = tokens[pos^].pos} if pos^ < len(tokens) {content_start = tokens[pos^].pos}
append(nodes, Node{kind = .Inverted, first_child = -1, pos = tok.pos}) append(nodes, Node{kind = .Inverted, pos = tok.pos})
pipe_key, perr := parse_pipeline(tok.value, &nodes[idx].filters, tok.pos) pipe_key, perr := parse_pipeline(tok.value, &nodes[idx].filters, tok.pos)
if perr != nil { if perr != nil {
return Error_Body { return Error_Body {
@@ -261,8 +258,7 @@ parse_section :: proc(
parse_section(tokens, pos, nodes, pipe_key, source, allocator, tok.pos) or_return parse_section(tokens, pos, nodes, pipe_key, source, allocator, tok.pos) or_return
close_pos := 0 close_pos := 0
if pos^ - 1 >= 0 && pos^ - 1 < len(tokens) {close_pos = tokens[pos^ - 1].pos} if pos^ - 1 >= 0 && pos^ - 1 < len(tokens) {close_pos = tokens[pos^ - 1].pos}
nodes[idx].first_child = idx + 1 nodes[idx].children = nodes[idx + 1:len(nodes)]
nodes[idx].child_count = len(nodes) - idx - 1
nodes[idx].content = source[content_start:close_pos] nodes[idx].content = source[content_start:close_pos]
case .Section_Close: case .Section_Close:
@@ -301,7 +297,6 @@ parse_section :: proc(
key = tok.value, key = tok.value,
is_dynamic = tok.is_dynamic, is_dynamic = tok.is_dynamic,
indent = tok.indent, indent = tok.indent,
first_child = -1,
pos = tok.pos, pos = tok.pos,
}, },
) )
@@ -316,13 +311,11 @@ parse_section :: proc(
kind = .Parent, kind = .Parent,
key = tok.value, key = tok.value,
indent = tok.indent, indent = tok.indent,
first_child = -1,
pos = tok.pos, pos = tok.pos,
}, },
) )
parse_section(tokens, pos, nodes, tok.value, source, allocator, tok.pos) or_return parse_section(tokens, pos, nodes, tok.value, source, allocator, tok.pos) or_return
nodes[idx].first_child = idx + 1 nodes[idx].children = nodes[idx + 1:len(nodes)]
nodes[idx].child_count = len(nodes) - idx - 1
case .Block_Open: case .Block_Open:
pos^ += 1 pos^ += 1
@@ -333,13 +326,11 @@ parse_section :: proc(
kind = .Block, kind = .Block,
key = tok.value, key = tok.value,
indent = tok.indent, indent = tok.indent,
first_child = -1,
pos = tok.pos, pos = tok.pos,
}, },
) )
parse_section(tokens, pos, nodes, tok.value, source, allocator, tok.pos) or_return parse_section(tokens, pos, nodes, tok.value, source, allocator, tok.pos) or_return
nodes[idx].first_child = idx + 1 nodes[idx].children = nodes[idx + 1:len(nodes)]
nodes[idx].child_count = len(nodes) - idx - 1
} }
} }
@@ -357,43 +348,38 @@ parse_section :: proc(
// Post-parse: de-indent block content // Post-parse: de-indent block content
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
deindent_blocks :: proc(all_nodes: []Node, start: int, end: int, allocator := context.allocator) { deindent_blocks :: proc(nodes: []Node, allocator := context.allocator) {
i := start i := 0
for i < end { for i < len(nodes) {
#partial switch all_nodes[i].kind { #partial switch nodes[i].kind {
case .Block: case .Block:
if all_nodes[i].child_count > 0 { if len(nodes[i].children) > 0 {
cs := all_nodes[i].first_child children := nodes[i].children
ce := cs + all_nodes[i].child_count deindent_blocks(children, allocator)
deindent_blocks(all_nodes, cs, ce, allocator)
children := all_nodes[cs:ce]
common := find_common_indent(children) common := find_common_indent(children)
if len(common) > 0 { if len(common) > 0 {
if len(all_nodes[i].indent) == 0 { if len(nodes[i].indent) == 0 {
all_nodes[i].indent = common nodes[i].indent = common
} }
for j := cs; j < ce; { for j := 0; j < len(children); {
if all_nodes[j].kind == .Text && len(all_nodes[j].text) > 0 { if children[j].kind == .Text && len(children[j].text) > 0 {
all_nodes[j].text = remove_line_indent( children[j].text = remove_line_indent(
all_nodes[j].text, children[j].text,
common, common,
allocator, allocator,
) )
} }
j += node_span(all_nodes[j]) j += node_span(children[j])
} }
} }
} }
case .Section, .Inverted, .Parent: case .Section, .Inverted, .Parent:
if all_nodes[i].child_count > 0 { if len(nodes[i].children) > 0 {
cs := all_nodes[i].first_child deindent_blocks(nodes[i].children, allocator)
ce := cs + all_nodes[i].child_count
deindent_blocks(all_nodes, cs, ce, allocator)
} }
} }
i += node_span(all_nodes[i]) i += node_span(nodes[i])
} }
} }
@@ -523,9 +509,9 @@ render_template :: proc(
context.temp_allocator, context.temp_allocator,
context.temp_allocator, context.temp_allocator,
) or_return ) or_return
return render_nodes(reparse, reparse.nodes[:], reparse.nodes[:], ctx, partials, b, blocks) return render_nodes(reparse, reparse.nodes[:], ctx, partials, b, blocks)
} }
return render_nodes(pt, pt.nodes[:], pt.nodes[:], ctx, partials, b, blocks) return render_nodes(pt, pt.nodes[:], ctx, partials, b, blocks)
} }
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
@@ -534,7 +520,6 @@ render_template :: proc(
render_nodes :: proc( render_nodes :: proc(
current: Template, current: Template,
all_nodes: []Node,
nodes: []Node, nodes: []Node,
ctx: ^[dynamic]any, ctx: ^[dynamic]any,
partials: map[string]Template, partials: map[string]Template,
@@ -574,7 +559,6 @@ render_nodes :: proc(
render_nodes( render_nodes(
sub_tpl, sub_tpl,
sub_tpl.nodes[:], sub_tpl.nodes[:],
sub_tpl.nodes[:],
ctx, ctx,
partials, partials,
&temp, &temp,
@@ -612,7 +596,6 @@ render_nodes :: proc(
render_nodes( render_nodes(
sub_tpl, sub_tpl,
sub_tpl.nodes[:], sub_tpl.nodes[:],
sub_tpl.nodes[:],
ctx, ctx,
partials, partials,
&temp, &temp,
@@ -648,7 +631,6 @@ render_nodes :: proc(
render_nodes( render_nodes(
sub_tpl, sub_tpl,
sub_tpl.nodes[:], sub_tpl.nodes[:],
sub_tpl.nodes[:],
ctx, ctx,
partials, partials,
b, b,
@@ -656,7 +638,7 @@ render_nodes :: proc(
) or_return ) or_return
} }
} else if is_truthy(val) { } else if is_truthy(val) {
children := all_nodes[node.first_child:node.first_child + node.child_count] children := node.children
elem_info, count, data := list_info(val) elem_info, count, data := list_info(val)
if elem_info != nil { if elem_info != nil {
for j in 0 ..< count { for j in 0 ..< count {
@@ -665,7 +647,6 @@ render_nodes :: proc(
defer pop(ctx) defer pop(ctx)
render_nodes( render_nodes(
current, current,
all_nodes,
children, children,
ctx, ctx,
partials, partials,
@@ -676,10 +657,10 @@ render_nodes :: proc(
} else { } else {
append(ctx, val) append(ctx, val)
defer pop(ctx) defer pop(ctx)
render_nodes(current, all_nodes, children, ctx, partials, b, blocks) or_return render_nodes(current, children, ctx, partials, b, blocks) or_return
} }
} }
i += 1 + node.child_count i += 1 + len(node.children)
case .Inverted: case .Inverted:
val := resolve_name(node.key, ctx[:]) val := resolve_name(node.key, ctx[:])
@@ -694,10 +675,9 @@ render_nodes :: proc(
val = transformed val = transformed
} }
if !is_truthy(val) { if !is_truthy(val) {
children := all_nodes[node.first_child:node.first_child + node.child_count] render_nodes(current, node.children, ctx, partials, b, blocks) or_return
render_nodes(current, all_nodes, children, ctx, partials, b, blocks) or_return
} }
i += 1 + node.child_count i += 1 + len(node.children)
case .Partial: case .Partial:
name := node.key name := node.key
@@ -715,22 +695,19 @@ render_nodes :: proc(
case .Block: case .Block:
content_nodes: []Node content_nodes: []Node
content_pool: []Node
content_blocks := blocks content_blocks := blocks
render_current := current render_current := current
found_override := false found_override := false
if blocks != nil { if blocks != nil {
if o, ok := blocks[node.key]; ok { if o, ok := blocks[node.key]; ok {
content_nodes = o.all_nodes[o.first:o.first + o.count] content_nodes = o.nodes
content_pool = o.all_nodes
found_override = true found_override = true
render_current = o.source render_current = o.source
} }
} }
if !found_override { if !found_override {
content_nodes = all_nodes[node.first_child:node.first_child + node.child_count] content_nodes = node.children
content_pool = all_nodes
} }
if len(node.indent) > 0 { if len(node.indent) > 0 {
@@ -738,7 +715,6 @@ render_nodes :: proc(
strings.builder_init(&temp, context.temp_allocator) strings.builder_init(&temp, context.temp_allocator)
render_nodes( render_nodes(
render_current, render_current,
content_pool,
content_nodes, content_nodes,
ctx, ctx,
partials, partials,
@@ -749,7 +725,6 @@ render_nodes :: proc(
} else { } else {
render_nodes( render_nodes(
render_current, render_current,
content_pool,
content_nodes, content_nodes,
ctx, ctx,
partials, partials,
@@ -757,11 +732,11 @@ render_nodes :: proc(
content_blocks, content_blocks,
) or_return ) or_return
} }
i += 1 + node.child_count i += 1 + len(node.children)
case .Parent: case .Parent:
parent_children := all_nodes[node.first_child:node.first_child + node.child_count] parent_children := node.children
merged := merge_block_overrides(parent_children, all_nodes, blocks, current) merged := merge_block_overrides(parent_children, blocks, current)
pt, found := partials[node.key] pt, found := partials[node.key]
if !found { if !found {
warn_missing_partial(current, partials, node, node.key) warn_missing_partial(current, partials, node, node.key)
@@ -769,7 +744,7 @@ render_nodes :: proc(
warn_unmatched_block_overrides(current, pt, parent_children) warn_unmatched_block_overrides(current, pt, parent_children)
render_template(pt, ctx, partials, b, merged, node.indent) or_return render_template(pt, ctx, partials, b, merged, node.indent) or_return
} }
i += 1 + node.child_count i += 1 + len(node.children)
} }
} }
return nil return nil
@@ -777,7 +752,6 @@ render_nodes :: proc(
merge_block_overrides :: proc( merge_block_overrides :: proc(
children: []Node, children: []Node,
all_nodes: []Node,
existing: map[string]Block_Override, existing: map[string]Block_Override,
source: Template, source: Template,
) -> map[string]Block_Override { ) -> map[string]Block_Override {
@@ -793,9 +767,7 @@ merge_block_overrides :: proc(
if child.kind == .Block { if child.kind == .Block {
if _, exists := result[child.key]; !exists { if _, exists := result[child.key]; !exists {
result[child.key] = Block_Override { result[child.key] = Block_Override {
all_nodes = all_nodes, nodes = child.children,
first = child.first_child,
count = child.child_count,
source = source, source = source,
} }
} }