From 4971a2956fedeae74e79eabe8f3d3bed8bb57e09 Mon Sep 17 00:00:00 2001 From: Charles Pick Date: Sat, 19 Sep 2020 21:56:58 +0100 Subject: [PATCH] Initial commit --- .gitignore | 4 + .npmignore | 0 README.md | 25 +++ package.json | 17 ++ scripts/Header.md | 27 +++ scripts/bundle.js | 65 ++++++ src/AST.ts | 152 ++++++++++++++ src/All.test.ts | 106 ++++++++++ src/Evaluator.ts | 436 +++++++++++++++++++++++++++++++++++++++ src/Parser.ts | 214 +++++++++++++++++++ src/Printer.ts | 69 +++++++ src/Query.ts | 5 + src/Schema.ts | 4 + src/Utils/NumberUtils.ts | 267 ++++++++++++++++++++++++ src/Utils/ObjectUtils.ts | 2 + src/Utils/StringUtils.ts | 74 +++++++ src/Utils/index.ts | 3 + src/index.ts | 6 + tsconfig.json | 70 +++++++ yarn.lock | 13 ++ 20 files changed, 1559 insertions(+) create mode 100644 .gitignore create mode 100644 .npmignore create mode 100644 README.md create mode 100644 package.json create mode 100644 scripts/Header.md create mode 100644 scripts/bundle.js create mode 100644 src/AST.ts create mode 100644 src/All.test.ts create mode 100644 src/Evaluator.ts create mode 100644 src/Parser.ts create mode 100644 src/Printer.ts create mode 100644 src/Query.ts create mode 100644 src/Schema.ts create mode 100644 src/Utils/NumberUtils.ts create mode 100644 src/Utils/ObjectUtils.ts create mode 100644 src/Utils/StringUtils.ts create mode 100644 src/Utils/index.ts create mode 100644 src/index.ts create mode 100644 tsconfig.json create mode 100644 yarn.lock diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..18fad34 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +.vscode +node_modules +lib +playground.ts \ No newline at end of file diff --git a/.npmignore b/.npmignore new file mode 100644 index 0000000..e69de29 diff --git a/README.md b/README.md new file mode 100644 index 0000000..f43c83f --- /dev/null +++ b/README.md @@ -0,0 +1,25 @@ +``` + ▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄ ▄ +▐░░░░░░░░░░░▌▐░░░░░░░░░░░▌ ▐░░░░░░░░░░░▌▐░░░░░░░░░░░▌▐░▌ + ▀▀▀▀█░█▀▀▀▀ ▐░█▀▀▀▀▀▀▀▀▀ ▐░█▀▀▀▀▀▀▀▀▀ ▐░█▀▀▀▀▀▀▀█░▌▐░▌ + ▐░▌ ▐░▌ ▐░▌ ▐░▌ ▐░▌▐░▌ + ▐░▌ ▐░█▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄ ▐░█▄▄▄▄▄▄▄▄▄ ▐░▌ ▐░▌▐░▌ + ▐░▌ ▐░░░░░░░░░░░▌▐░░░░░░░░░░░▌▐░░░░░░░░░░░▌▐░▌ ▐░▌▐░▌ + ▐░▌ ▀▀▀▀▀▀▀▀▀█░▌ ▀▀▀▀▀▀▀▀▀▀▀ ▀▀▀▀▀▀▀▀▀█░▌▐░█▄▄▄▄▄▄▄█░▌▐░▌ + ▐░▌ ▐░▌ ▐░▌▐░░░░░░░░░░░▌▐░▌ + ▐░▌ ▄▄▄▄▄▄▄▄▄█░▌ ▄▄▄▄▄▄▄▄▄█░▌ ▀▀▀▀▀▀█░█▀▀ ▐░█▄▄▄▄▄▄▄▄▄ + ▐░▌ ▐░░░░░░░░░░░▌ ▐░░░░░░░░░░░▌ ▐░▌ ▐░░░░░░░░░░░▌ + ▀ ▀▀▀▀▀▀▀▀▀▀▀ ▀▀▀▀▀▀▀▀▀▀▀ ▀ ▀▀▀▀▀▀▀▀▀▀▀ +``` + +This is a SQL database implemented purely in TypeScript type annotations. +This means that it operates solely on types - you define a "database", +(just a type annotation) and then query it using some more type annotations. +It supports a subset of SQL, including SELECT, INSERT, UPDATE and DELETE statements. + +This was written by Charles Pick ( charles@codemix.com, https://twitter.com/c_pick ). + +You can install ts-sql in your own project with `npm install @codemix/ts-sql` or +`yarn install @codemix/ts-sql` (TypeScript 4.1 is required). + +[See the demo on the TypeScript playground!](https://www.typescriptlang.org/play?ts=4.1.0-dev.20200919#code/PQKhCgAIUgDeo0oENJVvcyKMa3-B2OaWx6pikgCaSCJpHfQzYDGktjjThnrb9LPDfJpUiAA0jEjAEaQ0J4kdWmzFnLgsXj5MtVMHD8tDnubLjhfSbOnm+3Xgu3pZTETIbH8gwX3Xolj1m78fPx0QcGCKjo+nkYEauLaonHqiXHatBLEacw2-jEm9n75yl7BId5IhoXOGAlF1egJWqquZDnu0aVVKp3FMQE8QlGxnEnJdaNiJnJ4EyKU8LCUlAAqABYAlgDOkFuQAIaQAMoAigAykAAmewAuewBGe5sApjsAtgAOADZPr08AdtcnhdIO8AK4AJyenwAnjs-pBltD3k9DgBjcHrd7XSDXJEvPZ-P4Ae1u13WRL+mwAdCsNttfgTttdVjcdtiicjwTcnttNkTvjDIBScXjtgBaSDQomgy5PABm6z++MgACIrrcHs8VQAaSgACgAVqDNtiDrjkftCSSbuS-gBKS3A5n-SAAR1BT3BsPW2ONioA5pA+b9IK8iZCRRaCcTSbbqZQAJLYzag97vcPXbYHFN3Z7suVHM7auGoz6gi4Bo4AUVOVYAwstiwmAHKHKsAJUbkAAqgAFAAiAEFllXHZB+zWqyOg6Sfv9MzSfLTdu9wUSDU9UdjPusAG48-bY1bXa7vTYALmAwH9PtWoLuVNRRNewCfFx+6wAHsBM2LNq7PiWHw1l2AB3R5IFAjETxdO5YTrFlwW+bZe3WVEAGtID1SBUUQ5CAAE3w-T9H2fYtj1PC8r2uUCfUBcFSJfVEAH13jQzC7UXGBKAATWlHCCThE09k+T4cU2P8ALhSVpXBIVQPhVd103bFaOZOA-neV4hNuUTIEIol31eL8fwk-9PlgIVwXmaE9nBeFFWEvSDKMkzf3Myy9URZE0QxLFIAAFipABGHZtkhd11khC5OKAmAvMxc9IFWIl9zk1LPRxVYXnNfEd0eA87ihIlQJxIkgyeHLssgSEU0+TMhQLKUITdD0MR5O04uAcAeqvSAAGIe02PZ-SeXrgEgAAxKKTUgl53wVJUsvmm4zRZVSngAcgjf0iUra5yqK1rPWhSsRr2RzrhpJ9KWTdYPj5SlIAAXkgABvKAQSeDlviSgBtT6sDenYLiS4LiwVcETWbPZfiSlUACE7OuHVIE+R5rhhuHVUOe6zwpVGtgQ9ZPlBnFwQ9SAAF9dXwYH1jJgAmCGZsx2Gnnh04tj2VH0eh9n4dxh6CeLImNlJpLrgpl4acB96QaSgBmFmobZ7GVQAWRGm8nl5jGsY5nG8cewnNmJiXycp2W6YVwKVf59WtfBUa9Ydw2VSF-G-lN82yblETnmp2m8HpsmAFZ7bV92AAln09V2o8F42RbC32kv9z5A+tkPbYANkjg34Y1ilrl234vQTwvVQRiFKR98W-YDmXg6B22AHYC4F1UAHVsvR73iz5xOceM50ofrknG8z5u5dDpKAA5O-V5sSc+FLjV1wf9a7lUADVBNj0FAQH1OG8l6Wg9n22AE4l-d5soRNre3aL0EAQALwni2patlv5YZsGAAGO+8NewYj+KiTEIlK470OOhRUSpwRfynlnYOABdYOXw9ioh5P9K+fxYGrgDAqKEwIqzfF+ACOy0JUYlUQQmMmwVgFhUHFuPchsf4zxtgQ5eoJSxPDspAXsJVMq9n7ijYsdDPQMKSvnFhbD9zn1-vgneVY0r+khC6ZYnouQ4NoQpaRZMAqi02KwskijLZcJzjw92xdiRchJvo+hZNF7yPMYbDOqCVHq2WJo9egciQFljuCNcSDJEGPBDIyA4M3HsKUVY1uNjQHo2hBo6UfwLhOMMUlW+sSLGcMvlgDB4Aqb7G2DdE0ABucaCI6RhWWnCH06wRKQDSpsW0jUhQtXVPcAqNJcrjgRruUKr1cqBKDMnSk1TwB9VOE8a4m1tiLSdNVGx2xxnIh+i8NSqwGlD0gDYo2wtvY0j6t3F4G8GmwGOG1aElkBkHUgE8T8m4j4vHdJ6dYPJRbYluOhA8BwTinBmRND5XoymQEhrNAZ7w7Ls3osWAkwIDg9M1PiJk1Vng3QuKciazYSRVVZCJMSQLID-OhKBcMFwswRj7L2DsdZBxtn6XiSAVYAAaIzIA3JOgAHk+h7ScDZIWswNlNdsAB5DWX0tmQG7tHDso4h5iteptT2j1No6k+v2IZwVwAAD5pmzPmYskVGSGlQtNOa-ZazOnEoabhSekIHLwmdBM45mxwADI5YzF63Lbn8qwIK2swrLUG2flHcVUqZVfBePKxVp9J5+s4VqrAOrhkGqNRNOZCylmKhWfiC4oJ6rbFAilQOtqio3nhDsyAGsvWso5YrP1PKvSBpxkK5YPhJqSulZs2NcqFXtlHGLJNr1PEvEHM2fsIrVZitOAmAA0qOTaGsACkmrg7pr1YampObTVPhCSpBp5pyqKjrSTfxW1tg2KpEcRUOD9h6VyjSl491XhH3uN8aS3kUTokxOyCEILLirTRfU99n67jfoOkSYsfIsq7DBbCSE1xa5ZgOU8UqqK+kNotOm31r1W3QnbSqPsQ5pz9u-W2LtYb2Z+tXZe6UzxNqDoTQzP189U2DIzbu4D+7ynZQwllVkslICpnVNs8M-yLg9W9eygKLaA3BvrF2mAPbo2UbjUOkdwJXqceLPh3jxrc37AuMijDpVcJwq3J6RF-oLqurqShtD+wLMgY1DhgZ6bm2EYDQKlsbZOyQBbMsCVMaqNThBn64KMTaMhlVb4ok0JNoRpVZAVdqI6xlkhClxNpM-UTq4-hzNsnG3srDopvlynhVqd7eFrTCa4svAS2uZLqNvNGYmt2P4coMxv25DCYsoEXhKiBGVWU3xAQIla3WzL2WxpeaGQp3zfKBUTlrNOdTfbvoDvjcO2dbt6OJeS5AKdM7lV0dVRrObEItpFaGYrEruGXgctzpVttAq2whpo6KujW36usf26O-L46m73d3AFJ7ZyXiokEnsMzdaiRVSJKVvDQyKsrY+0GgLHYu0hbC5p6sXb2OvRi3fejxctqpcu+lw479oSbHRuhXLwPdOQrB1upbT25Nt3e8Rz7naQbk-+4TvbI6zYNwK03U706DuRoXcu9L67N3avR1D7NJqswI8p6a5Yew0p-Ge9xt7mP+dYE2jjoL+OQToxwdsajByaeayRwAHV1-rz0J8pGRLZ2TvJzXLEpdV7uMO6vIAACk9qUgAISQH45AUappn3VSwXbp9FJAy1rdba8ZzooryUQfGI3HLr585I19lTjvfjFk5I9KkTWa+ejrxdkMIvbcHhbM2DsEeJUtgB0yr6UNhQSubDb7BPIqTe4YX62vFIqQM3Bzu6ZMywDgBgM9Dfm+t-b537vvf++D+H6Pwftfp+Kh1GUFWWXErJpsvZYODWvZayHGLIcZYg4gs3+C4-2sGsr-v+WATBHzPzXxemP3AIgMgKgKPzX26lRxeAYXnHWBIXBF5TFWeWPmpRnHAUDFehNBwP1T9Q+iwFynhkQIBGQK+SQWqSwCSUgANmqSpmmQGQ1h+CKnBCrE-FXB5HaQpHbQlTuA3C3CeU-EwN5ClkrDwIkL+H9GDjAQ5E9FxBELEOwMkNUJkINSIM+lINVFYNeHYM4O4M2F4O9hoKFEEJUiSgEKEOuDMKUk5FxCSnkIcOhEYOYNZWbFBHLjQi5nohEl5X3jLBeAwP+CwL+C8PYL9XCP0M9EINemIMjHvi8M+VRF8M9GgTMN3BEg9CSkCI9DcKN0OGkP9DSK5E+ACOyOCNENCPEJwL9XwIDDiPem0LxEFmKNKIyM+iyKCNyMqIKIGQRiJH5AET+A6PKLyKqJULuCGO+EElemmOGIJCaISJ0MRhmJGLGJVEyMqN6KCIKKN08NEjGKIMSPhkOM+E2MqVaR2IOWLTEiYPgMgARkVGoQlU5BuHDD9RVGehVEgAAB9VRo8fj-jVQFcqwtijdniCEvRDDaoTD205k5RsQQiMlthYSeCOlXp0TjDbRg43j0iDo5IUSsCoTXj3jCS-VSSvR8SuRCTg52x1h-RjxlCai2UuC4TMS2SjCTDND4iWjkR4YqToRsSTCISsBvgkSkpETbDPoFDaTwwrDyTwwzCMQmTrgkoGS1T9iBlTgiQbxYdPgaSPi5JXoVQztfiASVQJV2wLTVQAAyO020lUP4v4iEnUvUtCESEU20BE+UZE6o1Erkjk4ULE9kjEikPEpUokgMrA3U-UkSI0ik16OMz0w0qM+kxk5k4ktEsMnEkMoM8Mw3ZY-k92FMg070gmMwiU9UuPP0swuU40xUgk5Uz6VU48DUzMmUh4o3aaUhQ4ZESBFA9tQ4WSR9bM4Ld8CglAkEvQgw3Mkwv1cgskFA4OQcfKbYccpcygzKV6LclA3k5okg1o1UXs0mfszcbc6gz6PkCEHBJKEc28p4MwkSZpC8U7dc7U1lPXKDFEAcy84c0cyY1kvcqgxcyc5cqg1c9clkwMkCnco4QCg8lY48lUb874c8wcqgsUoMQC+8wC589cpKNc18z8i0QcHE-0P4Sha4Csv4dtRdJ4WETc8Cy8sCpAlcz6CYmCrA2iv1WipCks+Gci9pSi6i2i7C8lJKBi1wrom4iY0il7eczEz6AEssr0pSvggkaEYsNStM5s8ERFP4aEfVFSp4l4mEjSuirS4sIUxM8MQy4y0y2cz0firAAEuC6yNyo4don0dIwCLyzw7w1I3yso0ywYxY0YkKkSUy84sY9wi0BMQkT0SPRUDC-8z6SaNcbScctC38i8ocrSpo3KtKgqoy-VYOXuT0ICwM3i0M7k20ASo8gU1URKxBFKv4EqrCswuULKpKTK58MwstKqpKSqyEBSiPKPTq+C1q5Kyav8lA+Kl4Q4KEFSIo7kai9tU8rA8craqa1Awqv6NBP1Xa+aqg3lA64pLAfq7KmM7YYq06z0c6sqv1e6-Ks6wq4Odqjc26ia1Kh68EQ6v1dqvaw6iq7KCMcc2qgsvMv4PEuUOUPMbim9CI+C6I9g4OLmUeJG24mIk0nG9gxq04nGFarcNawEai7CkhUmN8razYbq3qqaLKswg0KPN8r6kEw6wa8Gw2Uap82U+GvMKwgW+ZKs+6H0KUsWrsxa4LSkRQsmucAEdtXK9An6homQv1ayziyo76lQyEeHCkQUYSxkqi+cWip67Sy0YywG16LSwm1YxK54cEa4eWimswjUb4KuZW9mbYoIt8iYumkpaW7sd4CTF2+cJWr9J4FWlQtW3A9Q2QrW327Go20S02yy6207Cik2gEWi0Gz6Xm7GqG-i4spq92YO0O2cV27QyOz2mu722S323Yj0AOrAIayEEa7m8a-sFawEMOxWz6L2kMcc2O+o4osGqqwuyyviyyu2lC7uybFESu+cbC92qOruQevm1u7mjuqq8avu7EZ6Uy5a74UmpegEUyh2uWs+64Uy8u7kfe0y+e+ZRe9a+caWtRbI++6+9tHVbG-sUDAqc28qz6fFd8bGh+po0B6qrA4+1a6+z6AAfjZW6NBHvpJudu-p1WLCgZMqwCSigexsvqdofqwCQY-rLG5CIYwdfsVqwfoMMieFwcgHwYYexrvt7vgdIeQc-sBHYZfvJvDroZwc+hYbAfHKfo4ZoZvq4fIdQcBAkf4YVuuF5SEYYaYaSiVDSnfpQcodluIe-tVz-oAeeCAeDgIc3L0eoYEfPsgdYYscdqsaUd5UVDlEyg3tFh60yhTuzszCYaQYSPwD+kXWknJXGR1TQSkuxo3rlnwCQaofbBKnbRMA3uxtCYLF-qQZ1T+g3qOo0aeDSj-mUG8eos9RMCYcICSiycXTQTMKwCphEYwy0cePicSYHsjuxr1ouANthC0rzqwGKfnB1tZM6e6czpEp8dzrQQPIBiwFYOdieCSZ7D+FtGWCJESvoixTJD4JiffM+HbAES6b+BhF5WErYO+CrABHak2BObTBhAGYBGufud8f1XKcgCYeAawCpC+dyvABqceLXL2YOe6d5WWGWJqiBaOdhCCZCcYvGWWAiYRCCaOu7O9R0d4ZDq-qkZ-oRiMY8xMY+pAbsZ+r4Ygb9XMeJYxckeseUc+hcbcbrur1pc8bkiec2GDjpbkl5oNUQcPIIGhfPTScGQReCZyoZbGmUCQb4YSdAmuZ2cIBSfHMFYycGWybFdycadszlYIFZcKc4C5bKa1cqYRiRdqepgac0c9CDspaeGldlawFytXKzpKY6YhcNqdbTvqopD6blW5sJr+kSvfE-BhalALFyoRdke5DNtyv9YyWeQwR9aqsIPHM4TlkletelZBcjpjcDfjdZdeaSmjYDbjfGqldaawGlcdfGedfHJGchbGeNrEvTqmaaLmdGn9t5QrbGbOaeAuYkJ5Bua+GhFZZOfdYeZed4xYM9Dbe1pBeLG7BbanYWc+mBn5fhEFfhcicVdhYLG7EgElaReYcReqbNYndZUHFuaHdHczBHarcGZdf1rraecmZLsgFXbJW3frdToeYRdZYPfHKfcssWY8uceZcgGko+bwA5cgAmM+leaQZmc4Gkt1awFiqirEnHK4qQfCL0iSk4NLHLAWc4KlmwWuAmIqKCPKtuNEleaKTljyaaZRcbVEN0VI8qJBaKuxtQ78P8r3ao544LexvCtmMiu45A9cbkgmL8eg8qIaa7XHMCpSLGLE8ykk55Zg7wYRHAZ8tE6g9U64fU8PYtfBG0Z4aeAUf3uxdxd6XxbKrMaJZUPM-gdsbEZ+sc6xag-cbhHE4TchCk4CbwDfaVYRmFaibFa1cyZ7ptZKmuYVZ+qC946yZycM-ydsx88YcNZVeqdNfqY06M+loUdtczZ-OLF5qaOmnqk9G6wWkVCBHbX8+zeeWDbhcjvDbRZ7cA8Ldjc-HjdK+xpTcICQbfmq6VBkwqYRCzaLe67MPqd42Lza9gdPqxcMfEeMYWYJZQ-s9ZIW8ceooPPJZUO24s6Ze89pvZdA+urO+86+su8yl5pu7kglWFuuHu7j0lu5aQeutC5-L2qA5YqHKg4fPBBwQg6wGA6g+IseHeZ5cB7HLi4-Z1VTami+Wpuxr+hOreseuA5VBABVEo6x5x5eamYG6R4q-BF5t5TrH5BPuuESrBGuEK7oYu9+spEo9K52aIuMO7cK61auuR+pWQ7wCI5Y9psWeUHK-onJ8p9EhUlp6PgZ4RmLCZ6+tZ+5pB+MFO557wFbaXYv26xWbWYBCbxUh9M18IFOf0POcua+Wub+j+gh+2DtPjoM2NZh6eDQXd5o+ME98IG97wF9-zY1c8uS7SnAHo8tceKl+p9l-p+i8s5W7xbW9s4yqys+tZoPM+9Fe+-+vbTB9A9d4g9z+8-t5Mqk9d9Sfh4RkR-ZvHOBv+sOp2aQcj5Ui+pUYV5Vdd9zfXOLGV-Z-b8AqJ7wDD6D6H+lqb63Bb+W9c9W9MbaZ-PvcOcFF6cuvfNfOxtjtT8cmxtr4x4BubZemXcC4-bDaSjH5j9Ahb7oejeqeLHt+79Zv1UY4tFP+lYv5xfj+s8T+MuLE7ft7X7HuZ6GaBlt+mFT0IdTiKfRteHbEqJAEd7+da2goQ-iGwAEQoheJHdqvb15RfUkW+qBFjsywHHsLGbVOajvyK7oVs+hfTKMDUAp48-uZ1KDugPXIvMPG3nNnhK2Z5l8t2SAhHiYA+6sxyESjCVOCHOKi9jAqArcFtQRjQgDYIgkwOL09Dk9TenAU-u1SBCDgQkewaEJNHDAZtFBygLJlQMfLL8L8+AMQdcAYGvlMBrNbAQLyKAVtdBnAe3vYPwBq8L8+rYwS4PyDmDHg9g33s4M15D8MuRnT6FTBMo9QjcygqPKoPUGaDtBiTdqrlXn6jMl+d-RUL-2Hr-9cqnbXKmkNVrFFwBQMP6L2Ca4Fh4hLXJKHLEgE7NMhJUHZrAL+jh5Bw0kLwZsARalCfyhQtBDlwIB1D38P6SOvbwRbVDQIXQrAEw0f6KViO4gvnpIOkEOskepCA2E0QP7BMBWR-MofMNJjR1WS67LNseyQZX80EaPPngbHVZGdqY0taaKrH4HUVBBwg3KvkPG5z8a2rrHpkZXr6xNHh3wP6JtG+AyFmQm0I6uOUAQN8+OvfaNoAgH65c7i79ZjmgKjwYD2qTRdqoQySrghgB-5V6iANQLAcPqE5dinQNA7F8XmvHX-iP0eJyDwQVXeUDVwuDscXq2NfZg+2OZ-QoO0cA5sWC+bz5QOeuEmGgik5sj4c2NIbtSJG6I8KRVIxaLVx5GfAA+f0AURcA5FfNxRsbSUbSOlE4CGmnNckSTAl7c1SBTwYsIYTK46jKuKomkXVzlgNcg2qwpAcf24YUNAQUbCbl13jZGi+u0sEEZ1xza99hRqo6btziY6TDrgIvOYadxX4FRNgL7K0cUM+Fu8ko9XFYWuw-a00EWtNP9j9XR5YjlOckfPiCMZEXBpWW1fUVaPjau8b+65HkJ7zJF1NxqAAcXmQTFJB0lKAaBGLBIdwxzwSMX6k+jStsa2HMSEg3rGscgiTYxiryjw5lh3wLY4sP2Mo7tj7elY5hp9Gkrl8kB0rHltKyCaMUB+UlRiquPGQLjSmJIisZsC3HQgdxgfaWvmMLF89pxGwhUR2MXGvQtq2NZyhwUA7HdMoEqF7r2G5ZcNrxJUIsUOMbHQhmxnbb8Y+MjFNhaBj1XsJR0PHqN7xhDGCagSg5wSeWwEyoqOOIydtew5Y18pWPNYpdjO-zTnhbyi4yteUYY-+rcGNGkJABWBTMf+Quo8sV2k3GMTRL2A-syJP5DNtRNWgljCCXQskUbnN68TEmtNbGkxNKpW142nEhdvM3bR68KQqzdZkbzYTbN8AYky3n2ztYEB-OgTdiTaPGQpi8EsgvnqeMm6AiMxfPH7tZTxFTkCRRfRgb4LwBINHBRQIfjYJlqBt9x6TVaAlwElWTg+noLVn80IAjC3msHAMRaAjaOjAOnbN0YfS8pxSngulM2olK4JMMASqUoUhlJKiGispplVKW+PymtiuS2U+0XIyeDAdMp7wSqalK45+U7xRo4qW1yE4bE0OLUoqSlLa4KcMQwVUTnVIanzdtOzU4aSZwdFPA8pCUgqRVOnrvBBO5lYUh+KwBQdpSL3N4i901LHg-xvHN4tjW+IqhEeuU5aVWHdABwZBVUyNrNPKnSkPBWAVKWVO-6dlPeAfA6eORVBAljpHw06dCSkEkhzpqDTOFdKem3SdKfpB6ddPimes6KnbHadcDel0dIAH0n6iqDBI-S3J0M6actK5j-JQZbXZ6bWSRJQywZsMu8QjKRn4AAhuXYiZNOql5SLpIMjaZAEpl+ppSDIzshuM7LY1pSiPfroP3ZzTwGmE6emdyCFL4oaKTM65izLZlYl2pZ06Wb6SRIZk1SmhZNh6K4YToGmnCMWYCCFJ4yFmss16X6i1jXBcIRRHAobN5TGy1Sp7WKW13Sngz5pdVXmR6XLKrSvOmUDaZ+Ie7vBtpXMqTqjJUJmlp0mMmRo7PdkiQzsBM0zkTPukC8yZwZOGXNMpk7N3pi0z6daXDm8dUpula0rHKmnxzIZicwmc7LTk+9kZQQ2mU0zm6mddKMc22ceCaIcyNZHobmWqXdHtyPhAsjTtrL7lNw9ZaUqOYaXbA2y-SL0u2ezL9JdzxWvHAWR2U7lty55SDBeULOeBDzSpzspKVyVfFsEXKnsqDt+N9lCI9pSDMLJwPGTrjfpbXWqSVD+gSp42wHOCQH2rkhSSJdcqaXfPKk7zDCyE-EY9Sg6LCeWWwwMoK2vlYzNxJwqucRKIm1zUWpnJqWUW6n1SFpnHO4scSw53F35Q8jqQSCU7DS0F45PBSJ2am6dKiUnAziJIQVTTLZAYAhXNN-lcEtOVsrqeQoo5qcZONc8Pp-Oqn9SfCXUwha7Pk7JEBpSndhR6EoVcKcFjxVKXHyn4J8Z+WAQ4FpykYHlUpFnOhocHtkvBewF0cEKs2sIqReURQ54UyKhbOFFCoExig5T5F+pTFP1W2lwwMmvtMI56XsH9AhEIsPFwUcKXgGEmXijcqzbrNQkmhvwNJfwZSXRXnZ+pd245LSrxz1B2R-QSUbsA6GeiEFdwe0YEGSIGRRLVJhvKGMbz4IxLXowS-6WEogRbNIlyzEpUmx+p6hPoySpKFBwTDgB0lmS7JTyzaXcKP5AybST2yt79tPovbK5oQz8I-leU8AixWuBcLtitKfI9RUMoYnbAkw6RSZWhKk4OLdaLw19pYqdrWKLaCyxHpAPyUG8NmxSuinoqiiGKLCW4ExeOxgUh9el0tPRUUrpGvRPoby54BZ1Bao8Ae19ZgZlH2YmgjqCDOWL4nugdseQiM7GptEqQsYkG+9JcfgEhWvBoVJoepSoU2iIrvKUjd+SirwB5dHi3yxRhtT+WfKsApKw7t-T+UAlSVVDX5YQXpV2RngJLWlcyqESsqzOkXJla8u5U0qsWFKr5QKvQZZY9gG8OkeOTeVkh-Cgq6ltmPvFssvZckJnvQNZpArOW3NTVSjKe46rMaPoF5jyz+jyqnGYYpXhqrS7FhHuCNeZDpUlqUccVF4oISSu5WYiMRDwuTj9VgAAASN6AD0AqlIB8fq8HuuSpiWR9hkdH7sB3z7QSAFqBIkYSq9UqFfV-qvPoGsgAhrCRYaiNbGOjUoTeUsahyRBUeqJrzwA9P-nUUjVZ8SBwHNFex2JHlraCdM11UUu27irJVoLT6GUuxqwAK8wqLNSd2OHsxNgpSf7IOuBWAJw1e7CoYu15RvQqa1KJwtyuklUEuYJoKicOt+CRjSkjvUlddQ7UmN617YQBI8peWtrngB69GJ2qWH2siQ-yZZu-AWZ-LxyLI9NY+R1UnqwVEK+9f8HWBPqO2p61HptCZQs5QO7YXxZ6N-WPqFmEGrFayTfXOTXyn6xmJCIID+Meqz4AtlGvIEFqy+jvWOjQPjUnNoKBGvIQ-xgFcqil7VQ9bBsZi+8aZBAeMZhteDYaa1WYmNYBUo2EaKNe67lTRuvUmMT1iE4lUblJUCaJVR629dNihUvrvVnebvOHl7yj4J1ckDeqUhHyZq013nE9eGsR6kqMpQG19VB0MKfrINxPP+ZDRnpsC3oLNRyP9BmpojiBWY0le6qHIb1KORozoZRtJW81aNILDEOirg0MbA+vfN+Rp183c1-N9a0FoQVgF2bKQ-0ToReqeB+bBNz6z1YFqlU-UcViPYGG3UNgkKlOnCIScjJi3wbAysAMXFpqg6809NxPAzYB167GbQOpmlVazIhHhdd5Vm2GZr38YFakokAo0buqo3PADV1wfzcJv8GhblAjG6me9AG1PF1i+CrqSVpG2krxt0WrLbFpS2bb0tHygettoq1YFYAC6DWAmC7SqaOt9WrGaswfX-rYNRmn6ohu9mS0zNaGrhuNr8ky1AQo0cEHQpkJHjieb0HcKPCShqS-tAO-0KePG3ea+NRSm1XmEm3BQQt4WwWWjvjGg7xakAMUMFHW3crEd8yaLbuiwBiaCdT3Ynfv3tZHbe1N+SaA7iu26bFgHwu7X+oA3Cb-loHQnc93a0QaPt+0p7t9oh2egodQO9De9ECS2qaywu-7cUVPHc64do2slfOG0TghjIBCQkh2xR2BDYFY3DHRLqe5JQp1PmgVdfVV3q7jS7HZfAMmpVm7PQFuzXcKup2ybjt2wXLR8LeiRSC2NO8cvCs2ggjPdYWltWTqKWMqOV3Y+1r2stx45mwoWGrdyLFalIGd2mzKLTWnXgqsAMbBxr8rFbFhSVAHWGeuuUa00mB6WAEXAp4Uh62V1rJlRHs07jlYAZGYcKOCu3qaic8eodfRNKTVartdWiNXLEa3kzmtz2kzVwTgzFFv1hASzT9V4oZ7OAf0dlUKtz1K6C9ycovZuvomeaspxYJ1YSsIBnCm1BAA-RWob1N7pwrexPe3qu1p7+9mexfQqs8756r2tFdfSXpsrLbSFyCkrY6vL3nqq9PKherXspX17vV62KcKOHHUp61Nl+nvVAbS7p6B93Ks2sPpUIva5IbW2OpPoIDT6VCs+rVn9Dc4P7l9RonfQCL31H7iJ5B5tWlCTW9qwDm2OrBfp-IGwEDmewg04085FbVt0sUvU6or19LWUDKgtU7pAPYrcVx+u9fdoA1ybUDrI9ke1ulFgrIAcseUXCvEOUHD9WAVQ77v7G4q-oSCkSMWEUOHsVDBzOFZwj0NcHROJWowxdE+DqtTDgo33ROksMf6lOE6SjsYfKH4BtDcPJATLtF28d9DoigRaJwCNy65RBzI6o7zRqxFbDvI8g-6wLWqGyNBBeI-YcSN1ruNeQ0g383-1bzyZIh1nTBuy2oHU1R8qmFSCu29hw1Oq4w3Psz0FHk5vKCCXBPSNobl1oe4QzoqV1Q6lORR3tSqCu0TEqYQxuAyCuuDp7X2fRrqZJ2-4wqHDkehvZtGGOVEqYKx8YzCqmMmqxpyCuY6zIWMmGqV3Kpo4WSt1hCbd3KusMSijbSbytah3jhId6O7H-C5WzlaSreXzhso7SJ9RcFuPW7BD3KmaYUYeGkrrjokW45zu84syoOfOmdfgFbkz6p6DRvAMUYe1a7Xdr7I+f7N52ob4TnAYOayVspRleOmvMEzcYSn0aoTwKzsp+sVhYGTACMyerDPxNFA-owJ5oyzK2mszXp3-ek1QeMBPGTAQp+fbLN8UCnM9Yp3JjsxFOib-9TskE1TqV0cmzjMhhDetInm87xTKJtE+zpR3Um-ZKGhk3gEJOBl85JJpBlq0H3NH2wVJlrTptpO87+TKJwgIidwPInFBTJnrcnNZP5A-oCpzk5qe5OUy+TixuoCKeUARmKDNBzQ3yylMCn-TmpuE7GblOXGilkJ5KTJvRVqnAyOKx4xoZFVFKAzqpo7e8e5WfGAQ3xh7X8Y649GPjdkL4zyGrMZnRDrJWAHqCu2U9DeAIKmHaEZ1bHc1hQpA4By7PHxEZ8x0FTIv-2rrPQ6+ilcoaWPeqrt8ommBfpJjbH91tk7PvWvlGl7ORm5vsv9TnPba7DhPI462cq3LmDmQalRVdvt6lI1znwbYzOdQL1rSVpxmGryl3OeK+RRhrLUIeI3F8fzperwyfqXNwGVzp2W83AfvODmXzAWqFe+f3nvjyZ35iEZ4f-PcrgOQF9CzgNoMN6rz8OB83AelEbmV1W5kgTuYOYgW7D0pzPQebPLbnSzeRtM5eootZiBjBFuA1tRvMd6vGOa3jvBaQu40o2WWrai8x-N56sLwhrLUSOAuErhLc5RU-acygaxcjpJ-ANKnHIfn4SR8nVXBMEvsX-yal4tf+RfmHstLP1DyoZcPMkCTLGsQgofuTWskR6vA2yxxu6NxrHJj1WLQSsP2pnATRS1fYWTnN3He1hFi4KuZIt2GyLQV5-R1yy27nKO+57lcFZhrHnZNp5vC94YvMnaIrpSV6KsaCLEWPOMVwc2lfhJ1rEr1FyjtaZLNQrJO+qH85RtoqeHaL+F8C3IaIt+oirHobYxVZ9JVWoVSVqS+mYSsNWKFTViES1csrZWwLKa-K3xegPrnBzT+29jnXGvoqRr43BI4fqHNxX1rNFTaw2pYuBXngA1vgpxc6ugdpKBVpa9J2Ku5qLrdFYDuB1GvPAzaknZq471auEq5TQS6DeiaKM+7rr3nSC4+e2Pyj2jSakGwtYgvXntQENwc1DZ2sZGnLsNts-lY7PRWVrQRlG6Bed3ZnMT5R0Diub7M42nzyN+QwTazOlHMb8Noi5UiRt43qb7V9Gy7vCsM3IrdoKm-Dh312gyD7Nom5za6uRXKkvNh8X7rouvtytuRkleAmUZXWVCFAuSM2EIJINR8TlveSJcA56X2tBlpBqmolSVG-VNRyyFrZEVBVxFoHXeOrbgB+rd406i2z9RmM6cbbdtm5ASih0BFHL81ok24e4OSLeOm0Cwx1ZUJWHmpHh4Oy4bDuskDDA49LLocJV9R8Iv4Z5AOWuBigdEnxItDlHKjPI7c7SfcLKCxCrA-bgZFU5+fWk6qIJsJu26mrASKhlGpwCjX6uNtabG7itdsA-3Nvl3YyI8s2tXfa213wN9d02wrZtmt23o7d8e03Y7Y93Y7gZeCwDy8slrUJhI4kSiZUXMVALnvQ236sHA3b5tDdie9ot4sH2nbfdu6jhtrUFqAeRG7y+vaL6b25Y29n6o0P3tvRDgTt+21-fPtvRD7vdxc8rdRHoj3NN9rMSvfa2DhKOtW1Xr6eBpy7eU3whTe2B7wthcsndxWxA--Iv5TsvBkfJg4nuld42LGTaL7eAdbd0GR3QgFB0kmO9khVQnB-9wT3MH2YdR-oYwN1bqrN+DDt4UYMg6gc7uOzI+XqpEegdxtcsdW3gMmpIP8DmvNVJ2iDzmT6JiQutkv146IOcC1zLB2oK5DEY396WHfY5bL3eTNoW2ZR8YCwf6ifunnbIS5O8laOAwOjhW3o40GWDHIjq9LFDKwAF1iFAd6wzwcUFIN3d+QJKFVu0wd3iHqvWo5ry+3jlcdwdljOE7O0XatN422JyYG53Y1AESTw9rADp3J7udR9ggAI-wBkP05i9rAs9d5Qq2wOjFWBzbYoW+msATjwHcg-bGbRnoRDue5J1IfeOqnqyyxhZ1KtsPq87W-2pRqX7SP8AsAaPcFlj1hY-V6m825AD6hzPWwuOBZ3HuWeX7k9bT6HSYtcfRDyOzdR1cY8vvm4xQ8K+W03bcfEZ5zzlwMho-8aIC4W3iie8sGwGlJqFrKa4yaCh33OPluV2ogGEBr7CpzAyT2xwxwIgtK1jROvVDtHMOYYuxYFUJqkxMFJDbYx5YKMdWewANjuLzaLAFeW5PXo1jyu5VYLWbQ9gtL8hzvrBK5ZXbzUlUHsGGzBhdY47apDbq5TWPizn5z6JS8GvUvaXewel+lkZev4Xj5RVl+y7ji48IOIG6dJY4jtf6eDJfAExaF7AEYhEE90lbykUffYq8BomqFSEOQD41wHtP7HVg3hQwoLYmBZ13jQdKa+8lrg8A91HyggqQbr6fK9AYjsZqthyU0ghDsjIRfiZ2fYHK-iyWI48CYc7V2l9SFPIsisch7uht0+ZdXc9-Vxbk2dW5FnYmR2vbkixBvq4RIO4Oi5ebS0iMhaosIMis5oogGTRORXQ31dApK3jxXKsC-zHdMgXoLbl6yk4kNu0Q2UV4HsCjEpNjJBYYdz8C4lsbvgha3CDO9VZjO3eu6cYT9qeB-btgr0BDsAk+jgxPozMT6MrE+jGJPoEcT6PnE+gdxPoi8T6LfH3d7usAZOfd0e+fcnvn3Z759xe+fdXvn3N7593e+fcPusAjMJ95AEZgHvQPb7iDx+4g9fuIPP7iD3+4g8AeIPQHiDyB8gCKxwPisKD9h5g+Kw4PisBD4rCQ+KwUPisND4rAw+KwsPAUcDwFHw8BQYPAUODwFAQ8BQkPAUFDwFDQ8BQMPAULD2HHA9hx8PYcGD2HDg9hwEPYcJD2HBQ9hw0PYcDD2HCw+5xwPucfD7nBg+5w4PucBD7nCQ+5wUPucND7nAw+5wsPbccD23Hw9twYPbcOD23AQ9twkPbcFD23DQ9twMPbcLD-PHA-zx8P88GD-PDg-zwEP88JD-PBQ-zw0P88DD-PCw-XxwP18fD9fBg-Xw4P18BD9fCQ-XwUP18ND9fAw-XwsPTCcD0wnw9MIYPTCOD0wgQ9MIkPTCFD0wjQ9MIMPTCcr0wmDgxZqvwUWr8FHq-BRGvwUZr8FFa-BR2vwUTr8FHK9gfevkHxb7V8Zj1fGYjXxmM18ZitfGY7XxmJ18ZjlecPvXvDyd9q9EeTvjXsjyd9a9UeTvnXuj714Y9PfqvLHp7-V449PfmvPHp7+14E9PfyvIn3r2J+B+1epPwPxr3J+B+telPwPzr2p968afEf1XnT4j-q8GfEfzXkz4j-a8WfEf5Xmz717s9E-avTnon417c9E-WvXnon5178+9eAvDP6ryF4Z-1eIvDP5rzF4Z-teEvDP8ryl969pfBftXrL4L8a95fBfrXor4L869lfg4YH8D2B-w9geYPYHuD2B4Q9gekPYHlD2B7Q9geMPYHrD5B8V8vvQPA3+X0N8t+a+xvlv3X1N8t+G+5v8vhb4e6W9u-Vfq3l35r828u-dfu3l34b8O-y-jvh7072H9V8Xew-mv672H9193ew-hvx74e+e8p-lfb3lP+r8+8p-tfP3lP-r-+8p-jfQPw9yD9L+q-wfpfzX1D9L+5xfm0tB+KIRl3buN3W71HrEYMqQBORQCjAkocb-YhfnCVCBMC6edYFBW-f5v7xwn+G8t32TU4cHoGThHtHRBN5wWEn-ZG6iEEVNYunDUItm-SLdd1DtWaBV2CcLy+Wv5n8i65dTRJf847n-S1mII--txaEgHznH-K7NxUmNtGDC-o6EZFj0bNkLZHynxkm5WFWAMvTb1TXROzEuHnAqYNdFzVAA1YG9sWZUc3nAKHHkyXlIA6AO7NJjBAJuAgAnAguZaRFAJgCAQdAIgC4bKDlQCezeAN45EAqHTWonaceRVlIAagNhVD9UWQuNG0G4EuETQEN1fMmiEF1-soOZiCpgrtBGCmMcWJKBxVpaegOKJGA5RmuMMkBmG5ApLGCHsh4XGQgeElAisAkxsaPRXUD4QVeQvhD9HQJUCpsLiwMD6IP4DEC4DUQNzUCkDgMHlHiOQMICMkCniRRzAk1ysDPcTQP9BtAzwL0DpVG4GsD55EwM+gzAoINBtMoUQNNsQgz3CmNHAz6E4CjcVwIDAPA5QIkw1A0IPSECCJU0iDuQB4yQYJ0QlTSCZCBQIyDdA1QK5VDAzFwvhjAymEP0yg-0CIDKgrwOyDPcOoMpgGgl4CaD8ApAOKJxxHgNZh+AtoIkxareIPsgugl4B6DCVFIIGR7jHtQb17rCYymNj1GFRMdlgTVwQJY2B5i0IsAQBGkDLHYKGkDzceX1OC8ASx0VgLg8p2DgAoG4IIBLHMOAeDCASx1zgXgzgEsc24D4OUAg8ddyLZFaLsDmR9wMSFJxpNYEKhAhdXYMzBEeVNQBDMwRMxBDOhZZw38AwEp3Cc-VeENPE24ZELegu0VIzRCSXZpjJcfJcOk2g+4T4HKhKUJCAuBcsRWFCEBkA51RcjgJ4EWlVUYxzr1R-bYDQNIAFG278E9XkUR5pRNRwQF+dJBnlEwROwxFDYQWOneFOAcULMNfdf3R4F2BJB2lFX8VkNckkoJkMzYSYDUNQUlQ4J1-sUjeOhKdZtY0LMMCQmQlsDlqRaStD-QWwJ1D1QlkPqkzQwWVTUTQ2OjdCBOFSzkh5RfnS4YPQy0NNDmdTgCODpacPBJCdQ74TFcd9O4FyxNoVECTCEwszABE03VlHDwuUaMNIc-ich2f8XgcPB1dowmlzpccwvMKNxw8DN2LDRXBMITC7geMNyMd9XMNm4BkJFxLgUXWp00gj4PwKMN7dPwOk1o+KJj7DZgw-UHCuLc3VsCRAxIPCDQeLsP9JKA0DmYgp1ZZ3t1JwxcLx0HAmcJlo6eEW0XDbAicM3DGg5IOcCwhcACAA) diff --git a/package.json b/package.json new file mode 100644 index 0000000..76e1554 --- /dev/null +++ b/package.json @@ -0,0 +1,17 @@ +{ + "name": "@codemix/ts-sql", + "version": "1.0.0", + "description": "SQL database engine implemented purely in TypeScript type definitions.", + "main": "lib/index.js", + "author": "Charles Pick ", + "license": "MIT", + "dependencies": {}, + "devDependencies": { + "prettier": "^2.1.2", + "typescript": "beta" + }, + "scripts": { + "build": "tsc -b", + "playground": "node scripts/bundle.js > playground.ts" + } +} diff --git a/scripts/Header.md b/scripts/Header.md new file mode 100644 index 0000000..67cbb9b --- /dev/null +++ b/scripts/Header.md @@ -0,0 +1,27 @@ +``` + ▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄ ▄ +▐░░░░░░░░░░░▌▐░░░░░░░░░░░▌ ▐░░░░░░░░░░░▌▐░░░░░░░░░░░▌▐░▌ + ▀▀▀▀█░█▀▀▀▀ ▐░█▀▀▀▀▀▀▀▀▀ ▐░█▀▀▀▀▀▀▀▀▀ ▐░█▀▀▀▀▀▀▀█░▌▐░▌ + ▐░▌ ▐░▌ ▐░▌ ▐░▌ ▐░▌▐░▌ + ▐░▌ ▐░█▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄ ▐░█▄▄▄▄▄▄▄▄▄ ▐░▌ ▐░▌▐░▌ + ▐░▌ ▐░░░░░░░░░░░▌▐░░░░░░░░░░░▌▐░░░░░░░░░░░▌▐░▌ ▐░▌▐░▌ + ▐░▌ ▀▀▀▀▀▀▀▀▀█░▌ ▀▀▀▀▀▀▀▀▀▀▀ ▀▀▀▀▀▀▀▀▀█░▌▐░█▄▄▄▄▄▄▄█░▌▐░▌ + ▐░▌ ▐░▌ ▐░▌▐░░░░░░░░░░░▌▐░▌ + ▐░▌ ▄▄▄▄▄▄▄▄▄█░▌ ▄▄▄▄▄▄▄▄▄█░▌ ▀▀▀▀▀▀█░█▀▀ ▐░█▄▄▄▄▄▄▄▄▄ + ▐░▌ ▐░░░░░░░░░░░▌ ▐░░░░░░░░░░░▌ ▐░▌ ▐░░░░░░░░░░░▌ + ▀ ▀▀▀▀▀▀▀▀▀▀▀ ▀▀▀▀▀▀▀▀▀▀▀ ▀ ▀▀▀▀▀▀▀▀▀▀▀ +``` + +This is a SQL database implemented purely in TypeScript type annotations. +This means that it operates solely on types - you define a "database", +(just a type annotation) and then query it using some more type annotations. +It supports a subset of SQL, including SELECT, INSERT, UPDATE and DELETE statements. + +This project lives at https://github.com/codemix/ts-sql + +This was written by Charles Pick ( charles@codemix.com, https://twitter.com/c_pick ). + +You can install ts-sql in your own project with `npm install @codemix/ts-sql` or +`yarn install @codemix/ts-sql` (TypeScript 4.1 is required). + +(Tip: hover over the type aliases below to see the results of your queries) diff --git a/scripts/bundle.js b/scripts/bundle.js new file mode 100644 index 0000000..50d9f2d --- /dev/null +++ b/scripts/bundle.js @@ -0,0 +1,65 @@ +// @ts-check + +const fs = require("fs"); +const path = require("path"); + +const header = fs + .readFileSync(path.resolve(__dirname, "Header.md"), "utf-8") + .split(/\n/) + .map((line) => ` * ${line}`) + .join("\n"); + +const files = Array.from(findFiles(path.resolve(__dirname, "..", "src"))); +const sources = files + .filter((file) => !file.endsWith(".test.ts")) + .map(loadFile); +const tests = files.filter((file) => file.endsWith(".test.ts")).map(loadFile); + +const outputs = [ + `/**\n${header}\n */\n`, + ...tests, + ` + /** + * ======================================================================================== + * + * + * END OF EXAMPLES, START OF IMPLEMENTATION + * + * + * ======================================================================================== + */ + ` + .split(/\n/) + .map((line) => line.trim()) + .join("\n"), + ...sources, +]; + +console.log(outputs.join("\n\n")); + +/** + * @param {string} dir The path to search in. + */ +function* findFiles(dir) { + for (const name of fs.readdirSync(dir)) { + const filename = path.join(dir, name); + if (name.endsWith(".ts")) { + yield filename; + } else if (fs.statSync(filename).isDirectory()) { + yield* findFiles(filename); + } + } +} + +/** + * @param {string} filename + */ +function loadFile(filename) { + const raw = fs.readFileSync(filename, "utf-8"); + const content = raw + .replace(/export\s+type/g, "type") + .replace(/export\s+\*\s+from\s+"(.*)";?/g, "") + .replace(/import\s+\{([\s\S]*)\}\s+from\s+"(.*)";?/g, "") + .trim(); + return content; +} diff --git a/src/AST.ts b/src/AST.ts new file mode 100644 index 0000000..52eacd4 --- /dev/null +++ b/src/AST.ts @@ -0,0 +1,152 @@ +export type Identifier = { + type: "Identifier"; + name: Name; +}; + +export type MemberExpression< + Object extends string = string, + Property extends string = string +> = { + type: "MemberExpression"; + object: Object; + property: Property; +}; + +export type NumericLiteral = { + type: "NumericLiteral"; + value: Value; +}; +export type StringLiteral = { + type: "StringLiteral"; + value: Value; +}; +export type BooleanLiteral = { + type: "BooleanLiteral"; + value: Value; +}; + +export type NullLiteral = { type: "NullLiteral"; value: null }; + +export type BinaryOperator = "=" | "!=" | "LIKE"; +export type BinaryExpression< + Left extends Expression = Expression, + Operator extends BinaryOperator = BinaryOperator, + Right extends Expression = Expression +> = { + type: "BinaryExpression"; + left: Left; + operator: Operator; + right: Right; +}; + +export type LogicalOperator = "AND" | "OR" | "&&" | "||"; +export type LogicalExpression< + Left extends Expression = Expression, + Operator extends LogicalOperator = LogicalOperator, + Right extends Expression = Expression +> = { + type: "LogicalExpression"; + left: Left; + operator: Operator; + right: Right; +}; + +export type FieldSpecifier< + Source extends Identifier | MemberExpression = Identifier, + Alias extends Identifier = Identifier +> = { + type: "FieldSpecifier"; + source: Source; + alias: Alias; +}; + +export type TableSpecifier< + Source extends Identifier = Identifier, + Alias extends Identifier = Source +> = { + type: "TableSpecifier"; + source: Source; + alias: Alias; +}; + +export type AssignmentExpression< + Key extends Identifier = Identifier, + Value extends Expression = Expression +> = { + type: "AssignmentExpression"; + key: Key; + value: Value; +}; + +export type Expression = + | LogicalExpression + | BinaryExpression + | MemberExpression + | Identifier + | StringLiteral + | NumericLiteral + | BooleanLiteral + | NullLiteral; + +export type InnerJoinSpecifier< + From extends TableSpecifier = TableSpecifier, + Where extends Expression = Expression +> = { + type: "InnerJoinSpecifier"; + from: From; + where: Where; +}; + +export type JoinSpecifier = InnerJoinSpecifier; + +export type SelectStatement< + Fields extends FieldSpecifier[] = FieldSpecifier[], + From extends TableSpecifier = TableSpecifier, + Joins extends JoinSpecifier[] = JoinSpecifier[], + Where extends Expression = Expression, + Offset extends number = number, + Limit extends number = number +> = { + type: "SelectStatement"; + fields: Fields; + from: From; + joins: Joins | []; + where: Where; + offset: Offset; + limit: Limit; +}; + +export type InsertStatement< + TableName extends string = any, + Values extends readonly AssignmentExpression[] = any +> = { + type: "InsertStatement"; + tableName: TableName; + values: Values; +}; + +export type UpdateStatement< + TableName extends string = string, + Values extends AssignmentExpression[] = AssignmentExpression[], + Where extends Expression = Expression +> = { + type: "UpdateStatement"; + tableName: TableName; + values: Values; + where: Where; +}; + +export type DeleteStatement< + TableName extends string = string, + Where extends Expression = Expression +> = { + type: "DeleteStatement"; + tableName: TableName; + where: Where; +}; + +export type Statement = + | SelectStatement + | InsertStatement + | UpdateStatement + | DeleteStatement; diff --git a/src/All.test.ts b/src/All.test.ts new file mode 100644 index 0000000..e115901 --- /dev/null +++ b/src/All.test.ts @@ -0,0 +1,106 @@ +import { Query } from "./Query"; + +// # Usage + +// First we define the data that we're going to be querying against. +const simpsons = { + people: [ + { id: 1, firstName: "Bart", lastName: "Simpson", isChild: true }, + { id: 2, firstName: "Lisa", lastName: "Simpson", isChild: true }, + { id: 3, firstName: "Maggie", lastName: "Simpson", isChild: true }, + { id: 4, firstName: "Marge", lastName: "Simpson", isChild: false }, + { id: 5, firstName: "Homer", lastName: "Simpson", isChild: false }, + { id: 6, firstName: "Montgomery", lastName: "Burns", isChild: false }, + { id: 7, firstName: "Whelan", lastName: "Smithers", isChild: false }, + { id: 8, firstName: "Nillhouse", lastName: "Van Houten", isChild: true }, + { id: 9, firstName: "Nelson", lastName: "Muntz", isChild: true }, + { id: 10, firstName: "Principal", lastName: "Skinner", isChild: false }, + ], + places: [ + { name: "Springfield Elementary", ownerId: 10, isActive: true }, + { name: "Nuclear Power Plant", ownerId: 6, isActive: true }, + { name: "Evergreen Terrace", ownerId: 4, isActive: true }, + { name: "Monorail", ownerId: 8, isActive: false }, + { name: "Treehouse of Horror", ownerId: 1, isActive: true }, + { name: "Playground", ownerId: 9, isActive: true }, + ], +} as const; + +// This is the initial version of our database. +export type DBv1 = typeof simpsons; + +// Let's find the names of people with the last name "Simpson". +// We use the `Query` type to execute queries, it takes a SQL +// query as first type parameter, and a database as the second. +// Note that all SQL keywords are UPPERCASE. +export type EX1 = Query< + "SELECT firstName FROM people WHERE lastName = 'Simpson'", + DBv1 +>; + +// Let's find the first and last names of all the children in the simpsons +export type EX2 = Query< + "SELECT firstName, lastName FROM people WHERE isChild = true", + DBv1 +>; + +// Let's find the adults whose names begin with M +export type EX3 = Query< + "SELECT * FROM people WHERE isChild = false AND firstName LIKE 'M%'", + DBv1 +>; + +// Let's correct the typo in Milhouse's name. Since all types are immutable in TypeScript our +// database is immutable too, so this query returns a new database. +export type DBv2 = Query< + "UPDATE people SET firstName = 'Milhouse' WHERE id = 8", + DBv1 +>; + +// Let's check that our update worked + +export type EX4 = Query<"SELECT * FROM people WHERE id = 8", DBv2>; + +// Let's add a new character, again this returns a new database. +export type DBv3 = Query< + "INSERT INTO people SET id = 11, firstName = 'Troy', lastName = 'McClure', isChild = false", + DBv2 +>; + +export type EX5 = Query<"SELECT * FROM people WHERE firstName = 'Troy'", DBv3>; + +// Unfortunately, we need to delete Troy McClure +export type DBv4 = Query< + "DELETE FROM people WHERE firstName = 'Troy' AND lastName = 'McClure'", + DBv3 +>; + +export type EX6 = Query< + "SELECT firstName FROM people WHERE isChild = false", + DBv4 +>; + +// We can add Moe tho + +export type DBv5 = Query< + "INSERT INTO people SET id = 11, firstName = 'Moe', lastName = 'Szyslak', isChild = false", + DBv4 +>; + +export type EX7 = Query< + "SELECT id, firstName FROM people WHERE isChild = false AND firstName LIKE 'M%'", + DBv5 +>; + +// Let's add Moe's Tavern +export type DBv6 = Query< + 'INSERT INTO places SET name = "Moe\'s Tavern", ownerId = 11, isActive = true', + DBv5 +>; + +// Joins! Let's get all the places along with the names of their owners. + +export type EX9 = Query< + "SELECT name, person.firstName, person.lastName FROM places INNER JOIN people AS person ON places.ownerId = person.id", + DBv1 +>; diff --git a/src/Evaluator.ts b/src/Evaluator.ts new file mode 100644 index 0000000..089ea58 --- /dev/null +++ b/src/Evaluator.ts @@ -0,0 +1,436 @@ +import { + AssignmentExpression, + BinaryExpression, + BooleanLiteral, + DeleteStatement, + FieldSpecifier, + Identifier, + InnerJoinSpecifier, + InsertStatement, + JoinSpecifier, + LogicalExpression, + MemberExpression, + NullLiteral, + NumericLiteral, + SelectStatement, + Statement, + StringLiteral, + TableSpecifier, + UpdateStatement, +} from "./AST"; +import { Database } from "./Schema"; +import { MatchStringLike, Merge } from "./Utils"; + +type EvaluateStatement< + DB extends Database, + Node extends Statement +> = Node extends SelectStatement + ? EvaluateSelectStatement + : Node extends InsertStatement + ? EvaluateInsertStatement + : Node extends UpdateStatement + ? EvaluateUpdateStatement + : Node extends DeleteStatement + ? EvaluateDeleteStatement + : never; + +type EvaluateInsertStatement< + DB extends Database, + Node extends InsertStatement +> = Node extends InsertStatement + ? { + [K in keyof DB]: K extends TableName + ? InsertRow< + TableName extends keyof DB ? DB[TableName] : never, + Assignments + > + : DB[K]; + } + : never; + +type InsertRow< + Table extends readonly any[], + Assignments extends readonly AssignmentExpression[] +> = [ + Merge< + UnionToIntersection< + AllReadonly>> + > + >, + ...Table +]; + +type AllReadonly = { readonly [K in keyof T]: T[K] }; + +type EvaluateUpdateStatement< + DB extends Database, + Node extends UpdateStatement +> = Node extends UpdateStatement< + infer TableName, + infer Assignments, + infer Where +> + ? { + [K in keyof DB]: K extends TableName + ? UpdateRows< + TableName extends keyof DB ? DB[TableName] : never, + Assignments, + Where + > + : DB[K]; + } + : never; + +type UpdateRows< + Table, + Assignments extends readonly AssignmentExpression[], + Where +> = { + [Index in keyof Table]: EvaluateExpression extends true + ? UpdateRow + : Table[Index]; +}; + +type UpdateRow< + Row, + Assignments extends readonly AssignmentExpression[] +> = MergeValues>>; + +type MergeValues = Merge< + { [K in keyof T]: K extends keyof U ? U[K] : T[K] } +>; + +type ApplyAssignments = { + [K in keyof Assignments]: Assignments[K] extends AssignmentExpression< + Identifier, + infer Value + > + ? [ + Key, + NullLiteral extends Value ? null : Exclude, null> + ] + : never; +}; + +type ExtractValue = T extends NullLiteral + ? null + : T extends BooleanLiteral + ? Value + : T extends NumericLiteral + ? Value + : T extends StringLiteral + ? Value + : never; + +type EvaluateDeleteStatement< + DB extends Database, + Node extends DeleteStatement +> = Node extends DeleteStatement + ? { + [K in keyof DB]: K extends TableName + ? DeleteRows + : DB[K]; + } + : never; + +type DeleteRows = FilterUndefined< + { + [Index in keyof Table]: EvaluateExpression extends true + ? undefined + : Table[Index]; + } +>; + + +export type EvaluateSelectStatement< + DB extends Database, + Node extends SelectStatement +> = Node extends SelectStatement< + infer Fields, + infer From, + infer Joins, + infer Where, + infer Offset, + infer Limit +> +? From extends TableSpecifier< + Identifier, + Identifier + > + ? Source extends keyof DB + ? Fields extends [FieldSpecifier, Identifier<"*">>] + ? FilterWhere, Where> + : AssembleRows< + Fields, + ExtractFields< + FilterWhere, Where>, + Fields, + Merge< + UnionToIntersection< + AssembleEntries<[[Alias & string, DB[Source]]]> + > + > + > + > + : never + : never +: never; + +type CollectInputRows< + DB extends Database, + From, + Joins +> = From extends TableSpecifier< + Identifier, + Identifier +> + ? Source extends keyof DB + ? Joins extends JoinSpecifier[] + ? CollectJoins + : DB[Source] + : never + : never; + +type CollectJoins< + DB extends Database, + Table extends readonly any[], + Alias extends string, + Joins extends JoinSpecifier[] +> = + {[K in keyof Table]: CollectRowJoins}; + +type CollectRowJoins, Row, Alias extends string, Joins extends JoinSpecifier[]> = + Merge]: + Joins[K] extends InnerJoinSpecifier, Identifier>, infer Where> + ? JoinSource extends keyof DB + ? FirstElementOrNull< + ExtractFieldByName< + FilterWhere< + CollectJoinedArrayForRow< + DB[JoinSource], + ExtractJoinAlias, + Row, + Alias + >, + Where + >, + JoinAlias + > + > + : never + : never + }> + + + +type CollectJoinedArrayForRow = + {[P in keyof JoinTable]: + Merge< + TableRow + & {[JA in JoinAlias]: JoinTable[P]} + & {[TA in TableAlias]: TableRow} + > + }; + +type ExtractFieldByName = + {[K in keyof Table]: FieldName extends keyof Table[K] ? Table[K][FieldName] : never }; + +type FirstElementOrNull = + Table extends readonly any[] + ? Table['length'] extends 0 + ? null + : Table[0] + : null; + +type ExtractJoinAlias = Join extends InnerJoinSpecifier, Identifier>> ? Alias : never; + +type FilterUndefined = T extends Readonly<[infer Head, ...infer Tail]> + ? Head extends undefined + ? FilterUndefined + : [Head, ...FilterUndefined] + : []; + +type FilterWhere = FilterUndefined< + { + [Index in keyof Table]: EvaluateExpression extends true + ? Table[Index] + : undefined; + } +>; + +type ExtractFields = { + [Index in keyof Table]: { + [K in keyof Fields]: Fields[K] extends FieldSpecifier + ? ReadRowField + : never; + }; +}; + +type GetValueByKey = + Row extends null ? GetValueByKey, Key, Aliases> : + Key extends keyof Row + ? Row[Key] + : Key extends keyof Aliases + ? Aliases[Key] + : never; + +type ReadRowField = Field extends MemberExpression< + infer O, + infer P +> + ? ReadRowField, Identifier

, Aliases> + : Field extends Identifier + ? GetValueByKey + : never; + +type AssembleRows = Fields extends FieldSpecifier[] + ? { [Index in keyof Data]: AssembleRow } + : never; + +type AssembleRow[], Data> = Merge< + UnionToIntersection< + AssembleEntries< + { + [Index in keyof Fields]: [ + Fields[Index] extends FieldSpecifier> + ? Alias + : never, + Index extends keyof Data ? Data[Index] : never + ]; + } + > + > +>; + +type EvaluateExpression = + | EvaluateLogicalExpression + | EvaluateBinaryExpression + | EvaluateMemberExpression + | EvaluateIdentifier + | EvaluateNullLiteral + | EvaluateBooleanLiteral + | EvaluateNumericLiteral + | EvaluateStringLiteral; + +type EvaluateBinaryExpression = Exp extends BinaryExpression< + infer Left, + infer Op, + infer Right +> + ? Op extends "=" + ? EvaluateBinaryEquals< + EvaluateExpression, + EvaluateExpression + > + : Op extends "!=" + ? EvaluateBinaryNotEquals< + EvaluateExpression, + EvaluateExpression + > + : Op extends "LIKE" + ? EvaluateBinaryLike< + EvaluateExpression, + EvaluateExpression + > + : never + : never; + +type EvaluateBinaryEquals = Left extends Right + ? Right extends Left + ? true + : false + : false; + +type EvaluateBinaryNotEquals = EvaluateBinaryEquals< + Left, + Right +> extends true + ? false + : true; + +type EvaluateBinaryLike = MatchStringLike; + +type EvaluateLogicalExpression = Exp extends LogicalExpression< + infer Left, + infer Op, + infer Right +> + ? Op extends "AND" + ? EvaluateLogicalAND< + EvaluateExpression, + EvaluateExpression + > + : Op extends "OR" + ? EvaluateLogicalOR< + EvaluateExpression, + EvaluateExpression + > + : never + : never; + +type EvaluateLogicalAND = Left extends true + ? Right extends true + ? true + : false + : false; + +type EvaluateLogicalOR = Left extends true + ? true + : Right extends true + ? true + : false; + +type EvaluateMemberExpression = Exp extends MemberExpression< + infer O, + infer P +> + ? O extends keyof Row + ? EvaluateIdentifier> + : never + : never; + +type EvaluateIdentifier = Exp extends Identifier + ? Name extends keyof Row + ? Row[Name] + : never + : never; + +type EvaluateNullLiteral = Exp extends NullLiteral ? null : never; + +type EvaluateBooleanLiteral = Exp extends BooleanLiteral + ? Value + : never; + +type EvaluateStringLiteral = Exp extends StringLiteral + ? Value + : never; + +type EvaluateNumericLiteral = Exp extends NumericLiteral + ? Value + : never; + +export type Evaluate< + DB extends Database, + S extends Statement +> = EvaluateStatement; + +type PairToObject

= P extends any + ? { + [k in P[0]]: P[1]; + } + : never; +type ToUnaryFunctionUnion = U extends any ? (arg: U) => void : never; +type UnionToIntersection = ToUnaryFunctionUnion extends ( + arg: infer I +) => void + ? I + : never; + +type AssembleEntries< + Entries extends Iterable +> = Entries extends Iterable + ? P extends readonly [PropertyKey, any] + ? Merge>> + : never + : never; diff --git a/src/Parser.ts b/src/Parser.ts new file mode 100644 index 0000000..633869e --- /dev/null +++ b/src/Parser.ts @@ -0,0 +1,214 @@ +import { BinaryOperator, BinaryExpression, BooleanLiteral, Identifier, LogicalOperator, LogicalExpression, NullLiteral, NumericLiteral, SelectStatement, StringLiteral, Expression, FieldSpecifier, UpdateStatement, AssignmentExpression, InsertStatement, DeleteStatement, MemberExpression, TableSpecifier, InnerJoinSpecifier, JoinSpecifier } from "./AST"; +import { IntegerStrings, Merge, Trim } from "./Utils"; + +export type Parse = + ParseStatement extends [infer Statement, infer Rest] ? + Trim extends ';' ? Statement : + Trim extends '' ? Statement : never : + never; + +type ParseStatement = + ParseSelectStatement | ParseInsertStatement | ParseUpdateStatement | ParseDeleteStatement; + +type ParseSelectStatement = + ParseSelectClause extends Partial> + ? [SelectStatement, ''] + : never + + +type ParseTableSpecifier = + T extends `${infer Source} AS ${infer Alias}` ? TableSpecifier, Identifier> : + T extends `${infer Source} ${infer Alias}` ? TableSpecifier, Identifier> : + T extends string ? TableSpecifier>> : + never; + +type ParseSelectClause + = T extends `SELECT ${infer FieldNames} FROM ${infer R0}` ? + Merge<{fields: ParseFieldSpecifierList} & ParseFromClause>> + : never; + +type ParseFromClause = + Tokenize extends [infer Source, infer R0] ? + Tokenize extends ['AS', infer R1] + ? Tokenize extends [infer Alias, infer R2] + ? {from: TableSpecifier, Identifier>} & ParseJoinClause + : never + : {from: TableSpecifier>} & ParseJoinClause + : never; + +type ParseJoinClause = + Trim extends `INNER JOIN ${infer TableName} ON ${infer R0}` + ? ParseExpression extends [infer Exp, infer R1] + ? Exp extends Expression + ? {joins: [InnerJoinSpecifier, Exp>]} & ParseWhereClause> + : never + : never + : ParseWhereClause> & {joins: []} + +type ParseWhereClause = + Trim extends '' + ? { where: BooleanLiteral } + : Trim extends `WHERE ${infer Where}` + ? ParseExpression extends [infer Exp, infer R0] + ? Exp extends Expression + ? {where: Merge} & ParseLimitClause + : never + : never + : {where: BooleanLiteral} & ParseLimitClause> + +type ParseLimitClause = + Trim extends `LIMIT ${infer R0}` + ? Tokenize extends [infer Limit, infer R1] + ? Limit extends keyof IntegerStrings + ? {limit: IntegerStrings[Limit]} & ParseOffsetClause + : never + : never + : {limit: -1} & ParseOffsetClause; + +type ParseOffsetClause = + Trim extends `OFFSET ${infer R0}` + ? Tokenize extends [infer Offset, infer R1] + ? Offset extends keyof IntegerStrings + ? {offset: IntegerStrings[Offset]} & ParseStatementTerminator + : never + : never + : {offset: 0} & ParseStatementTerminator; + + +type ParseStatementTerminator = + Trim extends '' + ? {} + : Trim extends ';' + ? {} + : never; + +type ParseInsertStatement = + T extends `INSERT INTO ${infer TableName} SET ${infer Fields}` ? + [InsertStatement>, ''] + : never; + + +type ParseUpdateStatement = + T extends `UPDATE ${infer TableName} SET ${infer Fields} WHERE ${infer Where}` ? + ParseExpression extends [infer Exp, string] ? + Exp extends Expression ? + [UpdateStatement, Exp>, ''] : + never : + never : + T extends `UPDATE ${infer TableName} SET ${infer Fields}` ? + [UpdateStatement, BooleanLiteral>, ''] + : never; + +type ParseDeleteStatement = + T extends `DELETE FROM ${infer TableName} WHERE ${infer Where}` ? + ParseExpression extends [infer Exp, string] ? + Exp extends Expression ? + [DeleteStatement, ''] : + never : + never : + T extends `DELETE FROM ${infer TableName}` ? + [DeleteStatement>, ''] + : never; + +type ParseIdentifier = + T extends '' ? never : + Tokenize extends [infer Head, infer Tail] ? + Head extends '' ? never : + Head extends 'null' ? [NullLiteral, Tail] : + Head extends 'true' ? [BooleanLiteral, Tail] : + Head extends 'false' ? [BooleanLiteral, Tail] : + Head extends keyof IntegerStrings ? [NumericLiteral, Tail] : + [Identifier, Tail] : + [Identifier, '']; + +type ParseMemberExpression = + Tokenize extends [`${infer O}.${infer P}`, infer Tail] ? + [MemberExpression, Tail] + : ParseIdentifier; + +type ParseStringLiteral = + T extends `"${infer Value}"${infer Rest}` ? [StringLiteral, Rest] : + T extends `'${infer Value}'${infer Rest}` ? [StringLiteral, Rest] : + ParseMemberExpression; + + +type ParseCallExpression = + Trim extends '' ? never : + ParseStringLiteral> | ParseParenthesizedExpression; + + +type ParseBinaryExpression = + ParseCallExpression extends [infer Left, infer R1] ? + Left extends Expression ? + Tokenize extends [infer Op, infer R2] ? + Op extends BinaryOperator ? + ParseCallExpression extends [infer Right, infer R3] ? + Right extends Expression ? + [BinaryExpression, R3] : + never : + never : + [Left, R1] : + [Left, R1] : + never : + never; + + +type ParseLogicalExpression = + ParseBinaryExpression extends [infer Left, infer R1] ? + Tokenize extends [infer Op, infer R2] ? + Op extends LogicalOperator ? + ParseExpression extends [infer Right, infer R3] ? + Left extends Expression ? + Right extends Expression ? + [LogicalExpression, R3] : + never : + never : + never : + [Left, R1] : + [Left, R1] : + never; + + +type ParseExpression = + Trim extends '' ? never : + ParseLogicalExpression> | ParseParenthesizedExpression; + +type ParseParenthesizedExpression = T extends `(${infer Content})${infer Rest}` ? [ParseExpression, Rest] : never; + +type ParseFieldSpecifierList = + T extends `${infer Head},${infer Tail}` ? [ParseFieldSpecifier>, ...ParseFieldSpecifierList>] : + T extends `${infer Head} AS ${infer Alias} ${infer Tail}` ? [FieldSpecifier[0]>, Trim[0]>>, Tail] : + T extends `${infer Head} AS ${infer Alias}` ? [FieldSpecifier[0]>, Trim[0]>>] : + T extends `${infer Head} ${infer Tail}` ? [ParseFieldSpecifier>, Tail] : + [ParseFieldSpecifier>]; + +type ParseFieldSpecifier = + T extends `${infer Field} AS ${infer Alias}` ? FieldSpecifier>[0], ParseIdentifier>[0]> : + ParseMemberExpression extends [infer M, ''] ? + M extends MemberExpression ? FieldSpecifier> : M extends Identifier ? FieldSpecifier : + T extends string ? FieldSpecifier, Identifier> : never : + never; + + +type ParseAssignmentExpressionList = + T extends `${infer Head},${infer Tail}` ? [ParseAssignmentExpression>, ...ParseAssignmentExpressionList>] : + T extends `${infer Head} = ${infer Value} ${infer Tail}` ? [AssignmentExpression>, ParseExpression>[0] & Expression>, Tail] : + T extends `${infer Head} = ${infer Value}` ? [AssignmentExpression>, ParseExpression>[0] & Expression>] : + T extends `${infer Head} ${infer Tail}` ? [ParseAssignmentExpression>, Tail] : + [ParseAssignmentExpression>]; + +type ParseAssignmentExpression = + T extends `${infer Key} = ${infer Value}` ? AssignmentExpression, ParseExpression[0] & Expression> : + never; + + + +type Tokenize = + Trim extends `${infer Head} ${infer Tail}` ? [Head, Tail] : + Trim extends `${infer Head},${infer Tail}` ? [Head, Tail] : + Trim extends `${infer Head}(${infer Tail}` ? [Head, Tail] : + Trim extends `${infer Head})${infer Tail}` ? [Head, Tail] : + Trim extends `${infer Head};${infer Tail}` ? [Head, Tail] : + Trim extends `${infer Head})` ? [Head, ')'] : + Trim extends `${infer Head};` ? [Head, ';'] : + [Trim, ''] \ No newline at end of file diff --git a/src/Printer.ts b/src/Printer.ts new file mode 100644 index 0000000..5e0880b --- /dev/null +++ b/src/Printer.ts @@ -0,0 +1,69 @@ +import { AssignmentExpression, BinaryExpression, BooleanLiteral, FieldSpecifier, Identifier, InnerJoinSpecifier, InsertStatement, JoinSpecifier, LogicalExpression, MemberExpression, NullLiteral, NumericLiteral, SelectStatement, StringLiteral, TableSpecifier } from "./AST"; +import { Parse } from "./Parser"; +import { JoinStrings, StringContains } from "./Utils"; + +export type Print = + T extends Identifier ? N : + T extends MemberExpression ? `${O}.${P}` : + T extends NumericLiteral ? `${V}` : + T extends StringLiteral ? QuoteString : + T extends BooleanLiteral ? 'true' : + T extends BooleanLiteral ? 'false' : + T extends NullLiteral ? 'null' : + // @ts-expect-error due to excessive depth + T extends BinaryExpression ? `${Print} ${O} ${Print}` : + T extends LogicalExpression ? `${Print} ${O} ${Print}` : + T extends FieldSpecifier> ? + S extends Identifier + ? `${A}` + : `${Print} AS ${A}` : + T extends TableSpecifier, Identifier> ? + S extends A ? `${S}` : `${S} AS ${A}` : + T extends InnerJoinSpecifier, infer Where> ? + JoinStrings<['INNER JOIN', Print>, 'ON', Print], ' '> : + T extends SelectStatement< + infer Fields & any[], + TableSpecifier, + infer Joins & any[], + infer Where, + infer Offset, + infer Limit + > ? + JoinStrings< + [ + 'SELECT', + Fields extends readonly any[] ? JoinStrings, ', '> : '', + 'FROM', + Print>, + JoinStrings, ' '>, + Where extends BooleanLiteral + ? '' + : `WHERE ${Print}`, + Limit extends -1 ? '' : `LIMIT ${Limit}`, + Offset extends 0 ? '' : `OFFSET ${Offset}` + ], + ' ' + > : + T extends AssignmentExpression, infer Value> ? + JoinStrings<[Key, '=', Print], ' '> : + T extends InsertStatement ? + `INSERT INTO ${TableName}` : // `INSERT INTO ${TableName} SET ${JoinStrings, ', '>}` : + '-'; + +type PrintArray = T extends any[] ? {[K in keyof T]: Print} : never; + +type CastStringArray = T extends string[] ? T : never; + +type QuoteString = + StringContains extends true ? `"${T}"` : `'${T}'`; + +type P0 = Print, 'LIKE', StringLiteral<"awesome">>>; +type P1 = Print, 'LIKE', StringLiteral<"awesome">>, + 'AND', + BooleanLiteral> +>; + + +type P2 = Print>; +type P3 = Print>; diff --git a/src/Query.ts b/src/Query.ts new file mode 100644 index 0000000..bc6651a --- /dev/null +++ b/src/Query.ts @@ -0,0 +1,5 @@ +import { Evaluate } from "./Evaluator"; +import { Parse } from "./Parser"; +import { Database } from "./Schema"; + +export type Query> = Evaluate>; diff --git a/src/Schema.ts b/src/Schema.ts new file mode 100644 index 0000000..c71cd83 --- /dev/null +++ b/src/Schema.ts @@ -0,0 +1,4 @@ +export type Table = ReadonlyArray; +export type Database = { + [TableName in keyof Schema]: Table; +}; diff --git a/src/Utils/NumberUtils.ts b/src/Utils/NumberUtils.ts new file mode 100644 index 0000000..c4dd70e --- /dev/null +++ b/src/Utils/NumberUtils.ts @@ -0,0 +1,267 @@ +export type Integers = [ + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 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, + 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 +]; + +type NextIntegers = Integers extends [number, ...infer Next] ? Next : never; + +export type Inc = T extends keyof NextIntegers ? NextIntegers[T] : never; + +export type IntegerStrings = {[K in keyof Integers & string as `${K}`]: Integers[K]}; + +export type StringToNumber = IntegerStrings[T]; diff --git a/src/Utils/ObjectUtils.ts b/src/Utils/ObjectUtils.ts new file mode 100644 index 0000000..c1a0022 --- /dev/null +++ b/src/Utils/ObjectUtils.ts @@ -0,0 +1,2 @@ +type _ = T; +export type Merge = _<{ [k in keyof T]: T[k] }>; diff --git a/src/Utils/StringUtils.ts b/src/Utils/StringUtils.ts new file mode 100644 index 0000000..8c55e2a --- /dev/null +++ b/src/Utils/StringUtils.ts @@ -0,0 +1,74 @@ +export type MatchStringLike = + Right extends `%${infer Content}%` ? MatchString : + Right extends `%${infer Content}` ? MatchStringEnd : + Right extends `${infer Content}%` ? MatchStringStart : + false; + + +export type EatFirstChar = T extends `${infer _}${infer B}` ? B : ''; + +export type MatchStringStart = + Candidate extends Pattern ? true : + Candidate extends `${Pattern}${infer _}` ? true : + false; + +export type MatchStringEnd = + Candidate extends Pattern ? true : + Candidate extends `${infer _}${Pattern}` ? true : + false; + +export type MatchString = + Candidate extends '' ? false : + MatchStringStart extends true ? true : + MatchStringEnd extends true ? true : + MatchString, Pattern> extends true ? true : + false; + +export type Trim = T extends ` ${infer Rest}` ? Trim : T; + + +type Indents = { + 0: '', + 1: ' ', + 2: ' ', + 3: ' ', + 4: ' ', + 5: ' ', + 6: ' ', + 7: ' ', +}; + +export type Indent = + Level extends keyof Indents + ? `${Indents[Level]}${T & string}` + : `${Indents[7]}${T & string}`; + +type I0 = Indent<'hello world', 3> + +export type JoinStrings = + T extends [infer Head, ...infer Tail] + ? Tail extends readonly [] + ? Head + : Tail extends readonly string[] + ? Head extends '' + ? JoinStrings + : JoinStrings extends '' + ? `${Head & string}` + : `${Head & string}${Sep & string}${JoinStrings}` + : `${Head & string}` + : T extends [infer Head] + ? `${Head & string}` + : ''; + +type J0 = JoinStrings<['a', 'b', 'ccc', 'dd']>; +type J1 = JoinStrings<[], '|'>; +type J2 = JoinStrings<['aaa'], '|'>; +type J3 = JoinStrings<['aaa', '', 'bb', ''], '|'>; + + +export type StringContains = + Input extends Term ? true : + Input extends `${Term}${infer _}` ? true : + Input extends `${infer _0}${Term}${infer _1}` ? true : + Input extends `${infer _}${Term}` ? true : + false; diff --git a/src/Utils/index.ts b/src/Utils/index.ts new file mode 100644 index 0000000..4290358 --- /dev/null +++ b/src/Utils/index.ts @@ -0,0 +1,3 @@ +export * from "./NumberUtils"; +export * from "./ObjectUtils"; +export * from "./StringUtils"; diff --git a/src/index.ts b/src/index.ts new file mode 100644 index 0000000..40a7a32 --- /dev/null +++ b/src/index.ts @@ -0,0 +1,6 @@ +export * from "./Schema"; +export * from "./AST"; +export * from "./Parser"; +export * from "./Printer"; +export * from "./Evaluator"; +export * from "./Query"; diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..5964dd5 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,70 @@ +{ + "compilerOptions": { + /* Visit https://aka.ms/tsconfig.json to read more about this file */ + + /* Basic Options */ + // "incremental": true, /* Enable incremental compilation */ + "target": "es2020" /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */, + "module": "es2020" /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */, + "lib": ["dom", "dom.iterable", "esnext"], + // "allowJs": true, /* Allow javascript files to be compiled. */ + // "checkJs": true, /* Report errors in .js files. */ + // "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */ + "declaration": true /* Generates corresponding '.d.ts' file. */, + // "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */ + // "sourceMap": true, /* Generates corresponding '.map' file. */ + // "outFile": "./", /* Concatenate and emit output to single file. */ + "outDir": "./lib" /* Redirect output structure to the directory. */, + // "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */ + // "composite": true, /* Enable project compilation */ + // "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */ + // "removeComments": true, /* Do not emit comments to output. */ + // "noEmit": true, /* Do not emit outputs. */ + // "importHelpers": true, /* Import emit helpers from 'tslib'. */ + // "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */ + // "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */ + + /* Strict Type-Checking Options */ + "strict": true /* Enable all strict type-checking options. */, + // "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */ + // "strictNullChecks": true, /* Enable strict null checks. */ + // "strictFunctionTypes": true, /* Enable strict checking of function types. */ + // "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */ + // "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */ + // "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */ + // "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */ + + /* Additional Checks */ + // "noUnusedLocals": true, /* Report errors on unused locals. */ + // "noUnusedParameters": true, /* Report errors on unused parameters. */ + // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ + // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ + // "noUncheckedIndexedAccess": true, /* Include 'undefined' in index signature results */ + + /* Module Resolution Options */ + "moduleResolution": "node" /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */, + // "baseUrl": "./", /* Base directory to resolve non-absolute module names. */ + // "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */ + // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */ + // "typeRoots": [], /* List of folders to include type definitions from. */ + // "types": [], /* Type declaration files to be included in compilation. */ + // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ + "esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */, + // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */ + // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ + + /* Source Map Options */ + // "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */ + // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ + // "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */ + // "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */ + + /* Experimental Options */ + // "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */ + // "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */ + + /* Advanced Options */ + "skipLibCheck": true /* Skip type checking of declaration files. */, + "forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */ + } +} diff --git a/yarn.lock b/yarn.lock new file mode 100644 index 0000000..af261e1 --- /dev/null +++ b/yarn.lock @@ -0,0 +1,13 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +prettier@^2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.1.2.tgz#3050700dae2e4c8b67c4c3f666cdb8af405e1ce5" + integrity sha512-16c7K+x4qVlJg9rEbXl7HEGmQyZlG4R9AgP+oHKRMsMsuk8s+ATStlf1NpDqyBI1HpVyfjLOeMhH2LvuNvV5Vg== + +typescript@beta: + version "4.1.0-beta" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.1.0-beta.tgz#e4d054035d253b7a37bdc077dd71706508573e69" + integrity sha512-b/LAttdVl3G6FEmnMkDsK0xvfvaftXpSKrjXn+OVCRqrwz5WD/6QJOiN+dTorqDY+hkaH+r2gP5wI1jBDmdQ7A==