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]))) }