2017-06-25 18:36:28 +02:00
|
|
|
//go:generate stringer -type=ChunkType
|
|
|
|
|
2017-06-26 15:36:52 +02:00
|
|
|
package chunks
|
2017-06-25 17:23:20 +02:00
|
|
|
|
|
|
|
import (
|
|
|
|
"regexp"
|
|
|
|
)
|
|
|
|
|
2017-06-26 18:41:41 +02:00
|
|
|
// Chunk is a chunk of a template source. It is one of an object "{{…}}", a tag "{%…%}", or the text between objects and tags.
|
2017-06-25 17:23:20 +02:00
|
|
|
type Chunk struct {
|
2017-06-26 18:41:41 +02:00
|
|
|
Type ChunkType
|
|
|
|
SourceInfo SourceInfo
|
|
|
|
Source string // Source is the entirety of the chunk, including the "{{", "{%", etc. markers.
|
|
|
|
Tag string // Tag is the tag name of a tag Chunk. E.g. the tag name of "{% if 1 %}" is "if".
|
|
|
|
Args string // Args is the tag arguments of a tag Chunk. E.g. the tag arguments of "{% if 1 %}" is "1".
|
2017-06-25 17:23:20 +02:00
|
|
|
}
|
|
|
|
|
2017-06-26 18:41:41 +02:00
|
|
|
// SourceInfo contains a Chunk's source information
|
2017-06-25 17:23:20 +02:00
|
|
|
type SourceInfo struct {
|
|
|
|
Pathname string
|
|
|
|
lineNo int
|
|
|
|
}
|
|
|
|
|
2017-06-26 18:41:41 +02:00
|
|
|
// ChunkType is the type of a Chunk
|
2017-06-25 17:23:20 +02:00
|
|
|
type ChunkType int
|
|
|
|
|
|
|
|
const (
|
2017-06-26 18:41:41 +02:00
|
|
|
TextChunkType ChunkType = iota // TextChunkType is the type of a text Chunk
|
|
|
|
TagChunkType // TagChunkType is the type of a tag Chunk "{%…%}"
|
|
|
|
ObjChunkType // TextChunkType is the type of an object Chunk "{{…}}"
|
2017-06-25 17:23:20 +02:00
|
|
|
)
|
|
|
|
|
|
|
|
var chunkMatcher = regexp.MustCompile(`{{\s*(.+?)\s*}}|{%\s*(\w+)(?:\s+(.+?))?\s*%}`)
|
|
|
|
|
2017-06-26 18:41:41 +02:00
|
|
|
// Scan breaks a string into a sequence of Chunks.
|
2017-06-26 16:15:01 +02:00
|
|
|
func Scan(data string, pathname string) []Chunk {
|
2017-06-26 18:41:41 +02:00
|
|
|
// TODO error on unterminated {{ and {%
|
|
|
|
// TODO probably an error when a tag contains a {{ or {%, at least outside of a string
|
2017-06-25 17:23:20 +02:00
|
|
|
var (
|
|
|
|
sourceInfo = SourceInfo{pathname, 0}
|
|
|
|
out = make([]Chunk, 0)
|
|
|
|
p, pe = 0, len(data)
|
|
|
|
matches = chunkMatcher.FindAllStringSubmatchIndex(data, -1)
|
|
|
|
)
|
|
|
|
for _, m := range matches {
|
|
|
|
ts, te := m[0], m[1]
|
|
|
|
if p < ts {
|
2017-06-26 18:41:41 +02:00
|
|
|
out = append(out, Chunk{TextChunkType, sourceInfo, data[p:ts], "", ""})
|
2017-06-25 17:23:20 +02:00
|
|
|
}
|
|
|
|
switch data[ts+1] {
|
|
|
|
case '{':
|
2017-06-26 18:41:41 +02:00
|
|
|
out = append(out, Chunk{ObjChunkType, sourceInfo, data[ts:te], "", data[m[2]:m[3]]})
|
2017-06-25 17:23:20 +02:00
|
|
|
case '%':
|
|
|
|
var args string
|
|
|
|
if m[6] > 0 {
|
|
|
|
args = data[m[6]:m[7]]
|
|
|
|
}
|
2017-06-26 18:41:41 +02:00
|
|
|
out = append(out, Chunk{TagChunkType, sourceInfo, data[ts:te], data[m[4]:m[5]], args})
|
2017-06-25 17:23:20 +02:00
|
|
|
}
|
2017-06-25 23:26:14 +02:00
|
|
|
p = te
|
2017-06-25 17:23:20 +02:00
|
|
|
}
|
|
|
|
if p < pe {
|
2017-06-26 18:41:41 +02:00
|
|
|
out = append(out, Chunk{TextChunkType, sourceInfo, data[p:], "", ""})
|
2017-06-25 17:23:20 +02:00
|
|
|
}
|
|
|
|
return out
|
|
|
|
}
|