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,
bsInTag
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'}
func startsWithScheme(s: string): bool =
for i, c in s:
if i > 0 and c == ':':
return true
if c notin AsciiAlphaNumeric:
break
false
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 bs == bsInTag:
if c == '>': # done
if bracketChars.startsWithScheme(): # link
var linkChars = ""
for c in bracketChars:
if c == '\'':
linkChars &= "&apos"
else:
linkChars &= c
stdout.write("" & bracketChars & "")
else: # tag
stdout.write('<' & bracketChars & '>')
bracketChars = ""
bs = bsNone
elif c == '<':
stdout.write('<' & bracketChars)
bracketChars = ""
else:
bracketChars &= 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("")
else:
stdout.write("'>" & bracketChars & "")
image = false
bracketChars = ""
bs = bsNone
elif c == '\'' and bs == bsInParen:
stdout.write("'")
elif c == '<' and bs == bsNone:
bs = bsInTag
bracketChars = ""
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()