package info.mechyrdia.lore
data class TextParserScope<TContext>(
- var buffer: String = "",
val write: Appendable,
val tags: TextParserTags<TContext>,
val ctx: TContext
sealed class TextParserState<TContext>(
val scope: TextParserScope<TContext>,
- val insideTags: List<Pair<TextParserTagType.Indirect<TContext>, String?>>,
+ val insideTags: List<Pair<String, String?>>,
val insideDirectTags: List<String>
) {
abstract fun processCharacter(char: Char): TextParserState<TContext>
abstract fun processEndOfText()
protected fun appendText(text: String) {
- scope.buffer += censorText(text)
- }
-
- protected fun appendTextRaw(text: String) {
- scope.buffer += text
- }
-
- protected fun flushBuffer() {
scope.write.append(
- insideTags.foldRight(scope.buffer) { (tag, param), t ->
- tag.process(param, t, scope.ctx)
+ insideTags.foldRight(censorText(text)) { (tag, param), t ->
+ (scope.tags[tag] as? TextParserTagType.Indirect<TContext>)
+ ?.process(param, t, scope.ctx)
+ ?: "[$tag${param?.let { "=$it" } ?: ""}]$t[/$tag]"
}
)
- scope.buffer = ""
+ }
+
+ protected fun appendTextRaw(text: String) {
+ scope.write.append(text)
}
class Initial<TContext>(scope: TextParserScope<TContext>) : TextParserState<TContext>(scope, listOf(), listOf()) {
}
}
- class PlainText<TContext>(scope: TextParserScope<TContext>, val text: String, insideTags: List<Pair<TextParserTagType.Indirect<TContext>, String?>>, insideDirectTags: List<String>) : TextParserState<TContext>(scope, insideTags, insideDirectTags) {
+ class PlainText<TContext>(scope: TextParserScope<TContext>, val text: String, insideTags: List<Pair<String, String?>>, insideDirectTags: List<String>) : TextParserState<TContext>(scope, insideTags, insideDirectTags) {
override fun processCharacter(char: Char): TextParserState<TContext> {
return if (char == '[') {
appendText(text)
override fun processEndOfText() {
appendText(text)
- flushBuffer()
}
}
- class NoFormatText<TContext>(scope: TextParserScope<TContext>, val text: String, insideTags: List<Pair<TextParserTagType.Indirect<TContext>, String?>>, insideDirectTags: List<String>) : TextParserState<TContext>(scope, insideTags, insideDirectTags) {
+ class NoFormatText<TContext>(scope: TextParserScope<TContext>, val text: String, insideTags: List<Pair<String, String?>>, insideDirectTags: List<String>) : TextParserState<TContext>(scope, insideTags, insideDirectTags) {
override fun processCharacter(char: Char): TextParserState<TContext> {
val newText = text + char
return if (newText.endsWith("[/$NO_FORMAT_TAG]")) {
override fun processEndOfText() {
appendText(text)
- flushBuffer()
}
}
- class OpenTag<TContext>(scope: TextParserScope<TContext>, val tag: String, insideTags: List<Pair<TextParserTagType.Indirect<TContext>, String?>>, insideDirectTags: List<String>) : TextParserState<TContext>(scope, insideTags, insideDirectTags) {
+ class OpenTag<TContext>(scope: TextParserScope<TContext>, val tag: String, insideTags: List<Pair<String, String?>>, insideDirectTags: List<String>) : TextParserState<TContext>(scope, insideTags, insideDirectTags) {
override fun processCharacter(char: Char): TextParserState<TContext> {
return if (char == ']') {
if (tag.equals(NO_FORMAT_TAG, ignoreCase = true))
(scope.tags[tag] as? TextParserTagType.Direct<TContext>)?.begin(null, scope.ctx)?.let {
appendTextRaw(it)
}
- flushBuffer()
PlainText(scope, "", insideTags, insideDirectTags + tag)
- } else if (scope.tags[tag] is TextParserTagType.Indirect<TContext>) {
- val tagType = scope.tags[tag] as TextParserTagType.Indirect<TContext>
- flushBuffer()
-
- PlainText(scope, "", insideTags + (tagType to null), insideDirectTags)
- } else {
- appendText("[$tag]")
-
- PlainText(scope, "", insideTags, insideDirectTags)
- }
+ } else
+ PlainText(scope, "", insideTags + (tag to null), insideDirectTags)
} else if (char == '/' && tag == "")
CloseTag(scope, tag, insideTags, insideDirectTags)
else if (char == '=' && tag != "")
override fun processEndOfText() {
appendText("[$tag")
- flushBuffer()
}
}
- class TagParam<TContext>(scope: TextParserScope<TContext>, val tag: String, val param: String, insideTags: List<Pair<TextParserTagType.Indirect<TContext>, String?>>, insideDirectTags: List<String>) : TextParserState<TContext>(scope, insideTags, insideDirectTags) {
+ class TagParam<TContext>(scope: TextParserScope<TContext>, val tag: String, val param: String, insideTags: List<Pair<String, String?>>, insideDirectTags: List<String>) : TextParserState<TContext>(scope, insideTags, insideDirectTags) {
override fun processCharacter(char: Char): TextParserState<TContext> {
return if (char == ']') {
- when (val tagType = scope.tags[tag]) {
- is TextParserTagType.Direct<TContext> -> {
- appendTextRaw(tagType.begin(param, scope.ctx))
- flushBuffer()
-
- PlainText(scope, "", insideTags, insideDirectTags + tag)
- }
+ val tagType = scope.tags[tag]
+ if (tagType is TextParserTagType.Direct<TContext>) {
+ appendTextRaw(tagType.begin(param, scope.ctx))
- is TextParserTagType.Indirect<TContext> -> {
- flushBuffer()
- PlainText(scope, "", insideTags + (tagType to param), insideDirectTags)
- }
- else -> {
- appendText("[$tag=$param]")
-
- PlainText(scope, "", insideTags, insideDirectTags)
- }
- }
+ PlainText(scope, "", insideTags, insideDirectTags + tag)
+ } else
+ PlainText(scope, "", insideTags + (tag to param), insideDirectTags)
} else
TagParam(scope, tag, param + char, insideTags, insideDirectTags)
}
override fun processEndOfText() {
appendText("[$tag=$param")
- flushBuffer()
}
}
- class CloseTag<TContext>(scope: TextParserScope<TContext>, val tag: String, insideTags: List<Pair<TextParserTagType.Indirect<TContext>, String?>>, insideDirectTags: List<String>) : TextParserState<TContext>(scope, insideTags, insideDirectTags) {
+ class CloseTag<TContext>(scope: TextParserScope<TContext>, val tag: String, insideTags: List<Pair<String, String?>>, insideDirectTags: List<String>) : TextParserState<TContext>(scope, insideTags, insideDirectTags) {
override fun processCharacter(char: Char): TextParserState<TContext> {
return if (char == ']') {
val tagType = scope.tags[tag]
if (tagType is TextParserTagType.Direct<TContext> && insideDirectTags.last().equals(tag, ignoreCase = true)) {
appendTextRaw(tagType.end(scope.ctx))
- flushBuffer()
+
PlainText(scope, "", insideTags, insideDirectTags.dropLast(1))
- } else if (insideTags.isNotEmpty() && insideTags.last().first == scope.tags[tag]) {
- flushBuffer()
+ } else if (insideTags.isNotEmpty() && insideTags.last().first.equals(tag, ignoreCase = true)) {
PlainText(scope, "", insideTags.dropLast(1), insideDirectTags)
} else {
appendText("[/$tag]")
override fun processEndOfText() {
appendText("[/$tag")
- flushBuffer()
}
}
val builder = StringBuilder()
try {
val fixedText = text.replace("\r\n", "\n").replace('\r', '\n')
- fixedText.fold<TextParserState<TContext>>(Initial(TextParserScope("", builder, tags, context))) { state, char -> state.processCharacter(char) }.processEndOfText()
+ fixedText.fold<TextParserState<TContext>>(Initial(TextParserScope(builder, tags, context))) { state, char -> state.processCharacter(char) }.processEndOfText()
} catch (ex: Exception) {
return ParseOutcome("<p>$builder</p><h1>Internal Error!</h1><pre>${ex.stackTraceToString()}</pre>", false)
}