package parsekit_test import ( "fmt" "testing" "git.makaay.nl/mauricem/go-parsekit" ) func TestCombinators(t *testing.T) { RunMatcherTests(t, []MatcherTest{ {"xxx", c.Rune('x'), true, "x"}, {"x ", c.Rune(' '), false, ""}, {"aa", c.RuneRange('b', 'e'), false, ""}, {"bb", c.RuneRange('b', 'e'), true, "b"}, {"cc", c.RuneRange('b', 'e'), true, "c"}, {"dd", c.RuneRange('b', 'e'), true, "d"}, {"ee", c.RuneRange('b', 'e'), true, "e"}, {"ff", c.RuneRange('b', 'e'), false, ""}, {"Hello, world!", c.Str("Hello"), true, "Hello"}, {"HellÖ, world!", c.StrNoCase("hellö"), true, "HellÖ"}, {"+X", c.Runes('+', '-', '*', '/'), true, "+"}, {"-X", c.Runes('+', '-', '*', '/'), true, "-"}, {"*X", c.Runes('+', '-', '*', '/'), true, "*"}, {"/X", c.Runes('+', '-', '*', '/'), true, "/"}, {"!X", c.Runes('+', '-', '*', '/'), false, ""}, {"abc", c.Not(c.Rune('b')), true, "a"}, {"bcd", c.Not(c.Rune('b')), false, ""}, {"bcd", c.Not(c.Rune('b')), false, ""}, {"1010", c.Not(c.Seq(c.Rune('2'), c.Rune('0'))), true, "1"}, {"2020", c.Not(c.Seq(c.Rune('2'), c.Rune('0'))), false, ""}, {"abc", c.Any(c.Rune('a'), c.Rune('b')), true, "a"}, {"bcd", c.Any(c.Rune('a'), c.Rune('b')), true, "b"}, {"cde", c.Any(c.Rune('a'), c.Rune('b')), false, ""}, {"ababc", c.Rep(4, c.Runes('a', 'b')), true, "abab"}, {"ababc", c.Rep(5, c.Runes('a', 'b')), false, ""}, {"", c.Min(0, c.Rune('a')), true, ""}, {"a", c.Min(0, c.Rune('a')), true, "a"}, {"aaaaa", c.Min(4, c.Rune('a')), true, "aaaaa"}, {"aaaaa", c.Min(5, c.Rune('a')), true, "aaaaa"}, {"aaaaa", c.Min(6, c.Rune('a')), false, ""}, {"", c.Max(4, c.Rune('b')), true, ""}, {"X", c.Max(4, c.Rune('b')), true, ""}, {"bbbbbX", c.Max(4, c.Rune('b')), true, "bbbb"}, {"bbbbbX", c.Max(5, c.Rune('b')), true, "bbbbb"}, {"bbbbbX", c.Max(6, c.Rune('b')), true, "bbbbb"}, {"", c.MinMax(0, 0, c.Rune('c')), true, ""}, {"X", c.MinMax(0, 0, c.Rune('c')), true, ""}, {"cccccX", c.MinMax(0, 0, c.Rune('c')), true, ""}, {"cccccX", c.MinMax(0, 1, c.Rune('c')), true, "c"}, {"cccccX", c.MinMax(0, 5, c.Rune('c')), true, "ccccc"}, {"cccccX", c.MinMax(0, 6, c.Rune('c')), true, "ccccc"}, {"cccccX", c.MinMax(1, 1, c.Rune('c')), true, "c"}, {"", c.MinMax(1, 1, c.Rune('c')), false, ""}, {"X", c.MinMax(1, 1, c.Rune('c')), false, ""}, {"cccccX", c.MinMax(1, 3, c.Rune('c')), true, "ccc"}, {"cccccX", c.MinMax(1, 6, c.Rune('c')), true, "ccccc"}, {"cccccX", c.MinMax(3, 4, c.Rune('c')), true, "cccc"}, {"", c.OneOrMore(c.Rune('d')), false, ""}, {"X", c.OneOrMore(c.Rune('d')), false, ""}, {"dX", c.OneOrMore(c.Rune('d')), true, "d"}, {"dddddX", c.OneOrMore(c.Rune('d')), true, "ddddd"}, {"", c.ZeroOrMore(c.Rune('e')), true, ""}, {"X", c.ZeroOrMore(c.Rune('e')), true, ""}, {"eX", c.ZeroOrMore(c.Rune('e')), true, "e"}, {"eeeeeX", c.ZeroOrMore(c.Rune('e')), true, "eeeee"}, {"Hello, world!X", c.Seq(c.Str("Hello"), a.Comma, a.Space, c.Str("world"), a.Excl), true, "Hello, world!"}, {"101010123", c.OneOrMore(c.Seq(c.Rune('1'), c.Rune('0'))), true, "101010"}, {"", c.Opt(c.OneOrMore(c.Rune('f'))), true, ""}, {"ghijkl", c.Opt(c.Rune('h')), true, ""}, {"ghijkl", c.Opt(c.Rune('g')), true, "g"}, {"fffffX", c.Opt(c.OneOrMore(c.Rune('f'))), true, "fffff"}, {"1,2,3,b,c", c.Separated(a.Comma, a.Digit), true, "1,2,3"}, {`\x9a\x01\xF0\xfCAndSomeMoreStuff`, c.OneOrMore(c.Seq(a.Backslash, c.Rune('x'), c.Rep(2, a.HexDigit))), true, `\x9a\x01\xF0\xfC`}, {" ", m.Trim(c.OneOrMore(a.AnyRune), " "), true, ""}, {" ", m.TrimLeft(c.OneOrMore(a.AnyRune), " "), true, ""}, {" ", m.TrimRight(c.OneOrMore(a.AnyRune), " "), true, ""}, }) } func TestAtoms(t *testing.T) { RunMatcherTests(t, []MatcherTest{ {"", a.EndOfFile, true, ""}, {"⌘", a.AnyRune, true, "⌘"}, {"\xbc", a.AnyRune, false, ""}, // invalid UTF8 rune {"", a.AnyRune, false, ""}, // end of file {" ", a.Space, true, " "}, {"X", a.Space, false, ""}, {"\t", a.Tab, true, "\t"}, {"\r", a.CR, true, "\r"}, {"\n", a.LF, true, "\n"}, {"!", a.Excl, true, "!"}, {"\"", a.DoubleQuote, true, "\""}, {"#", a.Hash, true, "#"}, {"$", a.Dollar, true, "$"}, {"%", a.Percent, true, "%"}, {"&", a.Amp, true, "&"}, {"'", a.SingleQuote, true, "'"}, {"(", a.RoundOpen, true, "("}, {")", a.RoundClose, true, ")"}, {"*", a.Asterisk, true, "*"}, {"+", a.Plus, true, "+"}, {",", a.Comma, true, ","}, {"-", a.Minus, true, "-"}, {".", a.Dot, true, "."}, {"/", a.Slash, true, "/"}, {":", a.Colon, true, ":"}, {";", a.Semicolon, true, ";"}, {"<", a.AngleOpen, true, "<"}, {"=", a.Equal, true, "="}, {">", a.AngleClose, true, ">"}, {"?", a.Question, true, "?"}, {"@", a.At, true, "@"}, {"[", a.SquareOpen, true, "["}, {"\\", a.Backslash, true, "\\"}, {"]", a.SquareClose, true, "]"}, {"^", a.Caret, true, "^"}, {"_", a.Underscore, true, "_"}, {"`", a.Backquote, true, "`"}, {"{", a.CurlyOpen, true, "{"}, {"|", a.Pipe, true, "|"}, {"}", a.CurlyClose, true, "}"}, {"~", a.Tilde, true, "~"}, {" \t \t \r\n", a.Whitespace, true, " \t \t "}, {"\r", a.WhitespaceAndNewlines, false, ""}, {" \t\r\n \r", a.WhitespaceAndNewlines, true, " \t\r\n "}, {"", a.EndOfLine, true, ""}, {"\r\n", a.EndOfLine, true, "\r\n"}, {"\n", a.EndOfLine, true, "\n"}, {"0", a.Digit, true, "0"}, {"1", a.Digit, true, "1"}, {"2", a.Digit, true, "2"}, {"3", a.Digit, true, "3"}, {"4", a.Digit, true, "4"}, {"5", a.Digit, true, "5"}, {"6", a.Digit, true, "6"}, {"7", a.Digit, true, "7"}, {"8", a.Digit, true, "8"}, {"9", a.Digit, true, "9"}, {"X", a.Digit, false, ""}, {"a", a.ASCIILower, true, "a"}, {"z", a.ASCIILower, true, "z"}, {"A", a.ASCIILower, false, ""}, {"Z", a.ASCIILower, false, ""}, {"A", a.ASCIIUpper, true, "A"}, {"Z", a.ASCIIUpper, true, "Z"}, {"a", a.ASCIIUpper, false, ""}, {"z", a.ASCIIUpper, false, ""}, {"0", a.HexDigit, true, "0"}, {"9", a.HexDigit, true, "9"}, {"a", a.HexDigit, true, "a"}, {"f", a.HexDigit, true, "f"}, {"A", a.HexDigit, true, "A"}, {"F", a.HexDigit, true, "F"}, {"g", a.HexDigit, false, "g"}, {"G", a.HexDigit, false, "G"}, }) } func TestModifiers(t *testing.T) { RunMatcherTests(t, []MatcherTest{ {"--cool", c.Seq(m.Drop(c.OneOrMore(a.Minus)), c.Str("cool")), true, "cool"}, {" trim ", m.Trim(c.OneOrMore(a.AnyRune), " "), true, "trim"}, {" \t trim \t ", m.Trim(c.OneOrMore(a.AnyRune), " \t"), true, "trim"}, {" trim ", m.TrimLeft(c.OneOrMore(a.AnyRune), " "), true, "trim "}, {" trim ", m.TrimRight(c.OneOrMore(a.AnyRune), " "), true, " trim"}, {" \t trim \t ", m.TrimRight(c.OneOrMore(a.AnyRune), " \t"), true, " \t trim"}, {"dirtyword", m.Replace(c.OneOrMore(a.AnyRune), "*******"), true, "*******"}, {"abcdefghijk", m.ModifyByCallback(c.Str("abc"), func(s string) string { return "X" }), true, "X"}, {"NoTaLlUpPeR", m.ToUpper(c.StrNoCase("notallUPPER")), true, "NOTALLUPPER"}, {"NoTaLlLoWeR", m.ToLower(c.StrNoCase("NOTALLlower")), true, "notalllower"}, }) } // I know, this is hell, but that's the whole point for this test :-> func TestCombination(t *testing.T) { demonic := c.Seq( c.Opt(a.SquareOpen), m.Trim( c.Seq( c.Opt(a.Whitespace), c.Rep(3, a.AngleClose), m.ModifyByCallback(c.OneOrMore(c.StrNoCase("hello")), func(s string) string { return fmt.Sprintf("%d", len(s)) }), m.Replace(c.Separated(a.Comma, c.Opt(a.Whitespace)), ", "), m.ToUpper(c.Min(1, a.ASCIILower)), m.Drop(a.Excl), c.Rep(3, a.AngleOpen), c.Opt(a.Whitespace), ), " \t", ), c.Opt(a.SquareClose), ) RunMatcherTests(t, []MatcherTest{ {"[ \t >>>Hello, world!<<< ]", demonic, true, "[>>>5, WORLD<<<]"}, {"[ \t >>>Hello, world!<<< ", demonic, true, "[>>>5, WORLD<<<"}, {">>>HellohellO, world!<<< ]", demonic, true, ">>>10, WORLD<<<]"}, {"[ \t >>>HellohellO , , , world!<<< ", demonic, true, "[>>>10, WORLD<<<"}, }) } func TestSequenceOfRunes(t *testing.T) { sequence := c.Seq( a.Hash, a.Dollar, a.Percent, a.Amp, a.SingleQuote, a.RoundOpen, a.RoundClose, a.Asterisk, a.Plus, a.Comma, a.Minus, a.Dot, a.Slash, a.Colon, a.Semicolon, a.AngleOpen, a.Equal, a.AngleClose, a.Question, a.At, a.SquareOpen, a.Backslash, a.SquareClose, a.Caret, a.Underscore, a.Backquote, a.CurlyOpen, a.Pipe, a.CurlyClose, a.Tilde, ) input := "#$%&'()*+,-./:;<=>?@[\\]^_`{|}~" parser := parsekit.NewParser(func(p *parsekit.P) { p.Expects("Sequence of runes") if p.On(sequence).Accept() { p.EmitLiteral(TestItem) } }) item, err, ok := parser.Parse(input).Next() if !ok { t.Fatalf("Parsing failed: %s", err) } if item.Value != input { t.Fatalf("Unexpected output from parser:\nexpected: %s\nactual: %s\n", input, item.Value) } }