package main func parseReplacement(l *RuneReader) (output []TransducerOutput) { loop: for { r := l.next() switch r { case eof: panic("Missing closing \"") case '"': break loop case '$': slot := l.next() if slot == eof { panic("Missing slot character") } output = append(output, TransducerReplacementLoad(slot)) default: output = append(output, TransducerReplacementRune(r)) } } return output } func parseRegex(l *RuneReader, minPower int) RegexAST { var lhs RegexAST r := l.next() switch r { case eof: return nil case ')', '*', '-', '|', '?', '!': l.rewind() return nil case '(': lhs = parseRegex(l, 0) if !l.accept(")") { panic("Missing matching )") } case '.': lhs = RegexASTAny{} default: lhs = RegexASTRune(r) } loop: for { if minPower <= 0 { next := parseRegex(l, 1) if next != nil { lhs = RegexASTConcat{lhs, next} continue loop } } r := l.next() switch { case r == '*' && minPower <= 4: lhs = RegexASTMaximise{lhs} case r == '-' && minPower <= 4: lhs = RegexASTMinimise{lhs} case r == '!' && minPower <= 4: lhs = RegexASTTry{lhs} case r == '?' && minPower <= 4: lhs = RegexASTMaybe{lhs} case r == '|' && minPower <= 2: rhs := parseRegex(l, 3) if rhs == nil { panic("Missing regex after |") } lhs = RegexASTOr{lhs, rhs} default: l.rewind() break loop } } return lhs } func parseSubex(l *RuneReader, minPower int) SubexAST { var lhs SubexAST r := l.next() switch r { case eof: return nil case '(': lhs = parseSubex(l, 0) if !l.accept(")") { panic("Missing matching )") } case ')', '*', '-', '|', '!', '?', ';': l.rewind() return nil case '$': slot := l.next() if slot == eof { panic("Missing slot character") } match := parseRegex(l, 100) if match == nil { panic("Missing regex for store") } lhs = SubexASTStore{ match: match, slot: slot, } case '"': replacement := parseReplacement(l) lhs = SubexASTOutput{replacement} case '.': lhs = SubexASTCopyAny{} default: lhs = SubexASTCopyRune(r) } loop: for { if minPower <= 0 { next := parseSubex(l, 1) if next != nil { lhs = SubexASTConcat{lhs, next} continue loop } } r := l.next() switch { case r == '*' && minPower <= 8: lhs = SubexASTMaximise{lhs} case r == '-' && minPower <= 8: lhs = SubexASTMinimise{lhs} case r == '!' && minPower <= 8: lhs = SubexASTTry{lhs} case r == '?' && minPower <= 8: lhs = SubexASTMaybe{lhs} case r == '|' && minPower <= 4: rhs := parseSubex(l, 5) if rhs == nil { panic("Missing subex after |") } lhs = SubexASTOr{lhs, rhs} case r == ';' && minPower <= 2: rhs := parseSubex(l, 3) if rhs == nil { panic("Missing subex after ;") } lhs = SubexASTJoin{ content: lhs, delimiter: rhs, } default: l.rewind() break loop } } return lhs } func parse(input string) SubexAST { l := RuneReader { input: input, pos: 0, width: 0, } return parseSubex(&l, 0) }