diff options
author | Norbert Preining <norbert@preining.info> | 2022-11-08 03:01:43 +0000 |
---|---|---|
committer | Norbert Preining <norbert@preining.info> | 2022-11-08 03:01:43 +0000 |
commit | 118cb5bbc5236bf86ca1088b9b3bae04c7b09beb (patch) | |
tree | 250db680f60212600d0e887c6d63deb3d266da89 /macros/latex/contrib/songproj/song2tex.py | |
parent | 5fcfa99e5deaf9c601ba9e338f07fbefdd5d4487 (diff) |
CTAN sync 202211080301
Diffstat (limited to 'macros/latex/contrib/songproj/song2tex.py')
-rwxr-xr-x | macros/latex/contrib/songproj/song2tex.py | 101 |
1 files changed, 101 insertions, 0 deletions
diff --git a/macros/latex/contrib/songproj/song2tex.py b/macros/latex/contrib/songproj/song2tex.py new file mode 100755 index 0000000000..dc32da2b3f --- /dev/null +++ b/macros/latex/contrib/songproj/song2tex.py @@ -0,0 +1,101 @@ +#!/usr/bin/env python3 + +import argparse +import enum +import re + + +# The following regexp match the typical markers of refrain and couplet: +re_refrain = re.compile(r'(R(efrain)?|C(horus)?)( |[-./] ?)', re.IGNORECASE) +re_couplet = re.compile(r'\d+( |[-./] ?)') + + +def match_rest(regexp, s): + """Match the start of string s against the given regexp: + * if it matches, return the non-matching part; + * it it does not matches, return None.""" + if (m := regexp.match(s)) is not None: + return s[m.end():] + else: + return None + + +class State(enum.Enum): + """A state indicates in what context we are while parsing a song.""" + LIMBO = 0 # Not in the refrain or in a couplet + REFRAIN = 1 # In the refrain + COUPLET = 2 # In a couplet + + +def parse_song(lines, re_refrain, re_couplet): + # At the beginning of the song, we are not in the refrain or in a couplet. + state = State.LIMBO + # This variable will be used to record the longest line seen so far. + longest = '' + for line in lines: + line = line.rstrip() + if state == State.LIMBO: + # We are not in the refrain or in a couplet. Depending on what we find: + if (rest := match_rest(re_refrain, line)) is not None: + # when a refrain start marker is found, enter the refrain. + yield '\\begin{refrain}\n' + state = State.REFRAIN + line = rest + yield ' {} \\\\\n'.format(line) + elif (rest := match_rest(re_couplet, line)) is not None: + # when a couplet start marker is found, enter a couplet. + yield '\\begin{couplet}\n' + state = State.COUPLET + line = rest + yield ' {} \\\\\n'.format(line) + elif line != '': + # when a non-empty line is found, also enter a couplet as all. + yield '\\begin{couplet}\n' + state = State.COUPLET + yield ' {} \\\\\n'.format(line) + elif state == State.REFRAIN: + # We are in the refrain. Depending on what we find: + if line != '': + # when a non-empty line is found, stay in the refrain. + yield ' {} \\\\\n'.format(line) + else: + # when an empty line is found, leave the refrain. + yield '\\end{refrain}\n\n' + state = State.LIMBO + elif state == State.COUPLET: + # We are in a couplet. Depending on what we find: + if line != '': + # when a non-empty line is found, stay in the couplet. + yield ' {} \\\\\n'.format(line) + else: + # when an empty line is found, leave the couplet. + yield '\\end{couplet}\n\n' + state = State.LIMBO + # No matter what we found, we have a song line, possibly empty, that we + # have to check to see if it could not be the new longest line seen so far. + if len(line) > len(longest): + longest = line + # The song text file is now finished. We may still be in the refrain or in a + # couplet, that we have to close. + if state == State.REFRAIN: + yield '\\end{refrain}\n\n' + elif state == State.COUPLET: + yield '\\end{couplet}\n\n' + # Now print the longest line of the entire song. + yield '\\longest{{{}}}\n'.format(longest) + + +def main(args=None): + parser = argparse.ArgumentParser(description="Convert song text to LaTeX songproj markup") + parser.add_argument("--refrain", '-r', type=re.compile, default=re_refrain, metavar="REGEXP", + help="specify the refrain marker (regexp, default to \"{}\")".format(re_refrain.pattern)) + parser.add_argument("--couplet", '-c', type=re.compile, default=re_couplet, metavar="REGEXP", + help="specify the couplet marker (regexp, default \"{}\")".format(re_couplet.pattern)) + parser.add_argument("infile", help="input song text file", type=argparse.FileType('r')) + parser.add_argument("outfile", help="output LaTeX file", type=argparse.FileType('w')) + args = parser.parse_args(args) + for line in parse_song(args.infile, args.refrain, args.couplet): + args.outfile.write(line) + +if __name__ == "__main__": + main() |