-
Notifications
You must be signed in to change notification settings - Fork 0
/
line.go
92 lines (83 loc) · 2.81 KB
/
line.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
// Copyright 2017 Tomas Machalek <tomas.machalek@gmail.com>
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package vertigo
import (
"fmt"
"regexp"
"strings"
)
var (
tagSrchRegexp = regexp.MustCompile(`^<([\w\d\p{Po}]+)(\s+.*?|)>$`)
tagSrchRegexpSC = regexp.MustCompile(`^<([\w\d\p{Po}]+)(\s+.*?|)/>$`)
attrValRegexp = regexp.MustCompile(`(\w+)="([^"]+)"`)
closeTagRegexp = regexp.MustCompile(`</([^>]+)\s*>`)
)
// this is quite simplified but it should work for our purposes
func isElement(tagSrc string) bool {
return strings.HasPrefix(tagSrc, "<") && strings.HasSuffix(tagSrc, ">")
}
func isOpenElement(tagSrc string) bool {
return isElement(tagSrc) && !strings.HasPrefix(tagSrc, "</") &&
!strings.HasSuffix(tagSrc, "/>")
}
func isCloseElement(tagSrc string) bool {
return isElement(tagSrc) && strings.HasPrefix(tagSrc, "</")
}
func isSelfCloseElement(tagSrc string) bool {
return isElement(tagSrc) && strings.HasSuffix(tagSrc, "/>")
}
func parseAttrVal(src string) map[string]string {
ans := make(map[string]string)
srch := attrValRegexp.FindAllStringSubmatch(src, -1)
for i := 0; i < len(srch); i++ {
ans[srch[i][1]] = srch[i][2]
}
return ans
}
func parseLine(normLine string, elmStack structAttrAccumulator) (interface{}, error) {
normLine = strings.TrimSpace(normLine)
switch {
case isOpenElement(normLine):
srch := tagSrchRegexp.FindStringSubmatch(normLine)
if len(srch) < 3 {
return nil, fmt.Errorf("cannot parse open element '%s'", normLine)
}
meta := &Structure{Name: srch[1], Attrs: parseAttrVal(srch[2])}
err := elmStack.Begin(meta)
return meta, err
case isCloseElement(normLine):
srch := closeTagRegexp.FindStringSubmatch(normLine)
if len(srch) < 2 {
return nil, fmt.Errorf("cannot parse close element '%s'", normLine)
}
elm, err := elmStack.End(srch[1])
if err != nil {
return nil, err
}
return &StructureClose{Name: elm.Name}, nil
case isSelfCloseElement(normLine):
srch := tagSrchRegexpSC.FindStringSubmatch(normLine)
if len(srch) < 3 {
return nil, fmt.Errorf("cannot parse self closing element '%s'", normLine)
}
return &Structure{Name: srch[1], Attrs: parseAttrVal(srch[2]), IsEmpty: true}, nil
default:
items := strings.Split(normLine, "\t")
return &Token{
Word: items[0],
Attrs: items[1:],
StructAttrs: elmStack.GetAttrs(),
}, nil
}
}