<commit_before>;;; Convert lyrics from "chords above text" to chords inline in brackets.
;;;

(provide 'flktex-utils)
(setq max-lisp-eval-depth 6000)

(defun inline-chords-at-point ()
    "Convert the two lines at point from chords-over-lyrics to chords-inline.
If executed on an empty line, it inserts two backslashes to mark the end of a verse."
    (interactive)
    (beginning-of-line)
    (if (looking-at "^[:space:]*$")
	(progn (gobble-line) (insert "\\\\"))
      ;; this would be simpler in Haskell, where a string is just a list of characters.
      ;; as it is, we use lists to avoid creating unnecessary intermediate strings.
      (mapc 'insert (inline-chords (gobble-line) (gobble-line))))
    (insert "\n"))

(defun gobble-line ()
  "Remove the line at point, and return it as a list of 1-character strings."
  (let ((c (char-after)))
    (if (or (null c) (char-equal ?\n c))
	(progn (delete-char 1) nil)
      (cons (string  c) (progn (delete-char 1) (gobble-line)))
      )))

(defun inline-chords (chords lyrics)
  "This function takes a line of chords and a line of lyrics, passed as lists of
single-character strings, and converts them to a single line with chords inlined
in brackets."
  (cond ((null chords) lyrics)
	((equal " " (car chords))
	 (if (null lyrics)
	     (cons " " (inline-chords (cdr chords) nil))
	   (cons (car lyrics) (inline-chords (cdr chords) (cdr lyrics)))))
	((cons "[" (convert-chord chords lyrics "")))
	))

(defun convert-chord (chords lyrics lyrics-under-chord)
  "Convert a chord at the front of lyrics, and follow it with the remaining lyrics.
The lyrics under the chord are accumulated in a string."
  (cond ((null chords) (cons "]" (if (equal "" lyrics-under-chord)
				     lyrics
				   (cons lyrics-under-chord lyrics))))
	((equal " " (car chords))
	 (cons "]" (if (equal "" lyrics-under-chord)
		       (inline-chords chords lyrics)
		     (cons lyrics-under-chord (inline-chords chords lyrics)))))
	((null lyrics)
	 (cons (convert-chord-char (car chords) (cdr chords))
	       (convert-chord (cdr chords) nil lyrics-under-chord)))
	((cons (convert-chord-char (car chords) (cdr chords))
	       (convert-chord (cdr chords)
			      (cdr lyrics)
			      (concat lyrics-under-chord (car lyrics)))))
	))

		 
;;; Convert a single character in a chord.
;;;    # -> \sharp
;;;    b -> \flat
;;;    m -> \min -- just \m if followed by a or i
;;;    \s -> \s if followed by u, else s
;;;    
(defun convert-chord-char (char rest)
  (cond ((equal "#" char) "\\sharp")<commit_msg>refactor inlie-chords to use tail recursion<commit_after>;;; Convert lyrics from "chords above text" to chords inline in brackets.
;;;

(provide 'flktex-utils)
(setq max-lisp-eval-depth 6000)

(defun inline-chords-at-point ()
    "Convert the two lines at point from chords-over-lyrics to chords-inline.
If executed on an empty line, it inserts two backslashes to mark the end of a verse."
    (interactive)
    (beginning-of-line)
    (if (looking-at "^[:space:]*$")
	(progn (gobble-line) (insert "\\\\"))
      ;; this would be simpler in Haskell, where a string is just a list of characters.
      ;; as it is, we use lists to avoid creating unnecessary intermediate strings.
      (mapc 'insert (inline-chords (gobble-line) (gobble-line))))
    (insert "\n"))

(defun gobble-line ()
  "Remove the line at point, and return it as a list of 1-character strings."
  (let ((c (char-after)))
    (if (or (null c) (char-equal ?\n c))
	(progn (delete-char 1) nil)
      (cons (string  c) (progn (delete-char 1) (gobble-line)))
      )))

(defun inline-chords (chords lyrics)
  "This function takes a line of chords and a line of lyrics, passed as lists of
single-character strings, and converts them to a single line with chords inlined
in brackets, returned as a list of strings."
  (reverse (convert-chords chords lyrics nil)))

(defun convert-chords (chords lyrics result)
  "This function converts a line of chords and a line of lyrics into a list of strings
in reverse order.  We do it this way to take advantage of tail recursion."
  (cond ((null chords)
	 (if (null lyrics)
	     result
	   (convert-chords nil (cdr lyrics) (cons (car lyrics) result))))
	((equal " " (car chords))
	 (if (null lyrics)
	     (convert-chords (cdr chords) nil (cons " " result))
	   (convert-chords (cdr chords) (cdr lyrics) (cons (car lyrics) result))))
	((convert-chord chords lyrics "" (cons "[" result)))
	))

(defun convert-chord (chords lyrics lyrics-under-chord result)
  "Convert a chord at the front of lyrics, and follow it with the remaining lyrics.
The lyrics under the chord are accumulated in a string."
  (cond ((or (null chords) (equal " " (car chords)))
	 (convert-chords chords lyrics (cons (concat "]" lyrics-under-chord) result)))
	((null lyrics)
	 (convert-chord (cdr chords) nil
			lyrics-under-chord
			(cons (convert-chord-char (car chords) (cdr chords)) result)))
	((convert-chord (cdr chords) (cdr lyrics)
			(concat lyrics-under-chord (car lyrics))
			(cons (convert-chord-char (car chords) (cdr chords)) result)))
	))

;;; Convert a single character in a chord.
;;;    # -> \sharp
;;;    b -> \flat
;;;    m -> \min -- just \m if followed by a or i
;;;    \s -> \s if followed by u, else s
;;;    
(defun convert-chord-char (char rest)
  (cond ((equal "#" char) "\\sharp")
{'fraction_non_alphanumeric': 0.13874246987951808, 'fraction_numerical': 0.0026355421686746986, 'mean_word_length': 3.3160032493907394, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 11, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '314377', 'n_tokens_mistral': 1839, 'n_tokens_neox': 1738, 'n_words': 668}