Terence,
Break your regex into logical pieces. Assign each piece to a variable (or constant) whose name describes it. Catenate the variables together to create the finished string. (This might run at compile time.) Your regex is now human-readable (looks more like BNF), and each piece can be inspected visually by itself to see if it matches the variable-name description.
Eg, your
^\w+([-+.']\w+)@\w+([-.]\w+).\w+([-.]\w+)*$
becomes the pseudocode (doubling up the \s):
word = "\w+"
addressee_separator = "[-+.']"
addressee = word + optional_repeat(addressee_separator + word)
domain_separator = "[-.]"
domain_part = word + optional_repeat(domain_separator + word)
domain = domain_part + "\." + domain_part
address = "^" + addressee + "@" + domain + "$"
where function (or macro) optional_repeat(x) returns "(" + x + ")*" (or you can spell it out if you don't want the reader to have to consult the definition of optional_repeat()). NB. This is code, so comments can be included! Adjust verbosity according to taste, or wizardry comfort level.
Critique: I don't like that your regex confuses the optional .s and the compulsory . in the domain name, making the grammar ambiguous. The ambiguity is revealed by the definition of domain, the like of which no one should be using in a well-constructed grammar. 🙂
IMHO, better would be:
...
word_with_hyphen = word + optional_repeat("-" + word)
domain = word_with_hyphen + compulsory_repeat("\." + word_with_hyphen)
...
Cheers, Paul