import std/strutils proc toggle[T](s: var set[T], t: T): bool = result = t notin s if result: s.incl(t) else: s.excl(t) type BracketState = enum bsNone, bsInBracketRef, bsInBracket, bsAfterBracket, bsInParen, bsInImage proc getId(line: openArray[char]): string = result = "" var i = 0 var bs = bsNone var escape = false while i < line.len: let c = line[i] if bs == bsInParen: if escape: escape = false inc i continue if c == ')': bs = bsNone elif c == '\\': escape = true inc i continue case c of 'A'..'Z': result &= char(int(c) - int('A') + int('a')) of 'a'..'z', '-', '_', '.': result &= c of ' ': result &= '-' of '[': if bs != bsNone: bs = bsInBracket of ']': if bs == bsInBracket: bs = bsAfterBracket of '(': if bs == bsAfterBracket: bs = bsInParen else: discard inc i type InlineState = enum isItalic, isBold, isCode, isComment, isDel const AsciiAlphaNumeric = {'0'..'9', 'A'..'Z', 'a'..'z'} proc parseInline(line: openArray[char]) = var state: set[InlineState] = {} var bs = bsNone var i = 0 var bracketChars = "" var quote = false var image = false template append(s: untyped) = if bs in {bsInBracketRef, bsInBracket}: bracketChars &= s else: stdout.write(s) while i < line.len: let c = line[i] if bs == bsAfterBracket and c != '(': stdout.write("[" & bracketChars & "]") bracketChars = "" bs = bsNone image = false if quote: append c elif isComment in state: if i + 2 < line.len and line.toOpenArray(i, i + 2) == "-->": state.excl(isComment) append "-->" i += 2 else: append c elif isCode in state: case c of '<': append "<" of '>': append ">" of '"': append """ of '\'': append "'" of '&': append "&" of '`': append "" state.excl(isCode) else: append c elif c == '\\': quote = true elif c == '*' or c == '_' and (i == 0 or line[i - 1] notin AsciiAlphaNumeric or i + 1 >= line.len or line[i + 1] notin AsciiAlphaNumeric + {'_'}): if i + 1 < line.len and line[i + 1] == c: if state.toggle(isBold): append "" else: append "" inc i else: if state.toggle(isItalic): stdout.write("") else: stdout.write("") elif c == '`': state.incl(isCode) append "" elif c == '~' and i + 1 < line.len and line[i + 1] == '~': if state.toggle(isDel): append "" else: append "" inc i elif c == '!' and bs == bsNone and i + 1 < line.len and line[i + 1] == '[': image = true elif c == '[' and bs == bsNone: bs = bsInBracket if i + 1 < line.len and line[i + 1] == '^': inc i bs = bsInBracketRef elif c == ']' and bs == bsInBracketRef: let id = bracketChars.getId() stdout.write("" & bracketChars & "") bracketChars = "" elif c == ']' and bs == bsInBracket: bs = bsAfterBracket elif c == '(' and bs == bsAfterBracket: if image: stdout.write("" & bracketChars & "") else: stdout.write("'>" & bracketChars & "") image = false bracketChars = "" bs = bsNone elif c == '\'' and bs == bsInParen: stdout.write("'") elif i + 4 < line.len and line.toOpenArray(i, i + 3) == "") if i != -1: stdout.write(line.substr(0, i + 2)) state.blockType = btNone line.substr(i + 3).parseInline() else: stdout.write(line & "\n") proc main() = var line: string var state = ParseState(listDepth: -1) while state.reprocess or stdin.readLine(line): state.reprocess = false case state.blockType of btNone: state.parseNone(line) of btPre: state.parsePre(line) of btTabPre: state.parseTabPre(line) of btSpacePre: state.parseSpacePre(line) of btList: state.parseList(line) of btPar: state.parsePar(line) of btHTML: state.parseHTML(line) of btHTMLPre: state.parseHTMLPre(line) of btComment: state.parseComment(line) state.blockData.parseInline() main()