]> git.lizzy.rs Git - micro.git/blob - runtime/syntax/syntax_converter.go
Merge pull request #776 from tommyshem/syntax-checker
[micro.git] / runtime / syntax / syntax_converter.go
1 package main
2
3 import (
4         "fmt"
5         "io/ioutil"
6         "os"
7         "regexp"
8         "strings"
9 )
10
11 type SingleRule struct {
12         color string
13         regex string
14 }
15
16 type MultiRule struct {
17         color string
18         start string
19         end   string
20 }
21
22 // JoinRule takes a syntax rule (which can be multiple regular expressions)
23 // and joins it into one regular expression by ORing everything together
24 func JoinRule(rule string) string {
25         split := strings.Split(rule, `" "`)
26         joined := strings.Join(split, "|")
27         joined = joined
28         return joined
29 }
30
31 func parseFile(text, filename string) (filetype, syntax, header string, rules []interface{}) {
32         lines := strings.Split(text, "\n")
33
34         // Regex for parsing syntax statements
35         syntaxParser := regexp.MustCompile(`syntax "(.*?)"\s+"(.*)"+`)
36         // Regex for parsing header statements
37         headerParser := regexp.MustCompile(`header "(.*)"`)
38
39         // Regex for parsing standard syntax rules
40         ruleParser := regexp.MustCompile(`color (.*?)\s+(?:\((.+?)?\)\s+)?"(.*)"`)
41         // Regex for parsing syntax rules with start="..." end="..."
42         ruleStartEndParser := regexp.MustCompile(`color (.*?)\s+(?:\((.+?)?\)\s+)?start="(.*)"\s+end="(.*)"`)
43
44         for lineNum, line := range lines {
45                 line = strings.TrimSpace(line)
46                 if line == "" {
47                         continue
48                 }
49                 if strings.HasPrefix(line, "#") {
50                         continue
51                 }
52                 if strings.HasPrefix(line, "syntax") {
53                         syntaxMatches := syntaxParser.FindSubmatch([]byte(line))
54                         if len(syntaxMatches) == 3 {
55                                 filetype = string(syntaxMatches[1])
56                                 syntax = JoinRule(string(syntaxMatches[2]))
57                         } else {
58                                 fmt.Println(filename, lineNum, "Syntax statement is not valid: "+line)
59                                 continue
60                         }
61                 }
62                 if strings.HasPrefix(line, "header") {
63                         // Header statement
64                         headerMatches := headerParser.FindSubmatch([]byte(line))
65                         if len(headerMatches) == 2 {
66                                 header = JoinRule(string(headerMatches[1]))
67                         } else {
68                                 fmt.Println(filename, lineNum, "Header statement is not valid: "+line)
69                                 continue
70                         }
71                 }
72
73                 // Syntax rule, but it could be standard or start-end
74                 if ruleParser.MatchString(line) {
75                         // Standard syntax rule
76                         // Parse the line
77                         submatch := ruleParser.FindSubmatch([]byte(line))
78                         var color string
79                         var regexStr string
80                         var flags string
81                         if len(submatch) == 4 {
82                                 // If len is 4 then the user specified some additional flags to use
83                                 color = string(submatch[1])
84                                 flags = string(submatch[2])
85                                 if flags != "" {
86                                         regexStr = "(?" + flags + ")" + JoinRule(string(submatch[3]))
87                                 } else {
88                                         regexStr = JoinRule(string(submatch[3]))
89                                 }
90                         } else if len(submatch) == 3 {
91                                 // If len is 3, no additional flags were given
92                                 color = string(submatch[1])
93                                 regexStr = JoinRule(string(submatch[2]))
94                         } else {
95                                 // If len is not 3 or 4 there is a problem
96                                 fmt.Println(filename, lineNum, "Invalid statement: "+line)
97                                 continue
98                         }
99
100                         rules = append(rules, SingleRule{color, regexStr})
101                 } else if ruleStartEndParser.MatchString(line) {
102                         // Start-end syntax rule
103                         submatch := ruleStartEndParser.FindSubmatch([]byte(line))
104                         var color string
105                         var start string
106                         var end string
107                         // Use m and s flags by default
108                         if len(submatch) == 5 {
109                                 // If len is 5 the user provided some additional flags
110                                 color = string(submatch[1])
111                                 start = string(submatch[3])
112                                 end = string(submatch[4])
113                         } else if len(submatch) == 4 {
114                                 // If len is 4 the user did not provide additional flags
115                                 color = string(submatch[1])
116                                 start = string(submatch[2])
117                                 end = string(submatch[3])
118                         } else {
119                                 // If len is not 4 or 5 there is a problem
120                                 fmt.Println(filename, lineNum, "Invalid statement: "+line)
121                                 continue
122                         }
123
124                         // rules[color] = "(?" + flags + ")" + "(" + start + ").*?(" + end + ")"
125                         rules = append(rules, MultiRule{color, start, end})
126                 }
127         }
128
129         return
130 }
131
132 func generateFile(filetype, syntax, header string, rules []interface{}) string {
133         output := ""
134
135         output += fmt.Sprintf("filetype: %s\n\n", filetype)
136         output += fmt.Sprintf("detect: \n    filename: \"%s\"\n", strings.Replace(strings.Replace(syntax, "\\", "\\\\", -1), "\"", "\\\"", -1))
137
138         if header != "" {
139                 output += fmt.Sprintf("    header: \"%s\"\n", strings.Replace(strings.Replace(header, "\\", "\\\\", -1), "\"", "\\\"", -1))
140         }
141
142         output += "\nrules:\n"
143
144         for _, r := range rules {
145                 if rule, ok := r.(SingleRule); ok {
146                         output += fmt.Sprintf("    - %s: \"%s\"\n", rule.color, strings.Replace(strings.Replace(rule.regex, "\\", "\\\\", -1), "\"", "\\\"", -1))
147                 } else if rule, ok := r.(MultiRule); ok {
148                         output += fmt.Sprintf("    - %s:\n", rule.color)
149                         output += fmt.Sprintf("        start: \"%s\"\n", strings.Replace(strings.Replace(rule.start, "\\", "\\\\", -1), "\"", "\\\"", -1))
150                         output += fmt.Sprintf("        end: \"%s\"\n", strings.Replace(strings.Replace(rule.end, "\\", "\\\\", -1), "\"", "\\\"", -1))
151                         output += fmt.Sprintf("        rules: []\n\n")
152                 }
153         }
154
155         return output
156 }
157
158 func main() {
159         if len(os.Args) < 2 {
160                 fmt.Println("no args")
161                 return
162         }
163
164         data, _ := ioutil.ReadFile(os.Args[1])
165         fmt.Print(generateFile(parseFile(string(data), os.Args[1])))
166 }