JavaScript ES6, 440 bytes
This is a bunch of simple compression optimizations. As a one-liner:
eval("a='w_m_d_u_h_b_f_s_M_H_O_N_E_A_W_VX';b='w hTm b_d f,Tu s_M ETH A_O W N VX';c=bRA/,'Y');o={};'wWork It|mMake It|dDo It|uMakes Us|hHard&bBett&fFast&sStrong&MMore Than|HHour|OOur|NNev&EEv&AAft&WWork Is|VOv&X\\n\\n|YAf-|_\\n|T 'R&/g,'er|').split('|').map(x=>o[x[0]]=x.slice(1));console.log((a+a+b+b+b+b+b+b+b+bR._/g,'_')R,?T/g,',T')+c+c+c+bRT/,'_')R,/,'')+bR,?T. ._/g,'_')+bR,?T/g,'_'))R\\w/g,x=>o[x]).trim())".replace(/R/g,'.replace(/'))
This was written to be an executable script via iojs --harmony_arrow_functions file.js
; you can shave off the console.log()
overhead depending on the meaning of "output the lyrics".
Explanation
After performing the outer .replace()
, the code fed to eval
is:
// The first three lines, with each phrase compressed to a single character, newlines
// compressed to _, and block-endings compressed to X. Call this compressed-format.
a = 'w_m_d_u_h_b_f_s_M_H_O_N_E_A_W_VX';
// The compressed-format main block: this is repeated seven times literally but
// every other stanza, besides `a` above, ultimately uses some simple variant
// of this block.
b = 'w hTm b_d f,Tu s_M ETH A_O W N VX';
// The new character T above is a new character we're adding to compressed-format, it is
// a space in the main block but also a hook for some regular expressions later.
// We need one more entry in compressed-format: some blocks, here assigned to
// the variable `c`, shorten "After" to Y = "Af-".
c = b.replace(/A/, 'Y');
// Now we want to build a lookup table for this compressed format above. That is done by
// these lines, which have also been compressed:
o={};
'wWork It|mMake It|dDo It|uMakes Us|hHard&bBett&fFast&sStrong&MMore Than|HHour|OOur|NNev&EEv&AAft&WWork Is|VOv&X\n\n|YAf-|_\n|T '
.replace(/&/g, 'er|')
.split('|')
.map(x => o[x[0]] = x.slice(1));
// The fact that so many fragments end in 'er' allows us to actually shave a couple
// bytes above, but the compression scheme is fundamentally creating a dict like
// {a: "Phrase 1", b: "Phrase 2", c: "Phrase 3", d: "Phrase 4"}
// from the string "aPhrase 1|bPhrase 2|cPhrase 3|dPhrase4".
// Now we have the part that actually does the work:
console.log(
( // build-string phase
// first two opening blocks `a`
a + a +
// seven repetitions of `b`
b + b + b + b + b + b + b +
// a version of `b` without final words and with commas before each T.
b.replace(/._/g, '_').replace(/,?T/g, ',T') +
// three repetitions with the 'Af-' suffix.
c + c + c +
// one with the first T converted into a newline and no commas
b.replace(/T/, '_').replace(/,/, '') +
// one with only the first halfs of the three lines
b.replace(/,?T. ._/g, '_') +
// one with no commas and all T's converted to newlines.
b.replace(/,?T/g, '_')
) // end build-string phase
// Now we convert from compressed-format to actual format
.replace(/\w/g, x => o[x])
// We are only told that one trailing newline is allowed; we have two extra:
.trim()
)