diff options
author | bptato <nincsnevem662@gmail.com> | 2021-03-12 20:52:44 +0100 |
---|---|---|
committer | bptato <nincsnevem662@gmail.com> | 2021-03-12 20:53:18 +0100 |
commit | 6084e104d5c3868196c9e2d3748c6627cf983470 (patch) | |
tree | f5103ffd0b33049e74b44f7a91448a22e3c8ef74 | |
parent | 424f52e1f2eff84ee23a79b38e7932c2918351ed (diff) | |
download | chawan-6084e104d5c3868196c9e2d3748c6627cf983470.tar.gz |
Replaced parser, readline, etc...
-rw-r--r-- | .gitignore | 2 | ||||
-rw-r--r-- | Makefile | 4 | ||||
-rw-r--r-- | as | 5 | ||||
-rw-r--r-- | buffer.nim | 48 | ||||
-rw-r--r-- | config | 343 | ||||
-rw-r--r-- | config.nim | 118 | ||||
-rw-r--r-- | default.css | 0 | ||||
-rw-r--r-- | display.nim | 122 | ||||
-rw-r--r-- | dom.nim | 346 | ||||
-rw-r--r-- | entity.json | 2233 | ||||
-rw-r--r-- | enums.nim | 24 | ||||
-rw-r--r-- | htmlelement.nim | 368 | ||||
-rw-r--r-- | main.nim | 10 | ||||
-rw-r--r-- | parser.nim | 865 | ||||
-rw-r--r-- | radixtree.nim | 153 | ||||
-rw-r--r-- | style.nim | 0 | ||||
-rw-r--r-- | twtio.nim | 267 | ||||
-rw-r--r-- | twtstr.nim | 99 |
18 files changed, 3824 insertions, 1183 deletions
diff --git a/.gitignore b/.gitignore index 7dab9430..6197c0e1 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,2 @@ a twt -dtwt -twt_opt diff --git a/Makefile b/Makefile index 9d231e4a..74872b8a 100644 --- a/Makefile +++ b/Makefile @@ -1,9 +1,9 @@ debug: - nim compile -d:ssl -o:dtwt main.nim + nim compile -d:ssl -o:twt main.nim release: nim compile -d:release -d:ssl -o:twt main.nim release_opt: - nim compile -d:danger -d:ssl -o:twt_opt main.nim + nim compile -d:danger -d:ssl -o:twt main.nim clean: rm ./twt ./dtwt ./twt_opt all: debug release release_opt diff --git a/as b/as new file mode 100644 index 00000000..1b43b788 --- /dev/null +++ b/as @@ -0,0 +1,5 @@ +<html> +<body> +Hello? +</body> +</html> diff --git a/buffer.nim b/buffer.nim index 1897cdc4..cb67c417 100644 --- a/buffer.nim +++ b/buffer.nim @@ -6,10 +6,8 @@ import tables import strutils import unicode -import fusion/htmlparser/xmltree - import termattrs -import htmlelement +import dom import twtio import enums import twtstr @@ -21,21 +19,19 @@ type rawtext*: seq[string] title*: string hovertext*: string - htmlsource*: XmlNode width*: int height*: int cursorx*: int cursory*: int - cursorchar*: int xend*: int fromx*: int fromy*: int - nodes*: seq[HtmlNode] - links*: seq[HtmlNode] - clickables*: seq[HtmlNode] + nodes*: seq[Node] + links*: seq[Node] + clickables*: seq[Node] elements*: seq[HtmlElement] idelements*: Table[string, HtmlElement] - selectedlink*: HtmlNode + selectedlink*: Node printwrite*: bool attrs*: TermAttributes document*: Document @@ -52,13 +48,11 @@ func lastLine*(buffer: Buffer): int = func lastVisibleLine*(buffer: Buffer): int = return min(buffer.fromy + buffer.height - 1, buffer.lastLine()) -func realCurrentLineLength*(buffer: Buffer): int = - return mk_wcswidth_cjk(buffer.rawtext[buffer.cursory]) - func currentLineLength*(buffer: Buffer): int = - return buffer.rawtext[buffer.cursory].runeLen() + return buffer.rawtext[buffer.cursory].width() func atPercentOf*(buffer: Buffer): int = + if buffer.fmttext.len == 0: return 100 return (100 * (buffer.cursory + 1)) div (buffer.lastLine() + 1) func fmtBetween*(buffer: Buffer, sx: int, sy: int, ex: int, ey: int): string = @@ -75,10 +69,10 @@ func fmtBetween*(buffer: Buffer, sx: int, sy: int, ex: int, ey: int): string = func visibleText*(buffer: Buffer): string = return buffer.fmttext[buffer.fromy..buffer.lastVisibleLine()].join("\n") -func lastNode*(buffer: Buffer): HtmlNode = +func lastNode*(buffer: Buffer): Node = return buffer.nodes[^1] -func cursorOnNode*(buffer: Buffer, node: HtmlNode): bool = +func cursorOnNode*(buffer: Buffer, node: Node): bool = if node.y == node.ey and node.y == buffer.cursory: return buffer.cursorx >= node.x and buffer.cursorx < node.ex else: @@ -103,17 +97,18 @@ func getElementById*(buffer: Buffer, id: string): HtmlElement = return buffer.idelements[id] return nil -proc findSelectedNode*(buffer: Buffer): Option[HtmlNode] = +proc findSelectedNode*(buffer: Buffer): Option[Node] = for node in buffer.nodes: if node.getFmtLen() > 0 and node.displayed(): if buffer.cursory >= node.y and buffer.cursory <= node.y + node.height and buffer.cursorx >= node.x and buffer.cursorx <= node.x + node.width: return some(node) - return none(HtmlNode) + return none(Node) -proc addNode*(buffer: Buffer, htmlNode: HtmlNode) = +proc addNode*(buffer: Buffer, htmlNode: Node) = buffer.nodes.add(htmlNode) - if htmlNode.isTextNode() and htmlNode.parentElement != nil and htmlNode.parentElement.islink: + #TODO + if htmlNode.isTextNode() and htmlNode.parentElement != nil and HtmlElement(htmlNode.parentElement).islink: buffer.links.add(htmlNode) if htmlNode.isElemNode(): @@ -123,7 +118,8 @@ proc addNode*(buffer: Buffer, htmlNode: HtmlNode) = buffer.clickables.add(htmlNode) else: discard elif htmlNode.isTextNode(): - if htmlNode.parentElement != nil and htmlNode.parentElement.islink: + #TODO + if htmlNode.parentElement != nil and HtmlElement(htmlNode.parentElement).islink: let anchor = htmlNode.ancestor(TAG_A) assert(anchor != nil) buffer.clickables.add(anchor) @@ -216,9 +212,9 @@ proc cursorUp*(buffer: Buffer): bool = if buffer.cursorx > buffer.currentLineLength(): if buffer.cursorx == 0: buffer.xend = buffer.cursorx - buffer.cursorx = buffer.currentLineLength() + buffer.cursorx = max(buffer.currentLineLength() - 1, 0) elif buffer.xend > 0: - buffer.cursorx = min(buffer.currentLineLength(), buffer.xend) + buffer.cursorx = min(buffer.currentLineLength() - 1, buffer.xend) if buffer.cursory < buffer.fromy: dec buffer.fromy return true @@ -246,7 +242,7 @@ proc cursorLineEnd*(buffer: Buffer) = buffer.cursorx = buffer.currentLineLength() - 1 buffer.xend = buffer.cursorx -iterator revnodes*(buffer: Buffer): HtmlNode {.inline.} = +iterator revnodes*(buffer: Buffer): Node {.inline.} = var i = buffer.nodes.len - 1 while i >= 0: yield buffer.nodes[i] @@ -303,7 +299,7 @@ proc cursorPrevWord*(buffer: Buffer): bool = x = buffer.rawtext[y].runeLen() - 1 return buffer.cursorTo(x, y) -iterator revclickables*(buffer: Buffer): HtmlNode {.inline.} = +iterator revclickables*(buffer: Buffer): Node {.inline.} = var i = buffer.clickables.len - 1 while i >= 0: yield buffer.clickables[i] @@ -419,7 +415,7 @@ proc checkLinkSelection*(buffer: Buffer): bool = buffer.selectedlink.fmttext = buffer.selectedlink.getFmtText() buffer.selectedlink = nil buffer.hovertext = "" - var stack: seq[HtmlNode] + var stack: seq[Node] stack.add(anchor) while stack.len > 0: let elem = stack.pop() @@ -433,7 +429,7 @@ proc checkLinkSelection*(buffer: Buffer): bool = assert(anchor != nil) anchor.selected = true buffer.hovertext = HtmlAnchorElement(anchor).href - var stack: seq[HtmlNode] + var stack: seq[Node] stack.add(anchor) while stack.len > 0: let elem = stack.pop() diff --git a/config b/config index a40c4e16..99397288 100644 --- a/config +++ b/config @@ -45,6 +45,7 @@ lemap C-m LINED_SUBMIT lemap C-j LINED_SUBMIT lemap C-h LINED_BACKSPACE lemap C-? LINED_BACKSPACE +lemap C-d LINED_DELETE lemap C-c LINED_CANCEL lemap \eb LINED_PREV_WORD lemap \ef LINED_NEXT_WORD @@ -54,3 +55,345 @@ lemap C-u LINED_CLEAR lemap C-k LINED_KILL lemap C-w LINED_KILL_WORD lemap C-v LINED_ESC +lemap C-g LINED_COMPOSE_TOGGLE + +#compose keybindings + +#european +comp a: ä +comp o: ö +comp u: ü +comp u" ű +comp o" ő + +comp a' á +comp e' é +comp i' í +comp o' ó +comp u' ú + +comp sS ß +comp n~ ñ + +#hiragana +comp a あ +comp i い +comp u う +comp e え +comp o お + +comp la ぁ +comp li ぃ +comp lu ぅ +comp le ぇ +comp lo ぉ + +comp ka か +comp ki き +comp ku く +comp ke け +comp ko こ + +comp kka っか +comp kki っき +comp kku っく +comp kke っけ +comp kko っこ + +comp sa さ +comp shi し +comp su す +comp se せ +comp so そ + +comp ssa っさ +comp sshi っし +comp ssu っす +comp sse っせ +comp sso っそ + +comp ta た +comp chi ち +comp tsu つ +comp te て +comp to と + +comp tta った +comp tchi っち +comp ttsu っつ +comp tte って +comp tto っと + +comp na な +comp ni に +comp nu ぬ +comp ne ね +comp no の + +comp ha は +comp hi ひ +comp fu ふ +comp he へ +comp ho ほ + +comp ma ま +comp mi み +comp mu む +comp me め +comp mo も + +comp ya や +comp yu ゆ +comp yo よ + +comp ra ら +comp ri り +comp ru る +comp re れ +comp ro ろ + +comp wa わ +comp wi ゐ +comp we ゑ +comp wo を + +comp ga が +comp gi ぎ +comp gu ぐ +comp ge げ +comp go ご + +comp za ざ +comp ji じ +comp zu ず +comp ze ぜ +comp zo ぞ + +comp da だ +comp dji ぢ +comp dzu づ +comp de で +comp do ど + +comp ba ば +comp bi び +comp bu ぶ +comp be べ +comp bo ぼ + +comp pa ぱ +comp pi ぴ +comp pu ぷ +comp pe ぺ +comp po ぽ + +comp n ん + +comp kya きゃ +comp kyu きゅ +comp kyo きょ + +comp sha しゃ +comp shu しゅ +comp sho しょ + +comp cha ちゃ +comp chu ちゅ +comp cho ちょ + +comp nya にゃ +comp nyu にゅ +comp nyo にょ + +comp hya ひゃ +comp hyu ひゅ +comp hyo ひょ + +comp mya みゃ +comp myu みゅ +comp myo みょ + +comp rya りゃ +comp ryu りゅ +comp ryo りょ + +comp gya ぎゃ +comp gyu ぎゅ +comp gyo ぎょ + +comp ja じゃ +comp ju じゅ +comp jo じょ + +comp bya びゃ +comp byu びゅ +comp byo びょ + +comp pya ぴゃ +comp pyu ぴゅ +comp pyo ぴょ + +comp kwa くゎ +comp gwa ぐゎ + +#katakana +comp A ア +comp I イ +comp U ウ +comp E エ +comp O オ + +comp LA ォ +comp LI ィ +comp LU ゥ +comp LE ェ +comp LO ォ + +comp KA か +comp KI き +comp KU く +comp KE け +comp KO こ + +comp KKA ッカ +comp KKI ッキ +comp KKU ック +comp KKE ッケ +comp KKO ッコ + +comp SA サ +comp SHI シ +comp SU ス +comp SE セ +comp SO ソ + +comp SSA ッサ +comp SSHI ッシ +comp SSU ッス +comp SSE ッセ +comp SSO ッソ + +comp TA タ +comp CHI チ +comp TSU ツ +comp TE テ +comp TO ト + +comp TTA ッタ +comp TCHI ッチ +comp TTSU ッツ +comp TTE ッテ +comp TTO ッと + +comp NA ナ +comp NI ニ +comp NU ヌ +comp NE ネ +comp NO ノ + +comp HA ハ +comp HI ヒ +comp FU フ +comp HE ヘ +comp HO ホ + +comp MA マ +comp MI ミ +comp MU ム +comp ME メ +comp MO モ + +comp YA ヤ +comp YU ユ +comp YO ヨ + +comp RA ラ +comp RI リ +comp RU ル +comp RE レ +comp RO ロ + +comp WA ワ +comp WI ヰ +comp WE ヱ +comp WO ヲ + +comp GA ガ +comp GI ギ +comp GU グ +comp GE ゲ +comp GO ゴ + +comp ZA ザ +comp JI ジ +comp ZU ズ +comp ZE ゼ +comp ZO ゾ + +comp DA ダ +comp DJI ヂ +comp DZU ヅ +comp DE デ +comp DO ド + +comp BA バ +comp BI ビ +comp BU ブ +comp BE ベ +comp BO ボ + +comp PA パ +comp PI ピ +comp PU プ +comp PE ペ +comp PO ポ + +comp N ン +comp xc ー + +comp KYA キャ +comp KYU キュ +comp KYO キョ + +comp SHA シャ +comp SHU シュ +comp SHO ショ + +comp CHA チャ +comp CHU チュ +comp CHO チョ + +comp NYA ニャ +comp NYU ニュ +comp NYO ニョ + +comp HYA ヒャ +comp HYU ヒュ +comp HYO ヒョ + +comp MYA ミャ +comp MYU ミュ +comp MYO ミョ + +comp RYA リャ +comp RYU リュ +comp RYO リョ + +comp GYA ギャ +comp GYU ギュ +comp GYO ギョ + +comp JA ジャ +comp JU ジュ +comp JO ジョ + +comp BYA ビャ +comp BYU ビュ +comp BYO ビョ + +comp PYA ピャ +comp PYU ピュ +comp PYO ピョ + +comp KWA クヮ +comp GWA グヮ diff --git a/config.nim b/config.nim index 0e7aa92b..6d185a1d 100644 --- a/config.nim +++ b/config.nim @@ -1,8 +1,8 @@ import tables import strutils -import macros import twtstr +import radixtree type TwtAction* = @@ -25,15 +25,21 @@ type ACTION_CURSOR_TOP, ACTION_CURSOR_MIDDLE, ACTION_CURSOR_BOTTOM, ACTION_CENTER_LINE, ACTION_LINE_INFO, ACTION_LINED_SUBMIT, ACTION_LINED_CANCEL, - ACTION_LINED_BACKSPACE, ACTION_LINED_CLEAR, ACTION_LINED_KILL, ACTION_LINED_KILL_WORD, + ACTION_LINED_BACKSPACE, ACTION_LINED_DELETE, + ACTION_LINED_CLEAR, ACTION_LINED_KILL, ACTION_LINED_KILL_WORD, ACTION_LINED_BACK, ACTION_LINED_FORWARD, ACTION_LINED_PREV_WORD, ACTION_LINED_NEXT_WORD, + ACTION_LINED_COMPOSE_TOGGLE, ACTION_LINED_COMPOSE_ON, ACTION_LINED_COMPOSE_OFF ACTION_LINED_ESC -var normalActionRemap*: Table[string, TwtAction] -var linedActionRemap*: Table[string, TwtAction] + ActionMap = Table[string, TwtAction] + ComposeMap = RadixTree[string] -proc getRealKey(key: string): string = +var normalActionRemap*: ActionMap +var linedActionRemap*: ActionMap +var composeRemap*: ComposeMap + +func getRealKey(key: string): string = var realk: string var currchar: char var control = 0 @@ -64,9 +70,9 @@ proc getRealKey(key: string): string = realk &= 'C' return realk -proc constructActionTable*(origTable: var Table[string, TwtAction]): Table[string, TwtAction] = - var newTable: Table[string, TwtAction] - var strs = newSeq[string](0) +func constructActionTable*(origTable: ActionMap): ActionMap = + var newTable: ActionMap + var strs: seq[string] for k in origTable.keys: let realk = getRealKey(k) var teststr = "" @@ -74,7 +80,7 @@ proc constructActionTable*(origTable: var Table[string, TwtAction]): Table[strin teststr &= c strs.add(teststr) - for k, v in origTable.mpairs: + for k, v in origTable: let realk = getRealKey(k) var teststr = "" for c in realk: @@ -84,84 +90,50 @@ proc constructActionTable*(origTable: var Table[string, TwtAction]): Table[strin newTable[realk] = v return newTable -macro staticReadKeymap(): untyped = - var config = staticRead"config" - var normalActionMap: Table[string, TwtAction] - var linedActionMap: Table[string, TwtAction] +proc parseConfigLine(line: string, nmap: var ActionMap, lemap: var ActionMap, + compose: var ComposeMap) = + if line.len == 0 or line[0] == '#': + return + let cmd = line.split(' ') + if cmd.len == 3: + if cmd[0] == "nmap": + nmap[getRealKey(cmd[1])] = parseEnum[TwtAction]("ACTION_" & cmd[2]) + elif cmd[0] == "lemap": + lemap[getRealKey(cmd[1])] = parseEnum[TwtAction]("ACTION_" & cmd[2]) + elif cmd[0] == "comp": + compose[getRealKey(cmd[1])] = cmd[2] + +proc staticReadKeymap(): (ActionMap, ActionMap, ComposeMap) = + let config = staticRead"config" + var nmap: ActionMap + var lemap: ActionMap + var compose = newRadixTree[string]() for line in config.split('\n'): - if line.len == 0 or line[0] == '#': - continue - let cmd = line.split(' ') - if cmd.len == 3: - if cmd[0] == "nmap": - normalActionMap[getRealKey(cmd[1])] = parseEnum[TwtAction]("ACTION_" & cmd[2]) - elif cmd[0] == "lemap": - linedActionMap[getRealKey(cmd[1])] = parseEnum[TwtAction]("ACTION_" & cmd[2]) - - normalActionMap = constructActionTable(normalActionMap) - linedActionMap = constructActionTable(linedActionMap) - - let normalActionConstr = nnkTableConstr.newTree() - for k, v in normalActionMap: - let colonExpr = nnkExprColonExpr.newTree() - colonExpr.add(newLit(k)) - colonExpr.add(newLit(v)) - normalActionConstr.add(colonExpr) + parseConfigLine(line, nmap, lemap, compose) - let normalActionAsgn = nnkAsgn.newTree() - normalActionAsgn.add(ident("normalActionRemap")) - normalActionAsgn.add(newCall(ident("toTable"), normalActionConstr)) + nmap = constructActionTable(nmap) + lemap = constructActionTable(lemap) + return (nmap, lemap, compose) - let linedActionConstr = nnkTableConstr.newTree() - for k, v in linedActionMap: - let colonExpr = nnkExprColonExpr.newTree() - colonExpr.add(newLit(k)) - colonExpr.add(newLit(v)) - linedActionConstr.add(colonExpr) +const (normalActionMap, linedActionMap, composeMap) = staticReadKeymap() - let linedActionAsgn = nnkAsgn.newTree() - linedActionAsgn.add(ident("linedActionRemap")) - linedActionAsgn.add(newCall(ident("toTable"), linedActionConstr)) - result = newStmtList() - result.add(normalActionAsgn) - -staticReadKeymap() +normalActionRemap = normalActionMap +linedActionRemap = linedActionMap +composeRemap = composeMap proc readConfig*(filename: string): bool = var f: File let status = f.open(filename, fmRead) - var normalActionMap: Table[string, TwtAction] - var linedActionMap: Table[string, TwtAction] + var nmap: ActionMap + var lemap: ActionMap + var compose = newRadixTree[string]() if status: var line: TaintedString while f.readLine(line): - if line.string.len == 0 or line.string[0] == '#': - continue - let cmd = line.split(' ') - if cmd.len == 3: - if cmd[0] == "nmap": - normalActionMap[getRealKey(cmd[1])] = parseEnum[TwtAction]("ACTION_" & cmd[2]) - elif cmd[0] == "lemap": - linedActionMap[getRealKey(cmd[1])] = parseEnum[TwtAction]("ACTION_" & cmd[2]) + parseConfigLine(line, nmap, lemap, compose) normalActionRemap = constructActionTable(normalActionMap) linedActionRemap = constructActionTable(linedActionMap) return true else: return false - -proc parseKeymap*(keymap: string) = - var normalActionMap: Table[string, TwtAction] - var linedActionMap: Table[string, TwtAction] - for line in keymap.split('\n'): - if line.len == 0 or line[0] == '#': - continue - let cmd = line.split(' ') - if cmd.len == 3: - if cmd[0] == "nmap": - normalActionMap[getRealKey(cmd[1])] = parseEnum[TwtAction]("ACTION_" & cmd[2]) - elif cmd[0] == "lemap": - linedActionMap[getRealKey(cmd[1])] = parseEnum[TwtAction]("ACTION_" & cmd[2]) - - normalActionRemap = constructActionTable(normalActionMap) - linedActionRemap = constructActionTable(linedActionMap) diff --git a/default.css b/default.css new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/default.css diff --git a/display.nim b/display.nim index 4bf15921..ff5e63f1 100644 --- a/display.nim +++ b/display.nim @@ -4,11 +4,9 @@ import uri import strutils import unicode -import fusion/htmlparser/xmltree - import buffer import termattrs -import htmlelement +import dom import twtstr import twtio import config @@ -29,7 +27,7 @@ type lastwidth: int fmtline: string rawline: string - centerqueue: seq[HtmlNode] + centerqueue: seq[Node] centerlen: int blanklines: int blankspaces: int @@ -39,7 +37,7 @@ type listval: int func newRenderState(): RenderState = - return RenderState() + return RenderState(blanklines: 1) proc write(state: var RenderState, s: string) = state.fmtline &= s @@ -69,20 +67,21 @@ proc addSpaces(buffer: Buffer, state: var RenderState, n: int) = state.write(' '.repeat(n)) state.x += n -proc writeWrappedText(buffer: Buffer, state: var RenderState, node: HtmlNode) = +proc writeWrappedText(buffer: Buffer, state: var RenderState, node: Node) = state.lastwidth = 0 var n = 0 var fmtword = "" var rawword = "" var prevl = false - for w in node.fmttext: + let fmttext = node.getFmtText() + for w in fmttext: if w.len > 0 and w[0] == '\e': fmtword &= w continue for r in w.runes: if r == Rune(' '): - if rawword[0] == ' ' and prevl: #first byte can't fool comparison to ascii + if rawword.len > 0 and rawword[0] == ' ' and prevl: #first byte can't fool comparison to ascii fmtword = fmtword.substr(1) rawword = rawword.substr(1) state.x -= 1 @@ -91,15 +90,21 @@ proc writeWrappedText(buffer: Buffer, state: var RenderState, node: HtmlNode) = fmtword = "" rawword = "" - fmtword &= r - rawword &= r + if r == Rune('\n'): + state.write(fmtword, rawword) + buffer.flushLine(state) + rawword = "" + fmtword = "" + else: + fmtword &= r + rawword &= r - state.x += mk_wcwidth_cjk(r) + state.x += r.width() if state.x >= buffer.width: state.lastwidth = max(state.lastwidth, state.x) buffer.flushLine(state) - state.x = mk_wcswidth_cjk(rawword) + state.x = rawword.width() prevl = true else: state.lastwidth = max(state.lastwidth, state.x) @@ -108,20 +113,21 @@ proc writeWrappedText(buffer: Buffer, state: var RenderState, node: HtmlNode) = state.write(fmtword, rawword) if prevl: - state.x += mk_wcswidth_cjk(rawword) + state.x += rawword.width() prevl = false state.lastwidth = max(state.lastwidth, state.x) -proc preAlignNode(buffer: Buffer, node: HtmlNode, state: var RenderState) = +proc preAlignNode(buffer: Buffer, node: Node, state: var RenderState) = let elem = node.nodeAttr() - if state.rawline.len > 0 and node.openblock and state.blanklines == 0: + if state.rawline.len > 0 and node.firstNode() and state.blanklines == 0: buffer.flushLine(state) - if node.openblock: - while state.blanklines < max(elem.margin, elem.margintop): + if node.firstNode(): + while state.blanklines < max(node.parentElement.margin, node.parentElement.margintop): buffer.flushLine(state) - state.indent += elem.indent + if elem.parentNode.nodeType == ELEMENT_NODE: + state.indent += elem.parentElement.indent if state.rawline.len > 0 and state.blanklines == 0 and node.displayed(): buffer.addSpaces(state, state.nextspaces) @@ -134,7 +140,8 @@ proc preAlignNode(buffer: Buffer, node: HtmlNode, state: var RenderState) = state.centerlen = 0 if node.isElemNode() and elem.display == DISPLAY_LIST_ITEM and state.indent > 0: - buffer.flushLine(state) + if state.blanklines == 0: + buffer.flushLine(state) var listchar = "" case elem.parentElement.tagType of TAG_UL: @@ -149,7 +156,7 @@ proc preAlignNode(buffer: Buffer, node: HtmlNode, state: var RenderState) = state.x += listchar.runeLen() buffer.addSpaces(state, 1) -proc postAlignNode(buffer: Buffer, node: HtmlNode, state: var RenderState) = +proc postAlignNode(buffer: Buffer, node: Node, state: var RenderState) = let elem = node.nodeAttr() if node.getRawLen() > 0: @@ -158,25 +165,32 @@ proc postAlignNode(buffer: Buffer, node: HtmlNode, state: var RenderState) = if state.rawline.len > 0 and state.blanklines == 0: state.nextspaces += max(elem.margin, elem.marginright) - if node.closeblock and (node.isTextNode() or elem.childNodes.len == 0): - buffer.flushLine(state) + #if node.lastNode() and (node.isTextNode() or elem.childNodes.len == 0): + # buffer.flushLine(state) - if node.closeblock: - while state.blanklines < max(elem.margin, elem.marginbottom): + if node.lastNode(): + while state.blanklines < max(node.parentElement.margin, node.parentElement.marginbottom): buffer.flushLine(state) - if node.isElemNode(): - state.indent -= elem.indent + if elem.parentElement != nil: + state.indent -= elem.parentElement.indent - if elem.tagType == TAG_BR and not node.openblock: + if elem.tagType == TAG_BR and not node.firstNode(): buffer.flushLine(state) -proc renderNode(buffer: Buffer, node: HtmlNode, state: var RenderState) = - if node.isDocument(): + if elem.display == DISPLAY_LIST_ITEM and node.lastNode(): + buffer.flushLine(state) + +proc renderNode(buffer: Buffer, node: Node, state: var RenderState) = + if not (node.nodeType in {ELEMENT_NODE, TEXT_NODE}): + return + if node.parentNode.nodeType != ELEMENT_NODE: return let elem = node.nodeAttr() + if elem.tagType in {TAG_SCRIPT, TAG_STYLE, TAG_NOSCRIPT}: + return if elem.tagType == TAG_TITLE: if node.isTextNode(): - buffer.title = node.rawtext + buffer.title = node.getRawText() return else: discard if elem.hidden: return @@ -184,7 +198,7 @@ proc renderNode(buffer: Buffer, node: HtmlNode, state: var RenderState) = if not state.docenter: if elem.centered: state.centerqueue.add(node) - if node.closeblock or elem.tagType == TAG_BR: + if node.lastNode() or elem.tagType == TAG_BR: state.docenter = true state.centerlen = 0 for node in state.centerqueue: @@ -218,54 +232,12 @@ proc renderNode(buffer: Buffer, node: HtmlNode, state: var RenderState) = buffer.postAlignNode(node, state) -iterator revItems*(n: XmlNode): XmlNode {.inline.} = - var i = n.len - 1 - while i >= 0: - if n[i].kind != xnComment: - yield n[i] - i -= 1 - -type - XmlHtmlNode* = ref XmlHtmlNodeObj - XmlHtmlNodeObj = object - xml*: XmlNode - html*: HtmlNode - proc setLastHtmlLine(buffer: Buffer, state: var RenderState) = if state.rawline.len != 0: buffer.flushLine(state) proc renderHtml*(buffer: Buffer) = - var stack: seq[XmlHtmlNode] - let first = XmlHtmlNode(xml: buffer.htmlSource, - html: getHtmlNode(buffer.htmlSource, buffer.document)) - stack.add(first) - - var state = newRenderState() - while stack.len > 0: - let currElem = stack.pop() - buffer.addNode(currElem.html) - buffer.renderNode(currElem.html, state) - if currElem.xml.len > 0: - var last = false - for item in currElem.xml.revItems: - let child = XmlHtmlNode(xml: item, - html: getHtmlNode(item, currElem.html)) - stack.add(child) - currElem.html.childNodes.add(child.html) - if not last and not child.html.hidden: - last = true - if HtmlElement(currElem.html).display == DISPLAY_BLOCK: - eprint "elem", HtmlElement(currElem.html).tagType, "close @", child.html.nodeAttr().tagType - stack[^1].html.closeblock = true - if last: - eprint "elem", HtmlElement(currElem.html).tagType, "open @", stack[^1].html.nodeAttr().tagType - if HtmlElement(currElem.html).display == DISPLAY_BLOCK: - stack[^1].html.openblock = true - buffer.setLastHtmlLine(state) - -proc nrenderHtml*(buffer: Buffer) = - var stack: seq[HtmlNode] + var stack: seq[Node] let first = buffer.document stack.add(first) @@ -372,7 +344,7 @@ proc inputLoop(attrs: TermAttributes, buffer: Buffer): bool = buffer.setLocation(parseUri(url)) return true of ACTION_LINE_INFO: - statusMsg("line " & $buffer.cursory & "/" & $buffer.lastLine() & " col " & $buffer.cursorx & "/" & $buffer.realCurrentLineLength(), buffer.width) + statusMsg("line " & $buffer.cursory & "/" & $buffer.lastLine() & " col " & $(buffer.cursorx + 1) & "/" & $buffer.currentLineLength(), buffer.width) nostatus = true of ACTION_FEED_NEXT: feedNext = true diff --git a/dom.nim b/dom.nim new file mode 100644 index 00000000..b018e0f2 --- /dev/null +++ b/dom.nim @@ -0,0 +1,346 @@ +import terminal +import uri +import unicode +import strutils +import tables + +import twtstr +import twtio +import enums + +type + EventTarget* = ref EventTargetObj + EventTargetObj = object of RootObj + + Node* = ref NodeObj + NodeObj = object of EventTargetObj + nodeType*: NodeType + childNodes*: seq[Node] + firstChild*: Node + isConnected*: bool + lastChild*: Node + nextSibling*: Node + previousSibling*: Node + parentNode*: Node + parentElement*: Element + ownerDocument*: Document + + rawtext*: string + fmttext*: seq[string] + x*: int + y*: int + ex*: int + ey*: int + width*: int + height*: int + hidden*: bool + + Attr* = ref AttrObj + AttrObj = object of NodeObj + namespaceURI*: string + prefix*: string + localName*: string + name*: string + value*: string + ownerElement*: Element + + Document* = ref DocumentObj + DocumentObj = object of NodeObj + location*: Uri + + CharacterData* = ref CharacterDataObj + CharacterDataObj = object of NodeObj + data*: string + length*: int + + Text* = ref TextObj + TextObj = object of CharacterDataObj + wholeText*: string + + Comment* = ref CommentObj + CommentObj = object of CharacterDataObj + + Element* = ref ElementObj + ElementObj = object of NodeObj + namespaceURI*: string + prefix*: string + localName*: string + tagName*: string + tagType*: TagType + + id*: string + classList*: seq[string] + attributes*: Table[string, Attr] + + margintop*: int + marginbottom*: int + marginleft*: int + marginright*: int + margin*: int + centered*: bool + display*: DisplayType + innerText*: string + bold*: bool + italic*: bool + underscore*: bool + islink*: bool + selected*: bool + numChildNodes*: int + indent*: int + + HTMLElement* = ref HTMLElementObj + HTMLElementObj = object of ElementObj + + HTMLInputElement* = ref HTMLInputElementObj + HTMLInputElementObj = object of HTMLElementObj + itype*: InputType + autofocus*: bool + required*: bool + value*: string + size*: int + + HTMLAnchorElement* = ref HTMLAnchorElementObj + HTMLAnchorElementObj = object of HTMLElementObj + href*: string + + HTMLSelectElement* = ref HTMLSelectElementObj + HTMLSelectElementObj = object of HTMLElementObj + name*: string + value*: string + valueSet*: bool + + HTMLOptionElement* = ref HTMLOptionElementObj + HTMLOptionElementObj = object of HTMLElementObj + value*: string + + HTMLHeadingElement* = ref HTMLHeadingElementObj + HTMLHeadingElementObj = object of HTMLElementObj + rank*: uint16 + + HTMLBRElement* = ref HTMLBRElementObj + HTMLBRElementObj = object of HTMLElementObj + + +func getTagTypeMap(): Table[string, TagType] = + for i in low(TagType) .. high(TagType): + let enumname = $TagType(i) + let tagname = enumname.split('_')[1..^1].join("_").tolower() + result[tagname] = TagType(i) + +func getInputTypeMap(): Table[string, InputType] = + for i in low(InputType) .. high(InputType): + let enumname = $InputType(i) + let tagname = enumname.split('_')[1..^1].join("_").tolower() + result[tagname] = InputType(i) + +const tagTypeMap = getTagTypeMap() +const inputTypeMap = getInputTypeMap() + +func tagType*(s: string): TagType = + if tagTypeMap.hasKey(s): + return tagTypeMap[s] + else: + return TAG_UNKNOWN + +func inputType*(s: string): InputType = + if inputTypeMap.hasKey(s): + return inputTypeMap[s] + else: + return INPUT_UNKNOWN + +#TODO +func nodeAttr*(node: Node): HtmlElement = + case node.nodeType + of TEXT_NODE: return HtmlElement(node.parentElement) + of ELEMENT_NODE: return HtmlElement(node) + else: assert(false) + +func displayed*(node: Node): bool = + return node.rawtext.len > 0 and node.nodeAttr().display != DISPLAY_NONE + +func isTextNode*(node: Node): bool = + return node.nodeType == TEXT_NODE + +func isElemNode*(node: Node): bool = + return node.nodeType == ELEMENT_NODE + +func isComment*(node: Node): bool = + return node.nodeType == COMMENT_NODE + +func isCData*(node: Node): bool = + return node.nodeType == CDATA_SECTION_NODE + +func isDocument*(node: Node): bool = + return node.nodeType == DOCUMENT_NODE + +func getFmtLen*(htmlNode: Node): int = + return htmlNode.fmttext.join().runeLen() + +func getRawLen*(htmlNode: Node): int = + return htmlNode.rawtext.runeLen() + +func firstNode*(htmlNode: Node): bool = + return htmlNode.parentElement != nil and htmlNode.parentElement.childNodes[0] == htmlNode + +func lastNode*(htmlNode: Node): bool = + return htmlNode.parentElement != nil and htmlNode.parentElement.childNodes[^1] == htmlNode + +func toInputType*(str: string): InputType = + case str + of "button": INPUT_BUTTON + of "checkbox": INPUT_CHECKBOX + of "color": INPUT_COLOR + of "date": INPUT_DATE + of "datetime_local": INPUT_DATETIME_LOCAL + of "email": INPUT_EMAIL + of "file": INPUT_FILE + of "hidden": INPUT_HIDDEN + of "image": INPUT_IMAGE + of "month": INPUT_MONTH + of "number": INPUT_NUMBER + of "password": INPUT_PASSWORD + of "radio": INPUT_RADIO + of "range": INPUT_RANGE + of "reset": INPUT_RESET + of "search": INPUT_SEARCH + of "submit": INPUT_SUBMIT + of "tel": INPUT_TEL + of "text": INPUT_TEXT + of "time": INPUT_TIME + of "url": INPUT_URL + of "week": INPUT_WEEK + else: INPUT_UNKNOWN + +func toInputSize*(str: string): int = + if str.len == 0: + return 20 + for c in str: + if not c.isDigit: + return 20 + return str.parseInt() + +func getFmtInput(inputElement: HtmlInputElement): seq[string] = + case inputElement.itype + of INPUT_TEXT, INPUT_SEARCH: + let valueFit = fitValueToSize(inputElement.value, inputElement.size) + return valueFit.ansiStyle(styleUnderscore).ansiReset().buttonFmt() + of INPUT_SUBMIT: + return inputElement.value.buttonFmt() + else: discard + +func getRawInput(inputElement: HtmlInputElement): string = + case inputElement.itype + of INPUT_TEXT, INPUT_SEARCH: + return inputElement.value.fitValueToSize(inputElement.size).buttonRaw() + of INPUT_SUBMIT: + return inputElement.value.buttonRaw() + else: discard + +#TODO +func ancestor*(htmlNode: Node, tagType: TagType): HtmlElement = + result = HtmlElement(htmlNode.parentElement) + while result != nil and result.tagType != tagType: + result = HtmlElement(result.parentElement) + +func displayWhitespace*(htmlElem: HtmlElement): bool = + return htmlElem.display == DISPLAY_INLINE or htmlElem.display == DISPLAY_INLINE_BLOCK + +proc getRawText*(htmlNode: Node): string = + if htmlNode.isElemNode(): + case HtmlElement(htmlNode).tagType + of TAG_INPUT: return HtmlInputElement(htmlNode).getRawInput() + else: return "" + elif htmlNode.isTextNode(): + let chardata = CharacterData(htmlNode) + if htmlNode.parentElement != nil and htmlNode.parentElement.tagType != TAG_PRE: + result = chardata.data.remove("\n") + if unicode.strip(result).runeLen() > 0: + if htmlNode.nodeAttr().display != DISPLAY_INLINE: + if htmlNode.previousSibling == nil or htmlNode.previousSibling.nodeAttr().displayWhitespace(): + result = unicode.strip(result, true, false) + if htmlNode.nextSibling == nil or htmlNode.nextSibling.nodeAttr().displayWhitespace(): + result = unicode.strip(result, false, true) + else: + result = "" + else: + result = unicode.strip(chardata.data) + if htmlNode.parentElement != nil and htmlNode.parentElement.tagType == TAG_OPTION: + result = result.buttonRaw() + else: + assert(false) + +func getFmtText*(htmlNode: Node): seq[string] = + if htmlNode.isElemNode(): + case HtmlElement(htmlNode).tagType + of TAG_INPUT: return HtmlInputElement(htmlNode).getFmtInput() + else: return @[] + elif htmlNode.isTextNode(): + let chardata = CharacterData(htmlNode) + result &= chardata.data + if htmlNode.parentElement != nil: + #TODO + if HtmlElement(htmlNode.parentElement).islink: + result = result.ansiFgColor(fgBlue).ansiReset() + let anchor = htmlNode.ancestor(TAG_A) + if anchor != nil and anchor.selected: + result = result.ansiStyle(styleUnderscore).ansiReset() + + if htmlNode.parentElement.tagType == TAG_OPTION: + result = result.ansiFgColor(fgRed).ansiReset() + + #TODO + if HtmlElement(htmlNode.parentElement).bold: + result = result.ansiStyle(styleBright).ansiReset() + if HtmlElement(htmlNode.parentElement).italic: + result = result.ansiStyle(styleItalic).ansiReset() + if HtmlElement(htmlNode.parentElement).underscore: + result = result.ansiStyle(styleUnderscore).ansiReset() + else: + assert(false, "Uhhhh I'm pretty sure we should have parent elements for text nodes?" & htmlNode.rawtext) + else: + assert(false) + +func newDocument*(): Document = + new(result) + result.nodeType = DOCUMENT_NODE + +func newText*(): Text = + new(result) + result.nodeType = TEXT_NODE + +func newComment*(): Comment = + new(result) + result.nodeType = COMMENT_NODE + +func newHtmlElement*(tagType: TagType): HTMLElement = + case tagType + of TAG_INPUT: + result = new(HTMLInputElement) + of TAG_A: + result = new(HTMLAnchorElement) + of TAG_SELECT: + result = new(HTMLSelectElement) + of TAG_OPTION: + result = new(HTMLOptionElement) + of TAG_H1, TAG_H2, TAG_H3, TAG_H4, TAG_H5, TAG_H6: + result = new(HTMLHeadingElement) + of TAG_BR: + result = new(HTMLBRElement) + else: + new(result) + + result.nodeType = ELEMENT_NODE + result.tagType = tagType + +func newAttr*(parent: Element, key: string, value: string): Attr = + new(result) + result.nodeType = ATTRIBUTE_NODE + result.ownerElement = parent + result.name = key + result.value = value + +func getAttrValue*(element: Element, s: string): string = + let attr = element.attributes.getOrDefault(s, nil) + if attr != nil: + return attr.value + return "" diff --git a/entity.json b/entity.json new file mode 100644 index 00000000..557170b4 --- /dev/null +++ b/entity.json @@ -0,0 +1,2233 @@ +{ + "Æ": { "codepoints": [198], "characters": "\u00C6" }, + "Æ": { "codepoints": [198], "characters": "\u00C6" }, + "&": { "codepoints": [38], "characters": "\u0026" }, + "&": { "codepoints": [38], "characters": "\u0026" }, + "Á": { "codepoints": [193], "characters": "\u00C1" }, + "Á": { "codepoints": [193], "characters": "\u00C1" }, + "Ă": { "codepoints": [258], "characters": "\u0102" }, + "Â": { "codepoints": [194], "characters": "\u00C2" }, + "Â": { "codepoints": [194], "characters": "\u00C2" }, + "А": { "codepoints": [1040], "characters": "\u0410" }, + "𝔄": { "codepoints": [120068], "characters": "\uD835\uDD04" }, + "À": { "codepoints": [192], "characters": "\u00C0" }, + "À": { "codepoints": [192], "characters": "\u00C0" }, + "Α": { "codepoints": [913], "characters": "\u0391" }, + "Ā": { "codepoints": [256], "characters": "\u0100" }, + "⩓": { "codepoints": [10835], "characters": "\u2A53" }, + "Ą": { "codepoints": [260], "characters": "\u0104" }, + "𝔸": { "codepoints": [120120], "characters": "\uD835\uDD38" }, + "⁡": { "codepoints": [8289], "characters": "\u2061" }, + "Å": { "codepoints": [197], "characters": "\u00C5" }, + "Å": { "codepoints": [197], "characters": "\u00C5" }, + "𝒜": { "codepoints": [119964], "characters": "\uD835\uDC9C" }, + "≔": { "codepoints": [8788], "characters": "\u2254" }, + "Ã": { "codepoints": [195], "characters": "\u00C3" }, + "Ã": { "codepoints": [195], "characters": "\u00C3" }, + "Ä": { "codepoints": [196], "characters": "\u00C4" }, + "Ä": { "codepoints": [196], "characters": "\u00C4" }, + "∖": { "codepoints": [8726], "characters": "\u2216" }, + "⫧": { "codepoints": [10983], "characters": "\u2AE7" }, + "⌆": { "codepoints": [8966], "characters": "\u2306" }, + "Б": { "codepoints": [1041], "characters": "\u0411" }, + "∵": { "codepoints": [8757], "characters": "\u2235" }, + "ℬ": { "codepoints": [8492], "characters": "\u212C" }, + "Β": { "codepoints": [914], "characters": "\u0392" }, + "𝔅": { "codepoints": [120069], "characters": "\uD835\uDD05" }, + "𝔹": { "codepoints": [120121], "characters": "\uD835\uDD39" }, + "˘": { "codepoints": [728], "characters": "\u02D8" }, + "ℬ": { "codepoints": [8492], "characters": "\u212C" }, + "≎": { "codepoints": [8782], "characters": "\u224E" }, + "Ч": { "codepoints": [1063], "characters": "\u0427" }, + "©": { "codepoints": [169], "characters": "\u00A9" }, + "©": { "codepoints": [169], "characters": "\u00A9" }, + "Ć": { "codepoints": [262], "characters": "\u0106" }, + "⋒": { "codepoints": [8914], "characters": "\u22D2" }, + "ⅅ": { "codepoints": [8517], "characters": "\u2145" }, + "ℭ": { "codepoints": [8493], "characters": "\u212D" }, + "Č": { "codepoints": [268], "characters": "\u010C" }, + "Ç": { "codepoints": [199], "characters": "\u00C7" }, + "Ç": { "codepoints": [199], "characters": "\u00C7" }, + "Ĉ": { "codepoints": [264], "characters": "\u0108" }, + "∰": { "codepoints": [8752], "characters": "\u2230" }, + "Ċ": { "codepoints": [266], "characters": "\u010A" }, + "¸": { "codepoints": [184], "characters": "\u00B8" }, + "·": { "codepoints": [183], "characters": "\u00B7" }, + "ℭ": { "codepoints": [8493], "characters": "\u212D" }, + "Χ": { "codepoints": [935], "characters": "\u03A7" }, + "⊙": { "codepoints": [8857], "characters": "\u2299" }, + "⊖": { "codepoints": [8854], "characters": "\u2296" }, + "⊕": { "codepoints": [8853], "characters": "\u2295" }, + "⊗": { "codepoints": [8855], "characters": "\u2297" }, + "∲": { "codepoints": [8754], "characters": "\u2232" }, + "”": { "codepoints": [8221], "characters": "\u201D" }, + "’": { "codepoints": [8217], "characters": "\u2019" }, + "∷": { "codepoints": [8759], "characters": "\u2237" }, + "⩴": { "codepoints": [10868], "characters": "\u2A74" }, + "≡": { "codepoints": [8801], "characters": "\u2261" }, + "∯": { "codepoints": [8751], "characters": "\u222F" }, + "∮": { "codepoints": [8750], "characters": "\u222E" }, + "ℂ": { "codepoints": [8450], "characters": "\u2102" }, + "∐": { "codepoints": [8720], "characters": "\u2210" }, + "∳": { "codepoints": [8755], "characters": "\u2233" }, + "⨯": { "codepoints": [10799], "characters": "\u2A2F" }, + "𝒞": { "codepoints": [119966], "characters": "\uD835\uDC9E" }, + "⋓": { "codepoints": [8915], "characters": "\u22D3" }, + "≍": { "codepoints": [8781], "characters": "\u224D" }, + "ⅅ": { "codepoints": [8517], "characters": "\u2145" }, + "⤑": { "codepoints": [10513], "characters": "\u2911" }, + "Ђ": { "codepoints": [1026], "characters": "\u0402" }, + "Ѕ": { "codepoints": [1029], "characters": "\u0405" }, + "Џ": { "codepoints": [1039], "characters": "\u040F" }, + "‡": { "codepoints": [8225], "characters": "\u2021" }, + "↡": { "codepoints": [8609], "characters": "\u21A1" }, + "⫤": { "codepoints": [10980], "characters": "\u2AE4" }, + "Ď": { "codepoints": [270], "characters": "\u010E" }, + "Д": { "codepoints": [1044], "characters": "\u0414" }, + "∇": { "codepoints": [8711], "characters": "\u2207" }, + "Δ": { "codepoints": [916], "characters": "\u0394" }, + "𝔇": { "codepoints": [120071], "characters": "\uD835\uDD07" }, + "´": { "codepoints": [180], "characters": "\u00B4" }, + "˙": { "codepoints": [729], "characters": "\u02D9" }, + "˝": { "codepoints": [733], "characters": "\u02DD" }, + "`": { "codepoints": [96], "characters": "\u0060" }, + "˜": { "codepoints": [732], "characters": "\u02DC" }, + "⋄": { "codepoints": [8900], "characters": "\u22C4" }, + "ⅆ": { "codepoints": [8518], "characters": "\u2146" }, + "𝔻": { "codepoints": [120123], "characters": "\uD835\uDD3B" }, + "¨": { "codepoints": [168], "characters": "\u00A8" }, + "⃜": { "codepoints": [8412], "characters": "\u20DC" }, + "≐": { "codepoints": [8784], "characters": "\u2250" }, + "∯": { "codepoints": [8751], "characters": "\u222F" }, + "¨": { "codepoints": [168], "characters": "\u00A8" }, + "⇓": { "codepoints": [8659], "characters": "\u21D3" }, + "⇐": { "codepoints": [8656], "characters": "\u21D0" }, + "⇔": { "codepoints": [8660], "characters": "\u21D4" }, + "⫤": { "codepoints": [10980], "characters": "\u2AE4" }, + "⟸": { "codepoints": [10232], "characters": "\u27F8" }, + "⟺": { "codepoints": [10234], "characters": "\u27FA" }, + "⟹": { "codepoints": [10233], "characters": "\u27F9" }, + "⇒": { "codepoints": [8658], "characters": "\u21D2" }, + "⊨": { "codepoints": [8872], "characters": "\u22A8" }, + "⇑": { "codepoints": [8657], "characters": "\u21D1" }, + "⇕": { "codepoints": [8661], "characters": "\u21D5" }, + "∥": { "codepoints": [8741], "characters": "\u2225" }, + "↓": { "codepoints": [8595], "characters": "\u2193" }, + "⤓": { "codepoints": [10515], "characters": "\u2913" }, + "⇵": { "codepoints": [8693], "characters": "\u21F5" }, + "̑": { "codepoints": [785], "characters": "\u0311" }, + "⥐": { "codepoints": [10576], "characters": "\u2950" }, + "⥞": { "codepoints": [10590], "characters": "\u295E" }, + "↽": { "codepoints": [8637], "characters": "\u21BD" }, + "⥖": { "codepoints": [10582], "characters": "\u2956" }, + "⥟": { "codepoints": [10591], "characters": "\u295F" }, + "⇁": { "codepoints": [8641], "characters": "\u21C1" }, + "⥗": { "codepoints": [10583], "characters": "\u2957" }, + "⊤": { "codepoints": [8868], "characters": "\u22A4" }, + "↧": { "codepoints": [8615], "characters": "\u21A7" }, + "⇓": { "codepoints": [8659], "characters": "\u21D3" }, + "𝒟": { "codepoints": [119967], "characters": "\uD835\uDC9F" }, + "Đ": { "codepoints": [272], "characters": "\u0110" }, + "Ŋ": { "codepoints": [330], "characters": "\u014A" }, + "Ð": { "codepoints": [208], "characters": "\u00D0" }, + "Ð": { "codepoints": [208], "characters": "\u00D0" }, + "É": { "codepoints": [201], "characters": "\u00C9" }, + "É": { "codepoints": [201], "characters": "\u00C9" }, + "Ě": { "codepoints": [282], "characters": "\u011A" }, + "Ê": { "codepoints": [202], "characters": "\u00CA" }, + "Ê": { "codepoints": [202], "characters": "\u00CA" }, + "Э": { "codepoints": [1069], "characters": "\u042D" }, + "Ė": { "codepoints": [278], "characters": "\u0116" }, + "𝔈": { "codepoints": [120072], "characters": "\uD835\uDD08" }, + "È": { "codepoints": [200], "characters": "\u00C8" }, + "È": { "codepoints": [200], "characters": "\u00C8" }, + "∈": { "codepoints": [8712], "characters": "\u2208" }, + "Ē": { "codepoints": [274], "characters": "\u0112" }, + "◻": { "codepoints": [9723], "characters": "\u25FB" }, + "▫": { "codepoints": [9643], "characters": "\u25AB" }, + "Ę": { "codepoints": [280], "characters": "\u0118" }, + "𝔼": { "codepoints": [120124], "characters": "\uD835\uDD3C" }, + "Ε": { "codepoints": [917], "characters": "\u0395" }, + "⩵": { "codepoints": [10869], "characters": "\u2A75" }, + "≂": { "codepoints": [8770], "characters": "\u2242" }, + "⇌": { "codepoints": [8652], "characters": "\u21CC" }, + "ℰ": { "codepoints": [8496], "characters": "\u2130" }, + "⩳": { "codepoints": [10867], "characters": "\u2A73" }, + "Η": { "codepoints": [919], "characters": "\u0397" }, + "Ë": { "codepoints": [203], "characters": "\u00CB" }, + "Ë": { "codepoints": [203], "characters": "\u00CB" }, + "∃": { "codepoints": [8707], "characters": "\u2203" }, + "ⅇ": { "codepoints": [8519], "characters": "\u2147" }, + "Ф": { "codepoints": [1060], "characters": "\u0424" }, + "𝔉": { "codepoints": [120073], "characters": "\uD835\uDD09" }, + "◼": { "codepoints": [9724], "characters": "\u25FC" }, + "▪": { "codepoints": [9642], "characters": "\u25AA" }, + "𝔽": { "codepoints": [120125], "characters": "\uD835\uDD3D" }, + "∀": { "codepoints": [8704], "characters": "\u2200" }, + "ℱ": { "codepoints": [8497], "characters": "\u2131" }, + "ℱ": { "codepoints": [8497], "characters": "\u2131" }, + "Ѓ": { "codepoints": [1027], "characters": "\u0403" }, + ">": { "codepoints": [62], "characters": "\u003E" }, + ">": { "codepoints": [62], "characters": "\u003E" }, + "Γ": { "codepoints": [915], "characters": "\u0393" }, + "Ϝ": { "codepoints": [988], "characters": "\u03DC" }, + "Ğ": { "codepoints": [286], "characters": "\u011E" }, + "Ģ": { "codepoints": [290], "characters": "\u0122" }, + "Ĝ": { "codepoints": [284], "characters": "\u011C" }, + "Г": { "codepoints": [1043], "characters": "\u0413" }, + "Ġ": { "codepoints": [288], "characters": "\u0120" }, + "𝔊": { "codepoints": [120074], "characters": "\uD835\uDD0A" }, + "⋙": { "codepoints": [8921], "characters": "\u22D9" }, + "𝔾": { "codepoints": [120126], "characters": "\uD835\uDD3E" }, + "≥": { "codepoints": [8805], "characters": "\u2265" }, + "⋛": { "codepoints": [8923], "characters": "\u22DB" }, + "≧": { "codepoints": [8807], "characters": "\u2267" }, + "⪢": { "codepoints": [10914], "characters": "\u2AA2" }, + "≷": { "codepoints": [8823], "characters": "\u2277" }, + "⩾": { "codepoints": [10878], "characters": "\u2A7E" }, + "≳": { "codepoints": [8819], "characters": "\u2273" }, + "𝒢": { "codepoints": [119970], "characters": "\uD835\uDCA2" }, + "≫": { "codepoints": [8811], "characters": "\u226B" }, + "Ъ": { "codepoints": [1066], "characters": "\u042A" }, + "ˇ": { "codepoints": [711], "characters": "\u02C7" }, + "^": { "codepoints": [94], "characters": "\u005E" }, + "Ĥ": { "codepoints": [292], "characters": "\u0124" }, + "ℌ": { "codepoints": [8460], "characters": "\u210C" }, + "ℋ": { "codepoints": [8459], "characters": "\u210B" }, + "ℍ": { "codepoints": [8461], "characters": "\u210D" }, + "─": { "codepoints": [9472], "characters": "\u2500" }, + "ℋ": { "codepoints": [8459], "characters": "\u210B" }, + "Ħ": { "codepoints": [294], "characters": "\u0126" }, + "≎": { "codepoints": [8782], "characters": "\u224E" }, + "≏": { "codepoints": [8783], "characters": "\u224F" }, + "Е": { "codepoints": [1045], "characters": "\u0415" }, + "IJ": { "codepoints": [306], "characters": "\u0132" }, + "Ё": { "codepoints": [1025], "characters": "\u0401" }, + "Í": { "codepoints": [205], "characters": "\u00CD" }, + "Í": { "codepoints": [205], "characters": "\u00CD" }, + "Î": { "codepoints": [206], "characters": "\u00CE" }, + "Î": { "codepoints": [206], "characters": "\u00CE" }, + "И": { "codepoints": [1048], "characters": "\u0418" }, + "İ": { "codepoints": [304], "characters": "\u0130" }, + "ℑ": { "codepoints": [8465], "characters": "\u2111" }, + "Ì": { "codepoints": [204], "characters": "\u00CC" }, + "Ì": { "codepoints": [204], "characters": "\u00CC" }, + "ℑ": { "codepoints": [8465], "characters": "\u2111" }, + "Ī": { "codepoints": [298], "characters": "\u012A" }, + "ⅈ": { "codepoints": [8520], "characters": "\u2148" }, + "⇒": { "codepoints": [8658], "characters": "\u21D2" }, + "∬": { "codepoints": [8748], "characters": "\u222C" }, + "∫": { "codepoints": [8747], "characters": "\u222B" }, + "⋂": { "codepoints": [8898], "characters": "\u22C2" }, + "⁣": { "codepoints": [8291], "characters": "\u2063" }, + "⁢": { "codepoints": [8290], "characters": "\u2062" }, + "Į": { "codepoints": [302], "characters": "\u012E" }, + "𝕀": { "codepoints": [120128], "characters": "\uD835\uDD40" }, + "Ι": { "codepoints": [921], "characters": "\u0399" }, + "ℐ": { "codepoints": [8464], "characters": "\u2110" }, + "Ĩ": { "codepoints": [296], "characters": "\u0128" }, + "І": { "codepoints": [1030], "characters": "\u0406" }, + "Ï": { "codepoints": [207], "characters": "\u00CF" }, + "Ï": { "codepoints": [207], "characters": "\u00CF" }, + "Ĵ": { "codepoints": [308], "characters": "\u0134" }, + "Й": { "codepoints": [1049], "characters": "\u0419" }, + "𝔍": { "codepoints": [120077], "characters": "\uD835\uDD0D" }, + "𝕁": { "codepoints": [120129], "characters": "\uD835\uDD41" }, + "𝒥": { "codepoints": [119973], "characters": "\uD835\uDCA5" }, + "Ј": { "codepoints": [1032], "characters": "\u0408" }, + "Є": { "codepoints": [1028], "characters": "\u0404" }, + "Х": { "codepoints": [1061], "characters": "\u0425" }, + "Ќ": { "codepoints": [1036], "characters": "\u040C" }, + "Κ": { "codepoints": [922], "characters": "\u039A" }, + "Ķ": { "codepoints": [310], "characters": "\u0136" }, + "К": { "codepoints": [1050], "characters": "\u041A" }, + "𝔎": { "codepoints": [120078], "characters": "\uD835\uDD0E" }, + "𝕂": { "codepoints": [120130], "characters": "\uD835\uDD42" }, + "𝒦": { "codepoints": [119974], "characters": "\uD835\uDCA6" }, + "Љ": { "codepoints": [1033], "characters": "\u0409" }, + "<": { "codepoints": [60], "characters": "\u003C" }, + "<": { "codepoints": [60], "characters": "\u003C" }, + "Ĺ": { "codepoints": [313], "characters": "\u0139" }, + "Λ": { "codepoints": [923], "characters": "\u039B" }, + "⟪": { "codepoints": [10218], "characters": "\u27EA" }, + "ℒ": { "codepoints": [8466], "characters": "\u2112" }, + "↞": { "codepoints": [8606], "characters": "\u219E" }, + "Ľ": { "codepoints": [317], "characters": "\u013D" }, + "Ļ": { "codepoints": [315], "characters": "\u013B" }, + "Л": { "codepoints": [1051], "characters": "\u041B" }, + "⟨": { "codepoints": [10216], "characters": "\u27E8" }, + "←": { "codepoints": [8592], "characters": "\u2190" }, + "⇤": { "codepoints": [8676], "characters": "\u21E4" }, + "⇆": { "codepoints": [8646], "characters": "\u21C6" }, + "⌈": { "codepoints": [8968], "characters": "\u2308" }, + "⟦": { "codepoints": [10214], "characters": "\u27E6" }, + "⥡": { "codepoints": [10593], "characters": "\u2961" }, + "⇃": { "codepoints": [8643], "characters": "\u21C3" }, + "⥙": { "codepoints": [10585], "characters": "\u2959" }, + "⌊": { "codepoints": [8970], "characters": "\u230A" }, + "↔": { "codepoints": [8596], "characters": "\u2194" }, + "⥎": { "codepoints": [10574], "characters": "\u294E" }, + "⊣": { "codepoints": [8867], "characters": "\u22A3" }, + "↤": { "codepoints": [8612], "characters": "\u21A4" }, + "⥚": { "codepoints": [10586], "characters": "\u295A" }, + "⊲": { "codepoints": [8882], "characters": "\u22B2" }, + "⧏": { "codepoints": [10703], "characters": "\u29CF" }, + "⊴": { "codepoints": [8884], "characters": "\u22B4" }, + "⥑": { "codepoints": [10577], "characters": "\u2951" }, + "⥠": { "codepoints": [10592], "characters": "\u2960" }, + "↿": { "codepoints": [8639], "characters": "\u21BF" }, + "⥘": { "codepoints": [10584], "characters": "\u2958" }, + "↼": { "codepoints": [8636], "characters": "\u21BC" }, + "⥒": { "codepoints": [10578], "characters": "\u2952" }, + "⇐": { "codepoints": [8656], "characters": "\u21D0" }, + "⇔": { "codepoints": [8660], "characters": "\u21D4" }, + "⋚": { "codepoints": [8922], "characters": "\u22DA" }, + "≦": { "codepoints": [8806], "characters": "\u2266" }, + "≶": { "codepoints": [8822], "characters": "\u2276" }, + "⪡": { "codepoints": [10913], "characters": "\u2AA1" }, + "⩽": { "codepoints": [10877], "characters": "\u2A7D" }, + "≲": { "codepoints": [8818], "characters": "\u2272" }, + "𝔏": { "codepoints": [120079], "characters": "\uD835\uDD0F" }, + "⋘": { "codepoints": [8920], "characters": "\u22D8" }, + "⇚": { "codepoints": [8666], "characters": "\u21DA" }, + "Ŀ": { "codepoints": [319], "characters": "\u013F" }, + "⟵": { "codepoints": [10229], "characters": "\u27F5" }, + "⟷": { "codepoints": [10231], "characters": "\u27F7" }, + "⟶": { "codepoints": [10230], "characters": "\u27F6" }, + "⟸": { "codepoints": [10232], "characters": "\u27F8" }, + "⟺": { "codepoints": [10234], "characters": "\u27FA" }, + "⟹": { "codepoints": [10233], "characters": "\u27F9" }, + "𝕃": { "codepoints": [120131], "characters": "\uD835\uDD43" }, + "↙": { "codepoints": [8601], "characters": "\u2199" }, + "↘": { "codepoints": [8600], "characters": "\u2198" }, + "ℒ": { "codepoints": [8466], "characters": "\u2112" }, + "↰": { "codepoints": [8624], "characters": "\u21B0" }, + "Ł": { "codepoints": [321], "characters": "\u0141" }, + "≪": { "codepoints": [8810], "characters": "\u226A" }, + "⤅": { "codepoints": [10501], "characters": "\u2905" }, + "М": { "codepoints": [1052], "characters": "\u041C" }, + " ": { "codepoints": [8287], "characters": "\u205F" }, + "ℳ": { "codepoints": [8499], "characters": "\u2133" }, + "𝔐": { "codepoints": [120080], "characters": "\uD835\uDD10" }, + "∓": { "codepoints": [8723], "characters": "\u2213" }, + "𝕄": { "codepoints": [120132], "characters": "\uD835\uDD44" }, + "ℳ": { "codepoints": [8499], "characters": "\u2133" }, + "Μ": { "codepoints": [924], "characters": "\u039C" }, + "Њ": { "codepoints": [1034], "characters": "\u040A" }, + "Ń": { "codepoints": [323], "characters": "\u0143" }, + "Ň": { "codepoints": [327], "characters": "\u0147" }, + "Ņ": { "codepoints": [325], "characters": "\u0145" }, + "Н": { "codepoints": [1053], "characters": "\u041D" }, + "​": { "codepoints": [8203], "characters": "\u200B" }, + "​": { "codepoints": [8203], "characters": "\u200B" }, + "​": { "codepoints": [8203], "characters": "\u200B" }, + "​": { "codepoints": [8203], "characters": "\u200B" }, + "≫": { "codepoints": [8811], "characters": "\u226B" }, + "≪": { "codepoints": [8810], "characters": "\u226A" }, + "
": { "codepoints": [10], "characters": "\u000A" }, + "𝔑": { "codepoints": [120081], "characters": "\uD835\uDD11" }, + "⁠": { "codepoints": [8288], "characters": "\u2060" }, + " ": { "codepoints": [160], "characters": "\u00A0" }, + "ℕ": { "codepoints": [8469], "characters": "\u2115" }, + "⫬": { "codepoints": [10988], "characters": "\u2AEC" }, + "≢": { "codepoints": [8802], "characters": "\u2262" }, + "≭": { "codepoints": [8813], "characters": "\u226D" }, + "∦": { "codepoints": [8742], "characters": "\u2226" }, + "∉": { "codepoints": [8713], "characters": "\u2209" }, + "≠": { "codepoints": [8800], "characters": "\u2260" }, + "≂̸": { "codepoints": [8770, 824], "characters": "\u2242\u0338" }, + "∄": { "codepoints": [8708], "characters": "\u2204" }, + "≯": { "codepoints": [8815], "characters": "\u226F" }, + "≱": { "codepoints": [8817], "characters": "\u2271" }, + "≧̸": { "codepoints": [8807, 824], "characters": "\u2267\u0338" }, + "≫̸": { "codepoints": [8811, 824], "characters": "\u226B\u0338" }, + "≹": { "codepoints": [8825], "characters": "\u2279" }, + "⩾̸": { "codepoints": [10878, 824], "characters": "\u2A7E\u0338" }, + "≵": { "codepoints": [8821], "characters": "\u2275" }, + "≎̸": { "codepoints": [8782, 824], "characters": "\u224E\u0338" }, + "≏̸": { "codepoints": [8783, 824], "characters": "\u224F\u0338" }, + "⋪": { "codepoints": [8938], "characters": "\u22EA" }, + "⧏̸": { "codepoints": [10703, 824], "characters": "\u29CF\u0338" }, + "⋬": { "codepoints": [8940], "characters": "\u22EC" }, + "≮": { "codepoints": [8814], "characters": "\u226E" }, + "≰": { "codepoints": [8816], "characters": "\u2270" }, + "≸": { "codepoints": [8824], "characters": "\u2278" }, + "≪̸": { "codepoints": [8810, 824], "characters": "\u226A\u0338" }, + "⩽̸": { "codepoints": [10877, 824], "characters": "\u2A7D\u0338" }, + "≴": { "codepoints": [8820], "characters": "\u2274" }, + "⪢̸": { "codepoints": [10914, 824], "characters": "\u2AA2\u0338" }, + "⪡̸": { "codepoints": [10913, 824], "characters": "\u2AA1\u0338" }, + "⊀": { "codepoints": [8832], "characters": "\u2280" }, + "⪯̸": { "codepoints": [10927, 824], "characters": "\u2AAF\u0338" }, + "⋠": { "codepoints": [8928], "characters": "\u22E0" }, + "∌": { "codepoints": [8716], "characters": "\u220C" }, + "⋫": { "codepoints": [8939], "characters": "\u22EB" }, + "⧐̸": { "codepoints": [10704, 824], "characters": "\u29D0\u0338" }, + "⋭": { "codepoints": [8941], "characters": "\u22ED" }, + "⊏̸": { "codepoints": [8847, 824], "characters": "\u228F\u0338" }, + "⋢": { "codepoints": [8930], "characters": "\u22E2" }, + "⊐̸": { "codepoints": [8848, 824], "characters": "\u2290\u0338" }, + "⋣": { "codepoints": [8931], "characters": "\u22E3" }, + "⊂⃒": { "codepoints": [8834, 8402], "characters": "\u2282\u20D2" }, + "⊈": { "codepoints": [8840], "characters": "\u2288" }, + "⊁": { "codepoints": [8833], "characters": "\u2281" }, + "⪰̸": { "codepoints": [10928, 824], "characters": "\u2AB0\u0338" }, + "⋡": { "codepoints": [8929], "characters": "\u22E1" }, + "≿̸": { "codepoints": [8831, 824], "characters": "\u227F\u0338" }, + "⊃⃒": { "codepoints": [8835, 8402], "characters": "\u2283\u20D2" }, + "⊉": { "codepoints": [8841], "characters": "\u2289" }, + "≁": { "codepoints": [8769], "characters": "\u2241" }, + "≄": { "codepoints": [8772], "characters": "\u2244" }, + "≇": { "codepoints": [8775], "characters": "\u2247" }, + "≉": { "codepoints": [8777], "characters": "\u2249" }, + "∤": { "codepoints": [8740], "characters": "\u2224" }, + "𝒩": { "codepoints": [119977], "characters": "\uD835\uDCA9" }, + "Ñ": { "codepoints": [209], "characters": "\u00D1" }, + "Ñ": { "codepoints": [209], "characters": "\u00D1" }, + "Ν": { "codepoints": [925], "characters": "\u039D" }, + "Œ": { "codepoints": [338], "characters": "\u0152" }, + "Ó": { "codepoints": [211], "characters": "\u00D3" }, + "Ó": { "codepoints": [211], "characters": "\u00D3" }, + "Ô": { "codepoints": [212], "characters": "\u00D4" }, + "Ô": { "codepoints": [212], "characters": "\u00D4" }, + "О": { "codepoints": [1054], "characters": "\u041E" }, + "Ő": { "codepoints": [336], "characters": "\u0150" }, + "𝔒": { "codepoints": [120082], "characters": "\uD835\uDD12" }, + "Ò": { "codepoints": [210], "characters": "\u00D2" }, + "Ò": { "codepoints": [210], "characters": "\u00D2" }, + "Ō": { "codepoints": [332], "characters": "\u014C" }, + "Ω": { "codepoints": [937], "characters": "\u03A9" }, + "Ο": { "codepoints": [927], "characters": "\u039F" }, + "𝕆": { "codepoints": [120134], "characters": "\uD835\uDD46" }, + "“": { "codepoints": [8220], "characters": "\u201C" }, + "‘": { "codepoints": [8216], "characters": "\u2018" }, + "⩔": { "codepoints": [10836], "characters": "\u2A54" }, + "𝒪": { "codepoints": [119978], "characters": "\uD835\uDCAA" }, + "Ø": { "codepoints": [216], "characters": "\u00D8" }, + "Ø": { "codepoints": [216], "characters": "\u00D8" }, + "Õ": { "codepoints": [213], "characters": "\u00D5" }, + "Õ": { "codepoints": [213], "characters": "\u00D5" }, + "⨷": { "codepoints": [10807], "characters": "\u2A37" }, + "Ö": { "codepoints": [214], "characters": "\u00D6" }, + "Ö": { "codepoints": [214], "characters": "\u00D6" }, + "‾": { "codepoints": [8254], "characters": "\u203E" }, + "⏞": { "codepoints": [9182], "characters": "\u23DE" }, + "⎴": { "codepoints": [9140], "characters": "\u23B4" }, + "⏜": { "codepoints": [9180], "characters": "\u23DC" }, + "∂": { "codepoints": [8706], "characters": "\u2202" }, + "П": { "codepoints": [1055], "characters": "\u041F" }, + "𝔓": { "codepoints": [120083], "characters": "\uD835\uDD13" }, + "Φ": { "codepoints": [934], "characters": "\u03A6" }, + "Π": { "codepoints": [928], "characters": "\u03A0" }, + "±": { "codepoints": [177], "characters": "\u00B1" }, + "ℌ": { "codepoints": [8460], "characters": "\u210C" }, + "ℙ": { "codepoints": [8473], "characters": "\u2119" }, + "⪻": { "codepoints": [10939], "characters": "\u2ABB" }, + "≺": { "codepoints": [8826], "characters": "\u227A" }, + "⪯": { "codepoints": [10927], "characters": "\u2AAF" }, + "≼": { "codepoints": [8828], "characters": "\u227C" }, + "≾": { "codepoints": [8830], "characters": "\u227E" }, + "″": { "codepoints": [8243], "characters": "\u2033" }, + "∏": { "codepoints": [8719], "characters": "\u220F" }, + "∷": { "codepoints": [8759], "characters": "\u2237" }, + "∝": { "codepoints": [8733], "characters": "\u221D" }, + "𝒫": { "codepoints": [119979], "characters": "\uD835\uDCAB" }, + "Ψ": { "codepoints": [936], "characters": "\u03A8" }, + """: { "codepoints": [34], "characters": "\u0022" }, + """: { "codepoints": [34], "characters": "\u0022" }, + "𝔔": { "codepoints": [120084], "characters": "\uD835\uDD14" }, + "ℚ": { "codepoints": [8474], "characters": "\u211A" }, + "𝒬": { "codepoints": [119980], "characters": "\uD835\uDCAC" }, + "⤐": { "codepoints": [10512], "characters": "\u2910" }, + "®": { "codepoints": [174], "characters": "\u00AE" }, + "®": { "codepoints": [174], "characters": "\u00AE" }, + "Ŕ": { "codepoints": [340], "characters": "\u0154" }, + "⟫": { "codepoints": [10219], "characters": "\u27EB" }, + "↠": { "codepoints": [8608], "characters": "\u21A0" }, + "⤖": { "codepoints": [10518], "characters": "\u2916" }, + "Ř": { "codepoints": [344], "characters": "\u0158" }, + "Ŗ": { "codepoints": [342], "characters": "\u0156" }, + "Р": { "codepoints": [1056], "characters": "\u0420" }, + "ℜ": { "codepoints": [8476], "characters": "\u211C" }, + "∋": { "codepoints": [8715], "characters": "\u220B" }, + "⇋": { "codepoints": [8651], "characters": "\u21CB" }, + "⥯": { "codepoints": [10607], "characters": "\u296F" }, + "ℜ": { "codepoints": [8476], "characters": "\u211C" }, + "Ρ": { "codepoints": [929], "characters": "\u03A1" }, + "⟩": { "codepoints": [10217], "characters": "\u27E9" }, + "→": { "codepoints": [8594], "characters": "\u2192" }, + "⇥": { "codepoints": [8677], "characters": "\u21E5" }, + "⇄": { "codepoints": [8644], "characters": "\u21C4" }, + "⌉": { "codepoints": [8969], "characters": "\u2309" }, + "⟧": { "codepoints": [10215], "characters": "\u27E7" }, + "⥝": { "codepoints": [10589], "characters": "\u295D" }, + "⇂": { "codepoints": [8642], "characters": "\u21C2" }, + "⥕": { "codepoints": [10581], "characters": "\u2955" }, + "⌋": { "codepoints": [8971], "characters": "\u230B" }, + "⊢": { "codepoints": [8866], "characters": "\u22A2" }, + "↦": { "codepoints": [8614], "characters": "\u21A6" }, + "⥛": { "codepoints": [10587], "characters": "\u295B" }, + "⊳": { "codepoints": [8883], "characters": "\u22B3" }, + "⧐": { "codepoints": [10704], "characters": "\u29D0" }, + "⊵": { "codepoints": [8885], "characters": "\u22B5" }, + "⥏": { "codepoints": [10575], "characters": "\u294F" }, + "⥜": { "codepoints": [10588], "characters": "\u295C" }, + "↾": { "codepoints": [8638], "characters": "\u21BE" }, + "⥔": { "codepoints": [10580], "characters": "\u2954" }, + "⇀": { "codepoints": [8640], "characters": "\u21C0" }, + "⥓": { "codepoints": [10579], "characters": "\u2953" }, + "⇒": { "codepoints": [8658], "characters": "\u21D2" }, + "ℝ": { "codepoints": [8477], "characters": "\u211D" }, + "⥰": { "codepoints": [10608], "characters": "\u2970" }, + "⇛": { "codepoints": [8667], "characters": "\u21DB" }, + "ℛ": { "codepoints": [8475], "characters": "\u211B" }, + "↱": { "codepoints": [8625], "characters": "\u21B1" }, + "⧴": { "codepoints": [10740], "characters": "\u29F4" }, + "Щ": { "codepoints": [1065], "characters": "\u0429" }, + "Ш": { "codepoints": [1064], "characters": "\u0428" }, + "Ь": { "codepoints": [1068], "characters": "\u042C" }, + "Ś": { "codepoints": [346], "characters": "\u015A" }, + "⪼": { "codepoints": [10940], "characters": "\u2ABC" }, + "Š": { "codepoints": [352], "characters": "\u0160" }, + "Ş": { "codepoints": [350], "characters": "\u015E" }, + "Ŝ": { "codepoints": [348], "characters": "\u015C" }, + "С": { "codepoints": [1057], "characters": "\u0421" }, + "𝔖": { "codepoints": [120086], "characters": "\uD835\uDD16" }, + "↓": { "codepoints": [8595], "characters": "\u2193" }, + "←": { "codepoints": [8592], "characters": "\u2190" }, + "→": { "codepoints": [8594], "characters": "\u2192" }, + "↑": { "codepoints": [8593], "characters": "\u2191" }, + "Σ": { "codepoints": [931], "characters": "\u03A3" }, + "∘": { "codepoints": [8728], "characters": "\u2218" }, + "𝕊": { "codepoints": [120138], "characters": "\uD835\uDD4A" }, + "√": { "codepoints": [8730], "characters": "\u221A" }, + "□": { "codepoints": [9633], "characters": "\u25A1" }, + "⊓": { "codepoints": [8851], "characters": "\u2293" }, + "⊏": { "codepoints": [8847], "characters": "\u228F" }, + "⊑": { "codepoints": [8849], "characters": "\u2291" }, + "⊐": { "codepoints": [8848], "characters": "\u2290" }, + "⊒": { "codepoints": [8850], "characters": "\u2292" }, + "⊔": { "codepoints": [8852], "characters": "\u2294" }, + "𝒮": { "codepoints": [119982], "characters": "\uD835\uDCAE" }, + "⋆": { "codepoints": [8902], "characters": "\u22C6" }, + "⋐": { "codepoints": [8912], "characters": "\u22D0" }, + "⋐": { "codepoints": [8912], "characters": "\u22D0" }, + "⊆": { "codepoints": [8838], "characters": "\u2286" }, + "≻": { "codepoints": [8827], "characters": "\u227B" }, + "⪰": { "codepoints": [10928], "characters": "\u2AB0" }, + "≽": { "codepoints": [8829], "characters": "\u227D" }, + "≿": { "codepoints": [8831], "characters": "\u227F" }, + "∋": { "codepoints": [8715], "characters": "\u220B" }, + "∑": { "codepoints": [8721], "characters": "\u2211" }, + "⋑": { "codepoints": [8913], "characters": "\u22D1" }, + "⊃": { "codepoints": [8835], "characters": "\u2283" }, + "⊇": { "codepoints": [8839], "characters": "\u2287" }, + "⋑": { "codepoints": [8913], "characters": "\u22D1" }, + "Þ": { "codepoints": [222], "characters": "\u00DE" }, + "Þ": { "codepoints": [222], "characters": "\u00DE" }, + "™": { "codepoints": [8482], "characters": "\u2122" }, + "Ћ": { "codepoints": [1035], "characters": "\u040B" }, + "Ц": { "codepoints": [1062], "characters": "\u0426" }, + "	": { "codepoints": [9], "characters": "\u0009" }, + "Τ": { "codepoints": [932], "characters": "\u03A4" }, + "Ť": { "codepoints": [356], "characters": "\u0164" }, + "Ţ": { "codepoints": [354], "characters": "\u0162" }, + "Т": { "codepoints": [1058], "characters": "\u0422" }, + "𝔗": { "codepoints": [120087], "characters": "\uD835\uDD17" }, + "∴": { "codepoints": [8756], "characters": "\u2234" }, + "Θ": { "codepoints": [920], "characters": "\u0398" }, + "  ": { "codepoints": [8287, 8202], "characters": "\u205F\u200A" }, + " ": { "codepoints": [8201], "characters": "\u2009" }, + "∼": { "codepoints": [8764], "characters": "\u223C" }, + "≃": { "codepoints": [8771], "characters": "\u2243" }, + "≅": { "codepoints": [8773], "characters": "\u2245" }, + "≈": { "codepoints": [8776], "characters": "\u2248" }, + "𝕋": { "codepoints": [120139], "characters": "\uD835\uDD4B" }, + "⃛": { "codepoints": [8411], "characters": "\u20DB" }, + "𝒯": { "codepoints": [119983], "characters": "\uD835\uDCAF" }, + "Ŧ": { "codepoints": [358], "characters": "\u0166" }, + "Ú": { "codepoints": [218], "characters": "\u00DA" }, + "Ú": { "codepoints": [218], "characters": "\u00DA" }, + "↟": { "codepoints": [8607], "characters": "\u219F" }, + "⥉": { "codepoints": [10569], "characters": "\u2949" }, + "Ў": { "codepoints": [1038], "characters": "\u040E" }, + "Ŭ": { "codepoints": [364], "characters": "\u016C" }, + "Û": { "codepoints": [219], "characters": "\u00DB" }, + "Û": { "codepoints": [219], "characters": "\u00DB" }, + "У": { "codepoints": [1059], "characters": "\u0423" }, + "Ű": { "codepoints": [368], "characters": "\u0170" }, + "𝔘": { "codepoints": [120088], "characters": "\uD835\uDD18" }, + "Ù": { "codepoints": [217], "characters": "\u00D9" }, + "Ù": { "codepoints": [217], "characters": "\u00D9" }, + "Ū": { "codepoints": [362], "characters": "\u016A" }, + "_": { "codepoints": [95], "characters": "\u005F" }, + "⏟": { "codepoints": [9183], "characters": "\u23DF" }, + "⎵": { "codepoints": [9141], "characters": "\u23B5" }, + "⏝": { "codepoints": [9181], "characters": "\u23DD" }, + "⋃": { "codepoints": [8899], "characters": "\u22C3" }, + "⊎": { "codepoints": [8846], "characters": "\u228E" }, + "Ų": { "codepoints": [370], "characters": "\u0172" }, + "𝕌": { "codepoints": [120140], "characters": "\uD835\uDD4C" }, + "↑": { "codepoints": [8593], "characters": "\u2191" }, + "⤒": { "codepoints": [10514], "characters": "\u2912" }, + "⇅": { "codepoints": [8645], "characters": "\u21C5" }, + "↕": { "codepoints": [8597], "characters": "\u2195" }, + "⥮": { "codepoints": [10606], "characters": "\u296E" }, + "⊥": { "codepoints": [8869], "characters": "\u22A5" }, + "↥": { "codepoints": [8613], "characters": "\u21A5" }, + "⇑": { "codepoints": [8657], "characters": "\u21D1" }, + "⇕": { "codepoints": [8661], "characters": "\u21D5" }, + "↖": { "codepoints": [8598], "characters": "\u2196" }, + "↗": { "codepoints": [8599], "characters": "\u2197" }, + "ϒ": { "codepoints": [978], "characters": "\u03D2" }, + "Υ": { "codepoints": [933], "characters": "\u03A5" }, + "Ů": { "codepoints": [366], "characters": "\u016E" }, + "𝒰": { "codepoints": [119984], "characters": "\uD835\uDCB0" }, + "Ũ": { "codepoints": [360], "characters": "\u0168" }, + "Ü": { "codepoints": [220], "characters": "\u00DC" }, + "Ü": { "codepoints": [220], "characters": "\u00DC" }, + "⊫": { "codepoints": [8875], "characters": "\u22AB" }, + "⫫": { "codepoints": [10987], "characters": "\u2AEB" }, + "В": { "codepoints": [1042], "characters": "\u0412" }, + "⊩": { "codepoints": [8873], "characters": "\u22A9" }, + "⫦": { "codepoints": [10982], "characters": "\u2AE6" }, + "⋁": { "codepoints": [8897], "characters": "\u22C1" }, + "‖": { "codepoints": [8214], "characters": "\u2016" }, + "‖": { "codepoints": [8214], "characters": "\u2016" }, + "∣": { "codepoints": [8739], "characters": "\u2223" }, + "|": { "codepoints": [124], "characters": "\u007C" }, + "❘": { "codepoints": [10072], "characters": "\u2758" }, + "≀": { "codepoints": [8768], "characters": "\u2240" }, + " ": { "codepoints": [8202], "characters": "\u200A" }, + "𝔙": { "codepoints": [120089], "characters": "\uD835\uDD19" }, + "𝕍": { "codepoints": [120141], "characters": "\uD835\uDD4D" }, + "𝒱": { "codepoints": [119985], "characters": "\uD835\uDCB1" }, + "⊪": { "codepoints": [8874], "characters": "\u22AA" }, + "Ŵ": { "codepoints": [372], "characters": "\u0174" }, + "⋀": { "codepoints": [8896], "characters": "\u22C0" }, + "𝔚": { "codepoints": [120090], "characters": "\uD835\uDD1A" }, + "𝕎": { "codepoints": [120142], "characters": "\uD835\uDD4E" }, + "𝒲": { "codepoints": [119986], "characters": "\uD835\uDCB2" }, + "𝔛": { "codepoints": [120091], "characters": "\uD835\uDD1B" }, + "Ξ": { "codepoints": [926], "characters": "\u039E" }, + "𝕏": { "codepoints": [120143], "characters": "\uD835\uDD4F" }, + "𝒳": { "codepoints": [119987], "characters": "\uD835\uDCB3" }, + "Я": { "codepoints": [1071], "characters": "\u042F" }, + "Ї": { "codepoints": [1031], "characters": "\u0407" }, + "Ю": { "codepoints": [1070], "characters": "\u042E" }, + "Ý": { "codepoints": [221], "characters": "\u00DD" }, + "Ý": { "codepoints": [221], "characters": "\u00DD" }, + "Ŷ": { "codepoints": [374], "characters": "\u0176" }, + "Ы": { "codepoints": [1067], "characters": "\u042B" }, + "𝔜": { "codepoints": [120092], "characters": "\uD835\uDD1C" }, + "𝕐": { "codepoints": [120144], "characters": "\uD835\uDD50" }, + "𝒴": { "codepoints": [119988], "characters": "\uD835\uDCB4" }, + "Ÿ": { "codepoints": [376], "characters": "\u0178" }, + "Ж": { "codepoints": [1046], "characters": "\u0416" }, + "Ź": { "codepoints": [377], "characters": "\u0179" }, + "Ž": { "codepoints": [381], "characters": "\u017D" }, + "З": { "codepoints": [1047], "characters": "\u0417" }, + "Ż": { "codepoints": [379], "characters": "\u017B" }, + "​": { "codepoints": [8203], "characters": "\u200B" }, + "Ζ": { "codepoints": [918], "characters": "\u0396" }, + "ℨ": { "codepoints": [8488], "characters": "\u2128" }, + "ℤ": { "codepoints": [8484], "characters": "\u2124" }, + "𝒵": { "codepoints": [119989], "characters": "\uD835\uDCB5" }, + "á": { "codepoints": [225], "characters": "\u00E1" }, + "á": { "codepoints": [225], "characters": "\u00E1" }, + "ă": { "codepoints": [259], "characters": "\u0103" }, + "∾": { "codepoints": [8766], "characters": "\u223E" }, + "∾̳": { "codepoints": [8766, 819], "characters": "\u223E\u0333" }, + "∿": { "codepoints": [8767], "characters": "\u223F" }, + "â": { "codepoints": [226], "characters": "\u00E2" }, + "â": { "codepoints": [226], "characters": "\u00E2" }, + "´": { "codepoints": [180], "characters": "\u00B4" }, + "´": { "codepoints": [180], "characters": "\u00B4" }, + "а": { "codepoints": [1072], "characters": "\u0430" }, + "æ": { "codepoints": [230], "characters": "\u00E6" }, + "æ": { "codepoints": [230], "characters": "\u00E6" }, + "⁡": { "codepoints": [8289], "characters": "\u2061" }, + "𝔞": { "codepoints": [120094], "characters": "\uD835\uDD1E" }, + "à": { "codepoints": [224], "characters": "\u00E0" }, + "à": { "codepoints": [224], "characters": "\u00E0" }, + "ℵ": { "codepoints": [8501], "characters": "\u2135" }, + "ℵ": { "codepoints": [8501], "characters": "\u2135" }, + "α": { "codepoints": [945], "characters": "\u03B1" }, + "ā": { "codepoints": [257], "characters": "\u0101" }, + "⨿": { "codepoints": [10815], "characters": "\u2A3F" }, + "&": { "codepoints": [38], "characters": "\u0026" }, + "&": { "codepoints": [38], "characters": "\u0026" }, + "∧": { "codepoints": [8743], "characters": "\u2227" }, + "⩕": { "codepoints": [10837], "characters": "\u2A55" }, + "⩜": { "codepoints": [10844], "characters": "\u2A5C" }, + "⩘": { "codepoints": [10840], "characters": "\u2A58" }, + "⩚": { "codepoints": [10842], "characters": "\u2A5A" }, + "∠": { "codepoints": [8736], "characters": "\u2220" }, + "⦤": { "codepoints": [10660], "characters": "\u29A4" }, + "∠": { "codepoints": [8736], "characters": "\u2220" }, + "∡": { "codepoints": [8737], "characters": "\u2221" }, + "⦨": { "codepoints": [10664], "characters": "\u29A8" }, + "⦩": { "codepoints": [10665], "characters": "\u29A9" }, + "⦪": { "codepoints": [10666], "characters": "\u29AA" }, + "⦫": { "codepoints": [10667], "characters": "\u29AB" }, + "⦬": { "codepoints": [10668], "characters": "\u29AC" }, + "⦭": { "codepoints": [10669], "characters": "\u29AD" }, + "⦮": { "codepoints": [10670], "characters": "\u29AE" }, + "⦯": { "codepoints": [10671], "characters": "\u29AF" }, + "∟": { "codepoints": [8735], "characters": "\u221F" }, + "⊾": { "codepoints": [8894], "characters": "\u22BE" }, + "⦝": { "codepoints": [10653], "characters": "\u299D" }, + "∢": { "codepoints": [8738], "characters": "\u2222" }, + "Å": { "codepoints": [197], "characters": "\u00C5" }, + "⍼": { "codepoints": [9084], "characters": "\u237C" }, + "ą": { "codepoints": [261], "characters": "\u0105" }, + "𝕒": { "codepoints": [120146], "characters": "\uD835\uDD52" }, + "≈": { "codepoints": [8776], "characters": "\u2248" }, + "⩰": { "codepoints": [10864], "characters": "\u2A70" }, + "⩯": { "codepoints": [10863], "characters": "\u2A6F" }, + "≊": { "codepoints": [8778], "characters": "\u224A" }, + "≋": { "codepoints": [8779], "characters": "\u224B" }, + "'": { "codepoints": [39], "characters": "\u0027" }, + "≈": { "codepoints": [8776], "characters": "\u2248" }, + "≊": { "codepoints": [8778], "characters": "\u224A" }, + "å": { "codepoints": [229], "characters": "\u00E5" }, + "å": { "codepoints": [229], "characters": "\u00E5" }, + "𝒶": { "codepoints": [119990], "characters": "\uD835\uDCB6" }, + "*": { "codepoints": [42], "characters": "\u002A" }, + "≈": { "codepoints": [8776], "characters": "\u2248" }, + "≍": { "codepoints": [8781], "characters": "\u224D" }, + "ã": { "codepoints": [227], "characters": "\u00E3" }, + "ã": { "codepoints": [227], "characters": "\u00E3" }, + "ä": { "codepoints": [228], "characters": "\u00E4" }, + "ä": { "codepoints": [228], "characters": "\u00E4" }, + "∳": { "codepoints": [8755], "characters": "\u2233" }, + "⨑": { "codepoints": [10769], "characters": "\u2A11" }, + "⫭": { "codepoints": [10989], "characters": "\u2AED" }, + "≌": { "codepoints": [8780], "characters": "\u224C" }, + "϶": { "codepoints": [1014], "characters": "\u03F6" }, + "‵": { "codepoints": [8245], "characters": "\u2035" }, + "∽": { "codepoints": [8765], "characters": "\u223D" }, + "⋍": { "codepoints": [8909], "characters": "\u22CD" }, + "⊽": { "codepoints": [8893], "characters": "\u22BD" }, + "⌅": { "codepoints": [8965], "characters": "\u2305" }, + "⌅": { "codepoints": [8965], "characters": "\u2305" }, + "⎵": { "codepoints": [9141], "characters": "\u23B5" }, + "⎶": { "codepoints": [9142], "characters": "\u23B6" }, + "≌": { "codepoints": [8780], "characters": "\u224C" }, + "б": { "codepoints": [1073], "characters": "\u0431" }, + "„": { "codepoints": [8222], "characters": "\u201E" }, + "∵": { "codepoints": [8757], "characters": "\u2235" }, + "∵": { "codepoints": [8757], "characters": "\u2235" }, + "⦰": { "codepoints": [10672], "characters": "\u29B0" }, + "϶": { "codepoints": [1014], "characters": "\u03F6" }, + "ℬ": { "codepoints": [8492], "characters": "\u212C" }, + "β": { "codepoints": [946], "characters": "\u03B2" }, + "ℶ": { "codepoints": [8502], "characters": "\u2136" }, + "≬": { "codepoints": [8812], "characters": "\u226C" }, + "𝔟": { "codepoints": [120095], "characters": "\uD835\uDD1F" }, + "⋂": { "codepoints": [8898], "characters": "\u22C2" }, + "◯": { "codepoints": [9711], "characters": "\u25EF" }, + "⋃": { "codepoints": [8899], "characters": "\u22C3" }, + "⨀": { "codepoints": [10752], "characters": "\u2A00" }, + "⨁": { "codepoints": [10753], "characters": "\u2A01" }, + "⨂": { "codepoints": [10754], "characters": "\u2A02" }, + "⨆": { "codepoints": [10758], "characters": "\u2A06" }, + "★": { "codepoints": [9733], "characters": "\u2605" }, + "▽": { "codepoints": [9661], "characters": "\u25BD" }, + "△": { "codepoints": [9651], "characters": "\u25B3" }, + "⨄": { "codepoints": [10756], "characters": "\u2A04" }, + "⋁": { "codepoints": [8897], "characters": "\u22C1" }, + "⋀": { "codepoints": [8896], "characters": "\u22C0" }, + "⤍": { "codepoints": [10509], "characters": "\u290D" }, + "⧫": { "codepoints": [10731], "characters": "\u29EB" }, + "▪": { "codepoints": [9642], "characters": "\u25AA" }, + "▴": { "codepoints": [9652], "characters": "\u25B4" }, + "▾": { "codepoints": [9662], "characters": "\u25BE" }, + "◂": { "codepoints": [9666], "characters": "\u25C2" }, + "▸": { "codepoints": [9656], "characters": "\u25B8" }, + "␣": { "codepoints": [9251], "characters": "\u2423" }, + "▒": { "codepoints": [9618], "characters": "\u2592" }, + "░": { "codepoints": [9617], "characters": "\u2591" }, + "▓": { "codepoints": [9619], "characters": "\u2593" }, + "█": { "codepoints": [9608], "characters": "\u2588" }, + "=⃥": { "codepoints": [61, 8421], "characters": "\u003D\u20E5" }, + "≡⃥": { "codepoints": [8801, 8421], "characters": "\u2261\u20E5" }, + "⌐": { "codepoints": [8976], "characters": "\u2310" }, + "𝕓": { "codepoints": [120147], "characters": "\uD835\uDD53" }, + "⊥": { "codepoints": [8869], "characters": "\u22A5" }, + "⊥": { "codepoints": [8869], "characters": "\u22A5" }, + "⋈": { "codepoints": [8904], "characters": "\u22C8" }, + "╗": { "codepoints": [9559], "characters": "\u2557" }, + "╔": { "codepoints": [9556], "characters": "\u2554" }, + "╖": { "codepoints": [9558], "characters": "\u2556" }, + "╓": { "codepoints": [9555], "characters": "\u2553" }, + "═": { "codepoints": [9552], "characters": "\u2550" }, + "╦": { "codepoints": [9574], "characters": "\u2566" }, + "╩": { "codepoints": [9577], "characters": "\u2569" }, + "╤": { "codepoints": [9572], "characters": "\u2564" }, + "╧": { "codepoints": [9575], "characters": "\u2567" }, + "╝": { "codepoints": [9565], "characters": "\u255D" }, + "╚": { "codepoints": [9562], "characters": "\u255A" }, + "╜": { "codepoints": [9564], "characters": "\u255C" }, + "╙": { "codepoints": [9561], "characters": "\u2559" }, + "║": { "codepoints": [9553], "characters": "\u2551" }, + "╬": { "codepoints": [9580], "characters": "\u256C" }, + "╣": { "codepoints": [9571], "characters": "\u2563" }, + "╠": { "codepoints": [9568], "characters": "\u2560" }, + "╫": { "codepoints": [9579], "characters": "\u256B" }, + "╢": { "codepoints": [9570], "characters": "\u2562" }, + "╟": { "codepoints": [9567], "characters": "\u255F" }, + "⧉": { "codepoints": [10697], "characters": "\u29C9" }, + "╕": { "codepoints": [9557], "characters": "\u2555" }, + "╒": { "codepoints": [9554], "characters": "\u2552" }, + "┐": { "codepoints": [9488], "characters": "\u2510" }, + "┌": { "codepoints": [9484], "characters": "\u250C" }, + "─": { "codepoints": [9472], "characters": "\u2500" }, + "╥": { "codepoints": [9573], "characters": "\u2565" }, + "╨": { "codepoints": [9576], "characters": "\u2568" }, + "┬": { "codepoints": [9516], "characters": "\u252C" }, + "┴": { "codepoints": [9524], "characters": "\u2534" }, + "⊟": { "codepoints": [8863], "characters": "\u229F" }, + "⊞": { "codepoints": [8862], "characters": "\u229E" }, + "⊠": { "codepoints": [8864], "characters": "\u22A0" }, + "╛": { "codepoints": [9563], "characters": "\u255B" }, + "╘": { "codepoints": [9560], "characters": "\u2558" }, + "┘": { "codepoints": [9496], "characters": "\u2518" }, + "└": { "codepoints": [9492], "characters": "\u2514" }, + "│": { "codepoints": [9474], "characters": "\u2502" }, + "╪": { "codepoints": [9578], "characters": "\u256A" }, + "╡": { "codepoints": [9569], "characters": "\u2561" }, + "╞": { "codepoints": [9566], "characters": "\u255E" }, + "┼": { "codepoints": [9532], "characters": "\u253C" }, + "┤": { "codepoints": [9508], "characters": "\u2524" }, + "├": { "codepoints": [9500], "characters": "\u251C" }, + "‵": { "codepoints": [8245], "characters": "\u2035" }, + "˘": { "codepoints": [728], "characters": "\u02D8" }, + "¦": { "codepoints": [166], "characters": "\u00A6" }, + "¦": { "codepoints": [166], "characters": "\u00A6" }, + "𝒷": { "codepoints": [119991], "characters": "\uD835\uDCB7" }, + "⁏": { "codepoints": [8271], "characters": "\u204F" }, + "∽": { "codepoints": [8765], "characters": "\u223D" }, + "⋍": { "codepoints": [8909], "characters": "\u22CD" }, + "\": { "codepoints": [92], "characters": "\u005C" }, + "⧅": { "codepoints": [10693], "characters": "\u29C5" }, + "⟈": { "codepoints": [10184], "characters": "\u27C8" }, + "•": { "codepoints": [8226], "characters": "\u2022" }, + "•": { "codepoints": [8226], "characters": "\u2022" }, + "≎": { "codepoints": [8782], "characters": "\u224E" }, + "⪮": { "codepoints": [10926], "characters": "\u2AAE" }, + "≏": { "codepoints": [8783], "characters": "\u224F" }, + "≏": { "codepoints": [8783], "characters": "\u224F" }, + "ć": { "codepoints": [263], "characters": "\u0107" }, + "∩": { "codepoints": [8745], "characters": "\u2229" }, + "⩄": { "codepoints": [10820], "characters": "\u2A44" }, + "⩉": { "codepoints": [10825], "characters": "\u2A49" }, + "⩋": { "codepoints": [10827], "characters": "\u2A4B" }, + "⩇": { "codepoints": [10823], "characters": "\u2A47" }, + "⩀": { "codepoints": [10816], "characters": "\u2A40" }, + "∩︀": { "codepoints": [8745, 65024], "characters": "\u2229\uFE00" }, + "⁁": { "codepoints": [8257], "characters": "\u2041" }, + "ˇ": { "codepoints": [711], "characters": "\u02C7" }, + "⩍": { "codepoints": [10829], "characters": "\u2A4D" }, + "č": { "codepoints": [269], "characters": "\u010D" }, + "ç": { "codepoints": [231], "characters": "\u00E7" }, + "ç": { "codepoints": [231], "characters": "\u00E7" }, + "ĉ": { "codepoints": [265], "characters": "\u0109" }, + "⩌": { "codepoints": [10828], "characters": "\u2A4C" }, + "⩐": { "codepoints": [10832], "characters": "\u2A50" }, + "ċ": { "codepoints": [267], "characters": "\u010B" }, + "¸": { "codepoints": [184], "characters": "\u00B8" }, + "¸": { "codepoints": [184], "characters": "\u00B8" }, + "⦲": { "codepoints": [10674], "characters": "\u29B2" }, + "¢": { "codepoints": [162], "characters": "\u00A2" }, + "¢": { "codepoints": [162], "characters": "\u00A2" }, + "·": { "codepoints": [183], "characters": "\u00B7" }, + "𝔠": { "codepoints": [120096], "characters": "\uD835\uDD20" }, + "ч": { "codepoints": [1095], "characters": "\u0447" }, + "✓": { "codepoints": [10003], "characters": "\u2713" }, + "✓": { "codepoints": [10003], "characters": "\u2713" }, + "χ": { "codepoints": [967], "characters": "\u03C7" }, + "○": { "codepoints": [9675], "characters": "\u25CB" }, + "⧃": { "codepoints": [10691], "characters": "\u29C3" }, + "ˆ": { "codepoints": [710], "characters": "\u02C6" }, + "≗": { "codepoints": [8791], "characters": "\u2257" }, + "↺": { "codepoints": [8634], "characters": "\u21BA" }, + "↻": { "codepoints": [8635], "characters": "\u21BB" }, + "®": { "codepoints": [174], "characters": "\u00AE" }, + "Ⓢ": { "codepoints": [9416], "characters": "\u24C8" }, + "⊛": { "codepoints": [8859], "characters": "\u229B" }, + "⊚": { "codepoints": [8858], "characters": "\u229A" }, + "⊝": { "codepoints": [8861], "characters": "\u229D" }, + "≗": { "codepoints": [8791], "characters": "\u2257" }, + "⨐": { "codepoints": [10768], "characters": "\u2A10" }, + "⫯": { "codepoints": [10991], "characters": "\u2AEF" }, + "⧂": { "codepoints": [10690], "characters": "\u29C2" }, + "♣": { "codepoints": [9827], "characters": "\u2663" }, + "♣": { "codepoints": [9827], "characters": "\u2663" }, + ":": { "codepoints": [58], "characters": "\u003A" }, + "≔": { "codepoints": [8788], "characters": "\u2254" }, + "≔": { "codepoints": [8788], "characters": "\u2254" }, + ",": { "codepoints": [44], "characters": "\u002C" }, + "@": { "codepoints": [64], "characters": "\u0040" }, + "∁": { "codepoints": [8705], "characters": "\u2201" }, + "∘": { "codepoints": [8728], "characters": "\u2218" }, + "∁": { "codepoints": [8705], "characters": "\u2201" }, + "ℂ": { "codepoints": [8450], "characters": "\u2102" }, + "≅": { "codepoints": [8773], "characters": "\u2245" }, + "⩭": { "codepoints": [10861], "characters": "\u2A6D" }, + "∮": { "codepoints": [8750], "characters": "\u222E" }, + "𝕔": { "codepoints": [120148], "characters": "\uD835\uDD54" }, + "∐": { "codepoints": [8720], "characters": "\u2210" }, + "©": { "codepoints": [169], "characters": "\u00A9" }, + "©": { "codepoints": [169], "characters": "\u00A9" }, + "℗": { "codepoints": [8471], "characters": "\u2117" }, + "↵": { "codepoints": [8629], "characters": "\u21B5" }, + "✗": { "codepoints": [10007], "characters": "\u2717" }, + "𝒸": { "codepoints": [119992], "characters": "\uD835\uDCB8" }, + "⫏": { "codepoints": [10959], "characters": "\u2ACF" }, + "⫑": { "codepoints": [10961], "characters": "\u2AD1" }, + "⫐": { "codepoints": [10960], "characters": "\u2AD0" }, + "⫒": { "codepoints": [10962], "characters": "\u2AD2" }, + "⋯": { "codepoints": [8943], "characters": "\u22EF" }, + "⤸": { "codepoints": [10552], "characters": "\u2938" }, + "⤵": { "codepoints": [10549], "characters": "\u2935" }, + "⋞": { "codepoints": [8926], "characters": "\u22DE" }, + "⋟": { "codepoints": [8927], "characters": "\u22DF" }, + "↶": { "codepoints": [8630], "characters": "\u21B6" }, + "⤽": { "codepoints": [10557], "characters": "\u293D" }, + "∪": { "codepoints": [8746], "characters": "\u222A" }, + "⩈": { "codepoints": [10824], "characters": "\u2A48" }, + "⩆": { "codepoints": [10822], "characters": "\u2A46" }, + "⩊": { "codepoints": [10826], "characters": "\u2A4A" }, + "⊍": { "codepoints": [8845], "characters": "\u228D" }, + "⩅": { "codepoints": [10821], "characters": "\u2A45" }, + "∪︀": { "codepoints": [8746, 65024], "characters": "\u222A\uFE00" }, + "↷": { "codepoints": [8631], "characters": "\u21B7" }, + "⤼": { "codepoints": [10556], "characters": "\u293C" }, + "⋞": { "codepoints": [8926], "characters": "\u22DE" }, + "⋟": { "codepoints": [8927], "characters": "\u22DF" }, + "⋎": { "codepoints": [8910], "characters": "\u22CE" }, + "⋏": { "codepoints": [8911], "characters": "\u22CF" }, + "¤": { "codepoints": [164], "characters": "\u00A4" }, + "¤": { "codepoints": [164], "characters": "\u00A4" }, + "↶": { "codepoints": [8630], "characters": "\u21B6" }, + "↷": { "codepoints": [8631], "characters": "\u21B7" }, + "⋎": { "codepoints": [8910], "characters": "\u22CE" }, + "⋏": { "codepoints": [8911], "characters": "\u22CF" }, + "∲": { "codepoints": [8754], "characters": "\u2232" }, + "∱": { "codepoints": [8753], "characters": "\u2231" }, + "⌭": { "codepoints": [9005], "characters": "\u232D" }, + "⇓": { "codepoints": [8659], "characters": "\u21D3" }, + "⥥": { "codepoints": [10597], "characters": "\u2965" }, + "†": { "codepoints": [8224], "characters": "\u2020" }, + "ℸ": { "codepoints": [8504], "characters": "\u2138" }, + "↓": { "codepoints": [8595], "characters": "\u2193" }, + "‐": { "codepoints": [8208], "characters": "\u2010" }, + "⊣": { "codepoints": [8867], "characters": "\u22A3" }, + "⤏": { "codepoints": [10511], "characters": "\u290F" }, + "˝": { "codepoints": [733], "characters": "\u02DD" }, + "ď": { "codepoints": [271], "characters": "\u010F" }, + "д": { "codepoints": [1076], "characters": "\u0434" }, + "ⅆ": { "codepoints": [8518], "characters": "\u2146" }, + "‡": { "codepoints": [8225], "characters": "\u2021" }, + "⇊": { "codepoints": [8650], "characters": "\u21CA" }, + "⩷": { "codepoints": [10871], "characters": "\u2A77" }, + "°": { "codepoints": [176], "characters": "\u00B0" }, + "°": { "codepoints": [176], "characters": "\u00B0" }, + "δ": { "codepoints": [948], "characters": "\u03B4" }, + "⦱": { "codepoints": [10673], "characters": "\u29B1" }, + "⥿": { "codepoints": [10623], "characters": "\u297F" }, + "𝔡": { "codepoints": [120097], "characters": "\uD835\uDD21" }, + "⇃": { "codepoints": [8643], "characters": "\u21C3" }, + "⇂": { "codepoints": [8642], "characters": "\u21C2" }, + "⋄": { "codepoints": [8900], "characters": "\u22C4" }, + "⋄": { "codepoints": [8900], "characters": "\u22C4" }, + "♦": { "codepoints": [9830], "characters": "\u2666" }, + "♦": { "codepoints": [9830], "characters": "\u2666" }, + "¨": { "codepoints": [168], "characters": "\u00A8" }, + "ϝ": { "codepoints": [989], "characters": "\u03DD" }, + "⋲": { "codepoints": [8946], "characters": "\u22F2" }, + "÷": { "codepoints": [247], "characters": "\u00F7" }, + "÷": { "codepoints": [247], "characters": "\u00F7" }, + "÷": { "codepoints": [247], "characters": "\u00F7" }, + "⋇": { "codepoints": [8903], "characters": "\u22C7" }, + "⋇": { "codepoints": [8903], "characters": "\u22C7" }, + "ђ": { "codepoints": [1106], "characters": "\u0452" }, + "⌞": { "codepoints": [8990], "characters": "\u231E" }, + "⌍": { "codepoints": [8973], "characters": "\u230D" }, + "$": { "codepoints": [36], "characters": "\u0024" }, + "𝕕": { "codepoints": [120149], "characters": "\uD835\uDD55" }, + "˙": { "codepoints": [729], "characters": "\u02D9" }, + "≐": { "codepoints": [8784], "characters": "\u2250" }, + "≑": { "codepoints": [8785], "characters": "\u2251" }, + "∸": { "codepoints": [8760], "characters": "\u2238" }, + "∔": { "codepoints": [8724], "characters": "\u2214" }, + "⊡": { "codepoints": [8865], "characters": "\u22A1" }, + "⌆": { "codepoints": [8966], "characters": "\u2306" }, + "↓": { "codepoints": [8595], "characters": "\u2193" }, + "⇊": { "codepoints": [8650], "characters": "\u21CA" }, + "⇃": { "codepoints": [8643], "characters": "\u21C3" }, + "⇂": { "codepoints": [8642], "characters": "\u21C2" }, + "⤐": { "codepoints": [10512], "characters": "\u2910" }, + "⌟": { "codepoints": [8991], "characters": "\u231F" }, + "⌌": { "codepoints": [8972], "characters": "\u230C" }, + "𝒹": { "codepoints": [119993], "characters": "\uD835\uDCB9" }, + "ѕ": { "codepoints": [1109], "characters": "\u0455" }, + "⧶": { "codepoints": [10742], "characters": "\u29F6" }, + "đ": { "codepoints": [273], "characters": "\u0111" }, + "⋱": { "codepoints": [8945], "characters": "\u22F1" }, + "▿": { "codepoints": [9663], "characters": "\u25BF" }, + "▾": { "codepoints": [9662], "characters": "\u25BE" }, + "⇵": { "codepoints": [8693], "characters": "\u21F5" }, + "⥯": { "codepoints": [10607], "characters": "\u296F" }, + "⦦": { "codepoints": [10662], "characters": "\u29A6" }, + "џ": { "codepoints": [1119], "characters": "\u045F" }, + "⟿": { "codepoints": [10239], "characters": "\u27FF" }, + "⩷": { "codepoints": [10871], "characters": "\u2A77" }, + "≑": { "codepoints": [8785], "characters": "\u2251" }, + "é": { "codepoints": [233], "characters": "\u00E9" }, + "é": { "codepoints": [233], "characters": "\u00E9" }, + "⩮": { "codepoints": [10862], "characters": "\u2A6E" }, + "ě": { "codepoints": [283], "characters": "\u011B" }, + "≖": { "codepoints": [8790], "characters": "\u2256" }, + "ê": { "codepoints": [234], "characters": "\u00EA" }, + "ê": { "codepoints": [234], "characters": "\u00EA" }, + "≕": { "codepoints": [8789], "characters": "\u2255" }, + "э": { "codepoints": [1101], "characters": "\u044D" }, + "ė": { "codepoints": [279], "characters": "\u0117" }, + "ⅇ": { "codepoints": [8519], "characters": "\u2147" }, + "≒": { "codepoints": [8786], "characters": "\u2252" }, + "𝔢": { "codepoints": [120098], "characters": "\uD835\uDD22" }, + "⪚": { "codepoints": [10906], "characters": "\u2A9A" }, + "è": { "codepoints": [232], "characters": "\u00E8" }, + "è": { "codepoints": [232], "characters": "\u00E8" }, + "⪖": { "codepoints": [10902], "characters": "\u2A96" }, + "⪘": { "codepoints": [10904], "characters": "\u2A98" }, + "⪙": { "codepoints": [10905], "characters": "\u2A99" }, + "⏧": { "codepoints": [9191], "characters": "\u23E7" }, + "ℓ": { "codepoints": [8467], "characters": "\u2113" }, + "⪕": { "codepoints": [10901], "characters": "\u2A95" }, + "⪗": { "codepoints": [10903], "characters": "\u2A97" }, + "ē": { "codepoints": [275], "characters": "\u0113" }, + "∅": { "codepoints": [8709], "characters": "\u2205" }, + "∅": { "codepoints": [8709], "characters": "\u2205" }, + "∅": { "codepoints": [8709], "characters": "\u2205" }, + " ": { "codepoints": [8196], "characters": "\u2004" }, + " ": { "codepoints": [8197], "characters": "\u2005" }, + " ": { "codepoints": [8195], "characters": "\u2003" }, + "ŋ": { "codepoints": [331], "characters": "\u014B" }, + " ": { "codepoints": [8194], "characters": "\u2002" }, + "ę": { "codepoints": [281], "characters": "\u0119" }, + "𝕖": { "codepoints": [120150], "characters": "\uD835\uDD56" }, + "⋕": { "codepoints": [8917], "characters": "\u22D5" }, + "⧣": { "codepoints": [10723], "characters": "\u29E3" }, + "⩱": { "codepoints": [10865], "characters": "\u2A71" }, + "ε": { "codepoints": [949], "characters": "\u03B5" }, + "ε": { "codepoints": [949], "characters": "\u03B5" }, + "ϵ": { "codepoints": [1013], "characters": "\u03F5" }, + "≖": { "codepoints": [8790], "characters": "\u2256" }, + "≕": { "codepoints": [8789], "characters": "\u2255" }, + "≂": { "codepoints": [8770], "characters": "\u2242" }, + "⪖": { "codepoints": [10902], "characters": "\u2A96" }, + "⪕": { "codepoints": [10901], "characters": "\u2A95" }, + "=": { "codepoints": [61], "characters": "\u003D" }, + "≟": { "codepoints": [8799], "characters": "\u225F" }, + "≡": { "codepoints": [8801], "characters": "\u2261" }, + "⩸": { "codepoints": [10872], "characters": "\u2A78" }, + "⧥": { "codepoints": [10725], "characters": "\u29E5" }, + "≓": { "codepoints": [8787], "characters": "\u2253" }, + "⥱": { "codepoints": [10609], "characters": "\u2971" }, + "ℯ": { "codepoints": [8495], "characters": "\u212F" }, + "≐": { "codepoints": [8784], "characters": "\u2250" }, + "≂": { "codepoints": [8770], "characters": "\u2242" }, + "η": { "codepoints": [951], "characters": "\u03B7" }, + "ð": { "codepoints": [240], "characters": "\u00F0" }, + "ð": { "codepoints": [240], "characters": "\u00F0" }, + "ë": { "codepoints": [235], "characters": "\u00EB" }, + "ë": { "codepoints": [235], "characters": "\u00EB" }, + "€": { "codepoints": [8364], "characters": "\u20AC" }, + "!": { "codepoints": [33], "characters": "\u0021" }, + "∃": { "codepoints": [8707], "characters": "\u2203" }, + "ℰ": { "codepoints": [8496], "characters": "\u2130" }, + "ⅇ": { "codepoints": [8519], "characters": "\u2147" }, + "≒": { "codepoints": [8786], "characters": "\u2252" }, + "ф": { "codepoints": [1092], "characters": "\u0444" }, + "♀": { "codepoints": [9792], "characters": "\u2640" }, + "ffi": { "codepoints": [64259], "characters": "\uFB03" }, + "ff": { "codepoints": [64256], "characters": "\uFB00" }, + "ffl": { "codepoints": [64260], "characters": "\uFB04" }, + "𝔣": { "codepoints": [120099], "characters": "\uD835\uDD23" }, + "fi": { "codepoints": [64257], "characters": "\uFB01" }, + "fj": { "codepoints": [102, 106], "characters": "\u0066\u006A" }, + "♭": { "codepoints": [9837], "characters": "\u266D" }, + "fl": { "codepoints": [64258], "characters": "\uFB02" }, + "▱": { "codepoints": [9649], "characters": "\u25B1" }, + "ƒ": { "codepoints": [402], "characters": "\u0192" }, + "𝕗": { "codepoints": [120151], "characters": "\uD835\uDD57" }, + "∀": { "codepoints": [8704], "characters": "\u2200" }, + "⋔": { "codepoints": [8916], "characters": "\u22D4" }, + "⫙": { "codepoints": [10969], "characters": "\u2AD9" }, + "⨍": { "codepoints": [10765], "characters": "\u2A0D" }, + "½": { "codepoints": [189], "characters": "\u00BD" }, + "½": { "codepoints": [189], "characters": "\u00BD" }, + "⅓": { "codepoints": [8531], "characters": "\u2153" }, + "¼": { "codepoints": [188], "characters": "\u00BC" }, + "¼": { "codepoints": [188], "characters": "\u00BC" }, + "⅕": { "codepoints": [8533], "characters": "\u2155" }, + "⅙": { "codepoints": [8537], "characters": "\u2159" }, + "⅛": { "codepoints": [8539], "characters": "\u215B" }, + "⅔": { "codepoints": [8532], "characters": "\u2154" }, + "⅖": { "codepoints": [8534], "characters": "\u2156" }, + "¾": { "codepoints": [190], "characters": "\u00BE" }, + "¾": { "codepoints": [190], "characters": "\u00BE" }, + "⅗": { "codepoints": [8535], "characters": "\u2157" }, + "⅜": { "codepoints": [8540], "characters": "\u215C" }, + "⅘": { "codepoints": [8536], "characters": "\u2158" }, + "⅚": { "codepoints": [8538], "characters": "\u215A" }, + "⅝": { "codepoints": [8541], "characters": "\u215D" }, + "⅞": { "codepoints": [8542], "characters": "\u215E" }, + "⁄": { "codepoints": [8260], "characters": "\u2044" }, + "⌢": { "codepoints": [8994], "characters": "\u2322" }, + "𝒻": { "codepoints": [119995], "characters": "\uD835\uDCBB" }, + "≧": { "codepoints": [8807], "characters": "\u2267" }, + "⪌": { "codepoints": [10892], "characters": "\u2A8C" }, + "ǵ": { "codepoints": [501], "characters": "\u01F5" }, + "γ": { "codepoints": [947], "characters": "\u03B3" }, + "ϝ": { "codepoints": [989], "characters": "\u03DD" }, + "⪆": { "codepoints": [10886], "characters": "\u2A86" }, + "ğ": { "codepoints": [287], "characters": "\u011F" }, + "ĝ": { "codepoints": [285], "characters": "\u011D" }, + "г": { "codepoints": [1075], "characters": "\u0433" }, + "ġ": { "codepoints": [289], "characters": "\u0121" }, + "≥": { "codepoints": [8805], "characters": "\u2265" }, + "⋛": { "codepoints": [8923], "characters": "\u22DB" }, + "≥": { "codepoints": [8805], "characters": "\u2265" }, + "≧": { "codepoints": [8807], "characters": "\u2267" }, + "⩾": { "codepoints": [10878], "characters": "\u2A7E" }, + "⩾": { "codepoints": [10878], "characters": "\u2A7E" }, + "⪩": { "codepoints": [10921], "characters": "\u2AA9" }, + "⪀": { "codepoints": [10880], "characters": "\u2A80" }, + "⪂": { "codepoints": [10882], "characters": "\u2A82" }, + "⪄": { "codepoints": [10884], "characters": "\u2A84" }, + "⋛︀": { "codepoints": [8923, 65024], "characters": "\u22DB\uFE00" }, + "⪔": { "codepoints": [10900], "characters": "\u2A94" }, + "𝔤": { "codepoints": [120100], "characters": "\uD835\uDD24" }, + "≫": { "codepoints": [8811], "characters": "\u226B" }, + "⋙": { "codepoints": [8921], "characters": "\u22D9" }, + "ℷ": { "codepoints": [8503], "characters": "\u2137" }, + "ѓ": { "codepoints": [1107], "characters": "\u0453" }, + "≷": { "codepoints": [8823], "characters": "\u2277" }, + "⪒": { "codepoints": [10898], "characters": "\u2A92" }, + "⪥": { "codepoints": [10917], "characters": "\u2AA5" }, + "⪤": { "codepoints": [10916], "characters": "\u2AA4" }, + "≩": { "codepoints": [8809], "characters": "\u2269" }, + "⪊": { "codepoints": [10890], "characters": "\u2A8A" }, + "⪊": { "codepoints": [10890], "characters": "\u2A8A" }, + "⪈": { "codepoints": [10888], "characters": "\u2A88" }, + "⪈": { "codepoints": [10888], "characters": "\u2A88" }, + "≩": { "codepoints": [8809], "characters": "\u2269" }, + "⋧": { "codepoints": [8935], "characters": "\u22E7" }, + "𝕘": { "codepoints": [120152], "characters": "\uD835\uDD58" }, + "`": { "codepoints": [96], "characters": "\u0060" }, + "ℊ": { "codepoints": [8458], "characters": "\u210A" }, + "≳": { "codepoints": [8819], "characters": "\u2273" }, + "⪎": { "codepoints": [10894], "characters": "\u2A8E" }, + "⪐": { "codepoints": [10896], "characters": "\u2A90" }, + ">": { "codepoints": [62], "characters": "\u003E" }, + ">": { "codepoints": [62], "characters": "\u003E" }, + "⪧": { "codepoints": [10919], "characters": "\u2AA7" }, + "⩺": { "codepoints": [10874], "characters": "\u2A7A" }, + "⋗": { "codepoints": [8919], "characters": "\u22D7" }, + "⦕": { "codepoints": [10645], "characters": "\u2995" }, + "⩼": { "codepoints": [10876], "characters": "\u2A7C" }, + "⪆": { "codepoints": [10886], "characters": "\u2A86" }, + "⥸": { "codepoints": [10616], "characters": "\u2978" }, + "⋗": { "codepoints": [8919], "characters": "\u22D7" }, + "⋛": { "codepoints": [8923], "characters": "\u22DB" }, + "⪌": { "codepoints": [10892], "characters": "\u2A8C" }, + "≷": { "codepoints": [8823], "characters": "\u2277" }, + "≳": { "codepoints": [8819], "characters": "\u2273" }, + "≩︀": { "codepoints": [8809, 65024], "characters": "\u2269\uFE00" }, + "≩︀": { "codepoints": [8809, 65024], "characters": "\u2269\uFE00" }, + "⇔": { "codepoints": [8660], "characters": "\u21D4" }, + " ": { "codepoints": [8202], "characters": "\u200A" }, + "½": { "codepoints": [189], "characters": "\u00BD" }, + "ℋ": { "codepoints": [8459], "characters": "\u210B" }, + "ъ": { "codepoints": [1098], "characters": "\u044A" }, + "↔": { "codepoints": [8596], "characters": "\u2194" }, + "⥈": { "codepoints": [10568], "characters": "\u2948" }, + "↭": { "codepoints": [8621], "characters": "\u21AD" }, + "ℏ": { "codepoints": [8463], "characters": "\u210F" }, + "ĥ": { "codepoints": [293], "characters": "\u0125" }, + "♥": { "codepoints": [9829], "characters": "\u2665" }, + "♥": { "codepoints": [9829], "characters": "\u2665" }, + "…": { "codepoints": [8230], "characters": "\u2026" }, + "⊹": { "codepoints": [8889], "characters": "\u22B9" }, + "𝔥": { "codepoints": [120101], "characters": "\uD835\uDD25" }, + "⤥": { "codepoints": [10533], "characters": "\u2925" }, + "⤦": { "codepoints": [10534], "characters": "\u2926" }, + "⇿": { "codepoints": [8703], "characters": "\u21FF" }, + "∻": { "codepoints": [8763], "characters": "\u223B" }, + "↩": { "codepoints": [8617], "characters": "\u21A9" }, + "↪": { "codepoints": [8618], "characters": "\u21AA" }, + "𝕙": { "codepoints": [120153], "characters": "\uD835\uDD59" }, + "―": { "codepoints": [8213], "characters": "\u2015" }, + "𝒽": { "codepoints": [119997], "characters": "\uD835\uDCBD" }, + "ℏ": { "codepoints": [8463], "characters": "\u210F" }, + "ħ": { "codepoints": [295], "characters": "\u0127" }, + "⁃": { "codepoints": [8259], "characters": "\u2043" }, + "‐": { "codepoints": [8208], "characters": "\u2010" }, + "í": { "codepoints": [237], "characters": "\u00ED" }, + "í": { "codepoints": [237], "characters": "\u00ED" }, + "⁣": { "codepoints": [8291], "characters": "\u2063" }, + "î": { "codepoints": [238], "characters": "\u00EE" }, + "î": { "codepoints": [238], "characters": "\u00EE" }, + "и": { "codepoints": [1080], "characters": "\u0438" }, + "е": { "codepoints": [1077], "characters": "\u0435" }, + "¡": { "codepoints": [161], "characters": "\u00A1" }, + "¡": { "codepoints": [161], "characters": "\u00A1" }, + "⇔": { "codepoints": [8660], "characters": "\u21D4" }, + "𝔦": { "codepoints": [120102], "characters": "\uD835\uDD26" }, + "ì": { "codepoints": [236], "characters": "\u00EC" }, + "ì": { "codepoints": [236], "characters": "\u00EC" }, + "ⅈ": { "codepoints": [8520], "characters": "\u2148" }, + "⨌": { "codepoints": [10764], "characters": "\u2A0C" }, + "∭": { "codepoints": [8749], "characters": "\u222D" }, + "⧜": { "codepoints": [10716], "characters": "\u29DC" }, + "℩": { "codepoints": [8489], "characters": "\u2129" }, + "ij": { "codepoints": [307], "characters": "\u0133" }, + "ī": { "codepoints": [299], "characters": "\u012B" }, + "ℑ": { "codepoints": [8465], "characters": "\u2111" }, + "ℐ": { "codepoints": [8464], "characters": "\u2110" }, + "ℑ": { "codepoints": [8465], "characters": "\u2111" }, + "ı": { "codepoints": [305], "characters": "\u0131" }, + "⊷": { "codepoints": [8887], "characters": "\u22B7" }, + "Ƶ": { "codepoints": [437], "characters": "\u01B5" }, + "∈": { "codepoints": [8712], "characters": "\u2208" }, + "℅": { "codepoints": [8453], "characters": "\u2105" }, + "∞": { "codepoints": [8734], "characters": "\u221E" }, + "⧝": { "codepoints": [10717], "characters": "\u29DD" }, + "ı": { "codepoints": [305], "characters": "\u0131" }, + "∫": { "codepoints": [8747], "characters": "\u222B" }, + "⊺": { "codepoints": [8890], "characters": "\u22BA" }, + "ℤ": { "codepoints": [8484], "characters": "\u2124" }, + "⊺": { "codepoints": [8890], "characters": "\u22BA" }, + "⨗": { "codepoints": [10775], "characters": "\u2A17" }, + "⨼": { "codepoints": [10812], "characters": "\u2A3C" }, + "ё": { "codepoints": [1105], "characters": "\u0451" }, + "į": { "codepoints": [303], "characters": "\u012F" }, + "𝕚": { "codepoints": [120154], "characters": "\uD835\uDD5A" }, + "ι": { "codepoints": [953], "characters": "\u03B9" }, + "⨼": { "codepoints": [10812], "characters": "\u2A3C" }, + "¿": { "codepoints": [191], "characters": "\u00BF" }, + "¿": { "codepoints": [191], "characters": "\u00BF" }, + "𝒾": { "codepoints": [119998], "characters": "\uD835\uDCBE" }, + "∈": { "codepoints": [8712], "characters": "\u2208" }, + "⋹": { "codepoints": [8953], "characters": "\u22F9" }, + "⋵": { "codepoints": [8949], "characters": "\u22F5" }, + "⋴": { "codepoints": [8948], "characters": "\u22F4" }, + "⋳": { "codepoints": [8947], "characters": "\u22F3" }, + "∈": { "codepoints": [8712], "characters": "\u2208" }, + "⁢": { "codepoints": [8290], "characters": "\u2062" }, + "ĩ": { "codepoints": [297], "characters": "\u0129" }, + "і": { "codepoints": [1110], "characters": "\u0456" }, + "ï": { "codepoints": [239], "characters": "\u00EF" }, + "ï": { "codepoints": [239], "characters": "\u00EF" }, + "ĵ": { "codepoints": [309], "characters": "\u0135" }, + "й": { "codepoints": [1081], "characters": "\u0439" }, + "𝔧": { "codepoints": [120103], "characters": "\uD835\uDD27" }, + "ȷ": { "codepoints": [567], "characters": "\u0237" }, + "𝕛": { "codepoints": [120155], "characters": "\uD835\uDD5B" }, + "𝒿": { "codepoints": [119999], "characters": "\uD835\uDCBF" }, + "ј": { "codepoints": [1112], "characters": "\u0458" }, + "є": { "codepoints": [1108], "characters": "\u0454" }, + "κ": { "codepoints": [954], "characters": "\u03BA" }, + "ϰ": { "codepoints": [1008], "characters": "\u03F0" }, + "ķ": { "codepoints": [311], "characters": "\u0137" }, + "к": { "codepoints": [1082], "characters": "\u043A" }, + "𝔨": { "codepoints": [120104], "characters": "\uD835\uDD28" }, + "ĸ": { "codepoints": [312], "characters": "\u0138" }, + "х": { "codepoints": [1093], "characters": "\u0445" }, + "ќ": { "codepoints": [1116], "characters": "\u045C" }, + "𝕜": { "codepoints": [120156], "characters": "\uD835\uDD5C" }, + "𝓀": { "codepoints": [120000], "characters": "\uD835\uDCC0" }, + "⇚": { "codepoints": [8666], "characters": "\u21DA" }, + "⇐": { "codepoints": [8656], "characters": "\u21D0" }, + "⤛": { "codepoints": [10523], "characters": "\u291B" }, + "⤎": { "codepoints": [10510], "characters": "\u290E" }, + "≦": { "codepoints": [8806], "characters": "\u2266" }, + "⪋": { "codepoints": [10891], "characters": "\u2A8B" }, + "⥢": { "codepoints": [10594], "characters": "\u2962" }, + "ĺ": { "codepoints": [314], "characters": "\u013A" }, + "⦴": { "codepoints": [10676], "characters": "\u29B4" }, + "ℒ": { "codepoints": [8466], "characters": "\u2112" }, + "λ": { "codepoints": [955], "characters": "\u03BB" }, + "⟨": { "codepoints": [10216], "characters": "\u27E8" }, + "⦑": { "codepoints": [10641], "characters": "\u2991" }, + "⟨": { "codepoints": [10216], "characters": "\u27E8" }, + "⪅": { "codepoints": [10885], "characters": "\u2A85" }, + "«": { "codepoints": [171], "characters": "\u00AB" }, + "«": { "codepoints": [171], "characters": "\u00AB" }, + "←": { "codepoints": [8592], "characters": "\u2190" }, + "⇤": { "codepoints": [8676], "characters": "\u21E4" }, + "⤟": { "codepoints": [10527], "characters": "\u291F" }, + "⤝": { "codepoints": [10525], "characters": "\u291D" }, + "↩": { "codepoints": [8617], "characters": "\u21A9" }, + "↫": { "codepoints": [8619], "characters": "\u21AB" }, + "⤹": { "codepoints": [10553], "characters": "\u2939" }, + "⥳": { "codepoints": [10611], "characters": "\u2973" }, + "↢": { "codepoints": [8610], "characters": "\u21A2" }, + "⪫": { "codepoints": [10923], "characters": "\u2AAB" }, + "⤙": { "codepoints": [10521], "characters": "\u2919" }, + "⪭": { "codepoints": [10925], "characters": "\u2AAD" }, + "⪭︀": { "codepoints": [10925, 65024], "characters": "\u2AAD\uFE00" }, + "⤌": { "codepoints": [10508], "characters": "\u290C" }, + "❲": { "codepoints": [10098], "characters": "\u2772" }, + "{": { "codepoints": [123], "characters": "\u007B" }, + "[": { "codepoints": [91], "characters": "\u005B" }, + "⦋": { "codepoints": [10635], "characters": "\u298B" }, + "⦏": { "codepoints": [10639], "characters": "\u298F" }, + "⦍": { "codepoints": [10637], "characters": "\u298D" }, + "ľ": { "codepoints": [318], "characters": "\u013E" }, + "ļ": { "codepoints": [316], "characters": "\u013C" }, + "⌈": { "codepoints": [8968], "characters": "\u2308" }, + "{": { "codepoints": [123], "characters": "\u007B" }, + "л": { "codepoints": [1083], "characters": "\u043B" }, + "⤶": { "codepoints": [10550], "characters": "\u2936" }, + "“": { "codepoints": [8220], "characters": "\u201C" }, + "„": { "codepoints": [8222], "characters": "\u201E" }, + "⥧": { "codepoints": [10599], "characters": "\u2967" }, + "⥋": { "codepoints": [10571], "characters": "\u294B" }, + "↲": { "codepoints": [8626], "characters": "\u21B2" }, + "≤": { "codepoints": [8804], "characters": "\u2264" }, + "←": { "codepoints": [8592], "characters": "\u2190" }, + "↢": { "codepoints": [8610], "characters": "\u21A2" }, + "↽": { "codepoints": [8637], "characters": "\u21BD" }, + "↼": { "codepoints": [8636], "characters": "\u21BC" }, + "⇇": { "codepoints": [8647], "characters": "\u21C7" }, + "↔": { "codepoints": [8596], "characters": "\u2194" }, + "⇆": { "codepoints": [8646], "characters": "\u21C6" }, + "⇋": { "codepoints": [8651], "characters": "\u21CB" }, + "↭": { "codepoints": [8621], "characters": "\u21AD" }, + "⋋": { "codepoints": [8907], "characters": "\u22CB" }, + "⋚": { "codepoints": [8922], "characters": "\u22DA" }, + "≤": { "codepoints": [8804], "characters": "\u2264" }, + "≦": { "codepoints": [8806], "characters": "\u2266" }, + "⩽": { "codepoints": [10877], "characters": "\u2A7D" }, + "⩽": { "codepoints": [10877], "characters": "\u2A7D" }, + "⪨": { "codepoints": [10920], "characters": "\u2AA8" }, + "⩿": { "codepoints": [10879], "characters": "\u2A7F" }, + "⪁": { "codepoints": [10881], "characters": "\u2A81" }, + "⪃": { "codepoints": [10883], "characters": "\u2A83" }, + "⋚︀": { "codepoints": [8922, 65024], "characters": "\u22DA\uFE00" }, + "⪓": { "codepoints": [10899], "characters": "\u2A93" }, + "⪅": { "codepoints": [10885], "characters": "\u2A85" }, + "⋖": { "codepoints": [8918], "characters": "\u22D6" }, + "⋚": { "codepoints": [8922], "characters": "\u22DA" }, + "⪋": { "codepoints": [10891], "characters": "\u2A8B" }, + "≶": { "codepoints": [8822], "characters": "\u2276" }, + "≲": { "codepoints": [8818], "characters": "\u2272" }, + "⥼": { "codepoints": [10620], "characters": "\u297C" }, + "⌊": { "codepoints": [8970], "characters": "\u230A" }, + "𝔩": { "codepoints": [120105], "characters": "\uD835\uDD29" }, + "≶": { "codepoints": [8822], "characters": "\u2276" }, + "⪑": { "codepoints": [10897], "characters": "\u2A91" }, + "↽": { "codepoints": [8637], "characters": "\u21BD" }, + "↼": { "codepoints": [8636], "characters": "\u21BC" }, + "⥪": { "codepoints": [10602], "characters": "\u296A" }, + "▄": { "codepoints": [9604], "characters": "\u2584" }, + "љ": { "codepoints": [1113], "characters": "\u0459" }, + "≪": { "codepoints": [8810], "characters": "\u226A" }, + "⇇": { "codepoints": [8647], "characters": "\u21C7" }, + "⌞": { "codepoints": [8990], "characters": "\u231E" }, + "⥫": { "codepoints": [10603], "characters": "\u296B" }, + "◺": { "codepoints": [9722], "characters": "\u25FA" }, + "ŀ": { "codepoints": [320], "characters": "\u0140" }, + "⎰": { "codepoints": [9136], "characters": "\u23B0" }, + "⎰": { "codepoints": [9136], "characters": "\u23B0" }, + "≨": { "codepoints": [8808], "characters": "\u2268" }, + "⪉": { "codepoints": [10889], "characters": "\u2A89" }, + "⪉": { "codepoints": [10889], "characters": "\u2A89" }, + "⪇": { "codepoints": [10887], "characters": "\u2A87" }, + "⪇": { "codepoints": [10887], "characters": "\u2A87" }, + "≨": { "codepoints": [8808], "characters": "\u2268" }, + "⋦": { "codepoints": [8934], "characters": "\u22E6" }, + "⟬": { "codepoints": [10220], "characters": "\u27EC" }, + "⇽": { "codepoints": [8701], "characters": "\u21FD" }, + "⟦": { "codepoints": [10214], "characters": "\u27E6" }, + "⟵": { "codepoints": [10229], "characters": "\u27F5" }, + "⟷": { "codepoints": [10231], "characters": "\u27F7" }, + "⟼": { "codepoints": [10236], "characters": "\u27FC" }, + "⟶": { "codepoints": [10230], "characters": "\u27F6" }, + "↫": { "codepoints": [8619], "characters": "\u21AB" }, + "↬": { "codepoints": [8620], "characters": "\u21AC" }, + "⦅": { "codepoints": [10629], "characters": "\u2985" }, + "𝕝": { "codepoints": [120157], "characters": "\uD835\uDD5D" }, + "⨭": { "codepoints": [10797], "characters": "\u2A2D" }, + "⨴": { "codepoints": [10804], "characters": "\u2A34" }, + "∗": { "codepoints": [8727], "characters": "\u2217" }, + "_": { "codepoints": [95], "characters": "\u005F" }, + "◊": { "codepoints": [9674], "characters": "\u25CA" }, + "◊": { "codepoints": [9674], "characters": "\u25CA" }, + "⧫": { "codepoints": [10731], "characters": "\u29EB" }, + "(": { "codepoints": [40], "characters": "\u0028" }, + "⦓": { "codepoints": [10643], "characters": "\u2993" }, + "⇆": { "codepoints": [8646], "characters": "\u21C6" }, + "⌟": { "codepoints": [8991], "characters": "\u231F" }, + "⇋": { "codepoints": [8651], "characters": "\u21CB" }, + "⥭": { "codepoints": [10605], "characters": "\u296D" }, + "‎": { "codepoints": [8206], "characters": "\u200E" }, + "⊿": { "codepoints": [8895], "characters": "\u22BF" }, + "‹": { "codepoints": [8249], "characters": "\u2039" }, + "𝓁": { "codepoints": [120001], "characters": "\uD835\uDCC1" }, + "↰": { "codepoints": [8624], "characters": "\u21B0" }, + "≲": { "codepoints": [8818], "characters": "\u2272" }, + "⪍": { "codepoints": [10893], "characters": "\u2A8D" }, + "⪏": { "codepoints": [10895], "characters": "\u2A8F" }, + "[": { "codepoints": [91], "characters": "\u005B" }, + "‘": { "codepoints": [8216], "characters": "\u2018" }, + "‚": { "codepoints": [8218], "characters": "\u201A" }, + "ł": { "codepoints": [322], "characters": "\u0142" }, + "<": { "codepoints": [60], "characters": "\u003C" }, + "<": { "codepoints": [60], "characters": "\u003C" }, + "⪦": { "codepoints": [10918], "characters": "\u2AA6" }, + "⩹": { "codepoints": [10873], "characters": "\u2A79" }, + "⋖": { "codepoints": [8918], "characters": "\u22D6" }, + "⋋": { "codepoints": [8907], "characters": "\u22CB" }, + "⋉": { "codepoints": [8905], "characters": "\u22C9" }, + "⥶": { "codepoints": [10614], "characters": "\u2976" }, + "⩻": { "codepoints": [10875], "characters": "\u2A7B" }, + "⦖": { "codepoints": [10646], "characters": "\u2996" }, + "◃": { "codepoints": [9667], "characters": "\u25C3" }, + "⊴": { "codepoints": [8884], "characters": "\u22B4" }, + "◂": { "codepoints": [9666], "characters": "\u25C2" }, + "⥊": { "codepoints": [10570], "characters": "\u294A" }, + "⥦": { "codepoints": [10598], "characters": "\u2966" }, + "≨︀": { "codepoints": [8808, 65024], "characters": "\u2268\uFE00" }, + "≨︀": { "codepoints": [8808, 65024], "characters": "\u2268\uFE00" }, + "∺": { "codepoints": [8762], "characters": "\u223A" }, + "¯": { "codepoints": [175], "characters": "\u00AF" }, + "¯": { "codepoints": [175], "characters": "\u00AF" }, + "♂": { "codepoints": [9794], "characters": "\u2642" }, + "✠": { "codepoints": [10016], "characters": "\u2720" }, + "✠": { "codepoints": [10016], "characters": "\u2720" }, + "↦": { "codepoints": [8614], "characters": "\u21A6" }, + "↦": { "codepoints": [8614], "characters": "\u21A6" }, + "↧": { "codepoints": [8615], "characters": "\u21A7" }, + "↤": { "codepoints": [8612], "characters": "\u21A4" }, + "↥": { "codepoints": [8613], "characters": "\u21A5" }, + "▮": { "codepoints": [9646], "characters": "\u25AE" }, + "⨩": { "codepoints": [10793], "characters": "\u2A29" }, + "м": { "codepoints": [1084], "characters": "\u043C" }, + "—": { "codepoints": [8212], "characters": "\u2014" }, + "∡": { "codepoints": [8737], "characters": "\u2221" }, + "𝔪": { "codepoints": [120106], "characters": "\uD835\uDD2A" }, + "℧": { "codepoints": [8487], "characters": "\u2127" }, + "µ": { "codepoints": [181], "characters": "\u00B5" }, + "µ": { "codepoints": [181], "characters": "\u00B5" }, + "∣": { "codepoints": [8739], "characters": "\u2223" }, + "*": { "codepoints": [42], "characters": "\u002A" }, + "⫰": { "codepoints": [10992], "characters": "\u2AF0" }, + "·": { "codepoints": [183], "characters": "\u00B7" }, + "·": { "codepoints": [183], "characters": "\u00B7" }, + "−": { "codepoints": [8722], "characters": "\u2212" }, + "⊟": { "codepoints": [8863], "characters": "\u229F" }, + "∸": { "codepoints": [8760], "characters": "\u2238" }, + "⨪": { "codepoints": [10794], "characters": "\u2A2A" }, + "⫛": { "codepoints": [10971], "characters": "\u2ADB" }, + "…": { "codepoints": [8230], "characters": "\u2026" }, + "∓": { "codepoints": [8723], "characters": "\u2213" }, + "⊧": { "codepoints": [8871], "characters": "\u22A7" }, + "𝕞": { "codepoints": [120158], "characters": "\uD835\uDD5E" }, + "∓": { "codepoints": [8723], "characters": "\u2213" }, + "𝓂": { "codepoints": [120002], "characters": "\uD835\uDCC2" }, + "∾": { "codepoints": [8766], "characters": "\u223E" }, + "μ": { "codepoints": [956], "characters": "\u03BC" }, + "⊸": { "codepoints": [8888], "characters": "\u22B8" }, + "⊸": { "codepoints": [8888], "characters": "\u22B8" }, + "⋙̸": { "codepoints": [8921, 824], "characters": "\u22D9\u0338" }, + "≫⃒": { "codepoints": [8811, 8402], "characters": "\u226B\u20D2" }, + "≫̸": { "codepoints": [8811, 824], "characters": "\u226B\u0338" }, + "⇍": { "codepoints": [8653], "characters": "\u21CD" }, + "⇎": { "codepoints": [8654], "characters": "\u21CE" }, + "⋘̸": { "codepoints": [8920, 824], "characters": "\u22D8\u0338" }, + "≪⃒": { "codepoints": [8810, 8402], "characters": "\u226A\u20D2" }, + "≪̸": { "codepoints": [8810, 824], "characters": "\u226A\u0338" }, + "⇏": { "codepoints": [8655], "characters": "\u21CF" }, + "⊯": { "codepoints": [8879], "characters": "\u22AF" }, + "⊮": { "codepoints": [8878], "characters": "\u22AE" }, + "∇": { "codepoints": [8711], "characters": "\u2207" }, + "ń": { "codepoints": [324], "characters": "\u0144" }, + "∠⃒": { "codepoints": [8736, 8402], "characters": "\u2220\u20D2" }, + "≉": { "codepoints": [8777], "characters": "\u2249" }, + "⩰̸": { "codepoints": [10864, 824], "characters": "\u2A70\u0338" }, + "≋̸": { "codepoints": [8779, 824], "characters": "\u224B\u0338" }, + "ʼn": { "codepoints": [329], "characters": "\u0149" }, + "≉": { "codepoints": [8777], "characters": "\u2249" }, + "♮": { "codepoints": [9838], "characters": "\u266E" }, + "♮": { "codepoints": [9838], "characters": "\u266E" }, + "ℕ": { "codepoints": [8469], "characters": "\u2115" }, + " ": { "codepoints": [160], "characters": "\u00A0" }, + " ": { "codepoints": [160], "characters": "\u00A0" }, + "≎̸": { "codepoints": [8782, 824], "characters": "\u224E\u0338" }, + "≏̸": { "codepoints": [8783, 824], "characters": "\u224F\u0338" }, + "⩃": { "codepoints": [10819], "characters": "\u2A43" }, + "ň": { "codepoints": [328], "characters": "\u0148" }, + "ņ": { "codepoints": [326], "characters": "\u0146" }, + "≇": { "codepoints": [8775], "characters": "\u2247" }, + "⩭̸": { "codepoints": [10861, 824], "characters": "\u2A6D\u0338" }, + "⩂": { "codepoints": [10818], "characters": "\u2A42" }, + "н": { "codepoints": [1085], "characters": "\u043D" }, + "–": { "codepoints": [8211], "characters": "\u2013" }, + "≠": { "codepoints": [8800], "characters": "\u2260" }, + "⇗": { "codepoints": [8663], "characters": "\u21D7" }, + "⤤": { "codepoints": [10532], "characters": "\u2924" }, + "↗": { "codepoints": [8599], "characters": "\u2197" }, + "↗": { "codepoints": [8599], "characters": "\u2197" }, + "≐̸": { "codepoints": [8784, 824], "characters": "\u2250\u0338" }, + "≢": { "codepoints": [8802], "characters": "\u2262" }, + "⤨": { "codepoints": [10536], "characters": "\u2928" }, + "≂̸": { "codepoints": [8770, 824], "characters": "\u2242\u0338" }, + "∄": { "codepoints": [8708], "characters": "\u2204" }, + "∄": { "codepoints": [8708], "characters": "\u2204" }, + "𝔫": { "codepoints": [120107], "characters": "\uD835\uDD2B" }, + "≧̸": { "codepoints": [8807, 824], "characters": "\u2267\u0338" }, + "≱": { "codepoints": [8817], "characters": "\u2271" }, + "≱": { "codepoints": [8817], "characters": "\u2271" }, + "≧̸": { "codepoints": [8807, 824], "characters": "\u2267\u0338" }, + "⩾̸": { "codepoints": [10878, 824], "characters": "\u2A7E\u0338" }, + "⩾̸": { "codepoints": [10878, 824], "characters": "\u2A7E\u0338" }, + "≵": { "codepoints": [8821], "characters": "\u2275" }, + "≯": { "codepoints": [8815], "characters": "\u226F" }, + "≯": { "codepoints": [8815], "characters": "\u226F" }, + "⇎": { "codepoints": [8654], "characters": "\u21CE" }, + "↮": { "codepoints": [8622], "characters": "\u21AE" }, + "⫲": { "codepoints": [10994], "characters": "\u2AF2" }, + "∋": { "codepoints": [8715], "characters": "\u220B" }, + "⋼": { "codepoints": [8956], "characters": "\u22FC" }, + "⋺": { "codepoints": [8954], "characters": "\u22FA" }, + "∋": { "codepoints": [8715], "characters": "\u220B" }, + "њ": { "codepoints": [1114], "characters": "\u045A" }, + "⇍": { "codepoints": [8653], "characters": "\u21CD" }, + "≦̸": { "codepoints": [8806, 824], "characters": "\u2266\u0338" }, + "↚": { "codepoints": [8602], "characters": "\u219A" }, + "‥": { "codepoints": [8229], "characters": "\u2025" }, + "≰": { "codepoints": [8816], "characters": "\u2270" }, + "↚": { "codepoints": [8602], "characters": "\u219A" }, + "↮": { "codepoints": [8622], "characters": "\u21AE" }, + "≰": { "codepoints": [8816], "characters": "\u2270" }, + "≦̸": { "codepoints": [8806, 824], "characters": "\u2266\u0338" }, + "⩽̸": { "codepoints": [10877, 824], "characters": "\u2A7D\u0338" }, + "⩽̸": { "codepoints": [10877, 824], "characters": "\u2A7D\u0338" }, + "≮": { "codepoints": [8814], "characters": "\u226E" }, + "≴": { "codepoints": [8820], "characters": "\u2274" }, + "≮": { "codepoints": [8814], "characters": "\u226E" }, + "⋪": { "codepoints": [8938], "characters": "\u22EA" }, + "⋬": { "codepoints": [8940], "characters": "\u22EC" }, + "∤": { "codepoints": [8740], "characters": "\u2224" }, + "𝕟": { "codepoints": [120159], "characters": "\uD835\uDD5F" }, + "¬": { "codepoints": [172], "characters": "\u00AC" }, + "¬": { "codepoints": [172], "characters": "\u00AC" }, + "∉": { "codepoints": [8713], "characters": "\u2209" }, + "⋹̸": { "codepoints": [8953, 824], "characters": "\u22F9\u0338" }, + "⋵̸": { "codepoints": [8949, 824], "characters": "\u22F5\u0338" }, + "∉": { "codepoints": [8713], "characters": "\u2209" }, + "⋷": { "codepoints": [8951], "characters": "\u22F7" }, + "⋶": { "codepoints": [8950], "characters": "\u22F6" }, + "∌": { "codepoints": [8716], "characters": "\u220C" }, + "∌": { "codepoints": [8716], "characters": "\u220C" }, + "⋾": { "codepoints": [8958], "characters": "\u22FE" }, + "⋽": { "codepoints": [8957], "characters": "\u22FD" }, + "∦": { "codepoints": [8742], "characters": "\u2226" }, + "∦": { "codepoints": [8742], "characters": "\u2226" }, + "⫽⃥": { "codepoints": [11005, 8421], "characters": "\u2AFD\u20E5" }, + "∂̸": { "codepoints": [8706, 824], "characters": "\u2202\u0338" }, + "⨔": { "codepoints": [10772], "characters": "\u2A14" }, + "⊀": { "codepoints": [8832], "characters": "\u2280" }, + "⋠": { "codepoints": [8928], "characters": "\u22E0" }, + "⪯̸": { "codepoints": [10927, 824], "characters": "\u2AAF\u0338" }, + "⊀": { "codepoints": [8832], "characters": "\u2280" }, + "⪯̸": { "codepoints": [10927, 824], "characters": "\u2AAF\u0338" }, + "⇏": { "codepoints": [8655], "characters": "\u21CF" }, + "↛": { "codepoints": [8603], "characters": "\u219B" }, + "⤳̸": { "codepoints": [10547, 824], "characters": "\u2933\u0338" }, + "↝̸": { "codepoints": [8605, 824], "characters": "\u219D\u0338" }, + "↛": { "codepoints": [8603], "characters": "\u219B" }, + "⋫": { "codepoints": [8939], "characters": "\u22EB" }, + "⋭": { "codepoints": [8941], "characters": "\u22ED" }, + "⊁": { "codepoints": [8833], "characters": "\u2281" }, + "⋡": { "codepoints": [8929], "characters": "\u22E1" }, + "⪰̸": { "codepoints": [10928, 824], "characters": "\u2AB0\u0338" }, + "𝓃": { "codepoints": [120003], "characters": "\uD835\uDCC3" }, + "∤": { "codepoints": [8740], "characters": "\u2224" }, + "∦": { "codepoints": [8742], "characters": "\u2226" }, + "≁": { "codepoints": [8769], "characters": "\u2241" }, + "≄": { "codepoints": [8772], "characters": "\u2244" }, + "≄": { "codepoints": [8772], "characters": "\u2244" }, + "∤": { "codepoints": [8740], "characters": "\u2224" }, + "∦": { "codepoints": [8742], "characters": "\u2226" }, + "⋢": { "codepoints": [8930], "characters": "\u22E2" }, + "⋣": { "codepoints": [8931], "characters": "\u22E3" }, + "⊄": { "codepoints": [8836], "characters": "\u2284" }, + "⫅̸": { "codepoints": [10949, 824], "characters": "\u2AC5\u0338" }, + "⊈": { "codepoints": [8840], "characters": "\u2288" }, + "⊂⃒": { "codepoints": [8834, 8402], "characters": "\u2282\u20D2" }, + "⊈": { "codepoints": [8840], "characters": "\u2288" }, + "⫅̸": { "codepoints": [10949, 824], "characters": "\u2AC5\u0338" }, + "⊁": { "codepoints": [8833], "characters": "\u2281" }, + "⪰̸": { "codepoints": [10928, 824], "characters": "\u2AB0\u0338" }, + "⊅": { "codepoints": [8837], "characters": "\u2285" }, + "⫆̸": { "codepoints": [10950, 824], "characters": "\u2AC6\u0338" }, + "⊉": { "codepoints": [8841], "characters": "\u2289" }, + "⊃⃒": { "codepoints": [8835, 8402], "characters": "\u2283\u20D2" }, + "⊉": { "codepoints": [8841], "characters": "\u2289" }, + "⫆̸": { "codepoints": [10950, 824], "characters": "\u2AC6\u0338" }, + "≹": { "codepoints": [8825], "characters": "\u2279" }, + "ñ": { "codepoints": [241], "characters": "\u00F1" }, + "ñ": { "codepoints": [241], "characters": "\u00F1" }, + "≸": { "codepoints": [8824], "characters": "\u2278" }, + "⋪": { "codepoints": [8938], "characters": "\u22EA" }, + "⋬": { "codepoints": [8940], "characters": "\u22EC" }, + "⋫": { "codepoints": [8939], "characters": "\u22EB" }, + "⋭": { "codepoints": [8941], "characters": "\u22ED" }, + "ν": { "codepoints": [957], "characters": "\u03BD" }, + "#": { "codepoints": [35], "characters": "\u0023" }, + "№": { "codepoints": [8470], "characters": "\u2116" }, + " ": { "codepoints": [8199], "characters": "\u2007" }, + "⊭": { "codepoints": [8877], "characters": "\u22AD" }, + "⤄": { "codepoints": [10500], "characters": "\u2904" }, + "≍⃒": { "codepoints": [8781, 8402], "characters": "\u224D\u20D2" }, + "⊬": { "codepoints": [8876], "characters": "\u22AC" }, + "≥⃒": { "codepoints": [8805, 8402], "characters": "\u2265\u20D2" }, + ">⃒": { "codepoints": [62, 8402], "characters": "\u003E\u20D2" }, + "⧞": { "codepoints": [10718], "characters": "\u29DE" }, + "⤂": { "codepoints": [10498], "characters": "\u2902" }, + "≤⃒": { "codepoints": [8804, 8402], "characters": "\u2264\u20D2" }, + "<⃒": { "codepoints": [60, 8402], "characters": "\u003C\u20D2" }, + "⊴⃒": { "codepoints": [8884, 8402], "characters": "\u22B4\u20D2" }, + "⤃": { "codepoints": [10499], "characters": "\u2903" }, + "⊵⃒": { "codepoints": [8885, 8402], "characters": "\u22B5\u20D2" }, + "∼⃒": { "codepoints": [8764, 8402], "characters": "\u223C\u20D2" }, + "⇖": { "codepoints": [8662], "characters": "\u21D6" }, + "⤣": { "codepoints": [10531], "characters": "\u2923" }, + "↖": { "codepoints": [8598], "characters": "\u2196" }, + "↖": { "codepoints": [8598], "characters": "\u2196" }, + "⤧": { "codepoints": [10535], "characters": "\u2927" }, + "Ⓢ": { "codepoints": [9416], "characters": "\u24C8" }, + "ó": { "codepoints": [243], "characters": "\u00F3" }, + "ó": { "codepoints": [243], "characters": "\u00F3" }, + "⊛": { "codepoints": [8859], "characters": "\u229B" }, + "⊚": { "codepoints": [8858], "characters": "\u229A" }, + "ô": { "codepoints": [244], "characters": "\u00F4" }, + "ô": { "codepoints": [244], "characters": "\u00F4" }, + "о": { "codepoints": [1086], "characters": "\u043E" }, + "⊝": { "codepoints": [8861], "characters": "\u229D" }, + "ő": { "codepoints": [337], "characters": "\u0151" }, + "⨸": { "codepoints": [10808], "characters": "\u2A38" }, + "⊙": { "codepoints": [8857], "characters": "\u2299" }, + "⦼": { "codepoints": [10684], "characters": "\u29BC" }, + "œ": { "codepoints": [339], "characters": "\u0153" }, + "⦿": { "codepoints": [10687], "characters": "\u29BF" }, + "𝔬": { "codepoints": [120108], "characters": "\uD835\uDD2C" }, + "˛": { "codepoints": [731], "characters": "\u02DB" }, + "ò": { "codepoints": [242], "characters": "\u00F2" }, + "ò": { "codepoints": [242], "characters": "\u00F2" }, + "⧁": { "codepoints": [10689], "characters": "\u29C1" }, + "⦵": { "codepoints": [10677], "characters": "\u29B5" }, + "Ω": { "codepoints": [937], "characters": "\u03A9" }, + "∮": { "codepoints": [8750], "characters": "\u222E" }, + "↺": { "codepoints": [8634], "characters": "\u21BA" }, + "⦾": { "codepoints": [10686], "characters": "\u29BE" }, + "⦻": { "codepoints": [10683], "characters": "\u29BB" }, + "‾": { "codepoints": [8254], "characters": "\u203E" }, + "⧀": { "codepoints": [10688], "characters": "\u29C0" }, + "ō": { "codepoints": [333], "characters": "\u014D" }, + "ω": { "codepoints": [969], "characters": "\u03C9" }, + "ο": { "codepoints": [959], "characters": "\u03BF" }, + "⦶": { "codepoints": [10678], "characters": "\u29B6" }, + "⊖": { "codepoints": [8854], "characters": "\u2296" }, + "𝕠": { "codepoints": [120160], "characters": "\uD835\uDD60" }, + "⦷": { "codepoints": [10679], "characters": "\u29B7" }, + "⦹": { "codepoints": [10681], "characters": "\u29B9" }, + "⊕": { "codepoints": [8853], "characters": "\u2295" }, + "∨": { "codepoints": [8744], "characters": "\u2228" }, + "↻": { "codepoints": [8635], "characters": "\u21BB" }, + "⩝": { "codepoints": [10845], "characters": "\u2A5D" }, + "ℴ": { "codepoints": [8500], "characters": "\u2134" }, + "ℴ": { "codepoints": [8500], "characters": "\u2134" }, + "ª": { "codepoints": [170], "characters": "\u00AA" }, + "ª": { "codepoints": [170], "characters": "\u00AA" }, + "º": { "codepoints": [186], "characters": "\u00BA" }, + "º": { "codepoints": [186], "characters": "\u00BA" }, + "⊶": { "codepoints": [8886], "characters": "\u22B6" }, + "⩖": { "codepoints": [10838], "characters": "\u2A56" }, + "⩗": { "codepoints": [10839], "characters": "\u2A57" }, + "⩛": { "codepoints": [10843], "characters": "\u2A5B" }, + "ℴ": { "codepoints": [8500], "characters": "\u2134" }, + "ø": { "codepoints": [248], "characters": "\u00F8" }, + "ø": { "codepoints": [248], "characters": "\u00F8" }, + "⊘": { "codepoints": [8856], "characters": "\u2298" }, + "õ": { "codepoints": [245], "characters": "\u00F5" }, + "õ": { "codepoints": [245], "characters": "\u00F5" }, + "⊗": { "codepoints": [8855], "characters": "\u2297" }, + "⨶": { "codepoints": [10806], "characters": "\u2A36" }, + "ö": { "codepoints": [246], "characters": "\u00F6" }, + "ö": { "codepoints": [246], "characters": "\u00F6" }, + "⌽": { "codepoints": [9021], "characters": "\u233D" }, + "∥": { "codepoints": [8741], "characters": "\u2225" }, + "¶": { "codepoints": [182], "characters": "\u00B6" }, + "¶": { "codepoints": [182], "characters": "\u00B6" }, + "∥": { "codepoints": [8741], "characters": "\u2225" }, + "⫳": { "codepoints": [10995], "characters": "\u2AF3" }, + "⫽": { "codepoints": [11005], "characters": "\u2AFD" }, + "∂": { "codepoints": [8706], "characters": "\u2202" }, + "п": { "codepoints": [1087], "characters": "\u043F" }, + "%": { "codepoints": [37], "characters": "\u0025" }, + ".": { "codepoints": [46], "characters": "\u002E" }, + "‰": { "codepoints": [8240], "characters": "\u2030" }, + "⊥": { "codepoints": [8869], "characters": "\u22A5" }, + "‱": { "codepoints": [8241], "characters": "\u2031" }, + "𝔭": { "codepoints": [120109], "characters": "\uD835\uDD2D" }, + "φ": { "codepoints": [966], "characters": "\u03C6" }, + "ϕ": { "codepoints": [981], "characters": "\u03D5" }, + "ℳ": { "codepoints": [8499], "characters": "\u2133" }, + "☎": { "codepoints": [9742], "characters": "\u260E" }, + "π": { "codepoints": [960], "characters": "\u03C0" }, + "⋔": { "codepoints": [8916], "characters": "\u22D4" }, + "ϖ": { "codepoints": [982], "characters": "\u03D6" }, + "ℏ": { "codepoints": [8463], "characters": "\u210F" }, + "ℎ": { "codepoints": [8462], "characters": "\u210E" }, + "ℏ": { "codepoints": [8463], "characters": "\u210F" }, + "+": { "codepoints": [43], "characters": "\u002B" }, + "⨣": { "codepoints": [10787], "characters": "\u2A23" }, + "⊞": { "codepoints": [8862], "characters": "\u229E" }, + "⨢": { "codepoints": [10786], "characters": "\u2A22" }, + "∔": { "codepoints": [8724], "characters": "\u2214" }, + "⨥": { "codepoints": [10789], "characters": "\u2A25" }, + "⩲": { "codepoints": [10866], "characters": "\u2A72" }, + "±": { "codepoints": [177], "characters": "\u00B1" }, + "±": { "codepoints": [177], "characters": "\u00B1" }, + "⨦": { "codepoints": [10790], "characters": "\u2A26" }, + "⨧": { "codepoints": [10791], "characters": "\u2A27" }, + "±": { "codepoints": [177], "characters": "\u00B1" }, + "⨕": { "codepoints": [10773], "characters": "\u2A15" }, + "𝕡": { "codepoints": [120161], "characters": "\uD835\uDD61" }, + "£": { "codepoints": [163], "characters": "\u00A3" }, + "£": { "codepoints": [163], "characters": "\u00A3" }, + "≺": { "codepoints": [8826], "characters": "\u227A" }, + "⪳": { "codepoints": [10931], "characters": "\u2AB3" }, + "⪷": { "codepoints": [10935], "characters": "\u2AB7" }, + "≼": { "codepoints": [8828], "characters": "\u227C" }, + "⪯": { "codepoints": [10927], "characters": "\u2AAF" }, + "≺": { "codepoints": [8826], "characters": "\u227A" }, + "⪷": { "codepoints": [10935], "characters": "\u2AB7" }, + "≼": { "codepoints": [8828], "characters": "\u227C" }, + "⪯": { "codepoints": [10927], "characters": "\u2AAF" }, + "⪹": { "codepoints": [10937], "characters": "\u2AB9" }, + "⪵": { "codepoints": [10933], "characters": "\u2AB5" }, + "⋨": { "codepoints": [8936], "characters": "\u22E8" }, + "≾": { "codepoints": [8830], "characters": "\u227E" }, + "′": { "codepoints": [8242], "characters": "\u2032" }, + "ℙ": { "codepoints": [8473], "characters": "\u2119" }, + "⪵": { "codepoints": [10933], "characters": "\u2AB5" }, + "⪹": { "codepoints": [10937], "characters": "\u2AB9" }, + "⋨": { "codepoints": [8936], "characters": "\u22E8" }, + "∏": { "codepoints": [8719], "characters": "\u220F" }, + "⌮": { "codepoints": [9006], "characters": "\u232E" }, + "⌒": { "codepoints": [8978], "characters": "\u2312" }, + "⌓": { "codepoints": [8979], "characters": "\u2313" }, + "∝": { "codepoints": [8733], "characters": "\u221D" }, + "∝": { "codepoints": [8733], "characters": "\u221D" }, + "≾": { "codepoints": [8830], "characters": "\u227E" }, + "⊰": { "codepoints": [8880], "characters": "\u22B0" }, + "𝓅": { "codepoints": [120005], "characters": "\uD835\uDCC5" }, + "ψ": { "codepoints": [968], "characters": "\u03C8" }, + " ": { "codepoints": [8200], "characters": "\u2008" }, + "𝔮": { "codepoints": [120110], "characters": "\uD835\uDD2E" }, + "⨌": { "codepoints": [10764], "characters": "\u2A0C" }, + "𝕢": { "codepoints": [120162], "characters": "\uD835\uDD62" }, + "⁗": { "codepoints": [8279], "characters": "\u2057" }, + "𝓆": { "codepoints": [120006], "characters": "\uD835\uDCC6" }, + "ℍ": { "codepoints": [8461], "characters": "\u210D" }, + "⨖": { "codepoints": [10774], "characters": "\u2A16" }, + "?": { "codepoints": [63], "characters": "\u003F" }, + "≟": { "codepoints": [8799], "characters": "\u225F" }, + """: { "codepoints": [34], "characters": "\u0022" }, + """: { "codepoints": [34], "characters": "\u0022" }, + "⇛": { "codepoints": [8667], "characters": "\u21DB" }, + "⇒": { "codepoints": [8658], "characters": "\u21D2" }, + "⤜": { "codepoints": [10524], "characters": "\u291C" }, + "⤏": { "codepoints": [10511], "characters": "\u290F" }, + "⥤": { "codepoints": [10596], "characters": "\u2964" }, + "∽̱": { "codepoints": [8765, 817], "characters": "\u223D\u0331" }, + "ŕ": { "codepoints": [341], "characters": "\u0155" }, + "√": { "codepoints": [8730], "characters": "\u221A" }, + "⦳": { "codepoints": [10675], "characters": "\u29B3" }, + "⟩": { "codepoints": [10217], "characters": "\u27E9" }, + "⦒": { "codepoints": [10642], "characters": "\u2992" }, + "⦥": { "codepoints": [10661], "characters": "\u29A5" }, + "⟩": { "codepoints": [10217], "characters": "\u27E9" }, + "»": { "codepoints": [187], "characters": "\u00BB" }, + "»": { "codepoints": [187], "characters": "\u00BB" }, + "→": { "codepoints": [8594], "characters": "\u2192" }, + "⥵": { "codepoints": [10613], "characters": "\u2975" }, + "⇥": { "codepoints": [8677], "characters": "\u21E5" }, + "⤠": { "codepoints": [10528], "characters": "\u2920" }, + "⤳": { "codepoints": [10547], "characters": "\u2933" }, + "⤞": { "codepoints": [10526], "characters": "\u291E" }, + "↪": { "codepoints": [8618], "characters": "\u21AA" }, + "↬": { "codepoints": [8620], "characters": "\u21AC" }, + "⥅": { "codepoints": [10565], "characters": "\u2945" }, + "⥴": { "codepoints": [10612], "characters": "\u2974" }, + "↣": { "codepoints": [8611], "characters": "\u21A3" }, + "↝": { "codepoints": [8605], "characters": "\u219D" }, + "⤚": { "codepoints": [10522], "characters": "\u291A" }, + "∶": { "codepoints": [8758], "characters": "\u2236" }, + "ℚ": { "codepoints": [8474], "characters": "\u211A" }, + "⤍": { "codepoints": [10509], "characters": "\u290D" }, + "❳": { "codepoints": [10099], "characters": "\u2773" }, + "}": { "codepoints": [125], "characters": "\u007D" }, + "]": { "codepoints": [93], "characters": "\u005D" }, + "⦌": { "codepoints": [10636], "characters": "\u298C" }, + "⦎": { "codepoints": [10638], "characters": "\u298E" }, + "⦐": { "codepoints": [10640], "characters": "\u2990" }, + "ř": { "codepoints": [345], "characters": "\u0159" }, + "ŗ": { "codepoints": [343], "characters": "\u0157" }, + "⌉": { "codepoints": [8969], "characters": "\u2309" }, + "}": { "codepoints": [125], "characters": "\u007D" }, + "р": { "codepoints": [1088], "characters": "\u0440" }, + "⤷": { "codepoints": [10551], "characters": "\u2937" }, + "⥩": { "codepoints": [10601], "characters": "\u2969" }, + "”": { "codepoints": [8221], "characters": "\u201D" }, + "”": { "codepoints": [8221], "characters": "\u201D" }, + "↳": { "codepoints": [8627], "characters": "\u21B3" }, + "ℜ": { "codepoints": [8476], "characters": "\u211C" }, + "ℛ": { "codepoints": [8475], "characters": "\u211B" }, + "ℜ": { "codepoints": [8476], "characters": "\u211C" }, + "ℝ": { "codepoints": [8477], "characters": "\u211D" }, + "▭": { "codepoints": [9645], "characters": "\u25AD" }, + "®": { "codepoints": [174], "characters": "\u00AE" }, + "®": { "codepoints": [174], "characters": "\u00AE" }, + "⥽": { "codepoints": [10621], "characters": "\u297D" }, + "⌋": { "codepoints": [8971], "characters": "\u230B" }, + "𝔯": { "codepoints": [120111], "characters": "\uD835\uDD2F" }, + "⇁": { "codepoints": [8641], "characters": "\u21C1" }, + "⇀": { "codepoints": [8640], "characters": "\u21C0" }, + "⥬": { "codepoints": [10604], "characters": "\u296C" }, + "ρ": { "codepoints": [961], "characters": "\u03C1" }, + "ϱ": { "codepoints": [1009], "characters": "\u03F1" }, + "→": { "codepoints": [8594], "characters": "\u2192" }, + "↣": { "codepoints": [8611], "characters": "\u21A3" }, + "⇁": { "codepoints": [8641], "characters": "\u21C1" }, + "⇀": { "codepoints": [8640], "characters": "\u21C0" }, + "⇄": { "codepoints": [8644], "characters": "\u21C4" }, + "⇌": { "codepoints": [8652], "characters": "\u21CC" }, + "⇉": { "codepoints": [8649], "characters": "\u21C9" }, + "↝": { "codepoints": [8605], "characters": "\u219D" }, + "⋌": { "codepoints": [8908], "characters": "\u22CC" }, + "˚": { "codepoints": [730], "characters": "\u02DA" }, + "≓": { "codepoints": [8787], "characters": "\u2253" }, + "⇄": { "codepoints": [8644], "characters": "\u21C4" }, + "⇌": { "codepoints": [8652], "characters": "\u21CC" }, + "‏": { "codepoints": [8207], "characters": "\u200F" }, + "⎱": { "codepoints": [9137], "characters": "\u23B1" }, + "⎱": { "codepoints": [9137], "characters": "\u23B1" }, + "⫮": { "codepoints": [10990], "characters": "\u2AEE" }, + "⟭": { "codepoints": [10221], "characters": "\u27ED" }, + "⇾": { "codepoints": [8702], "characters": "\u21FE" }, + "⟧": { "codepoints": [10215], "characters": "\u27E7" }, + "⦆": { "codepoints": [10630], "characters": "\u2986" }, + "𝕣": { "codepoints": [120163], "characters": "\uD835\uDD63" }, + "⨮": { "codepoints": [10798], "characters": "\u2A2E" }, + "⨵": { "codepoints": [10805], "characters": "\u2A35" }, + ")": { "codepoints": [41], "characters": "\u0029" }, + "⦔": { "codepoints": [10644], "characters": "\u2994" }, + "⨒": { "codepoints": [10770], "characters": "\u2A12" }, + "⇉": { "codepoints": [8649], "characters": "\u21C9" }, + "›": { "codepoints": [8250], "characters": "\u203A" }, + "𝓇": { "codepoints": [120007], "characters": "\uD835\uDCC7" }, + "↱": { "codepoints": [8625], "characters": "\u21B1" }, + "]": { "codepoints": [93], "characters": "\u005D" }, + "’": { "codepoints": [8217], "characters": "\u2019" }, + "’": { "codepoints": [8217], "characters": "\u2019" }, + "⋌": { "codepoints": [8908], "characters": "\u22CC" }, + "⋊": { "codepoints": [8906], "characters": "\u22CA" }, + "▹": { "codepoints": [9657], "characters": "\u25B9" }, + "⊵": { "codepoints": [8885], "characters": "\u22B5" }, + "▸": { "codepoints": [9656], "characters": "\u25B8" }, + "⧎": { "codepoints": [10702], "characters": "\u29CE" }, + "⥨": { "codepoints": [10600], "characters": "\u2968" }, + "℞": { "codepoints": [8478], "characters": "\u211E" }, + "ś": { "codepoints": [347], "characters": "\u015B" }, + "‚": { "codepoints": [8218], "characters": "\u201A" }, + "≻": { "codepoints": [8827], "characters": "\u227B" }, + "⪴": { "codepoints": [10932], "characters": "\u2AB4" }, + "⪸": { "codepoints": [10936], "characters": "\u2AB8" }, + "š": { "codepoints": [353], "characters": "\u0161" }, + "≽": { "codepoints": [8829], "characters": "\u227D" }, + "⪰": { "codepoints": [10928], "characters": "\u2AB0" }, + "ş": { "codepoints": [351], "characters": "\u015F" }, + "ŝ": { "codepoints": [349], "characters": "\u015D" }, + "⪶": { "codepoints": [10934], "characters": "\u2AB6" }, + "⪺": { "codepoints": [10938], "characters": "\u2ABA" }, + "⋩": { "codepoints": [8937], "characters": "\u22E9" }, + "⨓": { "codepoints": [10771], "characters": "\u2A13" }, + "≿": { "codepoints": [8831], "characters": "\u227F" }, + "с": { "codepoints": [1089], "characters": "\u0441" }, + "⋅": { "codepoints": [8901], "characters": "\u22C5" }, + "⊡": { "codepoints": [8865], "characters": "\u22A1" }, + "⩦": { "codepoints": [10854], "characters": "\u2A66" }, + "⇘": { "codepoints": [8664], "characters": "\u21D8" }, + "⤥": { "codepoints": [10533], "characters": "\u2925" }, + "↘": { "codepoints": [8600], "characters": "\u2198" }, + "↘": { "codepoints": [8600], "characters": "\u2198" }, + "§": { "codepoints": [167], "characters": "\u00A7" }, + "§": { "codepoints": [167], "characters": "\u00A7" }, + ";": { "codepoints": [59], "characters": "\u003B" }, + "⤩": { "codepoints": [10537], "characters": "\u2929" }, + "∖": { "codepoints": [8726], "characters": "\u2216" }, + "∖": { "codepoints": [8726], "characters": "\u2216" }, + "✶": { "codepoints": [10038], "characters": "\u2736" }, + "𝔰": { "codepoints": [120112], "characters": "\uD835\uDD30" }, + "⌢": { "codepoints": [8994], "characters": "\u2322" }, + "♯": { "codepoints": [9839], "characters": "\u266F" }, + "щ": { "codepoints": [1097], "characters": "\u0449" }, + "ш": { "codepoints": [1096], "characters": "\u0448" }, + "∣": { "codepoints": [8739], "characters": "\u2223" }, + "∥": { "codepoints": [8741], "characters": "\u2225" }, + "­": { "codepoints": [173], "characters": "\u00AD" }, + "­": { "codepoints": [173], "characters": "\u00AD" }, + "σ": { "codepoints": [963], "characters": "\u03C3" }, + "ς": { "codepoints": [962], "characters": "\u03C2" }, + "ς": { "codepoints": [962], "characters": "\u03C2" }, + "∼": { "codepoints": [8764], "characters": "\u223C" }, + "⩪": { "codepoints": [10858], "characters": "\u2A6A" }, + "≃": { "codepoints": [8771], "characters": "\u2243" }, + "≃": { "codepoints": [8771], "characters": "\u2243" }, + "⪞": { "codepoints": [10910], "characters": "\u2A9E" }, + "⪠": { "codepoints": [10912], "characters": "\u2AA0" }, + "⪝": { "codepoints": [10909], "characters": "\u2A9D" }, + "⪟": { "codepoints": [10911], "characters": "\u2A9F" }, + "≆": { "codepoints": [8774], "characters": "\u2246" }, + "⨤": { "codepoints": [10788], "characters": "\u2A24" }, + "⥲": { "codepoints": [10610], "characters": "\u2972" }, + "←": { "codepoints": [8592], "characters": "\u2190" }, + "∖": { "codepoints": [8726], "characters": "\u2216" }, + "⨳": { "codepoints": [10803], "characters": "\u2A33" }, + "⧤": { "codepoints": [10724], "characters": "\u29E4" }, + "∣": { "codepoints": [8739], "characters": "\u2223" }, + "⌣": { "codepoints": [8995], "characters": "\u2323" }, + "⪪": { "codepoints": [10922], "characters": "\u2AAA" }, + "⪬": { "codepoints": [10924], "characters": "\u2AAC" }, + "⪬︀": { "codepoints": [10924, 65024], "characters": "\u2AAC\uFE00" }, + "ь": { "codepoints": [1100], "characters": "\u044C" }, + "/": { "codepoints": [47], "characters": "\u002F" }, + "⧄": { "codepoints": [10692], "characters": "\u29C4" }, + "⌿": { "codepoints": [9023], "characters": "\u233F" }, + "𝕤": { "codepoints": [120164], "characters": "\uD835\uDD64" }, + "♠": { "codepoints": [9824], "characters": "\u2660" }, + "♠": { "codepoints": [9824], "characters": "\u2660" }, + "∥": { "codepoints": [8741], "characters": "\u2225" }, + "⊓": { "codepoints": [8851], "characters": "\u2293" }, + "⊓︀": { "codepoints": [8851, 65024], "characters": "\u2293\uFE00" }, + "⊔": { "codepoints": [8852], "characters": "\u2294" }, + "⊔︀": { "codepoints": [8852, 65024], "characters": "\u2294\uFE00" }, + "⊏": { "codepoints": [8847], "characters": "\u228F" }, + "⊑": { "codepoints": [8849], "characters": "\u2291" }, + "⊏": { "codepoints": [8847], "characters": "\u228F" }, + "⊑": { "codepoints": [8849], "characters": "\u2291" }, + "⊐": { "codepoints": [8848], "characters": "\u2290" }, + "⊒": { "codepoints": [8850], "characters": "\u2292" }, + "⊐": { "codepoints": [8848], "characters": "\u2290" }, + "⊒": { "codepoints": [8850], "characters": "\u2292" }, + "□": { "codepoints": [9633], "characters": "\u25A1" }, + "□": { "codepoints": [9633], "characters": "\u25A1" }, + "▪": { "codepoints": [9642], "characters": "\u25AA" }, + "▪": { "codepoints": [9642], "characters": "\u25AA" }, + "→": { "codepoints": [8594], "characters": "\u2192" }, + "𝓈": { "codepoints": [120008], "characters": "\uD835\uDCC8" }, + "∖": { "codepoints": [8726], "characters": "\u2216" }, + "⌣": { "codepoints": [8995], "characters": "\u2323" }, + "⋆": { "codepoints": [8902], "characters": "\u22C6" }, + "☆": { "codepoints": [9734], "characters": "\u2606" }, + "★": { "codepoints": [9733], "characters": "\u2605" }, + "ϵ": { "codepoints": [1013], "characters": "\u03F5" }, + "ϕ": { "codepoints": [981], "characters": "\u03D5" }, + "¯": { "codepoints": [175], "characters": "\u00AF" }, + "⊂": { "codepoints": [8834], "characters": "\u2282" }, + "⫅": { "codepoints": [10949], "characters": "\u2AC5" }, + "⪽": { "codepoints": [10941], "characters": "\u2ABD" }, + "⊆": { "codepoints": [8838], "characters": "\u2286" }, + "⫃": { "codepoints": [10947], "characters": "\u2AC3" }, + "⫁": { "codepoints": [10945], "characters": "\u2AC1" }, + "⫋": { "codepoints": [10955], "characters": "\u2ACB" }, + "⊊": { "codepoints": [8842], "characters": "\u228A" }, + "⪿": { "codepoints": [10943], "characters": "\u2ABF" }, + "⥹": { "codepoints": [10617], "characters": "\u2979" }, + "⊂": { "codepoints": [8834], "characters": "\u2282" }, + "⊆": { "codepoints": [8838], "characters": "\u2286" }, + "⫅": { "codepoints": [10949], "characters": "\u2AC5" }, + "⊊": { "codepoints": [8842], "characters": "\u228A" }, + "⫋": { "codepoints": [10955], "characters": "\u2ACB" }, + "⫇": { "codepoints": [10951], "characters": "\u2AC7" }, + "⫕": { "codepoints": [10965], "characters": "\u2AD5" }, + "⫓": { "codepoints": [10963], "characters": "\u2AD3" }, + "≻": { "codepoints": [8827], "characters": "\u227B" }, + "⪸": { "codepoints": [10936], "characters": "\u2AB8" }, + "≽": { "codepoints": [8829], "characters": "\u227D" }, + "⪰": { "codepoints": [10928], "characters": "\u2AB0" }, + "⪺": { "codepoints": [10938], "characters": "\u2ABA" }, + "⪶": { "codepoints": [10934], "characters": "\u2AB6" }, + "⋩": { "codepoints": [8937], "characters": "\u22E9" }, + "≿": { "codepoints": [8831], "characters": "\u227F" }, + "∑": { "codepoints": [8721], "characters": "\u2211" }, + "♪": { "codepoints": [9834], "characters": "\u266A" }, + "¹": { "codepoints": [185], "characters": "\u00B9" }, + "¹": { "codepoints": [185], "characters": "\u00B9" }, + "²": { "codepoints": [178], "characters": "\u00B2" }, + "²": { "codepoints": [178], "characters": "\u00B2" }, + "³": { "codepoints": [179], "characters": "\u00B3" }, + "³": { "codepoints": [179], "characters": "\u00B3" }, + "⊃": { "codepoints": [8835], "characters": "\u2283" }, + "⫆": { "codepoints": [10950], "characters": "\u2AC6" }, + "⪾": { "codepoints": [10942], "characters": "\u2ABE" }, + "⫘": { "codepoints": [10968], "characters": "\u2AD8" }, + "⊇": { "codepoints": [8839], "characters": "\u2287" }, + "⫄": { "codepoints": [10948], "characters": "\u2AC4" }, + "⟉": { "codepoints": [10185], "characters": "\u27C9" }, + "⫗": { "codepoints": [10967], "characters": "\u2AD7" }, + "⥻": { "codepoints": [10619], "characters": "\u297B" }, + "⫂": { "codepoints": [10946], "characters": "\u2AC2" }, + "⫌": { "codepoints": [10956], "characters": "\u2ACC" }, + "⊋": { "codepoints": [8843], "characters": "\u228B" }, + "⫀": { "codepoints": [10944], "characters": "\u2AC0" }, + "⊃": { "codepoints": [8835], "characters": "\u2283" }, + "⊇": { "codepoints": [8839], "characters": "\u2287" }, + "⫆": { "codepoints": [10950], "characters": "\u2AC6" }, + "⊋": { "codepoints": [8843], "characters": "\u228B" }, + "⫌": { "codepoints": [10956], "characters": "\u2ACC" }, + "⫈": { "codepoints": [10952], "characters": "\u2AC8" }, + "⫔": { "codepoints": [10964], "characters": "\u2AD4" }, + "⫖": { "codepoints": [10966], "characters": "\u2AD6" }, + "⇙": { "codepoints": [8665], "characters": "\u21D9" }, + "⤦": { "codepoints": [10534], "characters": "\u2926" }, + "↙": { "codepoints": [8601], "characters": "\u2199" }, + "↙": { "codepoints": [8601], "characters": "\u2199" }, + "⤪": { "codepoints": [10538], "characters": "\u292A" }, + "ß": { "codepoints": [223], "characters": "\u00DF" }, + "ß": { "codepoints": [223], "characters": "\u00DF" }, + "⌖": { "codepoints": [8982], "characters": "\u2316" }, + "τ": { "codepoints": [964], "characters": "\u03C4" }, + "⎴": { "codepoints": [9140], "characters": "\u23B4" }, + "ť": { "codepoints": [357], "characters": "\u0165" }, + "ţ": { "codepoints": [355], "characters": "\u0163" }, + "т": { "codepoints": [1090], "characters": "\u0442" }, + "⃛": { "codepoints": [8411], "characters": "\u20DB" }, + "⌕": { "codepoints": [8981], "characters": "\u2315" }, + "𝔱": { "codepoints": [120113], "characters": "\uD835\uDD31" }, + "∴": { "codepoints": [8756], "characters": "\u2234" }, + "∴": { "codepoints": [8756], "characters": "\u2234" }, + "θ": { "codepoints": [952], "characters": "\u03B8" }, + "ϑ": { "codepoints": [977], "characters": "\u03D1" }, + "ϑ": { "codepoints": [977], "characters": "\u03D1" }, + "≈": { "codepoints": [8776], "characters": "\u2248" }, + "∼": { "codepoints": [8764], "characters": "\u223C" }, + " ": { "codepoints": [8201], "characters": "\u2009" }, + "≈": { "codepoints": [8776], "characters": "\u2248" }, + "∼": { "codepoints": [8764], "characters": "\u223C" }, + "þ": { "codepoints": [254], "characters": "\u00FE" }, + "þ": { "codepoints": [254], "characters": "\u00FE" }, + "˜": { "codepoints": [732], "characters": "\u02DC" }, + "×": { "codepoints": [215], "characters": "\u00D7" }, + "×": { "codepoints": [215], "characters": "\u00D7" }, + "⊠": { "codepoints": [8864], "characters": "\u22A0" }, + "⨱": { "codepoints": [10801], "characters": "\u2A31" }, + "⨰": { "codepoints": [10800], "characters": "\u2A30" }, + "∭": { "codepoints": [8749], "characters": "\u222D" }, + "⤨": { "codepoints": [10536], "characters": "\u2928" }, + "⊤": { "codepoints": [8868], "characters": "\u22A4" }, + "⌶": { "codepoints": [9014], "characters": "\u2336" }, + "⫱": { "codepoints": [10993], "characters": "\u2AF1" }, + "𝕥": { "codepoints": [120165], "characters": "\uD835\uDD65" }, + "⫚": { "codepoints": [10970], "characters": "\u2ADA" }, + "⤩": { "codepoints": [10537], "characters": "\u2929" }, + "‴": { "codepoints": [8244], "characters": "\u2034" }, + "™": { "codepoints": [8482], "characters": "\u2122" }, + "▵": { "codepoints": [9653], "characters": "\u25B5" }, + "▿": { "codepoints": [9663], "characters": "\u25BF" }, + "◃": { "codepoints": [9667], "characters": "\u25C3" }, + "⊴": { "codepoints": [8884], "characters": "\u22B4" }, + "≜": { "codepoints": [8796], "characters": "\u225C" }, + "▹": { "codepoints": [9657], "characters": "\u25B9" }, + "⊵": { "codepoints": [8885], "characters": "\u22B5" }, + "◬": { "codepoints": [9708], "characters": "\u25EC" }, + "≜": { "codepoints": [8796], "characters": "\u225C" }, + "⨺": { "codepoints": [10810], "characters": "\u2A3A" }, + "⨹": { "codepoints": [10809], "characters": "\u2A39" }, + "⧍": { "codepoints": [10701], "characters": "\u29CD" }, + "⨻": { "codepoints": [10811], "characters": "\u2A3B" }, + "⏢": { "codepoints": [9186], "characters": "\u23E2" }, + "𝓉": { "codepoints": [120009], "characters": "\uD835\uDCC9" }, + "ц": { "codepoints": [1094], "characters": "\u0446" }, + "ћ": { "codepoints": [1115], "characters": "\u045B" }, + "ŧ": { "codepoints": [359], "characters": "\u0167" }, + "≬": { "codepoints": [8812], "characters": "\u226C" }, + "↞": { "codepoints": [8606], "characters": "\u219E" }, + "↠": { "codepoints": [8608], "characters": "\u21A0" }, + "⇑": { "codepoints": [8657], "characters": "\u21D1" }, + "⥣": { "codepoints": [10595], "characters": "\u2963" }, + "ú": { "codepoints": [250], "characters": "\u00FA" }, + "ú": { "codepoints": [250], "characters": "\u00FA" }, + "↑": { "codepoints": [8593], "characters": "\u2191" }, + "ў": { "codepoints": [1118], "characters": "\u045E" }, + "ŭ": { "codepoints": [365], "characters": "\u016D" }, + "û": { "codepoints": [251], "characters": "\u00FB" }, + "û": { "codepoints": [251], "characters": "\u00FB" }, + "у": { "codepoints": [1091], "characters": "\u0443" }, + "⇅": { "codepoints": [8645], "characters": "\u21C5" }, + "ű": { "codepoints": [369], "characters": "\u0171" }, + "⥮": { "codepoints": [10606], "characters": "\u296E" }, + "⥾": { "codepoints": [10622], "characters": "\u297E" }, + "𝔲": { "codepoints": [120114], "characters": "\uD835\uDD32" }, + "ù": { "codepoints": [249], "characters": "\u00F9" }, + "ù": { "codepoints": [249], "characters": "\u00F9" }, + "↿": { "codepoints": [8639], "characters": "\u21BF" }, + "↾": { "codepoints": [8638], "characters": "\u21BE" }, + "▀": { "codepoints": [9600], "characters": "\u2580" }, + "⌜": { "codepoints": [8988], "characters": "\u231C" }, + "⌜": { "codepoints": [8988], "characters": "\u231C" }, + "⌏": { "codepoints": [8975], "characters": "\u230F" }, + "◸": { "codepoints": [9720], "characters": "\u25F8" }, + "ū": { "codepoints": [363], "characters": "\u016B" }, + "¨": { "codepoints": [168], "characters": "\u00A8" }, + "¨": { "codepoints": [168], "characters": "\u00A8" }, + "ų": { "codepoints": [371], "characters": "\u0173" }, + "𝕦": { "codepoints": [120166], "characters": "\uD835\uDD66" }, + "↑": { "codepoints": [8593], "characters": "\u2191" }, + "↕": { "codepoints": [8597], "characters": "\u2195" }, + "↿": { "codepoints": [8639], "characters": "\u21BF" }, + "↾": { "codepoints": [8638], "characters": "\u21BE" }, + "⊎": { "codepoints": [8846], "characters": "\u228E" }, + "υ": { "codepoints": [965], "characters": "\u03C5" }, + "ϒ": { "codepoints": [978], "characters": "\u03D2" }, + "υ": { "codepoints": [965], "characters": "\u03C5" }, + "⇈": { "codepoints": [8648], "characters": "\u21C8" }, + "⌝": { "codepoints": [8989], "characters": "\u231D" }, + "⌝": { "codepoints": [8989], "characters": "\u231D" }, + "⌎": { "codepoints": [8974], "characters": "\u230E" }, + "ů": { "codepoints": [367], "characters": "\u016F" }, + "◹": { "codepoints": [9721], "characters": "\u25F9" }, + "𝓊": { "codepoints": [120010], "characters": "\uD835\uDCCA" }, + "⋰": { "codepoints": [8944], "characters": "\u22F0" }, + "ũ": { "codepoints": [361], "characters": "\u0169" }, + "▵": { "codepoints": [9653], "characters": "\u25B5" }, + "▴": { "codepoints": [9652], "characters": "\u25B4" }, + "⇈": { "codepoints": [8648], "characters": "\u21C8" }, + "ü": { "codepoints": [252], "characters": "\u00FC" }, + "ü": { "codepoints": [252], "characters": "\u00FC" }, + "⦧": { "codepoints": [10663], "characters": "\u29A7" }, + "⇕": { "codepoints": [8661], "characters": "\u21D5" }, + "⫨": { "codepoints": [10984], "characters": "\u2AE8" }, + "⫩": { "codepoints": [10985], "characters": "\u2AE9" }, + "⊨": { "codepoints": [8872], "characters": "\u22A8" }, + "⦜": { "codepoints": [10652], "characters": "\u299C" }, + "ϵ": { "codepoints": [1013], "characters": "\u03F5" }, + "ϰ": { "codepoints": [1008], "characters": "\u03F0" }, + "∅": { "codepoints": [8709], "characters": "\u2205" }, + "ϕ": { "codepoints": [981], "characters": "\u03D5" }, + "ϖ": { "codepoints": [982], "characters": "\u03D6" }, + "∝": { "codepoints": [8733], "characters": "\u221D" }, + "↕": { "codepoints": [8597], "characters": "\u2195" }, + "ϱ": { "codepoints": [1009], "characters": "\u03F1" }, + "ς": { "codepoints": [962], "characters": "\u03C2" }, + "⊊︀": { "codepoints": [8842, 65024], "characters": "\u228A\uFE00" }, + "⫋︀": { "codepoints": [10955, 65024], "characters": "\u2ACB\uFE00" }, + "⊋︀": { "codepoints": [8843, 65024], "characters": "\u228B\uFE00" }, + "⫌︀": { "codepoints": [10956, 65024], "characters": "\u2ACC\uFE00" }, + "ϑ": { "codepoints": [977], "characters": "\u03D1" }, + "⊲": { "codepoints": [8882], "characters": "\u22B2" }, + "⊳": { "codepoints": [8883], "characters": "\u22B3" }, + "в": { "codepoints": [1074], "characters": "\u0432" }, + "⊢": { "codepoints": [8866], "characters": "\u22A2" }, + "∨": { "codepoints": [8744], "characters": "\u2228" }, + "⊻": { "codepoints": [8891], "characters": "\u22BB" }, + "≚": { "codepoints": [8794], "characters": "\u225A" }, + "⋮": { "codepoints": [8942], "characters": "\u22EE" }, + "|": { "codepoints": [124], "characters": "\u007C" }, + "|": { "codepoints": [124], "characters": "\u007C" }, + "𝔳": { "codepoints": [120115], "characters": "\uD835\uDD33" }, + "⊲": { "codepoints": [8882], "characters": "\u22B2" }, + "⊂⃒": { "codepoints": [8834, 8402], "characters": "\u2282\u20D2" }, + "⊃⃒": { "codepoints": [8835, 8402], "characters": "\u2283\u20D2" }, + "𝕧": { "codepoints": [120167], "characters": "\uD835\uDD67" }, + "∝": { "codepoints": [8733], "characters": "\u221D" }, + "⊳": { "codepoints": [8883], "characters": "\u22B3" }, + "𝓋": { "codepoints": [120011], "characters": "\uD835\uDCCB" }, + "⫋︀": { "codepoints": [10955, 65024], "characters": "\u2ACB\uFE00" }, + "⊊︀": { "codepoints": [8842, 65024], "characters": "\u228A\uFE00" }, + "⫌︀": { "codepoints": [10956, 65024], "characters": "\u2ACC\uFE00" }, + "⊋︀": { "codepoints": [8843, 65024], "characters": "\u228B\uFE00" }, + "⦚": { "codepoints": [10650], "characters": "\u299A" }, + "ŵ": { "codepoints": [373], "characters": "\u0175" }, + "⩟": { "codepoints": [10847], "characters": "\u2A5F" }, + "∧": { "codepoints": [8743], "characters": "\u2227" }, + "≙": { "codepoints": [8793], "characters": "\u2259" }, + "℘": { "codepoints": [8472], "characters": "\u2118" }, + "𝔴": { "codepoints": [120116], "characters": "\uD835\uDD34" }, + "𝕨": { "codepoints": [120168], "characters": "\uD835\uDD68" }, + "℘": { "codepoints": [8472], "characters": "\u2118" }, + "≀": { "codepoints": [8768], "characters": "\u2240" }, + "≀": { "codepoints": [8768], "characters": "\u2240" }, + "𝓌": { "codepoints": [120012], "characters": "\uD835\uDCCC" }, + "⋂": { "codepoints": [8898], "characters": "\u22C2" }, + "◯": { "codepoints": [9711], "characters": "\u25EF" }, + "⋃": { "codepoints": [8899], "characters": "\u22C3" }, + "▽": { "codepoints": [9661], "characters": "\u25BD" }, + "𝔵": { "codepoints": [120117], "characters": "\uD835\uDD35" }, + "⟺": { "codepoints": [10234], "characters": "\u27FA" }, + "⟷": { "codepoints": [10231], "characters": "\u27F7" }, + "ξ": { "codepoints": [958], "characters": "\u03BE" }, + "⟸": { "codepoints": [10232], "characters": "\u27F8" }, + "⟵": { "codepoints": [10229], "characters": "\u27F5" }, + "⟼": { "codepoints": [10236], "characters": "\u27FC" }, + "⋻": { "codepoints": [8955], "characters": "\u22FB" }, + "⨀": { "codepoints": [10752], "characters": "\u2A00" }, + "𝕩": { "codepoints": [120169], "characters": "\uD835\uDD69" }, + "⨁": { "codepoints": [10753], "characters": "\u2A01" }, + "⨂": { "codepoints": [10754], "characters": "\u2A02" }, + "⟹": { "codepoints": [10233], "characters": "\u27F9" }, + "⟶": { "codepoints": [10230], "characters": "\u27F6" }, + "𝓍": { "codepoints": [120013], "characters": "\uD835\uDCCD" }, + "⨆": { "codepoints": [10758], "characters": "\u2A06" }, + "⨄": { "codepoints": [10756], "characters": "\u2A04" }, + "△": { "codepoints": [9651], "characters": "\u25B3" }, + "⋁": { "codepoints": [8897], "characters": "\u22C1" }, + "⋀": { "codepoints": [8896], "characters": "\u22C0" }, + "ý": { "codepoints": [253], "characters": "\u00FD" }, + "ý": { "codepoints": [253], "characters": "\u00FD" }, + "я": { "codepoints": [1103], "characters": "\u044F" }, + "ŷ": { "codepoints": [375], "characters": "\u0177" }, + "ы": { "codepoints": [1099], "characters": "\u044B" }, + "¥": { "codepoints": [165], "characters": "\u00A5" }, + "¥": { "codepoints": [165], "characters": "\u00A5" }, + "𝔶": { "codepoints": [120118], "characters": "\uD835\uDD36" }, + "ї": { "codepoints": [1111], "characters": "\u0457" }, + "𝕪": { "codepoints": [120170], "characters": "\uD835\uDD6A" }, + "𝓎": { "codepoints": [120014], "characters": "\uD835\uDCCE" }, + "ю": { "codepoints": [1102], "characters": "\u044E" }, + "ÿ": { "codepoints": [255], "characters": "\u00FF" }, + "ÿ": { "codepoints": [255], "characters": "\u00FF" }, + "ź": { "codepoints": [378], "characters": "\u017A" }, + "ž": { "codepoints": [382], "characters": "\u017E" }, + "з": { "codepoints": [1079], "characters": "\u0437" }, + "ż": { "codepoints": [380], "characters": "\u017C" }, + "ℨ": { "codepoints": [8488], "characters": "\u2128" }, + "ζ": { "codepoints": [950], "characters": "\u03B6" }, + "𝔷": { "codepoints": [120119], "characters": "\uD835\uDD37" }, + "ж": { "codepoints": [1078], "characters": "\u0436" }, + "⇝": { "codepoints": [8669], "characters": "\u21DD" }, + "𝕫": { "codepoints": [120171], "characters": "\uD835\uDD6B" }, + "𝓏": { "codepoints": [120015], "characters": "\uD835\uDCCF" }, + "‍": { "codepoints": [8205], "characters": "\u200D" }, + "‌": { "codepoints": [8204], "characters": "\u200C" } +} diff --git a/enums.nim b/enums.nim index e95ca6ac..5b765d01 100644 --- a/enums.nim +++ b/enums.nim @@ -1,7 +1,19 @@ type NodeType* = enum - NODE_UNKNOWN, NODE_ELEMENT, NODE_TEXT, NODE_COMMENT, NODE_CDATA, NODE_DOCUMENT + UNKNOWN_NODE = 0, + ELEMENT_NODE = 1, + ATTRIBUTE_NODE = 2, + TEXT_NODE = 3, + CDATA_SECTION_NODE = 4, + ENTITY_REFERENCE_NODE = 5, + ENTITY_NODE = 6 + PROCESSING_INSTRUCTION_NODE = 7, + COMMENT_NODE = 8, + DOCUMENT_NODE = 9, + DOCUMENT_TYPE_NODE = 10, + DOCUMENT_FRAGMENT_NODE = 11, + NOTATION_NODE = 12 DisplayType* = enum @@ -69,11 +81,13 @@ const DisplayBlockTags* = { TAG_FIELDSET, TAG_FORM, TAG_H1, TAG_H2, TAG_H3, TAG_H4, TAG_H5, TAG_H6, TAG_HR, TAG_INS, TAG_MENU, TAG_NOFRAMES, TAG_NOSCRIPT, TAG_OL, TAG_P, TAG_PRE, TAG_TABLE, TAG_UL, TAG_CENTER, TAG_DIR, TAG_MENU, TAG_NOFRAMES, TAG_BODY, - #single - TAG_HR } -const SingleTagTypes* = { +const SelfClosingTagTypes* = { + TAG_LI, TAG_P +} + +const VoidTagTypes* = { TAG_AREA, TAG_BASE, TAG_BR, TAG_COL, TAG_FRAME, TAG_HR, TAG_IMG, TAG_INPUT, - TAG_SOURCE, TAG_TRACK, TAG_LINK, TAG_META, TAG_PARAM, TAG_WBR, TAG_LI + TAG_SOURCE, TAG_TRACK, TAG_LINK, TAG_META, TAG_PARAM, TAG_WBR, TAG_HR } diff --git a/htmlelement.nim b/htmlelement.nim deleted file mode 100644 index 2b7c1c64..00000000 --- a/htmlelement.nim +++ /dev/null @@ -1,368 +0,0 @@ -import strutils -import terminal -import uri -import unicode - -import fusion/htmlparser/xmltree - -import twtstr -import twtio -import enums -import macros - -type - HtmlNode* = ref HtmlNodeObj - HtmlNodeObj = object of RootObj - nodeType*: NodeType - childNodes*: seq[HtmlNode] - firstChild*: HtmlNode - isConnected*: bool - lastChild*: HtmlNode - nextSibling*: HtmlNode - previousSibling*: HtmlNode - parentNode*: HtmlNode - parentElement*: HtmlElement - - rawtext*: string - fmttext*: seq[string] - x*: int - y*: int - ex*: int - ey*: int - width*: int - height*: int - openblock*: bool - closeblock*: bool - hidden*: bool - - Document* = ref DocumentObj - DocumentObj = object of HtmlNodeObj - location*: Uri - - HtmlElement* = ref HtmlElementObj - HtmlElementObj = object of HtmlNodeObj - id*: string - class*: string - tagType*: TagType - centered*: bool - display*: DisplayType - innerText*: string - margintop*: int - marginbottom*: int - marginleft*: int - marginright*: int - margin*: int - bold*: bool - italic*: bool - underscore*: bool - islink*: bool - selected*: bool - numChildNodes*: int - indent*: int - - HtmlInputElement* = ref HtmlInputElementObj - HtmlInputElementObj = object of HtmlElementObj - itype*: InputType - autofocus*: bool - required*: bool - value*: string - size*: int - - HtmlAnchorElement* = ref HtmlAnchorElementObj - HtmlAnchorElementObj = object of HtmlElementObj - href*: string - - HtmlSelectElement* = ref HtmlSelectElementObj - HtmlSelectElementObj = object of HtmlElementObj - name*: string - value*: string - valueSet*: bool - - HtmlOptionElement* = ref HtmlOptionElementObj - HtmlOptionElementObj = object of HtmlElementObj - value*: string - -#no I won't manually write all this down -#maybe todo to accept stuff other than tagtype (idk how useful that'd be) -macro genEnumCase(s: string): untyped = - let casestmt = nnkCaseStmt.newTree() - casestmt.add(ident("s")) - for i in low(TagType) .. high(TagType): - let ret = nnkReturnStmt.newTree() - ret.add(newLit(TagType(i))) - let branch = nnkOfBranch.newTree() - let enumname = $TagType(i) - let tagname = enumname.substr("TAG_".len, enumname.len - 1).tolower() - branch.add(newLit(tagname)) - branch.add(ret) - casestmt.add(branch) - let ret = nnkReturnStmt.newTree() - ret.add(newLit(TAG_UNKNOWN)) - let branch = nnkElse.newTree() - branch.add(ret) - casestmt.add(branch) - -func tagType(s: string): TagType = - genEnumCase(s) - -func nodeAttr*(node: HtmlNode): HtmlElement = - case node.nodeType - of NODE_TEXT: return node.parentElement - of NODE_ELEMENT: return HtmlElement(node) - else: assert(false) - -func displayed*(node: HtmlNode): bool = - return node.rawtext.len > 0 and node.nodeAttr().display != DISPLAY_NONE - -func isTextNode*(node: HtmlNode): bool = - return node.nodeType == NODE_TEXT - -func isElemNode*(node: HtmlNode): bool = - return node.nodeType == NODE_ELEMENT - -func isComment*(node: HtmlNode): bool = - return node.nodeType == NODE_COMMENT - -func isCData*(node: HtmlNode): bool = - return node.nodeType == NODE_CDATA - -func isDocument*(node: HtmlNode): bool = - return node.nodeType == NODE_DOCUMENT - -func getFmtLen*(htmlNode: HtmlNode): int = - return htmlNode.fmttext.join().runeLen() - -func getRawLen*(htmlNode: HtmlNode): int = - return htmlNode.rawtext.runeLen() - -func toInputType*(str: string): InputType = - case str - of "button": INPUT_BUTTON - of "checkbox": INPUT_CHECKBOX - of "color": INPUT_COLOR - of "date": INPUT_DATE - of "datetime_local": INPUT_DATETIME_LOCAL - of "email": INPUT_EMAIL - of "file": INPUT_FILE - of "hidden": INPUT_HIDDEN - of "image": INPUT_IMAGE - of "month": INPUT_MONTH - of "number": INPUT_NUMBER - of "password": INPUT_PASSWORD - of "radio": INPUT_RADIO - of "range": INPUT_RANGE - of "reset": INPUT_RESET - of "search": INPUT_SEARCH - of "submit": INPUT_SUBMIT - of "tel": INPUT_TEL - of "text": INPUT_TEXT - of "time": INPUT_TIME - of "url": INPUT_URL - of "week": INPUT_WEEK - else: INPUT_UNKNOWN - -func toInputSize*(str: string): int = - if str.len == 0: - return 20 - for c in str: - if not c.isDigit: - return 20 - return str.parseInt() - -func getFmtInput(inputElement: HtmlInputElement): seq[string] = - case inputElement.itype - of INPUT_TEXT, INPUT_SEARCH: - let valueFit = fitValueToSize(inputElement.value, inputElement.size) - return valueFit.ansiStyle(styleUnderscore).ansiReset().buttonFmt() - of INPUT_SUBMIT: - return inputElement.value.buttonFmt() - else: discard - -func getRawInput(inputElement: HtmlInputElement): string = - case inputElement.itype - of INPUT_TEXT, INPUT_SEARCH: - return inputElement.value.fitValueToSize(inputElement.size).buttonRaw() - of INPUT_SUBMIT: - return inputElement.value.buttonRaw() - else: discard - -func ancestor*(htmlNode: HtmlNode, tagType: TagType): HtmlElement = - result = htmlNode.parentElement - while result != nil and result.tagType != tagType: - result = result.parentElement - -func displayWhitespace*(htmlElem: HtmlElement): bool = - return htmlElem.display == DISPLAY_INLINE or htmlElem.display == DISPLAY_INLINE_BLOCK - -proc getRawText*(htmlNode: HtmlNode): string = - if htmlNode.isElemNode(): - case HtmlElement(htmlNode).tagType - of TAG_INPUT: return HtmlInputElement(htmlNode).getRawInput() - else: return "" - elif htmlNode.isTextNode(): - if htmlNode.parentElement != nil and htmlNode.parentElement.tagType != TAG_PRE: - result = htmlNode.rawtext.remove("\n") - if unicode.strip(result).runeLen() > 0: - if htmlNode.nodeAttr().display != DISPLAY_INLINE: - if htmlNode.previousSibling == nil or htmlNode.previousSibling.nodeAttr().displayWhitespace(): - result = unicode.strip(result, true, false) - if htmlNode.nextSibling == nil or htmlNode.nextSibling.nodeAttr().displayWhitespace(): - result = unicode.strip(result, false, true) - else: - result = "" - else: - result = unicode.strip(htmlNode.rawtext) - if htmlNode.parentElement != nil and htmlNode.parentElement.tagType == TAG_OPTION: - result = result.buttonRaw() - else: - assert(false) - -func getFmtText*(htmlNode: HtmlNode): seq[string] = - if htmlNode.isElemNode(): - case HtmlElement(htmlNode).tagType - of TAG_INPUT: return HtmlInputElement(htmlNode).getFmtInput() - else: return @[] - elif htmlNode.isTextNode(): - result &= htmlNode.rawtext - if htmlNode.parentElement != nil: - if htmlNode.parentElement.islink: - result = result.ansiFgColor(fgBlue).ansiReset() - let anchor = htmlNode.ancestor(TAG_A) - if anchor != nil and anchor.selected: - result = result.ansiStyle(styleUnderscore).ansiReset() - - if htmlNode.parentElement.tagType == TAG_OPTION: - result = result.ansiFgColor(fgRed).ansiReset() - - if htmlNode.parentElement.bold: - result = result.ansiStyle(styleBright).ansiReset() - if htmlNode.parentElement.italic: - result = result.ansiStyle(styleItalic).ansiReset() - if htmlNode.parentElement.underscore: - result = result.ansiStyle(styleUnderscore).ansiReset() - else: - assert(false, "Uhhhh I'm pretty sure we should have parent elements for text nodes?" & htmlNode.rawtext) - else: - assert(false) - -proc getHtmlElement*(xmlElement: XmlNode, parentNode: HtmlNode): HtmlElement = - assert kind(xmlElement) == xnElement - let tagType = xmlElement.tag().tagType() - - case tagType - of TAG_INPUT: result = new(HtmlInputElement) - of TAG_A: result = new(HtmlAnchorElement) - else: new(result) - - result.tagType = tagType - result.parentNode = parentNode - if parentNode.isElemNode(): - result.parentElement = HtmlElement(parentNode) - - result.id = xmlElement.attr("id") - - if tagType in DisplayInlineTags: - result.display = DISPLAY_INLINE - elif tagType in DisplayBlockTags: - result.display = DISPLAY_BLOCK - elif tagType in DisplayInlineBlockTags: - result.display = DISPLAY_INLINE_BLOCK - elif tagType == TAG_LI: - result.display = DISPLAY_LIST_ITEM - else: - result.display = DISPLAY_NONE - - case tagType - of TAG_CENTER: - result.centered = true - of TAG_B: - result.bold = true - of TAG_I: - result.italic = true - of TAG_U: - result.underscore = true - of TAG_HEAD: - result.hidden = true - of TAG_STYLE: - result.hidden = true - of TAG_SCRIPT: - result.hidden = true - of TAG_INPUT: - let inputElement = HtmlInputElement(result) - inputElement.itype = xmlElement.attr("type").toInputType() - if inputElement.itype == INPUT_HIDDEN: - inputElement.hidden = true - inputElement.size = xmlElement.attr("size").toInputSize() - inputElement.value = xmlElement.attr("value") - result = inputElement - of TAG_A: - let anchorElement = HtmlAnchorElement(result) - anchorElement.href = xmlElement.attr("href") - anchorElement.islink = true - result = anchorElement - of TAG_SELECT: - var selectElement = new(HtmlSelectElement) - for item in xmlElement.items: - if item.kind == xnElement: - if item.tag == "option": - selectElement.value = item.attr("value") - break - selectElement.name = xmlElement.attr("name") - result = selectElement - of TAG_OPTION: - var optionElement = new(HtmlOptionElement) - optionElement.value = xmlElement.attr("value") - if parentNode.isElemNode() and HtmlSelectElement(parentNode).value != optionElement.value: - optionElement.hidden = true - result = optionElement - of TAG_PRE, TAG_TD, TAG_TH: - result.margin = 1 - of TAG_UL, TAG_OL: - result.indent = 1 - of TAG_H1, TAG_H2, TAG_H3, TAG_H4, TAG_H5, TAG_H6: - result.bold = true - result.marginbottom = 1 - else: discard - - if parentNode.isElemNode(): - let parent = HtmlElement(parentNode) - result.centered = result.centered or parent.centered - result.bold = result.bold or parent.bold - result.italic = result.italic or parent.italic - result.underscore = result.underscore or parent.underscore - result.hidden = result.hidden or parent.hidden - result.islink = result.islink or parent.islink - -proc getHtmlNode*(xmlElement: XmlNode, parent: HtmlNode): HtmlNode = - case kind(xmlElement) - of xnElement: - result = getHtmlElement(xmlElement, parent) - result.nodeType = NODE_ELEMENT - of xnText: - new(result) - result.nodeType = NODE_TEXT - result.rawtext = xmlElement.text - of xnComment: - new(result) - result.nodeType = NODE_COMMENT - result.rawtext = xmlElement.text - of xnCData: - new(result) - result.nodeType = NODE_CDATA - result.rawtext = xmlElement.text - else: assert(false) - - result.parentNode = parent - if parent.isElemNode(): - result.parentElement = HtmlElement(parent) - if parent.childNodes.len > 0: - result.previousSibling = parent.childNodes[^1] - result.previousSibling.nextSibling = result - parent.childNodes.add(result) - - result.rawtext = result.getRawText() - result.fmttext = result.getFmtText() - -func newDocument*(): Document = - new(result) - result.nodeType = NODE_DOCUMENT diff --git a/main.nim b/main.nim index b2ce1ace..677b812f 100644 --- a/main.nim +++ b/main.nim @@ -29,7 +29,6 @@ proc getPageUri(uri: Uri): Stream = if uri.scheme == "" or uri.scheme == "file": return getLocalPage($moduri) else: - #return nparseHtml(getRemotePage($moduri)) return getRemotePage($moduri) var buffers: seq[Buffer] @@ -44,9 +43,9 @@ proc main*() = let buffer = newBuffer(attrs) let uri = parseUri(paramStr(1)) buffers.add(buffer) - buffer.document = nparseHtml(getPageUri(uri)) + buffer.document = parseHtml(getPageUri(uri)) buffer.setLocation(uri) - buffer.nrenderHtml() + buffer.renderHtml() var lastUri = uri while displayPage(attrs, buffer): statusMsg("Loading...", buffer.height) @@ -58,10 +57,9 @@ proc main*() = if uri.scheme == "" and uri.path == "" and uri.anchor != "": discard else: - buffer.document = nparseHtml(getPageUri(buffer.document.location)) - buffer.nrenderHtml() + buffer.document = parseHtml(getPageUri(buffer.document.location)) + buffer.renderHtml() lastUri = newUri #waitFor loadPage("https://lite.duckduckgo.com/lite/?q=hello%20world") -#eprint mk_wcswidth_cjk("abc•de") main() diff --git a/parser.nim b/parser.nim index a8951368..11c39c76 100644 --- a/parser.nim +++ b/parser.nim @@ -1,131 +1,97 @@ -import parsexml -import htmlelement import streams -import macros import unicode +import strutils +import tables +import json import twtio import enums -import strutils +import twtstr +import dom +import radixtree type ParseState = object stream: Stream closed: bool - parents: seq[HtmlNode] - parsedNode: HtmlNode + parents: seq[Node] + parsedNode: Node a: string + b: string attrs: seq[string] + in_comment: bool + in_script: bool + in_style: bool + in_noscript: bool + parentNode: Node + textNode: Text - ParseEvent = - enum - NO_EVENT, EVENT_COMMENT, EVENT_STARTELEM, EVENT_ENDELEM, EVENT_OPENELEM, - EVENT_CLOSEELEM, EVENT_ATTRIBUTE, EVENT_TEXT - -#> no I won't manually write all this down -#yes this is incredibly ugly -#...but hey, so long as it works - -macro genEnumCase(s: string, t: typedesc) = - result = quote do: - let casestmt = nnkCaseStmt.newTree() - casestmt.add(ident(`s`)) - var first = true - for e in low(`t`) .. high(`t`): - if first: - first = false - continue - let ret = nnkReturnStmt.newTree() - ret.add(newLit(e)) - let branch = nnkOfBranch.newTree() - let enumname = $e - let tagname = enumname.split('_')[1..^1].join("_").tolower() - branch.add(newLit(tagname)) - branch.add(ret) - casestmt.add(branch) - let ret = nnkReturnStmt.newTree() - ret.add(newLit(low(`t`))) - let branch = nnkElse.newTree() - branch.add(ret) - casestmt.add(branch) - -macro genTagTypeCase() = - genEnumCase("s", TagType) - -macro genInputTypeCase() = - genEnumCase("s", InputType) - -func tagType(s: string): TagType = - genTagTypeCase - -func inputType(s: string): InputType = - genInputTypeCase - -func newHtmlElement(tagType: TagType, parentNode: HtmlNode): HtmlElement = - case tagType - of TAG_INPUT: result = new(HtmlInputElement) - of TAG_A: result = new(HtmlAnchorElement) - of TAG_SELECT: result = new(HtmlSelectElement) - of TAG_OPTION: result = new(HtmlOptionElement) - else: result = new(HtmlElement) - - result.nodeType = NODE_ELEMENT - result.tagType = tagType - result.parentNode = parentNode - if parentNode.isElemNode(): - result.parentElement = HtmlElement(parentNode) - - if tagType in DisplayInlineTags: - result.display = DISPLAY_INLINE - elif tagType in DisplayBlockTags: - result.display = DISPLAY_BLOCK - elif tagType in DisplayInlineBlockTags: - result.display = DISPLAY_INLINE_BLOCK - elif tagType == TAG_LI: - result.display = DISPLAY_LIST_ITEM - else: - result.display = DISPLAY_NONE - - case tagType - of TAG_CENTER: - result.centered = true - of TAG_B: - result.bold = true - of TAG_I: - result.italic = true - of TAG_U: - result.underscore = true - of TAG_HEAD: - result.hidden = true - of TAG_STYLE: - result.hidden = true - of TAG_SCRIPT: - result.hidden = true - of TAG_OPTION: - result.hidden = true #TODO - of TAG_PRE, TAG_TD, TAG_TH: - result.margin = 1 - of TAG_UL, TAG_OL: - result.indent = 1 - of TAG_H1, TAG_H2, TAG_H3, TAG_H4, TAG_H5, TAG_H6: - result.bold = true - result.marginbottom = 1 - of TAG_A: - result.islink = true - of TAG_INPUT: - HtmlInputElement(result).size = 20 - else: discard - - if parentNode.isElemNode(): - let parent = HtmlElement(parentNode) - result.centered = result.centered or parent.centered - result.bold = result.bold or parent.bold - result.italic = result.italic or parent.italic - result.underscore = result.underscore or parent.underscore - result.hidden = result.hidden or parent.hidden - result.islink = result.islink or parent.islink +#func newHtmlElement(tagType: TagType, parentNode: Node): HtmlElement = +# case tagType +# of TAG_INPUT: result = new(HtmlInputElement) +# of TAG_A: result = new(HtmlAnchorElement) +# of TAG_SELECT: result = new(HtmlSelectElement) +# of TAG_OPTION: result = new(HtmlOptionElement) +# else: result = new(HtmlElement) +# +# result.nodeType = ELEMENT_NODE +# result.tagType = tagType +# result.parentNode = parentNode +# if parentNode.isElemNode(): +# result.parentElement = HtmlElement(parentNode) +# +# if tagType in DisplayInlineTags: +# result.display = DISPLAY_INLINE +# elif tagType in DisplayBlockTags: +# result.display = DISPLAY_BLOCK +# elif tagType in DisplayInlineBlockTags: +# result.display = DISPLAY_INLINE_BLOCK +# elif tagType == TAG_LI: +# result.display = DISPLAY_LIST_ITEM +# else: +# result.display = DISPLAY_NONE +# +# case tagType +# of TAG_CENTER: +# result.centered = true +# of TAG_B: +# result.bold = true +# of TAG_I: +# result.italic = true +# of TAG_U: +# result.underscore = true +# of TAG_HEAD: +# result.hidden = true +# of TAG_STYLE: +# result.hidden = true +# of TAG_SCRIPT: +# result.hidden = true +# of TAG_OPTION: +# result.hidden = true #TODO +# of TAG_PRE, TAG_TD, TAG_TH: +# result.margin = 1 +# of TAG_UL, TAG_OL: +# result.indent = 2 +# result.margin = 1 +# of TAG_H1, TAG_H2, TAG_H3, TAG_H4, TAG_H5, TAG_H6: +# result.bold = true +# result.margin = 1 +# of TAG_A: +# result.islink = true +# of TAG_INPUT: +# HtmlInputElement(result).size = 20 +# else: discard +# +# if parentNode.isElemNode(): +# let parent = HtmlElement(parentNode) +# result.centered = result.centered or parent.centered +# result.bold = result.bold or parent.bold +# result.italic = result.italic or parent.italic +# result.underscore = result.underscore or parent.underscore +# result.hidden = result.hidden or parent.hidden +# result.islink = result.islink or parent.islink -func toInputSize*(str: string): int = +func inputSize*(str: string): int = if str.len == 0: return 20 for c in str: @@ -133,297 +99,400 @@ func toInputSize*(str: string): int = return 20 return str.parseInt() -proc applyAttribute(htmlElement: HtmlElement, key: string, value: string) = - case key - of "id": htmlElement.id = value - of "class": htmlElement.class = value - of "name": - case htmlElement.tagType - of TAG_SELECT: HtmlSelectElement(htmlElement).name = value - else: discard - of "value": - case htmlElement.tagType - of TAG_INPUT: HtmlInputElement(htmlElement).value = value - of TAG_SELECT: HtmlSelectElement(htmlElement).value = value - of TAG_OPTION: HtmlOptionElement(htmlElement).value = value - else: discard - of "href": - case htmlElement.tagType - of TAG_A: HtmlAnchorElement(htmlElement).href = value - else: discard - of "type": - case htmlElement.tagType - of TAG_INPUT: HtmlInputElement(htmlElement).itype = value.inputType() - else: discard - of "size": - case htmlElement.tagType - of TAG_INPUT: HtmlInputElement(htmlElement).size = value.toInputSize() - else: discard - else: return - -proc closeNode(state: var ParseState) = - let node = state.parents[^1] - if node.childNodes.len > 0 and node.isElemNode() and HtmlElement(node).display == DISPLAY_BLOCK: - node.childNodes[0].openblock = true - node.childNodes[^1].closeblock = true - state.parents.setLen(state.parents.len - 1) - state.closed = true - -proc closeSingleNodes(state: var ParseState) = - if not state.closed and state.parents[^1].isElemNode() and HtmlElement(state.parents[^1]).tagType in SingleTagTypes: - state.closeNode() - -proc applyNodeText(htmlNode: HtmlNode) = - htmlNode.rawtext = htmlNode.getRawText() - htmlNode.fmttext = htmlNode.getFmtText() - -proc setParent(state: var ParseState, htmlNode: HtmlNode) = - htmlNode.parentNode = state.parents[^1] - if state.parents[^1].isElemNode(): - htmlNode.parentElement = HtmlElement(state.parents[^1]) - if state.parents[^1].childNodes.len > 0: - htmlNode.previousSibling = state.parents[^1].childNodes[^1] - htmlNode.previousSibling.nextSibling = htmlNode - state.parents[^1].childNodes.add(htmlNode) - -proc processHtmlElement(state: var ParseState, htmlElement: HtmlElement) = - state.closed = false - state.setParent(htmlElement) - state.parents.add(htmlElement) - -proc parsecomment(state: var ParseState) = +proc genEntityMap(): RadixTree[string] = + let entity = staticRead"entity.json" + let entityJson = parseJson(entity) + var entityMap = newRadixTree[string]() + + for k, v in entityJson: + entityMap[k.substr(1)] = v{"characters"}.getStr() + + return entityMap + +const entityMap = genEntityMap() + +func genHexCharMap(): seq[int] = + for i in 0..255: + case chr(i) + of '0'..'9': result &= i - ord('0') + of 'a'..'f': result &= i - ord('a') + 10 + of 'A'..'F': result &= i - ord('A') + 10 + else: result &= -1 + +func genDecCharMap(): seq[int] = + for i in 0..255: + case chr(i) + of '0'..'9': result &= i - ord('0') + else: result &= -1 + +const hexCharMap = genHexCharMap() +const decCharMap = genDecCharMap() + +#w3m's getescapecmd and parse_tag, transpiled to nim. +#(C) Copyright 1994-2002 by Akinori Ito +#(C) Copyright 2002-2011 by Akinori Ito, Hironori Sakamoto, Fumitoshi Ukai +# +#Use, modification and redistribution of this software is hereby granted, +#provided that this entire copyright notice is included on any copies of +#this software and applications and derivations thereof. +# +#This software is provided on an "as is" basis, without warranty of any +#kind, either expressed or implied, as to any matter including, but not +#limited to warranty of fitness of purpose, or merchantability, or +#results obtained from use of this software. +proc getescapecmd(buf: string, at: var int): string = + var i = at + + if buf[i] == '#': #num + inc i + var num: int + if buf[i].tolower() == 'x': #hex + inc i + if not isdigit(buf[i]): + at = i + return "" + + num = hexCharMap[int(buf[i])] + inc i + while i < buf.len and hexCharMap[int(buf[i])] != -1: + num *= 0x10 + num += hexCharMap[int(buf[i])] + inc i + else: #dec + if not isDigit(buf[i]): + at = i + return "" + + num = decCharMap[int(buf[i])] + inc i + while i < buf.len and isDigit(buf[i]): + num *= 10 + num += decCharMap[int(buf[i])] + inc i + + if buf[i] == ';': + inc i + at = i + return $(Rune(num)) + elif not isAlphaAscii(buf[i]): + return "" + + var n: uint16 = 0 var s = "" - state.a = "" - var e = 0 - while not state.stream.atEnd(): - let c = cast[char](state.stream.readInt8()) - if c > char(127): - s &= c - if s.validateUtf8() == -1: - state.a &= s - s = "" + while true: + let c = buf[i] + s &= c + if not entityMap.hasPrefix(s, n): + break + let pn = n + n = entityMap.getPrefix(s, n) + if n != pn: + s = "" + inc i + + if entityMap.nodes[n].leaf: + at = i + return entityMap.nodes[n].value + + return "" + +type + DOMParsedTag = object + tagid: TagType + attrs: Table[string, string] + open: bool + +proc parse_tag(buf: string, at: var int): DOMParsedTag = + var tag = DOMParsedTag() + tag.open = true + + #Parse tag name + var tagname = "" + inc at + if buf[at] == '/': + inc at + tag.open = false + skipBlanks(buf, at) + + while at < buf.len and not buf[at].isWhitespace() and not (tag.open and buf[at] == '/') and buf[at] != '>': + tagname &= buf[at].tolower() + at += buf.runeLenAt(at) + + tag.tagid = tagType(tagname) + skipBlanks(buf, at) + + while at < buf.len and buf[at] != '>': + var value = "" + var attrname = "" + while at < buf.len and buf[at] != '=' and not buf[at].isWhitespace() and buf[at] != '>': + attrname &= buf[at].tolower() + at += buf.runeLenAt(at) + + skipBlanks(buf, at) + if buf[at] == '=': + inc at + skipBlanks(buf, at) + if at < buf.len and (buf[at] == '"' or buf[at] == '\''): + let startc = buf[at] + inc at + while at < buf.len and buf[at] != startc: + var r: Rune + fastRuneAt(buf, at, r) + if r == Rune('&'): + value &= getescapecmd(buf, at) + else: + value &= $r + if at < buf.len: + inc at + elif at < buf.len: + while at < buf.len and not buf[at].isWhitespace() and buf[at] != '>': + value &= buf[at] + at += buf.runeLenAt(at) + + if attrname.len > 0: + tag.attrs[attrname] = value + + while at < buf.len and buf[at] != '>': + at += buf.runeLenAt(at) + + if at < buf.len and buf[at] == '>': + inc at + return tag + +proc insertNode(parent: Node, node: Node) = + parent.childNodes.add(node) + + if parent.firstChild == nil: + parent.firstChild = node + + parent.lastChild = node + + if parent.childNodes.len > 1: + let prevSibling = parent.childNodes[^1] + prevSibling.nextSibling = node + node.previousSibling = prevSibling + + node.parentNode = parent + if parent.nodeType == ELEMENT_NODE: + node.parentElement = Element(parent) + + if parent.ownerDocument != nil: + node.ownerDocument = parent.ownerDocument + elif parent.nodeType == DOCUMENT_NODE: + node.ownerDocument = Document(parent) + +proc processDocumentStartNode(state: var ParseState, newNode: Node) = + insertNode(state.parentNode, newNode) + state.parentNode = newNode + +proc processDocumentEndNode(state: var ParseState) = + if state.parentNode == nil or state.parentNode.parentNode == nil: + return + state.parentNode = state.parentNode.parentNode + +proc processDocumentText(state: var ParseState) = + if state.textNode == nil: + state.textNode = newText() + + processDocumentStartNode(state, state.textNode) + processDocumentEndNode(state) + +proc processDocumentStartElement(state: var ParseState, element: Element, tag: DOMParsedTag) = + for k, v in tag.attrs: + element.attributes[k] = element.newAttr(k, v) + + element.id = element.getAttrValue("id") + if element.attributes.hasKey("class"): + for w in unicode.split(element.attributes["class"].value, Rune(' ')): + element.classList.add(w) + + case element.tagType + of TAG_SCRIPT: + state.in_script = true + of TAG_NOSCRIPT: + state.in_noscript = true + of TAG_STYLE: + state.in_style = true + of TAG_SELECT: + HTMLSelectElement(element).name = element.getAttrValue("name") + HTMLSelectElement(element).value = element.getAttrValue("value") + of TAG_INPUT: + HTMLInputElement(element).value = element.getAttrValue("value") + HTMLInputElement(element).itype = element.getAttrValue("type").inputType() + HTMLInputElement(element).size = element.getAttrValue("size").inputSize() + of TAG_A: + HTMLAnchorElement(element).href = element.getAttrValue("href") + of TAG_OPTION: + HTMLOptionElement(element).value = element.getAttrValue("href") + else: discard + + if state.parentNode.nodeType == ELEMENT_NODE: + case element.tagType + of TAG_LI, TAG_P: + if Element(state.parentNode).tagType == element.tagType: + processDocumentEndNode(state) + of TAG_H1: + HTMLHeadingElement(element).rank = 1 + of TAG_H2: + HTMLHeadingElement(element).rank = 2 + of TAG_H3: + HTMLHeadingElement(element).rank = 3 + of TAG_H4: + HTMLHeadingElement(element).rank = 4 + of TAG_H5: + HTMLHeadingElement(element).rank = 5 + of TAG_H6: + HTMLHeadingElement(element).rank = 6 + else: discard + + processDocumentStartNode(state, element) + + if element.tagType in VoidTagTypes: + processDocumentEndNode(state) + +proc processDocumentEndElement(state: var ParseState, tag: DOMParsedTag) = + if tag.tagid in VoidTagTypes: + return + if state.parentNode.nodeType == ELEMENT_NODE: + if Element(state.parentNode).tagType in {TAG_LI, TAG_P}: + processDocumentEndNode(state) + + processDocumentEndNode(state) + +proc processDocumentTag(state: var ParseState, tag: DOMParsedTag) = + if state.in_script: + if tag.tagid == TAG_SCRIPT: + state.in_script = false else: - case e - of 0: - if c == '-': inc e - of 1: - if c == '-': inc e - else: - e = 0 - state.a &= '-' & c - of 2: - if c == '>': return - else: - e = 0 - state.a &= "--" & c - else: state.a &= c - -proc parsecdata(state: var ParseState) = - var s = "" - var e = 0 - while not state.stream.atEnd(): - let c = cast[char](state.stream.readInt8()) - if c > char(127): - s &= c - if s.validateUtf8() == -1: - state.a &= s - s = "" + return + + if state.in_style: + if tag.tagid == TAG_STYLE: + state.in_style = false else: - case e - of 0: - if c == ']': inc e - of 1: - if c == ']': inc e - else: e = 0 - of 2: - if c == '>': return - else: e = 0 - else: discard - state.a &= c + return -proc next(state: var ParseState): ParseEvent = - result = NO_EVENT - if state.stream.atEnd(): return result + if state.in_noscript: + if tag.tagid == TAG_NOSCRIPT: + state.in_noscript = false + else: + return - var c = cast[char](state.stream.readInt8()) - var cdata = false - var s = "" - state.a = "" - if c < char(128): #ascii - case c - of '<': - if state.stream.atEnd(): - state.a = $c - return EVENT_TEXT - let d = char(state.stream.peekInt8()) - case d - of '/': result = EVENT_ENDELEM - of '!': - state.a = state.stream.readStr(2) - case state.a - of "[C": - state.a &= state.stream.readStr(7) - if state.a == "[CDATA[": - state.parsecdata() - return EVENT_COMMENT - result = EVENT_TEXT - of "--": - state.parsecomment() - return EVENT_COMMENT - else: - while not state.stream.atEnd(): - c = cast[char](state.stream.readInt8()) - if s.len == 0 and c == '>': - break - elif c > char(127): - s &= c - if s.validateUtf8() == -1: - s = "" - return NO_EVENT - of Letters: - result = EVENT_STARTELEM - else: - result = EVENT_TEXT - state.a = c & d - of '>': - return EVENT_CLOSEELEM - else: result = EVENT_TEXT - else: result = EVENT_TEXT - - case result - of EVENT_STARTELEM: - var atspace = false - var atattr = false - while not state.stream.atEnd(): - c = cast[char](state.stream.peekInt8()) - if s.len == 0 and c < char(128): - case c - of Whitespace: atspace = true - of '>': - discard state.stream.readInt8() - break - else: - if atspace: - return EVENT_OPENELEM - else: - state.a &= s + if tag.open: + processDocumentStartElement(state, newHtmlElement(tag.tagid), tag) + else: + processDocumentEndElement(state, tag) + #XXX PROCDOCCASE stuff... good lord I'll never finish this thing + +proc processDocumentPart(state: var ParseState, buf: string) = + var at = 0 + var max = 0 + var was_script = false + + max = buf.len + + while at < max: + case buf[at] + of '&': + inc at + let p = getescapecmd(buf, at) + if state.in_comment: + CharacterData(state.parentNode).data &= p else: - if atspace: - return EVENT_OPENELEM - s &= c - if s.validateUtf8() == -1: - state.a &= s - s = "" - discard state.stream.readInt8() - of EVENT_ENDELEM: - while not state.stream.atEnd(): - c = cast[char](state.stream.readInt8()) - if s.len == 0 and c < char(128): - if c == '>': break - elif c in Whitespace: discard - else: state.a &= c + processDocumentText(state) + state.textNode.data &= p + of '<': + if state.in_comment: + CharacterData(state.parentNode).data &= buf[at] + inc at else: - s &= c - if s.validateUtf8() == -1: - state.a &= s - s = "" - of EVENT_TEXT: - while not state.stream.atEnd(): - c = cast[char](state.stream.peekInt8()) - if s.len == 0 and c < char(128): - if c in {'<', '>'}: break - state.a &= c + var p = at + inc p + if p < max and buf[p] == '!': + inc p + if p < max and buf[p] == '-': + inc p + if p < max and buf[p] == '-': + inc p + at = p + state.in_comment = true + processDocumentStartNode(state, newComment()) + if state.textNode != nil: + state.textNode.rawtext = state.textNode.getRawText() + state.textNode = nil + + if not state.in_comment: + if state.textNode != nil: + state.textNode.rawtext = state.textNode.getRawText() + state.textNode = nil + p = at + var tag = parse_tag(buf, at) + was_script = state.in_script + + processDocumentTag(state, tag) +# if (was_script) { +# if (state->in_script) { +# ptr = p; +# processDocumentText(&state->parentNode, &state->textNode); +# Strcat_char(((CharacterData *)state->textNode)->data, *ptr++); +# } else if (buffer->javascript_enabled) { +# loadJSToBuffer(buffer, childTextContentNode(state->parentNode->lastChild)->ptr, "<inline>", state->document); +# } +# } + elif buf[at] == '-' and state.in_comment: + var p = at + inc p + if p < max and buf[p] == '-': + inc p + if p < max and buf[p] == '>': + inc p + at = p + state.in_comment = false + processDocumentEndNode(state) + + if state.in_comment: + CharacterData(state.parentNode).data &= buf[at] + inc at + else: + var r: Rune + fastRuneAt(buf, at, r) + if state.in_comment: + CharacterData(state.parentNode).data &= $r else: - s &= c - if s.validateUtf8() == -1: - state.a &= s - s = "" - discard state.stream.readInt8() - else: assert(false) - -proc nparseHtml*(inputStream: Stream): Document = - var state = ParseState(stream: inputStream) + processDocumentText(state) + state.textNode.data &= $r + +proc parseHtml*(inputStream: Stream): Document = let document = newDocument() - state.parents.add(document) - while state.parents.len > 0 and not inputStream.atEnd(): - let event = state.next() - case event - of EVENT_COMMENT: discard #TODO - of EVENT_STARTELEM: - state.closeSingleNodes() - let parsedNode = newHtmlElement(tagType(state.a), state.parents[^1]) - parsedNode.applyNodeText() - state.processHtmlElement(parsedNode) - of EVENT_ENDELEM: - state.closeNode() - of EVENT_OPENELEM: - state.closeSingleNodes() - let parsedNode = newHtmlElement(tagType(state.a), state.parents[^1]) - var next = state.next() - while next != EVENT_CLOSEELEM and not inputStream.atEnd(): - #TODO - #if next == EVENT_ATTRIBUTE: - # parsedNode.applyAttribute(state.a.tolower(), state.b) - # s &= " " & x.rawdata & "=\"" & x.rawdata2 & "\"" - #else: - # assert(false, "wtf " & $x.kind & " " & x.rawdata) #TODO - next = state.next() - parsedNode.applyNodeText() - state.processHtmlElement(parsedNode) - of EVENT_TEXT: - if unicode.strip(state.a).len == 0: - continue - let textNode = new(HtmlNode) - textNode.nodeType = NODE_TEXT - state.setParent(textNode) - textNode.rawtext = state.a - textNode.applyNodeText() - else: discard - return document -#old nparseHtml because I don't trust myself -#proc nparseHtml*(inputStream: Stream): Document = -# var x: XmlParser -# let options = {reportWhitespace, allowUnquotedAttribs, allowEmptyAttribs} -# x.open(inputStream, "", options) -# var state = ParseState(stream: inputStream) -# let document = newDocument() -# state.parents.add(document) -# while state.parents.len > 0 and x.kind != xmlEof: -# #let event = state.next() -# x.next() -# case x.kind -# of xmlComment: discard #TODO -# of xmlElementStart: -# state.closeSingleNodes() -# let parsedNode = newHtmlElement(tagType(x.rawData), state.parents[^1]) -# parsedNode.applyNodeText() -# state.processHtmlElement(parsedNode) -# of xmlElementEnd: -# state.closeNode() -# of xmlElementOpen: -# var s = "<" & x.rawdata -# state.closeSingleNodes() -# let parsedNode = newHtmlElement(tagType(x.rawData), state.parents[^1]) -# x.next() -# while x.kind != xmlElementClose and x.kind != xmlEof: -# if x.kind == xmlAttribute: -# parsedNode.applyAttribute(x.rawData.tolower(), x.rawData2) -# s &= " " & x.rawdata & "=\"" & x.rawdata2 & "\"" -# else: -# assert(false, "wtf " & $x.kind & " " & x.rawdata) #TODO -# x.next() -# s &= ">" -# parsedNode.applyNodeText() -# state.processHtmlElement(parsedNode) -# of xmlCharData: -# let textNode = new(HtmlNode) -# textNode.nodeType = NODE_TEXT -# -# state.setParent(textNode) -# textNode.rawtext = x.rawData -# textNode.applyNodeText() -# of xmlEntity: discard #TODO -# of xmlEof: break -# else: discard -# return document + var state = ParseState(stream: inputStream) + state.parentNode = document + + var till_when = false + + var buf = "" + var lineBuf: string + while not inputStream.atEnd(): + lineBuf = inputStream.readLine() + if lineBuf.len == 0: + break + buf &= lineBuf + + var at = 0 + while at < lineBuf.len: + case lineBuf[at] + of '<': + till_when = true + of '>': + till_when = false + else: discard + at += lineBuf.runeLenAt(at) + + if till_when: + continue + + processDocumentPart(state, buf) + buf = "" + + inputStream.close() + return document diff --git a/radixtree.nim b/radixtree.nim new file mode 100644 index 00000000..7933f7a3 --- /dev/null +++ b/radixtree.nim @@ -0,0 +1,153 @@ +import tables +import strutils +import json + +type + RadixNode[T] = object + children*: Table[string, uint16] + case leaf*: bool + of true: value*: T + of false: discard + + RadixTree*[T] = object + nodes*: seq[RadixNode[T]] + +func newRadixTree*[T](): RadixTree[T] = + result.nodes.add(RadixNode[T](leaf: false)) + +proc `[]=`*[T](tree: var RadixTree[T], key: string, value: T) = + var n: uint16 = 0 + var p: uint16 = 0 + var i = 0 + var j = 0 + var s = "" + var t = "" + var nodeKey = "" + # find last matching node + while i < key.len: + s &= key[i] + if s in tree.nodes[n].children: + p = n + n = tree.nodes[n].children[s] + t &= s + j += s.len + nodeKey = s + s = "" + inc i + + for k in tree.nodes[n].children.keys: + if s.len > 0 and k.startsWith(s[0]): + p = n + n = tree.nodes[n].children[k] + t &= k + nodeKey = k + break + + # if first node, just add normally + if n == 0: + tree.nodes.add(RadixNode[T](leaf: true, value: value)) + tree.nodes[n].children[key] = uint16(tree.nodes.len - 1) + else: + i = 0 + var conflict = false + # compare new key with the one we found so far + while i < t.len and i < key.len: + if key[i] == t[i]: + inc i + else: + conflict = true + break + + if conflict: + # conflict somewhere, so: + # * add new non-leaf to parent + # * add old to non-leaf + # * add new to non-leaf + # * remove old from parent + assert(i != 0) + + tree.nodes[p].children[key.substr(j, i - 1)] = uint16(tree.nodes.len) + tree.nodes.add(RadixNode[T](leaf: false)) + tree.nodes[^1].children[t.substr(i)] = n + tree.nodes[^1].children[key.substr(i)] = uint16(tree.nodes.len) + tree.nodes.add(RadixNode[T](leaf: true, value: value)) + tree.nodes[p].children.del(nodeKey) + else: # new is either substr of old or old is substr of new + # new matches a node, so replace + if key.len == t.len: + let children = tree.nodes[n].children + tree.nodes[n] = RadixNode[T](leaf: true, value: value) + tree.nodes[n].children = children + elif i == j: + # new is longer than the old, so add child to old + tree.nodes[n].children[key.substr(i)] = uint16(tree.nodes.len) + tree.nodes.add(RadixNode[T](leaf: true, value: value)) + elif i > 0: + # new is shorter than old, so: + # * add new to parent + # * add old to new + # * remove old from parent + tree.nodes[p].children[key.substr(j, i - 1)] = uint16(tree.nodes.len) + tree.nodes.add(RadixNode[T](leaf: true, value: value)) + tree.nodes[^1].children[t.substr(i)] = n + tree.nodes[p].children.del(nodeKey) + +func getPrefix*[T](tree: RadixTree[T], prefix: string, at: uint16 = 0): uint16 = + var s = "" + var t = "" + var n = at + var i = 0 + while t.len < prefix.len: + s &= prefix[i] + t &= prefix[i] + if s in tree.nodes[n].children: + n = tree.nodes[n].children[s] + s = "" + inc i + + return n + +func hasPrefix*[T](tree: RadixTree[T], prefix: string, at: uint16 = 0): bool = + var s = "" + var t = "" + var n = at + var i = 0 + while i < prefix.len: + s &= prefix[i] + if s in tree.nodes[n].children: + n = tree.nodes[n].children[s] + t &= s + s = "" + inc i + + if t.len == prefix.len: + return true + + for k in tree.nodes[n].children.keys: + if k.startsWith(prefix.substr(t.len)): + return true + + return false + +#tests +#var tree = newRadixTree[string]() +#tree.insert("hb", "abc") +#tree.insert("hi", "second") +#tree.insert("hia", "second") +#tree.insert("hia", "third") +#tree.insert("hiahhhooo", "two point fifth") +#tree.insert("hiahhho", "two point sixth") +#assert(tree.hasPrefix("h")) +#assert(tree.hasPrefix("hi")) +#assert(not tree.hasPrefix("hio")) +#assert(tree.hasPrefix("hiah")) +#assert(tree.hasPrefix("hiahhho")) +#assert(tree.hasPrefix("hiahhhooo")) +#assert(tree.lookup("hi", "error") != "error") +#assert(tree.lookup("hb", "error") != "error") +#assert(tree.lookup("hio", "error") == "error") +#assert(tree.lookup("hia", "error") != "error") +#assert(tree.lookup("hiahhhooo", "error") != "error") +#assert(tree.lookup("hiahhho", "error") != "error") +#assert(tree.lookup("hiahhhoo", "error") == "error") +#assert(tree.lookup("h", "error") == "error") diff --git a/style.nim b/style.nim new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/style.nim diff --git a/twtio.nim b/twtio.nim index acb4add7..db811f40 100644 --- a/twtio.nim +++ b/twtio.nim @@ -1,21 +1,22 @@ import terminal import tables -import strutils import unicode import twtstr import config +import radixtree template print*(s: varargs[string, `$`]) = for x in s: stdout.write(x) template printesc*(s: string) = - for ruby in s: - if ($ruby)[0].isControlChar(): - stdout.write(($($ruby)[0].getControlLetter()).ansiFgColor(fgBlue).ansiStyle(styleBright).ansiReset()) + for r in s.runes: + if r.isControlChar(): + stdout.write(('^' & $($r)[0].getControlLetter()) + .ansiFgColor(fgBlue).ansiStyle(styleBright).ansiReset()) else: - stdout.write($ruby) + stdout.write($r) template eprint*(s: varargs[string, `$`]) = {.cast(noSideEffect).}: var a = false @@ -40,181 +41,31 @@ proc getLinedAction*(s: string): TwtAction = return linedActionRemap[s] return NO_ACTION -const breakWord = [ - Rune('\n'), Rune('/'), Rune('\\'), Rune(' '), Rune('&'), Rune('=') -] - -#proc readLine*(prompt: string, current: var string, termwidth: int): bool = -# var new = current -# print(prompt) -# let maxlen = termwidth - prompt.len -# printesc(new) -# var s = "" -# var feedNext = false -# var escNext = false -# var cursor = new.runeLen -# var shift = 0 -# while true: -# var rl = new.runeLen() -# -# if cursor < shift: -# shift = cursor -# elif cursor - shift > maxlen: -# shift += cursor - maxlen -# -# if not feedNext: -# s = "" -# else: -# feedNext = false -# let c = getch() -# s &= c -# var action = getLinedAction(s) -# if escNext: -# action = NO_ACTION -# case action -# of ACTION_LINED_CANCEL: -# return false -# of ACTION_LINED_SUBMIT: -# current = new -# return true -# of ACTION_LINED_BACKSPACE: -# if cursor > 0: -# print(' '.repeat(rl - cursor + 1)) -# print('\b'.repeat(rl - cursor + 1)) -# print("\b \b") -# new = new.runeSubstr(0, cursor - 1) & new.runeSubstr(cursor) -# rl = new.runeLen() -# cursor -= 1 -# printesc(new.runeSubstr(cursor)) -# print('\b'.repeat(rl - cursor)) -# of ACTION_LINED_ESC: -# escNext = true -# of ACTION_LINED_CLEAR: -# print('\r') -# print(' '.repeat(termwidth)) -# print('\r') -# new = new.runeSubstr(cursor) -# rl = new.runeLen() -# printesc(prompt) -# printesc(new.maxString(maxlen + 1)) -# print('\r') -# printesc(prompt) -# cursor = 0 -# of ACTION_LINED_KILL: -# print(' '.repeat(rl - cursor + 1)) -# print('\b'.repeat(rl - cursor + 1)) -# new = new.runeSubstr(0, cursor) -# of ACTION_LINED_BACK: -# if cursor > 0: -# if cursor < maxlen: -# print('\b') -# dec cursor -# of ACTION_LINED_FORWARD: -# if cursor < rl: -# if cursor + 1 < maxlen: -# var rune: Rune -# new.fastRuneAt(cursor, rune, false) -# printesc($rune) -# elif cursor + 1 == maxlen: -# print('$') -# inc cursor -# of ACTION_LINED_PREV_WORD: -# while cursor > 0: -# print('\b') -# cursor -= 1 -# var rune: Rune -# new.fastRuneAt(cursor, rune, false) -# if rune in breakWord: -# break -# of ACTION_LINED_NEXT_WORD: -# while cursor < rl: -# var rune: Rune -# new.fastRuneAt(cursor, rune, false) -# printesc($rune) -# inc cursor -# if cursor < rl: -# new.fastRuneAt(cursor, rune, false) -# if rune in breakWord: -# break -# of ACTION_LINED_KILL_WORD: -# var chars = 0 -# while cursor > chars: -# inc chars -# var rune: Rune -# new.fastRuneAt(cursor - chars, rune, false) -# if rune in breakWord: -# break -# if chars > 0: -# print(' '.repeat(rl - cursor + 1)) -# print('\b'.repeat(rl - cursor + 1)) -# print("\b \b".repeat(chars)) -# new = new.runeSubstr(0, cursor - chars) & new.runeSubstr(cursor) -# rl = new.runeLen() -# cursor -= chars -# printesc(new.runeSubstr(cursor)) -# print('\b'.repeat(rl - cursor)) -# of ACTION_FEED_NEXT: -# feedNext = true -# elif validateUtf8(s) == -1: -# var cs = "" -# for c in s: -# if not c.isControlChar(): -# cs &= c -# elif escNext: -# cs &= c -# escNext = false -# escNext = false -# if cs.len == 0: -# continue -# if rl + 1 < maxlen: -# print(' '.repeat(rl - cursor + 1)) -# print('\b'.repeat(rl - cursor + 1)) -# new = new.runeSubstr(0, cursor) & cs & new.runeSubstr(cursor) -# rl = new.runeLen() -# if cursor - shift > maxlen: -# shift += maxlen - cursor -# if shift == 0: -# printesc(new.runeSubstr(cursor, min(maxlen - cursor - 1, rl))) -# print('\b'.repeat(max(min(maxlen - cursor - 2, rl - cursor - 1), 0))) -# else: -# print('\r') -# print(' '.repeat(termwidth)) -# print('\r') -# print(prompt) -# print(new.runeSubstr(shift, min(maxlen - 1, rl - shift))) -# if maxlen < rl - shift: -# print(new.runeSubstr(shift, maxlen - 1)) -# print('\b'.repeat(maxlen - cursor + shift)) -# else: -# print(new.runeSubstr(shift, rl - shift)) -# print('\b'.repeat(rl + shift - cursor)) -# inc cursor -# else: -# feedNext = true - proc readLine*(prompt: string, current: var string, termwidth: int): bool = - var new = current - print(prompt) + var news = current.toRunes() let maxlen = termwidth - prompt.len - printesc(new) var s = "" var feedNext = false var escNext = false - var cursor = new.runeLen + var comp = false + var compi: uint16 = 0 + var compa = 0 + var comps = "" + var cursor = news.len var shift = 0 while true: - var rl = new.runeLen() - print('\r') - print(' '.repeat(termwidth)) - print('\r') - printesc(prompt & new) - print('\r') - cursorForward(prompt.len + cursor) - + let rl = news.len if cursor < shift: - shift = cursor - elif cursor - shift > maxlen: - shift += cursor - maxlen + shift = max(cursor - 1, 0) + else: + while news.substr(shift, shift + cursor).width() > maxlen - 1: + shift += news[^1].width() + + eraseLine() + printesc(prompt & $news.substr(shift, shift + maxlen - 1)) + + print('\r') + cursorForward(prompt.len + news.substr(shift, cursor).width()) if not feedNext: s = "" @@ -229,21 +80,22 @@ proc readLine*(prompt: string, current: var string, termwidth: int): bool = of ACTION_LINED_CANCEL: return false of ACTION_LINED_SUBMIT: - current = new + current = $news return true of ACTION_LINED_BACKSPACE: if cursor > 0: - new = new.runeSubstr(0, cursor - 1) & new.runeSubstr(cursor) - rl = new.runeLen() + news = news.substr(0, cursor - 1) & news.substr(cursor) dec cursor + of ACTION_LINED_DELETE: + if cursor > 0 and cursor < rl: + news = news.substr(0, cursor) & news.substr(cursor + 1) of ACTION_LINED_ESC: escNext = true of ACTION_LINED_CLEAR: - new = new.runeSubstr(cursor) - rl = new.runeLen() + news = news.substr(cursor) cursor = 0 of ACTION_LINED_KILL: - new = new.runeSubstr(0, cursor) + news = news.substr(0, cursor) of ACTION_LINED_BACK: if cursor > 0: dec cursor @@ -253,32 +105,64 @@ proc readLine*(prompt: string, current: var string, termwidth: int): bool = of ACTION_LINED_PREV_WORD: while cursor > 0: dec cursor - var rune: Rune - new.fastRuneAt(cursor, rune, false) - if rune in breakWord: + if news[cursor].breaksWord(): break of ACTION_LINED_NEXT_WORD: while cursor < rl: - var rune: Rune inc cursor if cursor < rl: - new.fastRuneAt(cursor, rune, false) - if rune in breakWord: + if news[cursor].breaksWord(): break of ACTION_LINED_KILL_WORD: var chars = 0 while cursor > chars: inc chars - var rune: Rune - new.fastRuneAt(cursor - chars, rune, false) - if rune in breakWord: + if news[cursor - chars].breaksWord(): break if chars > 0: - new = new.runeSubstr(0, cursor - chars) & new.runeSubstr(cursor) - rl = new.runeLen() + news = news.substr(0, cursor - chars) & news.substr(cursor) cursor -= chars + of ACTION_LINED_COMPOSE_ON: + comp = true + compi = 0 + compa = 0 + comps = "" + of ACTION_LINED_COMPOSE_OFF: + comp = false + compi = 0 + compa = 0 + comps = "" + of ACTION_LINED_COMPOSE_TOGGLE: + comp = not comp + compi = 0 + compa = 0 + comps = "" of ACTION_FEED_NEXT: feedNext = true + elif comp: + comps &= c + let n = composeRemap.getPrefix(comps, compi) + if n != compi: + compi = n + compa += comps.len + comps = "" + if composeRemap.hasPrefix(comps, compi) and composeRemap.nodes[n].children.len > 0: + feedNext = true + else: + var cs = "" + if composeRemap.nodes[compi].leaf: + cs = composeRemap.nodes[compi].value + else: + cs = s.substr(0, compa - 1) + comps = s.substr(compa) + if not composeRemap.hasPrefix(comps, 0): + cs &= comps + comps = "" + + news = news.substr(0, cursor) & cs.toRunes() & news.substr(cursor) + cursor += cs.runeLen() + compi = 0 + compa = 0 elif validateUtf8(s) == -1: var cs = "" for c in s: @@ -290,8 +174,7 @@ proc readLine*(prompt: string, current: var string, termwidth: int): bool = escNext = false if cs.len == 0: continue - new = new.runeSubstr(0, cursor) & cs & new.runeSubstr(cursor) - rl = new.runeLen() - inc cursor + news = news.substr(0, cursor) & cs.toRunes() & news.substr(cursor) + cursor += cs.runeLen() else: feedNext = true diff --git a/twtstr.nim b/twtstr.nim index 63854879..42a64333 100644 --- a/twtstr.nim +++ b/twtstr.nim @@ -48,8 +48,18 @@ func remove*(str: string, c: string): string = if rem != rune: result &= $rune +const ControlChars = {chr(0x00)..chr(0x1F), chr(0x7F)} + +const Whitespace = { ' ', '\n', '\r', '\t' } + +func isWhitespace*(c: char): bool = + return c in Whitespace + func isControlChar*(c: char): bool = - return c <= char(0x1F) or c == char(0x7F) + return c in ControlChars + +func isControlChar*(r: Rune): bool = + return int(r) <= int(high(char)) and char(r) in ControlChars func getControlChar*(c: char): char = if c >= 'a': @@ -84,11 +94,40 @@ func findChar*(str: string, c: Rune, start: int = 0): int = i = n return -1 +func getLowerChars*(): string = + result = "" + for i in 0..255: + if chr(i) >= 'A' and chr(i) <= 'Z': + result &= chr(i + 32) + else: + result &= chr(i) + +const lowerChars = getLowerChars() + +func tolower*(c: char): char = + return lowerChars[int(c)] + +const breakWord = [ + Rune('\n'), Rune('/'), Rune('\\'), Rune(' '), Rune('&'), Rune('='), + Rune('?'), Rune('.'), Rune(';') +] + +func breaksWord*(r: Rune): bool = + return r in breakWord + +func substr*(s: seq[Rune], i: int, j: int): seq[Rune] = + if s.len == 0: + return @[] + return s[min(high(s), i)..min(high(s), j - 1)] -#Measure length of rune. Transpiled from https://www.cl.cam.ac.uk/~mgk25/ucs/wcwidth.c +func substr*(s: seq[Rune], i: int): seq[Rune] = + if i >= high(s) or s.len == 0: + return @[] + return s[min(high(s), i)..high(s)] + +#Measure length of rune. From https://www.cl.cam.ac.uk/~mgk25/ucs/wcwidth.c #auxiliary function for binary search in interval table -#TODO: use binary search from stdlib? func bisearch(ucs: Rune, table: openarray[(int, int)]): bool = var max = table.high var min = 0 @@ -107,14 +146,13 @@ func bisearch(ucs: Rune, table: openarray[(int, int)]): bool = return true return false - #The following two functions define the column width of an ISO 10646 #character as follows: # # - The null character (U+0000) has a column width of 0. # # - Other C0/C1 control characters and DEL will lead to a return -# value of -1. +# value of 2. # # - Non-spacing and enclosing combining characters (general # category code Mn or Me in the Unicode database) have a @@ -191,21 +229,18 @@ const combining = [ ( 0xE0100, 0xE01EF ) ] -func mk_wcwidth(r: Rune): int = +func width*(r: Rune): int = let ucs = int(r) # sorted list of non-overlapping intervals of non-spacing characters # generated by "uniset +cat=Me +cat=Mn +cat=Cf -00AD +1160-11FF +200B c" - # test for 8-bit control characters - if ucs == 0: - return 0 - if ucs < 32 or (ucs >= 0x7f and ucs < 0xa0): - return -1 - # binary search in table of non-spacing characters if bisearch(r, combining): return 0 + if r.isControlChar(): + return 2 + # if we arrive here, ucs is not a combining or C0/C1 control character if (ucs >= 0x1100 and @@ -224,11 +259,13 @@ func mk_wcwidth(r: Rune): int = return 2 return 1 +func width*(s: string): int = + for r in s.runes(): + result += width(r) -func mk_wcswidth(s: string): int = - for r in s.runes: - result += mk_wcwidth(r) - +func width*(s: seq[Rune]): int = + for r in s: + result += width(r) # # The following functions are the same as mk_wcwidth() and @@ -294,7 +331,7 @@ const ambiguous = [ ( 0xFFFD, 0xFFFD ), ( 0xF0000, 0xFFFFD ), ( 0x100000, 0x10FFFD ) ] -func mk_wcwidth_cjk*(ucs: Rune): int = +func mk_wcwidth_cjk(ucs: Rune): int = # sorted list of non-overlapping intervals of East Asian Ambiguous # characters, generated by "uniset +WIDTH-A -cat=Me -cat=Mn -cat=Cf c" @@ -302,24 +339,14 @@ func mk_wcwidth_cjk*(ucs: Rune): int = if bisearch(ucs, ambiguous): return 2; - return mk_wcwidth(ucs); + return width(ucs); +func mk_wcswidth_cjk(s: string): int = + for r in s.runes: + result += mk_wcwidth_cjk(r) + return result -func mk_wcswidth_cjk*(s: string): int = - #result = 0 - #for r in s.runes: - # result += mk_wcwidth_cjk(r) - #return result - result = 0 - var i = 0 - while i < len(s): - var r: Rune - fastRuneAt(s, i, r, false) - if uint(s[i]) <= 127: inc(i) - elif uint(s[i]) shr 5 == 0b110: inc(i, 2) - elif uint(s[i]) shr 4 == 0b1110: inc(i, 3) - elif uint(s[i]) shr 3 == 0b11110: inc(i, 4) - elif uint(s[i]) shr 2 == 0b111110: inc(i, 5) - elif uint(s[i]) shr 1 == 0b1111110: inc(i, 6) - else: inc i - inc(result, mk_wcwidth_cjk(r)) + +proc skipBlanks*(buf: string, at: var int) = + while at < buf.len and buf[at].isWhitespace(): + inc at |