Consider the most basic parsers you might want. For instance, a parser that only succeeds if it matches a string exactly, a parser that matches any single character and always succeeds, a parser that matches nothing and returns some constant, a parser that always fails. They're all simple and stupid and let's give them names:
string("foobar") : Parser<String>
char : Parser<Character>
always<A>(x: A): Parser<A>
never<A>: Parser<A>
These are parser combinators. They form the atoms at the foundation of a language for constructing parsers. For instance, we can imagine two parsers happening in sequence
then<A, B>(a: Parser<A>, b: Parser<B>): Parser<(A, B)>
then(char, char): Parser<(Character, Character)>
Or more fancily, a two parsers happening in sequence, but the second parser being defined using the output of the first. This creates context sensitivity
thenDependent<A, B>(a: Parser<A>, b: A -> Parser<B>): Parser<(A, B)>
We're constructing a fundamental language for building parsers up from our atoms to larger and larger things. All we need now is a way to "run" them
run<A>(p: Parser<A>): String -> Option<A>
Tada---parser combinators!