-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathcompiled.go
142 lines (120 loc) · 3.87 KB
/
compiled.go
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
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
package twig
import (
"bytes"
"encoding/gob"
"fmt"
"time"
)
func init() {
// Register all node types for serialization
gob.Register(&RootNode{})
gob.Register(&TextNode{})
gob.Register(&PrintNode{})
gob.Register(&BlockNode{})
gob.Register(&ForNode{})
gob.Register(&IfNode{})
gob.Register(&SetNode{})
gob.Register(&IncludeNode{})
gob.Register(&ExtendsNode{})
gob.Register(&MacroNode{})
gob.Register(&ImportNode{})
gob.Register(&FromImportNode{})
gob.Register(&FunctionNode{})
gob.Register(&CommentNode{})
// Expression nodes
gob.Register(&ExpressionNode{})
gob.Register(&VariableNode{})
gob.Register(&LiteralNode{})
gob.Register(&UnaryNode{})
gob.Register(&BinaryNode{})
}
// CompiledTemplate represents a compiled Twig template
type CompiledTemplate struct {
Name string // Template name
Source string // Original template source
LastModified int64 // Last modification timestamp
CompileTime int64 // Time when compilation occurred
AST []byte // Serialized AST data
}
// CompileTemplate compiles a parsed template into a compiled format
func CompileTemplate(tmpl *Template) (*CompiledTemplate, error) {
if tmpl == nil {
return nil, fmt.Errorf("%w: cannot compile nil template", ErrCompilation)
}
// Serialize the AST (Node tree)
var astBuf bytes.Buffer
enc := gob.NewEncoder(&astBuf)
// Encode the AST
if err := enc.Encode(tmpl.nodes); err != nil {
// If serialization fails, we'll still create the template but without AST
// and log the error with template details
fmt.Printf("Warning: Failed to serialize AST for template '%s': %v\n",
tmpl.name, err)
}
// Store the template source, metadata, and AST
compiled := &CompiledTemplate{
Name: tmpl.name,
Source: tmpl.source,
LastModified: tmpl.lastModified,
CompileTime: time.Now().Unix(),
AST: astBuf.Bytes(),
}
return compiled, nil
}
// LoadFromCompiled loads a template from its compiled representation
func LoadFromCompiled(compiled *CompiledTemplate, env *Environment, engine *Engine) (*Template, error) {
if compiled == nil {
return nil, fmt.Errorf("%w: cannot load from nil compiled template", ErrCompilation)
}
var nodes Node
// Try to use the cached AST if available
if len(compiled.AST) > 0 {
// Attempt to deserialize the AST
dec := gob.NewDecoder(bytes.NewReader(compiled.AST))
// Try to decode the AST
err := dec.Decode(&nodes)
if err != nil {
// Fall back to parsing if AST deserialization fails
fmt.Printf("Warning: Failed to deserialize AST for template '%s', falling back to parsing: %v\n",
compiled.Name, err)
nodes = nil
}
}
// If AST deserialization failed or AST is not available, parse the source
if nodes == nil {
parser := &Parser{}
var err error
nodes, err = parser.Parse(compiled.Source)
if err != nil {
return nil, fmt.Errorf("failed to parse compiled template: %w", err)
}
}
// Create the template from the nodes (either from AST or freshly parsed)
tmpl := &Template{
name: compiled.Name,
source: compiled.Source,
nodes: nodes,
env: env,
engine: engine,
lastModified: compiled.LastModified,
}
return tmpl, nil
}
// SerializeCompiledTemplate serializes a compiled template to a byte array
func SerializeCompiledTemplate(compiled *CompiledTemplate) ([]byte, error) {
var buf bytes.Buffer
enc := gob.NewEncoder(&buf)
if err := enc.Encode(compiled); err != nil {
return nil, fmt.Errorf("failed to serialize compiled template: %w", err)
}
return buf.Bytes(), nil
}
// DeserializeCompiledTemplate deserializes a compiled template from a byte array
func DeserializeCompiledTemplate(data []byte) (*CompiledTemplate, error) {
dec := gob.NewDecoder(bytes.NewReader(data))
var compiled CompiledTemplate
if err := dec.Decode(&compiled); err != nil {
return nil, fmt.Errorf("failed to deserialize compiled template: %w", err)
}
return &compiled, nil
}