diff --git a/.gitignore b/.gitignore index f8804fc..1d5dc4c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,2 @@ bin/*.n -bin/*.js* \ No newline at end of file +bin/*.js* diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..b0c6000 --- /dev/null +++ b/Makefile @@ -0,0 +1,17 @@ +LIB_NAME=comark +LIB_FILE=$(LIB_NAME).zip + +upload: + haxelib submit comark.zip + +lib: $(LIB_FILE) clean + +$(LIB_FILE): haxelib.json src/*/* + -mkdir temp + cp haxelib.json temp/ + cp -R src/comark temp/ + cd temp; zip -X -r $(LIB_FILE) .; mv $(LIB_FILE) ../ + +clean: + -rm -rf temp + diff --git a/bin/tests/github.txt b/bin/tests/github.txt new file mode 100644 index 0000000..7a09d0e --- /dev/null +++ b/bin/tests/github.txt @@ -0,0 +1,43 @@ +--- +title: CommonMark Github flavour Spec +author: +- ConstNW +version: 0.1 +date: 2014-10-29 +... + + +# Strike + + +. +~~foo~~ +. +
foo
~~foo
+. + +# Hashtag + +. +#hashtag +. + +. + +. +#hash tag +. +#hash tag
+. + + + +# Appendix diff --git a/bin/tests/spec.txt b/bin/tests/spec.txt index 0307df1..2459321 100644 --- a/bin/tests/spec.txt +++ b/bin/tests/spec.txt @@ -2,8 +2,8 @@ title: CommonMark Spec author: - John MacFarlane -version: 1 -date: 2014-09-06 +version: 0.6 +date: 2014-10-26 ... # Introduction @@ -192,10 +192,10 @@ In the examples, the `→` character is used to represent tabs. # Preprocessing A [line](#line) -is a sequence of zero or more characters followed by a line -ending (CR, LF, or CRLF) or by the end of -file. +is a sequence of zero or more [characters](#character) followed by a +line ending (CR, LF, or CRLF) or by the end of file. +A [character](#character) is a unicode code point. This spec does not specify an encoding; it thinks of lines as composed of characters rather than bytes. A conforming parser may be limited to a certain encoding. @@ -377,16 +377,18 @@ Spaces are allowed at the end:_ _ _ _ a
a------
+---a---
. It is required that all of the non-space characters be the same. @@ -426,8 +428,11 @@ barbar
. -Note, however, that this is a setext header, not a paragraph followed -by a horizontal rule: +If a line of dashes that meets the above conditions for being a +horizontal rule could also be interpreted as the underline of a [setext +header](#setext-header), the interpretation as a +[setext-header](#setext-header) takes precedence. Thus, for example, +this is a setext header, not a paragraph followed by a horizontal rule: . Foo @@ -474,11 +479,11 @@ consists of a string of characters, parsed as inline content, between an opening sequence of 1--6 unescaped `#` characters and an optional closing sequence of any number of `#` characters. The opening sequence of `#` characters cannot be followed directly by a nonspace character. -The closing `#` characters may be followed by spaces only. The opening -`#` character may be indented 0-3 spaces. The raw contents of the -header are stripped of leading and trailing spaces before being parsed -as inline content. The header level is equal to the number of `#` -characters in the opening sequence. +The optional closing sequence of `#`s must be preceded by a space and may be +followed by spaces only. The opening `#` character may be indented 0-3 +spaces. The raw contents of the header are stripped of leading and +trailing spaces before being parsed as inline content. The header level +is equal to the number of `#` characters in the opening sequence. Simple headers: @@ -609,16 +614,24 @@ header:of dashes"/>
. -The setext header underline cannot be a lazy line: +The setext header underline cannot be a [lazy continuation +line](#lazy-continuation-line) in a list item or block quote: . > Foo @@ -819,6 +836,16 @@ The setext header underline cannot be a lazy line:====
. +Setext header text lines must not be interpretable as block +constructs other than paragraphs. So, the line of dashes +in these examples gets interpreted as a horizontal rule: + +. +--- +--- +. +foo
+
+++foo
+
aaa
+
+.
+
+.
+ ```
+aaa
+ ```
+.
+aaa
+
+.
+
+This is not a closing fence, because it is indented 4 spaces:
+
+.
+```
+aaa
+ ```
+.
+aaa
+ ```
+
+.
+
+
Code fences (opening and closing) cannot contain internal spaces:
.
@@ -1355,8 +1466,8 @@ name is one of the following (case-insensitive):
`output`, `col`, `p`, `colgroup`, `pre`, `dd`, `progress`, `div`,
`section`, `dl`, `table`, `td`, `dt`, `tbody`, `embed`, `textarea`,
`fieldset`, `tfoot`, `figcaption`, `th`, `figure`, `thead`, `footer`,
-`footer`, `tr`, `form`, `ul`, `h1`, `h2`, `h3`, `h4`, `h5`, `h6`,
-`video`, `script`, `style`.
+`tr`, `form`, `ul`, `h1`, `h2`, `h3`, `h4`, `h5`, `h6`, `video`,
+`script`, `style`.
An [HTML block](#html-block) begins with an
[HTML block tag](#html-block-tag), [HTML comment](#html-comment),
@@ -1401,7 +1512,7 @@ okay.
baz
` tags, while -paragraphs in a tight list are not.) +A list is [loose](#loose) if it any of its constituent +list items are separated by blank lines, or if any of its constituent +list items directly contain two block-level elements with a blank line +between them. Otherwise a list is [tight](#tight). +(The difference in HTML output is that paragraphs in a loose list are +wrapped in `
` tags, while paragraphs in a tight list are not.) Changing the bullet or ordered list delimiter starts a new list: @@ -3247,6 +3376,87 @@ Changing the bullet or ordered list delimiter starts a new list: . +In CommonMark, a list can interrupt a paragraph. That is, +no blank line is needed to separate a paragraph from a following +list: + +. +Foo +- bar +- baz +. +
Foo
+The number of windows in my house is
+` tags, since the list is "tight"), +then + + I need to buy + - new shoes + - a coat + - a plane ticket + +by itself should be a paragraph followed by a nested sublist. + +Our adherence to the [principle of uniformity](#principle-of-uniformity) +thus inclines us to think that there are two coherent packages: + +1. Require blank lines before *all* lists and blockquotes, + including lists that occur as sublists inside other list items. + +2. Require blank lines in none of these places. + +[reStructuredText](http://docutils.sourceforge.net/rst.html) takes +the first approach, for which there is much to be said. But the second +seems more consistent with established practice with Markdown. + There can be blank lines between items, but two blank lines end a list: @@ -3463,8 +3673,8 @@ This is a tight list, because the blank lines are in a code block: . This is a tight list, because the blank line is between two -paragraphs of a sublist. So the inner list is loose while -the other list is tight: +paragraphs of a sublist. So the sublist is loose while +the outer list is tight: . - a @@ -3650,7 +3860,8 @@ If a backslash is itself escaped, the following character is not:
\emphasis
. -A backslash at the end of the line is a hard line break: +A backslash at the end of the line is a [hard line +break](#hard-line-break): . foo\ @@ -3686,9 +3897,9 @@ raw HTML: . . -& © Æ Ď ¾ ℋ ⅆ ∲
+& © Æ Ď ¾ ℋ ⅆ ∲
. [Decimal entities](#decimal-entities) -consist of `` + a string of 1--8 arabic digits + `;`. +consist of `` + a string of 1--8 arabic digits + `;`. Again, these +entities need to be recognised and tranformed into their corresponding +UTF8 codepoints. Invalid Unicode codepoints will be written as the +"unknown codepoint" character (`0xFFFD`) . - # Ӓ Ϡ +# Ӓ Ϡ . -# Ӓ Ϡ
+# Ӓ Ϡ �
. [Hexadecimal entities](#hexadecimal-entities) consist of `` + either `X` or `x` + a string of 1-8 hexadecimal digits -+ `;`. ++ `;`. They will also be parsed and turned into their corresponding UTF8 values in the AST. . - " ആ ಫ +" ആ ಫ . -" ആ ಫ
+" ആ ಫ
. Here are some nonentities: . -  &x; &ThisIsWayTooLongToBeAnEntityIsntIt; &hi?; +  &x; &ThisIsWayTooLongToBeAnEntityIsntIt; &hi?; . -  &x; &#; &#x; � &ThisIsWayTooLongToBeAnEntityIsntIt; &hi?;
+  &x; &#; &#x; &ThisIsWayTooLongToBeAnEntityIsntIt; &hi?;
. Although HTML5 does accept some entities without a trailing semicolon -(such as `©`), these are not recognized as entities here: +(such as `©`), these are not recognized as entities here, because it +makes the grammar too ambiguous: . © @@ -3775,13 +4004,13 @@ Although HTML5 does accept some entities without a trailing semicolon©
. -On the other hand, many strings that are not on the list of HTML5 -named entities are recognized as entities here: +Strings that are not on the list of HTML5 named entities are not +recognized as entities either: . &MadeUpEntity; . -&MadeUpEntity;
+&MadeUpEntity;
. Entities are recognized in any context besides code spans or @@ -3797,7 +4026,7 @@ code blocks, including raw HTML, URLs, [link titles](#link-title), and . [foo](/föö "föö") . - + . . @@ -3805,7 +4034,7 @@ code blocks, including raw HTML, URLs, [link titles](#link-title), and [foo]: /föö "föö" . - + . . @@ -3813,7 +4042,7 @@ code blocks, including raw HTML, URLs, [link titles](#link-title), and foo ``` . -foo
+foo
.
@@ -3946,7 +4175,7 @@ But this is a link:
.
`
.
-
+
.
And this is an HTML tag:
@@ -4024,15 +4253,15 @@ for efficient parsing strategies that do not backtrack:
(a) it is not part of a sequence of four or more unescaped `*`s,
(b) it is not followed by whitespace, and
(c) either it is not followed by a `*` character or it is
- followed immediately by strong emphasis.
+ followed immediately by emphasis or strong emphasis.
2. A single `_` character [can open emphasis](#can-open-emphasis) iff
(a) it is not part of a sequence of four or more unescaped `_`s,
(b) it is not followed by whitespace,
- (c) is is not preceded by an ASCII alphanumeric character, and
+ (c) it is not preceded by an ASCII alphanumeric character, and
(d) either it is not followed by a `_` character or it is
- followed immediately by strong emphasis.
+ followed immediately by emphasis or strong emphasis.
3. A single `*` character [can close emphasis](#can-close-emphasis)
iff
@@ -4077,16 +4306,42 @@ for efficient parsing strategies that do not backtrack:
(c) it is not followed by an ASCII alphanumeric character.
9. Emphasis begins with a delimiter that [can open
- emphasis](#can-open-emphasis) and includes inlines parsed
- sequentially until a delimiter that [can close
+ emphasis](#can-open-emphasis) and ends with a delimiter that [can close
emphasis](#can-close-emphasis), and that uses the same
- character (`_` or `*`) as the opening delimiter, is reached.
+ character (`_` or `*`) as the opening delimiter. The inlines
+ between the open delimiter and the closing delimiter are the
+ contents of the emphasis inline.
10. Strong emphasis begins with a delimiter that [can open strong
- emphasis](#can-open-strong-emphasis) and includes inlines parsed
- sequentially until a delimiter that [can close strong
- emphasis](#can-close-strong-emphasis), and that uses the
- same character (`_` or `*`) as the opening delimiter, is reached.
+ emphasis](#can-open-strong-emphasis) and ends with a delimiter that
+ [can close strong emphasis](#can-close-strong-emphasis), and that uses the
+ same character (`_` or `*`) as the opening delimiter. The inlines
+ between the open delimiter and the closing delimiter are the
+ contents of the strong emphasis inline.
+
+Where rules 1--10 above are compatible with multiple parsings,
+the following principles resolve ambiguity:
+
+11. An interpretation `...` is always preferred to
+ `...`.
+
+12. An interpretation `...` is always
+ preferred to `..`.
+
+13. Earlier closings are preferred to later closings. Thus,
+ when two potential emphasis or strong emphasis spans overlap,
+ the first takes precedence: for example, `*foo _bar* baz_`
+ is parsed as `foo _bar baz_` rather than
+ `*foo bar* baz`. For the same reason,
+ `**foo*bar**` is parsed as `foobar*`
+ rather than `foo*bar`.
+
+14. Inline code spans, links, images, and HTML tags group more tightly
+ than emphasis. So, when there is a choice between an interpretation
+ that contains one of these elements and one that does not, the
+ former always wins. Thus, for example, `*[foo*](bar)` is
+ parsed as `*foo*` rather than as
+ `[foo](bar)`.
These rules can be illustrated through a series of examples.
@@ -4334,6 +4589,32 @@ __this is a double underscore (`__`)__
this is a double underscore (__
)
.
+Or use the other emphasis character:
+
+.
+*_*
+.
+_
+.
+
+.
+_*_
+.
+*
+.
+
+.
+*__*
+.
+__
+.
+
+.
+_**_
+.
+**
+.
+
`*` delimiters allow intra-word emphasis; `_` delimiters do not:
.
@@ -4509,6 +4790,36 @@ __foo _bar_ baz__
foo bar baz
.
+.
+**foo, *bar*, baz**
+.
+foo, bar, baz
+.
+
+.
+__foo, _bar_, baz__
+.
+foo, bar, baz
+.
+
+But note:
+
+.
+*foo**bar**baz*
+.
+foobarbaz
+.
+
+.
+**foo*bar*baz**
+.
+foobarbaz**
+.
+
+The difference is that in the two preceding cases,
+the internal delimiters [can close emphasis](#can-close-emphasis),
+while in the cases with spaces, they cannot.
+
Note that you cannot nest emphasis directly inside emphasis
using the same delimeter, or strong emphasis directly inside
strong emphasis:
@@ -4561,6 +4872,18 @@ similarly for `_` and `__`):
foo*
.
+.
+***foo* bar***
+.
+foo bar*
+.
+
+.
+***foo** bar***
+.
+foo bar**
+.
+
The following contains no strong emphasis, because the opening
delimiter is closed by the first `*` before `bar`:
@@ -4578,7 +4901,7 @@ However, a string of four or more `****` can never close emphasis:
*foo****
.
-Note that there are some asymmetries here:
+We retain symmetry in these cases:
.
*foo**
@@ -4586,7 +4909,7 @@ Note that there are some asymmetries here:
**foo*
.
foo*
-**foo*
+*foo
.
.
@@ -4595,17 +4918,11 @@ Note that there are some asymmetries here:
**foo* bar*
.
foo bar
-**foo* bar*
+foo bar
.
More cases with mismatched delimiters:
-.
-**foo* bar*
-.
-**foo* bar*
-.
-
.
*bar***
.
@@ -4615,7 +4932,7 @@ More cases with mismatched delimiters:
.
***foo*
.
-***foo*
+**foo
.
.
@@ -4627,7 +4944,7 @@ More cases with mismatched delimiters:
.
***foo**
.
-***foo**
+*foo
.
.
@@ -4636,6 +4953,46 @@ More cases with mismatched delimiters:
***foo bar
.
+The following cases illustrate rule 13:
+
+.
+*foo _bar* baz_
+.
+foo _bar baz_
+.
+
+.
+**foo bar* baz**
+.
+foo bar baz*
+.
+
+The following cases illustrate rule 14:
+
+.
+*[foo*](bar)
+.
+*foo*
+.
+
+.
+*![foo*](bar)
+.
+*
+.
+
+.
+*
+.
+*
+.
+
+.
+*a`a*`
+.
+*aa*
+.
+
## Links
A link contains a [link label](#link-label) (the visible text),
@@ -4743,7 +5100,7 @@ braces:
.
[link]()
.
-
+
.
The destination cannot contain line breaks, even with pointy braces:
@@ -4794,12 +5151,15 @@ in Markdown:
.
-URL-escaping and entities should be left alone inside the destination:
+URL-escaping should be left alone inside the destination, as all
+URL-escaped characters are also valid URL characters. HTML entities in
+the destination will be parsed into their UTF-8 codepoints, as usual, and
+optionally URL-escaped when written as HTML.
.
[link](foo%20bä)
.
-
+
.
Note that, because titles can often be parsed as destinations,
@@ -4809,7 +5169,7 @@ get unexpected results:
.
[link]("title")
.
-
+
.
Titles may be in single quotes, double quotes, or parentheses:
@@ -5479,9 +5839,9 @@ spec](http://www.whatwg.org/specs/web-apps/current-work/multipage/forms.html#e-m
Examples of email autolinks:
.
-
+
.
-
+
.
.
@@ -5523,15 +5883,15 @@ These are not autolinks:
.
.
-http://google.com
+http://example.com
.
-http://google.com
+http://example.com
.
.
-foo@bar.baz.com
+foo@bar.example.com
.
-foo@bar.baz.com
+foo@bar.example.com
.
## Raw HTML
@@ -5771,7 +6131,8 @@ Backslash escapes do not work in HTML attributes:
## Hard line breaks
A line break (not in a code span or HTML tag) that is preceded
-by two or more spaces is parsed as a linebreak (rendered
+by two or more spaces is parsed as a [hard line
+break](#hard-line-break) (rendered
in HTML as a `
` tag):
.
@@ -6121,5 +6482,3 @@ an `emph`.
The document can be rendered as HTML, or in any other format, given
an appropriate renderer.
-
-
diff --git a/comark-client.hxml b/comark-js.hxml
similarity index 100%
rename from comark-client.hxml
rename to comark-js.hxml
diff --git a/comark-client.hxproj b/comark-js.hxproj
similarity index 100%
rename from comark-client.hxproj
rename to comark-js.hxproj
diff --git a/comark.zip b/comark.zip
new file mode 100644
index 0000000..04eb2b9
Binary files /dev/null and b/comark.zip differ
diff --git a/haxelib.json b/haxelib.json
index 2677503..bc19904 100644
--- a/haxelib.json
+++ b/haxelib.json
@@ -4,8 +4,8 @@
"tags": ["cross","utility"],
"description": "Comark is CommonMark (Markdown) library.",
"contributors": ["constnw"],
- "releasenote": "Github Flavor added",
- "version": "0.0.2",
+ "releasenote": "Updated to CommonMark spec v0.6",
+ "version": "0.0.3",
"url": "http://github.com/constnw/comark/",
"dependencies": {
diff --git a/src/comark/DocParser.hx b/src/comark/DocParser.hx
index ec2b19d..152959c 100644
--- a/src/comark/DocParser.hx
+++ b/src/comark/DocParser.hx
@@ -17,9 +17,6 @@ class DocParser
static var HTMLBLOCKOPEN = "<(?:" + BLOCKTAGNAME + "[\\s/>]" + "|" + "/" + BLOCKTAGNAME + "[\\s>]" + "|" + "[?!])";
static var reHtmlBlockOpen = new EReg('^' + HTMLBLOCKOPEN, 'i');
- public static var ESCAPABLE = '[!"#$%&\'()*+,./:;<=>?@[\\\\\\]^_`{|}~-]';
- public static var reAllEscapedChar = new EReg('\\\\(' + ESCAPABLE + ')', 'g');
-
var doc : BlockElement;
var tip : BlockElement;
@@ -54,7 +51,7 @@ class DocParser
var i = 0;
do incorporateLine(lines[i], i+1) while ( len > ++i );
- while ( tip != null ) finalize(tip, len - 1);
+ while ( tip != null ) { finalize(tip, len - 1); }
processInlines(doc);
@@ -254,7 +251,10 @@ class DocParser
// remove trailing ###s:
var s = ln.substr(offset);
- container.strings = [~/(?:(\\#) *#*| *#+) *$/.replace(s, '$1')];
+ container.strings = [
+ ~/ +#+ *$/.replace(
+ ~/^ *#+ *$/.replace(s, ''), '')
+ ];
break;
}
else if ( erCode.match(ln.substr(first_nonspace)) )
@@ -455,7 +455,7 @@ class DocParser
case 'FencedCode':
// first line becomes info string
- block.info = unescape(block.strings[0].trim());
+ block.info = InlineParser.unescapeString(block.strings[0].trim());
if ( block.strings.length == 1 )
block.string_content = '';
else
@@ -579,14 +579,6 @@ class DocParser
lastStop = offset + 1;
return result;
});
-
- /*
- return text.replace(reAllTab, function(match, offset) {
- var result = ' '.slice((offset - lastStop) % 4);
- lastStop = offset + 1;
- return result;
- });
- */
}
// Attempt to match a regex in string s at offset offset.
@@ -694,9 +686,6 @@ class DocParser
// Returns true if block type can accept lines of text.
function acceptsLines( block_type : String ) : Bool return ( block_type == 'Paragraph' || block_type == 'IndentedCode' || block_type == 'FencedCode' );
- // Replace backslash escapes with literal characters.
- function unescape( s ) return reAllEscapedChar.replace(s, '$1');
-
// Returns true if string contains only space characters.
function isBlank(s) return ~/^\s*$/.match(s);
@@ -713,4 +702,12 @@ class DocParser
else
return false;
}
+/*
+ breakOutOfLists: breakOutOfLists,
+ addLine: addLine,
+ addChild: addChild,
+ incorporateLine: incorporateLine,
+ finalize: finalize,
+ processInlines: processInlines,
+*/
}
\ No newline at end of file
diff --git a/src/comark/EntityToChar.hx b/src/comark/EntityToChar.hx
new file mode 100644
index 0000000..6271523
--- /dev/null
+++ b/src/comark/EntityToChar.hx
@@ -0,0 +1,2213 @@
+package comark;
+
+/**
+ * ...
+ * @author Const
+ */
+class EntityToChar
+{
+ static var entities = {
+ AAacute: 'Á',
+ aacute: 'á',
+ Abreve: 'Ă',
+ abreve: 'ă',
+ ac: '∾',
+ acd: '∿',
+ acE: '∾',
+ Acirc: 'Â',
+ acirc: 'â',
+ acute: '´',
+ Acy: 'А',
+ acy: 'а',
+ AElig: 'Æ',
+ aelig: 'æ',
+ af: '',
+ Afr: '𝔄',
+ afr: '𝔞',
+ Agrave: 'À',
+ agrave: 'à',
+ alefsym: 'ℵ',
+ aleph: 'ℵ',
+ Alpha: 'Α',
+ alpha: 'α',
+ Amacr: 'Ā',
+ amacr: 'ā',
+ amalg: '⨿',
+ amp: '&',
+ AMP: '&',
+ andand: '⩕',
+ And: '⩓',
+ and: '∧',
+ andd: '⩜',
+ andslope: '⩘',
+ andv: '⩚',
+ ang: '∠',
+ ange: '⦤',
+ angle: '∠',
+ angmsdaa: '⦨',
+ angmsdab: '⦩',
+ angmsdac: '⦪',
+ angmsdad: '⦫',
+ angmsdae: '⦬',
+ angmsdaf: '⦭',
+ angmsdag: '⦮',
+ angmsdah: '⦯',
+ angmsd: '∡',
+ angrt: '∟',
+ angrtvb: '⊾',
+ angrtvbd: '⦝',
+ angsph: '∢',
+ angst: 'Å',
+ angzarr: '⍼',
+ Aogon: 'Ą',
+ aogon: 'ą',
+ Aopf: '𝔸',
+ aopf: '𝕒',
+ apacir: '⩯',
+ ap: '≈',
+ apE: '⩰',
+ ape: '≊',
+ apid: '≋',
+ apos: '\'',
+ ApplyFunction: '',
+ approx: '≈',
+ approxeq: '≊',
+ Aring: 'Å',
+ aring: 'å',
+ Ascr: '𝒜',
+ ascr: '𝒶',
+ Assign: '≔',
+ ast: '*',
+ asymp: '≈',
+ asympeq: '≍',
+ Atilde: 'Ã',
+ atilde: 'ã',
+ Auml: 'Ä',
+ auml: 'ä',
+ awconint: '∳',
+ awint: '⨑',
+ backcong: '≌',
+ backepsilon: '϶',
+ backprime: '‵',
+ backsim: '∽',
+ backsimeq: '⋍',
+ Backslash: '∖',
+ Barv: '⫧',
+ barvee: '⊽',
+ barwed: '⌅',
+ Barwed: '⌆',
+ barwedge: '⌅',
+ bbrk: '⎵',
+ bbrktbrk: '⎶',
+ bcong: '≌',
+ Bcy: 'Б',
+ bcy: 'б',
+ bdquo: '„',
+ becaus: '∵',
+ because: '∵',
+ Because: '∵',
+ bemptyv: '⦰',
+ bepsi: '϶',
+ bernou: 'ℬ',
+ Bernoullis: 'ℬ',
+ Beta: 'Β',
+ beta: 'β',
+ beth: 'ℶ',
+ between: '≬',
+ Bfr: '𝔅',
+ bfr: '𝔟',
+ bigcap: '⋂',
+ bigcirc: '◯',
+ bigcup: '⋃',
+ bigodot: '⨀',
+ bigoplus: '⨁',
+ bigotimes: '⨂',
+ bigsqcup: '⨆',
+ bigstar: '★',
+ bigtriangledown: '▽',
+ bigtriangleup: '△',
+ biguplus: '⨄',
+ bigvee: '⋁',
+ bigwedge: '⋀',
+ bkarow: '⤍',
+ blacklozenge: '⧫',
+ blacksquare: '▪',
+ blacktriangle: '▴',
+ blacktriangledown: '▾',
+ blacktriangleleft: '◂',
+ blacktriangleright: '▸',
+ blank: '␣',
+ blk12: '▒',
+ blk14: '░',
+ blk34: '▓',
+ block: '█',
+ bne: '=',
+ bnequiv: '≡',
+ bNot: '⫭',
+ bnot: '⌐',
+ Bopf: '𝔹',
+ bopf: '𝕓',
+ bot: '⊥',
+ bottom: '⊥',
+ bowtie: '⋈',
+ boxbox: '⧉',
+ boxdl: '┐',
+ boxdL: '╕',
+ boxDl: '╖',
+ boxDL: '╗',
+ boxdr: '┌',
+ boxdR: '╒',
+ boxDr: '╓',
+ boxDR: '╔',
+ boxh: '─',
+ boxH: '═',
+ boxhd: '┬',
+ boxHd: '╤',
+ boxhD: '╥',
+ boxHD: '╦',
+ boxhu: '┴',
+ boxHu: '╧',
+ boxhU: '╨',
+ boxHU: '╩',
+ boxminus: '⊟',
+ boxplus: '⊞',
+ boxtimes: '⊠',
+ boxul: '┘',
+ boxuL: '╛',
+ boxUl: '╜',
+ boxUL: '╝',
+ boxur: '└',
+ boxuR: '╘',
+ boxUr: '╙',
+ boxUR: '╚',
+ boxv: '│',
+ boxV: '║',
+ boxvh: '┼',
+ boxvH: '╪',
+ boxVh: '╫',
+ boxVH: '╬',
+ boxvl: '┤',
+ boxvL: '╡',
+ boxVl: '╢',
+ boxVL: '╣',
+ boxvr: '├',
+ boxvR: '╞',
+ boxVr: '╟',
+ boxVR: '╠',
+ bprime: '‵',
+ breve: '˘',
+ Breve: '˘',
+ brvbar: '¦',
+ bscr: '𝒷',
+ Bscr: 'ℬ',
+ bsemi: '⁏',
+ bsim: '∽',
+ bsime: '⋍',
+ bsolb: '⧅',
+ bsol: '\\',
+ bsolhsub: '⟈',
+ bull: '•',
+ bullet: '•',
+ bump: '≎',
+ bumpE: '⪮',
+ bumpe: '≏',
+ Bumpeq: '≎',
+ bumpeq: '≏',
+ Cacute: 'Ć',
+ cacute: 'ć',
+ capand: '⩄',
+ capbrcup: '⩉',
+ capcap: '⩋',
+ cap: '∩',
+ Cap: '⋒',
+ capcup: '⩇',
+ capdot: '⩀',
+ CapitalDifferentialD: 'ⅅ',
+ caps: '∩',
+ caret: '⁁',
+ caron: 'ˇ',
+ Cayleys: 'ℭ',
+ ccaps: '⩍',
+ Ccaron: 'Č',
+ ccaron: 'č',
+ Ccedil: 'Ç',
+ ccedil: 'ç',
+ Ccirc: 'Ĉ',
+ ccirc: 'ĉ',
+ Cconint: '∰',
+ ccups: '⩌',
+ ccupssm: '⩐',
+ Cdot: 'Ċ',
+ cdot: 'ċ',
+ cedil: '¸',
+ Cedilla: '¸',
+ cemptyv: '⦲',
+ cent: '¢',
+ centerdot: '·',
+ CenterDot: '·',
+ cfr: '𝔠',
+ Cfr: 'ℭ',
+ CHcy: 'Ч',
+ chcy: 'ч',
+ check: '✓',
+ checkmark: '✓',
+ Chi: 'Χ',
+ chi: 'χ',
+ circ: 'ˆ',
+ circeq: '≗',
+ circlearrowleft: '↺',
+ circlearrowright: '↻',
+ circledast: '⊛',
+ circledcirc: '⊚',
+ circleddash: '⊝',
+ CircleDot: '⊙',
+ circledR: '®',
+ circledS: 'Ⓢ',
+ CircleMinus: '⊖',
+ CirclePlus: '⊕',
+ CircleTimes: '⊗',
+ cir: '○',
+ cirE: '⧃',
+ cire: '≗',
+ cirfnint: '⨐',
+ cirmid: '⫯',
+ cirscir: '⧂',
+ ClockwiseContourIntegral: '∲',
+ CloseCurlyDoubleQuote: '”',
+ CloseCurlyQuote: '’',
+ clubs: '♣',
+ clubsuit: '♣',
+ colon: ':',
+ Colon: '∷',
+ Colone: '⩴',
+ colone: '≔',
+ coloneq: '≔',
+ comma: ',',
+ commat: '@',
+ comp: '∁',
+ compfn: '∘',
+ complement: '∁',
+ complexes: 'ℂ',
+ cong: '≅',
+ congdot: '⩭',
+ Congruent: '≡',
+ conint: '∮',
+ Conint: '∯',
+ ContourIntegral: '∮',
+ copf: '𝕔',
+ Copf: 'ℂ',
+ coprod: '∐',
+ Coproduct: '∐',
+ copy: '©',
+ COPY: '©',
+ copysr: '℗',
+ CounterClockwiseContourIntegral: '∳',
+ crarr: '↵',
+ cross: '✗',
+ Cross: '⨯',
+ Cscr: '𝒞',
+ cscr: '𝒸',
+ csub: '⫏',
+ csube: '⫑',
+ csup: '⫐',
+ csupe: '⫒',
+ ctdot: '⋯',
+ cudarrl: '⤸',
+ cudarrr: '⤵',
+ cuepr: '⋞',
+ cuesc: '⋟',
+ cularr: '↶',
+ cularrp: '⤽',
+ cupbrcap: '⩈',
+ cupcap: '⩆',
+ CupCap: '≍',
+ cup: '∪',
+ Cup: '⋓',
+ cupcup: '⩊',
+ cupdot: '⊍',
+ cupor: '⩅',
+ cups: '∪',
+ curarr: '↷',
+ curarrm: '⤼',
+ curlyeqprec: '⋞',
+ curlyeqsucc: '⋟',
+ curlyvee: '⋎',
+ curlywedge: '⋏',
+ curren: '¤',
+ curvearrowleft: '↶',
+ curvearrowright: '↷',
+ cuvee: '⋎',
+ cuwed: '⋏',
+ cwconint: '∲',
+ cwint: '∱',
+ cylcty: '⌭',
+ dagger: '†',
+ Dagger: '‡',
+ daleth: 'ℸ',
+ darr: '↓',
+ Darr: '↡',
+ dArr: '⇓',
+ dash: '‐',
+ Dashv: '⫤',
+ dashv: '⊣',
+ dbkarow: '⤏',
+ dblac: '˝',
+ Dcaron: 'Ď',
+ dcaron: 'ď',
+ Dcy: 'Д',
+ dcy: 'д',
+ ddagger: '‡',
+ ddarr: '⇊',
+ DD: 'ⅅ',
+ dd: 'ⅆ',
+ DDotrahd: '⤑',
+ ddotseq: '⩷',
+ deg: '°',
+ Del: '∇',
+ Delta: 'Δ',
+ delta: 'δ',
+ demptyv: '⦱',
+ dfisht: '⥿',
+ Dfr: '𝔇',
+ dfr: '𝔡',
+ dHar: '⥥',
+ dharl: '⇃',
+ dharr: '⇂',
+ DiacriticalAcute: '´',
+ DiacriticalDot: '˙',
+ DiacriticalDoubleAcute: '˝',
+ DiacriticalGrave: '`',
+ DiacriticalTilde: '˜',
+ diam: '⋄',
+ diamond: '⋄',
+ Diamond: '⋄',
+ diamondsuit: '♦',
+ diams: '♦',
+ die: '¨',
+ DifferentialD: 'ⅆ',
+ digamma: 'ϝ',
+ disin: '⋲',
+ div: '÷',
+ divide: '÷',
+ divideontimes: '⋇',
+ divonx: '⋇',
+ DJcy: 'Ђ',
+ djcy: 'ђ',
+ dlcorn: '⌞',
+ dlcrop: '⌍',
+ dollar: '$',
+ Dopf: '𝔻',
+ dopf: '𝕕',
+ Dot: '¨',
+ dot: '˙',
+ DotDot: '⃜',
+ doteq: '≐',
+ doteqdot: '≑',
+ DotEqual: '≐',
+ dotminus: '∸',
+ dotplus: '∔',
+ dotsquare: '⊡',
+ doublebarwedge: '⌆',
+ DoubleContourIntegral: '∯',
+ DoubleDot: '¨',
+ DoubleDownArrow: '⇓',
+ DoubleLeftArrow: '⇐',
+ DoubleLeftRightArrow: '⇔',
+ DoubleLeftTee: '⫤',
+ DoubleLongLeftArrow: '⟸',
+ DoubleLongLeftRightArrow: '⟺',
+ DoubleLongRightArrow: '⟹',
+ DoubleRightArrow: '⇒',
+ DoubleRightTee: '⊨',
+ DoubleUpArrow: '⇑',
+ DoubleUpDownArrow: '⇕',
+ DoubleVerticalBar: '∥',
+ DownArrowBar: '⤓',
+ downarrow: '↓',
+ DownArrow: '↓',
+ Downarrow: '⇓',
+ DownArrowUpArrow: '⇵',
+ DownBreve: '̑',
+ downdownarrows: '⇊',
+ downharpoonleft: '⇃',
+ downharpoonright: '⇂',
+ DownLeftRightVector: '⥐',
+ DownLeftTeeVector: '⥞',
+ DownLeftVectorBar: '⥖',
+ DownLeftVector: '↽',
+ DownRightTeeVector: '⥟',
+ DownRightVectorBar: '⥗',
+ DownRightVector: '⇁',
+ DownTeeArrow: '↧',
+ DownTee: '⊤',
+ drbkarow: '⤐',
+ drcorn: '⌟',
+ drcrop: '⌌',
+ Dscr: '𝒟',
+ dscr: '𝒹',
+ DScy: 'Ѕ',
+ dscy: 'ѕ',
+ dsol: '⧶',
+ Dstrok: 'Đ',
+ dstrok: 'đ',
+ dtdot: '⋱',
+ dtri: '▿',
+ dtrif: '▾',
+ duarr: '⇵',
+ duhar: '⥯',
+ dwangle: '⦦',
+ DZcy: 'Џ',
+ dzcy: 'џ',
+ dzigrarr: '⟿',
+ Eacute: 'É',
+ eacute: 'é',
+ easter: '⩮',
+ Ecaron: 'Ě',
+ ecaron: 'ě',
+ Ecirc: 'Ê',
+ ecirc: 'ê',
+ ecir: '≖',
+ ecolon: '≕',
+ Ecy: 'Э',
+ ecy: 'э',
+ eDDot: '⩷',
+ Edot: 'Ė',
+ edot: 'ė',
+ eDot: '≑',
+ ee: 'ⅇ',
+ efDot: '≒',
+ Efr: '𝔈',
+ efr: '𝔢',
+ eg: '⪚',
+ Egrave: 'È',
+ egrave: 'è',
+ egs: '⪖',
+ egsdot: '⪘',
+ el: '⪙',
+ Element: '∈',
+ elinters: '⏧',
+ ell: 'ℓ',
+ els: '⪕',
+ elsdot: '⪗',
+ Emacr: 'Ē',
+ emacr: 'ē',
+ empty: '∅',
+ emptyset: '∅',
+ EmptySmallSquare: '◻',
+ emptyv: '∅',
+ EmptyVerySmallSquare: '▫',
+ emsp13: ' ',
+ emsp14: ' ',
+ emsp: ' ',
+ ENG: 'Ŋ',
+ eng: 'ŋ',
+ ensp: ' ',
+ Eogon: 'Ę',
+ eogon: 'ę',
+ Eopf: '𝔼',
+ eopf: '𝕖',
+ epar: '⋕',
+ eparsl: '⧣',
+ eplus: '⩱',
+ epsi: 'ε',
+ Epsilon: 'Ε',
+ epsilon: 'ε',
+ epsiv: 'ϵ',
+ eqcirc: '≖',
+ eqcolon: '≕',
+ eqsim: '≂',
+ eqslantgtr: '⪖',
+ eqslantless: '⪕',
+ Equal: '⩵',
+ equals: '=',
+ EqualTilde: '≂',
+ equest: '≟',
+ Equilibrium: '⇌',
+ equiv: '≡',
+ equivDD: '⩸',
+ eqvparsl: '⧥',
+ erarr: '⥱',
+ erDot: '≓',
+ escr: 'ℯ',
+ Escr: 'ℰ',
+ esdot: '≐',
+ Esim: '⩳',
+ esim: '≂',
+ Eta: 'Η',
+ eta: 'η',
+ ETH: 'Ð',
+ eth: 'ð',
+ Euml: 'Ë',
+ euml: 'ë',
+ euro: '€',
+ excl: '!',
+ exist: '∃',
+ Exists: '∃',
+ expectation: 'ℰ',
+ exponentiale: 'ⅇ',
+ ExponentialE: 'ⅇ',
+ fallingdotseq: '≒',
+ Fcy: 'Ф',
+ fcy: 'ф',
+ female: '♀',
+ ffilig: 'ffi',
+ fflig: 'ff',
+ ffllig: 'ffl',
+ Ffr: '𝔉',
+ ffr: '𝔣',
+ filig: 'fi',
+ FilledSmallSquare: '◼',
+ FilledVerySmallSquare: '▪',
+ fjlig: 'f',
+ flat: '♭',
+ fllig: 'fl',
+ fltns: '▱',
+ fnof: 'ƒ',
+ Fopf: '𝔽',
+ fopf: '𝕗',
+ forall: '∀',
+ ForAll: '∀',
+ fork: '⋔',
+ forkv: '⫙',
+ Fouriertrf: 'ℱ',
+ fpartint: '⨍',
+ frac12: '½',
+ frac13: '⅓',
+ frac14: '¼',
+ frac15: '⅕',
+ frac16: '⅙',
+ frac18: '⅛',
+ frac23: '⅔',
+ frac25: '⅖',
+ frac34: '¾',
+ frac35: '⅗',
+ frac38: '⅜',
+ frac45: '⅘',
+ frac56: '⅚',
+ frac58: '⅝',
+ frac78: '⅞',
+ frasl: '⁄',
+ frown: '⌢',
+ fscr: '𝒻',
+ Fscr: 'ℱ',
+ gacute: 'ǵ',
+ Gamma: 'Γ',
+ gamma: 'γ',
+ Gammad: 'Ϝ',
+ gammad: 'ϝ',
+ gap: '⪆',
+ Gbreve: 'Ğ',
+ gbreve: 'ğ',
+ Gcedil: 'Ģ',
+ Gcirc: 'Ĝ',
+ gcirc: 'ĝ',
+ Gcy: 'Г',
+ gcy: 'г',
+ Gdot: 'Ġ',
+ gdot: 'ġ',
+ ge: '≥',
+ gE: '≧',
+ gEl: '⪌',
+ gel: '⋛',
+ geq: '≥',
+ geqq: '≧',
+ geqslant: '⩾',
+ gescc: '⪩',
+ ges: '⩾',
+ gesdot: '⪀',
+ gesdoto: '⪂',
+ gesdotol: '⪄',
+ gesl: '⋛',
+ gesles: '⪔',
+ Gfr: '𝔊',
+ gfr: '𝔤',
+ gg: '≫',
+ Gg: '⋙',
+ ggg: '⋙',
+ gimel: 'ℷ',
+ GJcy: 'Ѓ',
+ gjcy: 'ѓ',
+ gla: '⪥',
+ gl: '≷',
+ glE: '⪒',
+ glj: '⪤',
+ gnap: '⪊',
+ gnapprox: '⪊',
+ gne: '⪈',
+ gnE: '≩',
+ gneq: '⪈',
+ gneqq: '≩',
+ gnsim: '⋧',
+ Gopf: '𝔾',
+ gopf: '𝕘',
+ grave: '`',
+ GreaterEqual: '≥',
+ GreaterEqualLess: '⋛',
+ GreaterFullEqual: '≧',
+ GreaterGreater: '⪢',
+ GreaterLess: '≷',
+ GreaterSlantEqual: '⩾',
+ GreaterTilde: '≳',
+ Gscr: '𝒢',
+ gscr: 'ℊ',
+ gsim: '≳',
+ gsime: '⪎',
+ gsiml: '⪐',
+ gtcc: '⪧',
+ gtcir: '⩺',
+ gt: '>',
+ GT: '>',
+ Gt: '≫',
+ gtdot: '⋗',
+ gtlPar: '⦕',
+ gtquest: '⩼',
+ gtrapprox: '⪆',
+ gtrarr: '⥸',
+ gtrdot: '⋗',
+ gtreqless: '⋛',
+ gtreqqless: '⪌',
+ gtrless: '≷',
+ gtrsim: '≳',
+ gvertneqq: '≩',
+ gvnE: '≩',
+ Hacek: 'ˇ',
+ hairsp: ' ',
+ half: '½',
+ hamilt: 'ℋ',
+ HARDcy: 'Ъ',
+ hardcy: 'ъ',
+ harrcir: '⥈',
+ harr: '↔',
+ hArr: '⇔',
+ harrw: '↭',
+ Hat: '^',
+ hbar: 'ℏ',
+ Hcirc: 'Ĥ',
+ hcirc: 'ĥ',
+ hearts: '♥',
+ heartsuit: '♥',
+ hellip: '…',
+ hercon: '⊹',
+ hfr: '𝔥',
+ Hfr: 'ℌ',
+ HilbertSpace: 'ℋ',
+ hksearow: '⤥',
+ hkswarow: '⤦',
+ hoarr: '⇿',
+ homtht: '∻',
+ hookleftarrow: '↩',
+ hookrightarrow: '↪',
+ hopf: '𝕙',
+ Hopf: 'ℍ',
+ horbar: '―',
+ HorizontalLine: '─',
+ hscr: '𝒽',
+ Hscr: 'ℋ',
+ hslash: 'ℏ',
+ Hstrok: 'Ħ',
+ hstrok: 'ħ',
+ HumpDownHump: '≎',
+ HumpEqual: '≏',
+ hybull: '⁃',
+ hyphen: '‐',
+ Iacute: 'Í',
+ iacute: 'í',
+ ic: '',
+ Icirc: 'Î',
+ icirc: 'î',
+ Icy: 'И',
+ icy: 'и',
+ Idot: 'İ',
+ IEcy: 'Е',
+ iecy: 'е',
+ iexcl: '¡',
+ iff: '⇔',
+ ifr: '𝔦',
+ Ifr: 'ℑ',
+ Igrave: 'Ì',
+ igrave: 'ì',
+ ii: 'ⅈ',
+ iiiint: '⨌',
+ iiint: '∭',
+ iinfin: '⧜',
+ iiota: '℩',
+ IJlig: 'IJ',
+ ijlig: 'ij',
+ Imacr: 'Ī',
+ imacr: 'ī',
+ image: 'ℑ',
+ ImaginaryI: 'ⅈ',
+ imagline: 'ℐ',
+ imagpart: 'ℑ',
+ imath: 'ı',
+ Im: 'ℑ',
+ imof: '⊷',
+ imped: 'Ƶ',
+ Implies: '⇒',
+ incare: '℅',
+ "in": '∈',
+ infin: '∞',
+ infintie: '⧝',
+ inodot: 'ı',
+ intcal: '⊺',
+ int: '∫',
+ Int: '∬',
+ integers: 'ℤ',
+ Integral: '∫',
+ intercal: '⊺',
+ Intersection: '⋂',
+ intlarhk: '⨗',
+ intprod: '⨼',
+ InvisibleComma: '',
+ InvisibleTimes: '',
+ IOcy: 'Ё',
+ iocy: 'ё',
+ Iogon: 'Į',
+ iogon: 'į',
+ Iopf: '𝕀',
+ iopf: '𝕚',
+ Iota: 'Ι',
+ iota: 'ι',
+ iprod: '⨼',
+ iquest: '¿',
+ iscr: '𝒾',
+ Iscr: 'ℐ',
+ isin: '∈',
+ isindot: '⋵',
+ isinE: '⋹',
+ isins: '⋴',
+ isinsv: '⋳',
+ isinv: '∈',
+ it: '',
+ Itilde: 'Ĩ',
+ itilde: 'ĩ',
+ Iukcy: 'І',
+ iukcy: 'і',
+ Iuml: 'Ï',
+ iuml: 'ï',
+ Jcirc: 'Ĵ',
+ jcirc: 'ĵ',
+ Jcy: 'Й',
+ jcy: 'й',
+ Jfr: '𝔍',
+ jfr: '𝔧',
+ jmath: 'ȷ',
+ Jopf: '𝕁',
+ jopf: '𝕛',
+ Jscr: '𝒥',
+ jscr: '𝒿',
+ Jsercy: 'Ј',
+ jsercy: 'ј',
+ Jukcy: 'Є',
+ jukcy: 'є',
+ Kappa: 'Κ',
+ kappa: 'κ',
+ kappav: 'ϰ',
+ Kcedil: 'Ķ',
+ kcedil: 'ķ',
+ Kcy: 'К',
+ kcy: 'к',
+ Kfr: '𝔎',
+ kfr: '𝔨',
+ kgreen: 'ĸ',
+ KHcy: 'Х',
+ khcy: 'х',
+ KJcy: 'Ќ',
+ kjcy: 'ќ',
+ Kopf: '𝕂',
+ kopf: '𝕜',
+ Kscr: '𝒦',
+ kscr: '𝓀',
+ lAarr: '⇚',
+ Lacute: 'Ĺ',
+ lacute: 'ĺ',
+ laemptyv: '⦴',
+ lagran: 'ℒ',
+ Lambda: 'Λ',
+ lambda: 'λ',
+ lang: '⟨',
+ Lang: '⟪',
+ langd: '⦑',
+ langle: '⟨',
+ lap: '⪅',
+ Laplacetrf: 'ℒ',
+ laquo: '«',
+ larrb: '⇤',
+ larrbfs: '⤟',
+ larr: '←',
+ Larr: '↞',
+ lArr: '⇐',
+ larrfs: '⤝',
+ larrhk: '↩',
+ larrlp: '↫',
+ larrpl: '⤹',
+ larrsim: '⥳',
+ larrtl: '↢',
+ latail: '⤙',
+ lAtail: '⤛',
+ lat: '⪫',
+ late: '⪭',
+ lates: '⪭',
+ lbarr: '⤌',
+ lBarr: '⤎',
+ lbbrk: '❲',
+ lbrace: '{',
+ lbrack: '[',
+ lbrke: '⦋',
+ lbrksld: '⦏',
+ lbrkslu: '⦍',
+ Lcaron: 'Ľ',
+ lcaron: 'ľ',
+ Lcedil: 'Ļ',
+ lcedil: 'ļ',
+ lceil: '⌈',
+ lcub: '{',
+ Lcy: 'Л',
+ lcy: 'л',
+ ldca: '⤶',
+ ldquo: '“',
+ ldquor: '„',
+ ldrdhar: '⥧',
+ ldrushar: '⥋',
+ ldsh: '↲',
+ le: '≤',
+ lE: '≦',
+ LeftAngleBracket: '⟨',
+ LeftArrowBar: '⇤',
+ leftarrow: '←',
+ LeftArrow: '←',
+ Leftarrow: '⇐',
+ LeftArrowRightArrow: '⇆',
+ leftarrowtail: '↢',
+ LeftCeiling: '⌈',
+ LeftDoubleBracket: '⟦',
+ LeftDownTeeVector: '⥡',
+ LeftDownVectorBar: '⥙',
+ LeftDownVector: '⇃',
+ LeftFloor: '⌊',
+ leftharpoondown: '↽',
+ leftharpoonup: '↼',
+ leftleftarrows: '⇇',
+ leftrightarrow: '↔',
+ LeftRightArrow: '↔',
+ Leftrightarrow: '⇔',
+ leftrightarrows: '⇆',
+ leftrightharpoons: '⇋',
+ leftrightsquigarrow: '↭',
+ LeftRightVector: '⥎',
+ LeftTeeArrow: '↤',
+ LeftTee: '⊣',
+ LeftTeeVector: '⥚',
+ leftthreetimes: '⋋',
+ LeftTriangleBar: '⧏',
+ LeftTriangle: '⊲',
+ LeftTriangleEqual: '⊴',
+ LeftUpDownVector: '⥑',
+ LeftUpTeeVector: '⥠',
+ LeftUpVectorBar: '⥘',
+ LeftUpVector: '↿',
+ LeftVectorBar: '⥒',
+ LeftVector: '↼',
+ lEg: '⪋',
+ leg: '⋚',
+ leq: '≤',
+ leqq: '≦',
+ leqslant: '⩽',
+ lescc: '⪨',
+ les: '⩽',
+ lesdot: '⩿',
+ lesdoto: '⪁',
+ lesdotor: '⪃',
+ lesg: '⋚',
+ lesges: '⪓',
+ lessapprox: '⪅',
+ lessdot: '⋖',
+ lesseqgtr: '⋚',
+ lesseqqgtr: '⪋',
+ LessEqualGreater: '⋚',
+ LessFullEqual: '≦',
+ LessGreater: '≶',
+ lessgtr: '≶',
+ LessLess: '⪡',
+ lesssim: '≲',
+ LessSlantEqual: '⩽',
+ LessTilde: '≲',
+ lfisht: '⥼',
+ lfloor: '⌊',
+ Lfr: '𝔏',
+ lfr: '𝔩',
+ lg: '≶',
+ lgE: '⪑',
+ lHar: '⥢',
+ lhard: '↽',
+ lharu: '↼',
+ lharul: '⥪',
+ lhblk: '▄',
+ LJcy: 'Љ',
+ ljcy: 'љ',
+ llarr: '⇇',
+ ll: '≪',
+ Ll: '⋘',
+ llcorner: '⌞',
+ Lleftarrow: '⇚',
+ llhard: '⥫',
+ lltri: '◺',
+ Lmidot: 'Ŀ',
+ lmidot: 'ŀ',
+ lmoustache: '⎰',
+ lmoust: '⎰',
+ lnap: '⪉',
+ lnapprox: '⪉',
+ lne: '⪇',
+ lnE: '≨',
+ lneq: '⪇',
+ lneqq: '≨',
+ lnsim: '⋦',
+ loang: '⟬',
+ loarr: '⇽',
+ lobrk: '⟦',
+ longleftarrow: '⟵',
+ LongLeftArrow: '⟵',
+ Longleftarrow: '⟸',
+ longleftrightarrow: '⟷',
+ LongLeftRightArrow: '⟷',
+ Longleftrightarrow: '⟺',
+ longmapsto: '⟼',
+ longrightarrow: '⟶',
+ LongRightArrow: '⟶',
+ Longrightarrow: '⟹',
+ looparrowleft: '↫',
+ looparrowright: '↬',
+ lopar: '⦅',
+ Lopf: '𝕃',
+ lopf: '𝕝',
+ loplus: '⨭',
+ lotimes: '⨴',
+ lowast: '∗',
+ lowbar: '_',
+ LowerLeftArrow: '↙',
+ LowerRightArrow: '↘',
+ loz: '◊',
+ lozenge: '◊',
+ lozf: '⧫',
+ lpar: '(',
+ lparlt: '⦓',
+ lrarr: '⇆',
+ lrcorner: '⌟',
+ lrhar: '⇋',
+ lrhard: '⥭',
+ lrm: '',
+ lrtri: '⊿',
+ lsaquo: '‹',
+ lscr: '𝓁',
+ Lscr: 'ℒ',
+ lsh: '↰',
+ Lsh: '↰',
+ lsim: '≲',
+ lsime: '⪍',
+ lsimg: '⪏',
+ lsqb: '[',
+ lsquo: '‘',
+ lsquor: '‚',
+ Lstrok: 'Ł',
+ lstrok: 'ł',
+ ltcc: '⪦',
+ ltcir: '⩹',
+ lt: '<',
+ LT: '<',
+ Lt: '≪',
+ ltdot: '⋖',
+ lthree: '⋋',
+ ltimes: '⋉',
+ ltlarr: '⥶',
+ ltquest: '⩻',
+ ltri: '◃',
+ ltrie: '⊴',
+ ltrif: '◂',
+ ltrPar: '⦖',
+ lurdshar: '⥊',
+ luruhar: '⥦',
+ lvertneqq: '≨',
+ lvnE: '≨',
+ macr: '¯',
+ male: '♂',
+ malt: '✠',
+ maltese: '✠',
+ Map: '⤅',
+ map: '↦',
+ mapsto: '↦',
+ mapstodown: '↧',
+ mapstoleft: '↤',
+ mapstoup: '↥',
+ marker: '▮',
+ mcomma: '⨩',
+ Mcy: 'М',
+ mcy: 'м',
+ mdash: '—',
+ mDDot: '∺',
+ measuredangle: '∡',
+ MediumSpace: ' ',
+ Mellintrf: 'ℳ',
+ Mfr: '𝔐',
+ mfr: '𝔪',
+ mho: '℧',
+ micro: 'µ',
+ midast: '*',
+ midcir: '⫰',
+ mid: '∣',
+ middot: '·',
+ minusb: '⊟',
+ minus: '−',
+ minusd: '∸',
+ minusdu: '⨪',
+ MinusPlus: '∓',
+ mlcp: '⫛',
+ mldr: '…',
+ mnplus: '∓',
+ models: '⊧',
+ Mopf: '𝕄',
+ mopf: '𝕞',
+ mp: '∓',
+ mscr: '𝓂',
+ Mscr: 'ℳ',
+ mstpos: '∾',
+ Mu: 'Μ',
+ mu: 'μ',
+ multimap: '⊸',
+ mumap: '⊸',
+ nabla: '∇',
+ Nacute: 'Ń',
+ nacute: 'ń',
+ nang: '∠',
+ nap: '≉',
+ napE: '⩰',
+ napid: '≋',
+ napos: 'ʼn',
+ napprox: '≉',
+ natural: '♮',
+ naturals: 'ℕ',
+ natur: '♮',
+ nbsp: ' ',
+ nbump: '≎',
+ nbumpe: '≏',
+ ncap: '⩃',
+ Ncaron: 'Ň',
+ ncaron: 'ň',
+ Ncedil: 'Ņ',
+ ncedil: 'ņ',
+ ncong: '≇',
+ ncongdot: '⩭',
+ ncup: '⩂',
+ Ncy: 'Н',
+ ncy: 'н',
+ ndash: '–',
+ nearhk: '⤤',
+ nearr: '↗',
+ neArr: '⇗',
+ nearrow: '↗',
+ ne: '≠',
+ nedot: '≐',
+ NegativeMediumSpace: '',
+ NegativeThickSpace: '',
+ NegativeThinSpace: '',
+ NegativeVeryThinSpace: '',
+ nequiv: '≢',
+ nesear: '⤨',
+ nesim: '≂',
+ NestedGreaterGreater: '≫',
+ NestedLessLess: '≪',
+ NewLine: '\n',
+ nexist: '∄',
+ nexists: '∄',
+ Nfr: '𝔑',
+ nfr: '𝔫',
+ ngE: '≧',
+ nge: '≱',
+ ngeq: '≱',
+ ngeqq: '≧',
+ ngeqslant: '⩾',
+ nges: '⩾',
+ nGg: '⋙',
+ ngsim: '≵',
+ nGt: '≫',
+ ngt: '≯',
+ ngtr: '≯',
+ nGtv: '≫',
+ nharr: '↮',
+ nhArr: '⇎',
+ nhpar: '⫲',
+ ni: '∋',
+ nis: '⋼',
+ nisd: '⋺',
+ niv: '∋',
+ NJcy: 'Њ',
+ njcy: 'њ',
+ nlarr: '↚',
+ nlArr: '⇍',
+ nldr: '‥',
+ nlE: '≦',
+ nle: '≰',
+ nleftarrow: '↚',
+ nLeftarrow: '⇍',
+ nleftrightarrow: '↮',
+ nLeftrightarrow: '⇎',
+ nleq: '≰',
+ nleqq: '≦',
+ nleqslant: '⩽',
+ nles: '⩽',
+ nless: '≮',
+ nLl: '⋘',
+ nlsim: '≴',
+ nLt: '≪',
+ nlt: '≮',
+ nltri: '⋪',
+ nltrie: '⋬',
+ nLtv: '≪',
+ nmid: '∤',
+ NoBreak: '',
+ NonBreakingSpace: ' ',
+ nopf: '𝕟',
+ Nopf: 'ℕ',
+ Not: '⫬',
+ not: '¬',
+ NotCongruent: '≢',
+ NotCupCap: '≭',
+ NotDoubleVerticalBar: '∦',
+ NotElement: '∉',
+ NotEqual: '≠',
+ NotEqualTilde: '≂',
+ NotExists: '∄',
+ NotGreater: '≯',
+ NotGreaterEqual: '≱',
+ NotGreaterFullEqual: '≧',
+ NotGreaterGreater: '≫',
+ NotGreaterLess: '≹',
+ NotGreaterSlantEqual: '⩾',
+ NotGreaterTilde: '≵',
+ NotHumpDownHump: '≎',
+ NotHumpEqual: '≏',
+ notin: '∉',
+ notindot: '⋵',
+ notinE: '⋹',
+ notinva: '∉',
+ notinvb: '⋷',
+ notinvc: '⋶',
+ NotLeftTriangleBar: '⧏',
+ NotLeftTriangle: '⋪',
+ NotLeftTriangleEqual: '⋬',
+ NotLess: '≮',
+ NotLessEqual: '≰',
+ NotLessGreater: '≸',
+ NotLessLess: '≪',
+ NotLessSlantEqual: '⩽',
+ NotLessTilde: '≴',
+ NotNestedGreaterGreater: '⪢',
+ NotNestedLessLess: '⪡',
+ notni: '∌',
+ notniva: '∌',
+ notnivb: '⋾',
+ notnivc: '⋽',
+ NotPrecedes: '⊀',
+ NotPrecedesEqual: '⪯',
+ NotPrecedesSlantEqual: '⋠',
+ NotReverseElement: '∌',
+ NotRightTriangleBar: '⧐',
+ NotRightTriangle: '⋫',
+ NotRightTriangleEqual: '⋭',
+ NotSquareSubset: '⊏',
+ NotSquareSubsetEqual: '⋢',
+ NotSquareSuperset: '⊐',
+ NotSquareSupersetEqual: '⋣',
+ NotSubset: '⊂',
+ NotSubsetEqual: '⊈',
+ NotSucceeds: '⊁',
+ NotSucceedsEqual: '⪰',
+ NotSucceedsSlantEqual: '⋡',
+ NotSucceedsTilde: '≿',
+ NotSuperset: '⊃',
+ NotSupersetEqual: '⊉',
+ NotTilde: '≁',
+ NotTildeEqual: '≄',
+ NotTildeFullEqual: '≇',
+ NotTildeTilde: '≉',
+ NotVerticalBar: '∤',
+ nparallel: '∦',
+ npar: '∦',
+ nparsl: '⫽',
+ npart: '∂',
+ npolint: '⨔',
+ npr: '⊀',
+ nprcue: '⋠',
+ nprec: '⊀',
+ npreceq: '⪯',
+ npre: '⪯',
+ nrarrc: '⤳',
+ nrarr: '↛',
+ nrArr: '⇏',
+ nrarrw: '↝',
+ nrightarrow: '↛',
+ nRightarrow: '⇏',
+ nrtri: '⋫',
+ nrtrie: '⋭',
+ nsc: '⊁',
+ nsccue: '⋡',
+ nsce: '⪰',
+ Nscr: '𝒩',
+ nscr: '𝓃',
+ nshortmid: '∤',
+ nshortparallel: '∦',
+ nsim: '≁',
+ nsime: '≄',
+ nsimeq: '≄',
+ nsmid: '∤',
+ nspar: '∦',
+ nsqsube: '⋢',
+ nsqsupe: '⋣',
+ nsub: '⊄',
+ nsubE: '⫅',
+ nsube: '⊈',
+ nsubset: '⊂',
+ nsubseteq: '⊈',
+ nsubseteqq: '⫅',
+ nsucc: '⊁',
+ nsucceq: '⪰',
+ nsup: '⊅',
+ nsupE: '⫆',
+ nsupe: '⊉',
+ nsupset: '⊃',
+ nsupseteq: '⊉',
+ nsupseteqq: '⫆',
+ ntgl: '≹',
+ Ntilde: 'Ñ',
+ ntilde: 'ñ',
+ ntlg: '≸',
+ ntriangleleft: '⋪',
+ ntrianglelefteq: '⋬',
+ ntriangleright: '⋫',
+ ntrianglerighteq: '⋭',
+ Nu: 'Ν',
+ nu: 'ν',
+ num: '#',
+ numero: '№',
+ numsp: ' ',
+ nvap: '≍',
+ nvdash: '⊬',
+ nvDash: '⊭',
+ nVdash: '⊮',
+ nVDash: '⊯',
+ nvge: '≥',
+ nvgt: '>',
+ nvHarr: '⤄',
+ nvinfin: '⧞',
+ nvlArr: '⤂',
+ nvle: '≤',
+ nvlt: '>',
+ nvltrie: '⊴',
+ nvrArr: '⤃',
+ nvrtrie: '⊵',
+ nvsim: '∼',
+ nwarhk: '⤣',
+ nwarr: '↖',
+ nwArr: '⇖',
+ nwarrow: '↖',
+ nwnear: '⤧',
+ Oacute: 'Ó',
+ oacute: 'ó',
+ oast: '⊛',
+ Ocirc: 'Ô',
+ ocirc: 'ô',
+ ocir: '⊚',
+ Ocy: 'О',
+ ocy: 'о',
+ odash: '⊝',
+ Odblac: 'Ő',
+ odblac: 'ő',
+ odiv: '⨸',
+ odot: '⊙',
+ odsold: '⦼',
+ OElig: 'Œ',
+ oelig: 'œ',
+ ofcir: '⦿',
+ Ofr: '𝔒',
+ ofr: '𝔬',
+ ogon: '˛',
+ Ograve: 'Ò',
+ ograve: 'ò',
+ ogt: '⧁',
+ ohbar: '⦵',
+ ohm: 'Ω',
+ oint: '∮',
+ olarr: '↺',
+ olcir: '⦾',
+ olcross: '⦻',
+ oline: '‾',
+ olt: '⧀',
+ Omacr: 'Ō',
+ omacr: 'ō',
+ Omega: 'Ω',
+ omega: 'ω',
+ Omicron: 'Ο',
+ omicron: 'ο',
+ omid: '⦶',
+ ominus: '⊖',
+ Oopf: '𝕆',
+ oopf: '𝕠',
+ opar: '⦷',
+ OpenCurlyDoubleQuote: '“',
+ OpenCurlyQuote: '‘',
+ operp: '⦹',
+ oplus: '⊕',
+ orarr: '↻',
+ Or: '⩔',
+ or: '∨',
+ ord: '⩝',
+ order: 'ℴ',
+ orderof: 'ℴ',
+ ordf: 'ª',
+ ordm: 'º',
+ origof: '⊶',
+ oror: '⩖',
+ orslope: '⩗',
+ orv: '⩛',
+ oS: 'Ⓢ',
+ Oscr: '𝒪',
+ oscr: 'ℴ',
+ Oslash: 'Ø',
+ oslash: 'ø',
+ osol: '⊘',
+ Otilde: 'Õ',
+ otilde: 'õ',
+ otimesas: '⨶',
+ Otimes: '⨷',
+ otimes: '⊗',
+ Ouml: 'Ö',
+ ouml: 'ö',
+ ovbar: '⌽',
+ OverBar: '‾',
+ OverBrace: '⏞',
+ OverBracket: '⎴',
+ OverParenthesis: '⏜',
+ para: '¶',
+ parallel: '∥',
+ par: '∥',
+ parsim: '⫳',
+ parsl: '⫽',
+ part: '∂',
+ PartialD: '∂',
+ Pcy: 'П',
+ pcy: 'п',
+ percnt: '%',
+ period: '.',
+ permil: '‰',
+ perp: '⊥',
+ pertenk: '‱',
+ Pfr: '𝔓',
+ pfr: '𝔭',
+ Phi: 'Φ',
+ phi: 'φ',
+ phiv: 'ϕ',
+ phmmat: 'ℳ',
+ phone: '☎',
+ Pi: 'Π',
+ pi: 'π',
+ pitchfork: '⋔',
+ piv: 'ϖ',
+ planck: 'ℏ',
+ planckh: 'ℎ',
+ plankv: 'ℏ',
+ plusacir: '⨣',
+ plusb: '⊞',
+ pluscir: '⨢',
+ plus: '+',
+ plusdo: '∔',
+ plusdu: '⨥',
+ pluse: '⩲',
+ PlusMinus: '±',
+ plusmn: '±',
+ plussim: '⨦',
+ plustwo: '⨧',
+ pm: '±',
+ Poincareplane: 'ℌ',
+ pointint: '⨕',
+ popf: '𝕡',
+ Popf: 'ℙ',
+ pound: '£',
+ prap: '⪷',
+ Pr: '⪻',
+ pr: '≺',
+ prcue: '≼',
+ precapprox: '⪷',
+ prec: '≺',
+ preccurlyeq: '≼',
+ Precedes: '≺',
+ PrecedesEqual: '⪯',
+ PrecedesSlantEqual: '≼',
+ PrecedesTilde: '≾',
+ preceq: '⪯',
+ precnapprox: '⪹',
+ precneqq: '⪵',
+ precnsim: '⋨',
+ pre: '⪯',
+ prE: '⪳',
+ precsim: '≾',
+ prime: '′',
+ Prime: '″',
+ primes: 'ℙ',
+ prnap: '⪹',
+ prnE: '⪵',
+ prnsim: '⋨',
+ prod: '∏',
+ Product: '∏',
+ profalar: '⌮',
+ profline: '⌒',
+ profsurf: '⌓',
+ prop: '∝',
+ Proportional: '∝',
+ Proportion: '∷',
+ propto: '∝',
+ prsim: '≾',
+ prurel: '⊰',
+ Pscr: '𝒫',
+ pscr: '𝓅',
+ Psi: 'Ψ',
+ psi: 'ψ',
+ puncsp: ' ',
+ Qfr: '𝔔',
+ qfr: '𝔮',
+ qint: '⨌',
+ qopf: '𝕢',
+ Qopf: 'ℚ',
+ qprime: '⁗',
+ Qscr: '𝒬',
+ qscr: '𝓆',
+ quaternions: 'ℍ',
+ quatint: '⨖',
+ quest: '?',
+ questeq: '≟',
+ quot: '"',
+ QUOT: '"',
+ rAarr: '⇛',
+ race: '∽',
+ Racute: 'Ŕ',
+ racute: 'ŕ',
+ radic: '√',
+ raemptyv: '⦳',
+ rang: '⟩',
+ Rang: '⟫',
+ rangd: '⦒',
+ range: '⦥',
+ rangle: '⟩',
+ raquo: '»',
+ rarrap: '⥵',
+ rarrb: '⇥',
+ rarrbfs: '⤠',
+ rarrc: '⤳',
+ rarr: '→',
+ Rarr: '↠',
+ rArr: '⇒',
+ rarrfs: '⤞',
+ rarrhk: '↪',
+ rarrlp: '↬',
+ rarrpl: '⥅',
+ rarrsim: '⥴',
+ Rarrtl: '⤖',
+ rarrtl: '↣',
+ rarrw: '↝',
+ ratail: '⤚',
+ rAtail: '⤜',
+ ratio: '∶',
+ rationals: 'ℚ',
+ rbarr: '⤍',
+ rBarr: '⤏',
+ RBarr: '⤐',
+ rbbrk: '❳',
+ rbrace: '}',
+ rbrack: ']',
+ rbrke: '⦌',
+ rbrksld: '⦎',
+ rbrkslu: '⦐',
+ Rcaron: 'Ř',
+ rcaron: 'ř',
+ Rcedil: 'Ŗ',
+ rcedil: 'ŗ',
+ rceil: '⌉',
+ rcub: '}',
+ Rcy: 'Р',
+ rcy: 'р',
+ rdca: '⤷',
+ rdldhar: '⥩',
+ rdquo: '”',
+ rdquor: '”',
+ rdsh: '↳',
+ real: 'ℜ',
+ realine: 'ℛ',
+ realpart: 'ℜ',
+ reals: 'ℝ',
+ Re: 'ℜ',
+ rect: '▭',
+ reg: '®',
+ REG: '®',
+ ReverseElement: '∋',
+ ReverseEquilibrium: '⇋',
+ ReverseUpEquilibrium: '⥯',
+ rfisht: '⥽',
+ rfloor: '⌋',
+ rfr: '𝔯',
+ Rfr: 'ℜ',
+ rHar: '⥤',
+ rhard: '⇁',
+ rharu: '⇀',
+ rharul: '⥬',
+ Rho: 'Ρ',
+ rho: 'ρ',
+ rhov: 'ϱ',
+ RightAngleBracket: '⟩',
+ RightArrowBar: '⇥',
+ rightarrow: '→',
+ RightArrow: '→',
+ Rightarrow: '⇒',
+ RightArrowLeftArrow: '⇄',
+ rightarrowtail: '↣',
+ RightCeiling: '⌉',
+ RightDoubleBracket: '⟧',
+ RightDownTeeVector: '⥝',
+ RightDownVectorBar: '⥕',
+ RightDownVector: '⇂',
+ RightFloor: '⌋',
+ rightharpoondown: '⇁',
+ rightharpoonup: '⇀',
+ rightleftarrows: '⇄',
+ rightleftharpoons: '⇌',
+ rightrightarrows: '⇉',
+ rightsquigarrow: '↝',
+ RightTeeArrow: '↦',
+ RightTee: '⊢',
+ RightTeeVector: '⥛',
+ rightthreetimes: '⋌',
+ RightTriangleBar: '⧐',
+ RightTriangle: '⊳',
+ RightTriangleEqual: '⊵',
+ RightUpDownVector: '⥏',
+ RightUpTeeVector: '⥜',
+ RightUpVectorBar: '⥔',
+ RightUpVector: '↾',
+ RightVectorBar: '⥓',
+ RightVector: '⇀',
+ ring: '˚',
+ risingdotseq: '≓',
+ rlarr: '⇄',
+ rlhar: '⇌',
+ rlm: '',
+ rmoustache: '⎱',
+ rmoust: '⎱',
+ rnmid: '⫮',
+ roang: '⟭',
+ roarr: '⇾',
+ robrk: '⟧',
+ ropar: '⦆',
+ ropf: '𝕣',
+ Ropf: 'ℝ',
+ roplus: '⨮',
+ rotimes: '⨵',
+ RoundImplies: '⥰',
+ rpar: ')',
+ rpargt: '⦔',
+ rppolint: '⨒',
+ rrarr: '⇉',
+ Rrightarrow: '⇛',
+ rsaquo: '›',
+ rscr: '𝓇',
+ Rscr: 'ℛ',
+ rsh: '↱',
+ Rsh: '↱',
+ rsqb: ']',
+ rsquo: '’',
+ rsquor: '’',
+ rthree: '⋌',
+ rtimes: '⋊',
+ rtri: '▹',
+ rtrie: '⊵',
+ rtrif: '▸',
+ rtriltri: '⧎',
+ RuleDelayed: '⧴',
+ ruluhar: '⥨',
+ rx: '℞',
+ Sacute: 'Ś',
+ sacute: 'ś',
+ sbquo: '‚',
+ scap: '⪸',
+ Scaron: 'Š',
+ scaron: 'š',
+ Sc: '⪼',
+ sc: '≻',
+ sccue: '≽',
+ sce: '⪰',
+ scE: '⪴',
+ Scedil: 'Ş',
+ scedil: 'ş',
+ Scirc: 'Ŝ',
+ scirc: 'ŝ',
+ scnap: '⪺',
+ scnE: '⪶',
+ scnsim: '⋩',
+ scpolint: '⨓',
+ scsim: '≿',
+ Scy: 'С',
+ scy: 'с',
+ sdotb: '⊡',
+ sdot: '⋅',
+ sdote: '⩦',
+ searhk: '⤥',
+ searr: '↘',
+ seArr: '⇘',
+ searrow: '↘',
+ sect: '§',
+ semi: ';',
+ seswar: '⤩',
+ setminus: '∖',
+ setmn: '∖',
+ sext: '✶',
+ Sfr: '𝔖',
+ sfr: '𝔰',
+ sfrown: '⌢',
+ sharp: '♯',
+ SHCHcy: 'Щ',
+ shchcy: 'щ',
+ SHcy: 'Ш',
+ shcy: 'ш',
+ ShortDownArrow: '↓',
+ ShortLeftArrow: '←',
+ shortmid: '∣',
+ shortparallel: '∥',
+ ShortRightArrow: '→',
+ ShortUpArrow: '↑',
+ shy: '',
+ Sigma: 'Σ',
+ sigma: 'σ',
+ sigmaf: 'ς',
+ sigmav: 'ς',
+ sim: '∼',
+ simdot: '⩪',
+ sime: '≃',
+ simeq: '≃',
+ simg: '⪞',
+ simgE: '⪠',
+ siml: '⪝',
+ simlE: '⪟',
+ simne: '≆',
+ simplus: '⨤',
+ simrarr: '⥲',
+ slarr: '←',
+ SmallCircle: '∘',
+ smallsetminus: '∖',
+ smashp: '⨳',
+ smeparsl: '⧤',
+ smid: '∣',
+ smile: '⌣',
+ smt: '⪪',
+ smte: '⪬',
+ smtes: '⪬',
+ SOFTcy: 'Ь',
+ softcy: 'ь',
+ solbar: '⌿',
+ solb: '⧄',
+ sol: '/',
+ Sopf: '𝕊',
+ sopf: '𝕤',
+ spades: '♠',
+ spadesuit: '♠',
+ spar: '∥',
+ sqcap: '⊓',
+ sqcaps: '⊓',
+ sqcup: '⊔',
+ sqcups: '⊔',
+ Sqrt: '√',
+ sqsub: '⊏',
+ sqsube: '⊑',
+ sqsubset: '⊏',
+ sqsubseteq: '⊑',
+ sqsup: '⊐',
+ sqsupe: '⊒',
+ sqsupset: '⊐',
+ sqsupseteq: '⊒',
+ square: '□',
+ Square: '□',
+ SquareIntersection: '⊓',
+ SquareSubset: '⊏',
+ SquareSubsetEqual: '⊑',
+ SquareSuperset: '⊐',
+ SquareSupersetEqual: '⊒',
+ SquareUnion: '⊔',
+ squarf: '▪',
+ squ: '□',
+ squf: '▪',
+ srarr: '→',
+ Sscr: '𝒮',
+ sscr: '𝓈',
+ ssetmn: '∖',
+ ssmile: '⌣',
+ sstarf: '⋆',
+ Star: '⋆',
+ star: '☆',
+ starf: '★',
+ straightepsilon: 'ϵ',
+ straightphi: 'ϕ',
+ strns: '¯',
+ sub: '⊂',
+ Sub: '⋐',
+ subdot: '⪽',
+ subE: '⫅',
+ sube: '⊆',
+ subedot: '⫃',
+ submult: '⫁',
+ subnE: '⫋',
+ subne: '⊊',
+ subplus: '⪿',
+ subrarr: '⥹',
+ subset: '⊂',
+ Subset: '⋐',
+ subseteq: '⊆',
+ subseteqq: '⫅',
+ SubsetEqual: '⊆',
+ subsetneq: '⊊',
+ subsetneqq: '⫋',
+ subsim: '⫇',
+ subsub: '⫕',
+ subsup: '⫓',
+ succapprox: '⪸',
+ succ: '≻',
+ succcurlyeq: '≽',
+ Succeeds: '≻',
+ SucceedsEqual: '⪰',
+ SucceedsSlantEqual: '≽',
+ SucceedsTilde: '≿',
+ succeq: '⪰',
+ succnapprox: '⪺',
+ succneqq: '⪶',
+ succnsim: '⋩',
+ succsim: '≿',
+ SuchThat: '∋',
+ sum: '∑',
+ Sum: '∑',
+ sung: '♪',
+ sup1: '¹',
+ sup2: '²',
+ sup3: '³',
+ sup: '⊃',
+ Sup: '⋑',
+ supdot: '⪾',
+ supdsub: '⫘',
+ supE: '⫆',
+ supe: '⊇',
+ supedot: '⫄',
+ Superset: '⊃',
+ SupersetEqual: '⊇',
+ suphsol: '⟉',
+ suphsub: '⫗',
+ suplarr: '⥻',
+ supmult: '⫂',
+ supnE: '⫌',
+ supne: '⊋',
+ supplus: '⫀',
+ supset: '⊃',
+ Supset: '⋑',
+ supseteq: '⊇',
+ supseteqq: '⫆',
+ supsetneq: '⊋',
+ supsetneqq: '⫌',
+ supsim: '⫈',
+ supsub: '⫔',
+ supsup: '⫖',
+ swarhk: '⤦',
+ swarr: '↙',
+ swArr: '⇙',
+ swarrow: '↙',
+ swnwar: '⤪',
+ szlig: 'ß',
+ Tab: ' ',
+ target: '⌖',
+ Tau: 'Τ',
+ tau: 'τ',
+ tbrk: '⎴',
+ Tcaron: 'Ť',
+ tcaron: 'ť',
+ Tcedil: 'Ţ',
+ tcedil: 'ţ',
+ Tcy: 'Т',
+ tcy: 'т',
+ tdot: '⃛',
+ telrec: '⌕',
+ Tfr: '𝔗',
+ tfr: '𝔱',
+ there4: '∴',
+ therefore: '∴',
+ Therefore: '∴',
+ Theta: 'Θ',
+ theta: 'θ',
+ thetasym: 'ϑ',
+ thetav: 'ϑ',
+ thickapprox: '≈',
+ thicksim: '∼',
+ ThickSpace: ' ',
+ ThinSpace: ' ',
+ thinsp: ' ',
+ thkap: '≈',
+ thksim: '∼',
+ THORN: 'Þ',
+ thorn: 'þ',
+ tilde: '˜',
+ Tilde: '∼',
+ TildeEqual: '≃',
+ TildeFullEqual: '≅',
+ TildeTilde: '≈',
+ timesbar: '⨱',
+ timesb: '⊠',
+ times: '×',
+ timesd: '⨰',
+ tint: '∭',
+ toea: '⤨',
+ topbot: '⌶',
+ topcir: '⫱',
+ top: '⊤',
+ Topf: '𝕋',
+ topf: '𝕥',
+ topfork: '⫚',
+ tosa: '⤩',
+ tprime: '‴',
+ trade: '™',
+ TRADE: '™',
+ triangle: '▵',
+ triangledown: '▿',
+ triangleleft: '◃',
+ trianglelefteq: '⊴',
+ triangleq: '≜',
+ triangleright: '▹',
+ trianglerighteq: '⊵',
+ tridot: '◬',
+ trie: '≜',
+ triminus: '⨺',
+ TripleDot: '⃛',
+ triplus: '⨹',
+ trisb: '⧍',
+ tritime: '⨻',
+ trpezium: '⏢',
+ Tscr: '𝒯',
+ tscr: '𝓉',
+ TScy: 'Ц',
+ tscy: 'ц',
+ TSHcy: 'Ћ',
+ tshcy: 'ћ',
+ Tstrok: 'Ŧ',
+ tstrok: 'ŧ',
+ twixt: '≬',
+ twoheadleftarrow: '↞',
+ twoheadrightarrow: '↠',
+ Uacute: 'Ú',
+ uacute: 'ú',
+ uarr: '↑',
+ Uarr: '↟',
+ uArr: '⇑',
+ Uarrocir: '⥉',
+ Ubrcy: 'Ў',
+ ubrcy: 'ў',
+ Ubreve: 'Ŭ',
+ ubreve: 'ŭ',
+ Ucirc: 'Û',
+ ucirc: 'û',
+ Ucy: 'У',
+ ucy: 'у',
+ udarr: '⇅',
+ Udblac: 'Ű',
+ udblac: 'ű',
+ udhar: '⥮',
+ ufisht: '⥾',
+ Ufr: '𝔘',
+ ufr: '𝔲',
+ Ugrave: 'Ù',
+ ugrave: 'ù',
+ uHar: '⥣',
+ uharl: '↿',
+ uharr: '↾',
+ uhblk: '▀',
+ ulcorn: '⌜',
+ ulcorner: '⌜',
+ ulcrop: '⌏',
+ ultri: '◸',
+ Umacr: 'Ū',
+ umacr: 'ū',
+ uml: '¨',
+ UnderBar: '_',
+ UnderBrace: '⏟',
+ UnderBracket: '⎵',
+ UnderParenthesis: '⏝',
+ Union: '⋃',
+ UnionPlus: '⊎',
+ Uogon: 'Ų',
+ uogon: 'ų',
+ Uopf: '𝕌',
+ uopf: '𝕦',
+ UpArrowBar: '⤒',
+ uparrow: '↑',
+ UpArrow: '↑',
+ Uparrow: '⇑',
+ UpArrowDownArrow: '⇅',
+ updownarrow: '↕',
+ UpDownArrow: '↕',
+ Updownarrow: '⇕',
+ UpEquilibrium: '⥮',
+ upharpoonleft: '↿',
+ upharpoonright: '↾',
+ uplus: '⊎',
+ UpperLeftArrow: '↖',
+ UpperRightArrow: '↗',
+ upsi: 'υ',
+ Upsi: 'ϒ',
+ upsih: 'ϒ',
+ Upsilon: 'Υ',
+ upsilon: 'υ',
+ UpTeeArrow: '↥',
+ UpTee: '⊥',
+ upuparrows: '⇈',
+ urcorn: '⌝',
+ urcorner: '⌝',
+ urcrop: '⌎',
+ Uring: 'Ů',
+ uring: 'ů',
+ urtri: '◹',
+ Uscr: '𝒰',
+ uscr: '𝓊',
+ utdot: '⋰',
+ Utilde: 'Ũ',
+ utilde: 'ũ',
+ utri: '▵',
+ utrif: '▴',
+ uuarr: '⇈',
+ Uuml: 'Ü',
+ uuml: 'ü',
+ uwangle: '⦧',
+ vangrt: '⦜',
+ varepsilon: 'ϵ',
+ varkappa: 'ϰ',
+ varnothing: '∅',
+ varphi: 'ϕ',
+ varpi: 'ϖ',
+ varpropto: '∝',
+ varr: '↕',
+ vArr: '⇕',
+ varrho: 'ϱ',
+ varsigma: 'ς',
+ varsubsetneq: '⊊',
+ varsubsetneqq: '⫋',
+ varsupsetneq: '⊋',
+ varsupsetneqq: '⫌',
+ vartheta: 'ϑ',
+ vartriangleleft: '⊲',
+ vartriangleright: '⊳',
+ vBar: '⫨',
+ Vbar: '⫫',
+ vBarv: '⫩',
+ Vcy: 'В',
+ vcy: 'в',
+ vdash: '⊢',
+ vDash: '⊨',
+ Vdash: '⊩',
+ VDash: '⊫',
+ Vdashl: '⫦',
+ veebar: '⊻',
+ vee: '∨',
+ Vee: '⋁',
+ veeeq: '≚',
+ vellip: '⋮',
+ verbar: '|',
+ Verbar: '‖',
+ vert: '|',
+ Vert: '‖',
+ VerticalBar: '∣',
+ VerticalLine: '|',
+ VerticalSeparator: '❘',
+ VerticalTilde: '≀',
+ VeryThinSpace: ' ',
+ Vfr: '𝔙',
+ vfr: '𝔳',
+ vltri: '⊲',
+ vnsub: '⊂',
+ vnsup: '⊃',
+ Vopf: '𝕍',
+ vopf: '𝕧',
+ vprop: '∝',
+ vrtri: '⊳',
+ Vscr: '𝒱',
+ vscr: '𝓋',
+ vsubnE: '⫋',
+ vsubne: '⊊',
+ vsupnE: '⫌',
+ vsupne: '⊋',
+ Vvdash: '⊪',
+ vzigzag: '⦚',
+ Wcirc: 'Ŵ',
+ wcirc: 'ŵ',
+ wedbar: '⩟',
+ wedge: '∧',
+ Wedge: '⋀',
+ wedgeq: '≙',
+ weierp: '℘',
+ Wfr: '𝔚',
+ wfr: '𝔴',
+ Wopf: '𝕎',
+ wopf: '𝕨',
+ wp: '℘',
+ wr: '≀',
+ wreath: '≀',
+ Wscr: '𝒲',
+ wscr: '𝓌',
+ xcap: '⋂',
+ xcirc: '◯',
+ xcup: '⋃',
+ xdtri: '▽',
+ Xfr: '𝔛',
+ xfr: '𝔵',
+ xharr: '⟷',
+ xhArr: '⟺',
+ Xi: 'Ξ',
+ xi: 'ξ',
+ xlarr: '⟵',
+ xlArr: '⟸',
+ xmap: '⟼',
+ xnis: '⋻',
+ xodot: '⨀',
+ Xopf: '𝕏',
+ xopf: '𝕩',
+ xoplus: '⨁',
+ xotime: '⨂',
+ xrarr: '⟶',
+ xrArr: '⟹',
+ Xscr: '𝒳',
+ xscr: '𝓍',
+ xsqcup: '⨆',
+ xuplus: '⨄',
+ xutri: '△',
+ xvee: '⋁',
+ xwedge: '⋀',
+ Yacute: 'Ý',
+ yacute: 'ý',
+ YAcy: 'Я',
+ yacy: 'я',
+ Ycirc: 'Ŷ',
+ ycirc: 'ŷ',
+ Ycy: 'Ы',
+ ycy: 'ы',
+ yen: '¥',
+ Yfr: '𝔜',
+ yfr: '𝔶',
+ YIcy: 'Ї',
+ yicy: 'ї',
+ Yopf: '𝕐',
+ yopf: '𝕪',
+ Yscr: '𝒴',
+ yscr: '𝓎',
+ YUcy: 'Ю',
+ yucy: 'ю',
+ yuml: 'ÿ',
+ Yuml: 'Ÿ',
+ Zacute: 'Ź',
+ zacute: 'ź',
+ Zcaron: 'Ž',
+ zcaron: 'ž',
+ Zcy: 'З',
+ zcy: 'з',
+ Zdot: 'Ż',
+ zdot: 'ż',
+ zeetrf: 'ℨ',
+ ZeroWidthSpace: '',
+ Zeta: 'Ζ',
+ zeta: 'ζ',
+ zfr: '𝔷',
+ Zfr: 'ℨ',
+ ZHcy: 'Ж',
+ zhcy: 'ж',
+ zigrarr: '⇝',
+ zopf: '𝕫',
+ Zopf: 'ℤ',
+ Zscr: '𝒵',
+ zscr: '𝓏',
+ zwj: '',
+ zwnj: ''
+ };
+
+ inline public static function entityToCharEr( er : EReg ) return entityToChar(er.matched(0));
+ public static function entityToChar( m : String )
+ {
+ var isNumeric = ~/^/.match(m);
+ var isHex = ~/^[Xx]/.match(m);
+ var uchar;
+
+ if ( isNumeric )
+ {
+ var num;
+ if ( isHex ) num = Std.parseInt('0x' + m.substr(3, m.length - 4));
+ else num = Std.parseInt(m.substr(2, m.length - 3));
+
+ uchar = fromCodePoint([num]);
+ }
+ else
+ {
+ uchar = Reflect.field(entities, m.substr(1, m.length - 2));
+ }
+
+ return uchar != null ? uchar : m;
+ }
+
+ public static function fromCodePoint( args : Array )
+ {
+ var MAX_SIZE = 0x4000;
+ var codeUnits = [];
+ var highSurrogate;
+ var lowSurrogate;
+ var index = -1;
+ var length = args.length;
+
+ if ( length < 1 )
+ return '';
+
+ var result = '';
+
+ while ( ++index < length )
+ {
+ var codePoint = args[index];
+ if (
+ !Math.isFinite(codePoint) || // `NaN`, `+Infinity`, or `-Infinity`
+ codePoint < 0 || // not a valid Unicode code point
+ codePoint > 0x10FFFF || // not a valid Unicode code point
+ Math.floor(codePoint) != codePoint // not an integer
+ )
+ {
+ return String.fromCharCode(0xFFFD);
+ }
+
+ if ( codePoint <= 0xFFFF )
+ {
+ // BMP code point
+ codeUnits.push(codePoint);
+ }
+ else
+ {
+ // Astral code point; split in surrogate halves
+ // http://mathiasbynens.be/notes/javascript-encoding#surrogate-formulae
+ codePoint -= 0x10000;
+ highSurrogate = (codePoint >> 10) + 0xD800;
+ lowSurrogate = (codePoint % 0x400) + 0xDC00;
+ codeUnits.push(highSurrogate);
+ codeUnits.push(lowSurrogate);
+ }
+
+ if ( index + 1 == length || codeUnits.length > MAX_SIZE )
+ {
+ for( cu in codeUnits )
+ result += String.fromCharCode(cu);
+ codeUnits = [];
+ }
+ }
+
+ return result;
+ }
+}
\ No newline at end of file
diff --git a/src/comark/GithubInlineParser.hx b/src/comark/GithubInlineParser.hx
index c11ae81..1218c78 100644
--- a/src/comark/GithubInlineParser.hx
+++ b/src/comark/GithubInlineParser.hx
@@ -12,31 +12,32 @@ class GithubInlineParser extends InlineParser
public function new( )
{
super();
-
- reMain = ~/^(?:[\n`\[\]\\!<&*_~]|[^\n`\[\]\\!<&*_~]+)/m;
+ //~/^(?:[\n`\[\]\\!<&*_]|[^\n`\[\]\\!<&*_]+)/m;
+ reMain = //~/^(?:[\n`\[\]\\!<&*_~]|[^\n`\[\]\\!<&*_~]+)/m;
+ ~/^(?:[_*`\n]+|[\[\]\\!<&*_]|(?: *[^\n `\[\]\\!<&*_~]+)+|[ \n]+)/m;
}
- override function applyParsers( c : String, inlines : Array ) : Int
+ override function applyParsers( c : Int, inlines : Array ) : Bool
{
return switch( c )
{
- case '~': parseStrike(inlines);
- case '#': parseHashtag(inlines);
+ case '~'.code: parseStrike(inlines);
+ case '#'.code: parseHashtag(inlines);
case _: super.applyParsers(c, inlines);
}
}
-
- function parseStrike( inlines : Array ) : Int
+ function parseStrike( inlines : Array ) : Bool
{
var startpos : Int = this.pos;
- var c : String;
+ var c : Int;
var first_close : Int = 0;
- var nxt : String = this.peek();
- if ( nxt == '~' ) c = nxt;
+
+ var nxt : Int = this.peek();
+ if ( nxt == '~'.code ) c = nxt;
else
- return 0;
+ return false;
// Get opening delimiters.
var res = this.scanDelims(c);
@@ -54,12 +55,13 @@ class GithubInlineParser extends InlineParser
var delimpos = inlines.length - 1;
if ( !res.can_open || numdelims != 2 )
- return 0;
+ return false;
// We started with ~~
while ( true )
{
res = this.scanDelims(c);
+
if ( res.numdelims >= 2 && res.can_close )
{
this.pos += 2;
@@ -68,19 +70,19 @@ class GithubInlineParser extends InlineParser
inlines.splice(delimpos + 1, inlines.length);
break;
}
- else if ( this.parseInline(inlines) == 0 )
+ else if( !this.parseInline(inlines) )
break;
}
- return (this.pos - startpos);
+ return (this.pos != startpos);
}
- function parseHashtag( inlines : Array ) : Int
+ function parseHashtag( inlines : Array ) : Bool
{
// Entity
var prev = subject.charAt(pos - 1);
if ( pos > 0 && prev != ' ' )
- return 0;
+ return false;
var startpos : Int = this.pos;
this.pos++;
@@ -93,12 +95,12 @@ class GithubInlineParser extends InlineParser
//title: m,
label: [ { t: 'Str', c: '#$m' } ],
});
- return m.length;
+ return true;
}
else
{
this.pos = startpos;
- return 0;
+ return false;
}
}
}
\ No newline at end of file
diff --git a/src/comark/GithubMarkdown.hx b/src/comark/GithubMarkdown.hx
index 65634b5..4f56060 100644
--- a/src/comark/GithubMarkdown.hx
+++ b/src/comark/GithubMarkdown.hx
@@ -7,11 +7,14 @@ package comark;
class GithubMarkdown extends Markdown
{
- public function new( )
+ public function new( ?header_start : Int = 1, ?allow_html : Bool = false )
{
super();
render = new GithubHtmlRenderer();
parser = new GithubDocParser();
+
+ render.header_start = header_start;
+ render.allow_html = allow_html;
}
}
\ No newline at end of file
diff --git a/src/comark/HtmlRenderer.hx b/src/comark/HtmlRenderer.hx
index badd80f..4d711f2 100644
--- a/src/comark/HtmlRenderer.hx
+++ b/src/comark/HtmlRenderer.hx
@@ -9,18 +9,25 @@ using StringTools;
class HtmlRenderer
{
+ public var header_start : Int;
+ public var allow_html : Bool;
+
// default options:
var blocksep : String; // '\n', // space between blocks
var innersep : String; // '\n', // space between block container tag and contents
var softbreak: String; // '\n', // by default, soft breaks are rendered as newlines in HTML
// set to "
" to make them hard breaks
// set to " " if you want to ignore line wrapping in source
+ //var asd : String;
public function new( )
{
blocksep = '\n';
innersep = '\n';
softbreak = '\n';
+
+ header_start = 1;
+ allow_html = true;
}
public function render( block : BlockElement ) return renderBlock(block);
@@ -66,10 +73,7 @@ class HtmlRenderer
return inTags('strong', [], renderInlines(inlineHtml.childs));
case 'Html':
- return inlineHtml.c;
-
- case 'Entity':
- return inlineHtml.c;
+ return allow_html ? inlineHtml.c : escape(inlineHtml.c);
case 'Link':
attrs = [['href', escape(inlineHtml.destination, true)]];
@@ -143,7 +147,7 @@ class HtmlRenderer
return inTags(tag, attr, innersep + renderBlocks(block.children, block.tight) + innersep);
case 'ATXHeader', 'SetextHeader':
- tag = 'h' + block.level;
+ tag = 'h' + Std.int(Math.min(6, header_start - 1 + block.level));
return inTags(tag, [], renderInlines(block.inline_content));
case 'IndentedCode':
@@ -156,7 +160,7 @@ class HtmlRenderer
return inTags('pre', [], inTags('code', attr, escape(block.string_content)));
case 'HtmlBlock':
- return block.string_content;
+ return allow_html ? block.string_content : escape(block.string_content);
case 'ReferenceDef':
return "";
@@ -196,3 +200,35 @@ class HtmlRenderer
return r;
}
}
+
+/*
+// The HtmlRenderer object.
+function HtmlRenderer(){
+ return {
+ // default options:
+ blocksep: '\n', // space between blocks
+ innersep: '\n', // space between block container tag and contents
+ softbreak: '\n', // by default, soft breaks are rendered as newlines in HTML
+ // set to "
" to make them hard breaks
+ // set to " " if you want to ignore line wrapping in source
+ escape: function(s, preserve_entities) {
+ if (preserve_entities) {
+ return s.replace(/[&](?![#](x[a-f0-9]{1,8}|[0-9]{1,8});|[a-z][a-z0-9]{1,31};)/gi,'&')
+ .replace(/[<]/g,'<')
+ .replace(/[>]/g,'>')
+ .replace(/["]/g,'"');
+ } else {
+ return s.replace(/[&]/g,'&')
+ .replace(/[<]/g,'<')
+ .replace(/[>]/g,'>')
+ .replace(/["]/g,'"');
+ }
+ },
+ renderInline: renderInline,
+ renderInlines: renderInlines,
+ renderBlock: renderBlock,
+ renderBlocks: renderBlocks,
+ render: renderBlock
+ };
+}
+*/
diff --git a/src/comark/InlineElement.hx b/src/comark/InlineElement.hx
index 2c6000e..dda6897 100644
--- a/src/comark/InlineElement.hx
+++ b/src/comark/InlineElement.hx
@@ -12,4 +12,5 @@ typedef InlineElement = {
@:optional var childs : Array;
@:optional var label : Array;
+ //@:optional var childs : Array;
};
\ No newline at end of file
diff --git a/src/comark/InlineParser.hx b/src/comark/InlineParser.hx
index ba99bd7..e06dfa1 100644
--- a/src/comark/InlineParser.hx
+++ b/src/comark/InlineParser.hx
@@ -7,9 +7,30 @@ import haxe.ds.StringMap;
using StringTools;
+typedef EmphasisOpener = {
+ var cc : Int; // cc,
+ var numdelims : Int; // numdelims,
+ var pos : Int; // inlines.length - 1,
+ var previous : EmphasisOpener; // this.emphasis_openers
+};
class InlineParser
{
+ inline static var C_NEWLINE = 10;
+ inline static var C_SPACE = 32;
+ inline static var C_ASTERISK = 42;
+ inline static var C_UNDERSCORE = 95;
+ inline static var C_BACKTICK = 96;
+ inline static var C_OPEN_BRACKET = 91;
+ inline static var C_CLOSE_BRACKET = 93;
+ inline static var C_LESSTHAN = 60;
+ inline static var C_GREATERTHAN = 62;
+ inline static var C_BANG = 33;
+ inline static var C_BACKSLASH = 92;
+ inline static var C_AMPERSAND = 38;
+ inline static var C_OPEN_PAREN = 40;
+ inline static var C_COLON = 58;
+
static var TAGNAME = '[A-Za-z][A-Za-z0-9]*';
static var ATTRIBUTENAME = '[a-zA-Z_:][a-zA-Z0-9:._-]*';
static var UNQUOTEDVALUE = "[^\"'=<>`\\x00-\\x20]+";
@@ -27,14 +48,20 @@ class InlineParser
static var HTMLTAG = "(?:" + OPENTAG + "|" + CLOSETAG + "|" + HTMLCOMMENT + "|" + PROCESSINGINSTRUCTION + "|" + DECLARATION + "|" + CDATA + ")";
public static var reHtmlTag = new EReg('^' + HTMLTAG, 'i');
+ public static var ESCAPABLE = '[!"#$%&\'()*+,./:;<=>?@[\\\\\\]^_`{|}~-]';
+ public static var reAllEscapedChar = new EReg('\\\\(' + ESCAPABLE + ')', 'g');
// Matches a character with a special meaning in markdown,
// or a string of non-special characters.
var reMain : EReg;
- static var reEscapable = new EReg(DocParser.ESCAPABLE, '');
+ static var reEscapable = new EReg(ESCAPABLE, '');
+
+ static var ENTITY = "&(?:#x[a-f0-9]{1,8}|#[0-9]{1,8}|[a-z][a-z0-9]{1,31});";
+ static var reEntity = new EReg(ENTITY, 'gi');
+ static var reEntityHere = new EReg('^' + ENTITY, 'i');
- static var ESCAPED_CHAR = '\\\\' + DocParser.ESCAPABLE;
+ static var ESCAPED_CHAR = '\\\\' + ESCAPABLE;
static var reLinkDestinationBraces = new EReg('^(?:[<](?:[^<>\\n\\\\\\x00]' + '|' + ESCAPED_CHAR + '|' + '\\\\)*[>])', '');
static var REG_CHAR = '[^\\\\()\\x00-\\x20]';
@@ -50,12 +77,14 @@ class InlineParser
var subject : String;
var label_nest_level : Int; //0, // used by parseLinkLabel method
+ var emphasis_openers : EmphasisOpener; // used by parseEmphasis method
var pos : Int; // 0,
var refmap : StringMap; // { },
public function new( )
{
- reMain = ~/^(?:[\n`\[\]\\!<&*_]|[^\n`\[\]\\!<&*_]+)/m;
+ reMain = // ~/^(?:[\n`\[\]\\!<&*_]|[^\n`\[\]\\!<&*_]+)/m;
+ ~/^(?:[_*`\n]+|[\[\]\\!<&*_]|(?: *[^\n `\[\]\\!<&*_]+)+|[ \n]+)/m;
subject = '';
label_nest_level = 0;
@@ -63,6 +92,8 @@ class InlineParser
refmap = new StringMap();
}
+ // Parse s as a list of inlines, using refmap to resolve references.
+ // parseInlines
public function parse( s : String, refmap : StringMap ) : Array
{
this.subject = s;
@@ -70,7 +101,7 @@ class InlineParser
this.refmap = refmap != null ? refmap : new StringMap();
var inlines : Array = [];
- while ( parseInline(inlines) > 0 ) { }
+ while ( parseInline(inlines) ) { }
return inlines;
}
@@ -80,29 +111,55 @@ class InlineParser
// number of characters parsed (possibly 0).
- // Parse the next inline element in subject, advancing subject position
- // and adding the result to 'inlines'.
- function parseInline( inlines : Array ) : Int
+ // Parse the next inline element in subject, advancing subject position.
+ // On success, add the result to the inlines list, and return true.
+ // On failure, return false.
+ function parseInline( inlines : Array ) : Bool
{
+ var startpos = this.pos;
+ var origlen = inlines.length;
+
var c = peek();
+ if ( c == -1 )
+ return false;
+
+ var res : Bool = applyParsers(c, inlines);
+ if ( !res )
+ {
+ pos += 1;
+ inlines.push({
+ t: 'Str',
+ c: EntityToChar.fromCodePoint([c])
+ });
+ }
- var res : Int = applyParsers(c, inlines);
- return res > 0 ? res : parseString(inlines);
+ return true;
+ // return res > 0 ? res : parseString(inlines);
}
- function applyParsers( c : String, inlines : Array ) : Int
+ function applyParsers( c : Int, inlines : Array ) : Bool
{
return switch( c )
{
- case '\n': parseNewline(inlines);
- case '\\': parseEscaped(inlines);
- case '`': parseBackticks(inlines);
- case '*', '_': parseEmphasis(inlines);
- case '[': parseLink(inlines);
- case '!': parseImage(inlines);
- case '<': var res = parseAutolink(inlines); res > 0 ? res : parseHtmlTag(inlines);
- case '&': parseEntity(inlines);
- case _: 0;
+ case C_NEWLINE,
+ C_SPACE: parseNewline(inlines);
+
+ case C_BACKSLASH: parseBackslash(inlines);
+
+ case C_BACKTICK: parseBackticks(inlines);
+
+ case C_ASTERISK,
+ C_UNDERSCORE: parseEmphasis(c, inlines);
+
+ case C_OPEN_BRACKET: parseLink(inlines);
+
+ case C_BANG: parseImage(inlines);
+
+ case C_LESSTHAN: parseAutolink(inlines) || parseHtmlTag(inlines);
+
+ case C_AMPERSAND: parseEntity(inlines);
+
+ case _: parseString(inlines);
}
}
@@ -124,12 +181,10 @@ class InlineParser
if ( matchChars == 0 )
return 0;
-
-
rawlabel = this.subject.substr(0, matchChars);
// colon:
- if ( this.peek() == ':' )
+ if ( this.peek() == C_COLON )
{
this.pos++;
}
@@ -141,7 +196,7 @@ class InlineParser
// link url
this.spnl();
-
+
dest = this.parseLinkDestination();
if ( dest == null || dest.length == 0 )
{
@@ -175,9 +230,22 @@ class InlineParser
}
// Parse a newline.
- // If it was preceded by two spaces, return a hard line break; otherwise a soft line break.
- function parseNewline( inlines : Array ) : Int
+ // If it was preceded by two spaces, return a hard line break;
+ // otherwise a soft line break.
+ function parseNewline( inlines : Array ) : Bool
{
+ var m : String = match(~/^ *\n/);
+
+ if ( m == null )
+ return false;
+
+ if ( m.length > 2 )
+ inlines.push( { t: 'Hardbreak' } );
+ else
+ inlines.push( { t: 'Softbreak' } );
+
+ return true;
+ /*
if ( this.peek() == '\n')
{
pos++;
@@ -198,48 +266,48 @@ class InlineParser
return 1;
}
else return 0;
+ */
}
// Parse a backslash-escaped special character, adding either the escaped
// character, a hard line break (if the backslash is followed by a newline),
// or a literal backslash to the 'inlines' list.
- function parseEscaped( inlines : Array ) : Int
+ function parseBackslash( inlines : Array ) : Bool
{
var subj = this.subject;
var pos = this.pos;
- if ( subj.charAt(pos) == '\\' )
+ if ( subj.charCodeAt(pos) == C_BACKSLASH )
{
if ( subj.charAt(pos + 1) == '\n' )
{
inlines.push({ t: 'Hardbreak' });
this.pos = this.pos + 2;
- return 2;
}
else if ( reEscapable.match(subj.charAt(pos + 1)) )
{
inlines.push({ t: 'Str', c: subj.charAt(pos + 1) });
this.pos = this.pos + 2;
- return 2;
}
else
{
this.pos++;
inlines.push({t: 'Str', c: '\\'});
- return 1;
}
+
+ return true;
}
- else return 0;
+ else return false;
}
// Attempt to parse backticks, adding either a backtick code span or a
// literal sequence of backticks to the 'inlines' list.
- function parseBackticks( inlines : Array ) : Int
+ function parseBackticks( inlines : Array ) : Bool
{
var startpos = this.pos;
var ticks = this.match(~/^`+/);
if ( ticks == null )
- return 0;
+ return false;
var afterOpenTicks = this.pos;
var foundCode = false;
@@ -252,174 +320,122 @@ class InlineParser
t: 'Code',
c: ~/[ \n]+/g.replace(subject.substring(afterOpenTicks, this.pos - ticks.length), ' ').trim()
});
- return (this.pos - startpos);
+ return true;
}
}
// If we got here, we didn't match a closing backtick sequence.
- inlines.push({ t: 'Str', c: ticks });
this.pos = afterOpenTicks;
-
- return (this.pos - startpos);
+ inlines.push({ t: 'Str', c: ticks });
+ return true;
}
- // Attempt to parse emphasis or strong emphasis in an efficient way, with no backtracking.
- function parseEmphasis( inlines : Array ) : Int
+ // Attempt to parse emphasis or strong emphasis.
+ function parseEmphasis( cc : Int, inlines : Array ) : Bool
{
var startpos = this.pos;
- var c ;
- var first_close = 0;
- var nxt = this.peek();
- if (nxt == '*' || nxt == '_') c = nxt;
- else
- return 0;
-
- var numdelims;
- var delimpos;
-
- // Get opening delimiters.
- var res = this.scanDelims(c);
- numdelims = res.numdelims;
- this.pos += numdelims;
-
- // We provisionally add a literal string. If we match appropriate
- // closing delimiters, we'll change this to Strong or Emph.
- inlines.push({
- t: 'Str',
- c: this.subject.substr(this.pos - numdelims, numdelims)
- });
- // Record the position of this opening delimiter:
- delimpos = inlines.length - 1;
+ var res = scanDelims(cc);
+ var numdelims = res.numdelims;
- if ( !res.can_open || numdelims == 0 )
- return 0;
-
- var first_close_delims = 0;
+ if ( numdelims == 0 )
+ {
+ this.pos = startpos;
+ return false;
+ }
- switch ( numdelims )
+ if ( res.can_close )
{
- case 1: // we started with * or _
- while ( true )
- {
- res = this.scanDelims(c);
- if ( res.numdelims >= 1 && res.can_close )
- {
- this.pos += 1;
- // Convert the inline at delimpos, currently a string with the delim,
- // into an Emph whose contents are the succeeding inlines
- inlines[delimpos].t = 'Emph';
- inlines[delimpos].childs = inlines.slice(delimpos + 1, inlines.length);
- inlines.splice(delimpos + 1, inlines.length);
- break;
- }
- else if ( this.parseInline(inlines) == 0 )
- break;
- }
- return (this.pos - startpos);
-
- case 2: // We started with ** or __
- while ( true )
- {
- res = this.scanDelims(c);
- if ( res.numdelims >= 2 && res.can_close )
- {
- this.pos += 2;
- inlines[delimpos].t = 'Strong';
- inlines[delimpos].childs = inlines.slice(delimpos + 1, inlines.length);
- inlines.splice(delimpos + 1, inlines.length);
- break;
- }
- else if ( this.parseInline(inlines) == 0 )
- break;
- }
- return (this.pos - startpos);
-
- case 3: // We started with *** or ___
- while ( true )
+ // Walk the stack and find a matching opener, if possible
+ var opener = this.emphasis_openers;
+ while ( opener != null )
+ {
+ // we have a match!
+ if ( opener.cc == cc )
{
- res = this.scanDelims(c);
- if ( res.numdelims >= 1 && res.numdelims <= 3 && res.can_close && res.numdelims != first_close_delims )
+ // all openers used
+ if ( opener.numdelims <= numdelims )
{
- if ( first_close_delims == 1 && numdelims > 2 ) res.numdelims = 2;
- else if ( first_close_delims == 2 ) res.numdelims = 1;
- else if ( res.numdelims == 3 )
+ this.pos += opener.numdelims;
+ var X : Dynamic -> Dynamic = null;
+ switch ( opener.numdelims )
{
- // If we opened with ***, then we interpret *** as ** followed by *
- // giving us
- res.numdelims = 1;
+ case 3: X = function(x) { return makeStrong([makeEmph(x)]); };
+ case 2: X = makeStrong;
+ case 1, _: X = makeEmph;
}
- this.pos += res.numdelims;
- if (first_close > 0)
- {
- // if we've already passed the first closer:
- inlines[delimpos].t = first_close_delims == 1 ? 'Strong' : 'Emph';
- inlines[delimpos].childs = ([{
- t: first_close_delims == 1 ? 'Emph' : 'Strong',
- childs: inlines.slice(delimpos + 1, first_close)
- }] : Array )
- .concat(inlines.slice(first_close + 1));
-
- inlines.splice(delimpos + 1, inlines.length);
- break;
- }
- else
- {
- // this is the first closer; for now, add literal string;
- // we'll change this when he hit the second closer
- inlines.push({
- t: 'Str',
- c: this.subject.substr(this.pos - res.numdelims, this.pos)
- });
- first_close = inlines.length - 1;
- first_close_delims = res.numdelims;
- }
+ inlines[opener.pos] = X(inlines.slice(opener.pos + 1));
+ inlines.splice(opener.pos + 1, inlines.length - (opener.pos + 1));
+
+ // Remove entries after this, to prevent overlapping nesting:
+ this.emphasis_openers = opener.previous;
+ return true;
}
- else
- {
- // parse another inline element, til we hit the end
- if ( this.parseInline(inlines) == 0 )
- break;
+ // only some openers used
+ else if ( opener.numdelims > numdelims )
+ {
+ this.pos += numdelims;
+ opener.numdelims -= numdelims;
+
+ inlines[opener.pos].c = inlines[opener.pos].c.substr(0, opener.numdelims);
+
+ var X : Dynamic -> Dynamic = numdelims == 2 ? makeStrong : makeEmph;
+
+ inlines[opener.pos + 1] = X(inlines.slice(opener.pos + 1));
+ inlines.splice(opener.pos + 2, inlines.length - (opener.pos + 2));
+
+ // Remove entries after this, to prevent overlapping nesting:
+ this.emphasis_openers = opener;
+ return true;
}
}
- return (this.pos - startpos);
-
- case _:
- //return res;
- return 0;
+ opener = opener.previous;
+ }
}
- return 0;
+ // If we're here, we didn't match a closer.
+ this.pos += numdelims;
+ inlines.push(makeStr(this.subject.substring(startpos, startpos + numdelims)));
+
+ if ( res.can_open )
+ {
+ // Add entry to stack for this opener
+ this.emphasis_openers = {
+ cc: cc,
+ numdelims: numdelims,
+ pos: inlines.length - 1,
+ previous: this.emphasis_openers
+ };
+ }
+
+ return true;
}
// Attempt to parse an image. If the opening '!' is not followed
// by a link, add a literal '!' to inlines.
- function parseImage( inlines : Array )
+ function parseImage( inlines : Array ) : Bool
{
if ( this.match(~/^!/) != null )
{
- var n = this.parseLink(inlines);
- if ( n == 0 )
- {
- inlines.push({ t: 'Str', c: '!' });
- return 1;
- }
- else if ( inlines[inlines.length - 1] != null && inlines[inlines.length - 1].t == 'Link' )
+ var link = parseLink(inlines);
+ if ( link )
{
+ // if ( inlines[inlines.length - 1] != null && inlines[inlines.length - 1].t == 'Link' )
inlines[inlines.length - 1].t = 'Image';
- return n+1;
+ return true;
}
else
- throw "Shouldn't happen";
-
+ {
+ inlines.push({ t: 'Str', c: '!' });
+ return true;
+ }
}
- else return 0;
+ else return false;
}
- // Attempt to parse a link. If successful, add the link to
- // inlines.
- function parseLink( inlines : Array ) : Int
+ // Attempt to parse a link. If successful, add the link to inlines.
+ function parseLink( inlines : Array ) : Bool
{
var startpos = this.pos;
var reflabel : String;
@@ -429,14 +445,14 @@ class InlineParser
n = this.parseLinkLabel();
if ( n == 0 )
- return 0;
+ return false;
var afterlabel = this.pos;
var rawlabel = this.subject.substr(startpos, n);
// if we got this far, we've parsed a label.
// Try to parse an explicit link: [label](url "title")
- if ( this.peek() == '(' )
+ if ( this.peek() == C_OPEN_PAREN )
{
this.pos++;
if ( this.spnl() &&
@@ -451,18 +467,18 @@ class InlineParser
if ( title == null )
title = '';
- inlines.push( {
+ inlines.push({
t: 'Link',
destination: dest,
title: title,
label: parseRawLabel(rawlabel)
});
- return this.pos - startpos;
+ return true;
}
else
{
this.pos = startpos;
- return 0;
+ return false;
}
}
@@ -470,6 +486,7 @@ class InlineParser
// first, see if there's another label
var savepos = this.pos;
this.spnl();
+
var beforelabel = this.pos;
n = this.parseLinkLabel();
if ( n == 2 )
@@ -497,17 +514,17 @@ class InlineParser
title: link.title,
label: parseRawLabel(rawlabel)
});
- return this.pos - startpos;
+ return true;
}
else
{
this.pos = startpos;
- return 0;
+ return false;
}
// Nothing worked, rewind:
this.pos = startpos;
- return 0;
+ return false;
}
// Attempt to parse link title (sans quotes), returning the string
@@ -517,7 +534,7 @@ class InlineParser
var title = this.match(reLinkTitle);
if ( title != null )
// chop off quotes from title and unescape:
- return unescape(title.substr(1, title.length - 2));
+ return unescapeString(title.substr(1, title.length - 2));
else
return null;
}
@@ -529,21 +546,29 @@ class InlineParser
var res = this.match(reLinkDestinationBraces);
if ( res != null )
// chop off surrounding <..>:
- return unescape(res.substr(1, res.length - 2));
+ return fixEncode(
+ unescape(
+ unescapeString(res.substr(1, res.length - 2))
+ ).urlEncode()
+ );
else
{
res = this.match(reLinkDestination);
if ( res != null )
- return unescape(res);
+ return fixEncode(
+ unescape(
+ unescapeString(res)
+ ).urlEncode()
+ );
else
return null;
}
}
// Attempt to parse a link label, returning number of characters parsed.
- function parseLinkLabel( )
+ function parseLinkLabel( ) : Int
{
- if ( this.peek() != '[' )
+ if ( this.peek() != C_OPEN_BRACKET )
return 0;
var startpos = this.pos;
@@ -563,29 +588,27 @@ class InlineParser
this.pos++; // advance past [
var c;
- while ( (c = this.peek()) != null && (c != ']' || nest_level > 0) )
+ while ( (c = this.peek()) != -1 && (c != C_CLOSE_BRACKET || nest_level > 0) )
{
switch (c)
{
- case '`': this.parseBackticks([]);
- case '<': this.parseAutolink([]) > 0 || this.parseHtmlTag([]) > 0 || this.parseString([]) > 0;
- case '[': // nested []
+ case C_BACKTICK: this.parseBackticks([]);
+ case C_LESSTHAN: if ( !(this.parseAutolink([]) || this.parseHtmlTag([])) ) this.pos++;
+ case C_OPEN_BRACKET: // nested []
nest_level++;
this.pos++;
- case ']': // nested []
+ case C_CLOSE_BRACKET: // nested []
nest_level--;
this.pos++;
- case '\\':
- this.parseEscaped([]);
+ case C_BACKSLASH: this.parseBackslash([]);
- default:
- this.parseString([]);
+ case _: this.parseString([]);
}
}
- if ( c == ']' )
+ if ( c == C_CLOSE_BRACKET )
{
this.label_nest_level = 0;
this.pos++; // advance past ]
@@ -593,7 +616,7 @@ class InlineParser
}
else
{
- if ( c == null )
+ if ( c == -1 )
this.label_nest_level = nest_level;
this.pos = startpos;
@@ -611,34 +634,42 @@ class InlineParser
}
// Attempt to parse an entity, adding to inlines if successful.
- function parseEntity( inlines : Array )
+ function parseEntity( inlines : Array ) : Bool
{
var m;
- if ( (m = this.match(~/^&(?:#x[a-f0-9]{1,8}|#[0-9]{1,8}|[a-z][a-z0-9]{1,31});/i)) != null )
+ /*
+ if ((m = this.match(reEntityHere))) {
+ inlines.push({ t: 'Str', c: entityToChar(m) });
+ return true;
+ } else {
+ return false;
+ }
+ */
+ if ( (m = this.match(reEntityHere)) != null )
{
- inlines.push({ t: 'Entity', c: m });
- return m.length;
+ inlines.push({ t: 'Str', c: EntityToChar.entityToChar(m) });
+ return true;
}
else
- return 0;
+ return false;
}
// Parse a run of ordinary characters, or a single character with
// a special meaning in markdown, as a plain string, adding to inlines.
- function parseString( inlines : Array ) : Int
+ function parseString( inlines : Array ) : Bool
{
var m;
if ( (m = this.match(reMain)) != null )
{
inlines.push({ t: 'Str', c: m });
- return m.length;
+ return true;
}
else
- return 0;
+ return false;
}
// Attempt to parse an autolink (URL or email in pointy brackets).
- function parseAutolink( inlines : Array ) : Int
+ function parseAutolink( inlines : Array ) : Bool
{
var erMail = ~/^<([a-zA-Z0-9.!#$%&'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*)>/;
var erLink = ~/^<(?:coap|doi|javascript|aaa|aaas|about|acap|cap|cid|crid|data|dav|dict|dns|file|ftp|geo|go|gopher|h323|http|https|iax|icap|im|imap|info|ipp|iris|iris.beep|iris.xpc|iris.xpcs|iris.lwz|ldap|mailto|mid|msrp|msrps|mtqp|mupdate|news|nfs|ni|nih|nntp|opaquelocktoken|pop|pres|rtsp|service|session|shttp|sieve|sip|sips|sms|snmp|soap.beep|soap.beeps|tag|tel|telnet|tftp|thismessage|tn3270|tip|tv|urn|vemmi|ws|wss|xcon|xcon-userid|xmlrpc.beep|xmlrpc.beeps|xmpp|z39.50r|z39.50s|adiumxtra|afp|afs|aim|apt|attachment|aw|beshare|bitcoin|bolo|callto|chrome|chrome-extension|com-eventbrite-attendee|content|cvs|dlna-playsingle|dlna-playcontainer|dtn|dvb|ed2k|facetime|feed|finger|fish|gg|git|gizmoproject|gtalk|hcp|icon|ipn|irc|irc6|ircs|itms|jar|jms|keyparc|lastfm|ldaps|magnet|maps|market|message|mms|ms-help|msnim|mumble|mvn|notes|oid|palm|paparazzi|platform|proxy|psyc|query|res|resource|rmi|rsync|rtmp|secondlife|sftp|sgn|skype|smb|soldat|spotify|ssh|steam|svn|teamspeak|things|udp|unreal|ut2004|ventrilo|view-source|webcal|wtai|wyciwyg|xfire|xri|ymsgr):[^<>\x00-\x20]*>/i;
@@ -648,50 +679,58 @@ class InlineParser
if ( (m = this.match(erMail)) != null )
{
// email autolink
- //dest = m.substr(1,-1);
dest = m.substr(1, m.length - 2);
inlines.push( {
t: 'Link',
label: [{ t: 'Str', c: dest }],
- destination: 'mailto:' + dest
+ destination: 'mailto:' + fixEncode(unescape(dest).urlEncode())
});
- return m.length;
+ return true;
}
else if ( (m = this.match(erLink)) != null )
{
- //dest = m.substr(1, -1);
dest = m.substr(1, m.length - 2);
- inlines.push( {
+
+ inlines.push({
t: 'Link',
label: [{ t: 'Str', c: dest }],
- destination: dest
+ destination: fixEncode(unescape(dest).urlEncode())
});
- return m.length;
+ return true;
}
else
- return 0;
+ return false;
}
// Attempt to parse a raw HTML tag.
- function parseHtmlTag(inlines : Array ) : Int
+ function parseHtmlTag(inlines : Array ) : Bool
{
var m = this.match(reHtmlTag);
if ( m != null )
{
inlines.push({ t: 'Html', c: m });
- return m.length;
+ return true;
}
else
- return 0;
+ return false;
}
// Replace backslash escapes with literal characters.
- function unescape( s : String ) : String return DocParser.reAllEscapedChar.replace(s, '$1');
+ function unescape( s : String ) : String
+ {
+ return s.replace('%20', ' '); // reAllEscapedChar.replace(s, '$1');
+ }
+
+ // Replace entities and backslash escapes with literal characters.
+ inline public static function unescapeString( s : String ) : String
+ {
+ return reEntity.map(reAllEscapedChar.replace(s, '$1'), EntityToChar.entityToCharEr);
+ }
// Returns the character at the current subject position, or null if
// there are no more characters.
- function peek( ) return pos == subject.length ? null : subject.charAt(pos); // ] || null;
+ function peek( ) : Int return pos == subject.length ? -1 : subject.charCodeAt(pos); // ] || null;
// Parse zero or more space characters, including at most one newline
function spnl( ) : Bool
@@ -720,13 +759,14 @@ class InlineParser
// the number of delimiters and whether they are positioned such that
// they can open and/or close emphasis or strong emphasis. A utility
// function for strong/emph parsing.
- function scanDelims( c )
+ function scanDelims( c : Int )
{
- var numdelims = 0;
- var first_close_delims = 0;
- var char_before;
- var char_after;
- var startpos = this.pos;
+ var numdelims : Int = 0;
+ var first_close_delims : Int = 0;
+ var char_before : String;
+ var char_after : String;
+ var c_after : Int;
+ var startpos : Int = this.pos;
char_before = this.pos == 0 ? '\n' : this.subject.charAt(this.pos - 1);
@@ -736,13 +776,13 @@ class InlineParser
this.pos++;
}
- char_after = this.peek();
- if( char_after == null ) char_after = '\n';
+ c_after = this.peek();
+ char_after = c_after == -1 ? '\n' : EntityToChar.fromCodePoint([c_after]);
var can_open = numdelims > 0 && numdelims <= 3 && !(~/\s/.match(char_after));
var can_close = numdelims > 0 && numdelims <= 3 && !(~/\s/.match(char_before));
- if ( c == '_' )
+ if ( c == C_UNDERSCORE )
{
can_open = can_open && !((~/[a-z0-9]/i).match(char_before));
can_close = can_close && !((~/[a-z0-9]/i).match(char_after));
@@ -755,4 +795,51 @@ class InlineParser
can_close: can_close
};
}
+
+ inline public function fixEncode( s : String ) : String return s
+ .replace('%2F', '/')
+ .replace('%28', '(')
+ .replace('%29', ')')
+ .replace('%2A', '*')
+ .replace('%3A', ':')
+ .replace('%3F', '?')
+ .replace('%3D', '=')
+ .replace('%26', '&')
+ .replace('%40', '@')
+ .replace('%2B', '+')
+ ;
+
+
+ inline public static function makeEmph( ils : Array ) : InlineElement return { t: 'Emph', childs: ils };
+ inline public static function makeStrong( ils : Array ) : InlineElement return { t: 'Strong', childs: ils };
+ inline public static function makeStr( s : String ) : InlineElement return { t: 'Str', c: s };
+
+/*
+ subject: '',
+ label_nest_level: 0, // used by parseLinkLabel method
+ pos: 0,
+ refmap: {},
+
+ match: match,
+ peek: peek,
+ spnl: spnl,
+
+ parseBackticks: parseBackticks,
+ parseEscaped: parseEscaped,
+ parseAutolink: parseAutolink,
+ parseHtmlTag: parseHtmlTag,
+ scanDelims: scanDelims,
+ parseEmphasis: parseEmphasis,
+ parseLinkTitle: parseLinkTitle,
+ parseLinkDestination: parseLinkDestination,
+ parseLinkLabel: parseLinkLabel,
+ parseLink: parseLink,
+ parseEntity: parseEntity,
+ parseString: parseString,
+ parseNewline: parseNewline,
+ parseImage: parseImage,
+ parseReference: parseReference,
+ parseInline: parseInline,
+ parse: parseInlines
+*/
}
\ No newline at end of file
diff --git a/src/comark/Markdown.hx b/src/comark/Markdown.hx
index c7c5541..116665c 100644
--- a/src/comark/Markdown.hx
+++ b/src/comark/Markdown.hx
@@ -4,12 +4,11 @@
*/
package comark;
-
class Markdown
{
var render : HtmlRenderer;
var parser : DocParser;
-
+
public function new( )
{
render = new HtmlRenderer();
diff --git a/test/Main.hx b/test/Main.hx
index 04dea14..15e51c0 100644
--- a/test/Main.hx
+++ b/test/Main.hx
@@ -20,6 +20,8 @@ class Main
{
var r : TestRunner;
var files : Array;
+ var spec : String;
+ var specG : String;
public function new( )
{
@@ -32,13 +34,24 @@ class Main
files = [];
#if neko
- var spec = File.getContent('tests/spec.txt');
+ spec = File.getContent('tests/spec.txt');
+ specG = File.getContent('tests/github.txt');
#elseif js
LoadResources.loadFile('spec.txt', 'bin/tests/spec.txt');
- var spec = Resource.getString('spec.txt');
+ LoadResources.loadFile('github.txt', 'bin/tests/github.txt');
+
+ spec = Resource.getString('spec.txt');
+ specG = Resource.getString('github.txt');
#else
#error
#end
+ process(spec);
+ process(spec, true);
+ process(specG, true, false);
+ }
+
+ function process( spec : String, github : Bool = false, skipUtf8 : Bool = true )
+ {
var lines = spec.split('\n');
var eStart : EReg = ~/^\.$/;
@@ -53,6 +66,7 @@ class Main
var markdown = '';
var html = '';
+ //var secLevel = 0; var sec
var header = '';
for ( l in lines )
@@ -69,15 +83,26 @@ class Main
else if ( stage == 0 )
{
example++;
-#if neko
- if ( example == 2 ) { } // UTF german
- else if ( example == 110 ) { } // UTF greek
- else if ( example == 349 ) { } // UTF russian
- else
-#end
- if ( example == 25 ) { } // Github Flavour #hashtag
+
+ var md = ~/␣/g.replace(~/→/g.replace(markdown, '\t'), ' ');
+ files.push(md);
+
+ if ( false ) { }
+ else if ( skipUtf8 && example == 2 ) { } // UTF german
+ else if ( skipUtf8 && example == 120 ) { } // UTF greek
+ else if ( skipUtf8 && example == 234 ) { } // UTF magic - Decimal entities
+ else if ( skipUtf8 && example == 235 ) { } // UTF magic - Hexadecimal entities
+ else if ( skipUtf8 && example == 377 ) { } // UTF russian
+
+ else if ( github && example == 25 ) { } // Github Flavour #hashtag
+
else
- r.add(new StringTest(new GithubMarkdown(), ~/␣/g.replace(~/→/g.replace(markdown, '\t'), ' '), html, header, example, exampleLine));
+ {
+ if ( github )
+ r.add(new StringTest(new GithubMarkdown(1, true), md, html, header, example, exampleLine));
+ else
+ r.add(new StringTest(new Markdown(), md, html, header, example, exampleLine));
+ }
markdown = '';
html = '';
@@ -94,8 +119,44 @@ class Main
}
}
+
+ public function run( ) : Void
+ {
+#if debug
+ r.run();
+#else
+ bench();
+#end
+ }
+
+#if !debug
+ function bench( ) : Void
+ {
+ var start1 = t();
+ for( i in 0...5 )
+ for ( f in files ) var out = new comark.Markdown().parse(f);
+ var end1 = t();
+
+ var start2 = t();
+ for( i in 0...5 )
+ for ( f in files ) var out = new mdcebe.Markdown().parse(f);
+ var end2 = t();
+
+ var start3 = t();
+ for( i in 0...5 )
+ for ( f in files ) var out = markdown.Markdown.markdownToHtml(f);
+ var end3 = t();
+
+ trace([end1 - start1, end2 - start2, end3 - start3]);
+ }
+#end
- public function run( ) : Void r.run();
+ inline static function t( ) return
+#if neko
+ Sys.time();
+#elseif js
+ Date.now().getTime();
+#end
static function main() new Main().run();
}
diff --git a/test/StringTest.hx b/test/StringTest.hx
index a015c7e..9ddb58d 100644
--- a/test/StringTest.hx
+++ b/test/StringTest.hx
@@ -3,13 +3,17 @@
* @author ...
*/
+package ;
+
import comark.Markdown;
+
class StringTest extends haxe.unit.TestCase
{
public static var PATH = '';
var md : Markdown;
+ //var name : String;
var src : String;
var res : String;
@@ -21,6 +25,7 @@ class StringTest extends haxe.unit.TestCase
public function new( md : Markdown, src : String, res : String, header : String, num : Int, line : Int )
{
this.md = md;
+ //this.name = name;
super();
@@ -33,6 +38,7 @@ class StringTest extends haxe.unit.TestCase
}
public function testCoversion( ) : Void assertEquals(res, md.parse(src));
+ //public function testCoversion( ) : Void assertEquals(src, md.parse(src));
override public function setup( ) : Void
{