forked from zyedidia/micro
-
Notifications
You must be signed in to change notification settings - Fork 0
/
syntax_converter.go
165 lines (145 loc) · 4.88 KB
/
syntax_converter.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
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
package main
import (
"fmt"
"io/ioutil"
"os"
"regexp"
"strings"
)
type SingleRule struct {
color string
regex string
}
type MultiRule struct {
color string
start string
end string
}
// JoinRule takes a syntax rule (which can be multiple regular expressions)
// and joins it into one regular expression by ORing everything together
func JoinRule(rule string) string {
split := strings.Split(rule, `" "`)
joined := strings.Join(split, "|")
return joined
}
func parseFile(text, filename string) (filetype, syntax, header string, rules []interface{}) {
lines := strings.Split(text, "\n")
// Regex for parsing syntax statements
syntaxParser := regexp.MustCompile(`syntax "(.*?)"\s+"(.*)"+`)
// Regex for parsing header statements
headerParser := regexp.MustCompile(`header "(.*)"`)
// Regex for parsing standard syntax rules
ruleParser := regexp.MustCompile(`color (.*?)\s+(?:\((.+?)?\)\s+)?"(.*)"`)
// Regex for parsing syntax rules with start="..." end="..."
ruleStartEndParser := regexp.MustCompile(`color (.*?)\s+(?:\((.+?)?\)\s+)?start="(.*)"\s+end="(.*)"`)
for lineNum, line := range lines {
line = strings.TrimSpace(line)
if line == "" {
continue
}
if strings.HasPrefix(line, "#") {
continue
}
if strings.HasPrefix(line, "syntax") {
syntaxMatches := syntaxParser.FindSubmatch([]byte(line))
if len(syntaxMatches) == 3 {
filetype = string(syntaxMatches[1])
syntax = JoinRule(string(syntaxMatches[2]))
} else {
fmt.Println(filename, lineNum, "Syntax statement is not valid: "+line)
continue
}
}
if strings.HasPrefix(line, "header") {
// Header statement
headerMatches := headerParser.FindSubmatch([]byte(line))
if len(headerMatches) == 2 {
header = JoinRule(string(headerMatches[1]))
} else {
fmt.Println(filename, lineNum, "Header statement is not valid: "+line)
continue
}
}
// Syntax rule, but it could be standard or start-end
if ruleParser.MatchString(line) {
// Standard syntax rule
// Parse the line
submatch := ruleParser.FindSubmatch([]byte(line))
var color string
var regexStr string
var flags string
if len(submatch) == 4 {
// If len is 4 then the user specified some additional flags to use
color = string(submatch[1])
flags = string(submatch[2])
if flags != "" {
regexStr = "(?" + flags + ")" + JoinRule(string(submatch[3]))
} else {
regexStr = JoinRule(string(submatch[3]))
}
} else if len(submatch) == 3 {
// If len is 3, no additional flags were given
color = string(submatch[1])
regexStr = JoinRule(string(submatch[2]))
} else {
// If len is not 3 or 4 there is a problem
fmt.Println(filename, lineNum, "Invalid statement: "+line)
continue
}
rules = append(rules, SingleRule{color, regexStr})
} else if ruleStartEndParser.MatchString(line) {
// Start-end syntax rule
submatch := ruleStartEndParser.FindSubmatch([]byte(line))
var color string
var start string
var end string
// Use m and s flags by default
if len(submatch) == 5 {
// If len is 5 the user provided some additional flags
color = string(submatch[1])
start = string(submatch[3])
end = string(submatch[4])
} else if len(submatch) == 4 {
// If len is 4 the user did not provide additional flags
color = string(submatch[1])
start = string(submatch[2])
end = string(submatch[3])
} else {
// If len is not 4 or 5 there is a problem
fmt.Println(filename, lineNum, "Invalid statement: "+line)
continue
}
// rules[color] = "(?" + flags + ")" + "(" + start + ").*?(" + end + ")"
rules = append(rules, MultiRule{color, start, end})
}
}
return
}
func generateFile(filetype, syntax, header string, rules []interface{}) string {
output := ""
output += fmt.Sprintf("filetype: %s\n\n", filetype)
output += fmt.Sprintf("detect: \n filename: \"%s\"\n", strings.Replace(strings.Replace(syntax, "\\", "\\\\", -1), "\"", "\\\"", -1))
if header != "" {
output += fmt.Sprintf(" header: \"%s\"\n", strings.Replace(strings.Replace(header, "\\", "\\\\", -1), "\"", "\\\"", -1))
}
output += "\nrules:\n"
for _, r := range rules {
if rule, ok := r.(SingleRule); ok {
output += fmt.Sprintf(" - %s: \"%s\"\n", rule.color, strings.Replace(strings.Replace(rule.regex, "\\", "\\\\", -1), "\"", "\\\"", -1))
} else if rule, ok := r.(MultiRule); ok {
output += fmt.Sprintf(" - %s:\n", rule.color)
output += fmt.Sprintf(" start: \"%s\"\n", strings.Replace(strings.Replace(rule.start, "\\", "\\\\", -1), "\"", "\\\"", -1))
output += fmt.Sprintf(" end: \"%s\"\n", strings.Replace(strings.Replace(rule.end, "\\", "\\\\", -1), "\"", "\\\"", -1))
output += fmt.Sprintf(" rules: []\n\n")
}
}
return output
}
func main() {
if len(os.Args) < 2 {
fmt.Println("no args")
return
}
data, _ := ioutil.ReadFile(os.Args[1])
fmt.Print(generateFile(parseFile(string(data), os.Args[1])))
}