├── .vscode ├── settings.json ├── extensions.json └── launch.json ├── .gitignore ├── src ├── annotations.lua ├── meta.lua ├── parser.lua ├── fetcher.lua ├── utils.lua ├── terminal.lua ├── generator.lua └── config.lua ├── main.lua ├── LICENSE ├── .github └── workflows │ └── release.yml ├── README.md └── libs ├── json.lua └── html_entities.lua /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Lua.diagnostics.disable": [ 3 | "redefined-local" 4 | ] 5 | } -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "tomblind.local-lua-debugger-vscode" 4 | ] 5 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # System 2 | .DS_Store 3 | Thumbs.db 4 | .idea/ 5 | 6 | # Temporary 7 | /doc 8 | info.json 9 | ref-doc.zip 10 | json_list.txt 11 | 12 | # Generated 13 | /api -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | "name": "Debug Lua Interpreter", 6 | "type": "lua-local", 7 | "request": "launch", 8 | "program": { 9 | "lua": "lua", 10 | "file": "main.lua" 11 | } 12 | } 13 | ] 14 | } -------------------------------------------------------------------------------- /src/annotations.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | annotations.lua 3 | github.com/astrochili/defold-annotations 4 | 5 | Copyright (c) 2023 Roman Silin 6 | MIT license. See LICENSE for details. 7 | --]] 8 | 9 | ---@meta 10 | 11 | ---@class module 12 | ---@field info info 13 | ---@field elements element[] 14 | 15 | ---@class info 16 | ---@field namespace string 17 | ---@field brief string 18 | ---@field description? string 19 | 20 | ---@class element 21 | ---@field type string 22 | ---@field name string 23 | ---@field description string 24 | ---@field parameters? parameter[] 25 | ---@field returnvalues? returnvalue[] 26 | ---@field alias? string 27 | ---@field fields? table 28 | ---@field operators? table 29 | 30 | ---@class parameter 31 | ---@field name string 32 | ---@field doc string 33 | 34 | ---@alias returnvalue parameter 35 | -------------------------------------------------------------------------------- /main.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | main.lua 3 | github.com/astrochili/defold-annotations 4 | 5 | Copyright (c) 2023 Roman Silin 6 | MIT license. See LICENSE for details. 7 | --]] 8 | 9 | local fetcher = require 'src.fetcher' 10 | local parser = require 'src.parser' 11 | local meta = require 'src.meta' 12 | local generator = require 'src.generator' 13 | 14 | -- Fetch the Defold version 15 | local defold_version = arg[1] or fetcher.fetch_version() 16 | 17 | -- Fetch docs from the Github release 18 | local json_paths = fetcher.fetch_docs(defold_version) 19 | 20 | -- Parse .json files to namespace modules 21 | local modules = parser.parse_json(json_paths) 22 | 23 | -- Append the known types and aliases module 24 | table.insert(modules, meta.make_module()) 25 | 26 | -- Generate the API folder with .lua files 27 | generator.generate_api(modules, defold_version) 28 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Roman Silin 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 6 | 7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /src/meta.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | meta.lua 3 | github.com/astrochili/defold-annotations 4 | 5 | Copyright (c) 2023 Roman Silin 6 | MIT license. See LICENSE for details. 7 | --]] 8 | 9 | local config = require 'src.config' 10 | local utils = require 'src.utils' 11 | 12 | local meta = {} 13 | 14 | -- 15 | -- Public 16 | 17 | ---Create a helper module with aliases and classes used by other modules 18 | ---@return module module 19 | function meta.make_module() 20 | local meta_module = { 21 | info = { 22 | namespace = 'meta', 23 | brief = 'Known types and aliases used in the Defold API' 24 | }, 25 | elements = {} 26 | } 27 | 28 | local class_names = utils.sorted_keys(config.known_classes) 29 | for _, class_name in ipairs(class_names) do 30 | local known_class = config.known_classes[class_name] 31 | 32 | local element = { 33 | type = 'BASIC_CLASS', 34 | name = class_name, 35 | fields = known_class.fields or known_class, 36 | operators = known_class.operators 37 | } 38 | 39 | table.insert(meta_module.elements, element) 40 | end 41 | 42 | local alias_names = utils.sorted_keys(config.known_aliases) 43 | for _, alias_name in ipairs(alias_names) do 44 | local element = { 45 | type = 'BASIC_ALIAS', 46 | name = alias_name, 47 | alias = config.known_aliases[alias_name] 48 | } 49 | 50 | table.insert(meta_module.elements, element) 51 | end 52 | 53 | return meta_module 54 | end 55 | 56 | return meta 57 | -------------------------------------------------------------------------------- /src/parser.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | parser.lua 3 | github.com/astrochili/defold-annotations 4 | 5 | Copyright (c) 2023 Roman Silin 6 | MIT license. See LICENSE for details. 7 | --]] 8 | 9 | local json = require 'libs.json' 10 | local config = require 'src.config' 11 | local utils = require 'src.utils' 12 | local terminal = require 'src.terminal' 13 | 14 | local parser = {} 15 | 16 | -- 17 | -- Local 18 | 19 | ---Parse documentation file and create module object 20 | ---@param json_path string paths to the json file 21 | ---@return module? module Parsed documentation object 22 | local function parse_path(json_path) 23 | local filename = json_path 24 | 25 | filename = filename:sub(#config.doc_folder + 2) 26 | filename = filename:sub(1, #filename - (1 + #config.json_extension)) or filename 27 | 28 | if utils.is_blacklisted(config.ignored_docs, filename) then 29 | print('[-] The file "' .. json_path .. '" is skipped because it\'s on the ignore list') 30 | return nil 31 | else 32 | local body = utils.read_file(json_path) 33 | return json.decode(body) 34 | end 35 | end 36 | 37 | -- 38 | -- Public 39 | 40 | ---Parse documentation files and create module objects 41 | ---@param json_paths string[] json_paths an array of paths to json files 42 | ---@return module[] modules Parsed documentation objects 43 | function parser.parse_json(json_paths) 44 | print('-- Modules Parsing') 45 | 46 | local modules = {} 47 | 48 | for _, json_path in ipairs(json_paths) do 49 | local module = parse_path(json_path) 50 | 51 | if module then 52 | table.insert(modules, module) 53 | end 54 | end 55 | 56 | if config.clean_traces then 57 | terminal.delete_folder(config.doc_folder) 58 | end 59 | 60 | print('-- Modules Parsed Successfully!\n') 61 | return modules 62 | end 63 | 64 | return parser 65 | -------------------------------------------------------------------------------- /src/fetcher.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | fetcher.lua 3 | github.com/astrochili/defold-annotations 4 | 5 | Copyright (c) 2023 Roman Silin 6 | MIT license. See LICENSE for details. 7 | --]] 8 | 9 | local json = require 'libs.json' 10 | local config = require 'src.config' 11 | local utils = require 'src.utils' 12 | local terminal = require 'src.terminal' 13 | 14 | local fetcher = {} 15 | 16 | -- 17 | -- Public 18 | 19 | ---Fetch the latest Defold version 20 | ---@return string version like `1.0.0` 21 | function fetcher.fetch_version() 22 | print('-- Version Fetching') 23 | 24 | local url = config.info_url() 25 | terminal.download(url, config.info_json) 26 | 27 | local info_content = utils.read_file(config.info_json) 28 | local info = json.decode(info_content) 29 | print('The "' .. config.info_json .. '" has been successfully decoded') 30 | 31 | local version = info.version 32 | 33 | assert(version, 'Can\'t find the version info in "' .. config.info_json .. '"') 34 | print('The latest Defold version is ' .. version) 35 | 36 | if config.clean_traces then 37 | terminal.delete_file(config.info_json) 38 | end 39 | 40 | print('-- Version Fetched Successfully!\n') 41 | return version 42 | end 43 | 44 | ---Fetch and unzip the Defold documantation files 45 | ---@param version string like `1.0.0' 46 | ---@return string[] json_paths an array of paths to json files 47 | function fetcher.fetch_docs(version) 48 | print('-- Documentation Fetching') 49 | 50 | local url = config.doc_url(version) 51 | local json_list_filename = config.json_list_txt 52 | 53 | terminal.delete_folder(config.doc_folder) 54 | terminal.download(url, config.doc_zip) 55 | terminal.unzip(config.doc_zip, '.') 56 | 57 | if config.clean_traces then 58 | terminal.delete_file(config.doc_zip) 59 | end 60 | 61 | terminal.list_all_files(config.doc_folder, config.json_extension, json_list_filename) 62 | 63 | local json_list_content = utils.read_file(json_list_filename) 64 | local json_paths = utils.get_lines(json_list_content) 65 | 66 | print('Detected ' .. #json_paths .. ' *.json files at "' .. config.doc_folder .. '"') 67 | 68 | for index, json_path in ipairs(json_paths) do 69 | json_paths[index] = config.doc_folder .. config.folder_separator .. json_path 70 | end 71 | 72 | if config.clean_traces then 73 | terminal.delete_file(json_list_filename) 74 | end 75 | 76 | print('-- Documentation Fetched Successfully!\n') 77 | return json_paths 78 | end 79 | 80 | return fetcher 81 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Update and Release 2 | 3 | on: 4 | workflow_dispatch: 5 | schedule: 6 | - cron: "0 0 * * *" 7 | 8 | permissions: 9 | contents: write 10 | 11 | jobs: 12 | check_defold_update: 13 | name: Check for Updates 14 | runs-on: ubuntu-latest 15 | outputs: 16 | defold_version: ${{ steps.fetch_defold_version.outputs.defold_version }} 17 | need_update: ${{ steps.compare_versions.outputs.need_update }} 18 | steps: 19 | - id: fetch_defold_version 20 | name: Get Latest Defold Version 21 | run: | 22 | DEFOLD_VERSION=$(curl https://d.defold.com/stable/info.json -silent | grep -o "\"version\": \"[^\"]*" | grep -o "[^\"]*$") 23 | echo "defold_version=$DEFOLD_VERSION" >> $GITHUB_OUTPUT 24 | - id: annotations_version 25 | name: Get Annotations Latest Version 26 | uses: pozetroninc/github-action-get-latest-release@v0.7.0 27 | with: 28 | repository: ${{ github.repository }} 29 | - id: compare_versions 30 | name: Compare Versions 31 | run: | 32 | DEFOLD_VERSION=${{ steps.fetch_defold_version.outputs.defold_version }} 33 | ANNOTATIONS_VERSION=${{ steps.annotations_version.outputs.release }} 34 | echo "Defold version is $DEFOLD_VERSION" 35 | echo "Annotations version is $ANNOTATIONS_VERSION" 36 | 37 | if [ -z "$ANNOTATIONS_VERSION" ] | [ -z "$DEFOLD_VERSION" ]; then 38 | echo "Something went wrong because version is empty. Stopping workflow." 39 | echo "need_update=false" >> $GITHUB_OUTPUT 40 | elif [ "$ANNOTATIONS_VERSION" = "$DEFOLD_VERSION" ]; then 41 | echo "Generation is not required. Stopping workflow." 42 | echo "need_update=false" >> $GITHUB_OUTPUT 43 | else 44 | echo "Generation is possible. Continue workflow." 45 | echo "need_update=true" >> $GITHUB_OUTPUT 46 | fi 47 | generate_api_release: 48 | name: Generate and Release 49 | needs: [check_defold_update] 50 | if: ${{ needs.check_defold_update.outputs.need_update == 'true'}} 51 | runs-on: ubuntu-latest 52 | steps: 53 | - name: Checkout 54 | uses: actions/checkout@v4.0.0 55 | - name: Setup 56 | uses: leafo/gh-actions-lua@v10.0.0 57 | with: 58 | luaVersion: "5.1" 59 | - name: Run 60 | run: lua main.lua 61 | - name: Zip 62 | run: | 63 | mv api defold_api 64 | zip -r defold_api_${{ needs.check_defold_update.outputs.defold_version }}.zip defold_api 65 | - name: Release 66 | uses: softprops/action-gh-release@v0.1.15 67 | with: 68 | name: ${{ needs.check_defold_update.outputs.defold_version }} 69 | tag_name: ${{ needs.check_defold_update.outputs.defold_version }} 70 | body: Auto-generated [Defold](https://defold.com/) ${{ needs.check_defold_update.outputs.defold_version }} annotations for [Lua Language Server](https://github.com/LuaLS/lua-language-server). 71 | files: defold_api_${{ needs.check_defold_update.outputs.defold_version }}.zip 72 | -------------------------------------------------------------------------------- /src/utils.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | utils.lua 3 | github.com/astrochili/defold-annotations 4 | 5 | Copyright (c) 2023 Roman Silin 6 | MIT license. See LICENSE for details. 7 | --]] 8 | 9 | local utils = {} 10 | 11 | -- 12 | -- Public 13 | 14 | ---Save the content to the file 15 | ---@param content string content of the file 16 | ---@param path string path to the file 17 | ---@return boolean? result 18 | function utils.save_file(content, path) 19 | local file = io.open(path, 'w') 20 | 21 | if file == nil then 22 | assert(file, 'Can\'t save a file at path: "' .. path .. '"') 23 | return false 24 | end 25 | 26 | assert(file:write(content), 'Error during writing to the file "' .. path .. '"') 27 | print('The file "' .. path .. '" has been successfully created') 28 | 29 | return file:close() 30 | end 31 | 32 | ---Read the content from the file 33 | ---@param path string path to the file 34 | ---@return string content content of the file 35 | function utils.read_file(path) 36 | local file = io.open(path, 'r') 37 | assert(file, 'File doesn\'t exist: "' .. path .. '"') 38 | 39 | local content = file:read('*a') 40 | print('The file "' .. path .. '" has been successfully read') 41 | file:close() 42 | 43 | return content 44 | end 45 | 46 | ---Get array of lines from the string 47 | ---@param content string 48 | ---@return string[] lines 49 | function utils.get_lines(content) 50 | local lines = {} 51 | 52 | for line in content:gmatch '[^\r\n]+' do 53 | table.insert(lines, line) 54 | end 55 | 56 | return lines 57 | end 58 | 59 | ---Get sorted keys of the table 60 | ---@param dictionary table 61 | ---@return string[] 62 | function utils.sorted_keys(dictionary) 63 | local keys = {} 64 | 65 | for key in pairs(dictionary) do 66 | table.insert(keys, key) 67 | end 68 | 69 | table.sort(keys) 70 | 71 | return keys 72 | end 73 | 74 | ---Check if the value matched to the mask `text` or `text_*` 75 | ---@param value string 76 | ---@param mask string 77 | ---@return boolean 78 | function utils.match(value, mask) 79 | if mask:sub(-1) == '*' then 80 | local list_prefix = mask:sub(1, #mask - 1) 81 | return value:sub(1, #list_prefix) == list_prefix 82 | else 83 | return value == mask 84 | end 85 | end 86 | 87 | ---Check if an item is present in the black list, including by the `*` suffix rule. 88 | ---@param list string[] 89 | ---@param item string 90 | ---@return boolean is_blacklisted 91 | function utils.is_blacklisted(list, item) 92 | for _, list_element in ipairs(list) do 93 | if utils.match(item, list_element) then 94 | return true 95 | end 96 | end 97 | 98 | return false 99 | end 100 | 101 | ---Returns the array copy without duplicates 102 | ---@generic T 103 | ---@param t table 104 | ---@return table 105 | function utils.unique(t) 106 | local result = {} 107 | local seen = {} 108 | 109 | for _, item in ipairs(t) do 110 | if not seen[item] then 111 | table.insert(result, item) 112 | seen[item] = true 113 | end 114 | end 115 | 116 | return result 117 | end 118 | 119 | return utils 120 | -------------------------------------------------------------------------------- /src/terminal.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | terminal.lua 3 | github.com/astrochili/defold-annotations 4 | 5 | Copyright (c) 2023 Roman Silin 6 | MIT license. See LICENSE for details. 7 | --]] 8 | 9 | local config = require 'src.config' 10 | 11 | local terminal = {} 12 | 13 | -- 14 | -- Local 15 | 16 | local is_windows = config.folder_separator == '\\' 17 | 18 | ---Wrap the path in double quotes 19 | ---@param path string 20 | ---@return string 21 | local function quoted(path) 22 | return '"' .. path .. '"' 23 | end 24 | 25 | ---Execute a command in the system shell 26 | ---@param unix_command string 27 | ---@param windows_command string 28 | ---@return boolean? 29 | local function execute(unix_command, windows_command) 30 | if is_windows then 31 | local pipe = io.popen('powershell -command -', 'w') 32 | assert(pipe, 'Can\'t init powershell session') 33 | 34 | if pipe:write(windows_command) then 35 | return pipe:close() 36 | end 37 | else 38 | return os.execute(unix_command) 39 | end 40 | end 41 | 42 | -- 43 | -- Public 44 | 45 | ---Download the file at url 46 | ---@param url string 47 | ---@param output_path string 48 | function terminal.download(url, output_path) 49 | print('Downloading the file from ' .. quoted(url)) 50 | 51 | local result = execute( 52 | 'curl -s -L ' .. url .. ' -o ' .. quoted(output_path), 53 | 'Invoke-WebRequest -URI ' .. url .. ' -OutFile ' .. quoted(output_path) .. ' -UseBasicParsing' 54 | ) 55 | 56 | assert(result, 'Error during downloading file "' .. url .. '"') 57 | print('The file ' .. quoted(url) .. ' has been successfully downloaded and saved as ' .. quoted(output_path)) 58 | end 59 | 60 | ---Delete the file 61 | ---@param path string 62 | function terminal.delete_file(path) 63 | print('Deleting the file ' .. quoted(path)) 64 | 65 | local result = execute( 66 | 'rm -f ' .. quoted(path), 67 | 'if (Test-Path ' .. quoted(path) .. ') { Remove-Item -Path ' .. quoted(path) .. ' -Force }' 68 | ) 69 | 70 | assert(result, 'Error during deleting the file "' .. path .. '"') 71 | print('The file ' .. quoted(path) .. ' has been successfully deleted') 72 | end 73 | 74 | ---Delete the folder with all the content 75 | ---@param path string 76 | function terminal.delete_folder(path) 77 | print('Deleting the folder ' .. quoted(path)) 78 | 79 | local result = execute( 80 | 'rm -rf ' .. quoted(path), 81 | 'if (Test-Path ' .. quoted(path) .. ') { Remove-Item -Path ' .. quoted(path) .. ' -Recurse -Force }' 82 | ) 83 | 84 | assert(result, 'Error during deleting the folder "' .. path .. '"') 85 | print('The folder ' .. quoted(path) .. ' has been successfully deleted') 86 | end 87 | 88 | ---Create a new folder 89 | ---@param path string 90 | function terminal.create_folder(path) 91 | print('Creating the folder ' .. quoted(path)) 92 | 93 | local result = execute( 94 | 'mkdir ' .. quoted(path), 95 | 'New-Item -Path ' .. quoted(path) .. ' -ItemType Directory | Out-Null' 96 | ) 97 | 98 | assert(result, 'Error during creating a folder "' .. path .. '"') 99 | print('The folder ' .. quoted(path) .. ' has been successfully created') 100 | end 101 | 102 | ---Unzip a zip archive 103 | ---@param path string 104 | ---@param destination string 105 | function terminal.unzip(path, destination) 106 | print('Unzipping the archive ' .. quoted(path)) 107 | 108 | local result = execute( 109 | 'unzip -q ' .. quoted(path) .. ' -d ' .. quoted(destination), 110 | 'Expand-Archive -Path ' .. quoted(path) .. ' -DestinationPath ' .. quoted(destination) 111 | ) 112 | 113 | assert(result, 'Error during unziping the archive "' .. path .. '"') 114 | print('The archive ' .. quoted(path) .. ' has been successfully unzipped') 115 | end 116 | 117 | ---Find all files in the folder with the exact extension and save the list in the output file 118 | ---@param folder_path string 119 | ---@param extension string 120 | ---@param output_path string 121 | function terminal.list_all_files(folder_path, extension, output_path) 122 | print('Listing the ' .. extension .. ' files at ' .. quoted(folder_path)) 123 | 124 | local result = execute( 125 | 'find ' .. 126 | quoted(folder_path) .. ' -type f -name "*.' .. extension .. '" | egrep -o -e "[^/]+$" > ' .. quoted(output_path), 127 | 'Get-ChildItem -Path ' .. 128 | quoted(folder_path) .. 129 | ' -Name -Filter "*.' .. extension .. '" | Out-File -FilePath ' .. quoted(output_path) .. ' -Encoding ASCII -append' 130 | ) 131 | 132 | assert(result, 'Error during listing the files at "' .. folder_path .. '"') 133 | print('The list of ' .. extension .. ' files has been successfully listed and saved to ' .. quoted(output_path)) 134 | end 135 | 136 | return terminal 137 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Defold Annotations 2 | 3 | [![Workflow](https://img.shields.io/github/actions/workflow/status/astrochili/defold-annotations/release.yml)](https://github.com/astrochili/defold-annotations/actions/workflows/release.yml) 4 | [![Release](https://img.shields.io/github/v/release/astrochili/defold-annotations.svg?include_prereleases=&sort=semver&color=blue)](https://github.com/astrochili/defold-annotations/releases) 5 | [![License](https://img.shields.io/badge/License-MIT-blue)](https://github.com/astrochili/defold-annotations/blob/master/LICENSE) 6 | [![Website](https://img.shields.io/badge/website-gray.svg?&logo=data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxOCIgaGVpZ2h0PSIxNiIgZmlsbD0ibm9uZSIgdmlld0JveD0iMCAwIDE4IDE2Ij48Y2lyY2xlIGN4PSIzLjY2IiBjeT0iMTQuNzUiIHI9IjEuMjUiIGZpbGw9InVybCgjYSkiLz48Y2lyY2xlIGN4PSI4LjY2IiBjeT0iMTQuNzUiIHI9IjEuMjUiIGZpbGw9InVybCgjYikiLz48Y2lyY2xlIGN4PSIxMy42NSIgY3k9IjE0Ljc1IiByPSIxLjI1IiBmaWxsPSJ1cmwoI2MpIi8+PHBhdGggZmlsbD0idXJsKCNkKSIgZmlsbC1ydWxlPSJldmVub2RkIiBkPSJNNy42MyAxLjQ4Yy41LS43IDEuNTUtLjcgMi4wNSAwbDYuMjIgOC44MWMuNTguODMtLjAxIDEuOTctMS4wMyAxLjk3SDIuNDRhMS4yNSAxLjI1IDAgMCAxLTEuMDItMS45N2w2LjIxLTguODFaIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiLz48ZGVmcz48bGluZWFyR3JhZGllbnQgaWQ9ImEiIHgxPSIyLjQxIiB4Mj0iMi40MSIgeTE9IjEzLjUiIHkyPSIxNiIgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiPjxzdG9wIHN0b3AtY29sb3I9IiNGRDhENDIiLz48c3RvcCBvZmZzZXQ9IjEiIHN0b3AtY29sb3I9IiNGOTU0MUYiLz48L2xpbmVhckdyYWRpZW50PjxsaW5lYXJHcmFkaWVudCBpZD0iYiIgeDE9IjcuNDEiIHgyPSI3LjQxIiB5MT0iMTMuNSIgeTI9IjE2IiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSI+PHN0b3Agc3RvcC1jb2xvcj0iI0ZEOEQ0MiIvPjxzdG9wIG9mZnNldD0iMSIgc3RvcC1jb2xvcj0iI0Y5NTQxRiIvPjwvbGluZWFyR3JhZGllbnQ+PGxpbmVhckdyYWRpZW50IGlkPSJjIiB4MT0iMTIuNCIgeDI9IjEyLjQiIHkxPSIxMy41IiB5Mj0iMTYiIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIj48c3RvcCBzdG9wLWNvbG9yPSIjRkQ4RDQyIi8+PHN0b3Agb2Zmc2V0PSIxIiBzdG9wLWNvbG9yPSIjRjk1NDFGIi8+PC9saW5lYXJHcmFkaWVudD48bGluZWFyR3JhZGllbnQgaWQ9ImQiIHgxPSIuMDMiIHgyPSIuMDMiIHkxPSIuMDMiIHkyPSIxMi4yNiIgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiPjxzdG9wIHN0b3AtY29sb3I9IiNGRkU2NUUiLz48c3RvcCBvZmZzZXQ9IjEiIHN0b3AtY29sb3I9IiNGRkM4MzAiLz48L2xpbmVhckdyYWRpZW50PjwvZGVmcz48L3N2Zz4=)](https://astronachos.com/) 7 | [![Mastodon](https://img.shields.io/badge/mastodon-gray?&logo=mastodon)](https://mastodon.gamedev.place/@astronachos) 8 | [![Twitter](https://img.shields.io/badge/twitter-gray?&logo=twitter)](https://twitter.com/astronachos) 9 | [![Telegram](https://img.shields.io/badge/telegram-gray?&logo=telegram)](https://t.me/astronachos) 10 | [![Buy me a coffee](https://img.shields.io/badge/buy_me_a_coffee-gray?&logo=buy%20me%20a%20coffee)](https://buymeacoffee.com/astrochili) 11 | 12 | A set of Lua scripts for parsing [Defold](https://defold.com) documentation and generating annotation files compatible with [Lua Language Server](https://github.com/LuaLS/lua-language-server). 13 | 14 | By design, it can be run on Windows, macOS and Linux. Only Lua needs to be installed. The Lua language was chosen because it allows all Defold users to contribute to this project. 15 | 16 | Generated annotations are available on the [Releases](https://github.com/astrochili/defold-annotations/releases) page. 17 | 18 | ## Use case 19 | 20 | These annotations are used by [Defold Kit](https://github.com/astrochili/vscode-defold), a Visual Studio Code extension for developing games with Defold. 21 | 22 | ## Automatic Releases 23 | 24 | This repository has an [action workflow](https://github.com/astrochili/defold-annotations/actions/workflows/release.yml) that checks the latest version of Defold daily and automatically generates and releases the new version of annotations if required. 25 | 26 | But if something goes wrong and edits are needed, there will be an additional manual update in the release. 27 | 28 | ## Manual Generation 29 | 30 | Install [Lua](https://www.lua.org/) and run the `main.lua` script. 31 | 32 | ```sh 33 | $ lua main.lua 34 | ``` 35 | 36 | By default it generates the annotations for the latest version of Defold. But you can also specify the Defold version as an argument. 37 | 38 | ```sh 39 | $ lua main.lua '1.5.0' 40 | ``` 41 | 42 | As a result, you will see the `api` folder with `.lua` files. These are the annotations. 43 | 44 | ## Contribution 45 | 46 | ### Issues 47 | 48 | If you find a typo in the annotations or outdated meta information, please comment to issue [#4](https://github.com/astrochili/defold-annotations/issues/4). I try to collect annotation errors there, and then prepare a pull request to [Defold source code](https://github.com/defold/defold/tree/master/engine), as this is the main source of documentation. 49 | 50 | Otherwise, on parsing and generation problems, craete a new issue. 51 | 52 | ### Debug 53 | 54 | There is a launch config for [tomblind/local-lua-debugger-vscode](https://github.com/tomblind/local-lua-debugger-vscode) to debug with breakpoints. 55 | 56 | It's also useful to turn the `config.clean_traces` to `true` in [`config.lua`](https://github.com/astrochili/defold-annotations/blob/639fa58f60687f0a40e702bc56196d0c0c73b5d5/src/config.lua#L15) file to avoid deleting temporary files. 57 | 58 | ## Third-party 59 | 60 | - [json.lua](https://github.com/rxi/json.lua) by rxi 61 | - [htmlEntities-for-lua](https://github.com/TiagoDanin/htmlEntities-for-lua) by Tiago Danin 62 | 63 | ## Alternatives 64 | 65 | - [defold-lua-annotations](https://github.com/mikatuo/defold-lua-annotations/) (C#) by Dennis Shendrik -------------------------------------------------------------------------------- /libs/json.lua: -------------------------------------------------------------------------------- 1 | -- 2 | -- json.lua 3 | -- 4 | -- Copyright (c) 2020 rxi 5 | -- 6 | -- Permission is hereby granted, free of charge, to any person obtaining a copy of 7 | -- this software and associated documentation files (the "Software"), to deal in 8 | -- the Software without restriction, including without limitation the rights to 9 | -- use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 10 | -- of the Software, and to permit persons to whom the Software is furnished to do 11 | -- so, subject to the following conditions: 12 | -- 13 | -- The above copyright notice and this permission notice shall be included in all 14 | -- copies or substantial portions of the Software. 15 | -- 16 | -- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | -- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | -- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | -- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | -- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | -- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | -- SOFTWARE. 23 | -- 24 | 25 | local json = { _version = "0.1.2" } 26 | 27 | ------------------------------------------------------------------------------- 28 | -- Encode 29 | ------------------------------------------------------------------------------- 30 | 31 | local encode 32 | 33 | local escape_char_map = { 34 | [ "\\" ] = "\\", 35 | [ "\"" ] = "\"", 36 | [ "\b" ] = "b", 37 | [ "\f" ] = "f", 38 | [ "\n" ] = "n", 39 | [ "\r" ] = "r", 40 | [ "\t" ] = "t", 41 | } 42 | 43 | local escape_char_map_inv = { [ "/" ] = "/" } 44 | for k, v in pairs(escape_char_map) do 45 | escape_char_map_inv[v] = k 46 | end 47 | 48 | 49 | local function escape_char(c) 50 | return "\\" .. (escape_char_map[c] or string.format("u%04x", c:byte())) 51 | end 52 | 53 | 54 | local function encode_nil(val) 55 | return "null" 56 | end 57 | 58 | 59 | local function encode_table(val, stack) 60 | local res = {} 61 | stack = stack or {} 62 | 63 | -- Circular reference? 64 | if stack[val] then error("circular reference") end 65 | 66 | stack[val] = true 67 | 68 | if rawget(val, 1) ~= nil or next(val) == nil then 69 | -- Treat as array -- check keys are valid and it is not sparse 70 | local n = 0 71 | for k in pairs(val) do 72 | if type(k) ~= "number" then 73 | error("invalid table: mixed or invalid key types") 74 | end 75 | n = n + 1 76 | end 77 | if n ~= #val then 78 | error("invalid table: sparse array") 79 | end 80 | -- Encode 81 | for i, v in ipairs(val) do 82 | table.insert(res, encode(v, stack)) 83 | end 84 | stack[val] = nil 85 | return "[" .. table.concat(res, ",") .. "]" 86 | 87 | else 88 | -- Treat as an object 89 | for k, v in pairs(val) do 90 | if type(k) ~= "string" then 91 | error("invalid table: mixed or invalid key types") 92 | end 93 | table.insert(res, encode(k, stack) .. ":" .. encode(v, stack)) 94 | end 95 | stack[val] = nil 96 | return "{" .. table.concat(res, ",") .. "}" 97 | end 98 | end 99 | 100 | 101 | local function encode_string(val) 102 | return '"' .. val:gsub('[%z\1-\31\\"]', escape_char) .. '"' 103 | end 104 | 105 | 106 | local function encode_number(val) 107 | -- Check for NaN, -inf and inf 108 | if val ~= val or val <= -math.huge or val >= math.huge then 109 | error("unexpected number value '" .. tostring(val) .. "'") 110 | end 111 | return string.format("%.14g", val) 112 | end 113 | 114 | 115 | local type_func_map = { 116 | [ "nil" ] = encode_nil, 117 | [ "table" ] = encode_table, 118 | [ "string" ] = encode_string, 119 | [ "number" ] = encode_number, 120 | [ "boolean" ] = tostring, 121 | } 122 | 123 | 124 | encode = function(val, stack) 125 | local t = type(val) 126 | local f = type_func_map[t] 127 | if f then 128 | return f(val, stack) 129 | end 130 | error("unexpected type '" .. t .. "'") 131 | end 132 | 133 | 134 | function json.encode(val) 135 | return ( encode(val) ) 136 | end 137 | 138 | 139 | ------------------------------------------------------------------------------- 140 | -- Decode 141 | ------------------------------------------------------------------------------- 142 | 143 | local parse 144 | 145 | local function create_set(...) 146 | local res = {} 147 | for i = 1, select("#", ...) do 148 | res[ select(i, ...) ] = true 149 | end 150 | return res 151 | end 152 | 153 | local space_chars = create_set(" ", "\t", "\r", "\n") 154 | local delim_chars = create_set(" ", "\t", "\r", "\n", "]", "}", ",") 155 | local escape_chars = create_set("\\", "/", '"', "b", "f", "n", "r", "t", "u") 156 | local literals = create_set("true", "false", "null") 157 | 158 | local literal_map = { 159 | [ "true" ] = true, 160 | [ "false" ] = false, 161 | [ "null" ] = nil, 162 | } 163 | 164 | 165 | local function next_char(str, idx, set, negate) 166 | for i = idx, #str do 167 | if set[str:sub(i, i)] ~= negate then 168 | return i 169 | end 170 | end 171 | return #str + 1 172 | end 173 | 174 | 175 | local function decode_error(str, idx, msg) 176 | local line_count = 1 177 | local col_count = 1 178 | for i = 1, idx - 1 do 179 | col_count = col_count + 1 180 | if str:sub(i, i) == "\n" then 181 | line_count = line_count + 1 182 | col_count = 1 183 | end 184 | end 185 | error( string.format("%s at line %d col %d", msg, line_count, col_count) ) 186 | end 187 | 188 | 189 | local function codepoint_to_utf8(n) 190 | -- http://scripts.sil.org/cms/scripts/page.php?site_id=nrsi&id=iws-appendixa 191 | local f = math.floor 192 | if n <= 0x7f then 193 | return string.char(n) 194 | elseif n <= 0x7ff then 195 | return string.char(f(n / 64) + 192, n % 64 + 128) 196 | elseif n <= 0xffff then 197 | return string.char(f(n / 4096) + 224, f(n % 4096 / 64) + 128, n % 64 + 128) 198 | elseif n <= 0x10ffff then 199 | return string.char(f(n / 262144) + 240, f(n % 262144 / 4096) + 128, 200 | f(n % 4096 / 64) + 128, n % 64 + 128) 201 | end 202 | error( string.format("invalid unicode codepoint '%x'", n) ) 203 | end 204 | 205 | 206 | local function parse_unicode_escape(s) 207 | local n1 = tonumber( s:sub(1, 4), 16 ) 208 | local n2 = tonumber( s:sub(7, 10), 16 ) 209 | -- Surrogate pair? 210 | if n2 then 211 | return codepoint_to_utf8((n1 - 0xd800) * 0x400 + (n2 - 0xdc00) + 0x10000) 212 | else 213 | return codepoint_to_utf8(n1) 214 | end 215 | end 216 | 217 | 218 | local function parse_string(str, i) 219 | local res = "" 220 | local j = i + 1 221 | local k = j 222 | 223 | while j <= #str do 224 | local x = str:byte(j) 225 | 226 | if x < 32 then 227 | decode_error(str, j, "control character in string") 228 | 229 | elseif x == 92 then -- `\`: Escape 230 | res = res .. str:sub(k, j - 1) 231 | j = j + 1 232 | local c = str:sub(j, j) 233 | if c == "u" then 234 | local hex = str:match("^[dD][89aAbB]%x%x\\u%x%x%x%x", j + 1) 235 | or str:match("^%x%x%x%x", j + 1) 236 | or decode_error(str, j - 1, "invalid unicode escape in string") 237 | res = res .. parse_unicode_escape(hex) 238 | j = j + #hex 239 | else 240 | if not escape_chars[c] then 241 | decode_error(str, j - 1, "invalid escape char '" .. c .. "' in string") 242 | end 243 | res = res .. escape_char_map_inv[c] 244 | end 245 | k = j + 1 246 | 247 | elseif x == 34 then -- `"`: End of string 248 | res = res .. str:sub(k, j - 1) 249 | return res, j + 1 250 | end 251 | 252 | j = j + 1 253 | end 254 | 255 | decode_error(str, i, "expected closing quote for string") 256 | end 257 | 258 | 259 | local function parse_number(str, i) 260 | local x = next_char(str, i, delim_chars) 261 | local s = str:sub(i, x - 1) 262 | local n = tonumber(s) 263 | if not n then 264 | decode_error(str, i, "invalid number '" .. s .. "'") 265 | end 266 | return n, x 267 | end 268 | 269 | 270 | local function parse_literal(str, i) 271 | local x = next_char(str, i, delim_chars) 272 | local word = str:sub(i, x - 1) 273 | if not literals[word] then 274 | decode_error(str, i, "invalid literal '" .. word .. "'") 275 | end 276 | return literal_map[word], x 277 | end 278 | 279 | 280 | local function parse_array(str, i) 281 | local res = {} 282 | local n = 1 283 | i = i + 1 284 | while 1 do 285 | local x 286 | i = next_char(str, i, space_chars, true) 287 | -- Empty / end of array? 288 | if str:sub(i, i) == "]" then 289 | i = i + 1 290 | break 291 | end 292 | -- Read token 293 | x, i = parse(str, i) 294 | res[n] = x 295 | n = n + 1 296 | -- Next token 297 | i = next_char(str, i, space_chars, true) 298 | local chr = str:sub(i, i) 299 | i = i + 1 300 | if chr == "]" then break end 301 | if chr ~= "," then decode_error(str, i, "expected ']' or ','") end 302 | end 303 | return res, i 304 | end 305 | 306 | 307 | local function parse_object(str, i) 308 | local res = {} 309 | i = i + 1 310 | while 1 do 311 | local key, val 312 | i = next_char(str, i, space_chars, true) 313 | -- Empty / end of object? 314 | if str:sub(i, i) == "}" then 315 | i = i + 1 316 | break 317 | end 318 | -- Read key 319 | if str:sub(i, i) ~= '"' then 320 | decode_error(str, i, "expected string for key") 321 | end 322 | key, i = parse(str, i) 323 | -- Read ':' delimiter 324 | i = next_char(str, i, space_chars, true) 325 | if str:sub(i, i) ~= ":" then 326 | decode_error(str, i, "expected ':' after key") 327 | end 328 | i = next_char(str, i + 1, space_chars, true) 329 | -- Read value 330 | val, i = parse(str, i) 331 | -- Set 332 | res[key] = val 333 | -- Next token 334 | i = next_char(str, i, space_chars, true) 335 | local chr = str:sub(i, i) 336 | i = i + 1 337 | if chr == "}" then break end 338 | if chr ~= "," then decode_error(str, i, "expected '}' or ','") end 339 | end 340 | return res, i 341 | end 342 | 343 | 344 | local char_func_map = { 345 | [ '"' ] = parse_string, 346 | [ "0" ] = parse_number, 347 | [ "1" ] = parse_number, 348 | [ "2" ] = parse_number, 349 | [ "3" ] = parse_number, 350 | [ "4" ] = parse_number, 351 | [ "5" ] = parse_number, 352 | [ "6" ] = parse_number, 353 | [ "7" ] = parse_number, 354 | [ "8" ] = parse_number, 355 | [ "9" ] = parse_number, 356 | [ "-" ] = parse_number, 357 | [ "t" ] = parse_literal, 358 | [ "f" ] = parse_literal, 359 | [ "n" ] = parse_literal, 360 | [ "[" ] = parse_array, 361 | [ "{" ] = parse_object, 362 | } 363 | 364 | 365 | parse = function(str, idx) 366 | local chr = str:sub(idx, idx) 367 | local f = char_func_map[chr] 368 | if f then 369 | return f(str, idx) 370 | end 371 | decode_error(str, idx, "unexpected character '" .. chr .. "'") 372 | end 373 | 374 | 375 | function json.decode(str) 376 | if type(str) ~= "string" then 377 | error("expected argument of type string, got " .. type(str)) 378 | end 379 | local res, idx = parse(str, next_char(str, 1, space_chars, true)) 380 | idx = next_char(str, idx, space_chars, true) 381 | if idx <= #str then 382 | decode_error(str, idx, "trailing garbage") 383 | end 384 | return res 385 | end 386 | 387 | 388 | return json 389 | -------------------------------------------------------------------------------- /src/generator.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | generator.lua 3 | github.com/astrochili/defold-annotations 4 | 5 | Copyright (c) 2023 Roman Silin 6 | MIT license. See LICENSE for details. 7 | --]] 8 | 9 | local html_entities = require 'libs.html_entities' 10 | local config = require 'src.config' 11 | local utils = require 'src.utils' 12 | local terminal = require 'src.terminal' 13 | 14 | local generator = {} 15 | 16 | -- 17 | -- Local 18 | 19 | -- Apply a list of inline tag rules, replacing html tags with their corresponding markdown syntax 20 | ---@param s string 21 | ---@param inline_rules table 22 | ---@return string 23 | local function apply_inline_rules(s, inline_rules) 24 | for tag, rule in pairs(inline_rules) do 25 | local markdown = rule 26 | local wrap_both_sides = true 27 | 28 | if type(rule) == 'table' then 29 | markdown = rule.markdown 30 | wrap_both_sides = rule.wrap_both_sides 31 | end 32 | 33 | s = s:gsub('<' .. tag .. '>(.-)', function(inner) 34 | return wrap_both_sides and (markdown .. inner .. markdown) or (markdown .. inner) 35 | end) 36 | end 37 | 38 | return s 39 | end 40 | 41 | ---Decode text to get rid of unnecessary html tags and entities 42 | ---@param text string 43 | ---@return string 44 | local function decode_text(text) 45 | local result = text or '' 46 | 47 | local inline_tags = { 48 | code = '`', 49 | strong = '**', 50 | b = '**', 51 | em = '*', 52 | i = '*', 53 | li = { markdown = '- ', wrap_both_sides = false }, 54 | } 55 | 56 | result = apply_inline_rules(result, inline_tags) 57 | 58 | -- Strip any remaining html tags 59 | result = result:gsub('%b<>', '') 60 | 61 | local decoded_result = html_entities.decode(result) 62 | 63 | if type(decoded_result) == 'string' then 64 | result = decoded_result 65 | end 66 | 67 | return result 68 | end 69 | 70 | ---Make an annotable comment 71 | ---@param text string 72 | ---@param tab? string Indent string, default is '---' 73 | ---@return string 74 | local function make_comment(text, tab) 75 | local tab = tab or '---' 76 | local text = decode_text(text or '') 77 | 78 | local lines = text == '' and { text } or utils.get_lines(text) 79 | local result = '' 80 | 81 | for index, line in ipairs(lines) do 82 | result = result .. tab .. line 83 | 84 | if index < #lines then 85 | result = result .. '\n' 86 | end 87 | end 88 | 89 | return result 90 | end 91 | 92 | ---Make an annotable module header 93 | ---@param defold_version string 94 | ---@param title string 95 | ---@param description string 96 | ---@return string 97 | local function make_header(defold_version, title, description) 98 | local result = '' 99 | 100 | result = result .. '--[[\n' 101 | result = result .. ' Generated with ' .. config.generator_url .. '\n' 102 | result = result .. ' Defold ' .. defold_version .. '\n\n' 103 | result = result .. ' ' .. decode_text(title) .. '\n' 104 | 105 | if description and description ~= title then 106 | result = result .. '\n' 107 | result = result .. make_comment(description, ' ') .. '\n' 108 | end 109 | 110 | result = result .. '--]]' 111 | 112 | return result 113 | end 114 | 115 | ---Make annotable diagnostic disable flags 116 | ---@param disabled_diagnostics string[] list of diagnostic disabel flags 117 | ---@return string 118 | local function make_disabled_diagnostics(disabled_diagnostics) 119 | local result = '' 120 | 121 | result = result .. '---@meta\n' 122 | 123 | for index, disabled_diagnostic in ipairs(disabled_diagnostics) do 124 | result = result .. '---@diagnostic disable: ' .. disabled_diagnostic 125 | 126 | if index < #config.disabled_diagnostics then 127 | result = result .. '\n' 128 | end 129 | end 130 | 131 | return result 132 | end 133 | 134 | ---Wrap the class body to an annotable namespace 135 | ---@param name string 136 | ---@param body string 137 | ---@return string 138 | local function make_namespace(name, body) 139 | local result = '' 140 | 141 | result = result .. '---@class defold_api.' .. name .. '\n' 142 | result = result .. name .. ' = {}\n\n' 143 | result = result .. body .. '\n\n' 144 | result = result .. 'return ' .. name 145 | 146 | return result 147 | end 148 | 149 | ---Make an annotatable constant 150 | ---@param element element 151 | ---@return string 152 | local function make_const(element) 153 | local result = '' 154 | 155 | result = result .. make_comment(element.description) .. '\n' 156 | result = result .. element.name .. ' = nil' 157 | 158 | return result 159 | end 160 | 161 | ---Make an annotatable param name 162 | ---@param parameter table 163 | ---@param is_return boolean 164 | ---@param element element 165 | ---@return string name 166 | local function make_param_name(parameter, is_return, element) 167 | local name = parameter.name 168 | name = config.global_name_replacements[name] or name 169 | 170 | local local_replacements = config.local_name_replacements[element.name] or {} 171 | name = local_replacements[(is_return and 'return_' or 'param_') .. name] or name 172 | 173 | if name:sub(-3) == '...' then 174 | name = '...' 175 | end 176 | 177 | name = name:gsub('-', '_') 178 | 179 | return name 180 | end 181 | 182 | ---Make annotatable param types 183 | ---@param name string 184 | ---@param types table 185 | ---@param is_return boolean 186 | ---@param element element 187 | ---@return string concated_string 188 | local function make_param_types(name, types, is_return, element) 189 | local local_replacements = config.local_type_replacements[element.name] or {} 190 | 191 | for index = 1, #types do 192 | local type = types[index] 193 | local is_known = false 194 | local replacement 195 | 196 | for key, value in pairs(config.global_type_replacements) do 197 | if utils.match(type, key) then 198 | replacement = value 199 | end 200 | end 201 | 202 | replacement = local_replacements[(is_return and 'return_' or 'param_') .. type .. '_' .. name] or replacement 203 | 204 | if replacement then 205 | type = replacement 206 | is_known = true 207 | end 208 | 209 | for _, known_type in ipairs(config.known_types) do 210 | is_known = is_known or type == known_type 211 | end 212 | 213 | local known_classes = utils.sorted_keys(config.known_classes) 214 | for _, known_class in ipairs(known_classes) do 215 | is_known = is_known or type == known_class 216 | end 217 | 218 | local known_aliases = utils.sorted_keys(config.known_aliases) 219 | for _, known_alias in ipairs(known_aliases) do 220 | is_known = is_known or type == known_alias 221 | end 222 | 223 | is_known = is_known or type:sub(1, 9) == 'function(' 224 | 225 | if not is_known then 226 | types[index] = config.unknown_type 227 | 228 | if os.getenv('LOCAL_LUA_DEBUGGER_VSCODE') == '1' then 229 | assert(nil, 'Unknown type `' .. type .. '`') 230 | else 231 | print('!!! WARNING: Unknown type `' .. type .. '` has been replaced with `' .. config.unknown_type .. '`') 232 | end 233 | else 234 | type = type:gsub('function%(%)', 'function') 235 | 236 | if type:sub(1, 9) == 'function(' then 237 | type = 'fun' .. type:sub(9) 238 | end 239 | 240 | types[index] = type 241 | end 242 | end 243 | 244 | local result = table.concat(utils.unique(types), '|') 245 | result = #result > 0 and result or config.unknown_type 246 | 247 | return result 248 | end 249 | 250 | ---Make an annotable param description 251 | ---@param description string 252 | ---@return string 253 | local function make_param_description(description) 254 | local result = decode_text(description) 255 | result = result:gsub('^%s+', '') 256 | result = result:gsub('\n', '\n---') 257 | return result 258 | end 259 | 260 | ---Make annotable param line 261 | ---@param parameter table 262 | ---@param element element 263 | ---@return string 264 | local function make_param(parameter, element) 265 | local name = make_param_name(parameter, false, element) 266 | local is_optional = parameter.is_optional == 'True' 267 | local joined_types = make_param_types(name, parameter.types, false, element) 268 | local description = make_param_description(parameter.doc) 269 | 270 | return '---@param ' .. name .. (is_optional and '? ' or ' ') .. joined_types .. ' ' .. description 271 | end 272 | 273 | ---Make an annotable return line 274 | ---@param returnvalue table 275 | ---@param element element 276 | ---@return string 277 | local function make_return(returnvalue, element) 278 | local name = make_param_name(returnvalue, true, element) 279 | local types = make_param_types(name, returnvalue.types, true, element) 280 | local description = make_param_description(returnvalue.doc) 281 | 282 | return '---@return ' .. types .. ' ' .. name .. ' ' .. description 283 | end 284 | 285 | ---Make annotable func lines 286 | ---@param element element 287 | ---@return string? 288 | local function make_func(element) 289 | if utils.is_blacklisted(config.ignored_funcs, element.name) then 290 | return 291 | end 292 | 293 | local comment = make_comment(element.description) .. '\n' 294 | 295 | local generic = config.generics[element.name] 296 | local generic_occuriences = 0 297 | 298 | local params = '' 299 | for _, parameter in ipairs(element.parameters) do 300 | local param = make_param(parameter, element) 301 | local count = 0 302 | 303 | if generic then 304 | param, count = param:gsub(' ' .. generic .. ' ', ' T ') 305 | generic_occuriences = generic_occuriences + count 306 | end 307 | 308 | params = params .. param .. '\n' 309 | end 310 | 311 | local returns = '' 312 | for _, returnvalue in ipairs(element.returnvalues) do 313 | local return_ = make_return(returnvalue, element) 314 | local count = 0 315 | 316 | if generic then 317 | return_, count = return_:gsub(' ' .. generic .. ' ', ' T ') 318 | generic_occuriences = generic_occuriences + count 319 | end 320 | 321 | returns = returns .. return_ .. '\n' 322 | end 323 | 324 | if generic_occuriences >= 2 then 325 | generic = ('---@generic T: ' .. generic .. '\n') 326 | else 327 | generic = '' 328 | end 329 | 330 | local func_params = {} 331 | 332 | for _, parameter in ipairs(element.parameters) do 333 | local name = make_param_name(parameter, false, element) 334 | table.insert(func_params, name) 335 | end 336 | 337 | local func = 'function ' .. element.name .. '(' .. table.concat(func_params, ', ') .. ') end' 338 | local result = comment .. generic .. params .. returns .. func 339 | 340 | return result 341 | end 342 | 343 | ---Make an annotable alias 344 | ---@param element element 345 | ---@return string 346 | local function make_alias(element) 347 | return '---@alias ' .. element.name .. ' ' .. element.alias 348 | end 349 | 350 | ---Make an annnotable class declaration 351 | ---@param element element 352 | ---@return string 353 | local function make_class(element) 354 | local name = element.name 355 | local fields = element.fields 356 | assert(fields) 357 | 358 | local result = '' 359 | result = result .. '---@class ' .. name .. '\n' 360 | 361 | if fields.is_global == true then 362 | fields.is_global = nil 363 | result = result .. name .. ' = {}' 364 | end 365 | 366 | local field_names = utils.sorted_keys(fields) 367 | for index, field_name in ipairs(field_names) do 368 | local type = fields[field_name] 369 | 370 | result = result .. '---@field ' .. field_name .. ' ' .. type 371 | 372 | if index < #field_names then 373 | result = result .. '\n' 374 | end 375 | end 376 | 377 | local operators = element.operators 378 | 379 | if operators then 380 | local operator_names = utils.sorted_keys(operators) 381 | 382 | result = result .. '\n' 383 | 384 | for index, operator_name in ipairs(operator_names) do 385 | local operator = operators[operator_name] 386 | 387 | if operator.param then 388 | result = result .. '---@operator ' .. operator_name .. '(' .. operator.param .. '): ' .. operator.result 389 | else 390 | result = result .. '---@operator ' .. operator_name .. ': ' .. operator.result 391 | end 392 | 393 | if index < #operator_names then 394 | result = result .. '\n' 395 | end 396 | end 397 | end 398 | 399 | return result 400 | end 401 | 402 | ---Generate API module with creating a .lua file 403 | ---@param module module 404 | ---@param defold_version string like '1.0.0' 405 | local function generate_api(module, defold_version) 406 | local content = make_header(defold_version, module.info.brief, module.info.description) 407 | content = content .. '\n\n' 408 | 409 | local makers = { 410 | FUNCTION = make_func, 411 | VARIABLE = make_const, 412 | CONSTANT = make_const, 413 | BASIC_CLASS = make_class, 414 | BASIC_ALIAS = make_alias 415 | } 416 | 417 | local elements = {} 418 | local namespace_is_required = false 419 | 420 | for _, element in ipairs(module.elements) do 421 | if makers[element.type] ~= nil then 422 | table.insert(elements, element) 423 | end 424 | 425 | if not namespace_is_required then 426 | local element_has_namespace = element.name:sub(1, #module.info.namespace) == module.info.namespace 427 | namespace_is_required = element_has_namespace 428 | end 429 | end 430 | 431 | if #elements == 0 then 432 | print('[-] The module "' .. module.info.namespace .. '" is skipped because there are no known elements') 433 | return 434 | end 435 | 436 | table.sort(elements, function(a, b) 437 | if a.type == b.type then 438 | return a.name < b.name 439 | else 440 | return a.type > b.type 441 | end 442 | end) 443 | 444 | local body = '' 445 | 446 | for index, element in ipairs(elements) do 447 | local maker = makers[element.type] 448 | local text = maker(element) 449 | 450 | if text then 451 | body = body .. text 452 | 453 | if index < #elements then 454 | local newline = element.type == 'BASIC_ALIAS' and '\n' or '\n\n' 455 | body = body .. newline 456 | end 457 | end 458 | end 459 | 460 | content = content .. make_disabled_diagnostics(config.disabled_diagnostics) .. '\n\n' 461 | 462 | if namespace_is_required then 463 | content = content .. make_namespace(module.info.namespace, body) 464 | else 465 | content = content .. body 466 | end 467 | 468 | local api_path = config.api_folder .. config.folder_separator .. module.info.namespace .. '.lua' 469 | utils.save_file(content, api_path) 470 | end 471 | 472 | -- 473 | -- Public 474 | 475 | ---Generate API modules with creating .lua files 476 | ---@param modules module[] 477 | ---@param defold_version string like '1.0.0' 478 | function generator.generate_api(modules, defold_version) 479 | print('-- Annotations Generation') 480 | 481 | terminal.delete_folder(config.api_folder) 482 | terminal.create_folder(config.api_folder) 483 | 484 | local merged_modules = {} 485 | 486 | for _, module in ipairs(modules) do 487 | local namespace = module.info.namespace 488 | 489 | -- The `font` module doesn't have any meta values 490 | if #namespace == 0 and #module.elements > 0 then 491 | namespace = module.elements[1].name:match('([^%.]+)') 492 | module.info.namespace = namespace 493 | end 494 | 495 | local merged_module = merged_modules[namespace] 496 | 497 | if merged_module then 498 | for _, element in ipairs(module.elements) do 499 | table.insert(merged_module.elements, element) 500 | end 501 | 502 | if module.info.description ~= module.info.brief then 503 | merged_module.info.description = module.info.description 504 | end 505 | else 506 | merged_modules[namespace] = module 507 | end 508 | end 509 | 510 | for _, module in pairs(merged_modules) do 511 | generate_api(module, defold_version) 512 | end 513 | 514 | print('-- Annotations Generated Successfully!\n') 515 | end 516 | 517 | return generator 518 | -------------------------------------------------------------------------------- /src/config.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | config.lua 3 | github.com/astrochili/defold-annotations 4 | 5 | Copyright (c) 2023 Roman Silin 6 | MIT license. See LICENSE for details. 7 | --]] 8 | 9 | local config = {} 10 | 11 | ---Folder separator 12 | config.folder_separator = package.config:sub(1, 1) 13 | 14 | ---Clean temporary files after completion 15 | config.clean_traces = true 16 | 17 | ---Url of this project on github 18 | config.generator_url = 'github.com/astrochili/defold-annotations' 19 | 20 | ---Url to find out the latest version of Defold 21 | function config.info_url() 22 | return 'https://d.defold.com/stable/' .. config.info_json 23 | end 24 | 25 | ---File name of the info about the letest version 26 | config.info_json = 'info.json' 27 | 28 | ---Url to find out the documentation archive 29 | function config.doc_url(version) 30 | return 'https://github.com/defold/defold/releases/download/' .. version .. '/' .. config.doc_zip 31 | end 32 | 33 | ---File name of the documentation archive 34 | config.doc_zip = 'ref-doc.zip' 35 | 36 | ---Name of the unpacked doc folder 37 | config.doc_folder = 'doc' 38 | 39 | ---Json extension 40 | config.json_extension = 'json' 41 | 42 | ---Name of a temporary text file with paths to json files 43 | config.json_list_txt = 'json_list.txt' 44 | 45 | ---Name of the output folder 46 | config.api_folder = 'api' 47 | 48 | ---Ignored docs 49 | ---Possible to use suffix `*` 50 | config.ignored_docs = { 51 | -- Starting from 1.10.2 52 | 'dmsdk-*', 53 | 'lua_*', 54 | 55 | -- Before 1.10.2 56 | 'dm*', 57 | 'debug_doc', 58 | 'coroutine_doc', 59 | 'math_doc', 60 | 'package_doc', 61 | 'string_doc', 62 | 'table_doc', 63 | 'engine_doc', 64 | 'base_doc', 65 | 'os_doc', 66 | 'io_doc' 67 | } 68 | 69 | ---Ignored functions 70 | ---Possible to use suffix `*` 71 | config.ignored_funcs = { 72 | 'init', 73 | 'update', 74 | 'fixed_update', 75 | 'on_input', 76 | 'on_message', 77 | 'on_reload', 78 | 'final', 79 | 'client:*', 80 | 'server:*', 81 | 'master:*', 82 | 'connected:*', 83 | 'unconnected:*' 84 | } 85 | 86 | --- Global replacements for param names 87 | config.global_name_replacements = { 88 | ['repeat'] = 'repeating', 89 | ['...commands'] = '...' 90 | } 91 | 92 | --- Local replacements for param names 93 | config.local_name_replacements = { 94 | pprint = { 95 | param_v = '...' 96 | } 97 | } 98 | 99 | --- Global replacements for param types 100 | config.global_type_replacements = { 101 | quat = 'quaternion', 102 | resource = 'resource_data', 103 | buffer = 'buffer_data', 104 | bufferstream = 'buffer_stream', 105 | handle = 'number', 106 | texture = 'number', 107 | predicate = 'render_predicate', 108 | client = 'socket_client', 109 | master = 'socket_master', 110 | unconnected = 'socket_unconnected', 111 | ['vmath.vector3'] = 'vector3', 112 | ['vmath.vector4'] = 'vector4', 113 | vecto4 = 'vector4', 114 | schema = 'editor.schema', 115 | component = 'editor.component', 116 | tiles = 'editor.tiles', 117 | transaction_step = 'editor.transaction_step', 118 | command = 'editor.command', 119 | response = 'http.response', 120 | route = 'http.route', 121 | ['type:graphics.*'] = 'constant', 122 | ['graphics.*'] = 'constant', 123 | ['go.*'] = 'constant', 124 | } 125 | 126 | --- Local replacements for param types 127 | config.local_type_replacements = { 128 | ['buffer.create'] = { 129 | param_table_declaration = '{ name:hash|string, type:constant, count:number }[]' 130 | }, 131 | ['buffer.set_metadata'] = { 132 | param_table_values = 'number[]' 133 | }, 134 | ['buffer.get_metadata'] = { 135 | return_table_values = 'number[]' 136 | }, 137 | ['collectionfactory.create'] = { 138 | return_table_ids = 'table' 139 | }, 140 | ['collectionproxy.get_resources'] = { 141 | return_table_resources = 'string[]' 142 | }, 143 | ['collectionproxy.missing_resources'] = { 144 | return_table_resources = 'string[]' 145 | }, 146 | ['crash.get_modules'] = { 147 | return_table_modules = '{ name:string, address:string }[]' 148 | }, 149 | ['editor.create_resources'] = { 150 | ['param_string[_resources'] = 'string[]' 151 | }, 152 | ['editor.bundle.check_boxes_grid_row'] = { 153 | ['return_component[_row'] = 'editor.component[]' 154 | }, 155 | ['editor.bundle.common_variant_grid_row'] = { 156 | ['return_component[_row'] = 'editor.component[]' 157 | }, 158 | ['editor.bundle.desktop_variant_grid_row'] = { 159 | ['return_component[_row'] = 'editor.component[]' 160 | }, 161 | ['editor.bundle.dialog'] = { 162 | ['param_component[_rows'] = 'editor.component[]' 163 | }, 164 | ['editor.bundle.grid_row'] = { 165 | ['param_component[_content'] = 'editor.component[]', 166 | ['return_component[_row'] = 'editor.component[]' 167 | }, 168 | ['editor.bundle.texture_compression_grid_row'] = { 169 | ['return_component[_row'] = 'editor.component[]' 170 | }, 171 | ['editor.bundle.assoc_in'] = { 172 | ['param_any[_keys'] = 'any[]' 173 | }, 174 | ['editor.bundle.select_box'] = { 175 | ['param_any[_options'] = 'any[]' 176 | }, 177 | ['editor.transact'] = { 178 | ['param_transaction_step[_txs'] = 'editor.transaction_step[]' 179 | }, 180 | ['editor.execute'] = { 181 | param_table_options = '{ reload_resources?:boolean, out?:string, err?:string }' 182 | }, 183 | ['editor.external_file_attributes'] = { 184 | return_table_attributes = '{ path:string, exists:boolean, is_file:boolean, is_directory:boolean }' 185 | }, 186 | ['editor.prefs.schema.array'] = { 187 | param_table_opts = '{ item:editor.schema, default?:any[], scope?:string }' 188 | }, 189 | ['editor.prefs.schema.boolean'] = { 190 | param_table_opts = '{ default?:boolean, scope?:string }' 191 | }, 192 | ['editor.prefs.schema.enum'] = { 193 | param_table_opts = '{ values:(nil|boolean|number|string)[], default?:any, scope?:string }' 194 | }, 195 | ['editor.prefs.schema.integer'] = { 196 | param_table_opts = '{ default?:integer, scope?:string }' 197 | }, 198 | ['editor.prefs.schema.keyword'] = { 199 | param_table_opts = '{ default?:string, scope?:string }' 200 | }, 201 | ['editor.prefs.schema.number'] = { 202 | param_table_opts = '{ default?:number, scope?:string }' 203 | }, 204 | ['editor.prefs.schema.object'] = { 205 | param_table_opts = '{ properties:table, default?:table, scope?:string }' 206 | }, 207 | ['editor.prefs.schema.object_of'] = { 208 | param_table_opts = '{ key:editor.schema, val:editor.schema, default?:table, scope?:string }' 209 | }, 210 | ['editor.prefs.schema.set'] = { 211 | param_table_opts = '{ item:editor.schema, default?:table, scope?:string }' 212 | }, 213 | ['editor.prefs.schema.string'] = { 214 | param_table_opts = '{ default?:string, scope?:string }' 215 | }, 216 | ['editor.prefs.schema.tuple'] = { 217 | param_table_opts = '{ items:editor.schema[], default?:any[], scope?:string }' 218 | }, 219 | ['editor.resource_attributes'] = { 220 | return_table_value = '{ exists:boolean, is_file:boolean, is_directory:boolean }' 221 | }, 222 | ['editor.ui.use_memo'] = { 223 | ['param_...any_...'] = 'any', 224 | ['return_...any_values'] = 'any ...' 225 | }, 226 | ['editor.ui.use_state'] = { 227 | ['param_...any_...'] = 'any' 228 | }, 229 | ['editor.ui.show_resource_dialog'] = { 230 | ['return_string[_value'] = 'string[]|nil' 231 | }, 232 | ['gui.clone_tree'] = { 233 | return_table_clones = 'table' 234 | }, 235 | ['gui.get_tree'] = { 236 | return_table_clones = 'table' 237 | }, 238 | ['gui.play_flipbook'] = { 239 | param_table_play_properties = '{ offset?:number, playback_rate?:number }' 240 | }, 241 | ['gui.stop_particlefx'] = { 242 | param_table_options = '{ clear?:boolean }' 243 | }, 244 | ['gui.get_layouts'] = { 245 | ['return_table_'] = 'table' 246 | }, 247 | ['http.server.external_file_response'] = { 248 | ['param_table<string,string>_headers'] = 'table' 249 | }, 250 | ['http.server.json_response'] = { 251 | ['param_table<string,string>_headers'] = 'table' 252 | }, 253 | ['http.server.resource_response'] = { 254 | ['param_table<string,string>_headers'] = 'table' 255 | }, 256 | ['http.server.response'] = { 257 | ['param_table<string,string>_headers'] = 'table' 258 | }, 259 | ['json.decode'] = { 260 | param_table_options = '{ decode_null_as_userdata?:boolean }' 261 | }, 262 | ['json.encode'] = { 263 | param_table_options = '{ encode_empty_table_as_object:string }' 264 | }, 265 | ['particlefx.stop'] = { 266 | param_table_options = '{ clear?:boolean }' 267 | }, 268 | ['sprite.play_flipbook'] = { 269 | param_table_options = '{ offset?:number, playback_rate?:number }' 270 | }, 271 | ['sound.play'] = { 272 | param_table_play_properties = '{ delay?:number, gain?:number, pan?:number, speed?:number, start_time?:number, start_frame?:number }' 273 | }, 274 | ['sound.stop'] = { 275 | param_table_stop_properties = '{ play_id:number }' 276 | }, 277 | ['model.play_anim'] = { 278 | param_table_play_properties = '{ blend_duration?:number, offset?:number, playback_rate?:number}' 279 | }, 280 | ['image.load'] = { 281 | return_table_image = '{ width:number, height:number, type:constant, buffer:string }' 282 | }, 283 | ['image.load_buffer'] = { 284 | return_table_image = '{ width:number, height:number, type:constant, buffer:buffer_data }' 285 | }, 286 | ['physics.get_joint_properties'] = { 287 | return_table_properties = '{ collide_connected?:boolean }' 288 | }, 289 | ['physics.raycast'] = { 290 | param_table_options = '{ all?:boolean }', 291 | return_table_result = 'physics.raycast_response[]|physics.raycast_response' 292 | }, 293 | ['physics.get_shape'] = { 294 | return_table_table = '{ type?:number, diameter?:number, dimensions?:vector3, height?:number }' 295 | }, 296 | ['physics.set_shape'] = { 297 | param_table_table = '{ diameter?:number, dimensions?:vector3, height?:number }' 298 | }, 299 | ['resource.create_atlas'] = { 300 | param_table_table = 'resource.atlas' 301 | }, 302 | ['resource.get_atlas'] = { 303 | return_table_data = 'resource.atlas' 304 | }, 305 | ['resource.set_atlas'] = { 306 | param_table_table = 'resource.atlas' 307 | }, 308 | ['resource.get_render_target_info'] = { 309 | return_table_table = '{ handle:number, attachments:{ handle:number, width:number, height:number, depth:number, mipmaps:number, type:number, buffer_type:number, texture:hash }[] }' 310 | }, 311 | ['resource.create_sound_data'] = { 312 | param_table_options = '{ data?:string, filesize?:number, partial?:boolean }' 313 | }, 314 | ['resource.create_texture'] = { 315 | param_table_table = '{ type:number, width:number, height:number, depth:number, format:number, flags?:number, max_mipmaps?:number, compression_type?:number}' 316 | }, 317 | ['resource.create_texture_async'] = { 318 | param_table_table = '{ type:number, width:number, height:number, depth:number, format:number, flags?:number, max_mipmaps?:number, compression_type?:number}', 319 | param_function_callback = 'fun(self, request_id, resource)' 320 | }, 321 | ['resource.set_texture'] = { 322 | param_table_table = '{ type:number, width:number, height:number, format:number, x?:number, y?:number, z?:number, mipmap?:number, compression_type?:number}' 323 | }, 324 | ['resource.get_texture_info'] = { 325 | return_table_table = '{ handle:number, width:number, height:number, depth:number, mipmaps:number, flags:number, type:number }' 326 | }, 327 | ['resource.get_text_metrics'] = { 328 | param_table_options = '{ width?:number, leading?:number, tracking?:number, line_break?:boolean}', 329 | return_table_metrics = '{ width:number, height:number, max_ascent:number, max_descent:number }' 330 | }, 331 | ['resource.create_buffer'] = { 332 | param_table_table = '{ buffer:buffer_data, transfer_ownership?:boolean }' 333 | }, 334 | ['resource.set_buffer'] = { 335 | param_table_table = '{ transfer_ownership?: boolean }' 336 | }, 337 | ['render.draw'] = { 338 | param_table_options = '{ frustum?:matrix4, frustum_planes?:number, constants?:constant_buffer, sort_order?:integer }' 339 | }, 340 | ['render.draw_debug3d'] = { 341 | param_table_options = '{ frustum?:matrix4, frustum_planes?:number }' 342 | }, 343 | ['render.predicate'] = { 344 | param_table_tags = '(string|hash)[]' 345 | }, 346 | ['render.render_target'] = { 347 | param_table_parameters = 'table' 348 | }, 349 | ['render.set_camera'] = { 350 | param_table_options = '{ use_frustum?:boolean }' 351 | }, 352 | ['render.set_render_target'] = { 353 | param_table_options = '{ transient?:number[] }' 354 | }, 355 | ['sound.get_groups'] = { 356 | return_table_groups = 'hash[]' 357 | }, 358 | ['sys.get_sys_info'] = { 359 | param_table_options = '{ ignore_secure?:boolean }', 360 | return_table_sys_info = '{ device_model?:string, manufacturer?:string, system_name:string, system_version:string, api_version:string, language:string, device_language:string, territory:string, gmt_offset:number, device_ident?:string, user_agent?:string }' 361 | }, 362 | ['sys.get_application_info'] = { 363 | return_table_app_info = '{ installed:boolean }' 364 | }, 365 | ['sys.get_engine_info'] = { 366 | return_table_engine_info = '{ version:string, version_sha1:string, is_debug:boolean }' 367 | }, 368 | ['sys.get_ifaddrs'] = { 369 | return_table_ifaddrs = '{ name:string, address?:string, mac?:string, up:boolean, running:boolean }' 370 | }, 371 | ['sys.open_url'] = { 372 | param_table_attributes = '{ target?:string, name?:string }' 373 | }, 374 | ['timer.get_info'] = { 375 | return_table_data = '{ time_remaining:number, delay:number, repeating:boolean }' 376 | }, 377 | ['vmath.euler_to_quat'] = { 378 | param_number_y = 'number?', 379 | param_number_z = 'number?' 380 | }, 381 | ['vmath.vector'] = { 382 | param_table_t = 'number[]' 383 | }, 384 | ['zip.pack'] = { 385 | param_table_opts = '{ method?:string, level?:integer }' 386 | } 387 | } 388 | 389 | config.generics = { 390 | ['vmath.clamp'] = 'number|vector3|vector4', 391 | ['vmath.dot'] = 'vector3|vector4', 392 | ['vmath.normalize'] = 'vector3|vector4|quaternion', 393 | ['vmath.mul_per_elem'] = 'vector3|vector4', 394 | ['vmath.slerp'] = 'vector3|vector4', 395 | ['vmath.lerp'] = 'vector3|vector4' 396 | } 397 | 398 | ---Default type for unknown types 399 | config.unknown_type = 'unknown' 400 | 401 | ---Known types 402 | config.known_types = { 403 | 'nil', 404 | 'any', 405 | 'boolean', 406 | 'number', 407 | 'integer', 408 | 'string', 409 | 'userdata', 410 | 'function', 411 | 'thread', 412 | 'table' 413 | } 414 | 415 | ---Known classes 416 | config.known_classes = { 417 | vector3 = { 418 | fields = { 419 | x = 'number', 420 | y = 'number', 421 | z = 'number' 422 | }, 423 | operators = { 424 | sub = { param = 'vector3', result = 'vector3' }, 425 | add = { param = 'vector3', result = 'vector3' }, 426 | mul = { param = 'number', result = 'vector3' }, 427 | unm = { result = 'vector3' } 428 | } 429 | }, 430 | vector4 = { 431 | fields = { 432 | x = 'number', 433 | y = 'number', 434 | z = 'number', 435 | w = 'number' 436 | }, 437 | operators = { 438 | sub = { param = 'vector4', result = 'vector4' }, 439 | add = { param = 'vector4', result = 'vector4' }, 440 | mul = { param = 'number', result = 'vector4' }, 441 | unm = { result = 'vector4' } 442 | } 443 | }, 444 | url = { 445 | socket = 'hash', 446 | path = 'hash', 447 | fragment = 'hash' 448 | }, 449 | ['http.server'] = { 450 | is_global = true 451 | }, 452 | ['zip'] = { 453 | is_global = true 454 | }, 455 | ['zip.METHOD'] = { 456 | is_global = true 457 | }, 458 | ['zip.ON_CONFLICT'] = { 459 | is_global = true 460 | }, 461 | ['socket.dns'] = { 462 | is_global = true 463 | }, 464 | ['editor.ui'] = { 465 | is_global = true 466 | }, 467 | ['editor.ui.ALIGNMENT'] = { 468 | is_global = true 469 | }, 470 | ['editor.ui.COLOR'] = { 471 | is_global = true 472 | }, 473 | ['editor.ui.HEADING_STYLE'] = { 474 | is_global = true 475 | }, 476 | ['editor.ui.ICON'] = { 477 | is_global = true 478 | }, 479 | ['editor.ui.ISSUE_SEVERITY'] = { 480 | is_global = true 481 | }, 482 | ['editor.ui.ORIENTATION'] = { 483 | is_global = true 484 | }, 485 | ['editor.ui.PADDING'] = { 486 | is_global = true 487 | }, 488 | ['editor.ui.SPACING'] = { 489 | is_global = true 490 | }, 491 | ['editor.ui.TEXT_ALIGNMENT'] = { 492 | is_global = true 493 | }, 494 | ['editor.prefs'] = { 495 | is_global = true 496 | }, 497 | ['editor.prefs.SCOPE'] = { 498 | is_global = true 499 | }, 500 | ['editor.prefs.schema'] = { 501 | is_global = true 502 | }, 503 | ['editor.tx'] = { 504 | is_global = true 505 | }, 506 | ['tilemap.tiles'] = { 507 | is_global = true 508 | }, 509 | ['editor.bundle'] = { 510 | is_global = true 511 | }, 512 | matrix4 = { 513 | m00 = 'number', 514 | m01 = 'number', 515 | m02 = 'number', 516 | m03 = 'number', 517 | m10 = 'number', 518 | m11 = 'number', 519 | m12 = 'number', 520 | m13 = 'number', 521 | m20 = 'number', 522 | m21 = 'number', 523 | m22 = 'number', 524 | m23 = 'number', 525 | m30 = 'number', 526 | m31 = 'number', 527 | m32 = 'number', 528 | m33 = 'number', 529 | c0 = 'vector4', 530 | c1 = 'vector4', 531 | c2 = 'vector4', 532 | c3 = 'vector4', 533 | }, 534 | ['resource.atlas'] = { 535 | texture = 'string|hash The path to the texture resource, e.g "/main/my_texture.texturec"', 536 | animations = 'resource.animation[] A list of the animations in the atlas', 537 | geometries = 'resource.geometry[] A list of the geometries that should map to the texture data', 538 | }, 539 | ['resource.animation'] = { 540 | id = 'string The id of the animation, used in e.g sprite.play_animation', 541 | width = 'integer The width of the animation', 542 | height = 'integer The height of the animation', 543 | frame_start = 'integer Index to the first geometry of the animation. Indices are lua based and must be in the range of 1 .. in atlas.', 544 | frame_end = 'integer Index to the last geometry of the animation (non-inclusive). Indices are lua based and must be in the range of 1 .. in atlas.', 545 | ['playback?'] = 'constant Optional playback mode of the animation, the default value is go.PLAYBACK_ONCE_FORWARD', 546 | ['fps?'] = 'integer Optional fps of the animation, the default value is 30', 547 | ['flip_vertical?'] = 'boolean Optional flip the animation vertically, the default value is false', 548 | ['flip_horizontal?'] = 'boolean Optional flip the animation horizontally, the default value is false' 549 | }, 550 | ['resource.geometry'] = { 551 | id = 'string The name of the geometry. Used when matching animations between multiple atlases', 552 | width = 'number The width of the image the sprite geometry represents', 553 | height = 'number The height of the image the sprite geometry represents', 554 | pivot_x = 'number The pivot x value of the image in unit coords. (0,0) is upper left corner, (1,1) is bottom right. Default is 0.5.', 555 | pivot_y = 'number The pivot y value of the image in unit coords. (0,0) is upper left corner, (1,1) is bottom right. Default is 0.5.', 556 | rotated = 'boolean Whether the image is rotated 90 degrees counter-clockwise in the atlas. This affects UV coordinate generation for proper rendering. Default is false.', 557 | vertices = 'number[] A list of the vertices in texture space of the geometry in the form { px0, py0, px1, py1, ..., pxn, pyn }', 558 | uvs = 'number[] A list of the uv coordinates in texture space of the geometry in the form of { u0, v0, u1, v1, ..., un, vn }', 559 | indices = 'number[] A list of the indices of the geometry in the form { i0, i1, i2, ..., in }. Each tripe in the list represents a triangle.' 560 | }, 561 | ['physics.raycast_response'] = { 562 | fraction = 'number The fraction of the hit measured along the ray, where 0 is the start of the ray and 1 is the end', 563 | position = 'vector3 The world position of the hit', 564 | normal = 'vector3 The normal of the surface of the collision object where it was hit', 565 | id = 'hash The instance id of the hit collision object', 566 | group = 'hash The collision group of the hit collision object as a hashed name', 567 | request_id = 'number The id supplied when the ray cast was requested' 568 | }, 569 | ['on_input.action'] = { 570 | ['value?'] = 'number The amount of input given by the user. This is usually 1 for buttons and 0-1 for analogue inputs. This is not present for mouse movement.', 571 | ['pressed?'] = 'boolean If the input was pressed this frame. This is not present for mouse movement.', 572 | ['released?'] = 'boolean If the input was released this frame. This is not present for mouse movement.', 573 | ['repeated?'] = 'boolean If the input was repeated this frame. This is similar to how a key on a keyboard is repeated when you hold it down. This is not present for mouse movement.', 574 | ['x?'] = 'number The x value of a pointer device, if present.', 575 | ['y?'] = 'number The y value of a pointer device, if present.', 576 | ['screen_x?'] = 'number The screen space x value of a pointer device, if present.', 577 | ['screen_y?'] = 'number The screen space y value of a pointer device, if present.', 578 | ['dx?'] = 'number The change in x value of a pointer device, if present.', 579 | ['dy?'] = 'number The change in y value of a pointer device, if present.', 580 | ['screen_dx?'] = 'number The change in screen space x value of a pointer device, if present.', 581 | ['gamepad?'] = 'integer The change in screen space y value of a pointer device, if present.', 582 | ['screen_dy?'] = 'number The index of the gamepad device that provided the input.', 583 | ['touch?'] = 'on_input.touch[] List of touch input, one element per finger, if present.', 584 | ['text?'] = 'string The text entered with the `text` action, if present' 585 | }, 586 | ['on_input.touch'] = { 587 | id = 'number A number identifying the touch input during its duration.', 588 | pressed = 'boolean True if the finger was pressed this frame.', 589 | released = 'boolean True if the finger was released this frame.', 590 | tap_count = 'integer Number of taps, one for single, two for double-tap, etc', 591 | x = 'number The x touch location.', 592 | y = 'number The y touch location.', 593 | dx = 'number The change in x value.', 594 | dy = 'number The change in y value.', 595 | ['acc_x?'] = 'number Accelerometer x value (if present).', 596 | ['acc_y?'] = 'number Accelerometer y value (if present).', 597 | ['acc_z?'] = 'number Accelerometer z value (if present).', 598 | ['screen_x?'] = 'number The screen space x value of a pointer device, if present.', 599 | ['screen_y?'] = 'number The screen space y value of a pointer device, if present.', 600 | ['screen_dx?'] = 'number The change in screen space x value of a pointer device, if present.', 601 | ['screen_dy?'] = 'number The index of the gamepad device that provided the input.' 602 | } 603 | } 604 | 605 | ---Known aliases 606 | config.known_aliases = { 607 | bool = 'boolean', 608 | float = 'number', 609 | array = 'table', 610 | 611 | quaternion = 'vector4', 612 | hash = 'userdata', 613 | node = 'userdata', 614 | constant = 'number', 615 | vector = 'userdata', 616 | 617 | resource_data = 'userdata', 618 | constant_buffer = 'userdata', 619 | render_target = 'string|userdata', 620 | render_predicate = 'userdata', 621 | buffer_stream = 'userdata', 622 | buffer_data = 'userdata', 623 | 624 | b2BodyType = 'number', 625 | b2World = 'userdata', 626 | b2Body = 'userdata', 627 | 628 | socket_client = 'userdata', 629 | socket_master = 'userdata', 630 | socket_unconnected = 'userdata', 631 | 632 | ['editor.schema'] = 'userdata', 633 | ['editor.component'] = 'userdata', 634 | ['editor.transaction_step'] = 'userdata', 635 | ['editor.tiles'] = 'userdata', 636 | ['editor.command'] = 'userdata', 637 | ['http.response'] = 'userdata', 638 | ['http.route'] = 'userdata' 639 | } 640 | 641 | config.disabled_diagnostics = { 642 | 'lowercase-global', 643 | 'missing-return', 644 | 'duplicate-doc-param', 645 | 'duplicate-set-field', 646 | 'args-after-dots' 647 | } 648 | 649 | return config 650 | -------------------------------------------------------------------------------- /libs/html_entities.lua: -------------------------------------------------------------------------------- 1 | -- Module options: 2 | local error_msg_htmlEntities = false 3 | local debug_htmlEntities = false 4 | local ASCII_htmlEntities = true 5 | local register_global_module_htmlEntities = false 6 | local global_module_name_htmlEntities = 'htmlEntities' 7 | 8 | local htmlEntities = { 9 | about = 'HTML entities decoding/encoding', 10 | version = '1.3.1', 11 | name = 'htmlEntities-for-lua', 12 | author = 'Tiago Danin', 13 | license = 'MIT', 14 | page = 'https://TiagoDanin.github.io/htmlEntities-for-lua/' 15 | } 16 | 17 | if register_global_module_htmlEntities then 18 | _G[global_module_name_htmlEntities] = htmlEntities 19 | end 20 | 21 | local htmlEntities_table = { 22 | [' '] = ' ', 23 | [' '] = '\n', 24 | ['!'] = '!', 25 | ['"'] = '"', 26 | ['"'] = '"', 27 | ['"'] = '"', 28 | ['"'] = '"', 29 | ['#'] = '#', 30 | ['$'] = '$', 31 | ['%'] = '%%', 32 | ['&'] = '&', 33 | ['&'] = '&', 34 | ['&'] = '&', 35 | ['&'] = '&', 36 | ['''] = "'", 37 | ['('] = '(', 38 | [')'] = ')', 39 | ['*'] = '*', 40 | ['*'] = '*', 41 | ['+'] = '+', 42 | [','] = ',', 43 | ['.'] = '.', 44 | ['/'] = '/', 45 | [':'] = ':', 46 | [';'] = ';', 47 | ['<'] = '<', 48 | ['<'] = '<', 49 | ['<'] = '<', 50 | ['<'] = '<', 51 | ['<⃒'] = '<⃒', 52 | ['='] = '=', 53 | ['=⃥'] = '=⃥', 54 | ['>'] = '>', 55 | ['>'] = '>', 56 | ['>'] = '>', 57 | ['>'] = '>', 58 | ['>⃒'] = '>⃒', 59 | ['?'] = '?', 60 | ['@'] = '@', 61 | ['['] = '[', 62 | ['['] = '[', 63 | ['\'] = '\\', 64 | [']'] = ']', 65 | [']'] = ']', 66 | ['^'] = '^', 67 | ['_'] = '_', 68 | ['_'] = '_', 69 | ['`'] = '`', 70 | ['`'] = '`', 71 | ['fj'] = 'fj', 72 | ['{'] = '{', 73 | ['{'] = '{', 74 | ['|'] = '|', 75 | ['|'] = '|', 76 | ['|'] = '|', 77 | ['}'] = '}', 78 | ['}'] = '}', 79 | [' '] = ' ', 80 | [' '] = ' ', 81 | [' '] = ' ', 82 | ['¡'] = '¡', 83 | ['¡'] = '¡', 84 | ['¢'] = '¢', 85 | ['¢'] = '¢', 86 | ['£'] = '£', 87 | ['£'] = '£', 88 | ['¤'] = '¤', 89 | ['¤'] = '¤', 90 | ['¥'] = '¥', 91 | ['¥'] = '¥', 92 | ['¦'] = '¦', 93 | ['¦'] = '¦', 94 | ['§'] = '§', 95 | ['§'] = '§', 96 | ['¨'] = '¨', 97 | ['¨'] = '¨', 98 | ['¨'] = '¨', 99 | ['¨'] = '¨', 100 | ['¨'] = '¨', 101 | ['©'] = '©', 102 | ['©'] = '©', 103 | ['©'] = '©', 104 | ['©'] = '©', 105 | ['ª'] = 'ª', 106 | ['ª'] = 'ª', 107 | ['«'] = '«', 108 | ['«'] = '«', 109 | ['¬'] = '¬', 110 | ['¬'] = '¬', 111 | ['­'] = '­', 112 | ['­'] = '­', 113 | ['®'] = '®', 114 | ['®'] = '®', 115 | ['®'] = '®', 116 | ['®'] = '®', 117 | ['®'] = '®', 118 | ['¯'] = '¯', 119 | ['¯'] = '¯', 120 | ['¯'] = '¯', 121 | ['°'] = '°', 122 | ['°'] = '°', 123 | ['±'] = '±', 124 | ['±'] = '±', 125 | ['±'] = '±', 126 | ['±'] = '±', 127 | ['²'] = '²', 128 | ['²'] = '²', 129 | ['³'] = '³', 130 | ['³'] = '³', 131 | ['´'] = '´', 132 | ['´'] = '´', 133 | ['´'] = '´', 134 | ['µ'] = 'µ', 135 | ['µ'] = 'µ', 136 | ['¶'] = '¶', 137 | ['¶'] = '¶', 138 | ['·'] = '·', 139 | ['·'] = '·', 140 | ['·'] = '·', 141 | ['·'] = '·', 142 | ['¸'] = '¸', 143 | ['¸'] = '¸', 144 | ['¸'] = '¸', 145 | ['¹'] = '¹', 146 | ['¹'] = '¹', 147 | ['º'] = 'º', 148 | ['º'] = 'º', 149 | ['»'] = '»', 150 | ['»'] = '»', 151 | ['¼'] = '¼', 152 | ['¼'] = '¼', 153 | ['½'] = '½', 154 | ['½'] = '½', 155 | ['½'] = '½', 156 | ['¾'] = '¾', 157 | ['¾'] = '¾', 158 | ['¿'] = '¿', 159 | ['¿'] = '¿', 160 | ['À'] = 'À', 161 | ['À'] = 'À', 162 | ['Á'] = 'Á', 163 | ['Á'] = 'Á', 164 | ['Â'] = 'Â', 165 | ['Â'] = 'Â', 166 | ['Ã'] = 'Ã', 167 | ['Ã'] = 'Ã', 168 | ['Ä'] = 'Ä', 169 | ['Ä'] = 'Ä', 170 | ['Å'] = 'Å', 171 | ['Å'] = 'Å', 172 | ['Å'] = 'Å', 173 | ['Æ'] = 'Æ', 174 | ['Æ'] = 'Æ', 175 | ['Ç'] = 'Ç', 176 | ['Ç'] = 'Ç', 177 | ['È'] = 'È', 178 | ['È'] = 'È', 179 | ['É'] = 'É', 180 | ['É'] = 'É', 181 | ['Ê'] = 'Ê', 182 | ['Ê'] = 'Ê', 183 | ['Ë'] = 'Ë', 184 | ['Ë'] = 'Ë', 185 | ['Ì'] = 'Ì', 186 | ['Ì'] = 'Ì', 187 | ['Í'] = 'Í', 188 | ['Í'] = 'Í', 189 | ['Î'] = 'Î', 190 | ['Î'] = 'Î', 191 | ['Ï'] = 'Ï', 192 | ['Ï'] = 'Ï', 193 | ['Ð'] = 'Ð', 194 | ['Ð'] = 'Ð', 195 | ['Ñ'] = 'Ñ', 196 | ['Ñ'] = 'Ñ', 197 | ['Ò'] = 'Ò', 198 | ['Ò'] = 'Ò', 199 | ['Ó'] = 'Ó', 200 | ['Ó'] = 'Ó', 201 | ['Ô'] = 'Ô', 202 | ['Ô'] = 'Ô', 203 | ['Õ'] = 'Õ', 204 | ['Õ'] = 'Õ', 205 | ['Ö'] = 'Ö', 206 | ['Ö'] = 'Ö', 207 | ['×'] = '×', 208 | ['×'] = '×', 209 | ['Ø'] = 'Ø', 210 | ['Ø'] = 'Ø', 211 | ['Ù'] = 'Ù', 212 | ['Ù'] = 'Ù', 213 | ['Ú'] = 'Ú', 214 | ['Ú'] = 'Ú', 215 | ['Û'] = 'Û', 216 | ['Û'] = 'Û', 217 | ['Ü'] = 'Ü', 218 | ['Ü'] = 'Ü', 219 | ['Ý'] = 'Ý', 220 | ['Ý'] = 'Ý', 221 | ['Þ'] = 'Þ', 222 | ['Þ'] = 'Þ', 223 | ['ß'] = 'ß', 224 | ['ß'] = 'ß', 225 | ['à'] = 'à', 226 | ['à'] = 'à', 227 | ['á'] = 'á', 228 | ['á'] = 'á', 229 | ['â'] = 'â', 230 | ['â'] = 'â', 231 | ['ã'] = 'ã', 232 | ['ã'] = 'ã', 233 | ['ä'] = 'ä', 234 | ['ä'] = 'ä', 235 | ['å'] = 'å', 236 | ['å'] = 'å', 237 | ['æ'] = 'æ', 238 | ['æ'] = 'æ', 239 | ['ç'] = 'ç', 240 | ['ç'] = 'ç', 241 | ['è'] = 'è', 242 | ['è'] = 'è', 243 | ['é'] = 'é', 244 | ['é'] = 'é', 245 | ['ê'] = 'ê', 246 | ['ê'] = 'ê', 247 | ['ë'] = 'ë', 248 | ['ë'] = 'ë', 249 | ['ì'] = 'ì', 250 | ['ì'] = 'ì', 251 | ['í'] = 'í', 252 | ['í'] = 'í', 253 | ['î'] = 'î', 254 | ['î'] = 'î', 255 | ['ï'] = 'ï', 256 | ['ï'] = 'ï', 257 | ['ð'] = 'ð', 258 | ['ð'] = 'ð', 259 | ['ñ'] = 'ñ', 260 | ['ñ'] = 'ñ', 261 | ['ò'] = 'ò', 262 | ['ò'] = 'ò', 263 | ['ó'] = 'ó', 264 | ['ó'] = 'ó', 265 | ['ô'] = 'ô', 266 | ['ô'] = 'ô', 267 | ['õ'] = 'õ', 268 | ['õ'] = 'õ', 269 | ['ö'] = 'ö', 270 | ['ö'] = 'ö', 271 | ['÷'] = '÷', 272 | ['÷'] = '÷', 273 | ['÷'] = '÷', 274 | ['ø'] = 'ø', 275 | ['ø'] = 'ø', 276 | ['ù'] = 'ù', 277 | ['ù'] = 'ù', 278 | ['ú'] = 'ú', 279 | ['ú'] = 'ú', 280 | ['û'] = 'û', 281 | ['û'] = 'û', 282 | ['ü'] = 'ü', 283 | ['ü'] = 'ü', 284 | ['ý'] = 'ý', 285 | ['ý'] = 'ý', 286 | ['þ'] = 'þ', 287 | ['þ'] = 'þ', 288 | ['ÿ'] = 'ÿ', 289 | ['ÿ'] = 'ÿ', 290 | ['Ā'] = 'Ā', 291 | ['ā'] = 'ā', 292 | ['Ă'] = 'Ă', 293 | ['ă'] = 'ă', 294 | ['Ą'] = 'Ą', 295 | ['ą'] = 'ą', 296 | ['Ć'] = 'Ć', 297 | ['ć'] = 'ć', 298 | ['Ĉ'] = 'Ĉ', 299 | ['ĉ'] = 'ĉ', 300 | ['Ċ'] = 'Ċ', 301 | ['ċ'] = 'ċ', 302 | ['Č'] = 'Č', 303 | ['č'] = 'č', 304 | ['Ď'] = 'Ď', 305 | ['ď'] = 'ď', 306 | ['Đ'] = 'Đ', 307 | ['đ'] = 'đ', 308 | ['Ē'] = 'Ē', 309 | ['ē'] = 'ē', 310 | ['Ė'] = 'Ė', 311 | ['ė'] = 'ė', 312 | ['Ę'] = 'Ę', 313 | ['ę'] = 'ę', 314 | ['Ě'] = 'Ě', 315 | ['ě'] = 'ě', 316 | ['Ĝ'] = 'Ĝ', 317 | ['ĝ'] = 'ĝ', 318 | ['Ğ'] = 'Ğ', 319 | ['ğ'] = 'ğ', 320 | ['Ġ'] = 'Ġ', 321 | ['ġ'] = 'ġ', 322 | ['Ģ'] = 'Ģ', 323 | ['Ĥ'] = 'Ĥ', 324 | ['ĥ'] = 'ĥ', 325 | ['Ħ'] = 'Ħ', 326 | ['ħ'] = 'ħ', 327 | ['Ĩ'] = 'Ĩ', 328 | ['ĩ'] = 'ĩ', 329 | ['Ī'] = 'Ī', 330 | ['ī'] = 'ī', 331 | ['Į'] = 'Į', 332 | ['į'] = 'į', 333 | ['İ'] = 'İ', 334 | ['ı'] = 'ı', 335 | ['ı'] = 'ı', 336 | ['IJ'] = 'IJ', 337 | ['ij'] = 'ij', 338 | ['Ĵ'] = 'Ĵ', 339 | ['ĵ'] = 'ĵ', 340 | ['Ķ'] = 'Ķ', 341 | ['ķ'] = 'ķ', 342 | ['ĸ'] = 'ĸ', 343 | ['Ĺ'] = 'Ĺ', 344 | ['ĺ'] = 'ĺ', 345 | ['Ļ'] = 'Ļ', 346 | ['ļ'] = 'ļ', 347 | ['Ľ'] = 'Ľ', 348 | ['ľ'] = 'ľ', 349 | ['Ŀ'] = 'Ŀ', 350 | ['ŀ'] = 'ŀ', 351 | ['Ł'] = 'Ł', 352 | ['ł'] = 'ł', 353 | ['Ń'] = 'Ń', 354 | ['ń'] = 'ń', 355 | ['Ņ'] = 'Ņ', 356 | ['ņ'] = 'ņ', 357 | ['Ň'] = 'Ň', 358 | ['ň'] = 'ň', 359 | ['ʼn'] = 'ʼn', 360 | ['Ŋ'] = 'Ŋ', 361 | ['ŋ'] = 'ŋ', 362 | ['Ō'] = 'Ō', 363 | ['ō'] = 'ō', 364 | ['Ő'] = 'Ő', 365 | ['ő'] = 'ő', 366 | ['Œ'] = 'Œ', 367 | ['œ'] = 'œ', 368 | ['Ŕ'] = 'Ŕ', 369 | ['ŕ'] = 'ŕ', 370 | ['Ŗ'] = 'Ŗ', 371 | ['ŗ'] = 'ŗ', 372 | ['Ř'] = 'Ř', 373 | ['ř'] = 'ř', 374 | ['Ś'] = 'Ś', 375 | ['ś'] = 'ś', 376 | ['Ŝ'] = 'Ŝ', 377 | ['ŝ'] = 'ŝ', 378 | ['Ş'] = 'Ş', 379 | ['ş'] = 'ş', 380 | ['Š'] = 'Š', 381 | ['š'] = 'š', 382 | ['Ţ'] = 'Ţ', 383 | ['ţ'] = 'ţ', 384 | ['Ť'] = 'Ť', 385 | ['ť'] = 'ť', 386 | ['Ŧ'] = 'Ŧ', 387 | ['ŧ'] = 'ŧ', 388 | ['Ũ'] = 'Ũ', 389 | ['ũ'] = 'ũ', 390 | ['Ū'] = 'Ū', 391 | ['ū'] = 'ū', 392 | ['Ŭ'] = 'Ŭ', 393 | ['ŭ'] = 'ŭ', 394 | ['Ů'] = 'Ů', 395 | ['ů'] = 'ů', 396 | ['Ű'] = 'Ű', 397 | ['ű'] = 'ű', 398 | ['Ų'] = 'Ų', 399 | ['ų'] = 'ų', 400 | ['Ŵ'] = 'Ŵ', 401 | ['ŵ'] = 'ŵ', 402 | ['Ŷ'] = 'Ŷ', 403 | ['ŷ'] = 'ŷ', 404 | ['Ÿ'] = 'Ÿ', 405 | ['Ź'] = 'Ź', 406 | ['ź'] = 'ź', 407 | ['Ż'] = 'Ż', 408 | ['ż'] = 'ż', 409 | ['Ž'] = 'Ž', 410 | ['ž'] = 'ž', 411 | ['ƒ'] = 'ƒ', 412 | ['Ƶ'] = 'Ƶ', 413 | ['ǵ'] = 'ǵ', 414 | ['ȷ'] = 'ȷ', 415 | ['ˆ'] = 'ˆ', 416 | ['ˇ'] = 'ˇ', 417 | ['ˇ'] = 'ˇ', 418 | ['˘'] = '˘', 419 | ['˘'] = '˘', 420 | ['˙'] = '˙', 421 | ['˙'] = '˙', 422 | ['˚'] = '˚', 423 | ['˛'] = '˛', 424 | ['˜'] = '˜', 425 | ['˜'] = '˜', 426 | ['˝'] = '˝', 427 | ['˝'] = '˝', 428 | ['̑'] = '̑', 429 | ['Α'] = 'Α', 430 | ['Β'] = 'Β', 431 | ['Γ'] = 'Γ', 432 | ['Δ'] = 'Δ', 433 | ['Ε'] = 'Ε', 434 | ['Ζ'] = 'Ζ', 435 | ['Η'] = 'Η', 436 | ['Θ'] = 'Θ', 437 | ['Ι'] = 'Ι', 438 | ['Κ'] = 'Κ', 439 | ['Λ'] = 'Λ', 440 | ['Μ'] = 'Μ', 441 | ['Ν'] = 'Ν', 442 | ['Ξ'] = 'Ξ', 443 | ['Ο'] = 'Ο', 444 | ['Π'] = 'Π', 445 | ['Ρ'] = 'Ρ', 446 | ['Σ'] = 'Σ', 447 | ['Τ'] = 'Τ', 448 | ['Υ'] = 'Υ', 449 | ['Φ'] = 'Φ', 450 | ['Χ'] = 'Χ', 451 | ['Ψ'] = 'Ψ', 452 | ['Ω'] = 'Ω', 453 | ['Ω'] = 'Ω', 454 | ['α'] = 'α', 455 | ['β'] = 'β', 456 | ['γ'] = 'γ', 457 | ['δ'] = 'δ', 458 | ['ε'] = 'ε', 459 | ['ε'] = 'ε', 460 | ['ζ'] = 'ζ', 461 | ['η'] = 'η', 462 | ['θ'] = 'θ', 463 | ['ι'] = 'ι', 464 | ['κ'] = 'κ', 465 | ['λ'] = 'λ', 466 | ['μ'] = 'μ', 467 | ['ν'] = 'ν', 468 | ['ξ'] = 'ξ', 469 | ['ο'] = 'ο', 470 | ['π'] = 'π', 471 | ['ρ'] = 'ρ', 472 | ['ς'] = 'ς', 473 | ['ς'] = 'ς', 474 | ['ς'] = 'ς', 475 | ['σ'] = 'σ', 476 | ['τ'] = 'τ', 477 | ['υ'] = 'υ', 478 | ['υ'] = 'υ', 479 | ['φ'] = 'φ', 480 | ['χ'] = 'χ', 481 | ['ψ'] = 'ψ', 482 | ['ω'] = 'ω', 483 | ['ϑ'] = 'ϑ', 484 | ['ϑ'] = 'ϑ', 485 | ['ϑ'] = 'ϑ', 486 | ['ϒ'] = 'ϒ', 487 | ['ϒ'] = 'ϒ', 488 | ['ϕ'] = 'ϕ', 489 | ['ϕ'] = 'ϕ', 490 | ['ϕ'] = 'ϕ', 491 | ['ϖ'] = 'ϖ', 492 | ['ϖ'] = 'ϖ', 493 | ['Ϝ'] = 'Ϝ', 494 | ['ϝ'] = 'ϝ', 495 | ['ϝ'] = 'ϝ', 496 | ['ϰ'] = 'ϰ', 497 | ['ϰ'] = 'ϰ', 498 | ['ϱ'] = 'ϱ', 499 | ['ϱ'] = 'ϱ', 500 | ['ϵ'] = 'ϵ', 501 | ['ϵ'] = 'ϵ', 502 | ['ϵ'] = 'ϵ', 503 | ['϶'] = '϶', 504 | ['϶'] = '϶', 505 | ['Ё'] = 'Ё', 506 | ['Ђ'] = 'Ђ', 507 | ['Ѓ'] = 'Ѓ', 508 | ['Є'] = 'Є', 509 | ['Ѕ'] = 'Ѕ', 510 | ['І'] = 'І', 511 | ['Ї'] = 'Ї', 512 | ['Ј'] = 'Ј', 513 | ['Љ'] = 'Љ', 514 | ['Њ'] = 'Њ', 515 | ['Ћ'] = 'Ћ', 516 | ['Ќ'] = 'Ќ', 517 | ['Ў'] = 'Ў', 518 | ['Џ'] = 'Џ', 519 | ['А'] = 'А', 520 | ['Б'] = 'Б', 521 | ['В'] = 'В', 522 | ['Г'] = 'Г', 523 | ['Д'] = 'Д', 524 | ['Е'] = 'Е', 525 | ['Ж'] = 'Ж', 526 | ['З'] = 'З', 527 | ['И'] = 'И', 528 | ['Й'] = 'Й', 529 | ['К'] = 'К', 530 | ['Л'] = 'Л', 531 | ['М'] = 'М', 532 | ['Н'] = 'Н', 533 | ['О'] = 'О', 534 | ['П'] = 'П', 535 | ['Р'] = 'Р', 536 | ['С'] = 'С', 537 | ['Т'] = 'Т', 538 | ['У'] = 'У', 539 | ['Ф'] = 'Ф', 540 | ['Х'] = 'Х', 541 | ['Ц'] = 'Ц', 542 | ['Ч'] = 'Ч', 543 | ['Ш'] = 'Ш', 544 | ['Щ'] = 'Щ', 545 | ['Ъ'] = 'Ъ', 546 | ['Ы'] = 'Ы', 547 | ['Ь'] = 'Ь', 548 | ['Э'] = 'Э', 549 | ['Ю'] = 'Ю', 550 | ['Я'] = 'Я', 551 | ['а'] = 'а', 552 | ['б'] = 'б', 553 | ['в'] = 'в', 554 | ['г'] = 'г', 555 | ['д'] = 'д', 556 | ['е'] = 'е', 557 | ['ж'] = 'ж', 558 | ['з'] = 'з', 559 | ['и'] = 'и', 560 | ['й'] = 'й', 561 | ['к'] = 'к', 562 | ['л'] = 'л', 563 | ['м'] = 'м', 564 | ['н'] = 'н', 565 | ['о'] = 'о', 566 | ['п'] = 'п', 567 | ['р'] = 'р', 568 | ['с'] = 'с', 569 | ['т'] = 'т', 570 | ['у'] = 'у', 571 | ['ф'] = 'ф', 572 | ['х'] = 'х', 573 | ['ц'] = 'ц', 574 | ['ч'] = 'ч', 575 | ['ш'] = 'ш', 576 | ['щ'] = 'щ', 577 | ['ъ'] = 'ъ', 578 | ['ы'] = 'ы', 579 | ['ь'] = 'ь', 580 | ['э'] = 'э', 581 | ['ю'] = 'ю', 582 | ['я'] = 'я', 583 | ['ё'] = 'ё', 584 | ['ђ'] = 'ђ', 585 | ['ѓ'] = 'ѓ', 586 | ['є'] = 'є', 587 | ['ѕ'] = 'ѕ', 588 | ['і'] = 'і', 589 | ['ї'] = 'ї', 590 | ['ј'] = 'ј', 591 | ['љ'] = 'љ', 592 | ['њ'] = 'њ', 593 | ['ћ'] = 'ћ', 594 | ['ќ'] = 'ќ', 595 | ['ў'] = 'ў', 596 | ['џ'] = 'џ', 597 | [' '] = ' ', 598 | [' '] = ' ', 599 | [' '] = ' ', 600 | [' '] = ' ', 601 | [' '] = ' ', 602 | [' '] = ' ', 603 | [' '] = ' ', 604 | [' '] = ' ', 605 | [' '] = ' ', 606 | [' '] = ' ', 607 | ['​'] = '​', 608 | ['​'] = '​', 609 | ['​'] = '​', 610 | ['​'] = '​', 611 | ['​'] = '​', 612 | ['‌'] = '‌', 613 | ['‍'] = '‍', 614 | ['‎'] = '‎', 615 | ['‏'] = '‏', 616 | ['‐'] = '‐', 617 | ['‐'] = '‐', 618 | ['–'] = '–', 619 | ['—'] = '—', 620 | ['―'] = '―', 621 | ['‖'] = '‖', 622 | ['‖'] = '‖', 623 | ['‘'] = '‘', 624 | ['‘'] = '‘', 625 | ['’'] = '’', 626 | ['’'] = '’', 627 | ['’'] = '’', 628 | ['‚'] = '‚', 629 | ['‚'] = '‚', 630 | ['“'] = '“', 631 | ['“'] = '“', 632 | ['”'] = '”', 633 | ['”'] = '”', 634 | ['”'] = '”', 635 | ['„'] = '„', 636 | ['„'] = '„', 637 | ['†'] = '†', 638 | ['‡'] = '‡', 639 | ['‡'] = '‡', 640 | ['•'] = '•', 641 | ['•'] = '•', 642 | ['‥'] = '‥', 643 | ['…'] = '…', 644 | ['…'] = '…', 645 | ['‰'] = '‰', 646 | ['‱'] = '‱', 647 | ['′'] = '′', 648 | ['″'] = '″', 649 | ['‴'] = '‴', 650 | ['‵'] = '‵', 651 | ['‵'] = '‵', 652 | ['‹'] = '‹', 653 | ['›'] = '›', 654 | ['‾'] = '‾', 655 | ['‾'] = '‾', 656 | ['⁁'] = '⁁', 657 | ['⁃'] = '⁃', 658 | ['⁄'] = '⁄', 659 | ['⁏'] = '⁏', 660 | ['⁗'] = '⁗', 661 | [' '] = ' ', 662 | ['  '] = '  ', 663 | ['⁠'] = '⁠', 664 | ['⁡'] = '⁡', 665 | ['⁡'] = '⁡', 666 | ['⁢'] = '⁢', 667 | ['⁢'] = '⁢', 668 | ['⁣'] = '⁣', 669 | ['⁣'] = '⁣', 670 | ['€'] = '€', 671 | ['⃛'] = '⃛', 672 | ['⃛'] = '⃛', 673 | ['⃜'] = '⃜', 674 | ['ℂ'] = 'ℂ', 675 | ['ℂ'] = 'ℂ', 676 | ['℅'] = '℅', 677 | ['ℊ'] = 'ℊ', 678 | ['ℋ'] = 'ℋ', 679 | ['ℋ'] = 'ℋ', 680 | ['ℋ'] = 'ℋ', 681 | ['ℌ'] = 'ℌ', 682 | ['ℌ'] = 'ℌ', 683 | ['ℍ'] = 'ℍ', 684 | ['ℍ'] = 'ℍ', 685 | ['ℎ'] = 'ℎ', 686 | ['ℏ'] = 'ℏ', 687 | ['ℏ'] = 'ℏ', 688 | ['ℏ'] = 'ℏ', 689 | ['ℏ'] = 'ℏ', 690 | ['ℐ'] = 'ℐ', 691 | ['ℐ'] = 'ℐ', 692 | ['ℑ'] = 'ℑ', 693 | ['ℑ'] = 'ℑ', 694 | ['ℑ'] = 'ℑ', 695 | ['ℑ'] = 'ℑ', 696 | ['ℒ'] = 'ℒ', 697 | ['ℒ'] = 'ℒ', 698 | ['ℒ'] = 'ℒ', 699 | ['ℓ'] = 'ℓ', 700 | ['ℕ'] = 'ℕ', 701 | ['ℕ'] = 'ℕ', 702 | ['№'] = '№', 703 | ['℗'] = '℗', 704 | ['℘'] = '℘', 705 | ['℘'] = '℘', 706 | ['ℙ'] = 'ℙ', 707 | ['ℙ'] = 'ℙ', 708 | ['ℚ'] = 'ℚ', 709 | ['ℚ'] = 'ℚ', 710 | ['ℛ'] = 'ℛ', 711 | ['ℛ'] = 'ℛ', 712 | ['ℜ'] = 'ℜ', 713 | ['ℜ'] = 'ℜ', 714 | ['ℜ'] = 'ℜ', 715 | ['ℜ'] = 'ℜ', 716 | ['ℝ'] = 'ℝ', 717 | ['ℝ'] = 'ℝ', 718 | ['℞'] = '℞', 719 | ['™'] = '™', 720 | ['™'] = '™', 721 | ['ℤ'] = 'ℤ', 722 | ['ℤ'] = 'ℤ', 723 | ['℧'] = '℧', 724 | ['ℨ'] = 'ℨ', 725 | ['ℨ'] = 'ℨ', 726 | ['℩'] = '℩', 727 | ['ℬ'] = 'ℬ', 728 | ['ℬ'] = 'ℬ', 729 | ['ℬ'] = 'ℬ', 730 | ['ℭ'] = 'ℭ', 731 | ['ℭ'] = 'ℭ', 732 | ['ℯ'] = 'ℯ', 733 | ['ℰ'] = 'ℰ', 734 | ['ℰ'] = 'ℰ', 735 | ['ℱ'] = 'ℱ', 736 | ['ℱ'] = 'ℱ', 737 | ['ℳ'] = 'ℳ', 738 | ['ℳ'] = 'ℳ', 739 | ['ℳ'] = 'ℳ', 740 | ['ℴ'] = 'ℴ', 741 | ['ℴ'] = 'ℴ', 742 | ['ℴ'] = 'ℴ', 743 | ['ℵ'] = 'ℵ', 744 | ['ℵ'] = 'ℵ', 745 | ['ℶ'] = 'ℶ', 746 | ['ℷ'] = 'ℷ', 747 | ['ℸ'] = 'ℸ', 748 | ['ⅅ'] = 'ⅅ', 749 | ['ⅅ'] = 'ⅅ', 750 | ['ⅆ'] = 'ⅆ', 751 | ['ⅆ'] = 'ⅆ', 752 | ['ⅇ'] = 'ⅇ', 753 | ['ⅇ'] = 'ⅇ', 754 | ['ⅇ'] = 'ⅇ', 755 | ['ⅈ'] = 'ⅈ', 756 | ['ⅈ'] = 'ⅈ', 757 | ['⅓'] = '⅓', 758 | ['⅔'] = '⅔', 759 | ['⅕'] = '⅕', 760 | ['⅖'] = '⅖', 761 | ['⅗'] = '⅗', 762 | ['⅘'] = '⅘', 763 | ['⅙'] = '⅙', 764 | ['⅚'] = '⅚', 765 | ['⅛'] = '⅛', 766 | ['⅜'] = '⅜', 767 | ['⅝'] = '⅝', 768 | ['⅞'] = '⅞', 769 | ['←'] = '←', 770 | ['←'] = '←', 771 | ['←'] = '←', 772 | ['←'] = '←', 773 | ['←'] = '←', 774 | ['↑'] = '↑', 775 | ['↑'] = '↑', 776 | ['↑'] = '↑', 777 | ['↑'] = '↑', 778 | ['→'] = '→', 779 | ['→'] = '→', 780 | ['→'] = '→', 781 | ['→'] = '→', 782 | ['→'] = '→', 783 | ['↓'] = '↓', 784 | ['↓'] = '↓', 785 | ['↓'] = '↓', 786 | ['↓'] = '↓', 787 | ['↔'] = '↔', 788 | ['↔'] = '↔', 789 | ['↔'] = '↔', 790 | ['↕'] = '↕', 791 | ['↕'] = '↕', 792 | ['↕'] = '↕', 793 | ['↖'] = '↖', 794 | ['↖'] = '↖', 795 | ['↖'] = '↖', 796 | ['↗'] = '↗', 797 | ['↗'] = '↗', 798 | ['↗'] = '↗', 799 | ['↘'] = '↘', 800 | ['↘'] = '↘', 801 | ['↘'] = '↘', 802 | ['↙'] = '↙', 803 | ['↙'] = '↙', 804 | ['↙'] = '↙', 805 | ['↚'] = '↚', 806 | ['↚'] = '↚', 807 | ['↛'] = '↛', 808 | ['↛'] = '↛', 809 | ['↝'] = '↝', 810 | ['↝'] = '↝', 811 | ['↝̸'] = '↝̸', 812 | ['↞'] = '↞', 813 | ['↞'] = '↞', 814 | ['↟'] = '↟', 815 | ['↠'] = '↠', 816 | ['↠'] = '↠', 817 | ['↡'] = '↡', 818 | ['↢'] = '↢', 819 | ['↢'] = '↢', 820 | ['↣'] = '↣', 821 | ['↣'] = '↣', 822 | ['↤'] = '↤', 823 | ['↤'] = '↤', 824 | ['↥'] = '↥', 825 | ['↥'] = '↥', 826 | ['↦'] = '↦', 827 | ['↦'] = '↦', 828 | ['↦'] = '↦', 829 | ['↧'] = '↧', 830 | ['↧'] = '↧', 831 | ['↩'] = '↩', 832 | ['↩'] = '↩', 833 | ['↪'] = '↪', 834 | ['↪'] = '↪', 835 | ['↫'] = '↫', 836 | ['↫'] = '↫', 837 | ['↬'] = '↬', 838 | ['↬'] = '↬', 839 | ['↭'] = '↭', 840 | ['↭'] = '↭', 841 | ['↮'] = '↮', 842 | ['↮'] = '↮', 843 | ['↰'] = '↰', 844 | ['↰'] = '↰', 845 | ['↱'] = '↱', 846 | ['↱'] = '↱', 847 | ['↲'] = '↲', 848 | ['↳'] = '↳', 849 | ['↵'] = '↵', 850 | ['↶'] = '↶', 851 | ['↶'] = '↶', 852 | ['↷'] = '↷', 853 | ['↷'] = '↷', 854 | ['↺'] = '↺', 855 | ['↺'] = '↺', 856 | ['↻'] = '↻', 857 | ['↻'] = '↻', 858 | ['↼'] = '↼', 859 | ['↼'] = '↼', 860 | ['↼'] = '↼', 861 | ['↽'] = '↽', 862 | ['↽'] = '↽', 863 | ['↽'] = '↽', 864 | ['↾'] = '↾', 865 | ['↾'] = '↾', 866 | ['↾'] = '↾', 867 | ['↿'] = '↿', 868 | ['↿'] = '↿', 869 | ['↿'] = '↿', 870 | ['⇀'] = '⇀', 871 | ['⇀'] = '⇀', 872 | ['⇀'] = '⇀', 873 | ['⇁'] = '⇁', 874 | ['⇁'] = '⇁', 875 | ['⇁'] = '⇁', 876 | ['⇂'] = '⇂', 877 | ['⇂'] = '⇂', 878 | ['⇂'] = '⇂', 879 | ['⇃'] = '⇃', 880 | ['⇃'] = '⇃', 881 | ['⇃'] = '⇃', 882 | ['⇄'] = '⇄', 883 | ['⇄'] = '⇄', 884 | ['⇄'] = '⇄', 885 | ['⇅'] = '⇅', 886 | ['⇅'] = '⇅', 887 | ['⇆'] = '⇆', 888 | ['⇆'] = '⇆', 889 | ['⇆'] = '⇆', 890 | ['⇇'] = '⇇', 891 | ['⇇'] = '⇇', 892 | ['⇈'] = '⇈', 893 | ['⇈'] = '⇈', 894 | ['⇉'] = '⇉', 895 | ['⇉'] = '⇉', 896 | ['⇊'] = '⇊', 897 | ['⇊'] = '⇊', 898 | ['⇋'] = '⇋', 899 | ['⇋'] = '⇋', 900 | ['⇋'] = '⇋', 901 | ['⇌'] = '⇌', 902 | ['⇌'] = '⇌', 903 | ['⇌'] = '⇌', 904 | ['⇍'] = '⇍', 905 | ['⇍'] = '⇍', 906 | ['⇎'] = '⇎', 907 | ['⇎'] = '⇎', 908 | ['⇏'] = '⇏', 909 | ['⇏'] = '⇏', 910 | ['⇐'] = '⇐', 911 | ['⇐'] = '⇐', 912 | ['⇐'] = '⇐', 913 | ['⇑'] = '⇑', 914 | ['⇑'] = '⇑', 915 | ['⇑'] = '⇑', 916 | ['⇒'] = '⇒', 917 | ['⇒'] = '⇒', 918 | ['⇒'] = '⇒', 919 | ['⇒'] = '⇒', 920 | ['⇓'] = '⇓', 921 | ['⇓'] = '⇓', 922 | ['⇓'] = '⇓', 923 | ['⇔'] = '⇔', 924 | ['⇔'] = '⇔', 925 | ['⇔'] = '⇔', 926 | ['⇔'] = '⇔', 927 | ['⇕'] = '⇕', 928 | ['⇕'] = '⇕', 929 | ['⇕'] = '⇕', 930 | ['⇖'] = '⇖', 931 | ['⇗'] = '⇗', 932 | ['⇘'] = '⇘', 933 | ['⇙'] = '⇙', 934 | ['⇚'] = '⇚', 935 | ['⇚'] = '⇚', 936 | ['⇛'] = '⇛', 937 | ['⇛'] = '⇛', 938 | ['⇝'] = '⇝', 939 | ['⇤'] = '⇤', 940 | ['⇤'] = '⇤', 941 | ['⇥'] = '⇥', 942 | ['⇥'] = '⇥', 943 | ['⇵'] = '⇵', 944 | ['⇵'] = '⇵', 945 | ['⇽'] = '⇽', 946 | ['⇾'] = '⇾', 947 | ['⇿'] = '⇿', 948 | ['∀'] = '∀', 949 | ['∀'] = '∀', 950 | ['∁'] = '∁', 951 | ['∁'] = '∁', 952 | ['∂'] = '∂', 953 | ['∂'] = '∂', 954 | ['∂̸'] = '∂̸', 955 | ['∃'] = '∃', 956 | ['∃'] = '∃', 957 | ['∄'] = '∄', 958 | ['∄'] = '∄', 959 | ['∄'] = '∄', 960 | ['∅'] = '∅', 961 | ['∅'] = '∅', 962 | ['∅'] = '∅', 963 | ['∅'] = '∅', 964 | ['∇'] = '∇', 965 | ['∇'] = '∇', 966 | ['∈'] = '∈', 967 | ['∈'] = '∈', 968 | ['∈'] = '∈', 969 | ['∈'] = '∈', 970 | ['∉'] = '∉', 971 | ['∉'] = '∉', 972 | ['∉'] = '∉', 973 | ['∋'] = '∋', 974 | ['∋'] = '∋', 975 | ['∋'] = '∋', 976 | ['∋'] = '∋', 977 | ['∌'] = '∌', 978 | ['∌'] = '∌', 979 | ['∌'] = '∌', 980 | ['∏'] = '∏', 981 | ['∏'] = '∏', 982 | ['∐'] = '∐', 983 | ['∐'] = '∐', 984 | ['∑'] = '∑', 985 | ['∑'] = '∑', 986 | ['−'] = '−', 987 | ['∓'] = '∓', 988 | ['∓'] = '∓', 989 | ['∓'] = '∓', 990 | ['∔'] = '∔', 991 | ['∔'] = '∔', 992 | ['∖'] = '∖', 993 | ['∖'] = '∖', 994 | ['∖'] = '∖', 995 | ['∖'] = '∖', 996 | ['∖'] = '∖', 997 | ['∗'] = '∗', 998 | ['∘'] = '∘', 999 | ['∘'] = '∘', 1000 | ['√'] = '√', 1001 | ['√'] = '√', 1002 | ['∝'] = '∝', 1003 | ['∝'] = '∝', 1004 | ['∝'] = '∝', 1005 | ['∝'] = '∝', 1006 | ['∝'] = '∝', 1007 | ['∞'] = '∞', 1008 | ['∟'] = '∟', 1009 | ['∠'] = '∠', 1010 | ['∠'] = '∠', 1011 | ['∠⃒'] = '∠⃒', 1012 | ['∡'] = '∡', 1013 | ['∡'] = '∡', 1014 | ['∢'] = '∢', 1015 | ['∣'] = '∣', 1016 | ['∣'] = '∣', 1017 | ['∣'] = '∣', 1018 | ['∣'] = '∣', 1019 | ['∤'] = '∤', 1020 | ['∤'] = '∤', 1021 | ['∤'] = '∤', 1022 | ['∤'] = '∤', 1023 | ['∥'] = '∥', 1024 | ['∥'] = '∥', 1025 | ['∥'] = '∥', 1026 | ['∥'] = '∥', 1027 | ['∥'] = '∥', 1028 | ['∦'] = '∦', 1029 | ['∦'] = '∦', 1030 | ['∦'] = '∦', 1031 | ['∦'] = '∦', 1032 | ['∦'] = '∦', 1033 | ['∧'] = '∧', 1034 | ['∧'] = '∧', 1035 | ['∨'] = '∨', 1036 | ['∨'] = '∨', 1037 | ['∩'] = '∩', 1038 | ['∩︀'] = '∩︀', 1039 | ['∪'] = '∪', 1040 | ['∪︀'] = '∪︀', 1041 | ['∫'] = '∫', 1042 | ['∫'] = '∫', 1043 | ['∬'] = '∬', 1044 | ['∭'] = '∭', 1045 | ['∭'] = '∭', 1046 | ['∮'] = '∮', 1047 | ['∮'] = '∮', 1048 | ['∮'] = '∮', 1049 | ['∯'] = '∯', 1050 | ['∯'] = '∯', 1051 | ['∰'] = '∰', 1052 | ['∱'] = '∱', 1053 | ['∲'] = '∲', 1054 | ['∲'] = '∲', 1055 | ['∳'] = '∳', 1056 | ['∳'] = '∳', 1057 | ['∴'] = '∴', 1058 | ['∴'] = '∴', 1059 | ['∴'] = '∴', 1060 | ['∵'] = '∵', 1061 | ['∵'] = '∵', 1062 | ['∵'] = '∵', 1063 | ['∶'] = '∶', 1064 | ['∷'] = '∷', 1065 | ['∷'] = '∷', 1066 | ['∸'] = '∸', 1067 | ['∸'] = '∸', 1068 | ['∺'] = '∺', 1069 | ['∻'] = '∻', 1070 | ['∼'] = '∼', 1071 | ['∼'] = '∼', 1072 | ['∼'] = '∼', 1073 | ['∼'] = '∼', 1074 | ['∼⃒'] = '∼⃒', 1075 | ['∽'] = '∽', 1076 | ['∽'] = '∽', 1077 | ['∽̱'] = '∽̱', 1078 | ['∾'] = '∾', 1079 | ['∾'] = '∾', 1080 | ['∾̳'] = '∾̳', 1081 | ['∿'] = '∿', 1082 | ['≀'] = '≀', 1083 | ['≀'] = '≀', 1084 | ['≀'] = '≀', 1085 | ['≁'] = '≁', 1086 | ['≁'] = '≁', 1087 | ['≂'] = '≂', 1088 | ['≂'] = '≂', 1089 | ['≂'] = '≂', 1090 | ['≂̸'] = '≂̸', 1091 | ['≂̸'] = '≂̸', 1092 | ['≃'] = '≃', 1093 | ['≃'] = '≃', 1094 | ['≃'] = '≃', 1095 | ['≄'] = '≄', 1096 | ['≄'] = '≄', 1097 | ['≄'] = '≄', 1098 | ['≅'] = '≅', 1099 | ['≅'] = '≅', 1100 | ['≆'] = '≆', 1101 | ['≇'] = '≇', 1102 | ['≇'] = '≇', 1103 | ['≈'] = '≈', 1104 | ['≈'] = '≈', 1105 | ['≈'] = '≈', 1106 | ['≈'] = '≈', 1107 | ['≈'] = '≈', 1108 | ['≈'] = '≈', 1109 | ['≉'] = '≉', 1110 | ['≉'] = '≉', 1111 | ['≉'] = '≉', 1112 | ['≊'] = '≊', 1113 | ['≊'] = '≊', 1114 | ['≋'] = '≋', 1115 | ['≋̸'] = '≋̸', 1116 | ['≌'] = '≌', 1117 | ['≌'] = '≌', 1118 | ['≍'] = '≍', 1119 | ['≍'] = '≍', 1120 | ['≍⃒'] = '≍⃒', 1121 | ['≎'] = '≎', 1122 | ['≎'] = '≎', 1123 | ['≎'] = '≎', 1124 | ['≎̸'] = '≎̸', 1125 | ['≎̸'] = '≎̸', 1126 | ['≏'] = '≏', 1127 | ['≏'] = '≏', 1128 | ['≏'] = '≏', 1129 | ['≏̸'] = '≏̸', 1130 | ['≏̸'] = '≏̸', 1131 | ['≐'] = '≐', 1132 | ['≐'] = '≐', 1133 | ['≐'] = '≐', 1134 | ['≐̸'] = '≐̸', 1135 | ['≑'] = '≑', 1136 | ['≑'] = '≑', 1137 | ['≒'] = '≒', 1138 | ['≒'] = '≒', 1139 | ['≓'] = '≓', 1140 | ['≓'] = '≓', 1141 | ['≔'] = '≔', 1142 | ['≔'] = '≔', 1143 | ['≔'] = '≔', 1144 | ['≕'] = '≕', 1145 | ['≕'] = '≕', 1146 | ['≖'] = '≖', 1147 | ['≖'] = '≖', 1148 | ['≗'] = '≗', 1149 | ['≗'] = '≗', 1150 | ['≙'] = '≙', 1151 | ['≚'] = '≚', 1152 | ['≜'] = '≜', 1153 | ['≜'] = '≜', 1154 | ['≟'] = '≟', 1155 | ['≟'] = '≟', 1156 | ['≠'] = '≠', 1157 | ['≠'] = '≠', 1158 | ['≡'] = '≡', 1159 | ['≡'] = '≡', 1160 | ['≡⃥'] = '≡⃥', 1161 | ['≢'] = '≢', 1162 | ['≢'] = '≢', 1163 | ['≤'] = '≤', 1164 | ['≤'] = '≤', 1165 | ['≤⃒'] = '≤⃒', 1166 | ['≥'] = '≥', 1167 | ['≥'] = '≥', 1168 | ['≥'] = '≥', 1169 | ['≥⃒'] = '≥⃒', 1170 | ['≦'] = '≦', 1171 | ['≦'] = '≦', 1172 | ['≦'] = '≦', 1173 | ['≦̸'] = '≦̸', 1174 | ['≦̸'] = '≦̸', 1175 | ['≧'] = '≧', 1176 | ['≧'] = '≧', 1177 | ['≧'] = '≧', 1178 | ['≧̸'] = '≧̸', 1179 | ['≧̸'] = '≧̸', 1180 | ['≧̸'] = '≧̸', 1181 | ['≨'] = '≨', 1182 | ['≨'] = '≨', 1183 | ['≨︀'] = '≨︀', 1184 | ['≨︀'] = '≨︀', 1185 | ['≩'] = '≩', 1186 | ['≩'] = '≩', 1187 | ['≩︀'] = '≩︀', 1188 | ['≩︀'] = '≩︀', 1189 | ['≪'] = '≪', 1190 | ['≪'] = '≪', 1191 | ['≪'] = '≪', 1192 | ['≪̸'] = '≪̸', 1193 | ['≪̸'] = '≪̸', 1194 | ['≪⃒'] = '≪⃒', 1195 | ['≫'] = '≫', 1196 | ['≫'] = '≫', 1197 | ['≫'] = '≫', 1198 | ['≫̸'] = '≫̸', 1199 | ['≫̸'] = '≫̸', 1200 | ['≫⃒'] = '≫⃒', 1201 | ['≬'] = '≬', 1202 | ['≬'] = '≬', 1203 | ['≭'] = '≭', 1204 | ['≮'] = '≮', 1205 | ['≮'] = '≮', 1206 | ['≮'] = '≮', 1207 | ['≯'] = '≯', 1208 | ['≯'] = '≯', 1209 | ['≯'] = '≯', 1210 | ['≰'] = '≰', 1211 | ['≰'] = '≰', 1212 | ['≰'] = '≰', 1213 | ['≱'] = '≱', 1214 | ['≱'] = '≱', 1215 | ['≱'] = '≱', 1216 | ['≲'] = '≲', 1217 | ['≲'] = '≲', 1218 | ['≲'] = '≲', 1219 | ['≳'] = '≳', 1220 | ['≳'] = '≳', 1221 | ['≳'] = '≳', 1222 | ['≴'] = '≴', 1223 | ['≴'] = '≴', 1224 | ['≵'] = '≵', 1225 | ['≵'] = '≵', 1226 | ['≶'] = '≶', 1227 | ['≶'] = '≶', 1228 | ['≶'] = '≶', 1229 | ['≷'] = '≷', 1230 | ['≷'] = '≷', 1231 | ['≷'] = '≷', 1232 | ['≸'] = '≸', 1233 | ['≸'] = '≸', 1234 | ['≹'] = '≹', 1235 | ['≹'] = '≹', 1236 | ['≺'] = '≺', 1237 | ['≺'] = '≺', 1238 | ['≺'] = '≺', 1239 | ['≻'] = '≻', 1240 | ['≻'] = '≻', 1241 | ['≻'] = '≻', 1242 | ['≼'] = '≼', 1243 | ['≼'] = '≼', 1244 | ['≼'] = '≼', 1245 | ['≽'] = '≽', 1246 | ['≽'] = '≽', 1247 | ['≽'] = '≽', 1248 | ['≾'] = '≾', 1249 | ['≾'] = '≾', 1250 | ['≾'] = '≾', 1251 | ['≿'] = '≿', 1252 | ['≿'] = '≿', 1253 | ['≿'] = '≿', 1254 | ['≿̸'] = '≿̸', 1255 | ['⊀'] = '⊀', 1256 | ['⊀'] = '⊀', 1257 | ['⊀'] = '⊀', 1258 | ['⊁'] = '⊁', 1259 | ['⊁'] = '⊁', 1260 | ['⊁'] = '⊁', 1261 | ['⊂'] = '⊂', 1262 | ['⊂'] = '⊂', 1263 | ['⊂⃒'] = '⊂⃒', 1264 | ['⊂⃒'] = '⊂⃒', 1265 | ['⊂⃒'] = '⊂⃒', 1266 | ['⊃'] = '⊃', 1267 | ['⊃'] = '⊃', 1268 | ['⊃'] = '⊃', 1269 | ['⊃⃒'] = '⊃⃒', 1270 | ['⊃⃒'] = '⊃⃒', 1271 | ['⊃⃒'] = '⊃⃒', 1272 | ['⊄'] = '⊄', 1273 | ['⊅'] = '⊅', 1274 | ['⊆'] = '⊆', 1275 | ['⊆'] = '⊆', 1276 | ['⊆'] = '⊆', 1277 | ['⊇'] = '⊇', 1278 | ['⊇'] = '⊇', 1279 | ['⊇'] = '⊇', 1280 | ['⊈'] = '⊈', 1281 | ['⊈'] = '⊈', 1282 | ['⊈'] = '⊈', 1283 | ['⊉'] = '⊉', 1284 | ['⊉'] = '⊉', 1285 | ['⊉'] = '⊉', 1286 | ['⊊'] = '⊊', 1287 | ['⊊'] = '⊊', 1288 | ['⊊︀'] = '⊊︀', 1289 | ['⊊︀'] = '⊊︀', 1290 | ['⊋'] = '⊋', 1291 | ['⊋'] = '⊋', 1292 | ['⊋︀'] = '⊋︀', 1293 | ['⊋︀'] = '⊋︀', 1294 | ['⊍'] = '⊍', 1295 | ['⊎'] = '⊎', 1296 | ['⊎'] = '⊎', 1297 | ['⊏'] = '⊏', 1298 | ['⊏'] = '⊏', 1299 | ['⊏'] = '⊏', 1300 | ['⊏̸'] = '⊏̸', 1301 | ['⊐'] = '⊐', 1302 | ['⊐'] = '⊐', 1303 | ['⊐'] = '⊐', 1304 | ['⊐̸'] = '⊐̸', 1305 | ['⊑'] = '⊑', 1306 | ['⊑'] = '⊑', 1307 | ['⊑'] = '⊑', 1308 | ['⊒'] = '⊒', 1309 | ['⊒'] = '⊒', 1310 | ['⊒'] = '⊒', 1311 | ['⊓'] = '⊓', 1312 | ['⊓'] = '⊓', 1313 | ['⊓︀'] = '⊓︀', 1314 | ['⊔'] = '⊔', 1315 | ['⊔'] = '⊔', 1316 | ['⊔︀'] = '⊔︀', 1317 | ['⊕'] = '⊕', 1318 | ['⊕'] = '⊕', 1319 | ['⊖'] = '⊖', 1320 | ['⊖'] = '⊖', 1321 | ['⊗'] = '⊗', 1322 | ['⊗'] = '⊗', 1323 | ['⊘'] = '⊘', 1324 | ['⊙'] = '⊙', 1325 | ['⊙'] = '⊙', 1326 | ['⊚'] = '⊚', 1327 | ['⊚'] = '⊚', 1328 | ['⊛'] = '⊛', 1329 | ['⊛'] = '⊛', 1330 | ['⊝'] = '⊝', 1331 | ['⊝'] = '⊝', 1332 | ['⊞'] = '⊞', 1333 | ['⊞'] = '⊞', 1334 | ['⊟'] = '⊟', 1335 | ['⊟'] = '⊟', 1336 | ['⊠'] = '⊠', 1337 | ['⊠'] = '⊠', 1338 | ['⊡'] = '⊡', 1339 | ['⊡'] = '⊡', 1340 | ['⊢'] = '⊢', 1341 | ['⊢'] = '⊢', 1342 | ['⊣'] = '⊣', 1343 | ['⊣'] = '⊣', 1344 | ['⊤'] = '⊤', 1345 | ['⊤'] = '⊤', 1346 | ['⊥'] = '⊥', 1347 | ['⊥'] = '⊥', 1348 | ['⊥'] = '⊥', 1349 | ['⊥'] = '⊥', 1350 | ['⊧'] = '⊧', 1351 | ['⊨'] = '⊨', 1352 | ['⊨'] = '⊨', 1353 | ['⊩'] = '⊩', 1354 | ['⊪'] = '⊪', 1355 | ['⊫'] = '⊫', 1356 | ['⊬'] = '⊬', 1357 | ['⊭'] = '⊭', 1358 | ['⊮'] = '⊮', 1359 | ['⊯'] = '⊯', 1360 | ['⊰'] = '⊰', 1361 | ['⊲'] = '⊲', 1362 | ['⊲'] = '⊲', 1363 | ['⊲'] = '⊲', 1364 | ['⊳'] = '⊳', 1365 | ['⊳'] = '⊳', 1366 | ['⊳'] = '⊳', 1367 | ['⊴'] = '⊴', 1368 | ['⊴'] = '⊴', 1369 | ['⊴'] = '⊴', 1370 | ['⊴⃒'] = '⊴⃒', 1371 | ['⊵'] = '⊵', 1372 | ['⊵'] = '⊵', 1373 | ['⊵'] = '⊵', 1374 | ['⊵⃒'] = '⊵⃒', 1375 | ['⊶'] = '⊶', 1376 | ['⊷'] = '⊷', 1377 | ['⊸'] = '⊸', 1378 | ['⊸'] = '⊸', 1379 | ['⊹'] = '⊹', 1380 | ['⊺'] = '⊺', 1381 | ['⊺'] = '⊺', 1382 | ['⊻'] = '⊻', 1383 | ['⊽'] = '⊽', 1384 | ['⊾'] = '⊾', 1385 | ['⊿'] = '⊿', 1386 | ['⋀'] = '⋀', 1387 | ['⋀'] = '⋀', 1388 | ['⋀'] = '⋀', 1389 | ['⋁'] = '⋁', 1390 | ['⋁'] = '⋁', 1391 | ['⋁'] = '⋁', 1392 | ['⋂'] = '⋂', 1393 | ['⋂'] = '⋂', 1394 | ['⋂'] = '⋂', 1395 | ['⋃'] = '⋃', 1396 | ['⋃'] = '⋃', 1397 | ['⋃'] = '⋃', 1398 | ['⋄'] = '⋄', 1399 | ['⋄'] = '⋄', 1400 | ['⋄'] = '⋄', 1401 | ['⋅'] = '⋅', 1402 | ['⋆'] = '⋆', 1403 | ['⋆'] = '⋆', 1404 | ['⋇'] = '⋇', 1405 | ['⋇'] = '⋇', 1406 | ['⋈'] = '⋈', 1407 | ['⋉'] = '⋉', 1408 | ['⋊'] = '⋊', 1409 | ['⋋'] = '⋋', 1410 | ['⋋'] = '⋋', 1411 | ['⋌'] = '⋌', 1412 | ['⋌'] = '⋌', 1413 | ['⋍'] = '⋍', 1414 | ['⋍'] = '⋍', 1415 | ['⋎'] = '⋎', 1416 | ['⋎'] = '⋎', 1417 | ['⋏'] = '⋏', 1418 | ['⋏'] = '⋏', 1419 | ['⋐'] = '⋐', 1420 | ['⋐'] = '⋐', 1421 | ['⋑'] = '⋑', 1422 | ['⋑'] = '⋑', 1423 | ['⋒'] = '⋒', 1424 | ['⋓'] = '⋓', 1425 | ['⋔'] = '⋔', 1426 | ['⋔'] = '⋔', 1427 | ['⋕'] = '⋕', 1428 | ['⋖'] = '⋖', 1429 | ['⋖'] = '⋖', 1430 | ['⋗'] = '⋗', 1431 | ['⋗'] = '⋗', 1432 | ['⋘'] = '⋘', 1433 | ['⋘̸'] = '⋘̸', 1434 | ['⋙'] = '⋙', 1435 | ['⋙'] = '⋙', 1436 | ['⋙̸'] = '⋙̸', 1437 | ['⋚'] = '⋚', 1438 | ['⋚'] = '⋚', 1439 | ['⋚'] = '⋚', 1440 | ['⋚︀'] = '⋚︀', 1441 | ['⋛'] = '⋛', 1442 | ['⋛'] = '⋛', 1443 | ['⋛'] = '⋛', 1444 | ['⋛︀'] = '⋛︀', 1445 | ['⋞'] = '⋞', 1446 | ['⋞'] = '⋞', 1447 | ['⋟'] = '⋟', 1448 | ['⋟'] = '⋟', 1449 | ['⋠'] = '⋠', 1450 | ['⋠'] = '⋠', 1451 | ['⋡'] = '⋡', 1452 | ['⋡'] = '⋡', 1453 | ['⋢'] = '⋢', 1454 | ['⋢'] = '⋢', 1455 | ['⋣'] = '⋣', 1456 | ['⋣'] = '⋣', 1457 | ['⋦'] = '⋦', 1458 | ['⋧'] = '⋧', 1459 | ['⋨'] = '⋨', 1460 | ['⋨'] = '⋨', 1461 | ['⋩'] = '⋩', 1462 | ['⋩'] = '⋩', 1463 | ['⋪'] = '⋪', 1464 | ['⋪'] = '⋪', 1465 | ['⋪'] = '⋪', 1466 | ['⋫'] = '⋫', 1467 | ['⋫'] = '⋫', 1468 | ['⋫'] = '⋫', 1469 | ['⋬'] = '⋬', 1470 | ['⋬'] = '⋬', 1471 | ['⋬'] = '⋬', 1472 | ['⋭'] = '⋭', 1473 | ['⋭'] = '⋭', 1474 | ['⋭'] = '⋭', 1475 | ['⋮'] = '⋮', 1476 | ['⋯'] = '⋯', 1477 | ['⋰'] = '⋰', 1478 | ['⋱'] = '⋱', 1479 | ['⋲'] = '⋲', 1480 | ['⋳'] = '⋳', 1481 | ['⋴'] = '⋴', 1482 | ['⋵'] = '⋵', 1483 | ['⋵̸'] = '⋵̸', 1484 | ['⋶'] = '⋶', 1485 | ['⋷'] = '⋷', 1486 | ['⋹'] = '⋹', 1487 | ['⋹̸'] = '⋹̸', 1488 | ['⋺'] = '⋺', 1489 | ['⋻'] = '⋻', 1490 | ['⋼'] = '⋼', 1491 | ['⋽'] = '⋽', 1492 | ['⋾'] = '⋾', 1493 | ['⌅'] = '⌅', 1494 | ['⌅'] = '⌅', 1495 | ['⌆'] = '⌆', 1496 | ['⌆'] = '⌆', 1497 | ['⌈'] = '⌈', 1498 | ['⌈'] = '⌈', 1499 | ['⌉'] = '⌉', 1500 | ['⌉'] = '⌉', 1501 | ['⌊'] = '⌊', 1502 | ['⌊'] = '⌊', 1503 | ['⌋'] = '⌋', 1504 | ['⌋'] = '⌋', 1505 | ['⌌'] = '⌌', 1506 | ['⌍'] = '⌍', 1507 | ['⌎'] = '⌎', 1508 | ['⌏'] = '⌏', 1509 | ['⌐'] = '⌐', 1510 | ['⌒'] = '⌒', 1511 | ['⌓'] = '⌓', 1512 | ['⌕'] = '⌕', 1513 | ['⌖'] = '⌖', 1514 | ['⌜'] = '⌜', 1515 | ['⌜'] = '⌜', 1516 | ['⌝'] = '⌝', 1517 | ['⌝'] = '⌝', 1518 | ['⌞'] = '⌞', 1519 | ['⌞'] = '⌞', 1520 | ['⌟'] = '⌟', 1521 | ['⌟'] = '⌟', 1522 | ['⌢'] = '⌢', 1523 | ['⌢'] = '⌢', 1524 | ['⌣'] = '⌣', 1525 | ['⌣'] = '⌣', 1526 | ['⌭'] = '⌭', 1527 | ['⌮'] = '⌮', 1528 | ['⌶'] = '⌶', 1529 | ['⌽'] = '⌽', 1530 | ['⌿'] = '⌿', 1531 | ['⍼'] = '⍼', 1532 | ['⎰'] = '⎰', 1533 | ['⎰'] = '⎰', 1534 | ['⎱'] = '⎱', 1535 | ['⎱'] = '⎱', 1536 | ['⎴'] = '⎴', 1537 | ['⎴'] = '⎴', 1538 | ['⎵'] = '⎵', 1539 | ['⎵'] = '⎵', 1540 | ['⎶'] = '⎶', 1541 | ['⏜'] = '⏜', 1542 | ['⏝'] = '⏝', 1543 | ['⏞'] = '⏞', 1544 | ['⏟'] = '⏟', 1545 | ['⏢'] = '⏢', 1546 | ['⏧'] = '⏧', 1547 | ['␣'] = '␣', 1548 | ['Ⓢ'] = 'Ⓢ', 1549 | ['Ⓢ'] = 'Ⓢ', 1550 | ['─'] = '─', 1551 | ['─'] = '─', 1552 | ['│'] = '│', 1553 | ['┌'] = '┌', 1554 | ['┐'] = '┐', 1555 | ['└'] = '└', 1556 | ['┘'] = '┘', 1557 | ['├'] = '├', 1558 | ['┤'] = '┤', 1559 | ['┬'] = '┬', 1560 | ['┴'] = '┴', 1561 | ['┼'] = '┼', 1562 | ['═'] = '═', 1563 | ['║'] = '║', 1564 | ['╒'] = '╒', 1565 | ['╓'] = '╓', 1566 | ['╔'] = '╔', 1567 | ['╕'] = '╕', 1568 | ['╖'] = '╖', 1569 | ['╗'] = '╗', 1570 | ['╘'] = '╘', 1571 | ['╙'] = '╙', 1572 | ['╚'] = '╚', 1573 | ['╛'] = '╛', 1574 | ['╜'] = '╜', 1575 | ['╝'] = '╝', 1576 | ['╞'] = '╞', 1577 | ['╟'] = '╟', 1578 | ['╠'] = '╠', 1579 | ['╡'] = '╡', 1580 | ['╢'] = '╢', 1581 | ['╣'] = '╣', 1582 | ['╤'] = '╤', 1583 | ['╥'] = '╥', 1584 | ['╦'] = '╦', 1585 | ['╧'] = '╧', 1586 | ['╨'] = '╨', 1587 | ['╩'] = '╩', 1588 | ['╪'] = '╪', 1589 | ['╫'] = '╫', 1590 | ['╬'] = '╬', 1591 | ['▀'] = '▀', 1592 | ['▄'] = '▄', 1593 | ['█'] = '█', 1594 | ['░'] = '░', 1595 | ['▒'] = '▒', 1596 | ['▓'] = '▓', 1597 | ['□'] = '□', 1598 | ['□'] = '□', 1599 | ['□'] = '□', 1600 | ['▪'] = '▪', 1601 | ['▪'] = '▪', 1602 | ['▪'] = '▪', 1603 | ['▪'] = '▪', 1604 | ['▫'] = '▫', 1605 | ['▭'] = '▭', 1606 | ['▮'] = '▮', 1607 | ['▱'] = '▱', 1608 | ['△'] = '△', 1609 | ['△'] = '△', 1610 | ['▴'] = '▴', 1611 | ['▴'] = '▴', 1612 | ['▵'] = '▵', 1613 | ['▵'] = '▵', 1614 | ['▸'] = '▸', 1615 | ['▸'] = '▸', 1616 | ['▹'] = '▹', 1617 | ['▹'] = '▹', 1618 | ['▽'] = '▽', 1619 | ['▽'] = '▽', 1620 | ['▾'] = '▾', 1621 | ['▾'] = '▾', 1622 | ['▿'] = '▿', 1623 | ['▿'] = '▿', 1624 | ['◂'] = '◂', 1625 | ['◂'] = '◂', 1626 | ['◃'] = '◃', 1627 | ['◃'] = '◃', 1628 | ['◊'] = '◊', 1629 | ['◊'] = '◊', 1630 | ['○'] = '○', 1631 | ['◬'] = '◬', 1632 | ['◯'] = '◯', 1633 | ['◯'] = '◯', 1634 | ['◸'] = '◸', 1635 | ['◹'] = '◹', 1636 | ['◺'] = '◺', 1637 | ['◻'] = '◻', 1638 | ['◼'] = '◼', 1639 | ['★'] = '★', 1640 | ['★'] = '★', 1641 | ['☆'] = '☆', 1642 | ['☎'] = '☎', 1643 | ['♀'] = '♀', 1644 | ['♂'] = '♂', 1645 | ['♠'] = '♠', 1646 | ['♠'] = '♠', 1647 | ['♣'] = '♣', 1648 | ['♣'] = '♣', 1649 | ['♥'] = '♥', 1650 | ['♥'] = '♥', 1651 | ['♦'] = '♦', 1652 | ['♦'] = '♦', 1653 | ['♪'] = '♪', 1654 | ['♭'] = '♭', 1655 | ['♮'] = '♮', 1656 | ['♮'] = '♮', 1657 | ['♯'] = '♯', 1658 | ['✓'] = '✓', 1659 | ['✓'] = '✓', 1660 | ['✗'] = '✗', 1661 | ['✠'] = '✠', 1662 | ['✠'] = '✠', 1663 | ['✶'] = '✶', 1664 | ['❘'] = '❘', 1665 | ['❲'] = '❲', 1666 | ['❳'] = '❳', 1667 | ['⟈'] = '⟈', 1668 | ['⟉'] = '⟉', 1669 | ['⟦'] = '⟦', 1670 | ['⟦'] = '⟦', 1671 | ['⟧'] = '⟧', 1672 | ['⟧'] = '⟧', 1673 | ['⟨'] = '⟨', 1674 | ['⟨'] = '⟨', 1675 | ['⟨'] = '⟨', 1676 | ['⟩'] = '⟩', 1677 | ['⟩'] = '⟩', 1678 | ['⟩'] = '⟩', 1679 | ['⟪'] = '⟪', 1680 | ['⟫'] = '⟫', 1681 | ['⟬'] = '⟬', 1682 | ['⟭'] = '⟭', 1683 | ['⟵'] = '⟵', 1684 | ['⟵'] = '⟵', 1685 | ['⟵'] = '⟵', 1686 | ['⟶'] = '⟶', 1687 | ['⟶'] = '⟶', 1688 | ['⟶'] = '⟶', 1689 | ['⟷'] = '⟷', 1690 | ['⟷'] = '⟷', 1691 | ['⟷'] = '⟷', 1692 | ['⟸'] = '⟸', 1693 | ['⟸'] = '⟸', 1694 | ['⟸'] = '⟸', 1695 | ['⟹'] = '⟹', 1696 | ['⟹'] = '⟹', 1697 | ['⟹'] = '⟹', 1698 | ['⟺'] = '⟺', 1699 | ['⟺'] = '⟺', 1700 | ['⟺'] = '⟺', 1701 | ['⟼'] = '⟼', 1702 | ['⟼'] = '⟼', 1703 | ['⟿'] = '⟿', 1704 | ['⤂'] = '⤂', 1705 | ['⤃'] = '⤃', 1706 | ['⤄'] = '⤄', 1707 | ['⤅'] = '⤅', 1708 | ['⤌'] = '⤌', 1709 | ['⤍'] = '⤍', 1710 | ['⤍'] = '⤍', 1711 | ['⤎'] = '⤎', 1712 | ['⤏'] = '⤏', 1713 | ['⤏'] = '⤏', 1714 | ['⤐'] = '⤐', 1715 | ['⤐'] = '⤐', 1716 | ['⤑'] = '⤑', 1717 | ['⤒'] = '⤒', 1718 | ['⤓'] = '⤓', 1719 | ['⤖'] = '⤖', 1720 | ['⤙'] = '⤙', 1721 | ['⤚'] = '⤚', 1722 | ['⤛'] = '⤛', 1723 | ['⤜'] = '⤜', 1724 | ['⤝'] = '⤝', 1725 | ['⤞'] = '⤞', 1726 | ['⤟'] = '⤟', 1727 | ['⤠'] = '⤠', 1728 | ['⤣'] = '⤣', 1729 | ['⤤'] = '⤤', 1730 | ['⤥'] = '⤥', 1731 | ['⤥'] = '⤥', 1732 | ['⤦'] = '⤦', 1733 | ['⤦'] = '⤦', 1734 | ['⤧'] = '⤧', 1735 | ['⤨'] = '⤨', 1736 | ['⤨'] = '⤨', 1737 | ['⤩'] = '⤩', 1738 | ['⤩'] = '⤩', 1739 | ['⤪'] = '⤪', 1740 | ['⤳'] = '⤳', 1741 | ['⤳̸'] = '⤳̸', 1742 | ['⤵'] = '⤵', 1743 | ['⤶'] = '⤶', 1744 | ['⤷'] = '⤷', 1745 | ['⤸'] = '⤸', 1746 | ['⤹'] = '⤹', 1747 | ['⤼'] = '⤼', 1748 | ['⤽'] = '⤽', 1749 | ['⥅'] = '⥅', 1750 | ['⥈'] = '⥈', 1751 | ['⥉'] = '⥉', 1752 | ['⥊'] = '⥊', 1753 | ['⥋'] = '⥋', 1754 | ['⥎'] = '⥎', 1755 | ['⥏'] = '⥏', 1756 | ['⥐'] = '⥐', 1757 | ['⥑'] = '⥑', 1758 | ['⥒'] = '⥒', 1759 | ['⥓'] = '⥓', 1760 | ['⥔'] = '⥔', 1761 | ['⥕'] = '⥕', 1762 | ['⥖'] = '⥖', 1763 | ['⥗'] = '⥗', 1764 | ['⥘'] = '⥘', 1765 | ['⥙'] = '⥙', 1766 | ['⥚'] = '⥚', 1767 | ['⥛'] = '⥛', 1768 | ['⥜'] = '⥜', 1769 | ['⥝'] = '⥝', 1770 | ['⥞'] = '⥞', 1771 | ['⥟'] = '⥟', 1772 | ['⥠'] = '⥠', 1773 | ['⥡'] = '⥡', 1774 | ['⥢'] = '⥢', 1775 | ['⥣'] = '⥣', 1776 | ['⥤'] = '⥤', 1777 | ['⥥'] = '⥥', 1778 | ['⥦'] = '⥦', 1779 | ['⥧'] = '⥧', 1780 | ['⥨'] = '⥨', 1781 | ['⥩'] = '⥩', 1782 | ['⥪'] = '⥪', 1783 | ['⥫'] = '⥫', 1784 | ['⥬'] = '⥬', 1785 | ['⥭'] = '⥭', 1786 | ['⥮'] = '⥮', 1787 | ['⥮'] = '⥮', 1788 | ['⥯'] = '⥯', 1789 | ['⥯'] = '⥯', 1790 | ['⥰'] = '⥰', 1791 | ['⥱'] = '⥱', 1792 | ['⥲'] = '⥲', 1793 | ['⥳'] = '⥳', 1794 | ['⥴'] = '⥴', 1795 | ['⥵'] = '⥵', 1796 | ['⥶'] = '⥶', 1797 | ['⥸'] = '⥸', 1798 | ['⥹'] = '⥹', 1799 | ['⥻'] = '⥻', 1800 | ['⥼'] = '⥼', 1801 | ['⥽'] = '⥽', 1802 | ['⥾'] = '⥾', 1803 | ['⥿'] = '⥿', 1804 | ['⦅'] = '⦅', 1805 | ['⦆'] = '⦆', 1806 | ['⦋'] = '⦋', 1807 | ['⦌'] = '⦌', 1808 | ['⦍'] = '⦍', 1809 | ['⦎'] = '⦎', 1810 | ['⦏'] = '⦏', 1811 | ['⦐'] = '⦐', 1812 | ['⦑'] = '⦑', 1813 | ['⦒'] = '⦒', 1814 | ['⦓'] = '⦓', 1815 | ['⦔'] = '⦔', 1816 | ['⦕'] = '⦕', 1817 | ['⦖'] = '⦖', 1818 | ['⦚'] = '⦚', 1819 | ['⦜'] = '⦜', 1820 | ['⦝'] = '⦝', 1821 | ['⦤'] = '⦤', 1822 | ['⦥'] = '⦥', 1823 | ['⦦'] = '⦦', 1824 | ['⦧'] = '⦧', 1825 | ['⦨'] = '⦨', 1826 | ['⦩'] = '⦩', 1827 | ['⦪'] = '⦪', 1828 | ['⦫'] = '⦫', 1829 | ['⦬'] = '⦬', 1830 | ['⦭'] = '⦭', 1831 | ['⦮'] = '⦮', 1832 | ['⦯'] = '⦯', 1833 | ['⦰'] = '⦰', 1834 | ['⦱'] = '⦱', 1835 | ['⦲'] = '⦲', 1836 | ['⦳'] = '⦳', 1837 | ['⦴'] = '⦴', 1838 | ['⦵'] = '⦵', 1839 | ['⦶'] = '⦶', 1840 | ['⦷'] = '⦷', 1841 | ['⦹'] = '⦹', 1842 | ['⦻'] = '⦻', 1843 | ['⦼'] = '⦼', 1844 | ['⦾'] = '⦾', 1845 | ['⦿'] = '⦿', 1846 | ['⧀'] = '⧀', 1847 | ['⧁'] = '⧁', 1848 | ['⧂'] = '⧂', 1849 | ['⧃'] = '⧃', 1850 | ['⧄'] = '⧄', 1851 | ['⧅'] = '⧅', 1852 | ['⧉'] = '⧉', 1853 | ['⧍'] = '⧍', 1854 | ['⧎'] = '⧎', 1855 | ['⧏'] = '⧏', 1856 | ['⧏̸'] = '⧏̸', 1857 | ['⧐'] = '⧐', 1858 | ['⧐̸'] = '⧐̸', 1859 | ['⧜'] = '⧜', 1860 | ['⧝'] = '⧝', 1861 | ['⧞'] = '⧞', 1862 | ['⧣'] = '⧣', 1863 | ['⧤'] = '⧤', 1864 | ['⧥'] = '⧥', 1865 | ['⧫'] = '⧫', 1866 | ['⧫'] = '⧫', 1867 | ['⧴'] = '⧴', 1868 | ['⧶'] = '⧶', 1869 | ['⨀'] = '⨀', 1870 | ['⨀'] = '⨀', 1871 | ['⨁'] = '⨁', 1872 | ['⨁'] = '⨁', 1873 | ['⨂'] = '⨂', 1874 | ['⨂'] = '⨂', 1875 | ['⨄'] = '⨄', 1876 | ['⨄'] = '⨄', 1877 | ['⨆'] = '⨆', 1878 | ['⨆'] = '⨆', 1879 | ['⨌'] = '⨌', 1880 | ['⨌'] = '⨌', 1881 | ['⨍'] = '⨍', 1882 | ['⨐'] = '⨐', 1883 | ['⨑'] = '⨑', 1884 | ['⨒'] = '⨒', 1885 | ['⨓'] = '⨓', 1886 | ['⨔'] = '⨔', 1887 | ['⨕'] = '⨕', 1888 | ['⨖'] = '⨖', 1889 | ['⨗'] = '⨗', 1890 | ['⨢'] = '⨢', 1891 | ['⨣'] = '⨣', 1892 | ['⨤'] = '⨤', 1893 | ['⨥'] = '⨥', 1894 | ['⨦'] = '⨦', 1895 | ['⨧'] = '⨧', 1896 | ['⨩'] = '⨩', 1897 | ['⨪'] = '⨪', 1898 | ['⨭'] = '⨭', 1899 | ['⨮'] = '⨮', 1900 | ['⨯'] = '⨯', 1901 | ['⨰'] = '⨰', 1902 | ['⨱'] = '⨱', 1903 | ['⨳'] = '⨳', 1904 | ['⨴'] = '⨴', 1905 | ['⨵'] = '⨵', 1906 | ['⨶'] = '⨶', 1907 | ['⨷'] = '⨷', 1908 | ['⨸'] = '⨸', 1909 | ['⨹'] = '⨹', 1910 | ['⨺'] = '⨺', 1911 | ['⨻'] = '⨻', 1912 | ['⨼'] = '⨼', 1913 | ['⨼'] = '⨼', 1914 | ['⨿'] = '⨿', 1915 | ['⩀'] = '⩀', 1916 | ['⩂'] = '⩂', 1917 | ['⩃'] = '⩃', 1918 | ['⩄'] = '⩄', 1919 | ['⩅'] = '⩅', 1920 | ['⩆'] = '⩆', 1921 | ['⩇'] = '⩇', 1922 | ['⩈'] = '⩈', 1923 | ['⩉'] = '⩉', 1924 | ['⩊'] = '⩊', 1925 | ['⩋'] = '⩋', 1926 | ['⩌'] = '⩌', 1927 | ['⩍'] = '⩍', 1928 | ['⩐'] = '⩐', 1929 | ['⩓'] = '⩓', 1930 | ['⩔'] = '⩔', 1931 | ['⩕'] = '⩕', 1932 | ['⩖'] = '⩖', 1933 | ['⩗'] = '⩗', 1934 | ['⩘'] = '⩘', 1935 | ['⩚'] = '⩚', 1936 | ['⩛'] = '⩛', 1937 | ['⩜'] = '⩜', 1938 | ['⩝'] = '⩝', 1939 | ['⩟'] = '⩟', 1940 | ['⩦'] = '⩦', 1941 | ['⩪'] = '⩪', 1942 | ['⩭'] = '⩭', 1943 | ['⩭̸'] = '⩭̸', 1944 | ['⩮'] = '⩮', 1945 | ['⩯'] = '⩯', 1946 | ['⩰'] = '⩰', 1947 | ['⩰̸'] = '⩰̸', 1948 | ['⩱'] = '⩱', 1949 | ['⩲'] = '⩲', 1950 | ['⩳'] = '⩳', 1951 | ['⩴'] = '⩴', 1952 | ['⩵'] = '⩵', 1953 | ['⩷'] = '⩷', 1954 | ['⩷'] = '⩷', 1955 | ['⩸'] = '⩸', 1956 | ['⩹'] = '⩹', 1957 | ['⩺'] = '⩺', 1958 | ['⩻'] = '⩻', 1959 | ['⩼'] = '⩼', 1960 | ['⩽'] = '⩽', 1961 | ['⩽'] = '⩽', 1962 | ['⩽'] = '⩽', 1963 | ['⩽̸'] = '⩽̸', 1964 | ['⩽̸'] = '⩽̸', 1965 | ['⩽̸'] = '⩽̸', 1966 | ['⩾'] = '⩾', 1967 | ['⩾'] = '⩾', 1968 | ['⩾'] = '⩾', 1969 | ['⩾̸'] = '⩾̸', 1970 | ['⩾̸'] = '⩾̸', 1971 | ['⩾̸'] = '⩾̸', 1972 | ['⩿'] = '⩿', 1973 | ['⪀'] = '⪀', 1974 | ['⪁'] = '⪁', 1975 | ['⪂'] = '⪂', 1976 | ['⪃'] = '⪃', 1977 | ['⪄'] = '⪄', 1978 | ['⪅'] = '⪅', 1979 | ['⪅'] = '⪅', 1980 | ['⪆'] = '⪆', 1981 | ['⪆'] = '⪆', 1982 | ['⪇'] = '⪇', 1983 | ['⪇'] = '⪇', 1984 | ['⪈'] = '⪈', 1985 | ['⪈'] = '⪈', 1986 | ['⪉'] = '⪉', 1987 | ['⪉'] = '⪉', 1988 | ['⪊'] = '⪊', 1989 | ['⪊'] = '⪊', 1990 | ['⪋'] = '⪋', 1991 | ['⪋'] = '⪋', 1992 | ['⪌'] = '⪌', 1993 | ['⪌'] = '⪌', 1994 | ['⪍'] = '⪍', 1995 | ['⪎'] = '⪎', 1996 | ['⪏'] = '⪏', 1997 | ['⪐'] = '⪐', 1998 | ['⪑'] = '⪑', 1999 | ['⪒'] = '⪒', 2000 | ['⪓'] = '⪓', 2001 | ['⪔'] = '⪔', 2002 | ['⪕'] = '⪕', 2003 | ['⪕'] = '⪕', 2004 | ['⪖'] = '⪖', 2005 | ['⪖'] = '⪖', 2006 | ['⪗'] = '⪗', 2007 | ['⪘'] = '⪘', 2008 | ['⪙'] = '⪙', 2009 | ['⪚'] = '⪚', 2010 | ['⪝'] = '⪝', 2011 | ['⪞'] = '⪞', 2012 | ['⪟'] = '⪟', 2013 | ['⪠'] = '⪠', 2014 | ['⪡'] = '⪡', 2015 | ['⪡̸'] = '⪡̸', 2016 | ['⪢'] = '⪢', 2017 | ['⪢̸'] = '⪢̸', 2018 | ['⪤'] = '⪤', 2019 | ['⪥'] = '⪥', 2020 | ['⪦'] = '⪦', 2021 | ['⪧'] = '⪧', 2022 | ['⪨'] = '⪨', 2023 | ['⪩'] = '⪩', 2024 | ['⪪'] = '⪪', 2025 | ['⪫'] = '⪫', 2026 | ['⪬'] = '⪬', 2027 | ['⪬︀'] = '⪬︀', 2028 | ['⪭'] = '⪭', 2029 | ['⪭︀'] = '⪭︀', 2030 | ['⪮'] = '⪮', 2031 | ['⪯'] = '⪯', 2032 | ['⪯'] = '⪯', 2033 | ['⪯'] = '⪯', 2034 | ['⪯̸'] = '⪯̸', 2035 | ['⪯̸'] = '⪯̸', 2036 | ['⪯̸'] = '⪯̸', 2037 | ['⪰'] = '⪰', 2038 | ['⪰'] = '⪰', 2039 | ['⪰'] = '⪰', 2040 | ['⪰̸'] = '⪰̸', 2041 | ['⪰̸'] = '⪰̸', 2042 | ['⪰̸'] = '⪰̸', 2043 | ['⪳'] = '⪳', 2044 | ['⪴'] = '⪴', 2045 | ['⪵'] = '⪵', 2046 | ['⪵'] = '⪵', 2047 | ['⪶'] = '⪶', 2048 | ['⪶'] = '⪶', 2049 | ['⪷'] = '⪷', 2050 | ['⪷'] = '⪷', 2051 | ['⪸'] = '⪸', 2052 | ['⪸'] = '⪸', 2053 | ['⪹'] = '⪹', 2054 | ['⪹'] = '⪹', 2055 | ['⪺'] = '⪺', 2056 | ['⪺'] = '⪺', 2057 | ['⪻'] = '⪻', 2058 | ['⪼'] = '⪼', 2059 | ['⪽'] = '⪽', 2060 | ['⪾'] = '⪾', 2061 | ['⪿'] = '⪿', 2062 | ['⫀'] = '⫀', 2063 | ['⫁'] = '⫁', 2064 | ['⫂'] = '⫂', 2065 | ['⫃'] = '⫃', 2066 | ['⫄'] = '⫄', 2067 | ['⫅'] = '⫅', 2068 | ['⫅'] = '⫅', 2069 | ['⫅̸'] = '⫅̸', 2070 | ['⫅̸'] = '⫅̸', 2071 | ['⫆'] = '⫆', 2072 | ['⫆'] = '⫆', 2073 | ['⫆̸'] = '⫆̸', 2074 | ['⫆̸'] = '⫆̸', 2075 | ['⫇'] = '⫇', 2076 | ['⫈'] = '⫈', 2077 | ['⫋'] = '⫋', 2078 | ['⫋'] = '⫋', 2079 | ['⫋︀'] = '⫋︀', 2080 | ['⫋︀'] = '⫋︀', 2081 | ['⫌'] = '⫌', 2082 | ['⫌'] = '⫌', 2083 | ['⫌︀'] = '⫌︀', 2084 | ['⫌︀'] = '⫌︀', 2085 | ['⫏'] = '⫏', 2086 | ['⫐'] = '⫐', 2087 | ['⫑'] = '⫑', 2088 | ['⫒'] = '⫒', 2089 | ['⫓'] = '⫓', 2090 | ['⫔'] = '⫔', 2091 | ['⫕'] = '⫕', 2092 | ['⫖'] = '⫖', 2093 | ['⫗'] = '⫗', 2094 | ['⫘'] = '⫘', 2095 | ['⫙'] = '⫙', 2096 | ['⫚'] = '⫚', 2097 | ['⫛'] = '⫛', 2098 | ['⫤'] = '⫤', 2099 | ['⫤'] = '⫤', 2100 | ['⫦'] = '⫦', 2101 | ['⫧'] = '⫧', 2102 | ['⫨'] = '⫨', 2103 | ['⫩'] = '⫩', 2104 | ['⫫'] = '⫫', 2105 | ['⫬'] = '⫬', 2106 | ['⫭'] = '⫭', 2107 | ['⫮'] = '⫮', 2108 | ['⫯'] = '⫯', 2109 | ['⫰'] = '⫰', 2110 | ['⫱'] = '⫱', 2111 | ['⫲'] = '⫲', 2112 | ['⫳'] = '⫳', 2113 | ['⫽'] = '⫽', 2114 | ['⫽⃥'] = '⫽⃥', 2115 | ['ff'] = 'ff', 2116 | ['fi'] = 'fi', 2117 | ['fl'] = 'fl', 2118 | ['ffi'] = 'ffi', 2119 | ['ffl'] = 'ffl', 2120 | ['𝒜'] = '𝒜', 2121 | ['𝒞'] = '𝒞', 2122 | ['𝒟'] = '𝒟', 2123 | ['𝒢'] = '𝒢', 2124 | ['𝒥'] = '𝒥', 2125 | ['𝒦'] = '𝒦', 2126 | ['𝒩'] = '𝒩', 2127 | ['𝒪'] = '𝒪', 2128 | ['𝒫'] = '𝒫', 2129 | ['𝒬'] = '𝒬', 2130 | ['𝒮'] = '𝒮', 2131 | ['𝒯'] = '𝒯', 2132 | ['𝒰'] = '𝒰', 2133 | ['𝒱'] = '𝒱', 2134 | ['𝒲'] = '𝒲', 2135 | ['𝒳'] = '𝒳', 2136 | ['𝒴'] = '𝒴', 2137 | ['𝒵'] = '𝒵', 2138 | ['𝒶'] = '𝒶', 2139 | ['𝒷'] = '𝒷', 2140 | ['𝒸'] = '𝒸', 2141 | ['𝒹'] = '𝒹', 2142 | ['𝒻'] = '𝒻', 2143 | ['𝒽'] = '𝒽', 2144 | ['𝒾'] = '𝒾', 2145 | ['𝒿'] = '𝒿', 2146 | ['𝓀'] = '𝓀', 2147 | ['𝓁'] = '𝓁', 2148 | ['𝓂'] = '𝓂', 2149 | ['𝓃'] = '𝓃', 2150 | ['𝓅'] = '𝓅', 2151 | ['𝓆'] = '𝓆', 2152 | ['𝓇'] = '𝓇', 2153 | ['𝓈'] = '𝓈', 2154 | ['𝓉'] = '𝓉', 2155 | ['𝓊'] = '𝓊', 2156 | ['𝓋'] = '𝓋', 2157 | ['𝓌'] = '𝓌', 2158 | ['𝓍'] = '𝓍', 2159 | ['𝓎'] = '𝓎', 2160 | ['𝓏'] = '𝓏', 2161 | ['𝔄'] = '𝔄', 2162 | ['𝔅'] = '𝔅', 2163 | ['𝔇'] = '𝔇', 2164 | ['𝔈'] = '𝔈', 2165 | ['𝔉'] = '𝔉', 2166 | ['𝔊'] = '𝔊', 2167 | ['𝔍'] = '𝔍', 2168 | ['𝔎'] = '𝔎', 2169 | ['𝔏'] = '𝔏', 2170 | ['𝔐'] = '𝔐', 2171 | ['𝔑'] = '𝔑', 2172 | ['𝔒'] = '𝔒', 2173 | ['𝔓'] = '𝔓', 2174 | ['𝔔'] = '𝔔', 2175 | ['𝔖'] = '𝔖', 2176 | ['𝔗'] = '𝔗', 2177 | ['𝔘'] = '𝔘', 2178 | ['𝔙'] = '𝔙', 2179 | ['𝔚'] = '𝔚', 2180 | ['𝔛'] = '𝔛', 2181 | ['𝔜'] = '𝔜', 2182 | ['𝔞'] = '𝔞', 2183 | ['𝔟'] = '𝔟', 2184 | ['𝔠'] = '𝔠', 2185 | ['𝔡'] = '𝔡', 2186 | ['𝔢'] = '𝔢', 2187 | ['𝔣'] = '𝔣', 2188 | ['𝔤'] = '𝔤', 2189 | ['𝔥'] = '𝔥', 2190 | ['𝔦'] = '𝔦', 2191 | ['𝔧'] = '𝔧', 2192 | ['𝔨'] = '𝔨', 2193 | ['𝔩'] = '𝔩', 2194 | ['𝔪'] = '𝔪', 2195 | ['𝔫'] = '𝔫', 2196 | ['𝔬'] = '𝔬', 2197 | ['𝔭'] = '𝔭', 2198 | ['𝔮'] = '𝔮', 2199 | ['𝔯'] = '𝔯', 2200 | ['𝔰'] = '𝔰', 2201 | ['𝔱'] = '𝔱', 2202 | ['𝔲'] = '𝔲', 2203 | ['𝔳'] = '𝔳', 2204 | ['𝔴'] = '𝔴', 2205 | ['𝔵'] = '𝔵', 2206 | ['𝔶'] = '𝔶', 2207 | ['𝔷'] = '𝔷', 2208 | ['𝔸'] = '𝔸', 2209 | ['𝔹'] = '𝔹', 2210 | ['𝔻'] = '𝔻', 2211 | ['𝔼'] = '𝔼', 2212 | ['𝔽'] = '𝔽', 2213 | ['𝔾'] = '𝔾', 2214 | ['𝕀'] = '𝕀', 2215 | ['𝕁'] = '𝕁', 2216 | ['𝕂'] = '𝕂', 2217 | ['𝕃'] = '𝕃', 2218 | ['𝕄'] = '𝕄', 2219 | ['𝕆'] = '𝕆', 2220 | ['𝕊'] = '𝕊', 2221 | ['𝕋'] = '𝕋', 2222 | ['𝕌'] = '𝕌', 2223 | ['𝕍'] = '𝕍', 2224 | ['𝕎'] = '𝕎', 2225 | ['𝕏'] = '𝕏', 2226 | ['𝕐'] = '𝕐', 2227 | ['𝕒'] = '𝕒', 2228 | ['𝕓'] = '𝕓', 2229 | ['𝕔'] = '𝕔', 2230 | ['𝕕'] = '𝕕', 2231 | ['𝕖'] = '𝕖', 2232 | ['𝕗'] = '𝕗', 2233 | ['𝕘'] = '𝕘', 2234 | ['𝕙'] = '𝕙', 2235 | ['𝕚'] = '𝕚', 2236 | ['𝕛'] = '𝕛', 2237 | ['𝕜'] = '𝕜', 2238 | ['𝕝'] = '𝕝', 2239 | ['𝕞'] = '𝕞', 2240 | ['𝕟'] = '𝕟', 2241 | ['𝕠'] = '𝕠', 2242 | ['𝕡'] = '𝕡', 2243 | ['𝕢'] = '𝕢', 2244 | ['𝕣'] = '𝕣', 2245 | ['𝕤'] = '𝕤', 2246 | ['𝕥'] = '𝕥', 2247 | ['𝕦'] = '𝕦', 2248 | ['𝕧'] = '𝕧', 2249 | ['𝕨'] = '𝕨', 2250 | ['𝕩'] = '𝕩', 2251 | ['𝕪'] = '𝕪', 2252 | ['𝕫'] = '𝕫', 2253 | [' '] = ' ', 2254 | ['!'] = '!', 2255 | ['"'] = '"', 2256 | ['#'] = '#', 2257 | ['$'] = '$', 2258 | ['%'] = '%%', 2259 | ['&'] = '&', 2260 | ['''] = "'", 2261 | ['('] = '(', 2262 | [')'] = ')', 2263 | ['*'] = '*', 2264 | ['+'] = '+', 2265 | [','] = ',', 2266 | ['-'] = '-', 2267 | ['.'] = '.', 2268 | ['/'] = '/', 2269 | [' '] = ' ', 2270 | ['Œ'] = 'Œ', 2271 | ['œ'] = 'œ', 2272 | ['Š'] = 'Š', 2273 | ['š'] = 'š', 2274 | ['Ÿ'] = 'Ÿ', 2275 | ['ƒ'] = 'ƒ', 2276 | ['–'] = '–', 2277 | ['—'] = '—', 2278 | ['‘'] = '‘', 2279 | ['’'] = '’', 2280 | ['‚'] = '‚', 2281 | ['“'] = '“', 2282 | ['”'] = '”', 2283 | ['„'] = '„', 2284 | ['†'] = '†', 2285 | ['‡'] = '‡', 2286 | ['•'] = '•', 2287 | ['…'] = '…', 2288 | ['‰'] = '‰', 2289 | ['€'] = '€', 2290 | ['™'] = '™' 2291 | } 2292 | 2293 | function htmlEntities.filter (input, table) 2294 | if not input then 2295 | if error_msg_htmlEntities then error('htmlEntities[filter] >> ERROR: input is value nil') end 2296 | return false 2297 | end 2298 | if not table then 2299 | if error_msg_htmlEntities then error('htmlEntities[filter] >> ERROR: table is value nil') end 2300 | return false 2301 | end 2302 | local output = input 2303 | for s, v in pairs(table) do 2304 | output = output:gsub(s, v) 2305 | end 2306 | return output 2307 | end 2308 | 2309 | function htmlEntities.ASCII_HEX (input) 2310 | if not input then 2311 | if error_msg_htmlEntities then error('htmlEntities[ASCII_HEX] >> ERROR: input is value nil') end 2312 | return false 2313 | end 2314 | if math.abs(_VERSION:sub(-1)) >= 3 then 2315 | return utf8.char(input) 2316 | else 2317 | input = math.abs(input) 2318 | if input < 128 then 2319 | return string.char(input) 2320 | else 2321 | --> FIX UTF8 for Lua 5.2 and 5.1 https://stackoverflow.com/a/26052539 2322 | local bytemarkers = {{0x7FF,192},{0xFFFF,224},{0x1FFFFF,240}} 2323 | local charbytes = {} 2324 | for bytes, vals in ipairs(bytemarkers) do 2325 | if input <= vals[1] then 2326 | for b = bytes+1, 2, -1 do 2327 | local mod = input % 64 2328 | input = (input - mod) / 64 2329 | charbytes[b] = string.char(128 + mod) 2330 | end 2331 | charbytes[1] = string.char(vals[2] + input) 2332 | break 2333 | end 2334 | end 2335 | return table.concat(charbytes) 2336 | end 2337 | end 2338 | end 2339 | 2340 | function htmlEntities.ASCII_DEC (input) 2341 | if not input then 2342 | if error_msg_htmlEntities then error('htmlEntities[ASCII_DEC] >> ERROR: input is value nil') end 2343 | return false 2344 | end 2345 | local output = htmlEntities.ASCII_HEX(tonumber(input, 16)) 2346 | return output 2347 | end 2348 | 2349 | function htmlEntities.decode (input) 2350 | if not input then 2351 | if error_msg_htmlEntities then error('htmlEntities[decode] >> ERROR: input is value nil') end 2352 | return false 2353 | end 2354 | local output = string.gsub(input, '&[%w#]-;', htmlEntities_table) 2355 | if ASCII_htmlEntities then 2356 | output = string.gsub(output, '&#x([%w%d]*);', htmlEntities.ASCII_DEC) 2357 | output = string.gsub(output, '&#([%d]*);', htmlEntities.ASCII_HEX) 2358 | end 2359 | 2360 | if debug_htmlEntities then print('>>'..output) end 2361 | return output 2362 | end 2363 | 2364 | function htmlEntities.encode (input) 2365 | if not input then 2366 | if error_msg_htmlEntities then error('htmlEntities[encode] >> ERROR: input is value nil') end 2367 | return false 2368 | end 2369 | input = htmlEntities.decode(input) 2370 | local output = input:gsub('([%z\1-\127\194-\244][\128-\191]*)', 2371 | function(char) 2372 | local charbyte = char:byte() 2373 | if (string.len(char) == 1) then 2374 | if charbyte == 32 then -- Space char 2375 | return ' ' 2376 | end 2377 | return '&#'.. charbyte ..';' 2378 | else 2379 | return char 2380 | end 2381 | end) 2382 | if debug_htmlEntities then print('>>'..output) end 2383 | return output 2384 | end 2385 | 2386 | function string:htmlDecode(filter) 2387 | if not self then return false end 2388 | return htmlEntities.decode(self) 2389 | end 2390 | 2391 | function string:htmlEncode(filter) 2392 | if not self then return false end 2393 | return htmlEntities.encode(self) 2394 | end 2395 | 2396 | return htmlEntities 2397 | --------------------------------------------------------------------------------