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