package space.bloggin.vellum /** * Verse markup: {words|H430} Strong's group, [words] translator italics, words of Jesus. */ object Markup { data class Seg(val text: String, val jesus: Boolean, val italic: Boolean, val strong: String?) fun parse(s: String): List { val out = ArrayList() var jesus = false var italic = false val buf = StringBuilder() fun flush(strong: String? = null) { if (buf.isNotEmpty()) { out.add(Seg(buf.toString(), jesus, italic, strong)); buf.clear() } } var i = 0 while (i < s.length) { when (val ch = s[i]) { '<' -> { flush(); jesus = true } '>' -> { flush(); jesus = false } '[' -> { flush(); italic = true } ']' -> { flush(); italic = false } '{' -> { flush() val bar = s.indexOf('|', i) val close = s.indexOf('}', i) if (bar in (i + 1) until close) { // the word itself may contain [ ] italics in rare cases; strip them val word = s.substring(i + 1, bar) val num = s.substring(bar + 1, close) out.add(Seg(word, jesus, italic, num)) i = close } else buf.append(ch) } else -> buf.append(ch) } i++ } flush() return out } fun plain(s: String): String { if (s.none { it == '{' || it == '[' || it == '<' }) return s val sb = StringBuilder(s.length) var i = 0 while (i < s.length) { val ch = s[i] when (ch) { '<', '>', '[', ']' -> {} '{' -> { val bar = s.indexOf('|', i) val close = s.indexOf('}', i) if (bar in (i + 1) until close) { sb.append(s, i + 1, bar); i = close } else sb.append(ch) } else -> sb.append(ch) } i++ } return sb.toString() } /** Strong's-tagged words in a verse, in order, deduplicated by number+word. */ fun words(s: String): List> = parse(s).filter { it.strong != null }.map { it.text to it.strong!! }.distinct() }