diff --git a/Zend/tests/markup_operand_position_bc.phpt b/Zend/tests/markup_operand_position_bc.phpt new file mode 100644 index 000000000000..1f42c284ff9c --- /dev/null +++ b/Zend/tests/markup_operand_position_bc.phpt @@ -0,0 +1,131 @@ +--TEST-- +Native markup: bare "<" after operand-ending tokens stays a comparison (no-space BC) +--FILE-- + 1 }check(new Base)); + +// Bare `exit` is an expression operand; "<" after it is a comparison. +// (Only reached when the left side is falsy-compared, so guard it.) +function never_exit(): bool { return false && exit` operator is untouched in infix (operator) position. +var_dump($a[0]<>2); + +// Plain variables, literals, and `)` end an operand too, with or without spaces. +$lhs = 1; $rhs = 2; +var_dump($lhs < $rhs); +var_dump($lhs<$rhs); +var_dump(3 < 4); +var_dump(($lhs) < $rhs); + +// Other "<"-led operators are unchanged: spaceship, less-or-equal, shifts. +var_dump($lhs <=> $rhs); +var_dump($lhs <= $rhs); +var_dump(1 << 4); +var_dump(64 >> 2); + +// A heredoc body containing "<" stays literal text, not markup. +echo << text +EOT, "\n"; + +// Error suppression with "@" still works before an expression. +$sup = []; var_dump(@$sup['missing']); + +// Bare `yield` is a complete operand: `<` and `<>` after it stay comparisons +// ((yield) != 5, (yield) < FOO), with or without spaces. Yielding markup +// therefore needs parentheses: `yield (
)`. +function gen_ne() { var_dump(yield <> 5); } +$g = gen_ne(); $g->current(); $g->send(7); +function gen_lt() { var_dump(yield < FOO); } +$g = gen_lt(); $g->current(); $g->send("aaa"); +function gen_lt_nospace() { $r = yieldcurrent(); $g->send("aaa"); + +// Inverse direction: in operand position (after `=`, `(`, `,`, `=>`, `return`, +// and parenthesized after `yield`) a "; + take(

hi

,
); + $f = fn() => a; + yield (
  • item
  • ); + return done; +} + +// Dynamic tags: "<$tag" and "<{expr}" in operand position are markup +// ("$i--<$n" above is the operator-position inverse, still a comparison). +function never_runs_dynamic() { + $tag = 'div'; + $x = <$tag class="a">hi; + $y = <{$tag . 'x'}>hi; + take(<{pick()}/>); + return <$tag/>; +} +echo "parsed\n"; +?> +--EXPECT-- +bool(true) +bool(true) +int(4) +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) +bool(false) +bool(false) +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) +int(-1) +bool(true) +int(16) +int(16) +literal text +NULL +bool(true) +bool(true) +bool(true) +parsed diff --git a/Zend/zend_globals.h b/Zend/zend_globals.h index 61499c0cc23d..062cf62f3a9c 100644 --- a/Zend/zend_globals.h +++ b/Zend/zend_globals.h @@ -402,6 +402,11 @@ struct _zend_php_scanner_globals { /* initial string length after scanning to first variable */ int scanned_string_len; + /* Last significant token returned, used to decide whether a bare "<" in + * ST_IN_SCRIPTING is in operand position (begins native markup) or operator + * position (a comparison). See zend_language_scanner.l. */ + int last_token; + /* hooks */ void (*on_event)( zend_php_scanner_event event, int token, int line, diff --git a/Zend/zend_language_parser.y b/Zend/zend_language_parser.y index b4dda00404ea..325dae1c0fca 100644 --- a/Zend/zend_language_parser.y +++ b/Zend/zend_language_parser.y @@ -26,6 +26,7 @@ #include "zend_constants.h" #include "zend_language_scanner.h" #include "zend_exceptions.h" +#include "zend_markup.h" #define YYSIZE_T size_t #define yytnamerr zend_yytnamerr @@ -250,6 +251,20 @@ static YYSIZE_T zend_yytnamerr(char*, const char*); /* Token used to force a parse error from the lexer */ %token T_ERROR +/* Native markup tokens. Declared last so they append to the token enum: + * inserting them mid-list would renumber every later token (T_INLINE_HTML + * onward), breaking code that hardcodes token integers. */ +%token T_MARKUP_NAME "markup tag name" +%token T_MARKUP_TEXT "markup text" +%token T_MARKUP_ATTR_VALUE "markup attribute value" +%token T_MARKUP_COMMENT "markup comment" +%token T_MARKUP_OPEN "markup tag start" +%token T_MARKUP_CLOSE_OPEN "markup closing tag start" +%token T_MARKUP_TAG_END "markup tag end" +%token T_MARKUP_SELF_CLOSE "markup self-close" +%token T_MARKUP_INTERP_START "markup interpolation start" +%token T_MARKUP_DOCTYPE "markup doctype" + %type top_statement namespace_name name statement function_declaration_statement %type class_declaration_statement trait_declaration_statement legacy_namespace_name %type interface_declaration_statement interface_extends_list @@ -283,6 +298,8 @@ static YYSIZE_T zend_yytnamerr(char*, const char*); %type attributed_statement attributed_top_statement attributed_class_statement attributed_parameter %type attribute_decl attribute attributes attribute_group namespace_declaration_name %type match match_arm_list non_empty_match_arm_list match_arm match_arm_cond_list +%type markup_element markup_children markup_child +%type markup_attributes markup_attribute %type enum_declaration_statement enum_backing_type enum_case enum_case_expr %type function_name non_empty_member_modifiers %type property_hook property_hook_list optional_property_hook_list hooked_property property_hook_body @@ -1249,9 +1266,58 @@ new_non_dereferenceable: { $$ = zend_ast_create(ZEND_AST_NEW, $2, zend_ast_create_list(0, ZEND_AST_ARG_LIST)); } ; +markup_element: + T_MARKUP_OPEN T_MARKUP_NAME markup_attributes T_MARKUP_TAG_END markup_children T_MARKUP_CLOSE_OPEN T_MARKUP_NAME T_MARKUP_TAG_END + { $$ = zend_ast_create_markup_checked($2, $3, $5, $7, false); if (!$$) { YYERROR; } } + | T_MARKUP_OPEN T_MARKUP_NAME markup_attributes T_MARKUP_SELF_CLOSE + { $$ = zend_ast_create_markup_element($2, $3, NULL); if (!$$) { YYERROR; } } + | T_MARKUP_OPEN T_MARKUP_TAG_END markup_children T_MARKUP_CLOSE_OPEN T_MARKUP_TAG_END + { $$ = zend_ast_create_markup_element(NULL, NULL, $3); if (!$$) { YYERROR; } } + | T_MARKUP_OPEN T_VARIABLE markup_attributes T_MARKUP_TAG_END markup_children T_MARKUP_CLOSE_OPEN T_VARIABLE T_MARKUP_TAG_END + { $$ = zend_ast_create_markup_checked($2, $3, $5, $7, true); if (!$$) { YYERROR; } } + | T_MARKUP_OPEN T_VARIABLE markup_attributes T_MARKUP_SELF_CLOSE + { $$ = zend_ast_create_markup_dynamic($2, $3, NULL); } + | T_MARKUP_OPEN T_MARKUP_INTERP_START expr '}' markup_attributes T_MARKUP_TAG_END markup_children T_MARKUP_CLOSE_OPEN T_MARKUP_TAG_END + { $$ = zend_ast_create_markup_dynamic_expr($3, $5, $7); } + | T_MARKUP_OPEN T_MARKUP_INTERP_START expr '}' markup_attributes T_MARKUP_SELF_CLOSE + { $$ = zend_ast_create_markup_dynamic_expr($3, $5, NULL); } +; + +markup_attributes: + %empty { $$ = zend_ast_create_list(0, ZEND_AST_ARRAY); } + | markup_attributes markup_attribute { $$ = zend_ast_list_add($1, $2); } +; + +markup_attribute: + T_MARKUP_NAME '=' T_MARKUP_ATTR_VALUE + { $$ = zend_ast_create(ZEND_AST_ARRAY_ELEM, $3, $1); } + | T_MARKUP_NAME '=' T_MARKUP_INTERP_START expr '}' + { $$ = zend_ast_create(ZEND_AST_ARRAY_ELEM, $4, $1); } + | T_MARKUP_NAME + { zval t; ZVAL_TRUE(&t); $$ = zend_ast_create(ZEND_AST_ARRAY_ELEM, zend_ast_create_zval(&t), $1); } + | T_MARKUP_INTERP_START T_ELLIPSIS expr '}' + { $$ = zend_ast_create(ZEND_AST_UNPACK, $3); } +; + +markup_children: + %empty { $$ = zend_ast_create_list(0, ZEND_AST_ARRAY); } + | markup_children markup_child { $$ = zend_ast_list_add($1, $2); } +; + +markup_child: + T_MARKUP_TEXT { $$ = zend_ast_create(ZEND_AST_ARRAY_ELEM, $1, NULL); } + | T_MARKUP_COMMENT { $$ = zend_ast_create(ZEND_AST_ARRAY_ELEM, zend_ast_create_markup_raw($1), NULL); } + | T_MARKUP_DOCTYPE { $$ = zend_ast_create(ZEND_AST_ARRAY_ELEM, zend_ast_create_markup_raw($1), NULL); } + | T_MARKUP_INTERP_START expr '}' { $$ = zend_ast_create(ZEND_AST_ARRAY_ELEM, $2, NULL); } + | T_MARKUP_INTERP_START '}' { zval n; ZVAL_NULL(&n); $$ = zend_ast_create(ZEND_AST_ARRAY_ELEM, zend_ast_create_zval(&n), NULL); } + | markup_element { $$ = zend_ast_create(ZEND_AST_ARRAY_ELEM, $1, NULL); } +; + expr: variable { $$ = $1; } + | markup_element + { $$ = $1; } | T_LIST '(' array_pair_list ')' '=' expr { $3->attr = ZEND_ARRAY_SYNTAX_LIST; $$ = zend_ast_create(ZEND_AST_ASSIGN, $3, $6); } | '[' array_pair_list ']' '=' expr diff --git a/Zend/zend_language_scanner.h b/Zend/zend_language_scanner.h index e502d91411b5..1415e2fa991d 100644 --- a/Zend/zend_language_scanner.h +++ b/Zend/zend_language_scanner.h @@ -30,6 +30,7 @@ typedef struct _zend_lex_state { unsigned char *yy_marker; unsigned char *yy_limit; int yy_state; + int last_token; /* operand-vs-operator position for native markup */ zend_stack state_stack; zend_ptr_stack heredoc_label_stack; zend_stack nest_location_stack; /* for syntax error reporting */ diff --git a/Zend/zend_language_scanner.l b/Zend/zend_language_scanner.l index 07f2d44cb5c6..0bbaaedf254a 100644 --- a/Zend/zend_language_scanner.l +++ b/Zend/zend_language_scanner.l @@ -46,6 +46,7 @@ #include "zend_strtod.h" #include "zend_exceptions.h" #include "zend_virtual_cwd.h" +#include "zend_markup.h" #define YYCTYPE unsigned char #define YYFILL(n) { if ((YYCURSOR + n) >= (YYLIMIT + ZEND_MMAP_AHEAD)) { return 0; } } @@ -194,6 +195,7 @@ void startup_scanner(void) zend_stack_init(&SCNG(nest_location_stack), sizeof(zend_nest_location)); zend_ptr_stack_init(&SCNG(heredoc_label_stack)); SCNG(heredoc_scan_ahead) = 0; + SCNG(last_token) = 0; } static void heredoc_label_dtor(zend_heredoc_label *heredoc_label) { @@ -233,6 +235,7 @@ ZEND_API void zend_save_lexical_state(zend_lex_state *lex_state) lex_state->in = SCNG(yy_in); lex_state->yy_state = YYSTATE; + lex_state->last_token = SCNG(last_token); lex_state->filename = CG(compiled_filename); lex_state->lineno = CG(zend_lineno); CG(compiled_filename) = NULL; @@ -273,6 +276,7 @@ ZEND_API void zend_restore_lexical_state(zend_lex_state *lex_state) SCNG(yy_in) = lex_state->in; YYSETCONDITION(lex_state->yy_state); + SCNG(last_token) = lex_state->last_token; CG(zend_lineno) = lex_state->lineno; zend_restore_compiled_filename(lex_state->filename); @@ -543,6 +547,7 @@ ZEND_API zend_result open_file_for_scanning(zend_file_handle *file_handle) /* Reset the scanner for scanning the new file */ SCNG(yy_in) = file_handle; SCNG(yy_start) = NULL; + SCNG(last_token) = 0; if (size != (size_t)-1) { if (CG(multibyte)) { @@ -744,6 +749,7 @@ ZEND_API void zend_prepare_string_for_scanning(zval *str, zend_string *filename) SCNG(yy_in) = NULL; SCNG(yy_start) = NULL; + SCNG(last_token) = 0; buf = Z_STRVAL_P(str); size = old_len; @@ -1357,6 +1363,119 @@ static zend_result check_nesting_at_end(void) } \ } while (0) +/* Native markup (RFC: Native Markup Expressions). A bare "<" in ST_IN_SCRIPTING + * begins a markup expression only in *operand* position (where a value, not an + * operator, is expected), decided from the last significant token. The decision + * is an allowlist that FAILS CLOSED: markup begins only after a token known to + * expect an operand - a position where an infix "<" cannot legally appear + * today - and after every other token "<" keeps its existing meaning. So a + * token missing from this list can never change what existing code means; the + * worst a gap can do is make markup unavailable in that one context (a loud + * feature-side parse error, fixed by parenthesizing - or by adding the token + * here). Contributors adding a new operator or expression-introducing keyword + * should add it to this list so markup can follow it directly. + * + * Deliberate exclusions, each because "<" after it is (or may be) valid PHP: + * - "}" ends match/interpolation values, so it must stay a comparison; the + * cost is that markup as an expression statement directly after a block - + * `if ($c) { ... }

    hi

    ;` - fails to parse. Assign or echo it instead. + * - bare `yield` is a complete operand (`yield < LIM`, `yield <> 5` are + * valid PHP), so yielding markup needs parentheses: `yield (
    )`. + * (`yield from` requires an operand, so markup after it just works.) + * - ")" would enable `if ($c)

    hi

    ;` but ends grouped operands + * (`($a+$b) < $c`); parenthesized markup `(

    )` already works. */ +static bool zend_markup_operand_position(void) +{ + switch (SCNG(last_token)) { + /* nothing scanned yet (an eval'd string can begin with an expression) */ + case 0: + /* statement starts and separators */ + case T_OPEN_TAG: + case T_OPEN_TAG_WITH_ECHO: + case ';': + case '{': + case ':': /* alternative syntax, case/default labels, ternary else */ + /* grouping and lists */ + case '(': + case '[': + case ',': + /* assignment */ + case '=': + case T_PLUS_EQUAL: + case T_MINUS_EQUAL: + case T_MUL_EQUAL: + case T_DIV_EQUAL: + case T_MOD_EQUAL: + case T_CONCAT_EQUAL: + case T_AND_EQUAL: + case T_OR_EQUAL: + case T_XOR_EQUAL: + case T_SL_EQUAL: + case T_SR_EQUAL: + case T_POW_EQUAL: + case T_COALESCE_EQUAL: + case T_DOUBLE_ARROW: + /* unary and binary operators */ + case '+': + case '-': + case '*': + case '/': + case '%': + case '.': + case '<': + case '>': + case '|': + case '^': + case '?': + case '!': + case '~': + case '@': + case T_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARG: + case T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG: + case T_SL: + case T_SR: + case T_POW: + case T_COALESCE: + case T_BOOLEAN_AND: + case T_BOOLEAN_OR: + case T_LOGICAL_AND: + case T_LOGICAL_OR: + case T_LOGICAL_XOR: + case T_IS_EQUAL: + case T_IS_NOT_EQUAL: + case T_IS_IDENTICAL: + case T_IS_NOT_IDENTICAL: + case T_IS_SMALLER_OR_EQUAL: + case T_IS_GREATER_OR_EQUAL: + case T_SPACESHIP: + case T_PIPE: + /* keywords that introduce an expression */ + case T_RETURN: + case T_ECHO: + case T_PRINT: + case T_THROW: + case T_CLONE: + case T_CASE: + case T_YIELD_FROM: + case T_ELLIPSIS: + /* casts */ + case T_INT_CAST: + case T_DOUBLE_CAST: + case T_STRING_CAST: + case T_ARRAY_CAST: + case T_OBJECT_CAST: + case T_BOOL_CAST: + case T_UNSET_CAST: + case T_VOID_CAST: + /* markup interpolation: `{` in markup content, child or attribute + * position, restarts expression context (nested markup must lex) */ + case T_MARKUP_INTERP_START: + return true; + default: + return false; + } +} + int ZEND_FASTCALL lex_scan(zval *zendlval, zend_parser_stack_elem *elem) { int token; @@ -1376,6 +1495,9 @@ HNUM "0x"[0-9a-fA-F]+(_[0-9a-fA-F]+)* BNUM "0b"[01]+(_[01]+)* ONUM "0o"[0-7]+(_[0-7]+)* LABEL [a-zA-Z_\x80-\xff][a-zA-Z0-9_\x80-\xff]* +/* Native markup: one segment of a tag or attribute name. Dots, + * colons and hyphens are allowed inside (, wire:poll.1s). */ +MARKUP_LABEL [a-zA-Z][a-zA-Z0-9_.:-]* WHITESPACE [ \n\r\t]+ TABS_AND_SPACES [ \t]* TOKENS [;:,.|^&+-/*=%!~$<>?@] @@ -1842,7 +1964,7 @@ OPTIONAL_WHITESPACE_OR_COMMENTS ({WHITESPACE}|{MULTI_LINE_COMMENT}|{SINGLE_LINE_ RETURN_TOKEN(T_IS_EQUAL); } -"!="|"<>" { +"!=" { RETURN_TOKEN(T_IS_NOT_EQUAL); } @@ -1977,6 +2099,199 @@ OPTIONAL_WHITESPACE_OR_COMMENTS ({WHITESPACE}|{MULTI_LINE_COMMENT}|{SINGLE_LINE_ } +/* Native markup (RFC: Native Markup Expressions). In operand position, "<" + * followed by a tag opener begins markup: "" (fragment), "<$var" / "<{expr}" (dynamic tags). In operator + * position everything keeps its existing meaning: "<" a comparison ("$a <$b", + * "$a < \Foo::BAR") and "<>" the legacy not-equal alias. The opener set here + * is mirrored by ST_MARKUP_CONTENT's nested-tag rule below. */ +"<"("\\"?[a-zA-Z]|[>${]) { + if (zend_markup_operand_position()) { + yyless(1); + yy_push_state(ST_MARKUP_TAG); + RETURN_TOKEN(T_MARKUP_OPEN); + } + if (yytext[1] == '>') { + RETURN_TOKEN(T_IS_NOT_EQUAL); + } + yyless(1); + RETURN_TOKEN('<'); +} + +{WHITESPACE} { + /* Count newlines and emit T_WHITESPACE in tokenizer mode, like ordinary + * whitespace (a bare restart would freeze line numbers and swallow the + * whitespace from token_get_all). */ + goto return_whitespace; +} + +"\\"?{MARKUP_LABEL}("\\"{MARKUP_LABEL})* { + /* A tag name, optionally namespace-qualified () or fully + * qualified (<\App\Card/>). A name containing "\" always dispatches as a + * component (see zend_markup_name_is_component in Zend/zend_markup.c). */ + RETURN_TOKEN_WITH_STR(T_MARKUP_NAME, 0); +} + +[:@]{MARKUP_LABEL} { + /* An attribute name with a ":" or "@" shorthand prefix (Vue/Alpine: + * :class, @click). Attribute position only: this can never lex as a tag + * name, because every markup-open rule requires a letter, "\", "$", "{" + * or ">" after "<", and ST_MARKUP_CLOSE is excluded here. */ + RETURN_TOKEN_WITH_STR(T_MARKUP_NAME, 0); +} + +"$"{LABEL} { + /* A dynamic tag name (<$tag>…): an ordinary variable whose runtime + * value decides element vs component (see Html\render_dynamic). */ + RETURN_TOKEN_WITH_STR(T_VARIABLE, 1); +} + +"=" { + RETURN_TOKEN('='); +} + +"\"" [^"]* "\"" { + /* Literal double-quoted attribute value (escaped at render time). The + * quotes stay part of the token text (yyleng untouched) so token_get_all + * round-trips; only the zval payload is the unquoted interior, with + * character references decoded for the compiler. */ + HANDLE_NEWLINES(yytext, yyleng); + zend_copy_value(zendlval, yytext + 1, yyleng - 2); + if (PARSER_MODE() && !SCNG(on_event) && Z_TYPE_P(zendlval) == IS_STRING) { + ZVAL_STR(zendlval, zend_markup_decode_entities(Z_STR_P(zendlval))); + } + RETURN_TOKEN_WITH_VAL(T_MARKUP_ATTR_VALUE); +} + +"{" { + yy_push_state(ST_IN_SCRIPTING); + enter_nesting('{'); + RETURN_TOKEN(T_MARKUP_INTERP_START); +} + +"/>" { + yy_pop_state(); + RETURN_TOKEN(T_MARKUP_SELF_CLOSE); +} + +">" { + BEGIN(ST_MARKUP_CONTENT); + RETURN_TOKEN(T_MARKUP_TAG_END); +} + +">" { + yy_pop_state(); + RETURN_TOKEN(T_MARKUP_TAG_END); +} + +{ANY_CHAR} { + if (YYCURSOR > YYLIMIT) { + RETURN_END_TOKEN; + } + RETURN_TOKEN(T_ERROR); +} + +""; + * content is literal (no interpolation). */ + while (YYCURSOR < YYLIMIT) { + if (YYCURSOR + 2 < YYLIMIT && YYCURSOR[0] == '-' && YYCURSOR[1] == '-' && YYCURSOR[2] == '>') { + YYCURSOR += 3; + yyleng = YYCURSOR - SCNG(yy_text); + HANDLE_NEWLINES(yytext, yyleng); + RETURN_TOKEN_WITH_STR(T_MARKUP_COMMENT, 0); + } + YYCURSOR++; + } + RETURN_TOKEN(T_ERROR); +} + +""; + * content is literal (no interpolation). */ + while (YYCURSOR < YYLIMIT) { + if (*YYCURSOR == '>') { + YYCURSOR++; + yyleng = YYCURSOR - SCNG(yy_text); + HANDLE_NEWLINES(yytext, yyleng); + RETURN_TOKEN_WITH_STR(T_MARKUP_DOCTYPE, 0); + } + YYCURSOR++; + } + RETURN_TOKEN(T_ERROR); +} + +"" shortcut (they end at "]]>" and may contain ">"), so + * reject with a targeted message rather than the generic stray-"<" one. */ + if (PARSER_MODE()) { + zend_throw_exception(zend_ce_parse_error, + "Unsupported markup declaration; only comments and are supported", 0); + } + RETURN_TOKEN(T_ERROR); +} + +""<"("\\"?[a-zA-Z]|[>${]) { + /* A nested tag ("") - the same opener set as the ST_IN_SCRIPTING rule, always + * markup here (content is operand position by construction). ""<" { + /* A stray "<" in text - give a clear error rather than a bare syntax + * error on T_ERROR. */ + if (PARSER_MODE()) { + zend_throw_exception(zend_ce_parse_error, + "Unescaped \"<\" in markup text; write {'<'} for a literal less-than sign", 0); + } + RETURN_TOKEN(T_ERROR); +} + +"{" { + yy_push_state(ST_IN_SCRIPTING); + enter_nesting('{'); + RETURN_TOKEN(T_MARKUP_INTERP_START); +} + +{ANY_CHAR} { + if (YYCURSOR > YYLIMIT) { + RETURN_END_TOKEN; + } + /* Accumulate a run of literal text up to the next "<" or "{". */ + while (YYCURSOR < YYLIMIT) { + if (*YYCURSOR == '<' || *YYCURSOR == '{') { + break; + } + YYCURSOR++; + } + yyleng = YYCURSOR - SCNG(yy_text); + HANDLE_NEWLINES(yytext, yyleng); + /* Normalize whitespace for the compiler only; when collecting tokens + * (token_get_all) emit the raw text so the source round-trips. */ + if (PARSER_MODE() && !SCNG(on_event)) { + zend_string *normalized = zend_markup_normalize_text(yytext, yyleng); + if (ZSTR_LEN(normalized) == 0) { + zend_string_release(normalized); + goto restart; + } + ZVAL_STR(zendlval, zend_markup_decode_entities(normalized)); + RETURN_TOKEN_WITH_VAL(T_MARKUP_TEXT); + } + RETURN_TOKEN_WITH_STR(T_MARKUP_TEXT, 0); +} + + "${" { yy_push_state(ST_LOOKING_FOR_VARNAME); enter_nesting('{'); @@ -3196,12 +3511,19 @@ emit_token_with_val: } emit_token: + /* Track the last significant token for operand-vs-operator detection of a + * bare "<" (native markup). Whitespace/comments are skipped in parser mode + * but emitted in tokenizer mode, so ignore them here for a consistent view. */ + if (token != T_WHITESPACE && token != T_COMMENT && token != T_DOC_COMMENT) { + SCNG(last_token) = token; + } if (SCNG(on_event)) { SCNG(on_event)(ON_TOKEN, token, start_line, yytext, yyleng, SCNG(on_event_context)); } return token; emit_token_with_ident: + SCNG(last_token) = token; if (PARSER_MODE()) { elem->ident = SCNG(yy_text); } diff --git a/Zend/zend_markup.c b/Zend/zend_markup.c new file mode 100644 index 000000000000..01f0014f6294 --- /dev/null +++ b/Zend/zend_markup.c @@ -0,0 +1,594 @@ +/* + +----------------------------------------------------------------------+ + | Zend Engine | + +----------------------------------------------------------------------+ + | Copyright © Zend Technologies Ltd., a subsidiary company of | + | Perforce Software, Inc., and Contributors. | + +----------------------------------------------------------------------+ + | This source file is subject to the Modified BSD License that is | + | bundled with this package in the file LICENSE, and is available | + | through the World Wide Web at . | + | | + | SPDX-License-Identifier: BSD-3-Clause | + +----------------------------------------------------------------------+ + | Author: Liam Hammett | + +----------------------------------------------------------------------+ +*/ + +/* Native Markup Expressions compile-time support: + * + * - text normalization and character-reference decoding, called by the + * lexer (Zend/zend_language_scanner.l) on markup text and literal + * attribute values; + * - AST lowering, called by the grammar actions + * (Zend/zend_language_parser.y) to turn markup elements into ordinary + * `new \Html\Element(...)` / `\Html\render_component(...)` ASTs, so no + * new AST node kinds or zend_compile.c support are needed. + * + * The lexing mechanics themselves (scanner states, operand-position + * detection) stay in the scanner; everything here is pure data-to-data. */ + +#include "zend.h" +#include "zend_ast.h" +#include "zend_compile.h" +#include "zend_exceptions.h" +#include "zend_smart_str.h" +#include "zend_markup.h" +#include "zend_markup_entities.h" + +/* Native markup whitespace normalization, following the JSX/Babel rules: + * - split the text node into lines; + * - trim leading whitespace on every line but the first, and trailing + * whitespace on every line but the last; + * - drop lines that are empty after trimming; + * - join the surviving lines with a single space. + * The net effect: indentation/newlines between block elements vanish, while a + * meaningful single space between inline content is preserved. Returns a new + * zend_string (possibly empty - the caller drops empty text nodes). + * + * One streaming pass: because a non-first line is left-trimmed and a non-last + * line is right-trimmed, any line that survives contains a non-whitespace + * character, so "join surviving lines with a single space" is simply a space + * before every surviving line except the first - no line table or lookahead + * for the last non-blank line is needed. A single-line text is untouched apart + * from the tab conversion, exactly as before. */ +zend_string *zend_markup_normalize_text(const char *text, size_t len) +{ + /* Output is never longer than the input. */ + zend_string *result = zend_string_alloc(len, 0); + char *out = ZSTR_VAL(result); + size_t outlen = 0; + size_t pos = 0; + bool pending_join = false; + + /* Line terminators are \r\n | \n | \r (matching JS String.split). A line + * is "last" when no terminator follows it, so text ending in a line break + * still right-trims its final content line, as the split-based version did + * via its synthetic trailing empty line. */ + for (;;) { + size_t e = pos; + while (e < len && text[e] != '\n' && text[e] != '\r') { + e++; + } + bool is_first = (pos == 0); + bool is_last = (e >= len); + size_t s = pos, en = e; + + if (!is_first) { + while (s < en && (text[s] == ' ' || text[s] == '\t')) s++; + } + if (!is_last) { + while (en > s && (text[en - 1] == ' ' || text[en - 1] == '\t')) en--; + } + if (en > s) { + if (pending_join) { + out[outlen++] = ' '; + } + for (size_t x = s; x < en; x++) { + out[outlen++] = (text[x] == '\t') ? ' ' : text[x]; + } + pending_join = true; + } + + if (is_last) { + break; + } + if (text[e] == '\r' && e + 1 < len && text[e + 1] == '\n') { + e++; + } + pos = e + 1; + } + + if (outlen == 0) { + zend_string_release(result); + return ZSTR_EMPTY_ALLOC(); + } + ZSTR_LEN(result) = outlen; + out[outlen] = '\0'; + return result; +} + +/* Native markup character references. Markup text and literal + * attribute values decode HTML character references at compile time: the + * frozen WHATWG named set (semicolon-terminated forms only; the standard + * guarantees the list will never grow) plus numeric { / 😀 forms. + * Decoding is lenient like HTML and JSX: anything that does not parse as a + * reference - a bare "&", an unknown name, an out-of-range or surrogate + * codepoint - stays literal text, so "fish & chips" needs no escaping. + * Decoded text is a plain string escaped at render time, so "&" in source + * round-trips to "&" in output. Text decodes after whitespace + * normalization, so " " survives it (the entity spelling of JSX's {' '}). */ + +static const zend_markup_entity *zend_markup_entity_lookup(const char *name, size_t len) +{ + uint32_t lo = 0, hi = ZEND_MARKUP_ENTITY_COUNT; + + while (lo < hi) { + uint32_t mid = lo + (hi - lo) / 2; + const zend_markup_entity *e = &zend_markup_entities[mid]; + int r = memcmp(name, e->name, MIN(len, e->name_len)); + if (r == 0) { + r = (len > e->name_len) - (len < e->name_len); + } + if (r == 0) { + return e; + } + if (r < 0) { + hi = mid; + } else { + lo = mid + 1; + } + } + return NULL; +} + +static size_t zend_markup_utf8_encode(char *out, uint32_t cp) +{ + if (cp < 0x80) { + out[0] = (char)cp; + return 1; + } + if (cp < 0x800) { + out[0] = (char)(0xC0 | (cp >> 6)); + out[1] = (char)(0x80 | (cp & 0x3F)); + return 2; + } + if (cp < 0x10000) { + out[0] = (char)(0xE0 | (cp >> 12)); + out[1] = (char)(0x80 | ((cp >> 6) & 0x3F)); + out[2] = (char)(0x80 | (cp & 0x3F)); + return 3; + } + out[0] = (char)(0xF0 | (cp >> 18)); + out[1] = (char)(0x80 | ((cp >> 12) & 0x3F)); + out[2] = (char)(0x80 | ((cp >> 6) & 0x3F)); + out[3] = (char)(0x80 | (cp & 0x3F)); + return 4; +} + +/* Takes ownership of str; returns it unchanged when it contains no "&". */ +zend_string *zend_markup_decode_entities(zend_string *str) +{ + const char *p = ZSTR_VAL(str); + const char *end = p + ZSTR_LEN(str); + const char *amp = memchr(p, '&', ZSTR_LEN(str)); + smart_str buf = {0}; + + if (!amp) { + return str; + } + + while (amp) { + const char *q = amp + 1; + const char *repl = NULL; + size_t repl_len = 0; + char utf8[4]; + + smart_str_appendl(&buf, p, amp - p); + + if (q < end && *q == '#') { + /* Numeric reference: { or 😀 */ + uint32_t cp = 0; + bool overflow = false, hex = false; + const char *digits; + + q++; + if (q < end && (*q == 'x' || *q == 'X')) { + hex = true; + q++; + } + digits = q; + while (q < end) { + uint32_t d; + if (*q >= '0' && *q <= '9') { + d = *q - '0'; + } else if (hex && *q >= 'a' && *q <= 'f') { + d = *q - 'a' + 10; + } else if (hex && *q >= 'A' && *q <= 'F') { + d = *q - 'A' + 10; + } else { + break; + } + if (cp > 0x10FFFF) { + overflow = true; + } else { + cp = cp * (hex ? 16 : 10) + d; + } + q++; + } + if (q > digits && q < end && *q == ';' + && !overflow && cp > 0 && cp <= 0x10FFFF + && !(cp >= 0xD800 && cp <= 0xDFFF)) { + repl_len = zend_markup_utf8_encode(utf8, cp); + repl = utf8; + q++; + } + } else { + /* Named reference: &name; (case-sensitive, ASCII alphanumeric) */ + const char *name = q; + while (q < end && (size_t)(q - name) <= ZEND_MARKUP_ENTITY_MAX_NAME + && ((*q >= 'a' && *q <= 'z') || (*q >= 'A' && *q <= 'Z') + || (*q >= '0' && *q <= '9'))) { + q++; + } + if (q > name && q < end && *q == ';') { + const zend_markup_entity *e = zend_markup_entity_lookup(name, q - name); + if (e) { + repl = e->utf8; + repl_len = e->utf8_len; + q++; + } + } + } + + if (repl) { + smart_str_appendl(&buf, repl, repl_len); + p = q; + } else { + smart_str_appendc(&buf, '&'); + p = amp + 1; + } + amp = (p < end) ? memchr(p, '&', end - p) : NULL; + } + smart_str_appendl(&buf, p, end - p); + + zend_string_release_ex(str, 0); + return smart_str_extract(&buf); +} + +/* Native markup AST lowering. + * Lower a markup element into an ordinary `new \Html\Element(tag, [], [children])` + * (or `new \Html\Fragment([children])` when the tag name is empty) so no new AST + * node kind or zend_compile.c support is needed. `name` is the tag-name zval AST + * or NULL for a fragment; `children` is a ZEND_AST_ARRAY of ZEND_AST_ARRAY_ELEM. + * + * All lowering targets are named through the ZEND_MARKUP_*_FQ macros (see + * zend_markup.h) so the markup-to-runtime vocabulary has a single home. */ + +/* Build a fully-qualified name AST for one of the ZEND_MARKUP_*_FQ symbols. */ +static zend_ast *zend_ast_markup_fq_name(const char *name, size_t len) +{ + zend_ast *ast = zend_ast_create_zval_from_str(zend_string_init(name, len, 0)); + ast->attr = ZEND_NAME_FQ; + return ast; +} + +/* Element vs component classification: a tag whose first character is + * uppercase, which is namespace-qualified (contains "\"), or which names a + * static method via "::", dispatches as a component; anything else is a literal + * HTML element. The single home for this rule: the compiler applies it to + * static tag names here, and Html\render_dynamic applies it to a dynamic tag's + * runtime value (hence ZEND_API). */ +ZEND_API bool zend_markup_name_is_component(zend_string *tag) +{ + return (ZSTR_LEN(tag) > 0 && ZSTR_VAL(tag)[0] >= 'A' && ZSTR_VAL(tag)[0] <= 'Z') + || memchr(ZSTR_VAL(tag), '\\', ZSTR_LEN(tag)) != NULL + || strstr(ZSTR_VAL(tag), "::") != NULL; +} + +/* Wrap a list of children in `new \Html\Fragment([children])`. Consumes (and, if + * NULL, creates) the children list. */ +static zend_ast *zend_ast_wrap_fragment(zend_ast *children) +{ + if (children == NULL) { + children = zend_ast_create_list(0, ZEND_AST_ARRAY); + } + children->attr = ZEND_ARRAY_SYNTAX_SHORT; + + zend_ast *frag_class = zend_ast_markup_fq_name(ZEND_STRL(ZEND_MARKUP_FRAGMENT_FQ)); + + return zend_ast_create(ZEND_AST_NEW, frag_class, + zend_ast_create_list(1, ZEND_AST_ARG_LIST, children)); +} + +/* Wrap a string AST in a `\Html\raw(...)` call (trusted passthrough). Used for + * `` comments, which are emitted as literal HTML. */ +zend_ast *zend_ast_create_markup_raw(zend_ast *str) +{ + zend_ast *fn = zend_ast_markup_fq_name(ZEND_STRL(ZEND_MARKUP_RAW_FQ)); + return zend_ast_create(ZEND_AST_CALL, fn, + zend_ast_create_list(1, ZEND_AST_ARG_LIST, str)); +} + +/* Wrap a children list in + * `new \Html\LazyFragment(fn() => new \Html\Fragment([children]))` (the + * `:lazy` directive). The arrow function defers building the child subtree - + * and running any side effects in its interpolations - until the component + * actually renders the slot, so a component that discards its body never + * evaluates it. Consumes `children`. */ +static zend_ast *zend_ast_wrap_lazy_fragment(zend_ast *children, uint32_t lineno) +{ + zend_ast *frag = zend_ast_wrap_fragment(children); + + /* fn() => new \Html\Fragment([...]) - an arrow function so its body + * auto-captures by value whatever it references, exactly as a hand-written + * one would; only the expression evaluation is deferred. */ + zend_ast *params = zend_ast_create_list(0, ZEND_AST_PARAM_LIST); + zend_ast *closure = zend_ast_create_decl(ZEND_AST_ARROW_FUNC, 0, lineno, + NULL, NULL, params, NULL, frag, NULL, NULL); + + zend_ast *lazy_class = zend_ast_markup_fq_name(ZEND_STRL(ZEND_MARKUP_LAZY_FRAGMENT_FQ)); + return zend_ast_create(ZEND_AST_NEW, lazy_class, + zend_ast_create_list(1, ZEND_AST_ARG_LIST, closure)); +} + +/* Detect and strip a bare `:lazy` directive from a component's attribute list. + * `:lazy` marks the component's body slot for lazy evaluation (see + * zend_ast_wrap_lazy_fragment); it is a compiler directive, not a prop, so it is + * removed from the list rather than passed on. Returns whether it was present. */ +static bool zend_markup_extract_lazy(zend_ast *attrs) +{ + if (attrs == NULL) { + return false; + } + zend_ast_list *list = zend_ast_get_list(attrs); + for (uint32_t i = 0; i < list->children; i++) { + zend_ast *elem = list->child[i]; + if (elem == NULL || elem->kind != ZEND_AST_ARRAY_ELEM || elem->child[1] == NULL) { + continue; + } + zval *key = zend_ast_get_zval(elem->child[1]); + if (Z_TYPE_P(key) == IS_STRING && zend_string_equals_literal(Z_STR_P(key), ":lazy")) { + zend_ast_destroy(elem); + for (uint32_t j = i; j + 1 < list->children; j++) { + list->child[j] = list->child[j + 1]; + } + list->children--; + return true; + } + } + return false; +} + +/* Lower a component tag (a capitalized or namespace-qualified name, or + * "Class::method") into a call to \Html\render_component(component, props, slot) + * Attributes become the props array and the body content becomes a + * single \Html\Fragment passed as the slot (or a lazy \Html\LazyFragment when + * the `:lazy` directive is present). + * + * Component resolution is two-stage, and every tag resolves through the *class* + * name rules only: a bare/qualified tag lowers to ZEND_AST_CLASS_NAME (the + * `Component::class` machinery), and a "Class::method" tag lowers the class + * part the same way with "::method" appended - both honor `use` imports and + * the current namespace, with a leading "\" fully qualified. The runtime then + * resolves the name to a class implementing Html\Htmlable (instantiated) or a + * public static method (called) - see PHP_FUNCTION(Html_render_component) in + * ext/html/html.c. + * + * Plain *function* components are deliberately out of v1: a bare tag gives no + * signal whether a class or a function is meant, and functions resolve through + * the separate `use function` import table, so supporting them would need a + * second compile-time resolution and a new AST kind to carry it. A static + * method has no such ambiguity - its class part resolves like any class. */ +static zend_ast *zend_ast_create_markup_component(zend_ast *name, zend_ast *attrs, zend_ast *children) +{ + uint32_t lineno = zend_ast_get_lineno(name); + zend_string *tag = zend_ast_get_str(name); + const char *sep = strstr(ZSTR_VAL(tag), "::"); + /* A leading "\" is a fully-qualified reference; otherwise resolve the name + * (unqualified or qualified) against the current use/namespace. */ + uint32_t name_type = (ZSTR_VAL(tag)[0] == '\\') ? ZEND_NAME_FQ : ZEND_NAME_NOT_FQ; + zend_ast *component_name; + + if (sep != NULL) { + /* "Class::method": resolve the class part as a class, then append + * "::method". */ + size_t class_len = sep - ZSTR_VAL(tag); + zend_ast *class_name = zend_ast_create_zval_from_str( + zend_string_init(ZSTR_VAL(tag), class_len, 0)); + class_name->attr = name_type; + + zval suffix; + ZVAL_STR(&suffix, zend_string_init(sep, ZSTR_LEN(tag) - class_len, 0)); + component_name = zend_ast_create_concat_op( + zend_ast_create(ZEND_AST_CLASS_NAME, class_name), + zend_ast_create_zval(&suffix)); + } else { + zend_ast *class_name = zend_ast_create_zval_from_str(zend_string_copy(tag)); + class_name->attr = name_type; + component_name = zend_ast_create(ZEND_AST_CLASS_NAME, class_name); + } + zend_ast_destroy(name); + + if (attrs == NULL) { + attrs = zend_ast_create_list(0, ZEND_AST_ARRAY); + } + attrs->attr = ZEND_ARRAY_SYNTAX_SHORT; + + /* The `:lazy` directive is a compiler marker, not a prop - strip it first. */ + bool lazy = zend_markup_extract_lazy(attrs); + + /* The body becomes a single slot Fragment, or null when there is no body. + * With `:lazy` the fragment is wrapped in a deferred Html\LazyFragment. */ + zend_ast *slot; + if (children != NULL && zend_ast_get_list(children)->children > 0) { + children->attr = ZEND_ARRAY_SYNTAX_SHORT; + slot = lazy + ? zend_ast_wrap_lazy_fragment(children, lineno) + : zend_ast_wrap_fragment(children); + } else { + if (children != NULL) { + zend_ast_destroy(children); + } + zval null_zv; + ZVAL_NULL(&null_zv); + slot = zend_ast_create_zval(&null_zv); + } + + zend_ast *fn = zend_ast_markup_fq_name(ZEND_STRL(ZEND_MARKUP_COMPONENT_FQ)); + + zend_ast *args = zend_ast_create_list(0, ZEND_AST_ARG_LIST); + args = zend_ast_list_add(args, component_name); + args = zend_ast_list_add(args, attrs); + args = zend_ast_list_add(args, slot); + + return zend_ast_create(ZEND_AST_CALL, fn, args); +} + +zend_ast *zend_ast_create_markup_element(zend_ast *name, zend_ast *attrs, zend_ast *children) +{ + zend_ast *class_name, *args, *result; + + /* NULL children (a self-closed tag) means an empty list. A fragment always + * has a children list (the grammar builds one as content begins). */ + if (children == NULL) { + children = zend_ast_create_list(0, ZEND_AST_ARRAY); + } + + /* The grammar reduces a markup element only once its closing tag has been + * scanned, so nodes created here would inherit the *close* line. Stamp the + * lowered expression with the line the element opens on instead, so errors + * in a multi-line element report like any multi-line call. The tag-name + * token carries that line; a fragment has none, but its children list is + * created as content begins. */ + uint32_t lineno = (name != NULL) + ? zend_ast_get_lineno(name) : zend_ast_get_lineno(children); + + /* A capitalized name, a namespace-qualified name, or one containing "::" + * is a component. */ + if (name != NULL && zend_markup_name_is_component(zend_ast_get_str(name))) { + result = zend_ast_create_markup_component(name, attrs, children); + result->lineno = lineno; + return result; + } + + children->attr = ZEND_ARRAY_SYNTAX_SHORT; + + args = zend_ast_create_list(0, ZEND_AST_ARG_LIST); + + if (name == NULL) { + /* A fragment has no attributes (the grammar forbids them on `<>`). */ + if (attrs != NULL) { + zend_ast_destroy(attrs); + } + class_name = zend_ast_markup_fq_name(ZEND_STRL(ZEND_MARKUP_FRAGMENT_FQ)); + args = zend_ast_list_add(args, children); + } else { + if (attrs == NULL) { + attrs = zend_ast_create_list(0, ZEND_AST_ARRAY); + } + attrs->attr = ZEND_ARRAY_SYNTAX_SHORT; + class_name = zend_ast_markup_fq_name(ZEND_STRL(ZEND_MARKUP_ELEMENT_FQ)); + args = zend_ast_list_add(args, name); + args = zend_ast_list_add(args, attrs); + args = zend_ast_list_add(args, children); + } + + result = zend_ast_create(ZEND_AST_NEW, class_name, args); + result->lineno = lineno; + return result; +} + +/* Build a markup element (or, with `dynamic`, a dynamic tag) after checking + * the closing tag matches the opener: `

    …` and + * `<$a>…` are compile errors alike. Both `open` and `close` NULL means a + * `<>…` fragment. `close` is consumed. */ +zend_ast *zend_ast_create_markup_checked(zend_ast *open, zend_ast *attrs, zend_ast *children, zend_ast *close, bool dynamic) +{ + bool match; + if (open == NULL || close == NULL) { + match = (open == NULL && close == NULL); + } else { + match = zend_string_equals(Z_STR_P(zend_ast_get_zval(open)), Z_STR_P(zend_ast_get_zval(close))); + } + if (!match) { + const char *sigil = dynamic ? "$" : ""; + zend_throw_exception_ex(zend_ce_compile_error, 0, + "Mismatched markup closing tag: expected , found ", + sigil, open ? Z_STRVAL_P(zend_ast_get_zval(open)) : "", + sigil, close ? Z_STRVAL_P(zend_ast_get_zval(close)) : ""); + if (open != NULL) { + zend_ast_destroy(open); + } + if (attrs != NULL) { + zend_ast_destroy(attrs); + } + if (children != NULL) { + zend_ast_destroy(children); + } + if (close != NULL) { + zend_ast_destroy(close); + } + return NULL; + } + if (close != NULL) { + zend_ast_destroy(close); + } + + return dynamic + ? zend_ast_create_markup_dynamic(open, attrs, children) + : zend_ast_create_markup_element(open, attrs, children); +} + +/* Build the \Html\render_dynamic(tag_expr, attributes, children) call both + * dynamic-tag forms lower into. `tag_expr` is an already-built expression AST + * producing the tag name; `lineno` is the opening-tag line (see + * zend_ast_create_markup_element for why it is stamped explicitly). */ +static zend_ast *zend_markup_dynamic_call(zend_ast *tag_expr, zend_ast *attrs, zend_ast *children, uint32_t lineno) +{ + if (attrs == NULL) { + attrs = zend_ast_create_list(0, ZEND_AST_ARRAY); + } + attrs->attr = ZEND_ARRAY_SYNTAX_SHORT; + + if (children == NULL) { + children = zend_ast_create_list(0, ZEND_AST_ARRAY); + } + children->attr = ZEND_ARRAY_SYNTAX_SHORT; + + zend_ast *fn = zend_ast_markup_fq_name(ZEND_STRL(ZEND_MARKUP_DYNAMIC_FQ)); + + zend_ast *args = zend_ast_create_list(0, ZEND_AST_ARG_LIST); + args = zend_ast_list_add(args, tag_expr); + args = zend_ast_list_add(args, attrs); + args = zend_ast_list_add(args, children); + + zend_ast *result = zend_ast_create(ZEND_AST_CALL, fn, args); + result->lineno = lineno; + return result; +} + +/* Lower a dynamic tag `<$tag …>…` into a call to + * \Html\render_dynamic($tag, attributes, children). The variable's runtime + * value decides what a static tag name decides at compile time, by the same + * classification rule (zend_markup_name_is_component); only classification + * moves to runtime. `name` is the T_VARIABLE zval AST (without the "$"). */ +zend_ast *zend_ast_create_markup_dynamic(zend_ast *name, zend_ast *attrs, zend_ast *children) +{ + uint32_t lineno = zend_ast_get_lineno(name); + + zend_ast *var = zend_ast_create(ZEND_AST_VAR, name); + var->lineno = lineno; + + return zend_markup_dynamic_call(var, attrs, children, lineno); +} + +/* The brace form `<{expr} …>…` / `<{expr} …/>`: the tag is an arbitrary + * expression, evaluated once. It closes anonymously (``, like a fragment) + * because a closing tag cannot restate the expression - re-evaluating it + * would run its side effects twice, and matching it textually is meaningless + * for computed values. */ +zend_ast *zend_ast_create_markup_dynamic_expr(zend_ast *expr, zend_ast *attrs, zend_ast *children) +{ + return zend_markup_dynamic_call(expr, attrs, children, zend_ast_get_lineno(expr)); +} diff --git a/Zend/zend_markup.h b/Zend/zend_markup.h new file mode 100644 index 000000000000..35644855295c --- /dev/null +++ b/Zend/zend_markup.h @@ -0,0 +1,67 @@ +/* + +----------------------------------------------------------------------+ + | Zend Engine | + +----------------------------------------------------------------------+ + | Copyright © Zend Technologies Ltd., a subsidiary company of | + | Perforce Software, Inc., and Contributors. | + +----------------------------------------------------------------------+ + | This source file is subject to the Modified BSD License that is | + | bundled with this package in the file LICENSE, and is available | + | through the World Wide Web at . | + | | + | SPDX-License-Identifier: BSD-3-Clause | + +----------------------------------------------------------------------+ + | Author: Liam Hammett | + +----------------------------------------------------------------------+ +*/ + +#ifndef ZEND_MARKUP_H +#define ZEND_MARKUP_H + +#include "zend_ast.h" + +/* The fully-qualified names that Native Markup Expressions + * lowers into. This is the single home for the markup-to-runtime vocabulary: + * the parser (Zend/zend_language_parser.y) lowers markup expressions to exactly + * these symbols, and the ext/html runtime registers exactly them (asserted at + * MINIT, see PHP_MINIT_FUNCTION(html)). + * + * Renaming the markup namespace (e.g. the open `Html\` vs `PHP\Html\` question) + * is a one-line edit here rather than a hunt across the parser, stub, and + * runtime. Keep in sync with ext/html/html.stub.php, which declares the classes + * and functions these names refer to. */ + +#define ZEND_MARKUP_ELEMENT_FQ "Html\\Element" +#define ZEND_MARKUP_FRAGMENT_FQ "Html\\Fragment" +#define ZEND_MARKUP_LAZY_FRAGMENT_FQ "Html\\LazyFragment" +#define ZEND_MARKUP_RAW_FQ "Html\\raw" +#define ZEND_MARKUP_COMPONENT_FQ "Html\\render_component" +#define ZEND_MARKUP_DYNAMIC_FQ "Html\\render_dynamic" + +BEGIN_EXTERN_C() + +/* Element vs component classification: uppercase first character, + * a "\", or a "::" dispatches as a component; anything else is a literal HTML + * element. The single home for this rule - the compiler applies it to static + * tag names, and Html\render_dynamic applies it to a dynamic tag's runtime + * value, so `<$tag>` decides at runtime exactly what `` decides at + * compile time. */ +ZEND_API bool zend_markup_name_is_component(zend_string *tag); + +/* Compile-time text handling, called by the lexer (zend_language_scanner.l) on + * markup text nodes and literal attribute values. Both live in zend_markup.c. */ +zend_string *zend_markup_normalize_text(const char *text, size_t len); +zend_string *zend_markup_decode_entities(zend_string *str); + +/* AST lowering, called by the markup grammar actions (zend_language_parser.y). + * Each returns NULL with a CompileError exception pending on invalid markup + * (e.g. a mismatched closing tag); the grammar action then YYERRORs. */ +zend_ast *zend_ast_create_markup_raw(zend_ast *str); +zend_ast *zend_ast_create_markup_element(zend_ast *name, zend_ast *attrs, zend_ast *children); +zend_ast *zend_ast_create_markup_checked(zend_ast *open, zend_ast *attrs, zend_ast *children, zend_ast *close, bool dynamic); +zend_ast *zend_ast_create_markup_dynamic(zend_ast *name, zend_ast *attrs, zend_ast *children); +zend_ast *zend_ast_create_markup_dynamic_expr(zend_ast *expr, zend_ast *attrs, zend_ast *children); + +END_EXTERN_C() + +#endif /* ZEND_MARKUP_H */ diff --git a/Zend/zend_markup_entities.h b/Zend/zend_markup_entities.h new file mode 100644 index 000000000000..8611ee080df5 --- /dev/null +++ b/Zend/zend_markup_entities.h @@ -0,0 +1,2149 @@ +/* This is a generated file - edit Zend/zend_markup_entities_gen.php instead. + * Source data: ext/standard/html_tables/ents_html5.txt (the WHATWG HTML5 + * named-reference table, frozen by the HTML standard; also feeds + * html_entity_decode()). Canonical semicolon-terminated forms only; sorted + * by name in byte order for bsearch. */ + +#ifndef ZEND_MARKUP_ENTITIES_H +#define ZEND_MARKUP_ENTITIES_H + +typedef struct _zend_markup_entity { + const char *name; /* without the leading '&' and trailing ';' */ + const char *utf8; /* replacement text, UTF-8 */ + uint8_t name_len; + uint8_t utf8_len; +} zend_markup_entity; + +#define ZEND_MARKUP_ENTITY_COUNT 2125 +#define ZEND_MARKUP_ENTITY_MAX_NAME 31 +#define ZEND_MARKUP_ENTITY_MAX_UTF8 6 + +static const zend_markup_entity zend_markup_entities[ZEND_MARKUP_ENTITY_COUNT] = { + {"AElig", "\xC3\x86", 5, 2}, + {"AMP", "\x26", 3, 1}, + {"Aacute", "\xC3\x81", 6, 2}, + {"Abreve", "\xC4\x82", 6, 2}, + {"Acirc", "\xC3\x82", 5, 2}, + {"Acy", "\xD0\x90", 3, 2}, + {"Afr", "\xF0\x9D\x94\x84", 3, 4}, + {"Agrave", "\xC3\x80", 6, 2}, + {"Alpha", "\xCE\x91", 5, 2}, + {"Amacr", "\xC4\x80", 5, 2}, + {"And", "\xE2\xA9\x93", 3, 3}, + {"Aogon", "\xC4\x84", 5, 2}, + {"Aopf", "\xF0\x9D\x94\xB8", 4, 4}, + {"ApplyFunction", "\xE2\x81\xA1", 13, 3}, + {"Aring", "\xC3\x85", 5, 2}, + {"Ascr", "\xF0\x9D\x92\x9C", 4, 4}, + {"Assign", "\xE2\x89\x94", 6, 3}, + {"Atilde", "\xC3\x83", 6, 2}, + {"Auml", "\xC3\x84", 4, 2}, + {"Backslash", "\xE2\x88\x96", 9, 3}, + {"Barv", "\xE2\xAB\xA7", 4, 3}, + {"Barwed", "\xE2\x8C\x86", 6, 3}, + {"Bcy", "\xD0\x91", 3, 2}, + {"Because", "\xE2\x88\xB5", 7, 3}, + {"Bernoullis", "\xE2\x84\xAC", 10, 3}, + {"Beta", "\xCE\x92", 4, 2}, + {"Bfr", "\xF0\x9D\x94\x85", 3, 4}, + {"Bopf", "\xF0\x9D\x94\xB9", 4, 4}, + {"Breve", "\xCB\x98", 5, 2}, + {"Bscr", "\xE2\x84\xAC", 4, 3}, + {"Bumpeq", "\xE2\x89\x8E", 6, 3}, + {"CHcy", "\xD0\xA7", 4, 2}, + {"COPY", "\xC2\xA9", 4, 2}, + {"Cacute", "\xC4\x86", 6, 2}, + {"Cap", "\xE2\x8B\x92", 3, 3}, + {"CapitalDifferentialD", "\xE2\x85\x85", 20, 3}, + {"Cayleys", "\xE2\x84\xAD", 7, 3}, + {"Ccaron", "\xC4\x8C", 6, 2}, + {"Ccedil", "\xC3\x87", 6, 2}, + {"Ccirc", "\xC4\x88", 5, 2}, + {"Cconint", "\xE2\x88\xB0", 7, 3}, + {"Cdot", "\xC4\x8A", 4, 2}, + {"Cedilla", "\xC2\xB8", 7, 2}, + {"CenterDot", "\xC2\xB7", 9, 2}, + {"Cfr", "\xE2\x84\xAD", 3, 3}, + {"Chi", "\xCE\xA7", 3, 2}, + {"CircleDot", "\xE2\x8A\x99", 9, 3}, + {"CircleMinus", "\xE2\x8A\x96", 11, 3}, + {"CirclePlus", "\xE2\x8A\x95", 10, 3}, + {"CircleTimes", "\xE2\x8A\x97", 11, 3}, + {"ClockwiseContourIntegral", "\xE2\x88\xB2", 24, 3}, + {"CloseCurlyDoubleQuote", "\xE2\x80\x9D", 21, 3}, + {"CloseCurlyQuote", "\xE2\x80\x99", 15, 3}, + {"Colon", "\xE2\x88\xB7", 5, 3}, + {"Colone", "\xE2\xA9\xB4", 6, 3}, + {"Congruent", "\xE2\x89\xA1", 9, 3}, + {"Conint", "\xE2\x88\xAF", 6, 3}, + {"ContourIntegral", "\xE2\x88\xAE", 15, 3}, + {"Copf", "\xE2\x84\x82", 4, 3}, + {"Coproduct", "\xE2\x88\x90", 9, 3}, + {"CounterClockwiseContourIntegral", "\xE2\x88\xB3", 31, 3}, + {"Cross", "\xE2\xA8\xAF", 5, 3}, + {"Cscr", "\xF0\x9D\x92\x9E", 4, 4}, + {"Cup", "\xE2\x8B\x93", 3, 3}, + {"CupCap", "\xE2\x89\x8D", 6, 3}, + {"DD", "\xE2\x85\x85", 2, 3}, + {"DDotrahd", "\xE2\xA4\x91", 8, 3}, + {"DJcy", "\xD0\x82", 4, 2}, + {"DScy", "\xD0\x85", 4, 2}, + {"DZcy", "\xD0\x8F", 4, 2}, + {"Dagger", "\xE2\x80\xA1", 6, 3}, + {"Darr", "\xE2\x86\xA1", 4, 3}, + {"Dashv", "\xE2\xAB\xA4", 5, 3}, + {"Dcaron", "\xC4\x8E", 6, 2}, + {"Dcy", "\xD0\x94", 3, 2}, + {"Del", "\xE2\x88\x87", 3, 3}, + {"Delta", "\xCE\x94", 5, 2}, + {"Dfr", "\xF0\x9D\x94\x87", 3, 4}, + {"DiacriticalAcute", "\xC2\xB4", 16, 2}, + {"DiacriticalDot", "\xCB\x99", 14, 2}, + {"DiacriticalDoubleAcute", "\xCB\x9D", 22, 2}, + {"DiacriticalGrave", "\x60", 16, 1}, + {"DiacriticalTilde", "\xCB\x9C", 16, 2}, + {"Diamond", "\xE2\x8B\x84", 7, 3}, + {"DifferentialD", "\xE2\x85\x86", 13, 3}, + {"Dopf", "\xF0\x9D\x94\xBB", 4, 4}, + {"Dot", "\xC2\xA8", 3, 2}, + {"DotDot", "\xE2\x83\x9C", 6, 3}, + {"DotEqual", "\xE2\x89\x90", 8, 3}, + {"DoubleContourIntegral", "\xE2\x88\xAF", 21, 3}, + {"DoubleDot", "\xC2\xA8", 9, 2}, + {"DoubleDownArrow", "\xE2\x87\x93", 15, 3}, + {"DoubleLeftArrow", "\xE2\x87\x90", 15, 3}, + {"DoubleLeftRightArrow", "\xE2\x87\x94", 20, 3}, + {"DoubleLeftTee", "\xE2\xAB\xA4", 13, 3}, + {"DoubleLongLeftArrow", "\xE2\x9F\xB8", 19, 3}, + {"DoubleLongLeftRightArrow", "\xE2\x9F\xBA", 24, 3}, + {"DoubleLongRightArrow", "\xE2\x9F\xB9", 20, 3}, + {"DoubleRightArrow", "\xE2\x87\x92", 16, 3}, + {"DoubleRightTee", "\xE2\x8A\xA8", 14, 3}, + {"DoubleUpArrow", "\xE2\x87\x91", 13, 3}, + {"DoubleUpDownArrow", "\xE2\x87\x95", 17, 3}, + {"DoubleVerticalBar", "\xE2\x88\xA5", 17, 3}, + {"DownArrow", "\xE2\x86\x93", 9, 3}, + {"DownArrowBar", "\xE2\xA4\x93", 12, 3}, + {"DownArrowUpArrow", "\xE2\x87\xB5", 16, 3}, + {"DownBreve", "\xCC\x91", 9, 2}, + {"DownLeftRightVector", "\xE2\xA5\x90", 19, 3}, + {"DownLeftTeeVector", "\xE2\xA5\x9E", 17, 3}, + {"DownLeftVector", "\xE2\x86\xBD", 14, 3}, + {"DownLeftVectorBar", "\xE2\xA5\x96", 17, 3}, + {"DownRightTeeVector", "\xE2\xA5\x9F", 18, 3}, + {"DownRightVector", "\xE2\x87\x81", 15, 3}, + {"DownRightVectorBar", "\xE2\xA5\x97", 18, 3}, + {"DownTee", "\xE2\x8A\xA4", 7, 3}, + {"DownTeeArrow", "\xE2\x86\xA7", 12, 3}, + {"Downarrow", "\xE2\x87\x93", 9, 3}, + {"Dscr", "\xF0\x9D\x92\x9F", 4, 4}, + {"Dstrok", "\xC4\x90", 6, 2}, + {"ENG", "\xC5\x8A", 3, 2}, + {"ETH", "\xC3\x90", 3, 2}, + {"Eacute", "\xC3\x89", 6, 2}, + {"Ecaron", "\xC4\x9A", 6, 2}, + {"Ecirc", "\xC3\x8A", 5, 2}, + {"Ecy", "\xD0\xAD", 3, 2}, + {"Edot", "\xC4\x96", 4, 2}, + {"Efr", "\xF0\x9D\x94\x88", 3, 4}, + {"Egrave", "\xC3\x88", 6, 2}, + {"Element", "\xE2\x88\x88", 7, 3}, + {"Emacr", "\xC4\x92", 5, 2}, + {"EmptySmallSquare", "\xE2\x97\xBB", 16, 3}, + {"EmptyVerySmallSquare", "\xE2\x96\xAB", 20, 3}, + {"Eogon", "\xC4\x98", 5, 2}, + {"Eopf", "\xF0\x9D\x94\xBC", 4, 4}, + {"Epsilon", "\xCE\x95", 7, 2}, + {"Equal", "\xE2\xA9\xB5", 5, 3}, + {"EqualTilde", "\xE2\x89\x82", 10, 3}, + {"Equilibrium", "\xE2\x87\x8C", 11, 3}, + {"Escr", "\xE2\x84\xB0", 4, 3}, + {"Esim", "\xE2\xA9\xB3", 4, 3}, + {"Eta", "\xCE\x97", 3, 2}, + {"Euml", "\xC3\x8B", 4, 2}, + {"Exists", "\xE2\x88\x83", 6, 3}, + {"ExponentialE", "\xE2\x85\x87", 12, 3}, + {"Fcy", "\xD0\xA4", 3, 2}, + {"Ffr", "\xF0\x9D\x94\x89", 3, 4}, + {"FilledSmallSquare", "\xE2\x97\xBC", 17, 3}, + {"FilledVerySmallSquare", "\xE2\x96\xAA", 21, 3}, + {"Fopf", "\xF0\x9D\x94\xBD", 4, 4}, + {"ForAll", "\xE2\x88\x80", 6, 3}, + {"Fouriertrf", "\xE2\x84\xB1", 10, 3}, + {"Fscr", "\xE2\x84\xB1", 4, 3}, + {"GJcy", "\xD0\x83", 4, 2}, + {"GT", "\x3E", 2, 1}, + {"Gamma", "\xCE\x93", 5, 2}, + {"Gammad", "\xCF\x9C", 6, 2}, + {"Gbreve", "\xC4\x9E", 6, 2}, + {"Gcedil", "\xC4\xA2", 6, 2}, + {"Gcirc", "\xC4\x9C", 5, 2}, + {"Gcy", "\xD0\x93", 3, 2}, + {"Gdot", "\xC4\xA0", 4, 2}, + {"Gfr", "\xF0\x9D\x94\x8A", 3, 4}, + {"Gg", "\xE2\x8B\x99", 2, 3}, + {"Gopf", "\xF0\x9D\x94\xBE", 4, 4}, + {"GreaterEqual", "\xE2\x89\xA5", 12, 3}, + {"GreaterEqualLess", "\xE2\x8B\x9B", 16, 3}, + {"GreaterFullEqual", "\xE2\x89\xA7", 16, 3}, + {"GreaterGreater", "\xE2\xAA\xA2", 14, 3}, + {"GreaterLess", "\xE2\x89\xB7", 11, 3}, + {"GreaterSlantEqual", "\xE2\xA9\xBE", 17, 3}, + {"GreaterTilde", "\xE2\x89\xB3", 12, 3}, + {"Gscr", "\xF0\x9D\x92\xA2", 4, 4}, + {"Gt", "\xE2\x89\xAB", 2, 3}, + {"HARDcy", "\xD0\xAA", 6, 2}, + {"Hacek", "\xCB\x87", 5, 2}, + {"Hat", "\x5E", 3, 1}, + {"Hcirc", "\xC4\xA4", 5, 2}, + {"Hfr", "\xE2\x84\x8C", 3, 3}, + {"HilbertSpace", "\xE2\x84\x8B", 12, 3}, + {"Hopf", "\xE2\x84\x8D", 4, 3}, + {"HorizontalLine", "\xE2\x94\x80", 14, 3}, + {"Hscr", "\xE2\x84\x8B", 4, 3}, + {"Hstrok", "\xC4\xA6", 6, 2}, + {"HumpDownHump", "\xE2\x89\x8E", 12, 3}, + {"HumpEqual", "\xE2\x89\x8F", 9, 3}, + {"IEcy", "\xD0\x95", 4, 2}, + {"IJlig", "\xC4\xB2", 5, 2}, + {"IOcy", "\xD0\x81", 4, 2}, + {"Iacute", "\xC3\x8D", 6, 2}, + {"Icirc", "\xC3\x8E", 5, 2}, + {"Icy", "\xD0\x98", 3, 2}, + {"Idot", "\xC4\xB0", 4, 2}, + {"Ifr", "\xE2\x84\x91", 3, 3}, + {"Igrave", "\xC3\x8C", 6, 2}, + {"Im", "\xE2\x84\x91", 2, 3}, + {"Imacr", "\xC4\xAA", 5, 2}, + {"ImaginaryI", "\xE2\x85\x88", 10, 3}, + {"Implies", "\xE2\x87\x92", 7, 3}, + {"Int", "\xE2\x88\xAC", 3, 3}, + {"Integral", "\xE2\x88\xAB", 8, 3}, + {"Intersection", "\xE2\x8B\x82", 12, 3}, + {"InvisibleComma", "\xE2\x81\xA3", 14, 3}, + {"InvisibleTimes", "\xE2\x81\xA2", 14, 3}, + {"Iogon", "\xC4\xAE", 5, 2}, + {"Iopf", "\xF0\x9D\x95\x80", 4, 4}, + {"Iota", "\xCE\x99", 4, 2}, + {"Iscr", "\xE2\x84\x90", 4, 3}, + {"Itilde", "\xC4\xA8", 6, 2}, + {"Iukcy", "\xD0\x86", 5, 2}, + {"Iuml", "\xC3\x8F", 4, 2}, + {"Jcirc", "\xC4\xB4", 5, 2}, + {"Jcy", "\xD0\x99", 3, 2}, + {"Jfr", "\xF0\x9D\x94\x8D", 3, 4}, + {"Jopf", "\xF0\x9D\x95\x81", 4, 4}, + {"Jscr", "\xF0\x9D\x92\xA5", 4, 4}, + {"Jsercy", "\xD0\x88", 6, 2}, + {"Jukcy", "\xD0\x84", 5, 2}, + {"KHcy", "\xD0\xA5", 4, 2}, + {"KJcy", "\xD0\x8C", 4, 2}, + {"Kappa", "\xCE\x9A", 5, 2}, + {"Kcedil", "\xC4\xB6", 6, 2}, + {"Kcy", "\xD0\x9A", 3, 2}, + {"Kfr", "\xF0\x9D\x94\x8E", 3, 4}, + {"Kopf", "\xF0\x9D\x95\x82", 4, 4}, + {"Kscr", "\xF0\x9D\x92\xA6", 4, 4}, + {"LJcy", "\xD0\x89", 4, 2}, + {"LT", "\x3C", 2, 1}, + {"Lacute", "\xC4\xB9", 6, 2}, + {"Lambda", "\xCE\x9B", 6, 2}, + {"Lang", "\xE2\x9F\xAA", 4, 3}, + {"Laplacetrf", "\xE2\x84\x92", 10, 3}, + {"Larr", "\xE2\x86\x9E", 4, 3}, + {"Lcaron", "\xC4\xBD", 6, 2}, + {"Lcedil", "\xC4\xBB", 6, 2}, + {"Lcy", "\xD0\x9B", 3, 2}, + {"LeftAngleBracket", "\xE2\x9F\xA8", 16, 3}, + {"LeftArrow", "\xE2\x86\x90", 9, 3}, + {"LeftArrowBar", "\xE2\x87\xA4", 12, 3}, + {"LeftArrowRightArrow", "\xE2\x87\x86", 19, 3}, + {"LeftCeiling", "\xE2\x8C\x88", 11, 3}, + {"LeftDoubleBracket", "\xE2\x9F\xA6", 17, 3}, + {"LeftDownTeeVector", "\xE2\xA5\xA1", 17, 3}, + {"LeftDownVector", "\xE2\x87\x83", 14, 3}, + {"LeftDownVectorBar", "\xE2\xA5\x99", 17, 3}, + {"LeftFloor", "\xE2\x8C\x8A", 9, 3}, + {"LeftRightArrow", "\xE2\x86\x94", 14, 3}, + {"LeftRightVector", "\xE2\xA5\x8E", 15, 3}, + {"LeftTee", "\xE2\x8A\xA3", 7, 3}, + {"LeftTeeArrow", "\xE2\x86\xA4", 12, 3}, + {"LeftTeeVector", "\xE2\xA5\x9A", 13, 3}, + {"LeftTriangle", "\xE2\x8A\xB2", 12, 3}, + {"LeftTriangleBar", "\xE2\xA7\x8F", 15, 3}, + {"LeftTriangleEqual", "\xE2\x8A\xB4", 17, 3}, + {"LeftUpDownVector", "\xE2\xA5\x91", 16, 3}, + {"LeftUpTeeVector", "\xE2\xA5\xA0", 15, 3}, + {"LeftUpVector", "\xE2\x86\xBF", 12, 3}, + {"LeftUpVectorBar", "\xE2\xA5\x98", 15, 3}, + {"LeftVector", "\xE2\x86\xBC", 10, 3}, + {"LeftVectorBar", "\xE2\xA5\x92", 13, 3}, + {"Leftarrow", "\xE2\x87\x90", 9, 3}, + {"Leftrightarrow", "\xE2\x87\x94", 14, 3}, + {"LessEqualGreater", "\xE2\x8B\x9A", 16, 3}, + {"LessFullEqual", "\xE2\x89\xA6", 13, 3}, + {"LessGreater", "\xE2\x89\xB6", 11, 3}, + {"LessLess", "\xE2\xAA\xA1", 8, 3}, + {"LessSlantEqual", "\xE2\xA9\xBD", 14, 3}, + {"LessTilde", "\xE2\x89\xB2", 9, 3}, + {"Lfr", "\xF0\x9D\x94\x8F", 3, 4}, + {"Ll", "\xE2\x8B\x98", 2, 3}, + {"Lleftarrow", "\xE2\x87\x9A", 10, 3}, + {"Lmidot", "\xC4\xBF", 6, 2}, + {"LongLeftArrow", "\xE2\x9F\xB5", 13, 3}, + {"LongLeftRightArrow", "\xE2\x9F\xB7", 18, 3}, + {"LongRightArrow", "\xE2\x9F\xB6", 14, 3}, + {"Longleftarrow", "\xE2\x9F\xB8", 13, 3}, + {"Longleftrightarrow", "\xE2\x9F\xBA", 18, 3}, + {"Longrightarrow", "\xE2\x9F\xB9", 14, 3}, + {"Lopf", "\xF0\x9D\x95\x83", 4, 4}, + {"LowerLeftArrow", "\xE2\x86\x99", 14, 3}, + {"LowerRightArrow", "\xE2\x86\x98", 15, 3}, + {"Lscr", "\xE2\x84\x92", 4, 3}, + {"Lsh", "\xE2\x86\xB0", 3, 3}, + {"Lstrok", "\xC5\x81", 6, 2}, + {"Lt", "\xE2\x89\xAA", 2, 3}, + {"Map", "\xE2\xA4\x85", 3, 3}, + {"Mcy", "\xD0\x9C", 3, 2}, + {"MediumSpace", "\xE2\x81\x9F", 11, 3}, + {"Mellintrf", "\xE2\x84\xB3", 9, 3}, + {"Mfr", "\xF0\x9D\x94\x90", 3, 4}, + {"MinusPlus", "\xE2\x88\x93", 9, 3}, + {"Mopf", "\xF0\x9D\x95\x84", 4, 4}, + {"Mscr", "\xE2\x84\xB3", 4, 3}, + {"Mu", "\xCE\x9C", 2, 2}, + {"NJcy", "\xD0\x8A", 4, 2}, + {"Nacute", "\xC5\x83", 6, 2}, + {"Ncaron", "\xC5\x87", 6, 2}, + {"Ncedil", "\xC5\x85", 6, 2}, + {"Ncy", "\xD0\x9D", 3, 2}, + {"NegativeMediumSpace", "\xE2\x80\x8B", 19, 3}, + {"NegativeThickSpace", "\xE2\x80\x8B", 18, 3}, + {"NegativeThinSpace", "\xE2\x80\x8B", 17, 3}, + {"NegativeVeryThinSpace", "\xE2\x80\x8B", 21, 3}, + {"NestedGreaterGreater", "\xE2\x89\xAB", 20, 3}, + {"NestedLessLess", "\xE2\x89\xAA", 14, 3}, + {"NewLine", "\x0A", 7, 1}, + {"Nfr", "\xF0\x9D\x94\x91", 3, 4}, + {"NoBreak", "\xE2\x81\xA0", 7, 3}, + {"NonBreakingSpace", "\xC2\xA0", 16, 2}, + {"Nopf", "\xE2\x84\x95", 4, 3}, + {"Not", "\xE2\xAB\xAC", 3, 3}, + {"NotCongruent", "\xE2\x89\xA2", 12, 3}, + {"NotCupCap", "\xE2\x89\xAD", 9, 3}, + {"NotDoubleVerticalBar", "\xE2\x88\xA6", 20, 3}, + {"NotElement", "\xE2\x88\x89", 10, 3}, + {"NotEqual", "\xE2\x89\xA0", 8, 3}, + {"NotEqualTilde", "\xE2\x89\x82\xCC\xB8", 13, 5}, + {"NotExists", "\xE2\x88\x84", 9, 3}, + {"NotGreater", "\xE2\x89\xAF", 10, 3}, + {"NotGreaterEqual", "\xE2\x89\xB1", 15, 3}, + {"NotGreaterFullEqual", "\xE2\x89\xA7\xCC\xB8", 19, 5}, + {"NotGreaterGreater", "\xE2\x89\xAB\xCC\xB8", 17, 5}, + {"NotGreaterLess", "\xE2\x89\xB9", 14, 3}, + {"NotGreaterSlantEqual", "\xE2\xA9\xBE\xCC\xB8", 20, 5}, + {"NotGreaterTilde", "\xE2\x89\xB5", 15, 3}, + {"NotHumpDownHump", "\xE2\x89\x8E\xCC\xB8", 15, 5}, + {"NotHumpEqual", "\xE2\x89\x8F\xCC\xB8", 12, 5}, + {"NotLeftTriangle", "\xE2\x8B\xAA", 15, 3}, + {"NotLeftTriangleBar", "\xE2\xA7\x8F\xCC\xB8", 18, 5}, + {"NotLeftTriangleEqual", "\xE2\x8B\xAC", 20, 3}, + {"NotLess", "\xE2\x89\xAE", 7, 3}, + {"NotLessEqual", "\xE2\x89\xB0", 12, 3}, + {"NotLessGreater", "\xE2\x89\xB8", 14, 3}, + {"NotLessLess", "\xE2\x89\xAA\xCC\xB8", 11, 5}, + {"NotLessSlantEqual", "\xE2\xA9\xBD\xCC\xB8", 17, 5}, + {"NotLessTilde", "\xE2\x89\xB4", 12, 3}, + {"NotNestedGreaterGreater", "\xE2\xAA\xA2\xCC\xB8", 23, 5}, + {"NotNestedLessLess", "\xE2\xAA\xA1\xCC\xB8", 17, 5}, + {"NotPrecedes", "\xE2\x8A\x80", 11, 3}, + {"NotPrecedesEqual", "\xE2\xAA\xAF\xCC\xB8", 16, 5}, + {"NotPrecedesSlantEqual", "\xE2\x8B\xA0", 21, 3}, + {"NotReverseElement", "\xE2\x88\x8C", 17, 3}, + {"NotRightTriangle", "\xE2\x8B\xAB", 16, 3}, + {"NotRightTriangleBar", "\xE2\xA7\x90\xCC\xB8", 19, 5}, + {"NotRightTriangleEqual", "\xE2\x8B\xAD", 21, 3}, + {"NotSquareSubset", "\xE2\x8A\x8F\xCC\xB8", 15, 5}, + {"NotSquareSubsetEqual", "\xE2\x8B\xA2", 20, 3}, + {"NotSquareSuperset", "\xE2\x8A\x90\xCC\xB8", 17, 5}, + {"NotSquareSupersetEqual", "\xE2\x8B\xA3", 22, 3}, + {"NotSubset", "\xE2\x8A\x82\xE2\x83\x92", 9, 6}, + {"NotSubsetEqual", "\xE2\x8A\x88", 14, 3}, + {"NotSucceeds", "\xE2\x8A\x81", 11, 3}, + {"NotSucceedsEqual", "\xE2\xAA\xB0\xCC\xB8", 16, 5}, + {"NotSucceedsSlantEqual", "\xE2\x8B\xA1", 21, 3}, + {"NotSucceedsTilde", "\xE2\x89\xBF\xCC\xB8", 16, 5}, + {"NotSuperset", "\xE2\x8A\x83\xE2\x83\x92", 11, 6}, + {"NotSupersetEqual", "\xE2\x8A\x89", 16, 3}, + {"NotTilde", "\xE2\x89\x81", 8, 3}, + {"NotTildeEqual", "\xE2\x89\x84", 13, 3}, + {"NotTildeFullEqual", "\xE2\x89\x87", 17, 3}, + {"NotTildeTilde", "\xE2\x89\x89", 13, 3}, + {"NotVerticalBar", "\xE2\x88\xA4", 14, 3}, + {"Nscr", "\xF0\x9D\x92\xA9", 4, 4}, + {"Ntilde", "\xC3\x91", 6, 2}, + {"Nu", "\xCE\x9D", 2, 2}, + {"OElig", "\xC5\x92", 5, 2}, + {"Oacute", "\xC3\x93", 6, 2}, + {"Ocirc", "\xC3\x94", 5, 2}, + {"Ocy", "\xD0\x9E", 3, 2}, + {"Odblac", "\xC5\x90", 6, 2}, + {"Ofr", "\xF0\x9D\x94\x92", 3, 4}, + {"Ograve", "\xC3\x92", 6, 2}, + {"Omacr", "\xC5\x8C", 5, 2}, + {"Omega", "\xCE\xA9", 5, 2}, + {"Omicron", "\xCE\x9F", 7, 2}, + {"Oopf", "\xF0\x9D\x95\x86", 4, 4}, + {"OpenCurlyDoubleQuote", "\xE2\x80\x9C", 20, 3}, + {"OpenCurlyQuote", "\xE2\x80\x98", 14, 3}, + {"Or", "\xE2\xA9\x94", 2, 3}, + {"Oscr", "\xF0\x9D\x92\xAA", 4, 4}, + {"Oslash", "\xC3\x98", 6, 2}, + {"Otilde", "\xC3\x95", 6, 2}, + {"Otimes", "\xE2\xA8\xB7", 6, 3}, + {"Ouml", "\xC3\x96", 4, 2}, + {"OverBar", "\xE2\x80\xBE", 7, 3}, + {"OverBrace", "\xE2\x8F\x9E", 9, 3}, + {"OverBracket", "\xE2\x8E\xB4", 11, 3}, + {"OverParenthesis", "\xE2\x8F\x9C", 15, 3}, + {"PartialD", "\xE2\x88\x82", 8, 3}, + {"Pcy", "\xD0\x9F", 3, 2}, + {"Pfr", "\xF0\x9D\x94\x93", 3, 4}, + {"Phi", "\xCE\xA6", 3, 2}, + {"Pi", "\xCE\xA0", 2, 2}, + {"PlusMinus", "\xC2\xB1", 9, 2}, + {"Poincareplane", "\xE2\x84\x8C", 13, 3}, + {"Popf", "\xE2\x84\x99", 4, 3}, + {"Pr", "\xE2\xAA\xBB", 2, 3}, + {"Precedes", "\xE2\x89\xBA", 8, 3}, + {"PrecedesEqual", "\xE2\xAA\xAF", 13, 3}, + {"PrecedesSlantEqual", "\xE2\x89\xBC", 18, 3}, + {"PrecedesTilde", "\xE2\x89\xBE", 13, 3}, + {"Prime", "\xE2\x80\xB3", 5, 3}, + {"Product", "\xE2\x88\x8F", 7, 3}, + {"Proportion", "\xE2\x88\xB7", 10, 3}, + {"Proportional", "\xE2\x88\x9D", 12, 3}, + {"Pscr", "\xF0\x9D\x92\xAB", 4, 4}, + {"Psi", "\xCE\xA8", 3, 2}, + {"QUOT", "\x22", 4, 1}, + {"Qfr", "\xF0\x9D\x94\x94", 3, 4}, + {"Qopf", "\xE2\x84\x9A", 4, 3}, + {"Qscr", "\xF0\x9D\x92\xAC", 4, 4}, + {"RBarr", "\xE2\xA4\x90", 5, 3}, + {"REG", "\xC2\xAE", 3, 2}, + {"Racute", "\xC5\x94", 6, 2}, + {"Rang", "\xE2\x9F\xAB", 4, 3}, + {"Rarr", "\xE2\x86\xA0", 4, 3}, + {"Rarrtl", "\xE2\xA4\x96", 6, 3}, + {"Rcaron", "\xC5\x98", 6, 2}, + {"Rcedil", "\xC5\x96", 6, 2}, + {"Rcy", "\xD0\xA0", 3, 2}, + {"Re", "\xE2\x84\x9C", 2, 3}, + {"ReverseElement", "\xE2\x88\x8B", 14, 3}, + {"ReverseEquilibrium", "\xE2\x87\x8B", 18, 3}, + {"ReverseUpEquilibrium", "\xE2\xA5\xAF", 20, 3}, + {"Rfr", "\xE2\x84\x9C", 3, 3}, + {"Rho", "\xCE\xA1", 3, 2}, + {"RightAngleBracket", "\xE2\x9F\xA9", 17, 3}, + {"RightArrow", "\xE2\x86\x92", 10, 3}, + {"RightArrowBar", "\xE2\x87\xA5", 13, 3}, + {"RightArrowLeftArrow", "\xE2\x87\x84", 19, 3}, + {"RightCeiling", "\xE2\x8C\x89", 12, 3}, + {"RightDoubleBracket", "\xE2\x9F\xA7", 18, 3}, + {"RightDownTeeVector", "\xE2\xA5\x9D", 18, 3}, + {"RightDownVector", "\xE2\x87\x82", 15, 3}, + {"RightDownVectorBar", "\xE2\xA5\x95", 18, 3}, + {"RightFloor", "\xE2\x8C\x8B", 10, 3}, + {"RightTee", "\xE2\x8A\xA2", 8, 3}, + {"RightTeeArrow", "\xE2\x86\xA6", 13, 3}, + {"RightTeeVector", "\xE2\xA5\x9B", 14, 3}, + {"RightTriangle", "\xE2\x8A\xB3", 13, 3}, + {"RightTriangleBar", "\xE2\xA7\x90", 16, 3}, + {"RightTriangleEqual", "\xE2\x8A\xB5", 18, 3}, + {"RightUpDownVector", "\xE2\xA5\x8F", 17, 3}, + {"RightUpTeeVector", "\xE2\xA5\x9C", 16, 3}, + {"RightUpVector", "\xE2\x86\xBE", 13, 3}, + {"RightUpVectorBar", "\xE2\xA5\x94", 16, 3}, + {"RightVector", "\xE2\x87\x80", 11, 3}, + {"RightVectorBar", "\xE2\xA5\x93", 14, 3}, + {"Rightarrow", "\xE2\x87\x92", 10, 3}, + {"Ropf", "\xE2\x84\x9D", 4, 3}, + {"RoundImplies", "\xE2\xA5\xB0", 12, 3}, + {"Rrightarrow", "\xE2\x87\x9B", 11, 3}, + {"Rscr", "\xE2\x84\x9B", 4, 3}, + {"Rsh", "\xE2\x86\xB1", 3, 3}, + {"RuleDelayed", "\xE2\xA7\xB4", 11, 3}, + {"SHCHcy", "\xD0\xA9", 6, 2}, + {"SHcy", "\xD0\xA8", 4, 2}, + {"SOFTcy", "\xD0\xAC", 6, 2}, + {"Sacute", "\xC5\x9A", 6, 2}, + {"Sc", "\xE2\xAA\xBC", 2, 3}, + {"Scaron", "\xC5\xA0", 6, 2}, + {"Scedil", "\xC5\x9E", 6, 2}, + {"Scirc", "\xC5\x9C", 5, 2}, + {"Scy", "\xD0\xA1", 3, 2}, + {"Sfr", "\xF0\x9D\x94\x96", 3, 4}, + {"ShortDownArrow", "\xE2\x86\x93", 14, 3}, + {"ShortLeftArrow", "\xE2\x86\x90", 14, 3}, + {"ShortRightArrow", "\xE2\x86\x92", 15, 3}, + {"ShortUpArrow", "\xE2\x86\x91", 12, 3}, + {"Sigma", "\xCE\xA3", 5, 2}, + {"SmallCircle", "\xE2\x88\x98", 11, 3}, + {"Sopf", "\xF0\x9D\x95\x8A", 4, 4}, + {"Sqrt", "\xE2\x88\x9A", 4, 3}, + {"Square", "\xE2\x96\xA1", 6, 3}, + {"SquareIntersection", "\xE2\x8A\x93", 18, 3}, + {"SquareSubset", "\xE2\x8A\x8F", 12, 3}, + {"SquareSubsetEqual", "\xE2\x8A\x91", 17, 3}, + {"SquareSuperset", "\xE2\x8A\x90", 14, 3}, + {"SquareSupersetEqual", "\xE2\x8A\x92", 19, 3}, + {"SquareUnion", "\xE2\x8A\x94", 11, 3}, + {"Sscr", "\xF0\x9D\x92\xAE", 4, 4}, + {"Star", "\xE2\x8B\x86", 4, 3}, + {"Sub", "\xE2\x8B\x90", 3, 3}, + {"Subset", "\xE2\x8B\x90", 6, 3}, + {"SubsetEqual", "\xE2\x8A\x86", 11, 3}, + {"Succeeds", "\xE2\x89\xBB", 8, 3}, + {"SucceedsEqual", "\xE2\xAA\xB0", 13, 3}, + {"SucceedsSlantEqual", "\xE2\x89\xBD", 18, 3}, + {"SucceedsTilde", "\xE2\x89\xBF", 13, 3}, + {"SuchThat", "\xE2\x88\x8B", 8, 3}, + {"Sum", "\xE2\x88\x91", 3, 3}, + {"Sup", "\xE2\x8B\x91", 3, 3}, + {"Superset", "\xE2\x8A\x83", 8, 3}, + {"SupersetEqual", "\xE2\x8A\x87", 13, 3}, + {"Supset", "\xE2\x8B\x91", 6, 3}, + {"THORN", "\xC3\x9E", 5, 2}, + {"TRADE", "\xE2\x84\xA2", 5, 3}, + {"TSHcy", "\xD0\x8B", 5, 2}, + {"TScy", "\xD0\xA6", 4, 2}, + {"Tab", "\x09", 3, 1}, + {"Tau", "\xCE\xA4", 3, 2}, + {"Tcaron", "\xC5\xA4", 6, 2}, + {"Tcedil", "\xC5\xA2", 6, 2}, + {"Tcy", "\xD0\xA2", 3, 2}, + {"Tfr", "\xF0\x9D\x94\x97", 3, 4}, + {"Therefore", "\xE2\x88\xB4", 9, 3}, + {"Theta", "\xCE\x98", 5, 2}, + {"ThickSpace", "\xE2\x81\x9F\xE2\x80\x8A", 10, 6}, + {"ThinSpace", "\xE2\x80\x89", 9, 3}, + {"Tilde", "\xE2\x88\xBC", 5, 3}, + {"TildeEqual", "\xE2\x89\x83", 10, 3}, + {"TildeFullEqual", "\xE2\x89\x85", 14, 3}, + {"TildeTilde", "\xE2\x89\x88", 10, 3}, + {"Topf", "\xF0\x9D\x95\x8B", 4, 4}, + {"TripleDot", "\xE2\x83\x9B", 9, 3}, + {"Tscr", "\xF0\x9D\x92\xAF", 4, 4}, + {"Tstrok", "\xC5\xA6", 6, 2}, + {"Uacute", "\xC3\x9A", 6, 2}, + {"Uarr", "\xE2\x86\x9F", 4, 3}, + {"Uarrocir", "\xE2\xA5\x89", 8, 3}, + {"Ubrcy", "\xD0\x8E", 5, 2}, + {"Ubreve", "\xC5\xAC", 6, 2}, + {"Ucirc", "\xC3\x9B", 5, 2}, + {"Ucy", "\xD0\xA3", 3, 2}, + {"Udblac", "\xC5\xB0", 6, 2}, + {"Ufr", "\xF0\x9D\x94\x98", 3, 4}, + {"Ugrave", "\xC3\x99", 6, 2}, + {"Umacr", "\xC5\xAA", 5, 2}, + {"UnderBar", "\x5F", 8, 1}, + {"UnderBrace", "\xE2\x8F\x9F", 10, 3}, + {"UnderBracket", "\xE2\x8E\xB5", 12, 3}, + {"UnderParenthesis", "\xE2\x8F\x9D", 16, 3}, + {"Union", "\xE2\x8B\x83", 5, 3}, + {"UnionPlus", "\xE2\x8A\x8E", 9, 3}, + {"Uogon", "\xC5\xB2", 5, 2}, + {"Uopf", "\xF0\x9D\x95\x8C", 4, 4}, + {"UpArrow", "\xE2\x86\x91", 7, 3}, + {"UpArrowBar", "\xE2\xA4\x92", 10, 3}, + {"UpArrowDownArrow", "\xE2\x87\x85", 16, 3}, + {"UpDownArrow", "\xE2\x86\x95", 11, 3}, + {"UpEquilibrium", "\xE2\xA5\xAE", 13, 3}, + {"UpTee", "\xE2\x8A\xA5", 5, 3}, + {"UpTeeArrow", "\xE2\x86\xA5", 10, 3}, + {"Uparrow", "\xE2\x87\x91", 7, 3}, + {"Updownarrow", "\xE2\x87\x95", 11, 3}, + {"UpperLeftArrow", "\xE2\x86\x96", 14, 3}, + {"UpperRightArrow", "\xE2\x86\x97", 15, 3}, + {"Upsi", "\xCF\x92", 4, 2}, + {"Upsilon", "\xCE\xA5", 7, 2}, + {"Uring", "\xC5\xAE", 5, 2}, + {"Uscr", "\xF0\x9D\x92\xB0", 4, 4}, + {"Utilde", "\xC5\xA8", 6, 2}, + {"Uuml", "\xC3\x9C", 4, 2}, + {"VDash", "\xE2\x8A\xAB", 5, 3}, + {"Vbar", "\xE2\xAB\xAB", 4, 3}, + {"Vcy", "\xD0\x92", 3, 2}, + {"Vdash", "\xE2\x8A\xA9", 5, 3}, + {"Vdashl", "\xE2\xAB\xA6", 6, 3}, + {"Vee", "\xE2\x8B\x81", 3, 3}, + {"Verbar", "\xE2\x80\x96", 6, 3}, + {"Vert", "\xE2\x80\x96", 4, 3}, + {"VerticalBar", "\xE2\x88\xA3", 11, 3}, + {"VerticalLine", "\x7C", 12, 1}, + {"VerticalSeparator", "\xE2\x9D\x98", 17, 3}, + {"VerticalTilde", "\xE2\x89\x80", 13, 3}, + {"VeryThinSpace", "\xE2\x80\x8A", 13, 3}, + {"Vfr", "\xF0\x9D\x94\x99", 3, 4}, + {"Vopf", "\xF0\x9D\x95\x8D", 4, 4}, + {"Vscr", "\xF0\x9D\x92\xB1", 4, 4}, + {"Vvdash", "\xE2\x8A\xAA", 6, 3}, + {"Wcirc", "\xC5\xB4", 5, 2}, + {"Wedge", "\xE2\x8B\x80", 5, 3}, + {"Wfr", "\xF0\x9D\x94\x9A", 3, 4}, + {"Wopf", "\xF0\x9D\x95\x8E", 4, 4}, + {"Wscr", "\xF0\x9D\x92\xB2", 4, 4}, + {"Xfr", "\xF0\x9D\x94\x9B", 3, 4}, + {"Xi", "\xCE\x9E", 2, 2}, + {"Xopf", "\xF0\x9D\x95\x8F", 4, 4}, + {"Xscr", "\xF0\x9D\x92\xB3", 4, 4}, + {"YAcy", "\xD0\xAF", 4, 2}, + {"YIcy", "\xD0\x87", 4, 2}, + {"YUcy", "\xD0\xAE", 4, 2}, + {"Yacute", "\xC3\x9D", 6, 2}, + {"Ycirc", "\xC5\xB6", 5, 2}, + {"Ycy", "\xD0\xAB", 3, 2}, + {"Yfr", "\xF0\x9D\x94\x9C", 3, 4}, + {"Yopf", "\xF0\x9D\x95\x90", 4, 4}, + {"Yscr", "\xF0\x9D\x92\xB4", 4, 4}, + {"Yuml", "\xC5\xB8", 4, 2}, + {"ZHcy", "\xD0\x96", 4, 2}, + {"Zacute", "\xC5\xB9", 6, 2}, + {"Zcaron", "\xC5\xBD", 6, 2}, + {"Zcy", "\xD0\x97", 3, 2}, + {"Zdot", "\xC5\xBB", 4, 2}, + {"ZeroWidthSpace", "\xE2\x80\x8B", 14, 3}, + {"Zeta", "\xCE\x96", 4, 2}, + {"Zfr", "\xE2\x84\xA8", 3, 3}, + {"Zopf", "\xE2\x84\xA4", 4, 3}, + {"Zscr", "\xF0\x9D\x92\xB5", 4, 4}, + {"aacute", "\xC3\xA1", 6, 2}, + {"abreve", "\xC4\x83", 6, 2}, + {"ac", "\xE2\x88\xBE", 2, 3}, + {"acE", "\xE2\x88\xBE\xCC\xB3", 3, 5}, + {"acd", "\xE2\x88\xBF", 3, 3}, + {"acirc", "\xC3\xA2", 5, 2}, + {"acute", "\xC2\xB4", 5, 2}, + {"acy", "\xD0\xB0", 3, 2}, + {"aelig", "\xC3\xA6", 5, 2}, + {"af", "\xE2\x81\xA1", 2, 3}, + {"afr", "\xF0\x9D\x94\x9E", 3, 4}, + {"agrave", "\xC3\xA0", 6, 2}, + {"alefsym", "\xE2\x84\xB5", 7, 3}, + {"aleph", "\xE2\x84\xB5", 5, 3}, + {"alpha", "\xCE\xB1", 5, 2}, + {"amacr", "\xC4\x81", 5, 2}, + {"amalg", "\xE2\xA8\xBF", 5, 3}, + {"amp", "\x26", 3, 1}, + {"and", "\xE2\x88\xA7", 3, 3}, + {"andand", "\xE2\xA9\x95", 6, 3}, + {"andd", "\xE2\xA9\x9C", 4, 3}, + {"andslope", "\xE2\xA9\x98", 8, 3}, + {"andv", "\xE2\xA9\x9A", 4, 3}, + {"ang", "\xE2\x88\xA0", 3, 3}, + {"ange", "\xE2\xA6\xA4", 4, 3}, + {"angle", "\xE2\x88\xA0", 5, 3}, + {"angmsd", "\xE2\x88\xA1", 6, 3}, + {"angmsdaa", "\xE2\xA6\xA8", 8, 3}, + {"angmsdab", "\xE2\xA6\xA9", 8, 3}, + {"angmsdac", "\xE2\xA6\xAA", 8, 3}, + {"angmsdad", "\xE2\xA6\xAB", 8, 3}, + {"angmsdae", "\xE2\xA6\xAC", 8, 3}, + {"angmsdaf", "\xE2\xA6\xAD", 8, 3}, + {"angmsdag", "\xE2\xA6\xAE", 8, 3}, + {"angmsdah", "\xE2\xA6\xAF", 8, 3}, + {"angrt", "\xE2\x88\x9F", 5, 3}, + {"angrtvb", "\xE2\x8A\xBE", 7, 3}, + {"angrtvbd", "\xE2\xA6\x9D", 8, 3}, + {"angsph", "\xE2\x88\xA2", 6, 3}, + {"angst", "\xC3\x85", 5, 2}, + {"angzarr", "\xE2\x8D\xBC", 7, 3}, + {"aogon", "\xC4\x85", 5, 2}, + {"aopf", "\xF0\x9D\x95\x92", 4, 4}, + {"ap", "\xE2\x89\x88", 2, 3}, + {"apE", "\xE2\xA9\xB0", 3, 3}, + {"apacir", "\xE2\xA9\xAF", 6, 3}, + {"ape", "\xE2\x89\x8A", 3, 3}, + {"apid", "\xE2\x89\x8B", 4, 3}, + {"apos", "\x27", 4, 1}, + {"approx", "\xE2\x89\x88", 6, 3}, + {"approxeq", "\xE2\x89\x8A", 8, 3}, + {"aring", "\xC3\xA5", 5, 2}, + {"ascr", "\xF0\x9D\x92\xB6", 4, 4}, + {"ast", "\x2A", 3, 1}, + {"asymp", "\xE2\x89\x88", 5, 3}, + {"asympeq", "\xE2\x89\x8D", 7, 3}, + {"atilde", "\xC3\xA3", 6, 2}, + {"auml", "\xC3\xA4", 4, 2}, + {"awconint", "\xE2\x88\xB3", 8, 3}, + {"awint", "\xE2\xA8\x91", 5, 3}, + {"bNot", "\xE2\xAB\xAD", 4, 3}, + {"backcong", "\xE2\x89\x8C", 8, 3}, + {"backepsilon", "\xCF\xB6", 11, 2}, + {"backprime", "\xE2\x80\xB5", 9, 3}, + {"backsim", "\xE2\x88\xBD", 7, 3}, + {"backsimeq", "\xE2\x8B\x8D", 9, 3}, + {"barvee", "\xE2\x8A\xBD", 6, 3}, + {"barwed", "\xE2\x8C\x85", 6, 3}, + {"barwedge", "\xE2\x8C\x85", 8, 3}, + {"bbrk", "\xE2\x8E\xB5", 4, 3}, + {"bbrktbrk", "\xE2\x8E\xB6", 8, 3}, + {"bcong", "\xE2\x89\x8C", 5, 3}, + {"bcy", "\xD0\xB1", 3, 2}, + {"bdquo", "\xE2\x80\x9E", 5, 3}, + {"becaus", "\xE2\x88\xB5", 6, 3}, + {"because", "\xE2\x88\xB5", 7, 3}, + {"bemptyv", "\xE2\xA6\xB0", 7, 3}, + {"bepsi", "\xCF\xB6", 5, 2}, + {"bernou", "\xE2\x84\xAC", 6, 3}, + {"beta", "\xCE\xB2", 4, 2}, + {"beth", "\xE2\x84\xB6", 4, 3}, + {"between", "\xE2\x89\xAC", 7, 3}, + {"bfr", "\xF0\x9D\x94\x9F", 3, 4}, + {"bigcap", "\xE2\x8B\x82", 6, 3}, + {"bigcirc", "\xE2\x97\xAF", 7, 3}, + {"bigcup", "\xE2\x8B\x83", 6, 3}, + {"bigodot", "\xE2\xA8\x80", 7, 3}, + {"bigoplus", "\xE2\xA8\x81", 8, 3}, + {"bigotimes", "\xE2\xA8\x82", 9, 3}, + {"bigsqcup", "\xE2\xA8\x86", 8, 3}, + {"bigstar", "\xE2\x98\x85", 7, 3}, + {"bigtriangledown", "\xE2\x96\xBD", 15, 3}, + {"bigtriangleup", "\xE2\x96\xB3", 13, 3}, + {"biguplus", "\xE2\xA8\x84", 8, 3}, + {"bigvee", "\xE2\x8B\x81", 6, 3}, + {"bigwedge", "\xE2\x8B\x80", 8, 3}, + {"bkarow", "\xE2\xA4\x8D", 6, 3}, + {"blacklozenge", "\xE2\xA7\xAB", 12, 3}, + {"blacksquare", "\xE2\x96\xAA", 11, 3}, + {"blacktriangle", "\xE2\x96\xB4", 13, 3}, + {"blacktriangledown", "\xE2\x96\xBE", 17, 3}, + {"blacktriangleleft", "\xE2\x97\x82", 17, 3}, + {"blacktriangleright", "\xE2\x96\xB8", 18, 3}, + {"blank", "\xE2\x90\xA3", 5, 3}, + {"blk12", "\xE2\x96\x92", 5, 3}, + {"blk14", "\xE2\x96\x91", 5, 3}, + {"blk34", "\xE2\x96\x93", 5, 3}, + {"block", "\xE2\x96\x88", 5, 3}, + {"bne", "\x3D\xE2\x83\xA5", 3, 4}, + {"bnequiv", "\xE2\x89\xA1\xE2\x83\xA5", 7, 6}, + {"bnot", "\xE2\x8C\x90", 4, 3}, + {"bopf", "\xF0\x9D\x95\x93", 4, 4}, + {"bot", "\xE2\x8A\xA5", 3, 3}, + {"bottom", "\xE2\x8A\xA5", 6, 3}, + {"bowtie", "\xE2\x8B\x88", 6, 3}, + {"boxDL", "\xE2\x95\x97", 5, 3}, + {"boxDR", "\xE2\x95\x94", 5, 3}, + {"boxDl", "\xE2\x95\x96", 5, 3}, + {"boxDr", "\xE2\x95\x93", 5, 3}, + {"boxH", "\xE2\x95\x90", 4, 3}, + {"boxHD", "\xE2\x95\xA6", 5, 3}, + {"boxHU", "\xE2\x95\xA9", 5, 3}, + {"boxHd", "\xE2\x95\xA4", 5, 3}, + {"boxHu", "\xE2\x95\xA7", 5, 3}, + {"boxUL", "\xE2\x95\x9D", 5, 3}, + {"boxUR", "\xE2\x95\x9A", 5, 3}, + {"boxUl", "\xE2\x95\x9C", 5, 3}, + {"boxUr", "\xE2\x95\x99", 5, 3}, + {"boxV", "\xE2\x95\x91", 4, 3}, + {"boxVH", "\xE2\x95\xAC", 5, 3}, + {"boxVL", "\xE2\x95\xA3", 5, 3}, + {"boxVR", "\xE2\x95\xA0", 5, 3}, + {"boxVh", "\xE2\x95\xAB", 5, 3}, + {"boxVl", "\xE2\x95\xA2", 5, 3}, + {"boxVr", "\xE2\x95\x9F", 5, 3}, + {"boxbox", "\xE2\xA7\x89", 6, 3}, + {"boxdL", "\xE2\x95\x95", 5, 3}, + {"boxdR", "\xE2\x95\x92", 5, 3}, + {"boxdl", "\xE2\x94\x90", 5, 3}, + {"boxdr", "\xE2\x94\x8C", 5, 3}, + {"boxh", "\xE2\x94\x80", 4, 3}, + {"boxhD", "\xE2\x95\xA5", 5, 3}, + {"boxhU", "\xE2\x95\xA8", 5, 3}, + {"boxhd", "\xE2\x94\xAC", 5, 3}, + {"boxhu", "\xE2\x94\xB4", 5, 3}, + {"boxminus", "\xE2\x8A\x9F", 8, 3}, + {"boxplus", "\xE2\x8A\x9E", 7, 3}, + {"boxtimes", "\xE2\x8A\xA0", 8, 3}, + {"boxuL", "\xE2\x95\x9B", 5, 3}, + {"boxuR", "\xE2\x95\x98", 5, 3}, + {"boxul", "\xE2\x94\x98", 5, 3}, + {"boxur", "\xE2\x94\x94", 5, 3}, + {"boxv", "\xE2\x94\x82", 4, 3}, + {"boxvH", "\xE2\x95\xAA", 5, 3}, + {"boxvL", "\xE2\x95\xA1", 5, 3}, + {"boxvR", "\xE2\x95\x9E", 5, 3}, + {"boxvh", "\xE2\x94\xBC", 5, 3}, + {"boxvl", "\xE2\x94\xA4", 5, 3}, + {"boxvr", "\xE2\x94\x9C", 5, 3}, + {"bprime", "\xE2\x80\xB5", 6, 3}, + {"breve", "\xCB\x98", 5, 2}, + {"brvbar", "\xC2\xA6", 6, 2}, + {"bscr", "\xF0\x9D\x92\xB7", 4, 4}, + {"bsemi", "\xE2\x81\x8F", 5, 3}, + {"bsim", "\xE2\x88\xBD", 4, 3}, + {"bsime", "\xE2\x8B\x8D", 5, 3}, + {"bsol", "\x5C", 4, 1}, + {"bsolb", "\xE2\xA7\x85", 5, 3}, + {"bsolhsub", "\xE2\x9F\x88", 8, 3}, + {"bull", "\xE2\x80\xA2", 4, 3}, + {"bullet", "\xE2\x80\xA2", 6, 3}, + {"bump", "\xE2\x89\x8E", 4, 3}, + {"bumpE", "\xE2\xAA\xAE", 5, 3}, + {"bumpe", "\xE2\x89\x8F", 5, 3}, + {"bumpeq", "\xE2\x89\x8F", 6, 3}, + {"cacute", "\xC4\x87", 6, 2}, + {"cap", "\xE2\x88\xA9", 3, 3}, + {"capand", "\xE2\xA9\x84", 6, 3}, + {"capbrcup", "\xE2\xA9\x89", 8, 3}, + {"capcap", "\xE2\xA9\x8B", 6, 3}, + {"capcup", "\xE2\xA9\x87", 6, 3}, + {"capdot", "\xE2\xA9\x80", 6, 3}, + {"caps", "\xE2\x88\xA9\xEF\xB8\x80", 4, 6}, + {"caret", "\xE2\x81\x81", 5, 3}, + {"caron", "\xCB\x87", 5, 2}, + {"ccaps", "\xE2\xA9\x8D", 5, 3}, + {"ccaron", "\xC4\x8D", 6, 2}, + {"ccedil", "\xC3\xA7", 6, 2}, + {"ccirc", "\xC4\x89", 5, 2}, + {"ccups", "\xE2\xA9\x8C", 5, 3}, + {"ccupssm", "\xE2\xA9\x90", 7, 3}, + {"cdot", "\xC4\x8B", 4, 2}, + {"cedil", "\xC2\xB8", 5, 2}, + {"cemptyv", "\xE2\xA6\xB2", 7, 3}, + {"cent", "\xC2\xA2", 4, 2}, + {"centerdot", "\xC2\xB7", 9, 2}, + {"cfr", "\xF0\x9D\x94\xA0", 3, 4}, + {"chcy", "\xD1\x87", 4, 2}, + {"check", "\xE2\x9C\x93", 5, 3}, + {"checkmark", "\xE2\x9C\x93", 9, 3}, + {"chi", "\xCF\x87", 3, 2}, + {"cir", "\xE2\x97\x8B", 3, 3}, + {"cirE", "\xE2\xA7\x83", 4, 3}, + {"circ", "\xCB\x86", 4, 2}, + {"circeq", "\xE2\x89\x97", 6, 3}, + {"circlearrowleft", "\xE2\x86\xBA", 15, 3}, + {"circlearrowright", "\xE2\x86\xBB", 16, 3}, + {"circledR", "\xC2\xAE", 8, 2}, + {"circledS", "\xE2\x93\x88", 8, 3}, + {"circledast", "\xE2\x8A\x9B", 10, 3}, + {"circledcirc", "\xE2\x8A\x9A", 11, 3}, + {"circleddash", "\xE2\x8A\x9D", 11, 3}, + {"cire", "\xE2\x89\x97", 4, 3}, + {"cirfnint", "\xE2\xA8\x90", 8, 3}, + {"cirmid", "\xE2\xAB\xAF", 6, 3}, + {"cirscir", "\xE2\xA7\x82", 7, 3}, + {"clubs", "\xE2\x99\xA3", 5, 3}, + {"clubsuit", "\xE2\x99\xA3", 8, 3}, + {"colon", "\x3A", 5, 1}, + {"colone", "\xE2\x89\x94", 6, 3}, + {"coloneq", "\xE2\x89\x94", 7, 3}, + {"comma", "\x2C", 5, 1}, + {"commat", "\x40", 6, 1}, + {"comp", "\xE2\x88\x81", 4, 3}, + {"compfn", "\xE2\x88\x98", 6, 3}, + {"complement", "\xE2\x88\x81", 10, 3}, + {"complexes", "\xE2\x84\x82", 9, 3}, + {"cong", "\xE2\x89\x85", 4, 3}, + {"congdot", "\xE2\xA9\xAD", 7, 3}, + {"conint", "\xE2\x88\xAE", 6, 3}, + {"copf", "\xF0\x9D\x95\x94", 4, 4}, + {"coprod", "\xE2\x88\x90", 6, 3}, + {"copy", "\xC2\xA9", 4, 2}, + {"copysr", "\xE2\x84\x97", 6, 3}, + {"crarr", "\xE2\x86\xB5", 5, 3}, + {"cross", "\xE2\x9C\x97", 5, 3}, + {"cscr", "\xF0\x9D\x92\xB8", 4, 4}, + {"csub", "\xE2\xAB\x8F", 4, 3}, + {"csube", "\xE2\xAB\x91", 5, 3}, + {"csup", "\xE2\xAB\x90", 4, 3}, + {"csupe", "\xE2\xAB\x92", 5, 3}, + {"ctdot", "\xE2\x8B\xAF", 5, 3}, + {"cudarrl", "\xE2\xA4\xB8", 7, 3}, + {"cudarrr", "\xE2\xA4\xB5", 7, 3}, + {"cuepr", "\xE2\x8B\x9E", 5, 3}, + {"cuesc", "\xE2\x8B\x9F", 5, 3}, + {"cularr", "\xE2\x86\xB6", 6, 3}, + {"cularrp", "\xE2\xA4\xBD", 7, 3}, + {"cup", "\xE2\x88\xAA", 3, 3}, + {"cupbrcap", "\xE2\xA9\x88", 8, 3}, + {"cupcap", "\xE2\xA9\x86", 6, 3}, + {"cupcup", "\xE2\xA9\x8A", 6, 3}, + {"cupdot", "\xE2\x8A\x8D", 6, 3}, + {"cupor", "\xE2\xA9\x85", 5, 3}, + {"cups", "\xE2\x88\xAA\xEF\xB8\x80", 4, 6}, + {"curarr", "\xE2\x86\xB7", 6, 3}, + {"curarrm", "\xE2\xA4\xBC", 7, 3}, + {"curlyeqprec", "\xE2\x8B\x9E", 11, 3}, + {"curlyeqsucc", "\xE2\x8B\x9F", 11, 3}, + {"curlyvee", "\xE2\x8B\x8E", 8, 3}, + {"curlywedge", "\xE2\x8B\x8F", 10, 3}, + {"curren", "\xC2\xA4", 6, 2}, + {"curvearrowleft", "\xE2\x86\xB6", 14, 3}, + {"curvearrowright", "\xE2\x86\xB7", 15, 3}, + {"cuvee", "\xE2\x8B\x8E", 5, 3}, + {"cuwed", "\xE2\x8B\x8F", 5, 3}, + {"cwconint", "\xE2\x88\xB2", 8, 3}, + {"cwint", "\xE2\x88\xB1", 5, 3}, + {"cylcty", "\xE2\x8C\xAD", 6, 3}, + {"dArr", "\xE2\x87\x93", 4, 3}, + {"dHar", "\xE2\xA5\xA5", 4, 3}, + {"dagger", "\xE2\x80\xA0", 6, 3}, + {"daleth", "\xE2\x84\xB8", 6, 3}, + {"darr", "\xE2\x86\x93", 4, 3}, + {"dash", "\xE2\x80\x90", 4, 3}, + {"dashv", "\xE2\x8A\xA3", 5, 3}, + {"dbkarow", "\xE2\xA4\x8F", 7, 3}, + {"dblac", "\xCB\x9D", 5, 2}, + {"dcaron", "\xC4\x8F", 6, 2}, + {"dcy", "\xD0\xB4", 3, 2}, + {"dd", "\xE2\x85\x86", 2, 3}, + {"ddagger", "\xE2\x80\xA1", 7, 3}, + {"ddarr", "\xE2\x87\x8A", 5, 3}, + {"ddotseq", "\xE2\xA9\xB7", 7, 3}, + {"deg", "\xC2\xB0", 3, 2}, + {"delta", "\xCE\xB4", 5, 2}, + {"demptyv", "\xE2\xA6\xB1", 7, 3}, + {"dfisht", "\xE2\xA5\xBF", 6, 3}, + {"dfr", "\xF0\x9D\x94\xA1", 3, 4}, + {"dharl", "\xE2\x87\x83", 5, 3}, + {"dharr", "\xE2\x87\x82", 5, 3}, + {"diam", "\xE2\x8B\x84", 4, 3}, + {"diamond", "\xE2\x8B\x84", 7, 3}, + {"diamondsuit", "\xE2\x99\xA6", 11, 3}, + {"diams", "\xE2\x99\xA6", 5, 3}, + {"die", "\xC2\xA8", 3, 2}, + {"digamma", "\xCF\x9D", 7, 2}, + {"disin", "\xE2\x8B\xB2", 5, 3}, + {"div", "\xC3\xB7", 3, 2}, + {"divide", "\xC3\xB7", 6, 2}, + {"divideontimes", "\xE2\x8B\x87", 13, 3}, + {"divonx", "\xE2\x8B\x87", 6, 3}, + {"djcy", "\xD1\x92", 4, 2}, + {"dlcorn", "\xE2\x8C\x9E", 6, 3}, + {"dlcrop", "\xE2\x8C\x8D", 6, 3}, + {"dollar", "\x24", 6, 1}, + {"dopf", "\xF0\x9D\x95\x95", 4, 4}, + {"dot", "\xCB\x99", 3, 2}, + {"doteq", "\xE2\x89\x90", 5, 3}, + {"doteqdot", "\xE2\x89\x91", 8, 3}, + {"dotminus", "\xE2\x88\xB8", 8, 3}, + {"dotplus", "\xE2\x88\x94", 7, 3}, + {"dotsquare", "\xE2\x8A\xA1", 9, 3}, + {"doublebarwedge", "\xE2\x8C\x86", 14, 3}, + {"downarrow", "\xE2\x86\x93", 9, 3}, + {"downdownarrows", "\xE2\x87\x8A", 14, 3}, + {"downharpoonleft", "\xE2\x87\x83", 15, 3}, + {"downharpoonright", "\xE2\x87\x82", 16, 3}, + {"drbkarow", "\xE2\xA4\x90", 8, 3}, + {"drcorn", "\xE2\x8C\x9F", 6, 3}, + {"drcrop", "\xE2\x8C\x8C", 6, 3}, + {"dscr", "\xF0\x9D\x92\xB9", 4, 4}, + {"dscy", "\xD1\x95", 4, 2}, + {"dsol", "\xE2\xA7\xB6", 4, 3}, + {"dstrok", "\xC4\x91", 6, 2}, + {"dtdot", "\xE2\x8B\xB1", 5, 3}, + {"dtri", "\xE2\x96\xBF", 4, 3}, + {"dtrif", "\xE2\x96\xBE", 5, 3}, + {"duarr", "\xE2\x87\xB5", 5, 3}, + {"duhar", "\xE2\xA5\xAF", 5, 3}, + {"dwangle", "\xE2\xA6\xA6", 7, 3}, + {"dzcy", "\xD1\x9F", 4, 2}, + {"dzigrarr", "\xE2\x9F\xBF", 8, 3}, + {"eDDot", "\xE2\xA9\xB7", 5, 3}, + {"eDot", "\xE2\x89\x91", 4, 3}, + {"eacute", "\xC3\xA9", 6, 2}, + {"easter", "\xE2\xA9\xAE", 6, 3}, + {"ecaron", "\xC4\x9B", 6, 2}, + {"ecir", "\xE2\x89\x96", 4, 3}, + {"ecirc", "\xC3\xAA", 5, 2}, + {"ecolon", "\xE2\x89\x95", 6, 3}, + {"ecy", "\xD1\x8D", 3, 2}, + {"edot", "\xC4\x97", 4, 2}, + {"ee", "\xE2\x85\x87", 2, 3}, + {"efDot", "\xE2\x89\x92", 5, 3}, + {"efr", "\xF0\x9D\x94\xA2", 3, 4}, + {"eg", "\xE2\xAA\x9A", 2, 3}, + {"egrave", "\xC3\xA8", 6, 2}, + {"egs", "\xE2\xAA\x96", 3, 3}, + {"egsdot", "\xE2\xAA\x98", 6, 3}, + {"el", "\xE2\xAA\x99", 2, 3}, + {"elinters", "\xE2\x8F\xA7", 8, 3}, + {"ell", "\xE2\x84\x93", 3, 3}, + {"els", "\xE2\xAA\x95", 3, 3}, + {"elsdot", "\xE2\xAA\x97", 6, 3}, + {"emacr", "\xC4\x93", 5, 2}, + {"empty", "\xE2\x88\x85", 5, 3}, + {"emptyset", "\xE2\x88\x85", 8, 3}, + {"emptyv", "\xE2\x88\x85", 6, 3}, + {"emsp", "\xE2\x80\x83", 4, 3}, + {"emsp13", "\xE2\x80\x84", 6, 3}, + {"emsp14", "\xE2\x80\x85", 6, 3}, + {"eng", "\xC5\x8B", 3, 2}, + {"ensp", "\xE2\x80\x82", 4, 3}, + {"eogon", "\xC4\x99", 5, 2}, + {"eopf", "\xF0\x9D\x95\x96", 4, 4}, + {"epar", "\xE2\x8B\x95", 4, 3}, + {"eparsl", "\xE2\xA7\xA3", 6, 3}, + {"eplus", "\xE2\xA9\xB1", 5, 3}, + {"epsi", "\xCE\xB5", 4, 2}, + {"epsilon", "\xCE\xB5", 7, 2}, + {"epsiv", "\xCF\xB5", 5, 2}, + {"eqcirc", "\xE2\x89\x96", 6, 3}, + {"eqcolon", "\xE2\x89\x95", 7, 3}, + {"eqsim", "\xE2\x89\x82", 5, 3}, + {"eqslantgtr", "\xE2\xAA\x96", 10, 3}, + {"eqslantless", "\xE2\xAA\x95", 11, 3}, + {"equals", "\x3D", 6, 1}, + {"equest", "\xE2\x89\x9F", 6, 3}, + {"equiv", "\xE2\x89\xA1", 5, 3}, + {"equivDD", "\xE2\xA9\xB8", 7, 3}, + {"eqvparsl", "\xE2\xA7\xA5", 8, 3}, + {"erDot", "\xE2\x89\x93", 5, 3}, + {"erarr", "\xE2\xA5\xB1", 5, 3}, + {"escr", "\xE2\x84\xAF", 4, 3}, + {"esdot", "\xE2\x89\x90", 5, 3}, + {"esim", "\xE2\x89\x82", 4, 3}, + {"eta", "\xCE\xB7", 3, 2}, + {"eth", "\xC3\xB0", 3, 2}, + {"euml", "\xC3\xAB", 4, 2}, + {"euro", "\xE2\x82\xAC", 4, 3}, + {"excl", "\x21", 4, 1}, + {"exist", "\xE2\x88\x83", 5, 3}, + {"expectation", "\xE2\x84\xB0", 11, 3}, + {"exponentiale", "\xE2\x85\x87", 12, 3}, + {"fallingdotseq", "\xE2\x89\x92", 13, 3}, + {"fcy", "\xD1\x84", 3, 2}, + {"female", "\xE2\x99\x80", 6, 3}, + {"ffilig", "\xEF\xAC\x83", 6, 3}, + {"fflig", "\xEF\xAC\x80", 5, 3}, + {"ffllig", "\xEF\xAC\x84", 6, 3}, + {"ffr", "\xF0\x9D\x94\xA3", 3, 4}, + {"filig", "\xEF\xAC\x81", 5, 3}, + {"fjlig", "\x66\x6A", 5, 2}, + {"flat", "\xE2\x99\xAD", 4, 3}, + {"fllig", "\xEF\xAC\x82", 5, 3}, + {"fltns", "\xE2\x96\xB1", 5, 3}, + {"fnof", "\xC6\x92", 4, 2}, + {"fopf", "\xF0\x9D\x95\x97", 4, 4}, + {"forall", "\xE2\x88\x80", 6, 3}, + {"fork", "\xE2\x8B\x94", 4, 3}, + {"forkv", "\xE2\xAB\x99", 5, 3}, + {"fpartint", "\xE2\xA8\x8D", 8, 3}, + {"frac12", "\xC2\xBD", 6, 2}, + {"frac13", "\xE2\x85\x93", 6, 3}, + {"frac14", "\xC2\xBC", 6, 2}, + {"frac15", "\xE2\x85\x95", 6, 3}, + {"frac16", "\xE2\x85\x99", 6, 3}, + {"frac18", "\xE2\x85\x9B", 6, 3}, + {"frac23", "\xE2\x85\x94", 6, 3}, + {"frac25", "\xE2\x85\x96", 6, 3}, + {"frac34", "\xC2\xBE", 6, 2}, + {"frac35", "\xE2\x85\x97", 6, 3}, + {"frac38", "\xE2\x85\x9C", 6, 3}, + {"frac45", "\xE2\x85\x98", 6, 3}, + {"frac56", "\xE2\x85\x9A", 6, 3}, + {"frac58", "\xE2\x85\x9D", 6, 3}, + {"frac78", "\xE2\x85\x9E", 6, 3}, + {"frasl", "\xE2\x81\x84", 5, 3}, + {"frown", "\xE2\x8C\xA2", 5, 3}, + {"fscr", "\xF0\x9D\x92\xBB", 4, 4}, + {"gE", "\xE2\x89\xA7", 2, 3}, + {"gEl", "\xE2\xAA\x8C", 3, 3}, + {"gacute", "\xC7\xB5", 6, 2}, + {"gamma", "\xCE\xB3", 5, 2}, + {"gammad", "\xCF\x9D", 6, 2}, + {"gap", "\xE2\xAA\x86", 3, 3}, + {"gbreve", "\xC4\x9F", 6, 2}, + {"gcirc", "\xC4\x9D", 5, 2}, + {"gcy", "\xD0\xB3", 3, 2}, + {"gdot", "\xC4\xA1", 4, 2}, + {"ge", "\xE2\x89\xA5", 2, 3}, + {"gel", "\xE2\x8B\x9B", 3, 3}, + {"geq", "\xE2\x89\xA5", 3, 3}, + {"geqq", "\xE2\x89\xA7", 4, 3}, + {"geqslant", "\xE2\xA9\xBE", 8, 3}, + {"ges", "\xE2\xA9\xBE", 3, 3}, + {"gescc", "\xE2\xAA\xA9", 5, 3}, + {"gesdot", "\xE2\xAA\x80", 6, 3}, + {"gesdoto", "\xE2\xAA\x82", 7, 3}, + {"gesdotol", "\xE2\xAA\x84", 8, 3}, + {"gesl", "\xE2\x8B\x9B\xEF\xB8\x80", 4, 6}, + {"gesles", "\xE2\xAA\x94", 6, 3}, + {"gfr", "\xF0\x9D\x94\xA4", 3, 4}, + {"gg", "\xE2\x89\xAB", 2, 3}, + {"ggg", "\xE2\x8B\x99", 3, 3}, + {"gimel", "\xE2\x84\xB7", 5, 3}, + {"gjcy", "\xD1\x93", 4, 2}, + {"gl", "\xE2\x89\xB7", 2, 3}, + {"glE", "\xE2\xAA\x92", 3, 3}, + {"gla", "\xE2\xAA\xA5", 3, 3}, + {"glj", "\xE2\xAA\xA4", 3, 3}, + {"gnE", "\xE2\x89\xA9", 3, 3}, + {"gnap", "\xE2\xAA\x8A", 4, 3}, + {"gnapprox", "\xE2\xAA\x8A", 8, 3}, + {"gne", "\xE2\xAA\x88", 3, 3}, + {"gneq", "\xE2\xAA\x88", 4, 3}, + {"gneqq", "\xE2\x89\xA9", 5, 3}, + {"gnsim", "\xE2\x8B\xA7", 5, 3}, + {"gopf", "\xF0\x9D\x95\x98", 4, 4}, + {"grave", "\x60", 5, 1}, + {"gscr", "\xE2\x84\x8A", 4, 3}, + {"gsim", "\xE2\x89\xB3", 4, 3}, + {"gsime", "\xE2\xAA\x8E", 5, 3}, + {"gsiml", "\xE2\xAA\x90", 5, 3}, + {"gt", "\x3E", 2, 1}, + {"gtcc", "\xE2\xAA\xA7", 4, 3}, + {"gtcir", "\xE2\xA9\xBA", 5, 3}, + {"gtdot", "\xE2\x8B\x97", 5, 3}, + {"gtlPar", "\xE2\xA6\x95", 6, 3}, + {"gtquest", "\xE2\xA9\xBC", 7, 3}, + {"gtrapprox", "\xE2\xAA\x86", 9, 3}, + {"gtrarr", "\xE2\xA5\xB8", 6, 3}, + {"gtrdot", "\xE2\x8B\x97", 6, 3}, + {"gtreqless", "\xE2\x8B\x9B", 9, 3}, + {"gtreqqless", "\xE2\xAA\x8C", 10, 3}, + {"gtrless", "\xE2\x89\xB7", 7, 3}, + {"gtrsim", "\xE2\x89\xB3", 6, 3}, + {"gvertneqq", "\xE2\x89\xA9\xEF\xB8\x80", 9, 6}, + {"gvnE", "\xE2\x89\xA9\xEF\xB8\x80", 4, 6}, + {"hArr", "\xE2\x87\x94", 4, 3}, + {"hairsp", "\xE2\x80\x8A", 6, 3}, + {"half", "\xC2\xBD", 4, 2}, + {"hamilt", "\xE2\x84\x8B", 6, 3}, + {"hardcy", "\xD1\x8A", 6, 2}, + {"harr", "\xE2\x86\x94", 4, 3}, + {"harrcir", "\xE2\xA5\x88", 7, 3}, + {"harrw", "\xE2\x86\xAD", 5, 3}, + {"hbar", "\xE2\x84\x8F", 4, 3}, + {"hcirc", "\xC4\xA5", 5, 2}, + {"hearts", "\xE2\x99\xA5", 6, 3}, + {"heartsuit", "\xE2\x99\xA5", 9, 3}, + {"hellip", "\xE2\x80\xA6", 6, 3}, + {"hercon", "\xE2\x8A\xB9", 6, 3}, + {"hfr", "\xF0\x9D\x94\xA5", 3, 4}, + {"hksearow", "\xE2\xA4\xA5", 8, 3}, + {"hkswarow", "\xE2\xA4\xA6", 8, 3}, + {"hoarr", "\xE2\x87\xBF", 5, 3}, + {"homtht", "\xE2\x88\xBB", 6, 3}, + {"hookleftarrow", "\xE2\x86\xA9", 13, 3}, + {"hookrightarrow", "\xE2\x86\xAA", 14, 3}, + {"hopf", "\xF0\x9D\x95\x99", 4, 4}, + {"horbar", "\xE2\x80\x95", 6, 3}, + {"hscr", "\xF0\x9D\x92\xBD", 4, 4}, + {"hslash", "\xE2\x84\x8F", 6, 3}, + {"hstrok", "\xC4\xA7", 6, 2}, + {"hybull", "\xE2\x81\x83", 6, 3}, + {"hyphen", "\xE2\x80\x90", 6, 3}, + {"iacute", "\xC3\xAD", 6, 2}, + {"ic", "\xE2\x81\xA3", 2, 3}, + {"icirc", "\xC3\xAE", 5, 2}, + {"icy", "\xD0\xB8", 3, 2}, + {"iecy", "\xD0\xB5", 4, 2}, + {"iexcl", "\xC2\xA1", 5, 2}, + {"iff", "\xE2\x87\x94", 3, 3}, + {"ifr", "\xF0\x9D\x94\xA6", 3, 4}, + {"igrave", "\xC3\xAC", 6, 2}, + {"ii", "\xE2\x85\x88", 2, 3}, + {"iiiint", "\xE2\xA8\x8C", 6, 3}, + {"iiint", "\xE2\x88\xAD", 5, 3}, + {"iinfin", "\xE2\xA7\x9C", 6, 3}, + {"iiota", "\xE2\x84\xA9", 5, 3}, + {"ijlig", "\xC4\xB3", 5, 2}, + {"imacr", "\xC4\xAB", 5, 2}, + {"image", "\xE2\x84\x91", 5, 3}, + {"imagline", "\xE2\x84\x90", 8, 3}, + {"imagpart", "\xE2\x84\x91", 8, 3}, + {"imath", "\xC4\xB1", 5, 2}, + {"imof", "\xE2\x8A\xB7", 4, 3}, + {"imped", "\xC6\xB5", 5, 2}, + {"in", "\xE2\x88\x88", 2, 3}, + {"incare", "\xE2\x84\x85", 6, 3}, + {"infin", "\xE2\x88\x9E", 5, 3}, + {"infintie", "\xE2\xA7\x9D", 8, 3}, + {"inodot", "\xC4\xB1", 6, 2}, + {"int", "\xE2\x88\xAB", 3, 3}, + {"intcal", "\xE2\x8A\xBA", 6, 3}, + {"integers", "\xE2\x84\xA4", 8, 3}, + {"intercal", "\xE2\x8A\xBA", 8, 3}, + {"intlarhk", "\xE2\xA8\x97", 8, 3}, + {"intprod", "\xE2\xA8\xBC", 7, 3}, + {"iocy", "\xD1\x91", 4, 2}, + {"iogon", "\xC4\xAF", 5, 2}, + {"iopf", "\xF0\x9D\x95\x9A", 4, 4}, + {"iota", "\xCE\xB9", 4, 2}, + {"iprod", "\xE2\xA8\xBC", 5, 3}, + {"iquest", "\xC2\xBF", 6, 2}, + {"iscr", "\xF0\x9D\x92\xBE", 4, 4}, + {"isin", "\xE2\x88\x88", 4, 3}, + {"isinE", "\xE2\x8B\xB9", 5, 3}, + {"isindot", "\xE2\x8B\xB5", 7, 3}, + {"isins", "\xE2\x8B\xB4", 5, 3}, + {"isinsv", "\xE2\x8B\xB3", 6, 3}, + {"isinv", "\xE2\x88\x88", 5, 3}, + {"it", "\xE2\x81\xA2", 2, 3}, + {"itilde", "\xC4\xA9", 6, 2}, + {"iukcy", "\xD1\x96", 5, 2}, + {"iuml", "\xC3\xAF", 4, 2}, + {"jcirc", "\xC4\xB5", 5, 2}, + {"jcy", "\xD0\xB9", 3, 2}, + {"jfr", "\xF0\x9D\x94\xA7", 3, 4}, + {"jmath", "\xC8\xB7", 5, 2}, + {"jopf", "\xF0\x9D\x95\x9B", 4, 4}, + {"jscr", "\xF0\x9D\x92\xBF", 4, 4}, + {"jsercy", "\xD1\x98", 6, 2}, + {"jukcy", "\xD1\x94", 5, 2}, + {"kappa", "\xCE\xBA", 5, 2}, + {"kappav", "\xCF\xB0", 6, 2}, + {"kcedil", "\xC4\xB7", 6, 2}, + {"kcy", "\xD0\xBA", 3, 2}, + {"kfr", "\xF0\x9D\x94\xA8", 3, 4}, + {"kgreen", "\xC4\xB8", 6, 2}, + {"khcy", "\xD1\x85", 4, 2}, + {"kjcy", "\xD1\x9C", 4, 2}, + {"kopf", "\xF0\x9D\x95\x9C", 4, 4}, + {"kscr", "\xF0\x9D\x93\x80", 4, 4}, + {"lAarr", "\xE2\x87\x9A", 5, 3}, + {"lArr", "\xE2\x87\x90", 4, 3}, + {"lAtail", "\xE2\xA4\x9B", 6, 3}, + {"lBarr", "\xE2\xA4\x8E", 5, 3}, + {"lE", "\xE2\x89\xA6", 2, 3}, + {"lEg", "\xE2\xAA\x8B", 3, 3}, + {"lHar", "\xE2\xA5\xA2", 4, 3}, + {"lacute", "\xC4\xBA", 6, 2}, + {"laemptyv", "\xE2\xA6\xB4", 8, 3}, + {"lagran", "\xE2\x84\x92", 6, 3}, + {"lambda", "\xCE\xBB", 6, 2}, + {"lang", "\xE2\x9F\xA8", 4, 3}, + {"langd", "\xE2\xA6\x91", 5, 3}, + {"langle", "\xE2\x9F\xA8", 6, 3}, + {"lap", "\xE2\xAA\x85", 3, 3}, + {"laquo", "\xC2\xAB", 5, 2}, + {"larr", "\xE2\x86\x90", 4, 3}, + {"larrb", "\xE2\x87\xA4", 5, 3}, + {"larrbfs", "\xE2\xA4\x9F", 7, 3}, + {"larrfs", "\xE2\xA4\x9D", 6, 3}, + {"larrhk", "\xE2\x86\xA9", 6, 3}, + {"larrlp", "\xE2\x86\xAB", 6, 3}, + {"larrpl", "\xE2\xA4\xB9", 6, 3}, + {"larrsim", "\xE2\xA5\xB3", 7, 3}, + {"larrtl", "\xE2\x86\xA2", 6, 3}, + {"lat", "\xE2\xAA\xAB", 3, 3}, + {"latail", "\xE2\xA4\x99", 6, 3}, + {"late", "\xE2\xAA\xAD", 4, 3}, + {"lates", "\xE2\xAA\xAD\xEF\xB8\x80", 5, 6}, + {"lbarr", "\xE2\xA4\x8C", 5, 3}, + {"lbbrk", "\xE2\x9D\xB2", 5, 3}, + {"lbrace", "\x7B", 6, 1}, + {"lbrack", "\x5B", 6, 1}, + {"lbrke", "\xE2\xA6\x8B", 5, 3}, + {"lbrksld", "\xE2\xA6\x8F", 7, 3}, + {"lbrkslu", "\xE2\xA6\x8D", 7, 3}, + {"lcaron", "\xC4\xBE", 6, 2}, + {"lcedil", "\xC4\xBC", 6, 2}, + {"lceil", "\xE2\x8C\x88", 5, 3}, + {"lcub", "\x7B", 4, 1}, + {"lcy", "\xD0\xBB", 3, 2}, + {"ldca", "\xE2\xA4\xB6", 4, 3}, + {"ldquo", "\xE2\x80\x9C", 5, 3}, + {"ldquor", "\xE2\x80\x9E", 6, 3}, + {"ldrdhar", "\xE2\xA5\xA7", 7, 3}, + {"ldrushar", "\xE2\xA5\x8B", 8, 3}, + {"ldsh", "\xE2\x86\xB2", 4, 3}, + {"le", "\xE2\x89\xA4", 2, 3}, + {"leftarrow", "\xE2\x86\x90", 9, 3}, + {"leftarrowtail", "\xE2\x86\xA2", 13, 3}, + {"leftharpoondown", "\xE2\x86\xBD", 15, 3}, + {"leftharpoonup", "\xE2\x86\xBC", 13, 3}, + {"leftleftarrows", "\xE2\x87\x87", 14, 3}, + {"leftrightarrow", "\xE2\x86\x94", 14, 3}, + {"leftrightarrows", "\xE2\x87\x86", 15, 3}, + {"leftrightharpoons", "\xE2\x87\x8B", 17, 3}, + {"leftrightsquigarrow", "\xE2\x86\xAD", 19, 3}, + {"leftthreetimes", "\xE2\x8B\x8B", 14, 3}, + {"leg", "\xE2\x8B\x9A", 3, 3}, + {"leq", "\xE2\x89\xA4", 3, 3}, + {"leqq", "\xE2\x89\xA6", 4, 3}, + {"leqslant", "\xE2\xA9\xBD", 8, 3}, + {"les", "\xE2\xA9\xBD", 3, 3}, + {"lescc", "\xE2\xAA\xA8", 5, 3}, + {"lesdot", "\xE2\xA9\xBF", 6, 3}, + {"lesdoto", "\xE2\xAA\x81", 7, 3}, + {"lesdotor", "\xE2\xAA\x83", 8, 3}, + {"lesg", "\xE2\x8B\x9A\xEF\xB8\x80", 4, 6}, + {"lesges", "\xE2\xAA\x93", 6, 3}, + {"lessapprox", "\xE2\xAA\x85", 10, 3}, + {"lessdot", "\xE2\x8B\x96", 7, 3}, + {"lesseqgtr", "\xE2\x8B\x9A", 9, 3}, + {"lesseqqgtr", "\xE2\xAA\x8B", 10, 3}, + {"lessgtr", "\xE2\x89\xB6", 7, 3}, + {"lesssim", "\xE2\x89\xB2", 7, 3}, + {"lfisht", "\xE2\xA5\xBC", 6, 3}, + {"lfloor", "\xE2\x8C\x8A", 6, 3}, + {"lfr", "\xF0\x9D\x94\xA9", 3, 4}, + {"lg", "\xE2\x89\xB6", 2, 3}, + {"lgE", "\xE2\xAA\x91", 3, 3}, + {"lhard", "\xE2\x86\xBD", 5, 3}, + {"lharu", "\xE2\x86\xBC", 5, 3}, + {"lharul", "\xE2\xA5\xAA", 6, 3}, + {"lhblk", "\xE2\x96\x84", 5, 3}, + {"ljcy", "\xD1\x99", 4, 2}, + {"ll", "\xE2\x89\xAA", 2, 3}, + {"llarr", "\xE2\x87\x87", 5, 3}, + {"llcorner", "\xE2\x8C\x9E", 8, 3}, + {"llhard", "\xE2\xA5\xAB", 6, 3}, + {"lltri", "\xE2\x97\xBA", 5, 3}, + {"lmidot", "\xC5\x80", 6, 2}, + {"lmoust", "\xE2\x8E\xB0", 6, 3}, + {"lmoustache", "\xE2\x8E\xB0", 10, 3}, + {"lnE", "\xE2\x89\xA8", 3, 3}, + {"lnap", "\xE2\xAA\x89", 4, 3}, + {"lnapprox", "\xE2\xAA\x89", 8, 3}, + {"lne", "\xE2\xAA\x87", 3, 3}, + {"lneq", "\xE2\xAA\x87", 4, 3}, + {"lneqq", "\xE2\x89\xA8", 5, 3}, + {"lnsim", "\xE2\x8B\xA6", 5, 3}, + {"loang", "\xE2\x9F\xAC", 5, 3}, + {"loarr", "\xE2\x87\xBD", 5, 3}, + {"lobrk", "\xE2\x9F\xA6", 5, 3}, + {"longleftarrow", "\xE2\x9F\xB5", 13, 3}, + {"longleftrightarrow", "\xE2\x9F\xB7", 18, 3}, + {"longmapsto", "\xE2\x9F\xBC", 10, 3}, + {"longrightarrow", "\xE2\x9F\xB6", 14, 3}, + {"looparrowleft", "\xE2\x86\xAB", 13, 3}, + {"looparrowright", "\xE2\x86\xAC", 14, 3}, + {"lopar", "\xE2\xA6\x85", 5, 3}, + {"lopf", "\xF0\x9D\x95\x9D", 4, 4}, + {"loplus", "\xE2\xA8\xAD", 6, 3}, + {"lotimes", "\xE2\xA8\xB4", 7, 3}, + {"lowast", "\xE2\x88\x97", 6, 3}, + {"lowbar", "\x5F", 6, 1}, + {"loz", "\xE2\x97\x8A", 3, 3}, + {"lozenge", "\xE2\x97\x8A", 7, 3}, + {"lozf", "\xE2\xA7\xAB", 4, 3}, + {"lpar", "\x28", 4, 1}, + {"lparlt", "\xE2\xA6\x93", 6, 3}, + {"lrarr", "\xE2\x87\x86", 5, 3}, + {"lrcorner", "\xE2\x8C\x9F", 8, 3}, + {"lrhar", "\xE2\x87\x8B", 5, 3}, + {"lrhard", "\xE2\xA5\xAD", 6, 3}, + {"lrm", "\xE2\x80\x8E", 3, 3}, + {"lrtri", "\xE2\x8A\xBF", 5, 3}, + {"lsaquo", "\xE2\x80\xB9", 6, 3}, + {"lscr", "\xF0\x9D\x93\x81", 4, 4}, + {"lsh", "\xE2\x86\xB0", 3, 3}, + {"lsim", "\xE2\x89\xB2", 4, 3}, + {"lsime", "\xE2\xAA\x8D", 5, 3}, + {"lsimg", "\xE2\xAA\x8F", 5, 3}, + {"lsqb", "\x5B", 4, 1}, + {"lsquo", "\xE2\x80\x98", 5, 3}, + {"lsquor", "\xE2\x80\x9A", 6, 3}, + {"lstrok", "\xC5\x82", 6, 2}, + {"lt", "\x3C", 2, 1}, + {"ltcc", "\xE2\xAA\xA6", 4, 3}, + {"ltcir", "\xE2\xA9\xB9", 5, 3}, + {"ltdot", "\xE2\x8B\x96", 5, 3}, + {"lthree", "\xE2\x8B\x8B", 6, 3}, + {"ltimes", "\xE2\x8B\x89", 6, 3}, + {"ltlarr", "\xE2\xA5\xB6", 6, 3}, + {"ltquest", "\xE2\xA9\xBB", 7, 3}, + {"ltrPar", "\xE2\xA6\x96", 6, 3}, + {"ltri", "\xE2\x97\x83", 4, 3}, + {"ltrie", "\xE2\x8A\xB4", 5, 3}, + {"ltrif", "\xE2\x97\x82", 5, 3}, + {"lurdshar", "\xE2\xA5\x8A", 8, 3}, + {"luruhar", "\xE2\xA5\xA6", 7, 3}, + {"lvertneqq", "\xE2\x89\xA8\xEF\xB8\x80", 9, 6}, + {"lvnE", "\xE2\x89\xA8\xEF\xB8\x80", 4, 6}, + {"mDDot", "\xE2\x88\xBA", 5, 3}, + {"macr", "\xC2\xAF", 4, 2}, + {"male", "\xE2\x99\x82", 4, 3}, + {"malt", "\xE2\x9C\xA0", 4, 3}, + {"maltese", "\xE2\x9C\xA0", 7, 3}, + {"map", "\xE2\x86\xA6", 3, 3}, + {"mapsto", "\xE2\x86\xA6", 6, 3}, + {"mapstodown", "\xE2\x86\xA7", 10, 3}, + {"mapstoleft", "\xE2\x86\xA4", 10, 3}, + {"mapstoup", "\xE2\x86\xA5", 8, 3}, + {"marker", "\xE2\x96\xAE", 6, 3}, + {"mcomma", "\xE2\xA8\xA9", 6, 3}, + {"mcy", "\xD0\xBC", 3, 2}, + {"mdash", "\xE2\x80\x94", 5, 3}, + {"measuredangle", "\xE2\x88\xA1", 13, 3}, + {"mfr", "\xF0\x9D\x94\xAA", 3, 4}, + {"mho", "\xE2\x84\xA7", 3, 3}, + {"micro", "\xC2\xB5", 5, 2}, + {"mid", "\xE2\x88\xA3", 3, 3}, + {"midast", "\x2A", 6, 1}, + {"midcir", "\xE2\xAB\xB0", 6, 3}, + {"middot", "\xC2\xB7", 6, 2}, + {"minus", "\xE2\x88\x92", 5, 3}, + {"minusb", "\xE2\x8A\x9F", 6, 3}, + {"minusd", "\xE2\x88\xB8", 6, 3}, + {"minusdu", "\xE2\xA8\xAA", 7, 3}, + {"mlcp", "\xE2\xAB\x9B", 4, 3}, + {"mldr", "\xE2\x80\xA6", 4, 3}, + {"mnplus", "\xE2\x88\x93", 6, 3}, + {"models", "\xE2\x8A\xA7", 6, 3}, + {"mopf", "\xF0\x9D\x95\x9E", 4, 4}, + {"mp", "\xE2\x88\x93", 2, 3}, + {"mscr", "\xF0\x9D\x93\x82", 4, 4}, + {"mstpos", "\xE2\x88\xBE", 6, 3}, + {"mu", "\xCE\xBC", 2, 2}, + {"multimap", "\xE2\x8A\xB8", 8, 3}, + {"mumap", "\xE2\x8A\xB8", 5, 3}, + {"nGg", "\xE2\x8B\x99\xCC\xB8", 3, 5}, + {"nGt", "\xE2\x89\xAB\xE2\x83\x92", 3, 6}, + {"nGtv", "\xE2\x89\xAB\xCC\xB8", 4, 5}, + {"nLeftarrow", "\xE2\x87\x8D", 10, 3}, + {"nLeftrightarrow", "\xE2\x87\x8E", 15, 3}, + {"nLl", "\xE2\x8B\x98\xCC\xB8", 3, 5}, + {"nLt", "\xE2\x89\xAA\xE2\x83\x92", 3, 6}, + {"nLtv", "\xE2\x89\xAA\xCC\xB8", 4, 5}, + {"nRightarrow", "\xE2\x87\x8F", 11, 3}, + {"nVDash", "\xE2\x8A\xAF", 6, 3}, + {"nVdash", "\xE2\x8A\xAE", 6, 3}, + {"nabla", "\xE2\x88\x87", 5, 3}, + {"nacute", "\xC5\x84", 6, 2}, + {"nang", "\xE2\x88\xA0\xE2\x83\x92", 4, 6}, + {"nap", "\xE2\x89\x89", 3, 3}, + {"napE", "\xE2\xA9\xB0\xCC\xB8", 4, 5}, + {"napid", "\xE2\x89\x8B\xCC\xB8", 5, 5}, + {"napos", "\xC5\x89", 5, 2}, + {"napprox", "\xE2\x89\x89", 7, 3}, + {"natur", "\xE2\x99\xAE", 5, 3}, + {"natural", "\xE2\x99\xAE", 7, 3}, + {"naturals", "\xE2\x84\x95", 8, 3}, + {"nbsp", "\xC2\xA0", 4, 2}, + {"nbump", "\xE2\x89\x8E\xCC\xB8", 5, 5}, + {"nbumpe", "\xE2\x89\x8F\xCC\xB8", 6, 5}, + {"ncap", "\xE2\xA9\x83", 4, 3}, + {"ncaron", "\xC5\x88", 6, 2}, + {"ncedil", "\xC5\x86", 6, 2}, + {"ncong", "\xE2\x89\x87", 5, 3}, + {"ncongdot", "\xE2\xA9\xAD\xCC\xB8", 8, 5}, + {"ncup", "\xE2\xA9\x82", 4, 3}, + {"ncy", "\xD0\xBD", 3, 2}, + {"ndash", "\xE2\x80\x93", 5, 3}, + {"ne", "\xE2\x89\xA0", 2, 3}, + {"neArr", "\xE2\x87\x97", 5, 3}, + {"nearhk", "\xE2\xA4\xA4", 6, 3}, + {"nearr", "\xE2\x86\x97", 5, 3}, + {"nearrow", "\xE2\x86\x97", 7, 3}, + {"nedot", "\xE2\x89\x90\xCC\xB8", 5, 5}, + {"nequiv", "\xE2\x89\xA2", 6, 3}, + {"nesear", "\xE2\xA4\xA8", 6, 3}, + {"nesim", "\xE2\x89\x82\xCC\xB8", 5, 5}, + {"nexist", "\xE2\x88\x84", 6, 3}, + {"nexists", "\xE2\x88\x84", 7, 3}, + {"nfr", "\xF0\x9D\x94\xAB", 3, 4}, + {"ngE", "\xE2\x89\xA7\xCC\xB8", 3, 5}, + {"nge", "\xE2\x89\xB1", 3, 3}, + {"ngeq", "\xE2\x89\xB1", 4, 3}, + {"ngeqq", "\xE2\x89\xA7\xCC\xB8", 5, 5}, + {"ngeqslant", "\xE2\xA9\xBE\xCC\xB8", 9, 5}, + {"nges", "\xE2\xA9\xBE\xCC\xB8", 4, 5}, + {"ngsim", "\xE2\x89\xB5", 5, 3}, + {"ngt", "\xE2\x89\xAF", 3, 3}, + {"ngtr", "\xE2\x89\xAF", 4, 3}, + {"nhArr", "\xE2\x87\x8E", 5, 3}, + {"nharr", "\xE2\x86\xAE", 5, 3}, + {"nhpar", "\xE2\xAB\xB2", 5, 3}, + {"ni", "\xE2\x88\x8B", 2, 3}, + {"nis", "\xE2\x8B\xBC", 3, 3}, + {"nisd", "\xE2\x8B\xBA", 4, 3}, + {"niv", "\xE2\x88\x8B", 3, 3}, + {"njcy", "\xD1\x9A", 4, 2}, + {"nlArr", "\xE2\x87\x8D", 5, 3}, + {"nlE", "\xE2\x89\xA6\xCC\xB8", 3, 5}, + {"nlarr", "\xE2\x86\x9A", 5, 3}, + {"nldr", "\xE2\x80\xA5", 4, 3}, + {"nle", "\xE2\x89\xB0", 3, 3}, + {"nleftarrow", "\xE2\x86\x9A", 10, 3}, + {"nleftrightarrow", "\xE2\x86\xAE", 15, 3}, + {"nleq", "\xE2\x89\xB0", 4, 3}, + {"nleqq", "\xE2\x89\xA6\xCC\xB8", 5, 5}, + {"nleqslant", "\xE2\xA9\xBD\xCC\xB8", 9, 5}, + {"nles", "\xE2\xA9\xBD\xCC\xB8", 4, 5}, + {"nless", "\xE2\x89\xAE", 5, 3}, + {"nlsim", "\xE2\x89\xB4", 5, 3}, + {"nlt", "\xE2\x89\xAE", 3, 3}, + {"nltri", "\xE2\x8B\xAA", 5, 3}, + {"nltrie", "\xE2\x8B\xAC", 6, 3}, + {"nmid", "\xE2\x88\xA4", 4, 3}, + {"nopf", "\xF0\x9D\x95\x9F", 4, 4}, + {"not", "\xC2\xAC", 3, 2}, + {"notin", "\xE2\x88\x89", 5, 3}, + {"notinE", "\xE2\x8B\xB9\xCC\xB8", 6, 5}, + {"notindot", "\xE2\x8B\xB5\xCC\xB8", 8, 5}, + {"notinva", "\xE2\x88\x89", 7, 3}, + {"notinvb", "\xE2\x8B\xB7", 7, 3}, + {"notinvc", "\xE2\x8B\xB6", 7, 3}, + {"notni", "\xE2\x88\x8C", 5, 3}, + {"notniva", "\xE2\x88\x8C", 7, 3}, + {"notnivb", "\xE2\x8B\xBE", 7, 3}, + {"notnivc", "\xE2\x8B\xBD", 7, 3}, + {"npar", "\xE2\x88\xA6", 4, 3}, + {"nparallel", "\xE2\x88\xA6", 9, 3}, + {"nparsl", "\xE2\xAB\xBD\xE2\x83\xA5", 6, 6}, + {"npart", "\xE2\x88\x82\xCC\xB8", 5, 5}, + {"npolint", "\xE2\xA8\x94", 7, 3}, + {"npr", "\xE2\x8A\x80", 3, 3}, + {"nprcue", "\xE2\x8B\xA0", 6, 3}, + {"npre", "\xE2\xAA\xAF\xCC\xB8", 4, 5}, + {"nprec", "\xE2\x8A\x80", 5, 3}, + {"npreceq", "\xE2\xAA\xAF\xCC\xB8", 7, 5}, + {"nrArr", "\xE2\x87\x8F", 5, 3}, + {"nrarr", "\xE2\x86\x9B", 5, 3}, + {"nrarrc", "\xE2\xA4\xB3\xCC\xB8", 6, 5}, + {"nrarrw", "\xE2\x86\x9D\xCC\xB8", 6, 5}, + {"nrightarrow", "\xE2\x86\x9B", 11, 3}, + {"nrtri", "\xE2\x8B\xAB", 5, 3}, + {"nrtrie", "\xE2\x8B\xAD", 6, 3}, + {"nsc", "\xE2\x8A\x81", 3, 3}, + {"nsccue", "\xE2\x8B\xA1", 6, 3}, + {"nsce", "\xE2\xAA\xB0\xCC\xB8", 4, 5}, + {"nscr", "\xF0\x9D\x93\x83", 4, 4}, + {"nshortmid", "\xE2\x88\xA4", 9, 3}, + {"nshortparallel", "\xE2\x88\xA6", 14, 3}, + {"nsim", "\xE2\x89\x81", 4, 3}, + {"nsime", "\xE2\x89\x84", 5, 3}, + {"nsimeq", "\xE2\x89\x84", 6, 3}, + {"nsmid", "\xE2\x88\xA4", 5, 3}, + {"nspar", "\xE2\x88\xA6", 5, 3}, + {"nsqsube", "\xE2\x8B\xA2", 7, 3}, + {"nsqsupe", "\xE2\x8B\xA3", 7, 3}, + {"nsub", "\xE2\x8A\x84", 4, 3}, + {"nsubE", "\xE2\xAB\x85\xCC\xB8", 5, 5}, + {"nsube", "\xE2\x8A\x88", 5, 3}, + {"nsubset", "\xE2\x8A\x82\xE2\x83\x92", 7, 6}, + {"nsubseteq", "\xE2\x8A\x88", 9, 3}, + {"nsubseteqq", "\xE2\xAB\x85\xCC\xB8", 10, 5}, + {"nsucc", "\xE2\x8A\x81", 5, 3}, + {"nsucceq", "\xE2\xAA\xB0\xCC\xB8", 7, 5}, + {"nsup", "\xE2\x8A\x85", 4, 3}, + {"nsupE", "\xE2\xAB\x86\xCC\xB8", 5, 5}, + {"nsupe", "\xE2\x8A\x89", 5, 3}, + {"nsupset", "\xE2\x8A\x83\xE2\x83\x92", 7, 6}, + {"nsupseteq", "\xE2\x8A\x89", 9, 3}, + {"nsupseteqq", "\xE2\xAB\x86\xCC\xB8", 10, 5}, + {"ntgl", "\xE2\x89\xB9", 4, 3}, + {"ntilde", "\xC3\xB1", 6, 2}, + {"ntlg", "\xE2\x89\xB8", 4, 3}, + {"ntriangleleft", "\xE2\x8B\xAA", 13, 3}, + {"ntrianglelefteq", "\xE2\x8B\xAC", 15, 3}, + {"ntriangleright", "\xE2\x8B\xAB", 14, 3}, + {"ntrianglerighteq", "\xE2\x8B\xAD", 16, 3}, + {"nu", "\xCE\xBD", 2, 2}, + {"num", "\x23", 3, 1}, + {"numero", "\xE2\x84\x96", 6, 3}, + {"numsp", "\xE2\x80\x87", 5, 3}, + {"nvDash", "\xE2\x8A\xAD", 6, 3}, + {"nvHarr", "\xE2\xA4\x84", 6, 3}, + {"nvap", "\xE2\x89\x8D\xE2\x83\x92", 4, 6}, + {"nvdash", "\xE2\x8A\xAC", 6, 3}, + {"nvge", "\xE2\x89\xA5\xE2\x83\x92", 4, 6}, + {"nvgt", "\x3E\xE2\x83\x92", 4, 4}, + {"nvinfin", "\xE2\xA7\x9E", 7, 3}, + {"nvlArr", "\xE2\xA4\x82", 6, 3}, + {"nvle", "\xE2\x89\xA4\xE2\x83\x92", 4, 6}, + {"nvlt", "\x3C\xE2\x83\x92", 4, 4}, + {"nvltrie", "\xE2\x8A\xB4\xE2\x83\x92", 7, 6}, + {"nvrArr", "\xE2\xA4\x83", 6, 3}, + {"nvrtrie", "\xE2\x8A\xB5\xE2\x83\x92", 7, 6}, + {"nvsim", "\xE2\x88\xBC\xE2\x83\x92", 5, 6}, + {"nwArr", "\xE2\x87\x96", 5, 3}, + {"nwarhk", "\xE2\xA4\xA3", 6, 3}, + {"nwarr", "\xE2\x86\x96", 5, 3}, + {"nwarrow", "\xE2\x86\x96", 7, 3}, + {"nwnear", "\xE2\xA4\xA7", 6, 3}, + {"oS", "\xE2\x93\x88", 2, 3}, + {"oacute", "\xC3\xB3", 6, 2}, + {"oast", "\xE2\x8A\x9B", 4, 3}, + {"ocir", "\xE2\x8A\x9A", 4, 3}, + {"ocirc", "\xC3\xB4", 5, 2}, + {"ocy", "\xD0\xBE", 3, 2}, + {"odash", "\xE2\x8A\x9D", 5, 3}, + {"odblac", "\xC5\x91", 6, 2}, + {"odiv", "\xE2\xA8\xB8", 4, 3}, + {"odot", "\xE2\x8A\x99", 4, 3}, + {"odsold", "\xE2\xA6\xBC", 6, 3}, + {"oelig", "\xC5\x93", 5, 2}, + {"ofcir", "\xE2\xA6\xBF", 5, 3}, + {"ofr", "\xF0\x9D\x94\xAC", 3, 4}, + {"ogon", "\xCB\x9B", 4, 2}, + {"ograve", "\xC3\xB2", 6, 2}, + {"ogt", "\xE2\xA7\x81", 3, 3}, + {"ohbar", "\xE2\xA6\xB5", 5, 3}, + {"ohm", "\xCE\xA9", 3, 2}, + {"oint", "\xE2\x88\xAE", 4, 3}, + {"olarr", "\xE2\x86\xBA", 5, 3}, + {"olcir", "\xE2\xA6\xBE", 5, 3}, + {"olcross", "\xE2\xA6\xBB", 7, 3}, + {"oline", "\xE2\x80\xBE", 5, 3}, + {"olt", "\xE2\xA7\x80", 3, 3}, + {"omacr", "\xC5\x8D", 5, 2}, + {"omega", "\xCF\x89", 5, 2}, + {"omicron", "\xCE\xBF", 7, 2}, + {"omid", "\xE2\xA6\xB6", 4, 3}, + {"ominus", "\xE2\x8A\x96", 6, 3}, + {"oopf", "\xF0\x9D\x95\xA0", 4, 4}, + {"opar", "\xE2\xA6\xB7", 4, 3}, + {"operp", "\xE2\xA6\xB9", 5, 3}, + {"oplus", "\xE2\x8A\x95", 5, 3}, + {"or", "\xE2\x88\xA8", 2, 3}, + {"orarr", "\xE2\x86\xBB", 5, 3}, + {"ord", "\xE2\xA9\x9D", 3, 3}, + {"order", "\xE2\x84\xB4", 5, 3}, + {"orderof", "\xE2\x84\xB4", 7, 3}, + {"ordf", "\xC2\xAA", 4, 2}, + {"ordm", "\xC2\xBA", 4, 2}, + {"origof", "\xE2\x8A\xB6", 6, 3}, + {"oror", "\xE2\xA9\x96", 4, 3}, + {"orslope", "\xE2\xA9\x97", 7, 3}, + {"orv", "\xE2\xA9\x9B", 3, 3}, + {"oscr", "\xE2\x84\xB4", 4, 3}, + {"oslash", "\xC3\xB8", 6, 2}, + {"osol", "\xE2\x8A\x98", 4, 3}, + {"otilde", "\xC3\xB5", 6, 2}, + {"otimes", "\xE2\x8A\x97", 6, 3}, + {"otimesas", "\xE2\xA8\xB6", 8, 3}, + {"ouml", "\xC3\xB6", 4, 2}, + {"ovbar", "\xE2\x8C\xBD", 5, 3}, + {"par", "\xE2\x88\xA5", 3, 3}, + {"para", "\xC2\xB6", 4, 2}, + {"parallel", "\xE2\x88\xA5", 8, 3}, + {"parsim", "\xE2\xAB\xB3", 6, 3}, + {"parsl", "\xE2\xAB\xBD", 5, 3}, + {"part", "\xE2\x88\x82", 4, 3}, + {"pcy", "\xD0\xBF", 3, 2}, + {"percnt", "\x25", 6, 1}, + {"period", "\x2E", 6, 1}, + {"permil", "\xE2\x80\xB0", 6, 3}, + {"perp", "\xE2\x8A\xA5", 4, 3}, + {"pertenk", "\xE2\x80\xB1", 7, 3}, + {"pfr", "\xF0\x9D\x94\xAD", 3, 4}, + {"phi", "\xCF\x86", 3, 2}, + {"phiv", "\xCF\x95", 4, 2}, + {"phmmat", "\xE2\x84\xB3", 6, 3}, + {"phone", "\xE2\x98\x8E", 5, 3}, + {"pi", "\xCF\x80", 2, 2}, + {"pitchfork", "\xE2\x8B\x94", 9, 3}, + {"piv", "\xCF\x96", 3, 2}, + {"planck", "\xE2\x84\x8F", 6, 3}, + {"planckh", "\xE2\x84\x8E", 7, 3}, + {"plankv", "\xE2\x84\x8F", 6, 3}, + {"plus", "\x2B", 4, 1}, + {"plusacir", "\xE2\xA8\xA3", 8, 3}, + {"plusb", "\xE2\x8A\x9E", 5, 3}, + {"pluscir", "\xE2\xA8\xA2", 7, 3}, + {"plusdo", "\xE2\x88\x94", 6, 3}, + {"plusdu", "\xE2\xA8\xA5", 6, 3}, + {"pluse", "\xE2\xA9\xB2", 5, 3}, + {"plusmn", "\xC2\xB1", 6, 2}, + {"plussim", "\xE2\xA8\xA6", 7, 3}, + {"plustwo", "\xE2\xA8\xA7", 7, 3}, + {"pm", "\xC2\xB1", 2, 2}, + {"pointint", "\xE2\xA8\x95", 8, 3}, + {"popf", "\xF0\x9D\x95\xA1", 4, 4}, + {"pound", "\xC2\xA3", 5, 2}, + {"pr", "\xE2\x89\xBA", 2, 3}, + {"prE", "\xE2\xAA\xB3", 3, 3}, + {"prap", "\xE2\xAA\xB7", 4, 3}, + {"prcue", "\xE2\x89\xBC", 5, 3}, + {"pre", "\xE2\xAA\xAF", 3, 3}, + {"prec", "\xE2\x89\xBA", 4, 3}, + {"precapprox", "\xE2\xAA\xB7", 10, 3}, + {"preccurlyeq", "\xE2\x89\xBC", 11, 3}, + {"preceq", "\xE2\xAA\xAF", 6, 3}, + {"precnapprox", "\xE2\xAA\xB9", 11, 3}, + {"precneqq", "\xE2\xAA\xB5", 8, 3}, + {"precnsim", "\xE2\x8B\xA8", 8, 3}, + {"precsim", "\xE2\x89\xBE", 7, 3}, + {"prime", "\xE2\x80\xB2", 5, 3}, + {"primes", "\xE2\x84\x99", 6, 3}, + {"prnE", "\xE2\xAA\xB5", 4, 3}, + {"prnap", "\xE2\xAA\xB9", 5, 3}, + {"prnsim", "\xE2\x8B\xA8", 6, 3}, + {"prod", "\xE2\x88\x8F", 4, 3}, + {"profalar", "\xE2\x8C\xAE", 8, 3}, + {"profline", "\xE2\x8C\x92", 8, 3}, + {"profsurf", "\xE2\x8C\x93", 8, 3}, + {"prop", "\xE2\x88\x9D", 4, 3}, + {"propto", "\xE2\x88\x9D", 6, 3}, + {"prsim", "\xE2\x89\xBE", 5, 3}, + {"prurel", "\xE2\x8A\xB0", 6, 3}, + {"pscr", "\xF0\x9D\x93\x85", 4, 4}, + {"psi", "\xCF\x88", 3, 2}, + {"puncsp", "\xE2\x80\x88", 6, 3}, + {"qfr", "\xF0\x9D\x94\xAE", 3, 4}, + {"qint", "\xE2\xA8\x8C", 4, 3}, + {"qopf", "\xF0\x9D\x95\xA2", 4, 4}, + {"qprime", "\xE2\x81\x97", 6, 3}, + {"qscr", "\xF0\x9D\x93\x86", 4, 4}, + {"quaternions", "\xE2\x84\x8D", 11, 3}, + {"quatint", "\xE2\xA8\x96", 7, 3}, + {"quest", "\x3F", 5, 1}, + {"questeq", "\xE2\x89\x9F", 7, 3}, + {"quot", "\x22", 4, 1}, + {"rAarr", "\xE2\x87\x9B", 5, 3}, + {"rArr", "\xE2\x87\x92", 4, 3}, + {"rAtail", "\xE2\xA4\x9C", 6, 3}, + {"rBarr", "\xE2\xA4\x8F", 5, 3}, + {"rHar", "\xE2\xA5\xA4", 4, 3}, + {"race", "\xE2\x88\xBD\xCC\xB1", 4, 5}, + {"racute", "\xC5\x95", 6, 2}, + {"radic", "\xE2\x88\x9A", 5, 3}, + {"raemptyv", "\xE2\xA6\xB3", 8, 3}, + {"rang", "\xE2\x9F\xA9", 4, 3}, + {"rangd", "\xE2\xA6\x92", 5, 3}, + {"range", "\xE2\xA6\xA5", 5, 3}, + {"rangle", "\xE2\x9F\xA9", 6, 3}, + {"raquo", "\xC2\xBB", 5, 2}, + {"rarr", "\xE2\x86\x92", 4, 3}, + {"rarrap", "\xE2\xA5\xB5", 6, 3}, + {"rarrb", "\xE2\x87\xA5", 5, 3}, + {"rarrbfs", "\xE2\xA4\xA0", 7, 3}, + {"rarrc", "\xE2\xA4\xB3", 5, 3}, + {"rarrfs", "\xE2\xA4\x9E", 6, 3}, + {"rarrhk", "\xE2\x86\xAA", 6, 3}, + {"rarrlp", "\xE2\x86\xAC", 6, 3}, + {"rarrpl", "\xE2\xA5\x85", 6, 3}, + {"rarrsim", "\xE2\xA5\xB4", 7, 3}, + {"rarrtl", "\xE2\x86\xA3", 6, 3}, + {"rarrw", "\xE2\x86\x9D", 5, 3}, + {"ratail", "\xE2\xA4\x9A", 6, 3}, + {"ratio", "\xE2\x88\xB6", 5, 3}, + {"rationals", "\xE2\x84\x9A", 9, 3}, + {"rbarr", "\xE2\xA4\x8D", 5, 3}, + {"rbbrk", "\xE2\x9D\xB3", 5, 3}, + {"rbrace", "\x7D", 6, 1}, + {"rbrack", "\x5D", 6, 1}, + {"rbrke", "\xE2\xA6\x8C", 5, 3}, + {"rbrksld", "\xE2\xA6\x8E", 7, 3}, + {"rbrkslu", "\xE2\xA6\x90", 7, 3}, + {"rcaron", "\xC5\x99", 6, 2}, + {"rcedil", "\xC5\x97", 6, 2}, + {"rceil", "\xE2\x8C\x89", 5, 3}, + {"rcub", "\x7D", 4, 1}, + {"rcy", "\xD1\x80", 3, 2}, + {"rdca", "\xE2\xA4\xB7", 4, 3}, + {"rdldhar", "\xE2\xA5\xA9", 7, 3}, + {"rdquo", "\xE2\x80\x9D", 5, 3}, + {"rdquor", "\xE2\x80\x9D", 6, 3}, + {"rdsh", "\xE2\x86\xB3", 4, 3}, + {"real", "\xE2\x84\x9C", 4, 3}, + {"realine", "\xE2\x84\x9B", 7, 3}, + {"realpart", "\xE2\x84\x9C", 8, 3}, + {"reals", "\xE2\x84\x9D", 5, 3}, + {"rect", "\xE2\x96\xAD", 4, 3}, + {"reg", "\xC2\xAE", 3, 2}, + {"rfisht", "\xE2\xA5\xBD", 6, 3}, + {"rfloor", "\xE2\x8C\x8B", 6, 3}, + {"rfr", "\xF0\x9D\x94\xAF", 3, 4}, + {"rhard", "\xE2\x87\x81", 5, 3}, + {"rharu", "\xE2\x87\x80", 5, 3}, + {"rharul", "\xE2\xA5\xAC", 6, 3}, + {"rho", "\xCF\x81", 3, 2}, + {"rhov", "\xCF\xB1", 4, 2}, + {"rightarrow", "\xE2\x86\x92", 10, 3}, + {"rightarrowtail", "\xE2\x86\xA3", 14, 3}, + {"rightharpoondown", "\xE2\x87\x81", 16, 3}, + {"rightharpoonup", "\xE2\x87\x80", 14, 3}, + {"rightleftarrows", "\xE2\x87\x84", 15, 3}, + {"rightleftharpoons", "\xE2\x87\x8C", 17, 3}, + {"rightrightarrows", "\xE2\x87\x89", 16, 3}, + {"rightsquigarrow", "\xE2\x86\x9D", 15, 3}, + {"rightthreetimes", "\xE2\x8B\x8C", 15, 3}, + {"ring", "\xCB\x9A", 4, 2}, + {"risingdotseq", "\xE2\x89\x93", 12, 3}, + {"rlarr", "\xE2\x87\x84", 5, 3}, + {"rlhar", "\xE2\x87\x8C", 5, 3}, + {"rlm", "\xE2\x80\x8F", 3, 3}, + {"rmoust", "\xE2\x8E\xB1", 6, 3}, + {"rmoustache", "\xE2\x8E\xB1", 10, 3}, + {"rnmid", "\xE2\xAB\xAE", 5, 3}, + {"roang", "\xE2\x9F\xAD", 5, 3}, + {"roarr", "\xE2\x87\xBE", 5, 3}, + {"robrk", "\xE2\x9F\xA7", 5, 3}, + {"ropar", "\xE2\xA6\x86", 5, 3}, + {"ropf", "\xF0\x9D\x95\xA3", 4, 4}, + {"roplus", "\xE2\xA8\xAE", 6, 3}, + {"rotimes", "\xE2\xA8\xB5", 7, 3}, + {"rpar", "\x29", 4, 1}, + {"rpargt", "\xE2\xA6\x94", 6, 3}, + {"rppolint", "\xE2\xA8\x92", 8, 3}, + {"rrarr", "\xE2\x87\x89", 5, 3}, + {"rsaquo", "\xE2\x80\xBA", 6, 3}, + {"rscr", "\xF0\x9D\x93\x87", 4, 4}, + {"rsh", "\xE2\x86\xB1", 3, 3}, + {"rsqb", "\x5D", 4, 1}, + {"rsquo", "\xE2\x80\x99", 5, 3}, + {"rsquor", "\xE2\x80\x99", 6, 3}, + {"rthree", "\xE2\x8B\x8C", 6, 3}, + {"rtimes", "\xE2\x8B\x8A", 6, 3}, + {"rtri", "\xE2\x96\xB9", 4, 3}, + {"rtrie", "\xE2\x8A\xB5", 5, 3}, + {"rtrif", "\xE2\x96\xB8", 5, 3}, + {"rtriltri", "\xE2\xA7\x8E", 8, 3}, + {"ruluhar", "\xE2\xA5\xA8", 7, 3}, + {"rx", "\xE2\x84\x9E", 2, 3}, + {"sacute", "\xC5\x9B", 6, 2}, + {"sbquo", "\xE2\x80\x9A", 5, 3}, + {"sc", "\xE2\x89\xBB", 2, 3}, + {"scE", "\xE2\xAA\xB4", 3, 3}, + {"scap", "\xE2\xAA\xB8", 4, 3}, + {"scaron", "\xC5\xA1", 6, 2}, + {"sccue", "\xE2\x89\xBD", 5, 3}, + {"sce", "\xE2\xAA\xB0", 3, 3}, + {"scedil", "\xC5\x9F", 6, 2}, + {"scirc", "\xC5\x9D", 5, 2}, + {"scnE", "\xE2\xAA\xB6", 4, 3}, + {"scnap", "\xE2\xAA\xBA", 5, 3}, + {"scnsim", "\xE2\x8B\xA9", 6, 3}, + {"scpolint", "\xE2\xA8\x93", 8, 3}, + {"scsim", "\xE2\x89\xBF", 5, 3}, + {"scy", "\xD1\x81", 3, 2}, + {"sdot", "\xE2\x8B\x85", 4, 3}, + {"sdotb", "\xE2\x8A\xA1", 5, 3}, + {"sdote", "\xE2\xA9\xA6", 5, 3}, + {"seArr", "\xE2\x87\x98", 5, 3}, + {"searhk", "\xE2\xA4\xA5", 6, 3}, + {"searr", "\xE2\x86\x98", 5, 3}, + {"searrow", "\xE2\x86\x98", 7, 3}, + {"sect", "\xC2\xA7", 4, 2}, + {"semi", "\x3B", 4, 1}, + {"seswar", "\xE2\xA4\xA9", 6, 3}, + {"setminus", "\xE2\x88\x96", 8, 3}, + {"setmn", "\xE2\x88\x96", 5, 3}, + {"sext", "\xE2\x9C\xB6", 4, 3}, + {"sfr", "\xF0\x9D\x94\xB0", 3, 4}, + {"sfrown", "\xE2\x8C\xA2", 6, 3}, + {"sharp", "\xE2\x99\xAF", 5, 3}, + {"shchcy", "\xD1\x89", 6, 2}, + {"shcy", "\xD1\x88", 4, 2}, + {"shortmid", "\xE2\x88\xA3", 8, 3}, + {"shortparallel", "\xE2\x88\xA5", 13, 3}, + {"shy", "\xC2\xAD", 3, 2}, + {"sigma", "\xCF\x83", 5, 2}, + {"sigmaf", "\xCF\x82", 6, 2}, + {"sigmav", "\xCF\x82", 6, 2}, + {"sim", "\xE2\x88\xBC", 3, 3}, + {"simdot", "\xE2\xA9\xAA", 6, 3}, + {"sime", "\xE2\x89\x83", 4, 3}, + {"simeq", "\xE2\x89\x83", 5, 3}, + {"simg", "\xE2\xAA\x9E", 4, 3}, + {"simgE", "\xE2\xAA\xA0", 5, 3}, + {"siml", "\xE2\xAA\x9D", 4, 3}, + {"simlE", "\xE2\xAA\x9F", 5, 3}, + {"simne", "\xE2\x89\x86", 5, 3}, + {"simplus", "\xE2\xA8\xA4", 7, 3}, + {"simrarr", "\xE2\xA5\xB2", 7, 3}, + {"slarr", "\xE2\x86\x90", 5, 3}, + {"smallsetminus", "\xE2\x88\x96", 13, 3}, + {"smashp", "\xE2\xA8\xB3", 6, 3}, + {"smeparsl", "\xE2\xA7\xA4", 8, 3}, + {"smid", "\xE2\x88\xA3", 4, 3}, + {"smile", "\xE2\x8C\xA3", 5, 3}, + {"smt", "\xE2\xAA\xAA", 3, 3}, + {"smte", "\xE2\xAA\xAC", 4, 3}, + {"smtes", "\xE2\xAA\xAC\xEF\xB8\x80", 5, 6}, + {"softcy", "\xD1\x8C", 6, 2}, + {"sol", "\x2F", 3, 1}, + {"solb", "\xE2\xA7\x84", 4, 3}, + {"solbar", "\xE2\x8C\xBF", 6, 3}, + {"sopf", "\xF0\x9D\x95\xA4", 4, 4}, + {"spades", "\xE2\x99\xA0", 6, 3}, + {"spadesuit", "\xE2\x99\xA0", 9, 3}, + {"spar", "\xE2\x88\xA5", 4, 3}, + {"sqcap", "\xE2\x8A\x93", 5, 3}, + {"sqcaps", "\xE2\x8A\x93\xEF\xB8\x80", 6, 6}, + {"sqcup", "\xE2\x8A\x94", 5, 3}, + {"sqcups", "\xE2\x8A\x94\xEF\xB8\x80", 6, 6}, + {"sqsub", "\xE2\x8A\x8F", 5, 3}, + {"sqsube", "\xE2\x8A\x91", 6, 3}, + {"sqsubset", "\xE2\x8A\x8F", 8, 3}, + {"sqsubseteq", "\xE2\x8A\x91", 10, 3}, + {"sqsup", "\xE2\x8A\x90", 5, 3}, + {"sqsupe", "\xE2\x8A\x92", 6, 3}, + {"sqsupset", "\xE2\x8A\x90", 8, 3}, + {"sqsupseteq", "\xE2\x8A\x92", 10, 3}, + {"squ", "\xE2\x96\xA1", 3, 3}, + {"square", "\xE2\x96\xA1", 6, 3}, + {"squarf", "\xE2\x96\xAA", 6, 3}, + {"squf", "\xE2\x96\xAA", 4, 3}, + {"srarr", "\xE2\x86\x92", 5, 3}, + {"sscr", "\xF0\x9D\x93\x88", 4, 4}, + {"ssetmn", "\xE2\x88\x96", 6, 3}, + {"ssmile", "\xE2\x8C\xA3", 6, 3}, + {"sstarf", "\xE2\x8B\x86", 6, 3}, + {"star", "\xE2\x98\x86", 4, 3}, + {"starf", "\xE2\x98\x85", 5, 3}, + {"straightepsilon", "\xCF\xB5", 15, 2}, + {"straightphi", "\xCF\x95", 11, 2}, + {"strns", "\xC2\xAF", 5, 2}, + {"sub", "\xE2\x8A\x82", 3, 3}, + {"subE", "\xE2\xAB\x85", 4, 3}, + {"subdot", "\xE2\xAA\xBD", 6, 3}, + {"sube", "\xE2\x8A\x86", 4, 3}, + {"subedot", "\xE2\xAB\x83", 7, 3}, + {"submult", "\xE2\xAB\x81", 7, 3}, + {"subnE", "\xE2\xAB\x8B", 5, 3}, + {"subne", "\xE2\x8A\x8A", 5, 3}, + {"subplus", "\xE2\xAA\xBF", 7, 3}, + {"subrarr", "\xE2\xA5\xB9", 7, 3}, + {"subset", "\xE2\x8A\x82", 6, 3}, + {"subseteq", "\xE2\x8A\x86", 8, 3}, + {"subseteqq", "\xE2\xAB\x85", 9, 3}, + {"subsetneq", "\xE2\x8A\x8A", 9, 3}, + {"subsetneqq", "\xE2\xAB\x8B", 10, 3}, + {"subsim", "\xE2\xAB\x87", 6, 3}, + {"subsub", "\xE2\xAB\x95", 6, 3}, + {"subsup", "\xE2\xAB\x93", 6, 3}, + {"succ", "\xE2\x89\xBB", 4, 3}, + {"succapprox", "\xE2\xAA\xB8", 10, 3}, + {"succcurlyeq", "\xE2\x89\xBD", 11, 3}, + {"succeq", "\xE2\xAA\xB0", 6, 3}, + {"succnapprox", "\xE2\xAA\xBA", 11, 3}, + {"succneqq", "\xE2\xAA\xB6", 8, 3}, + {"succnsim", "\xE2\x8B\xA9", 8, 3}, + {"succsim", "\xE2\x89\xBF", 7, 3}, + {"sum", "\xE2\x88\x91", 3, 3}, + {"sung", "\xE2\x99\xAA", 4, 3}, + {"sup", "\xE2\x8A\x83", 3, 3}, + {"sup1", "\xC2\xB9", 4, 2}, + {"sup2", "\xC2\xB2", 4, 2}, + {"sup3", "\xC2\xB3", 4, 2}, + {"supE", "\xE2\xAB\x86", 4, 3}, + {"supdot", "\xE2\xAA\xBE", 6, 3}, + {"supdsub", "\xE2\xAB\x98", 7, 3}, + {"supe", "\xE2\x8A\x87", 4, 3}, + {"supedot", "\xE2\xAB\x84", 7, 3}, + {"suphsol", "\xE2\x9F\x89", 7, 3}, + {"suphsub", "\xE2\xAB\x97", 7, 3}, + {"suplarr", "\xE2\xA5\xBB", 7, 3}, + {"supmult", "\xE2\xAB\x82", 7, 3}, + {"supnE", "\xE2\xAB\x8C", 5, 3}, + {"supne", "\xE2\x8A\x8B", 5, 3}, + {"supplus", "\xE2\xAB\x80", 7, 3}, + {"supset", "\xE2\x8A\x83", 6, 3}, + {"supseteq", "\xE2\x8A\x87", 8, 3}, + {"supseteqq", "\xE2\xAB\x86", 9, 3}, + {"supsetneq", "\xE2\x8A\x8B", 9, 3}, + {"supsetneqq", "\xE2\xAB\x8C", 10, 3}, + {"supsim", "\xE2\xAB\x88", 6, 3}, + {"supsub", "\xE2\xAB\x94", 6, 3}, + {"supsup", "\xE2\xAB\x96", 6, 3}, + {"swArr", "\xE2\x87\x99", 5, 3}, + {"swarhk", "\xE2\xA4\xA6", 6, 3}, + {"swarr", "\xE2\x86\x99", 5, 3}, + {"swarrow", "\xE2\x86\x99", 7, 3}, + {"swnwar", "\xE2\xA4\xAA", 6, 3}, + {"szlig", "\xC3\x9F", 5, 2}, + {"target", "\xE2\x8C\x96", 6, 3}, + {"tau", "\xCF\x84", 3, 2}, + {"tbrk", "\xE2\x8E\xB4", 4, 3}, + {"tcaron", "\xC5\xA5", 6, 2}, + {"tcedil", "\xC5\xA3", 6, 2}, + {"tcy", "\xD1\x82", 3, 2}, + {"tdot", "\xE2\x83\x9B", 4, 3}, + {"telrec", "\xE2\x8C\x95", 6, 3}, + {"tfr", "\xF0\x9D\x94\xB1", 3, 4}, + {"there4", "\xE2\x88\xB4", 6, 3}, + {"therefore", "\xE2\x88\xB4", 9, 3}, + {"theta", "\xCE\xB8", 5, 2}, + {"thetasym", "\xCF\x91", 8, 2}, + {"thetav", "\xCF\x91", 6, 2}, + {"thickapprox", "\xE2\x89\x88", 11, 3}, + {"thicksim", "\xE2\x88\xBC", 8, 3}, + {"thinsp", "\xE2\x80\x89", 6, 3}, + {"thkap", "\xE2\x89\x88", 5, 3}, + {"thksim", "\xE2\x88\xBC", 6, 3}, + {"thorn", "\xC3\xBE", 5, 2}, + {"tilde", "\xCB\x9C", 5, 2}, + {"times", "\xC3\x97", 5, 2}, + {"timesb", "\xE2\x8A\xA0", 6, 3}, + {"timesbar", "\xE2\xA8\xB1", 8, 3}, + {"timesd", "\xE2\xA8\xB0", 6, 3}, + {"tint", "\xE2\x88\xAD", 4, 3}, + {"toea", "\xE2\xA4\xA8", 4, 3}, + {"top", "\xE2\x8A\xA4", 3, 3}, + {"topbot", "\xE2\x8C\xB6", 6, 3}, + {"topcir", "\xE2\xAB\xB1", 6, 3}, + {"topf", "\xF0\x9D\x95\xA5", 4, 4}, + {"topfork", "\xE2\xAB\x9A", 7, 3}, + {"tosa", "\xE2\xA4\xA9", 4, 3}, + {"tprime", "\xE2\x80\xB4", 6, 3}, + {"trade", "\xE2\x84\xA2", 5, 3}, + {"triangle", "\xE2\x96\xB5", 8, 3}, + {"triangledown", "\xE2\x96\xBF", 12, 3}, + {"triangleleft", "\xE2\x97\x83", 12, 3}, + {"trianglelefteq", "\xE2\x8A\xB4", 14, 3}, + {"triangleq", "\xE2\x89\x9C", 9, 3}, + {"triangleright", "\xE2\x96\xB9", 13, 3}, + {"trianglerighteq", "\xE2\x8A\xB5", 15, 3}, + {"tridot", "\xE2\x97\xAC", 6, 3}, + {"trie", "\xE2\x89\x9C", 4, 3}, + {"triminus", "\xE2\xA8\xBA", 8, 3}, + {"triplus", "\xE2\xA8\xB9", 7, 3}, + {"trisb", "\xE2\xA7\x8D", 5, 3}, + {"tritime", "\xE2\xA8\xBB", 7, 3}, + {"trpezium", "\xE2\x8F\xA2", 8, 3}, + {"tscr", "\xF0\x9D\x93\x89", 4, 4}, + {"tscy", "\xD1\x86", 4, 2}, + {"tshcy", "\xD1\x9B", 5, 2}, + {"tstrok", "\xC5\xA7", 6, 2}, + {"twixt", "\xE2\x89\xAC", 5, 3}, + {"twoheadleftarrow", "\xE2\x86\x9E", 16, 3}, + {"twoheadrightarrow", "\xE2\x86\xA0", 17, 3}, + {"uArr", "\xE2\x87\x91", 4, 3}, + {"uHar", "\xE2\xA5\xA3", 4, 3}, + {"uacute", "\xC3\xBA", 6, 2}, + {"uarr", "\xE2\x86\x91", 4, 3}, + {"ubrcy", "\xD1\x9E", 5, 2}, + {"ubreve", "\xC5\xAD", 6, 2}, + {"ucirc", "\xC3\xBB", 5, 2}, + {"ucy", "\xD1\x83", 3, 2}, + {"udarr", "\xE2\x87\x85", 5, 3}, + {"udblac", "\xC5\xB1", 6, 2}, + {"udhar", "\xE2\xA5\xAE", 5, 3}, + {"ufisht", "\xE2\xA5\xBE", 6, 3}, + {"ufr", "\xF0\x9D\x94\xB2", 3, 4}, + {"ugrave", "\xC3\xB9", 6, 2}, + {"uharl", "\xE2\x86\xBF", 5, 3}, + {"uharr", "\xE2\x86\xBE", 5, 3}, + {"uhblk", "\xE2\x96\x80", 5, 3}, + {"ulcorn", "\xE2\x8C\x9C", 6, 3}, + {"ulcorner", "\xE2\x8C\x9C", 8, 3}, + {"ulcrop", "\xE2\x8C\x8F", 6, 3}, + {"ultri", "\xE2\x97\xB8", 5, 3}, + {"umacr", "\xC5\xAB", 5, 2}, + {"uml", "\xC2\xA8", 3, 2}, + {"uogon", "\xC5\xB3", 5, 2}, + {"uopf", "\xF0\x9D\x95\xA6", 4, 4}, + {"uparrow", "\xE2\x86\x91", 7, 3}, + {"updownarrow", "\xE2\x86\x95", 11, 3}, + {"upharpoonleft", "\xE2\x86\xBF", 13, 3}, + {"upharpoonright", "\xE2\x86\xBE", 14, 3}, + {"uplus", "\xE2\x8A\x8E", 5, 3}, + {"upsi", "\xCF\x85", 4, 2}, + {"upsih", "\xCF\x92", 5, 2}, + {"upsilon", "\xCF\x85", 7, 2}, + {"upuparrows", "\xE2\x87\x88", 10, 3}, + {"urcorn", "\xE2\x8C\x9D", 6, 3}, + {"urcorner", "\xE2\x8C\x9D", 8, 3}, + {"urcrop", "\xE2\x8C\x8E", 6, 3}, + {"uring", "\xC5\xAF", 5, 2}, + {"urtri", "\xE2\x97\xB9", 5, 3}, + {"uscr", "\xF0\x9D\x93\x8A", 4, 4}, + {"utdot", "\xE2\x8B\xB0", 5, 3}, + {"utilde", "\xC5\xA9", 6, 2}, + {"utri", "\xE2\x96\xB5", 4, 3}, + {"utrif", "\xE2\x96\xB4", 5, 3}, + {"uuarr", "\xE2\x87\x88", 5, 3}, + {"uuml", "\xC3\xBC", 4, 2}, + {"uwangle", "\xE2\xA6\xA7", 7, 3}, + {"vArr", "\xE2\x87\x95", 4, 3}, + {"vBar", "\xE2\xAB\xA8", 4, 3}, + {"vBarv", "\xE2\xAB\xA9", 5, 3}, + {"vDash", "\xE2\x8A\xA8", 5, 3}, + {"vangrt", "\xE2\xA6\x9C", 6, 3}, + {"varepsilon", "\xCF\xB5", 10, 2}, + {"varkappa", "\xCF\xB0", 8, 2}, + {"varnothing", "\xE2\x88\x85", 10, 3}, + {"varphi", "\xCF\x95", 6, 2}, + {"varpi", "\xCF\x96", 5, 2}, + {"varpropto", "\xE2\x88\x9D", 9, 3}, + {"varr", "\xE2\x86\x95", 4, 3}, + {"varrho", "\xCF\xB1", 6, 2}, + {"varsigma", "\xCF\x82", 8, 2}, + {"varsubsetneq", "\xE2\x8A\x8A\xEF\xB8\x80", 12, 6}, + {"varsubsetneqq", "\xE2\xAB\x8B\xEF\xB8\x80", 13, 6}, + {"varsupsetneq", "\xE2\x8A\x8B\xEF\xB8\x80", 12, 6}, + {"varsupsetneqq", "\xE2\xAB\x8C\xEF\xB8\x80", 13, 6}, + {"vartheta", "\xCF\x91", 8, 2}, + {"vartriangleleft", "\xE2\x8A\xB2", 15, 3}, + {"vartriangleright", "\xE2\x8A\xB3", 16, 3}, + {"vcy", "\xD0\xB2", 3, 2}, + {"vdash", "\xE2\x8A\xA2", 5, 3}, + {"vee", "\xE2\x88\xA8", 3, 3}, + {"veebar", "\xE2\x8A\xBB", 6, 3}, + {"veeeq", "\xE2\x89\x9A", 5, 3}, + {"vellip", "\xE2\x8B\xAE", 6, 3}, + {"verbar", "\x7C", 6, 1}, + {"vert", "\x7C", 4, 1}, + {"vfr", "\xF0\x9D\x94\xB3", 3, 4}, + {"vltri", "\xE2\x8A\xB2", 5, 3}, + {"vnsub", "\xE2\x8A\x82\xE2\x83\x92", 5, 6}, + {"vnsup", "\xE2\x8A\x83\xE2\x83\x92", 5, 6}, + {"vopf", "\xF0\x9D\x95\xA7", 4, 4}, + {"vprop", "\xE2\x88\x9D", 5, 3}, + {"vrtri", "\xE2\x8A\xB3", 5, 3}, + {"vscr", "\xF0\x9D\x93\x8B", 4, 4}, + {"vsubnE", "\xE2\xAB\x8B\xEF\xB8\x80", 6, 6}, + {"vsubne", "\xE2\x8A\x8A\xEF\xB8\x80", 6, 6}, + {"vsupnE", "\xE2\xAB\x8C\xEF\xB8\x80", 6, 6}, + {"vsupne", "\xE2\x8A\x8B\xEF\xB8\x80", 6, 6}, + {"vzigzag", "\xE2\xA6\x9A", 7, 3}, + {"wcirc", "\xC5\xB5", 5, 2}, + {"wedbar", "\xE2\xA9\x9F", 6, 3}, + {"wedge", "\xE2\x88\xA7", 5, 3}, + {"wedgeq", "\xE2\x89\x99", 6, 3}, + {"weierp", "\xE2\x84\x98", 6, 3}, + {"wfr", "\xF0\x9D\x94\xB4", 3, 4}, + {"wopf", "\xF0\x9D\x95\xA8", 4, 4}, + {"wp", "\xE2\x84\x98", 2, 3}, + {"wr", "\xE2\x89\x80", 2, 3}, + {"wreath", "\xE2\x89\x80", 6, 3}, + {"wscr", "\xF0\x9D\x93\x8C", 4, 4}, + {"xcap", "\xE2\x8B\x82", 4, 3}, + {"xcirc", "\xE2\x97\xAF", 5, 3}, + {"xcup", "\xE2\x8B\x83", 4, 3}, + {"xdtri", "\xE2\x96\xBD", 5, 3}, + {"xfr", "\xF0\x9D\x94\xB5", 3, 4}, + {"xhArr", "\xE2\x9F\xBA", 5, 3}, + {"xharr", "\xE2\x9F\xB7", 5, 3}, + {"xi", "\xCE\xBE", 2, 2}, + {"xlArr", "\xE2\x9F\xB8", 5, 3}, + {"xlarr", "\xE2\x9F\xB5", 5, 3}, + {"xmap", "\xE2\x9F\xBC", 4, 3}, + {"xnis", "\xE2\x8B\xBB", 4, 3}, + {"xodot", "\xE2\xA8\x80", 5, 3}, + {"xopf", "\xF0\x9D\x95\xA9", 4, 4}, + {"xoplus", "\xE2\xA8\x81", 6, 3}, + {"xotime", "\xE2\xA8\x82", 6, 3}, + {"xrArr", "\xE2\x9F\xB9", 5, 3}, + {"xrarr", "\xE2\x9F\xB6", 5, 3}, + {"xscr", "\xF0\x9D\x93\x8D", 4, 4}, + {"xsqcup", "\xE2\xA8\x86", 6, 3}, + {"xuplus", "\xE2\xA8\x84", 6, 3}, + {"xutri", "\xE2\x96\xB3", 5, 3}, + {"xvee", "\xE2\x8B\x81", 4, 3}, + {"xwedge", "\xE2\x8B\x80", 6, 3}, + {"yacute", "\xC3\xBD", 6, 2}, + {"yacy", "\xD1\x8F", 4, 2}, + {"ycirc", "\xC5\xB7", 5, 2}, + {"ycy", "\xD1\x8B", 3, 2}, + {"yen", "\xC2\xA5", 3, 2}, + {"yfr", "\xF0\x9D\x94\xB6", 3, 4}, + {"yicy", "\xD1\x97", 4, 2}, + {"yopf", "\xF0\x9D\x95\xAA", 4, 4}, + {"yscr", "\xF0\x9D\x93\x8E", 4, 4}, + {"yucy", "\xD1\x8E", 4, 2}, + {"yuml", "\xC3\xBF", 4, 2}, + {"zacute", "\xC5\xBA", 6, 2}, + {"zcaron", "\xC5\xBE", 6, 2}, + {"zcy", "\xD0\xB7", 3, 2}, + {"zdot", "\xC5\xBC", 4, 2}, + {"zeetrf", "\xE2\x84\xA8", 6, 3}, + {"zeta", "\xCE\xB6", 4, 2}, + {"zfr", "\xF0\x9D\x94\xB7", 3, 4}, + {"zhcy", "\xD0\xB6", 4, 2}, + {"zigrarr", "\xE2\x87\x9D", 7, 3}, + {"zopf", "\xF0\x9D\x95\xAB", 4, 4}, + {"zscr", "\xF0\x9D\x93\x8F", 4, 4}, + {"zwj", "\xE2\x80\x8D", 3, 3}, + {"zwnj", "\xE2\x80\x8C", 4, 3}, +}; + +#endif /* ZEND_MARKUP_ENTITIES_H */ diff --git a/Zend/zend_markup_entities_gen.php b/Zend/zend_markup_entities_gen.php new file mode 100644 index 000000000000..d9efd2f9dd07 --- /dev/null +++ b/Zend/zend_markup_entities_gen.php @@ -0,0 +1,108 @@ + Zend/zend_markup_entities.h + * + * The source data is ext/standard/html_tables/ents_html5.txt - the same + * WHATWG HTML5 named-reference table that feeds html_entity_decode(), so + * there is a single copy of the data in the tree. The HTML standard + * guarantees this list is frozen - no named entity will ever be added - so + * the generated header only changes if this generator does. The table + * contains only the canonical semicolon-terminated forms: markup requires + * the well-formed "&name;" spelling and treats legacy forms like "&" + * as literal text. + */ + +$source = $argv[1] ?? __DIR__ . '/../ext/standard/html_tables/ents_html5.txt'; + +$lines = @file($source, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES); +if ($lines === false) { + fwrite(STDERR, "cannot read {$source}\n"); + exit(1); +} + +function utf8_bytes(int $cp): string +{ + if ($cp < 0x80) { + return chr($cp); + } elseif ($cp < 0x800) { + return chr(0xC0 | ($cp >> 6)) . chr(0x80 | ($cp & 0x3F)); + } elseif ($cp < 0x10000) { + return chr(0xE0 | ($cp >> 12)) . chr(0x80 | (($cp >> 6) & 0x3F)) . chr(0x80 | ($cp & 0x3F)); + } + return chr(0xF0 | ($cp >> 18)) . chr(0x80 | (($cp >> 12) & 0x3F)) + . chr(0x80 | (($cp >> 6) & 0x3F)) . chr(0x80 | ($cp & 0x3F)); +} + +$entities = []; +foreach ($lines as $line) { + $parts = explode(' ', trim($line)); + $name = array_shift($parts); + if (!preg_match('/^[A-Za-z][A-Za-z0-9]*$/', $name)) { + fwrite(STDERR, "unexpected entity name: $name\n"); + exit(1); + } + if ($parts === []) { + fwrite(STDERR, "no codepoints for entity: $name\n"); + exit(1); + } + $utf8 = ''; + foreach ($parts as $hex) { + if (!preg_match('/^[0-9A-F]{1,6}$/', $hex)) { + fwrite(STDERR, "unexpected codepoint \"$hex\" for entity: $name\n"); + exit(1); + } + $utf8 .= utf8_bytes(hexdec($hex)); + } + $entities[$name] = $utf8; +} + +/* Byte order, matching the C-side memcmp binary search. */ +ksort($entities, SORT_STRING); + +$maxName = max(array_map('strlen', array_keys($entities))); +$maxUtf8 = max(array_map('strlen', $entities)); +$count = count($entities); + +echo << $utf8) { + $hex = ''; + foreach (str_split($utf8) as $byte) { + $hex .= sprintf('\\x%02X', ord($byte)); + } + printf("\t{\"%s\", \"%s\", %d, %d},\n", $name, $hex, strlen($name), strlen($utf8)); +} + +echo <<

    +

    Hello, {$this->name ?: ucfirst($this->type)}!

    +

    Welcome to PHP, where markup is a first-class expression.

    + ; + } +} + +echo ; // prints the rendered HTML, dynamic values escaped +``` + +Despite appearances, this is not a new template language grafted onto PHP - the syntax is **pure compile-time sugar**. Every markup expression lowers, during compilation, to a plain `new` expression: + +```php +// The new syntax... +$html = ; + +// ...compiles to exactly this - same AST, same opcodes: +$html = new \Html\Element('button', ['class' => 'btn'], ['Sign in']); +``` + +Nothing downstream of the parser changes, and markup obeys the expression semantics every PHP developer already knows. + +The two headline benefits over every existing option are **composition** (components, attributes, slots) and **escape-by-default safety** - neither of which PHP's existing markup facilities provide. And because markup is ordinary PHP code rather than a string dialect, static analysis reaches inside it: component props are named arguments checked against real signatures, so templates become type-checkable end-to-end. + +### Why this is worth doing + +Generating HTML is not a niche PHP task - it is arguably *the* PHP task, and essentially every major framework has independently built the same answer to it: an escape-by-default, composable template engine. Their adoption is enormous - on Packagist, `laravel/framework` (Blade) has over **540 million** installs and `twig/twig` over **460 million**, before counting Latte, Smarty, Plates, or hand-rolled templates that never appear in a dependency graph. + +That every engine converged on the same two features is strong evidence the need is real and the language does not meet it: developers reach for a third-party compiler, a separate dialect, and a build step to obtain what the runtime could provide natively. And escape-by-default is no mere convenience - cross-site scripting has sat in the OWASP Top 10 for its entire existence, and automatic HTML escaping is one of the disciplines that prevents it. + +## History + +This idea is not new - PHP is where it started. Around 2010, Facebook built **XHP**, a PHP extension that made XML/HTML fragments valid PHP expressions with automatic escaping - `Link` as a real value - and it survives today as a native feature of Hack. **JSX was born directly out of XHP**, and has since become *the* way most frontend developers write user interfaces. The pattern has proven itself at ecosystem scale; this RFC brings it home. + +The approach has surfaced in various forms many times over the years without a formal proposal; discussions about "automatic template escaping" resulted in arguments that "XHP really is the right solution for this problem," though that also raised the central objection - escaping is context-dependent (HTML vs URL vs JS vs CSS) - which this RFC answers by scoping escaping to HTML context only (see Escape-by-default). + +### Why core, and not an extension + +An obvious question - especially given the XHP precedent - is whether this could be proven out as a PECL extension first. It cannot, for one technical reason and one practical one. + +Technically, markup must begin at a bare `<` in operand position, and telling that apart from the comparison and shift operators requires the scanner change described under Parsing - state no extension can reach. The only route open to an extension is rewriting source text before compilation (the original XHP extension's approach), which shifts line numbers in errors and stack traces and leaves the raw file something `token_get_all()` - and therefore every tool built on it - cannot tokenize. + +Practically, a *syntax* lives or dies by its ecosystem. Code using an extension-only syntax cannot be reasonably published to Packagist - it is a parse error on any install without the extension - and no IDE, formatter, or static analyser updates its grammar for syntax that may not exist on a given machine. That chicken-and-egg is part of where XHP for PHP stalled, while the same feature thrived in Hack, where it is part of the language. Shipping in core is what makes markup part of PHP itself: parsers, IDEs, and analysis tools implement it once, and every developer's markup gets the same highlighting, formatting, and static analysis their ordinary code already enjoys. + +## Goals & Non-Goals + +The goal of this RFC is a native, ergonomic syntax for building HTML - the output format the overwhelming majority of PHP produces - with escaping and composition built in. + +This RFC is deliberately narrower than XHP: it targets **HTML specifically**, not general XML. That choice is visible throughout - escaping uses `htmlspecialchars`; void elements serialize as HTML5 (`
    `, not `
    `); text and attribute handling follow HTML, not XML, semantics. There is no XML namespace support, no CDATA, no DTD, and no requirement that output be well-formed XML. This keeps the surface small and the output aimed at the one thing PHP overwhelmingly produces: web pages. Emitting XML (RSS, SVG, SOAP) remains the job of the existing DOM / `XMLWriter` APIs and is out of scope. + +Semantic validation of attribute *values* is also not a goal - a `javascript:` URL in `href`/`src` escapes cleanly yet is not blocked: sometimes it is intentional, and the policy is the application's to set. (Latte nullifies such URLs and React warns; a framework can enforce the same here via a component decorator or userland wrapper.) Attribute and tag *names*, by contrast, are always validated - see Escape-by-default. + +The aim, in short: enough syntax to make HTML a first-class concern of PHP, with the extension points for frameworks to customize behaviour, and room to grow without breaking backward compatibility (see Future Scope). + +## Proposal + +### 1. A markup expression that evaluates to an object + +The core proposal is that a markup expression evaluates to an instance of a built-in type implementing `Html\Htmlable`. It is **not** a string - but casting it to one will return a string value. + +This is the JSX model (elements are objects, strings appear only after rendering) and is what makes composition and a single, central escaping step possible. + +#### Desugaring at a glance + +Every construct in this proposal lowers the same way - at compile time, to a `new` expression or a function call, behaviour PHP already supports well; no other part of the engine changes. + +```php +// Attributes: literal, ={expr}, bare boolean, {...spread} (see Attributes) +{$label} +// → new \Html\Element('a', ['href' => $url, 'class' => 'btn'], [$label]) + +// Fragments group children with no wrapper element (see Syntax structure) +<>

    {$title}

    +// → new \Html\Fragment([ +// new \Html\Element('h1', [], [$title]), +// new \Html\Element('input', [...$attrs, 'required' => true], []), +// ]) + +// Components dispatch through Html\render_component() (see Tags) + +// → \Html\render_component(Card::class, ['title' => 'Hi']) + +// Component body → slot; extra regions are markup-valued props (see Children & slots) +Title}> +

    Body content.

    +
    +// → \Html\render_component( +// Layout::class, // resolved as a class name string +// ['theme' => 'dark', // props → named arguments +// 'header' => new \Html\Element('h1', [], ['Title'])], +// new \Html\Fragment([/*

    ...

    */]), // body slot +// ) + +// Dynamic tags: the value classifies at runtime (see Tags) +<$tag class="box">{$content} +// → \Html\render_dynamic($tag, ['class' => 'box'], [$content]) +``` + +Two properties fall directly out of this lowering. Markup is **zero-cost sugar** - writing the objects by hand produces the same AST and opcodes. And markup obeys **ordinary expression semantics** - attributes and children are argument expressions, evaluated eagerly, scoped and typed like any other PHP code. + +Every lowering in this document can be reproduced on the reference implementation with no extra tooling: `php -d zend.assertions=1 -r 'assert(false && );'` prints the desugared form in the assertion message. + +### 2. Escape-by-default + +This is a *templating* feature, and like every major PHP template engine (Blade, Twig, Latte) it **escapes by default**: + +* Interpolations `{$expr}` and **attribute values** are escaped with `htmlspecialchars($v, ENT_QUOTES | ENT_SUBSTITUTE | ENT_HTML401)` - like `htmlspecialchars()` itself, the charset comes from ini `default_charset` (UTF-8 by default), so non-UTF-8 applications keep working. +* Any value implementing `Html\Htmlable` (including elements and fragments) passes through **un-escaped** - it is already assumed to be safe HTML. +* The explicit, greppable opt-out for trusted raw strings is `Html\raw($trustedString)`, which returns an `Html\Htmlable`. `Html\escape($string)` is also provided. + +Escaping is **HTML-context only**. URL/JS/CSS-context escaping is explicitly **out of scope** and remains the author's responsibility. Full context-aware escaping is noted as Future Scope. + +A single escaping context is sound here in a way it is not for string-template engines, because there is no context to *detect*: + +* The grammar admits dynamic data in few positions - child content and attribute values - each escaped by one fixed rule (attribute values are always serialized double-quoted; no unquoted output form exists). +* Dynamically-supplied *names* (spread keys, `new Element($tag)`) are **validated** and can throw a runtime exception, not escaped - an invalid name is a `ValueError`, so a name can never break out of the markup. +* `` breakout is inexpressible. + +### 3. Tags: HTML elements vs components + +Dispatch is purely **syntactic** (the compiler cannot know runtime types): + +| Tag form | Meaning | +|---|---| +| `
    `, `` (lowercase / hyphenated) | literal HTML element → `Html\Element` | +| `` (bare, capitalized) | a **component** - resolved as a class name honoring `use` / namespace | +| ``, `<\App\Card>` (namespace-qualified) | a **component** - a namespace-qualified or fully-qualified class name | +| `` | a **component** - a public static method on a class | +| `<$tag>`, `<{expr}>` (a variable / any expression) | a **dynamic tag** - the runtime string value is classified by the same rule, at runtime | + +Any tag whose name is capitalized, contains a namespace separator (`\`), or names a static method (`::`) is a component; everything else is a literal HTML element. + +A **component produces `Html\Htmlable`**, and every component tag resolves through the *class* name rules alone. There are two forms: + +```php +// Class implementing Html\Htmlable - the instance IS the renderable +class Card implements Html\Htmlable { + public function __construct(public string $title) {} + public function toHtml(): Html\Htmlable { + return
    {$this->title}
    ; + } +} +// → new Card(title: 'Hi') + +// Static method - multiple small components can live together on one class +class Author { + public static function byline(string $name): Html\Htmlable { /* ... */ } + public static function avatar(string $src): Html\Htmlable { /* ... */ } +} +// → Author::byline(name: 'Rasmus') +``` + +`toHtml()` returns markup, not a string, so it composes under `strict_types` with no cast, and callers can still reach the produced `Html\Element` tree. It may also return another `Htmlable` - e.g. delegate to another component instance. A static-method component must likewise return `Html\Htmlable`, and must be public, static, and user-defined. + +Both forms resolve through the class resolution every PHP developer already knows - a `` tag's *class part* resolves exactly as `Author::class` would. Plain *function* components are deferred to Future Scope: a bare `` gives no syntactic signal whether a class or a function is meant, and PHP resolves the two through separate `use` / `use function` import tables, so supporting functions means dual-candidate name resolution in the compiler and a class-vs-function dispatch order at runtime (plus guarding tags like `` from resolving to internal functions such as `date()`). A static method has no such ambiguity. + +#### Bare-tag and qualified-tag resolution + +A component tag resolves exactly as a class name does - honoring `use` imports and the current namespace, precisely as `Foo::class` would where the tag appears. The same applies to the class part of a static-method tag: + +```php +namespace Views; + +use App\Card; +use App\Author; + + // → new App\Card(title: 'Hi') + // → App\Author::byline(name: 'R') +``` + +Tags may also be written **namespace-qualified** or **fully-qualified** directly; any tag containing `\` is a component regardless of the first letter's case: + +```php + // namespace-relative +<\App\Card title="Hi"/> // fully-qualified +``` + +#### Dynamic tags - `<$tag>` and `<{expr}>` + +The tag position also accepts a dynamic value, for the common CMS/builder case where the element or component is data ("render this list of blocks"). It follows the rule PHP developers already know from double-quoted string interpolation: a simple variable may appear bare, and braces admit any expression: + +```php +$tag = $isImportant ? 'strong' : 'span'; +echo <$tag class="note">{$text}; + +echo <{$block->type} {...$block->attributes} />; +echo <{$isImportant ? 'strong' : 'span'}>{$text}; +``` + +This is consistent with the rest of the grammar, where braces already mark every dynamic value. + +The value is classified **at runtime by exactly the compile-time rule**: a string with an uppercase first character, a `\`, or a `::` dispatches as a component; anything else constructs a literal `Html\Element`. So `$tag = 'div'` behaves like `
    ` and `$component = Card::class` behaves like ``, and only classification moves to runtime. Two differences follow from the name being data rather than source: it is not resolved against `use` imports (there is no compile-time name to resolve - pass a fully-qualified name, which `::class` already produces), and element names are validated when the tree is serialized rather than when it is parsed (the existing `Html\Element` rule; an invalid name throws, it can never break out of the markup). + +Closing follows the form: +- A variable tag closes by repeating the same variable such as `<$a>...` + - `<$a>...` is a compile error, exactly as `
    ...` is +- An expression tag with a brace closes **anonymously** with `` + - A closing tag cannot restate the expression - re-evaluating it would run its side effects twice, and matching it textually is meaningless for a computed value; the expression is evaluated exactly once. +- A self-closing `<$tag/>` / `<{expr}/>` needs no closer at all. + +#### Userland integration: dispatch hooks + +By default the engine constructs a class component with `new` and calls a static-method component directly. In userland, particularly within frameworks, they will often want to intervene - to resolve components through a dependency-injection container (autowiring the constructor dependencies the props do not supply), or to apply a cross-cutting transform to every component's output. Two request-scoped hook lists make this possible without touching any component's code. Each behaves like `spl_autoload_register` - handlers are kept in registration order, with a matching `unregister_*` that reports whether a handler matched: + +| Hook | Fires for | Signature | Purpose | +|---|---|---|---| +| `Html\register_component_factory` | class components | `(string $class, array $args): ?object` | replace `new` (e.g. container construction) | +| `Html\register_component_decorator` | **every** component kind | `(Html\Htmlable $rendered, string $component): Html\Htmlable` | transform / wrap / log / cache the output | + +The engine always does the part only it can: resolve the name, confirm the target, and route props + `#[Html\Slot]` content into `$args`. A **factory** only changes *how a class component's instance is produced* - it returns an instance of the requested class, or `null` to defer to the next handler (and finally to the engine's own `new`). A **decorator** is the cross-cutting seam: it runs on the `Html\Htmlable` every component produces - class and static-method alike - composing in registration order. (Plain HTML elements such as `
    ` are not components; no hook fires for them. A static-method component's *call* has no production hook; a component that needs container construction is a class component.) + +Every `register_*`/`unregister_*` function also takes an optional second argument, `?string $component = null`, that scopes the hook to a single component: the hook only fires when the resolved name it would receive - the class FQCN, or `"Class::method"` - matches (case-insensitively, with or without a leading backslash). Scoped and unscoped hooks share one chain in registration order, and the dispatch skips out-of-scope hooks inside the engine, before any userland call, so a hook registered for one component costs the others nothing. Unregistering matches on both the callable and the scope it was registered with. + +Registration is scoped to the current runtime, so a framework registers hooks during bootstrap; when nothing is registered the hooks add no cost - the default `new`/call path is unchanged. + +```php +$container = ...; + +// A framework's bootstrap: wire components to the service container... +Html\register_component_factory( + fn(string $class, array $args) => $container->make($class, $args) +); + +// ...and a cross-cutting decorator (profiling, caching, wrapping, ...). +Html\register_component_decorator(function (Html\Htmlable $rendered, string $component) { + return $rendered; +}); +``` + +A component can then simply declare its dependencies; the container supplies them while markup attributes still fill the remaining parameters as named arguments: + +```php +final class UserCard implements Html\Htmlable { + public function __construct(private Clock $clock, public string $name) {} + public function toHtml(): Html\Htmlable { /* ...uses $this->clock... */ } +} + + // the container injects $clock; the prop fills $name +``` + +### 4. Attributes + +```php +{$label} +``` + +| Form | Meaning | +|---|---| +| `class="btn"` | literal string | +| `href={$expr}` | PHP expression (same `{}` as interpolation) | +| `disabled` (bare) | boolean `true` | +| `{...$attrs}` | spread (array → attributes; named-argument unpacking for components) | + +* Attribute names are **native HTML names** (`class`, `for`, `data-x`) - *not* `className`. PHP accepts reserved words as parameter names and named-argument labels, so no `className`/`htmlFor` aliasing is needed like JSX. +* On **HTML elements**, attributes become a string-keyed map. +* On **components**, attributes become **named arguments**. Unknown attributes are a `TypeError` (`Unknown named parameter`) unless the component declares a `...$attrs` variadic, which collects them as a string-keyed array - exactly PHP's existing named-argument + variadic semantics, no new machinery. Hyphenated names (`data-x`, `aria-y`) are not legal named-argument *labels* in a normal call, but as markup attributes they pass through the same mechanism and are collected by the variadic. + +#### Attribute value coercion + +| Value | Result | +|---|---| +| `null` / `false` | attribute **omitted** | +| `true` | boolean attribute (bare name) | +| `string` / `int` / `float` / `Stringable` | `="escaped value"` | +| `array` / other | `TypeError` | + +### 5. Children and slots + +#### HTML elements + +Children are an ordered list of child nodes (text, interpolations, sub-elements). + +#### Components - the body slot via `#[Html\Slot]` + +The body of a component is routed to the constructor parameter marked with the `#[Html\Slot]` attribute. + +```php +class Time implements Html\Htmlable { + public function __construct( + public DateTimeInterface $datetime, + #[Html\Slot] public ?Html\Htmlable $slot = null, + ) {} + public function toHtml(): Html\Htmlable { + return ; + } +} + +//