├── .github └── workflows │ └── release.yml ├── .gitignore ├── .vscode ├── extensions.json ├── launch.json └── settings.json ├── LICENSE ├── README.md ├── libs ├── html_entities.lua └── json.lua ├── main.lua └── src ├── annotations.lua ├── config.lua ├── fetcher.lua ├── generator.lua ├── meta.lua ├── parser.lua ├── terminal.lua └── utils.lua /.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 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # System 2 | .DS_Store 3 | Thumbs.db 4 | 5 | # Temporary 6 | /doc 7 | info.json 8 | ref-doc.zip 9 | json_list.txt 10 | 11 | # Generated 12 | /api -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "tomblind.local-lua-debugger-vscode" 4 | ] 5 | } -------------------------------------------------------------------------------- /.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 | } -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Lua.diagnostics.disable": [ 3 | "redefined-local" 4 | ] 5 | } -------------------------------------------------------------------------------- /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. -------------------------------------------------------------------------------- /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-api-emmylua](https://github.com/d954mas/defold-api-emmylua/) (Java) by Dmitry Popov 66 | - [defold-lua-annotations](https://github.com/mikatuo/defold-lua-annotations/) (C#) by Dennis Shendrik -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | 'dm*', 52 | 'debug_doc', 53 | 'coroutine_doc', 54 | 'math_doc', 55 | 'package_doc', 56 | 'string_doc', 57 | 'table_doc', 58 | 'engine_doc', 59 | 'base_doc', 60 | 'os_doc', 61 | 'io_doc' 62 | } 63 | 64 | ---Ignored functions 65 | ---Possible to use suffix `*` 66 | config.ignored_funcs = { 67 | 'init', 68 | 'update', 69 | 'fixed_update', 70 | 'on_input', 71 | 'on_message', 72 | 'on_reload', 73 | 'final', 74 | 'client:*', 75 | 'server:*', 76 | 'master:*', 77 | 'connected:*', 78 | 'unconnected:*' 79 | } 80 | 81 | --- Global replacements for param names 82 | config.global_name_replacements = { 83 | ['repeat'] = 'repeating', 84 | ['...commands'] = '...' 85 | } 86 | 87 | --- Local replacements for param names 88 | config.local_name_replacements = { 89 | pprint = { 90 | param_v = '...' 91 | } 92 | } 93 | 94 | --- Global replacements for param types 95 | config.global_type_replacements = { 96 | quat = 'quaternion', 97 | resource = 'resource_data', 98 | buffer = 'buffer_data', 99 | bufferstream = 'buffer_stream', 100 | handle = 'resource_handle', 101 | texture = 'resource_handle', 102 | predicate = 'render_predicate', 103 | client = 'socket_client', 104 | master = 'socket_master', 105 | unconnected = 'socket_unconnected', 106 | ['vmath.vector3'] = 'vector3', 107 | ['vmath.vector4'] = 'vector4', 108 | vecto4 = 'vector4', 109 | schema = 'editor.schema', 110 | component = 'editor.component', 111 | transaction_step = 'editor.transaction_step', 112 | } 113 | 114 | --- Local replacements for param types 115 | config.local_type_replacements = { 116 | ['buffer.create'] = { 117 | param_table_declaration = '{ name:hash|string, type:constant, count:number }[]' 118 | }, 119 | ['buffer.set_metadata'] = { 120 | param_table_values = 'number[]' 121 | }, 122 | ['buffer.get_metadata'] = { 123 | return_table_values = 'number[]' 124 | }, 125 | ['collectionfactory.create'] = { 126 | return_table_ids = 'table' 127 | }, 128 | ['collectionproxy.get_resources'] = { 129 | return_table_resources = 'string[]' 130 | }, 131 | ['collectionproxy.missing_resources'] = { 132 | return_table_resources = 'string[]' 133 | }, 134 | ['crash.get_modules'] = { 135 | return_table_modules = '{ name:string, address:string }[]' 136 | }, 137 | ['editor.bundle.check_boxes_grid_row'] = { 138 | ['return_component[_row'] = 'editor.component[]' 139 | }, 140 | ['editor.bundle.common_variant_grid_row'] = { 141 | ['return_component[_row'] = 'editor.component[]' 142 | }, 143 | ['editor.bundle.desktop_variant_grid_row'] = { 144 | ['return_component[_row'] = 'editor.component[]' 145 | }, 146 | ['editor.bundle.dialog'] = { 147 | ['param_component[_rows'] = 'editor.component[]' 148 | }, 149 | ['editor.bundle.grid_row'] = { 150 | ['param_component[_content'] = 'editor.component[]', 151 | ['return_component[_row'] = 'editor.component[]' 152 | }, 153 | ['editor.bundle.texture_compression_grid_row'] = { 154 | ['return_component[_row'] = 'editor.component[]' 155 | }, 156 | ['editor.transact'] = { 157 | ['param_transaction_step[_txs'] = 'editor.transaction_step[]' 158 | }, 159 | ['editor.execute'] = { 160 | param_table_options = '{ reload_resources:boolean|nil, out:string|nil, err:string|nil }' 161 | }, 162 | ['editor.external_file_attributes'] = { 163 | return_table_attributes = '{ path:string, exists:boolean, is_file:boolean, is_directory:boolean }' 164 | }, 165 | ['editor.prefs.schema.array'] = { 166 | param_table_opts = '{ item:editor.schema, default:any[]|nil, scope:string|nil }' 167 | }, 168 | ['editor.prefs.schema.boolean'] = { 169 | param_table_opts = '{ default:boolean|nil, scope:string|nil }' 170 | }, 171 | ['editor.prefs.schema.enum'] = { 172 | param_table_opts = '{ values:(nil|boolean|number|string)[], default:any|nil, scope:string|nil }' 173 | }, 174 | ['editor.prefs.schema.integer'] = { 175 | param_table_opts = '{ default:integer|nil, scope:string|nil }' 176 | }, 177 | ['editor.prefs.schema.keyword'] = { 178 | param_table_opts = '{ default:string|nil, scope:string|nil }' 179 | }, 180 | ['editor.prefs.schema.number'] = { 181 | param_table_opts = '{ default:number|nil, scope:string|nil }' 182 | }, 183 | ['editor.prefs.schema.object'] = { 184 | param_table_opts = '{ properties:table, default:table|nil, scope:string|nil }' 185 | }, 186 | ['editor.prefs.schema.object_of'] = { 187 | param_table_opts = '{ key:editor.schema, val:editor.schema, default:table|nil, scope:string|nil }' 188 | }, 189 | ['editor.prefs.schema.set'] = { 190 | param_table_opts = '{ item:editor.schema, default:table|nil, scope:string|nil }' 191 | }, 192 | ['editor.prefs.schema.string'] = { 193 | param_table_opts = '{ default:string|nil, scope:string|nil }' 194 | }, 195 | ['editor.prefs.schema.tuple'] = { 196 | param_table_opts = '{ items:editor.schema[], default:any[]|nil, scope:string|nil }' 197 | }, 198 | ['editor.resource_attributes'] = { 199 | return_table_value = '{ exists:boolean, is_file:boolean, is_directory:boolean }' 200 | }, 201 | ['editor.ui.use_memo'] = { 202 | ['param_...any_...'] = 'any', 203 | ['return_...any_values'] = 'any ...' 204 | }, 205 | ['editor.ui.use_state'] = { 206 | ['param_...any_...'] = 'any' 207 | }, 208 | ['gui.clone_tree'] = { 209 | return_table_clones = 'table' 210 | }, 211 | ['gui.get_tree'] = { 212 | return_table_clones = 'table' 213 | }, 214 | ['gui.play_flipbook'] = { 215 | param_table_play_properties = '{ offset:number|nil, playback_rate:number|nil }' 216 | }, 217 | ['gui.stop_particlefx'] = { 218 | param_table_options = '{ clear:boolean|nil }' 219 | }, 220 | ['http.server.external_file_response'] = { 221 | ['param_table<string,string>_headers'] = 'table' 222 | }, 223 | ['http.server.json_response'] = { 224 | ['param_table<string,string>_headers'] = 'table' 225 | }, 226 | ['http.server.resource_response'] = { 227 | ['param_table<string,string>_headers'] = 'table' 228 | }, 229 | ['http.server.response'] = { 230 | ['param_table<string,string>_headers'] = 'table' 231 | }, 232 | ['json.decode'] = { 233 | param_table_options = '{ decode_null_as_userdata:boolean|nil }' 234 | }, 235 | ['json.encode'] = { 236 | param_table_options = '{ encode_empty_table_as_object:string }' 237 | }, 238 | ['particlefx.stop'] = { 239 | param_table_options = '{ clear:boolean|nil }' 240 | }, 241 | ['sprite.play_flipbook'] = { 242 | param_table_options = '{ offset:number|nil, playback_rate:number|nil }' 243 | }, 244 | ['sound.play'] = { 245 | param_table_play_properties = '{ delay:number|nil, gain:number|nil, pan:number|nil, speed:number|nil }' 246 | }, 247 | ['sound.stop'] = { 248 | param_table_stop_properties = '{ play_id:number }' 249 | }, 250 | ['model.play_anim'] = { 251 | param_table_play_properties = '{ blend_duration:number|nil, offset:number|nil, playback_rate:number|nil}' 252 | }, 253 | ['image.load'] = { 254 | return_table_image = '{ width:number, height:number, type:constant, buffer:string }' 255 | }, 256 | ['image.load_buffer'] = { 257 | return_table_image = '{ width:number, height:number, type:constant, buffer:buffer_data }' 258 | }, 259 | ['physics.get_joint_properties'] = { 260 | return_table_properties = '{ collide_connected:boolean|nil }' 261 | }, 262 | ['physics.raycast'] = { 263 | param_table_options = '{ all:boolean|nil }', 264 | return_table_result = 'physics.raycast_response[]|physics.raycast_response' 265 | }, 266 | ['physics.get_shape'] = { 267 | return_table_table = '{ type:number|nil, diameter:number|nil, dimensions:vector3|nil, height:number|nil }' 268 | }, 269 | ['physics.set_shape'] = { 270 | param_table_table = '{ diameter:number|nil, dimensions:vector3|nil, height:number|nil }' 271 | }, 272 | ['resource.create_atlas'] = { 273 | param_table_table = 'resource.atlas' 274 | }, 275 | ['resource.get_atlas'] = { 276 | return_table_data = 'resource.atlas' 277 | }, 278 | ['resource.set_atlas'] = { 279 | param_table_table = 'resource.atlas' 280 | }, 281 | ['resource.get_render_target_info'] = { 282 | return_table_table = '{ handle:resource_handle, attachments:{ handle:resource_handle, width:number, height:number, depth:number, mipmaps:number, type:number, buffer_type:number, texture:hash }[] }' 283 | }, 284 | ['resource.create_sound_data'] = { 285 | param_table_options = '{ data:string|nil, filesize:number|nil, partial:boolean|nil }' 286 | }, 287 | ['resource.create_texture'] = { 288 | param_table_table = '{ type:number, width:number, height:number, depth:number, format:number, flags:number|nil, max_mipmaps:number|nil, compression_type:number|nil}' 289 | }, 290 | ['resource.create_texture_async'] = { 291 | param_table_table = '{ type:number, width:number, height:number, depth:number, format:number, flags:number|nil, max_mipmaps:number|nil, compression_type:number|nil}' 292 | }, 293 | ['resource.set_texture'] = { 294 | param_table_table = '{ type:number, width:number, height:number, format:number, x:number|nil, y:number|nil, z:number|nil, mipmap:number|nil, compression_type:number|nil}' 295 | }, 296 | ['resource.get_texture_info'] = { 297 | return_table_table = '{ handle:resource_handle, width:number, height:number, depth:number, mipmaps:number, flags:number, type:number }' 298 | }, 299 | ['resource.get_text_metrics'] = { 300 | param_table_options = '{ width:number|nil, leading:number|nil, tracking:number|nil, line_break:boolean|nil}', 301 | return_table_metrics = '{ width:number, height:number, max_ascent:number, max_descent:number }' 302 | }, 303 | ['resource.create_buffer'] = { 304 | param_table_table = '{ buffer:buffer_data, transfer_ownership:boolean|nil }' 305 | }, 306 | ['resource.set_buffer'] = { 307 | param_table_table = '{ transfer_ownership: boolean|nil }' 308 | }, 309 | ['render.draw'] = { 310 | param_table_options = '{ frustum:matrix4|nil, frustum_planes:number|nil, constants:constant_buffer|nil }' 311 | }, 312 | ['render.draw_debug3d'] = { 313 | param_table_options = '{ frustum:matrix4|nil, frustum_planes:number|nil }' 314 | }, 315 | ['render.predicate'] = { 316 | param_table_tags = '(string|hash)[]' 317 | }, 318 | ['render.render_target'] = { 319 | param_table_parameters = 'table' 320 | }, 321 | ['render.set_camera'] = { 322 | param_table_options = '{ use_frustum:boolean|nil }' 323 | }, 324 | ['render.set_render_target'] = { 325 | param_table_options = '{ transient:number[]|nil }' 326 | }, 327 | ['sound.get_groups'] = { 328 | return_table_groups = 'hash[]' 329 | }, 330 | ['sys.get_sys_info'] = { 331 | param_table_options = '{ ignore_secure:boolean|nil }', 332 | return_table_sys_info = '{ device_model:string|nil, manufacturer:string|nil, system_name:string, system_version:string, api_version:string, language:string, device_language:string, territory:string, gmt_offset:number, device_ident:string|nil, user_agent:string|nil }' 333 | }, 334 | ['sys.get_application_info'] = { 335 | return_table_app_info = '{ installed:boolean }' 336 | }, 337 | ['sys.get_engine_info'] = { 338 | return_table_engine_info = '{ version:string, version_sha1:string, is_debug:boolean }' 339 | }, 340 | ['sys.get_ifaddrs'] = { 341 | return_table_ifaddrs = '{ name:string, address:string|nil, mac:string|nil, up:boolean, running:boolean }' 342 | }, 343 | ['sys.open_url'] = { 344 | param_table_attributes = '{ target:string|nil, name:string|nil }' 345 | }, 346 | ['timer.get_info'] = { 347 | return_table_data = '{ time_remaining:number, delay:number, repeating:boolean }' 348 | }, 349 | ['vmath.euler_to_quat'] = { 350 | param_number_y = 'number|nil', 351 | param_number_z = 'number|nil' 352 | }, 353 | ['vmath.vector'] = { 354 | param_table_t = 'number[]' 355 | }, 356 | ['zip.pack'] = { 357 | param_table_opts = '{ method:string|nil, level:integer|nil }' 358 | } 359 | } 360 | 361 | config.generics = { 362 | ['vmath.clamp'] = 'number|vector3|vector4', 363 | ['vmath.dot'] = 'vector3|vector4', 364 | ['vmath.normalize'] = 'vector3|vector4|quaternion', 365 | ['vmath.mul_per_elem'] = 'vector3|vector4', 366 | ['vmath.slerp'] = 'vector3|vector4', 367 | ['vmath.lerp'] = 'vector3|vector4' 368 | } 369 | 370 | ---Default type for unknown types 371 | config.unknown_type = 'unknown' 372 | 373 | ---Known types 374 | config.known_types = { 375 | 'nil', 376 | 'any', 377 | 'boolean', 378 | 'number', 379 | 'integer', 380 | 'string', 381 | 'userdata', 382 | 'function', 383 | 'thread', 384 | 'table' 385 | } 386 | 387 | ---Known classes 388 | config.known_classes = { 389 | vector3 = { 390 | fields = { 391 | x = 'number', 392 | y = 'number', 393 | z = 'number' 394 | }, 395 | operators = { 396 | sub = { param = 'vector3', result = 'vector3' }, 397 | add = { param = 'vector3', result = 'vector3' }, 398 | mul = { param = 'number', result = 'vector3' }, 399 | unm = { result = 'vector3' } 400 | } 401 | }, 402 | vector4 = { 403 | fields = { 404 | x = 'number', 405 | y = 'number', 406 | z = 'number', 407 | w = 'number' 408 | }, 409 | operators = { 410 | sub = { param = 'vector4', result = 'vector4' }, 411 | add = { param = 'vector4', result = 'vector4' }, 412 | mul = { param = 'number', result = 'vector4' }, 413 | unm = { result = 'vector4' } 414 | } 415 | }, 416 | url = { 417 | socket = 'hash', 418 | path = 'hash', 419 | fragment = 'hash' 420 | }, 421 | ['http.server'] = { 422 | is_global = true 423 | }, 424 | ['zip'] = { 425 | is_global = true 426 | }, 427 | ['zip.METHOD'] = { 428 | is_global = true 429 | }, 430 | ['socket.dns'] = { 431 | is_global = true 432 | }, 433 | ['editor.ui'] = { 434 | is_global = true 435 | }, 436 | ['editor.ui.ALIGNMENT'] = { 437 | is_global = true 438 | }, 439 | ['editor.ui.COLOR'] = { 440 | is_global = true 441 | }, 442 | ['editor.ui.HEADING_STYLE'] = { 443 | is_global = true 444 | }, 445 | ['editor.ui.ICON'] = { 446 | is_global = true 447 | }, 448 | ['editor.ui.ISSUE_SEVERITY'] = { 449 | is_global = true 450 | }, 451 | ['editor.ui.ORIENTATION'] = { 452 | is_global = true 453 | }, 454 | ['editor.ui.PADDING'] = { 455 | is_global = true 456 | }, 457 | ['editor.ui.SPACING'] = { 458 | is_global = true 459 | }, 460 | ['editor.ui.TEXT_ALIGNMENT'] = { 461 | is_global = true 462 | }, 463 | ['editor.prefs'] = { 464 | is_global = true 465 | }, 466 | ['editor.prefs.SCOPE'] = { 467 | is_global = true 468 | }, 469 | ['editor.prefs.schema'] = { 470 | is_global = true 471 | }, 472 | ['editor.tx'] = { 473 | is_global = true 474 | }, 475 | ['editor.bundle'] = { 476 | is_global = true 477 | }, 478 | matrix4 = { 479 | m00 = 'number', 480 | m01 = 'number', 481 | m02 = 'number', 482 | m03 = 'number', 483 | m10 = 'number', 484 | m11 = 'number', 485 | m12 = 'number', 486 | m13 = 'number', 487 | m20 = 'number', 488 | m21 = 'number', 489 | m22 = 'number', 490 | m23 = 'number', 491 | m30 = 'number', 492 | m31 = 'number', 493 | m32 = 'number', 494 | m33 = 'number', 495 | c0 = 'vector4', 496 | c1 = 'vector4', 497 | c2 = 'vector4', 498 | c3 = 'vector4', 499 | }, 500 | ['resource.atlas'] = { 501 | texture = 'string|hash The path to the texture resource, e.g "/main/my_texture.texturec"', 502 | animations = 'resource.animation[] A list of the animations in the atlas', 503 | geometries = 'resource.geometry[] A list of the geometries that should map to the texture data', 504 | }, 505 | ['resource.animation'] = { 506 | id = 'string The id of the animation, used in e.g sprite.play_animation', 507 | width = 'integer The width of the animation', 508 | height = 'integer The height of the animation', 509 | 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.', 510 | 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.', 511 | playback = 'constant|nil Optional playback mode of the animation, the default value is go.PLAYBACK_ONCE_FORWARD', 512 | fps = 'integer|nil Optional fps of the animation, the default value is 30', 513 | flip_vertical = 'boolean|nil Optional flip the animation vertically, the default value is false', 514 | flip_horizontal = 'boolean|nil Optional flip the animation horizontally, the default value is false' 515 | }, 516 | ['resource.geometry'] = { 517 | id = 'string The name of the geometry. Used when matching animations between multiple atlases', 518 | vertices = 'number[] A list of the vertices in texture space of the geometry in the form { px0, py0, px1, py1, ..., pxn, pyn }', 519 | uvs = 'number[] A list of the uv coordinates in texture space of the geometry in the form of { u0, v0, u1, v1, ..., un, vn }', 520 | 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.' 521 | }, 522 | ['physics.raycast_response'] = { 523 | fraction = 'number The fraction of the hit measured along the ray, where 0 is the start of the ray and 1 is the end', 524 | position = 'vector3 The world position of the hit', 525 | normal = 'vector3 The normal of the surface of the collision object where it was hit', 526 | id = 'hash The instance id of the hit collision object', 527 | group = 'hash The collision group of the hit collision object as a hashed name', 528 | request_id = 'number The id supplied when the ray cast was requested' 529 | }, 530 | ['on_input.action'] = { 531 | value = 'number|nil 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.', 532 | pressed = 'boolean|nil If the input was pressed this frame. This is not present for mouse movement.', 533 | released = 'boolean|nil If the input was released this frame. This is not present for mouse movement.', 534 | repeated = 'boolean|nil 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.', 535 | x = 'number|nil The x value of a pointer device, if present.', 536 | y = 'number|nil The y value of a pointer device, if present.', 537 | screen_x = 'number|nil The screen space x value of a pointer device, if present.', 538 | screen_y = 'number|nil The screen space y value of a pointer device, if present.', 539 | dx = 'number|nil The change in x value of a pointer device, if present.', 540 | dy = 'number|nil The change in y value of a pointer device, if present.', 541 | screen_dx = 'number|nil The change in screen space x value of a pointer device, if present.', 542 | gamepad = 'integer|nil The change in screen space y value of a pointer device, if present.', 543 | screen_dy = 'number|nil The index of the gamepad device that provided the input.', 544 | touch = 'on_input.touch[]|nil List of touch input, one element per finger, if present.', 545 | text = 'string|nil The text entered with the `text` action, if present' 546 | }, 547 | ['on_input.touch'] = { 548 | id = 'number A number identifying the touch input during its duration.', 549 | pressed = 'boolean True if the finger was pressed this frame.', 550 | released = 'boolean True if the finger was released this frame.', 551 | tap_count = 'integer Number of taps, one for single, two for double-tap, etc', 552 | x = 'number The x touch location.', 553 | y = 'number The y touch location.', 554 | dx = 'number The change in x value.', 555 | dy = 'number The change in y value.', 556 | acc_x = 'number|nil Accelerometer x value (if present).', 557 | acc_y = 'number|nil Accelerometer y value (if present).', 558 | acc_z = 'number|nil Accelerometer z value (if present).', 559 | screen_x = 'number|nil The screen space x value of a pointer device, if present.', 560 | screen_y = 'number|nil The screen space y value of a pointer device, if present.', 561 | screen_dx = 'number|nil The change in screen space x value of a pointer device, if present.', 562 | screen_dy = 'number|nil The index of the gamepad device that provided the input.' 563 | } 564 | } 565 | 566 | ---Known aliases 567 | config.known_aliases = { 568 | bool = 'boolean', 569 | float = 'number', 570 | array = 'table', 571 | 572 | quaternion = 'vector4', 573 | hash = 'userdata', 574 | node = 'userdata', 575 | constant = 'userdata', 576 | vector = 'userdata', 577 | 578 | resource_data = 'userdata', 579 | constant_buffer = 'userdata', 580 | render_target = 'string|userdata', 581 | render_predicate = 'userdata', 582 | resource_handle = 'number|userdata', 583 | buffer_stream = 'userdata', 584 | buffer_data = 'userdata', 585 | 586 | b2BodyType = 'number', 587 | b2World = 'userdata', 588 | b2Body = 'userdata', 589 | 590 | socket_client = 'userdata', 591 | socket_master = 'userdata', 592 | socket_unconnected = 'userdata', 593 | 594 | ['editor.schema'] = 'userdata', 595 | ['editor.component'] = 'userdata', 596 | ['editor.transaction_step'] = 'userdata' 597 | } 598 | 599 | config.disabled_diagnostics = { 600 | 'lowercase-global', 601 | 'missing-return', 602 | 'duplicate-doc-param', 603 | 'duplicate-set-field', 604 | 'args-after-dots' 605 | } 606 | 607 | return config 608 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | ---Decode text to get rid of html tags and entities 20 | ---@param text string 21 | ---@return string 22 | local function decode_text(text) 23 | local result = text:gsub('%b<>', '') 24 | local decoded_result = html_entities.decode(result) 25 | 26 | if type(decoded_result) == 'string' then 27 | result = decoded_result 28 | end 29 | 30 | return result 31 | end 32 | 33 | ---Make an annotable comment 34 | ---@param text string 35 | ---@param tab? string Indent string, default is '---' 36 | ---@return string 37 | local function make_comment(text, tab) 38 | local tab = tab or '---' 39 | local text = decode_text(text or '') 40 | 41 | local lines = text == '' and { text } or utils.get_lines(text) 42 | local result = '' 43 | 44 | for index, line in ipairs(lines) do 45 | result = result .. tab .. line 46 | 47 | if index < #lines then 48 | result = result .. '\n' 49 | end 50 | end 51 | 52 | return result 53 | end 54 | 55 | ---Make an annotable module header 56 | ---@param defold_version string 57 | ---@param title string 58 | ---@param description string 59 | ---@return string 60 | local function make_header(defold_version, title, description) 61 | local result = '' 62 | 63 | result = result .. '--[[\n' 64 | result = result .. ' Generated with ' .. config.generator_url .. '\n' 65 | result = result .. ' Defold ' .. defold_version .. '\n\n' 66 | result = result .. ' ' .. decode_text(title) .. '\n' 67 | 68 | if description and description ~= title then 69 | result = result .. '\n' 70 | result = result .. make_comment(description, ' ') .. '\n' 71 | end 72 | 73 | result = result .. '--]]' 74 | 75 | return result 76 | end 77 | 78 | ---Make annotable diagnostic disable flags 79 | ---@param disabled_diagnostics string[] list of diagnostic disabel flags 80 | ---@return string 81 | local function make_disabled_diagnostics(disabled_diagnostics) 82 | local result = '' 83 | 84 | result = result .. '---@meta\n' 85 | 86 | for index, disabled_diagnostic in ipairs(disabled_diagnostics) do 87 | result = result .. '---@diagnostic disable: ' .. disabled_diagnostic 88 | 89 | if index < #config.disabled_diagnostics then 90 | result = result .. '\n' 91 | end 92 | end 93 | 94 | return result 95 | end 96 | 97 | ---Wrap the class body to an annotable namespace 98 | ---@param name string 99 | ---@param body string 100 | ---@return string 101 | local function make_namespace(name, body) 102 | local result = '' 103 | 104 | result = result .. '---@class defold_api.' .. name .. '\n' 105 | result = result .. name .. ' = {}\n\n' 106 | result = result .. body .. '\n\n' 107 | result = result .. 'return ' .. name 108 | 109 | return result 110 | end 111 | 112 | ---Make an annotatable constant 113 | ---@param element element 114 | ---@return string 115 | local function make_const(element) 116 | local result = '' 117 | 118 | result = result .. make_comment(element.description) .. '\n' 119 | result = result .. element.name .. ' = nil' 120 | 121 | return result 122 | end 123 | 124 | ---Make an annotatable param name 125 | ---@param parameter table 126 | ---@param is_return boolean 127 | ---@param element element 128 | ---@return string name 129 | ---@return boolean is_optional 130 | local function make_param_name(parameter, is_return, element) 131 | local name = parameter.name 132 | local is_optional = false 133 | 134 | if name:sub(1, 1) == '[' and name:sub(-1) == ']' then 135 | is_optional = true 136 | name = name:sub(2, #name - 1) 137 | end 138 | 139 | name = config.global_name_replacements[name] or name 140 | 141 | local local_replacements = config.local_name_replacements[element.name] or {} 142 | name = local_replacements[(is_return and 'return_' or 'param_') .. name] or name 143 | 144 | if name:sub(-3) == '...' then 145 | name = '...' 146 | end 147 | 148 | name = name:gsub('-', '_') 149 | 150 | return name, is_optional 151 | end 152 | 153 | ---Make annotatable param types 154 | ---@param name string 155 | ---@param types table 156 | ---@param is_optional boolean 157 | ---@param is_return boolean 158 | ---@param element element 159 | ---@return string concated_string 160 | local function make_param_types(name, types, is_optional, is_return, element) 161 | local local_replacements = config.local_type_replacements[element.name] or {} 162 | 163 | for index = 1, #types do 164 | local type = types[index] 165 | local is_known = false 166 | 167 | local replacement = config.global_type_replacements[type] or type 168 | replacement = local_replacements[(is_return and 'return_' or 'param_') .. type .. '_' .. name] or replacement 169 | 170 | if replacement then 171 | type = replacement 172 | is_known = true 173 | end 174 | 175 | for _, known_type in ipairs(config.known_types) do 176 | is_known = is_known or type == known_type 177 | end 178 | 179 | local known_classes = utils.sorted_keys(config.known_classes) 180 | for _, known_class in ipairs(known_classes) do 181 | is_known = is_known or type == known_class 182 | end 183 | 184 | local known_aliases = utils.sorted_keys(config.known_aliases) 185 | for _, known_alias in ipairs(known_aliases) do 186 | is_known = is_known or type == known_alias 187 | end 188 | 189 | is_known = is_known or type:sub(1, 9) == 'function(' 190 | 191 | if not is_known then 192 | types[index] = config.unknown_type 193 | else 194 | type = type:gsub('function%(%)', 'function') 195 | 196 | if type:sub(1, 9) == 'function(' then 197 | type = 'fun' .. type:sub(9) 198 | end 199 | 200 | types[index] = type 201 | end 202 | end 203 | 204 | if is_optional then 205 | local is_already_optional = false 206 | 207 | for _, type in ipairs(types) do 208 | is_already_optional = is_already_optional or type == 'nil' 209 | end 210 | 211 | if not is_already_optional then 212 | table.insert(types, 'nil') 213 | end 214 | end 215 | 216 | local result = table.concat(types, '|') 217 | result = #result > 0 and result or config.unknown_type 218 | 219 | return result 220 | end 221 | 222 | ---Make an annotable param description 223 | ---@param description string 224 | ---@return string 225 | local function make_param_description(description) 226 | local result = decode_text(description) 227 | result = result:gsub('\n', '\n---') 228 | return result 229 | end 230 | 231 | ---Make annotable param line 232 | ---@param parameter table 233 | ---@param element element 234 | ---@return string 235 | local function make_param(parameter, element) 236 | local name, is_optional = make_param_name(parameter, false, element) 237 | local joined_types = make_param_types(name, parameter.types, is_optional, false, element) 238 | local description = make_param_description(parameter.doc) 239 | 240 | return '---@param ' .. name .. ' ' .. joined_types .. ' ' .. description 241 | end 242 | 243 | ---Make an annotable return line 244 | ---@param returnvalue table 245 | ---@param element element 246 | ---@return string 247 | local function make_return(returnvalue, element) 248 | local name, is_optional = make_param_name(returnvalue, true, element) 249 | local types = make_param_types(name, returnvalue.types, is_optional, true, element) 250 | local description = make_param_description(returnvalue.doc) 251 | 252 | return '---@return ' .. types .. ' ' .. name .. ' ' .. description 253 | end 254 | 255 | ---Make annotable func lines 256 | ---@param element element 257 | ---@return string? 258 | local function make_func(element) 259 | if utils.is_blacklisted(config.ignored_funcs, element.name) then 260 | return 261 | end 262 | 263 | local comment = make_comment(element.description) .. '\n' 264 | 265 | local generic = config.generics[element.name] 266 | local generic_occuriences = 0 267 | 268 | local params = '' 269 | for _, parameter in ipairs(element.parameters) do 270 | local param = make_param(parameter, element) 271 | local count = 0 272 | 273 | if generic then 274 | param, count = param:gsub(' ' .. generic .. ' ', ' T ') 275 | generic_occuriences = generic_occuriences + count 276 | end 277 | 278 | params = params .. param .. '\n' 279 | end 280 | 281 | local returns = '' 282 | for _, returnvalue in ipairs(element.returnvalues) do 283 | local return_ = make_return(returnvalue, element) 284 | local count = 0 285 | 286 | if generic then 287 | return_, count = return_:gsub(' ' .. generic .. ' ', ' T ') 288 | generic_occuriences = generic_occuriences + count 289 | end 290 | 291 | returns = returns .. return_ .. '\n' 292 | end 293 | 294 | if generic_occuriences >= 2 then 295 | generic = ('---@generic T: ' .. generic .. '\n') 296 | else 297 | generic = '' 298 | end 299 | 300 | local func_params = {} 301 | 302 | for _, parameter in ipairs(element.parameters) do 303 | local name = make_param_name(parameter, false, element) 304 | table.insert(func_params, name) 305 | end 306 | 307 | local func = 'function ' .. element.name .. '(' .. table.concat(func_params, ', ') .. ') end' 308 | local result = comment .. generic .. params .. returns .. func 309 | 310 | return result 311 | end 312 | 313 | ---Make an annotable alias 314 | ---@param element element 315 | ---@return string 316 | local function make_alias(element) 317 | return '---@alias ' .. element.name .. ' ' .. element.alias 318 | end 319 | 320 | ---Make an annnotable class declaration 321 | ---@param element element 322 | ---@return string 323 | local function make_class(element) 324 | local name = element.name 325 | local fields = element.fields 326 | assert(fields) 327 | 328 | local result = '' 329 | result = result .. '---@class ' .. name .. '\n' 330 | 331 | if fields.is_global == true then 332 | fields.is_global = nil 333 | result = result .. name .. ' = {}' 334 | end 335 | 336 | local field_names = utils.sorted_keys(fields) 337 | for index, field_name in ipairs(field_names) do 338 | local type = fields[field_name] 339 | 340 | result = result .. '---@field ' .. field_name .. ' ' .. type 341 | 342 | if index < #field_names then 343 | result = result .. '\n' 344 | end 345 | end 346 | 347 | local operators = element.operators 348 | 349 | if operators then 350 | local operator_names = utils.sorted_keys(operators) 351 | 352 | result = result .. '\n' 353 | 354 | for index, operator_name in ipairs(operator_names) do 355 | local operator = operators[operator_name] 356 | 357 | if operator.param then 358 | result = result .. '---@operator ' .. operator_name .. '(' .. operator.param .. '): ' .. operator.result 359 | else 360 | result = result .. '---@operator ' .. operator_name .. ': ' .. operator.result 361 | end 362 | 363 | if index < #operator_names then 364 | result = result .. '\n' 365 | end 366 | end 367 | end 368 | 369 | return result 370 | end 371 | 372 | ---Generate API module with creating a .lua file 373 | ---@param module module 374 | ---@param defold_version string like '1.0.0' 375 | local function generate_api(module, defold_version) 376 | local content = make_header(defold_version, module.info.brief, module.info.description) 377 | content = content .. '\n\n' 378 | 379 | local makers = { 380 | FUNCTION = make_func, 381 | VARIABLE = make_const, 382 | BASIC_CLASS = make_class, 383 | BASIC_ALIAS = make_alias 384 | } 385 | 386 | local elements = {} 387 | local namespace_is_required = false 388 | 389 | for _, element in ipairs(module.elements) do 390 | if makers[element.type] ~= nil then 391 | table.insert(elements, element) 392 | end 393 | 394 | if not namespace_is_required then 395 | local element_has_namespace = element.name:sub(1, #module.info.namespace) == module.info.namespace 396 | namespace_is_required = element_has_namespace 397 | end 398 | end 399 | 400 | if #elements == 0 then 401 | print('[-] The module "' .. module.info.namespace .. '" is skipped because there are no known elements') 402 | return 403 | end 404 | 405 | table.sort(elements, function(a, b) 406 | if a.type == b.type then 407 | return a.name < b.name 408 | else 409 | return a.type > b.type 410 | end 411 | end) 412 | 413 | local body = '' 414 | 415 | for index, element in ipairs(elements) do 416 | local maker = makers[element.type] 417 | local text = maker(element) 418 | 419 | if text then 420 | body = body .. text 421 | 422 | if index < #elements then 423 | local newline = element.type == 'BASIC_ALIAS' and '\n' or '\n\n' 424 | body = body .. newline 425 | end 426 | end 427 | end 428 | 429 | content = content .. make_disabled_diagnostics(config.disabled_diagnostics) .. '\n\n' 430 | 431 | if namespace_is_required then 432 | content = content .. make_namespace(module.info.namespace, body) 433 | else 434 | content = content .. body 435 | end 436 | 437 | local api_path = config.api_folder .. config.folder_separator .. module.info.namespace .. '.lua' 438 | utils.save_file(content, api_path) 439 | end 440 | 441 | -- 442 | -- Public 443 | 444 | ---Generate API modules with creating .lua files 445 | ---@param modules module[] 446 | ---@param defold_version string like '1.0.0' 447 | function generator.generate_api(modules, defold_version) 448 | print('-- Annotations Generation') 449 | 450 | terminal.delete_folder(config.api_folder) 451 | terminal.create_folder(config.api_folder) 452 | 453 | for _, module in ipairs(modules) do 454 | generate_api(module, defold_version) 455 | end 456 | 457 | print('-- Annotations Generated Successfully!\n') 458 | end 459 | 460 | return generator 461 | -------------------------------------------------------------------------------- /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)) 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/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 | -------------------------------------------------------------------------------- /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 an item is present in the black list, including by the `*` suffix rule. 75 | ---@param list string[] 76 | ---@param item string 77 | ---@return boolean is_blacklisted 78 | function utils.is_blacklisted(list, item) 79 | for _, list_element in ipairs(list) do 80 | if list_element:sub(-1) == '*' then 81 | local list_prefix = list_element:sub(1, #list_element - 1) 82 | 83 | if item:sub(1, #list_prefix) == list_prefix then 84 | return true 85 | end 86 | elseif item == list_element then 87 | return true 88 | end 89 | end 90 | 91 | return false 92 | end 93 | 94 | return utils 95 | --------------------------------------------------------------------------------